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 
50 namespace android::idmap2 {
51 
52 class Idmap2BinaryTests : public Idmap2Tests {};
53 
54 namespace {
55 
AssertIdmap(const Idmap & idmap,const std::string & target_apk_path,const std::string & overlay_apk_path)56 void AssertIdmap(const Idmap& idmap, const std::string& target_apk_path,
57                  const std::string& overlay_apk_path) {
58   // check that the idmap file looks reasonable (IdmapTests is responsible for
59   // more in-depth verification)
60   ASSERT_EQ(idmap.GetHeader()->GetMagic(), kIdmapMagic);
61   ASSERT_EQ(idmap.GetHeader()->GetVersion(), kIdmapCurrentVersion);
62   ASSERT_EQ(idmap.GetHeader()->GetTargetPath(), target_apk_path);
63   ASSERT_EQ(idmap.GetHeader()->GetOverlayPath(), overlay_apk_path);
64   ASSERT_EQ(idmap.GetData().size(), 1U);
65 }
66 
67 #define ASSERT_IDMAP(idmap_ref, target_apk_path, overlay_apk_path)                      \
68   do {                                                                                  \
69     ASSERT_NO_FATAL_FAILURE(AssertIdmap(idmap_ref, target_apk_path, overlay_apk_path)); \
70   } while (0)
71 
72 #ifdef __ANDROID__
73 #define SKIP_TEST_IF_CANT_EXEC_IDMAP2           \
74   do {                                          \
75     const uid_t uid = getuid();                 \
76     if (uid != AID_ROOT && uid != AID_SYSTEM) { \
77       GTEST_SKIP();                             \
78     }                                           \
79   } while (0)
80 #else
81 #define SKIP_TEST_IF_CANT_EXEC_IDMAP2
82 #endif
83 
84 }  // namespace
85 
TEST_F(Idmap2BinaryTests,Create)86 TEST_F(Idmap2BinaryTests, Create) {
87   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
88 
89   // clang-format off
90   auto result = ExecuteBinary({"idmap2",
91                                "create",
92                                "--target-apk-path", GetTargetApkPath(),
93                                "--overlay-apk-path", GetOverlayApkPath(),
94                                "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
95                                "--idmap-path", GetIdmapPath()});
96   // clang-format on
97   ASSERT_TRUE((bool)result);
98   ASSERT_EQ(result.status, EXIT_SUCCESS) << result.stderr_str;
99 
100   struct stat st;
101   ASSERT_EQ(stat(GetIdmapPath().c_str(), &st), 0);
102 
103   std::ifstream fin(GetIdmapPath());
104   const auto idmap = Idmap::FromBinaryStream(fin);
105   fin.close();
106 
107   ASSERT_TRUE(idmap);
108   ASSERT_IDMAP(**idmap, GetTargetApkPath(), GetOverlayApkPath());
109 
110   unlink(GetIdmapPath().c_str());
111 }
112 
TEST_F(Idmap2BinaryTests,Dump)113 TEST_F(Idmap2BinaryTests, Dump) {
114   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
115 
116   // clang-format off
117   auto result = ExecuteBinary({"idmap2",
118                                "create",
119                                "--target-apk-path", GetTargetApkPath(),
120                                "--overlay-apk-path", GetOverlayApkPath(),
121                                "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
122                                "--idmap-path", GetIdmapPath()});
123   // clang-format on
124   ASSERT_TRUE((bool)result);
125   ASSERT_EQ(result.status, EXIT_SUCCESS) << result.stderr_str;
126 
127   // clang-format off
128   result = ExecuteBinary({"idmap2",
129                           "dump",
130                           "--idmap-path", GetIdmapPath()});
131   // clang-format on
132   ASSERT_TRUE((bool)result);
133   ASSERT_EQ(result.status, EXIT_SUCCESS) << result.stderr_str;
134 
135   ASSERT_NE(result.stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::integer::int1,
136                                                  R::overlay::integer::int1)),
137             std::string::npos)
138       << result.stdout_str;
139   ASSERT_NE(result.stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str1,
140                                                  R::overlay::string::str1)),
141             std::string::npos)
142       << result.stdout_str;
143   ASSERT_NE(result.stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str3,
144                                                  R::overlay::string::str3)),
145             std::string::npos)
146       << result.stdout_str;
147   ASSERT_NE(result.stdout_str.find(StringPrintf("0x%08x -> 0x%08x", R::target::string::str4,
148                                                  R::overlay::string::str4)),
149             std::string::npos)
150       << result.stdout_str;
151 
152   // clang-format off
153   result = ExecuteBinary({"idmap2",
154                           "dump",
155                           "--verbose",
156                           "--idmap-path", GetIdmapPath()});
157   // clang-format on
158   ASSERT_TRUE((bool)result);
159   ASSERT_EQ(result.status, EXIT_SUCCESS) << result.stderr_str;
160   ASSERT_NE(result.stdout_str.find("00000000: 504d4449  magic"), std::string::npos);
161 
162   // clang-format off
163   result = ExecuteBinary({"idmap2",
164                           "dump",
165                           "--verbose",
166                           "--idmap-path", GetTestDataPath() + "/DOES-NOT-EXIST"});
167   // clang-format on
168   ASSERT_TRUE((bool)result);
169   ASSERT_NE(result.status, EXIT_SUCCESS);
170 
171   unlink(GetIdmapPath().c_str());
172 }
173 
TEST_F(Idmap2BinaryTests,Lookup)174 TEST_F(Idmap2BinaryTests, Lookup) {
175   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
176 
177   // clang-format off
178   auto result = ExecuteBinary({"idmap2",
179                                "create",
180                                "--target-apk-path", GetTargetApkPath(),
181                                "--overlay-apk-path", GetOverlayApkPath(),
182                                "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
183                                "--idmap-path", GetIdmapPath()});
184   // clang-format on
185   ASSERT_TRUE((bool)result);
186   ASSERT_EQ(result.status, EXIT_SUCCESS) << result.stderr_str;
187 
188   // clang-format off
189   result = ExecuteBinary({"idmap2",
190                           "lookup",
191                           "--idmap-path", GetIdmapPath(),
192                           "--config", "",
193                           "--resid", StringPrintf("0x%08x", R::target::string::str1)});
194   // clang-format on
195   ASSERT_TRUE((bool)result);
196   ASSERT_EQ(result.status, EXIT_SUCCESS) << result.stderr_str;
197   ASSERT_NE(result.stdout_str.find("overlay-1"), std::string::npos);
198   ASSERT_EQ(result.stdout_str.find("overlay-1-sv"), std::string::npos);
199 
200   // clang-format off
201   result = ExecuteBinary({"idmap2",
202                           "lookup",
203                           "--idmap-path", GetIdmapPath(),
204                           "--config", "",
205                           "--resid", "test.target:string/str1"});
206   // clang-format on
207   ASSERT_TRUE((bool)result);
208   ASSERT_EQ(result.status, EXIT_SUCCESS) << result.stderr_str;
209   ASSERT_NE(result.stdout_str.find("overlay-1"), std::string::npos);
210   ASSERT_EQ(result.stdout_str.find("overlay-1-sv"), std::string::npos);
211 
212   // clang-format off
213   result = ExecuteBinary({"idmap2",
214                           "lookup",
215                           "--idmap-path", GetIdmapPath(),
216                           "--config", "sv",
217                           "--resid", "test.target:string/str1"});
218   // clang-format on
219   ASSERT_TRUE((bool)result);
220   ASSERT_EQ(result.status, EXIT_SUCCESS) << result.stderr_str;
221   ASSERT_NE(result.stdout_str.find("overlay-1-sv"), std::string::npos);
222 
223   unlink(GetIdmapPath().c_str());
224 }
225 
TEST_F(Idmap2BinaryTests,InvalidCommandLineOptions)226 TEST_F(Idmap2BinaryTests, InvalidCommandLineOptions) {
227   SKIP_TEST_IF_CANT_EXEC_IDMAP2;
228 
229   const std::string invalid_target_apk_path = GetTestDataPath() + "/DOES-NOT-EXIST";
230 
231   // missing mandatory options
232   // clang-format off
233   auto result = ExecuteBinary({"idmap2",
234                                "create"});
235   // clang-format on
236   ASSERT_TRUE((bool)result);
237   ASSERT_NE(result.status, EXIT_SUCCESS);
238 
239   // missing argument to option
240   // clang-format off
241   result = ExecuteBinary({"idmap2",
242                           "create",
243                           "--target-apk-path", GetTargetApkPath(),
244                           "--overlay-apk-path", GetOverlayApkPath(),
245                           "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
246                           "--idmap-path"});
247   // clang-format on
248   ASSERT_TRUE((bool)result);
249   ASSERT_NE(result.status, EXIT_SUCCESS);
250 
251   // invalid target apk path
252   // clang-format off
253   result = ExecuteBinary({"idmap2",
254                           "create",
255                           "--target-apk-path", invalid_target_apk_path,
256                           "--overlay-apk-path", GetOverlayApkPath(),
257                           "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
258                           "--idmap-path", GetIdmapPath()});
259   // clang-format on
260   ASSERT_TRUE((bool)result);
261   ASSERT_NE(result.status, EXIT_SUCCESS);
262 
263   // unknown policy
264   // clang-format off
265   result = ExecuteBinary({"idmap2",
266                           "create",
267                           "--target-apk-path", GetTargetApkPath(),
268                           "--overlay-apk-path", GetOverlayApkPath(),
269                           "--overlay-name", TestConstants::OVERLAY_NAME_DEFAULT,
270                           "--idmap-path", GetIdmapPath(),
271                           "--policy", "this-does-not-exist"});
272   // clang-format on
273   ASSERT_TRUE((bool)result);
274   ASSERT_NE(result.status, EXIT_SUCCESS);
275 }
276 
277 }  // namespace android::idmap2
278