1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * The tests in this file operate on a higher level than the tests in the other
19  * files. Here, all tests execute the idmap2 binary and only depend on
20  * libidmap2 to verify the output of idmap2.
21  */
22 #include <fcntl.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <unistd.h>
27 
28 #include <cerrno>
29 #include <cstdlib>
30 #include <cstring>  // strerror
31 #include <fstream>
32 #include <memory>
33 #include <sstream>
34 #include <string>
35 #include <vector>
36 
37 #include "R.h"
38 #include "TestConstants.h"
39 #include "TestHelpers.h"
40 #include "androidfw/PosixUtils.h"
41 #include "gmock/gmock.h"
42 #include "gtest/gtest.h"
43 #include "idmap2/FileUtils.h"
44 #include "idmap2/Idmap.h"
45 #include "private/android_filesystem_config.h"
46 
47 using ::android::base::StringPrintf;
48 using ::android::util::ExecuteBinary;
49 using ::testing::NotNull;
50 
51 namespace android::idmap2 {
52 
53 class Idmap2BinaryTests : public Idmap2Tests {};
54 
55 namespace {
56 
AssertIdmap(const Idmap & idmap,const std::string & target_apk_path,const std::string & overlay_apk_path)57 void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
58                  const std::string& overlay_apk_path) {
59   // check that the idmap file looks reasonable (IdmapTests is responsible for
60   // more in-depth verification)
61   ASSERT_EQ(idmap.GetHeader()->GetMagic(), kIdmapMagic);
62   ASSERT_EQ(idmap.GetHeader()->GetVersion(), kIdmapCurrentVersion);
63   ASSERT_EQ(idmap.GetHeader()->GetTargetPath(), target_apk_path);
64   ASSERT_EQ(idmap.GetHeader()->GetOverlayPath(), overlay_apk_path);
65   ASSERT_EQ(idmap.GetData().size(), 1U);
66 }
67 
68 #define ASSERT_IDMAP(idmap_ref, target_apk_path, overlay_apk_path)                      \
69   do {                                                                                  \
70     ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \
71   } while (0)
72 
73 #ifdef __ANDROID__
74 #define SKIP_TEST_IF_CANT_EXEC_IDMAP2           \
75   do {                                          \
76     const uid_t uid = getuid();                 \
77     if (uid != AID_ROOT && uid != AID_SYSTEM) { \
78       GTEST_SKIP();                             \
79     }                                           \
80   } while (0)
81 #else
82 #define SKIP_TEST_IF_CANT_EXEC_IDMAP2
83 #endif
84 
85 }  // namespace
86 
TEST_F(Idmap2BinaryTests,Create)87 TEST_F(Idmap2BinaryTests, Create) {
88   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
89 
90   // clang-format off
91   auto result = ExecuteBinary({"idmap2",
92                                "create",
93                                "--target-apk-path", GetTargetApkPath(),
94                                "--overlay-apk-path", GetOverlayApkPath(),
95                                "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
96                                "--idmap-path", GetIdmapPath()});
97   // clang-format on
98   ASSERT_THAT(result, NotNull());
99   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
100 
101   struct stat st;
102   ASSERT_EQ(stat(GetIdmapPath().c_str(), &st), 0);
103 
104   std::ifstream fin(GetIdmapPath());
105   const auto idmap = Idmap::FromBinaryStream(fin);
106   fin.close();
107 
108   ASSERT_TRUE(idmap);
109   ASSERT_IDMAP(**idmap, GetTargetApkPath(), GetOverlayApkPath());
110 
111   unlink(GetIdmapPath().c_str());
112 }
113 
TEST_F(Idmap2BinaryTests,Dump)114 TEST_F(Idmap2BinaryTests, Dump) {
115   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
116 
117   // clang-format off
118   auto result = ExecuteBinary({"idmap2",
119                                "create",
120                                "--target-apk-path", GetTargetApkPath(),
121                                "--overlay-apk-path", GetOverlayApkPath(),
122                                "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
123                                "--idmap-path", GetIdmapPath()});
124   // clang-format on
125   ASSERT_THAT(result, NotNull());
126   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
127 
128   // clang-format off
129   result = ExecuteBinary({"idmap2",
130                           "dump",
131                           "--idmap-path", GetIdmapPath()});
132   // clang-format on
133   ASSERT_THAT(result, NotNull());
134   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
135 
136   ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::integer::int1,
137                                              R::overlay::integer::int1)),
138             std::string::npos)
139       << result->stdout;
140   ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str1,
141                                              R::overlay::string::str1)),
142             std::string::npos)
143       << result->stdout;
144   ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str3,
145                                              R::overlay::string::str3)),
146             std::string::npos)
147       << result->stdout;
148   ASSERT_NE(result->stdout.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str4,
149                                              R::overlay::string::str4)),
150             std::string::npos)
151       << result->stdout;
152 
153   // clang-format off
154   result = ExecuteBinary({"idmap2",
155                           "dump",
156                           "--verbose",
157                           "--idmap-path", GetIdmapPath()});
158   // clang-format on
159   ASSERT_THAT(result, NotNull());
160   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
161   ASSERT_NE(result->stdout.find("00000000: 504d4449  magic"), std::string::npos);
162 
163   // clang-format off
164   result = ExecuteBinary({"idmap2",
165                           "dump",
166                           "--verbose",
167                           "--idmap-path", GetTestDataPath() + "/DOES-NOT-EXIST"});
168   // clang-format on
169   ASSERT_THAT(result, NotNull());
170   ASSERT_NE(result->status, EXIT_SUCCESS);
171 
172   unlink(GetIdmapPath().c_str());
173 }
174 
TEST_F(Idmap2BinaryTests,Lookup)175 TEST_F(Idmap2BinaryTests, Lookup) {
176   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
177 
178   // clang-format off
179   auto result = ExecuteBinary({"idmap2",
180                                "create",
181                                "--target-apk-path", GetTargetApkPath(),
182                                "--overlay-apk-path", GetOverlayApkPath(),
183                                "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
184                                "--idmap-path", GetIdmapPath()});
185   // clang-format on
186   ASSERT_THAT(result, NotNull());
187   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
188 
189   // clang-format off
190   result = ExecuteBinary({"idmap2",
191                           "lookup",
192                           "--idmap-path", GetIdmapPath(),
193                           "--config", "",
194                           "--resid", StringPrintf("0x%08x", R::target::string::str1)});
195   // clang-format on
196   ASSERT_THAT(result, NotNull());
197   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
198   ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
199   ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
200 
201   // clang-format off
202   result = ExecuteBinary({"idmap2",
203                           "lookup",
204                           "--idmap-path", GetIdmapPath(),
205                           "--config", "",
206                           "--resid", "test.target:string/str1"});
207   // clang-format on
208   ASSERT_THAT(result, NotNull());
209   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
210   ASSERT_NE(result->stdout.find("overlay-1"), std::string::npos);
211   ASSERT_EQ(result->stdout.find("overlay-1-sv"), std::string::npos);
212 
213   // clang-format off
214   result = ExecuteBinary({"idmap2",
215                           "lookup",
216                           "--idmap-path", GetIdmapPath(),
217                           "--config", "sv",
218                           "--resid", "test.target:string/str1"});
219   // clang-format on
220   ASSERT_THAT(result, NotNull());
221   ASSERT_EQ(result->status, EXIT_SUCCESS) << result->stderr;
222   ASSERT_NE(result->stdout.find("overlay-1-sv"), std::string::npos);
223 
224   unlink(GetIdmapPath().c_str());
225 }
226 
TEST_F(Idmap2BinaryTests,InvalidCommandLineOptions)227 TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) {
228   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
229 
230   const std::string invalid_target_apk_path = GetTestDataPath() + "/DOES-NOT-EXIST";
231 
232   // missing mandatory options
233   // clang-format off
234   auto result = ExecuteBinary({"idmap2",
235                                "create"});
236   // clang-format on
237   ASSERT_THAT(result, NotNull());
238   ASSERT_NE(result->status, EXIT_SUCCESS);
239 
240   // missing argument to option
241   // clang-format off
242   result = ExecuteBinary({"idmap2",
243                           "create",
244                           "--target-apk-path", GetTargetApkPath(),
245                           "--overlay-apk-path", GetOverlayApkPath(),
246                           "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
247                           "--idmap-path"});
248   // clang-format on
249   ASSERT_THAT(result, NotNull());
250   ASSERT_NE(result->status, EXIT_SUCCESS);
251 
252   // invalid target apk path
253   // clang-format off
254   result = ExecuteBinary({"idmap2",
255                           "create",
256                           "--target-apk-path", invalid_target_apk_path,
257                           "--overlay-apk-path", GetOverlayApkPath(),
258                           "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
259                           "--idmap-path", GetIdmapPath()});
260   // clang-format on
261   ASSERT_THAT(result, NotNull());
262   ASSERT_NE(result->status, EXIT_SUCCESS);
263 
264   // unknown policy
265   // clang-format off
266   result = ExecuteBinary({"idmap2",
267                           "create",
268                           "--target-apk-path", GetTargetApkPath(),
269                           "--overlay-apk-path", GetOverlayApkPath(),
270                           "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
271                           "--idmap-path", GetIdmapPath(),
272                           "--policy", "this-does-not-exist"});
273   // clang-format on
274   ASSERT_THAT(result, NotNull());
275   ASSERT_NE(result->status, EXIT_SUCCESS);
276 }
277 
278 }  // namespace android::idmap2
279