• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2019 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 #include <inttypes.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <sys/mount.h>
21 #include <sys/stat.h>
22 #include <sys/types.h>
23 #include <sys/vfs.h>
24 
25 #include <chrono>
26 #include <iostream>
27 #include <thread>
28 
29 #include <android-base/file.h>
30 #include <android-base/properties.h>
31 #include <android-base/stringprintf.h>
32 #include <android-base/strings.h>
33 #include <android-base/unique_fd.h>
34 #include <ext4_utils/ext4_utils.h>
35 #include <fs_mgr/file_wait.h>
36 #include <gtest/gtest.h>
37 #include <libdm/dm.h>
38 #include <libdm/loop_control.h>
39 #include <libfiemap/image_manager.h>
40 
41 #include "utility.h"
42 
43 using namespace android::dm;
44 using namespace std::literals;
45 using android::base::unique_fd;
46 using android::fiemap::ImageManager;
47 using android::fiemap::IsSubdir;
48 using android::fs_mgr::BlockDeviceInfo;
49 using android::fs_mgr::PartitionOpener;
50 using android::fs_mgr::WaitForFile;
51 
52 static std::string gDataPath;
53 static std::string gTestDir;
54 static constexpr char kMetadataPath[] = "/metadata/gsi/test";
55 
56 static constexpr uint64_t kTestImageSize = 1024 * 1024;
57 
58 class TestPartitionOpener final : public PartitionOpener {
59   public:
Open(const std::string & partition_name,int flags) const60     android::base::unique_fd Open(const std::string& partition_name, int flags) const override {
61         return PartitionOpener::Open(GetPathForBlockDeviceName(partition_name), flags);
62     }
GetInfo(const std::string & partition_name,BlockDeviceInfo * info) const63     bool GetInfo(const std::string& partition_name, BlockDeviceInfo* info) const override {
64         return PartitionOpener::GetInfo(GetPathForBlockDeviceName(partition_name), info);
65     }
GetDeviceString(const std::string & partition_name) const66     std::string GetDeviceString(const std::string& partition_name) const override {
67         return PartitionOpener::GetDeviceString(GetPathForBlockDeviceName(partition_name));
68     }
69 
70   private:
GetPathForBlockDeviceName(const std::string & name)71     static std::string GetPathForBlockDeviceName(const std::string& name) {
72         if (android::base::StartsWith(name, "loop") || android::base::StartsWith(name, "dm-")) {
73             return "/dev/block/"s + name;
74         }
75         return name;
76     }
77 };
78 
79 // This fixture is for tests against the device's native configuration.
80 class NativeTest : public ::testing::Test {
81   protected:
SetUp()82     void SetUp() override {
83         manager_ = ImageManager::Open(kMetadataPath, gDataPath);
84         ASSERT_NE(manager_, nullptr);
85 
86         manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
87 
88         const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
89         base_name_ = tinfo->name();
90     }
91 
TearDown()92     void TearDown() override {
93         manager_->UnmapImageDevice(base_name_);
94         manager_->DeleteBackingImage(base_name_);
95     }
96 
PropertyName()97     std::string PropertyName() { return "gsid.mapped_image." + base_name_; }
98 
99     std::unique_ptr<ImageManager> manager_;
100     std::string base_name_;
101 };
102 
TEST_F(NativeTest,CreateAndMap)103 TEST_F(NativeTest, CreateAndMap) {
104     ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
105 
106     std::string path;
107     ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &path));
108     ASSERT_TRUE(manager_->IsImageMapped(base_name_));
109     ASSERT_EQ(android::base::GetProperty(PropertyName(), ""), path);
110 
111     {
112         unique_fd fd(open(path.c_str(), O_RDWR | O_NOFOLLOW | O_CLOEXEC));
113         ASSERT_GE(fd, 0);
114         ASSERT_EQ(get_block_device_size(fd), kTestImageSize);
115     }
116 
117     ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
118     ASSERT_FALSE(manager_->IsImageMapped(base_name_));
119     ASSERT_EQ(android::base::GetProperty(PropertyName(), ""), "");
120 }
121 
TEST_F(NativeTest,DisableImage)122 TEST_F(NativeTest, DisableImage) {
123     ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
124     ASSERT_TRUE(manager_->BackingImageExists(base_name_));
125     ASSERT_TRUE(manager_->DisableImage(base_name_));
126     ASSERT_TRUE(manager_->IsImageDisabled(base_name_));
127     ASSERT_TRUE(manager_->RemoveDisabledImages());
128     ASSERT_TRUE(!manager_->BackingImageExists(base_name_));
129 }
130 
TEST_F(NativeTest,GetMappedImageDevice)131 TEST_F(NativeTest, GetMappedImageDevice) {
132     ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestImageSize, false, nullptr));
133 
134     std::string path1, path2;
135     ASSERT_TRUE(manager_->MapImageDevice(base_name_, 5s, &path1));
136     ASSERT_TRUE(manager_->GetMappedImageDevice(base_name_, &path2));
137     EXPECT_EQ(path1, path2);
138 
139     ASSERT_TRUE(manager_->UnmapImageDevice(base_name_));
140 }
141 
142 namespace {
143 
144 struct IsSubdirTestParam {
145     std::string child;
146     std::string parent;
147     bool result;
148 };
149 
150 class IsSubdirTest : public ::testing::TestWithParam<IsSubdirTestParam> {};
151 
TEST_P(IsSubdirTest,Test)152 TEST_P(IsSubdirTest, Test) {
153     const auto& param = GetParam();
154     EXPECT_EQ(param.result, IsSubdir(param.child, param.parent))
155             << "IsSubdir(child=\"" << param.child << "\", parent=\"" << param.parent
156             << "\") != " << (param.result ? "true" : "false");
157 }
158 
IsSubdirTestValues()159 std::vector<IsSubdirTestParam> IsSubdirTestValues() {
160     // clang-format off
161     std::vector<IsSubdirTestParam> base_cases{
162             {"/foo/bar",     "/foo",     true},
163             {"/foo/bar/baz", "/foo",     true},
164             {"/foo",         "/foo",     true},
165             {"/foo",         "/",        true},
166             {"/",            "/",        true},
167             {"/foo",         "/foo/bar", false},
168             {"/foo",         "/bar",     false},
169             {"/foo-bar",     "/foo",     false},
170             {"/",            "/foo",     false},
171     };
172     // clang-format on
173     std::vector<IsSubdirTestParam> ret;
174     for (const auto& e : base_cases) {
175         ret.push_back(e);
176         ret.push_back({e.child + "/", e.parent, e.result});
177         ret.push_back({e.child, e.parent + "/", e.result});
178         ret.push_back({e.child + "/", e.parent + "/", e.result});
179     }
180     return ret;
181 }
182 
183 INSTANTIATE_TEST_SUITE_P(IsSubdirTest, IsSubdirTest, ::testing::ValuesIn(IsSubdirTestValues()));
184 
185 // This allows test cases for filesystems with larger than 4KiB alignment.
186 // It creates a loop device, formats it with a FAT filesystem, and then
187 // creates an ImageManager so backing images can be created on that filesystem.
188 class VfatTest : public ::testing::Test {
189   protected:
190     // 64MB Filesystem and 32k block size by default
191     static constexpr uint64_t kBlockSize = 32768;
192     static constexpr uint64_t kFilesystemSize = 64 * 1024 * 1024;
193 
SetUp()194     void SetUp() override {
195         const ::testing::TestInfo* tinfo = ::testing::UnitTest::GetInstance()->current_test_info();
196         base_name_ = tinfo->name();
197 
198         fs_path_ = gTestDir + "/vfat.img";
199         uint64_t count = kFilesystemSize / kBlockSize;
200         std::string dd_cmd =
201                 ::android::base::StringPrintf("/system/bin/dd if=/dev/zero of=%s bs=%" PRIu64
202                                               " count=%" PRIu64 " > /dev/null 2>&1",
203                                               fs_path_.c_str(), kBlockSize, count);
204         // create mount point
205         mntpoint_ = std::string(getenv("TMPDIR")) + "/fiemap_mnt";
206         if (mkdir(mntpoint_.c_str(), S_IRWXU) < 0) {
207             ASSERT_EQ(errno, EEXIST) << strerror(errno);
208         }
209 
210         // create file for the file system
211         int ret = system(dd_cmd.c_str());
212         ASSERT_EQ(ret, 0);
213 
214         // Get and attach a loop device to the filesystem we created
215         loop_device_.emplace(fs_path_, 10s);
216         ASSERT_TRUE(loop_device_->valid());
217 
218         // create file system
219         uint64_t sectors = kFilesystemSize / 512;
220         std::string mkfs_cmd =
221                 ::android::base::StringPrintf("/system/bin/newfs_msdos -A -O Android -s %" PRIu64
222                                               " -b %" PRIu64 " %s > /dev/null 2>&1",
223                                               sectors, kBlockSize, loop_device_->device().c_str());
224         ret = system(mkfs_cmd.c_str());
225         ASSERT_EQ(ret, 0);
226 
227         // Create a wrapping DM device to prevent gsid taking the loopback path.
228         auto& dm = DeviceMapper::Instance();
229         DmTable table;
230         table.Emplace<DmTargetLinear>(0, kFilesystemSize / 512, loop_device_->device(), 0);
231 
232         dm_name_ = android::base::Basename(loop_device_->device()) + "-wrapper";
233         ASSERT_TRUE(dm.CreateDevice(dm_name_, table, &dm_path_, 10s));
234 
235         // mount the file system
236         ASSERT_EQ(mount(dm_path_.c_str(), mntpoint_.c_str(), "vfat", 0, nullptr), 0)
237                 << strerror(errno);
238     }
239 
TearDown()240     void TearDown() override {
241         // Clear up anything backed on the temporary FS.
242         if (manager_) {
243             manager_->UnmapImageIfExists(base_name_);
244             manager_->DeleteBackingImage(base_name_);
245         }
246 
247         // Unmount temporary FS.
248         if (umount(mntpoint_.c_str()) < 0) {
249             ASSERT_EQ(errno, EINVAL) << strerror(errno);
250         }
251 
252         // Destroy the dm wrapper.
253         auto& dm = DeviceMapper::Instance();
254         ASSERT_TRUE(dm.DeleteDeviceIfExists(dm_name_));
255 
256         // Destroy the loop device.
257         loop_device_ = {};
258 
259         // Destroy the temporary FS.
260         if (rmdir(mntpoint_.c_str()) < 0) {
261             ASSERT_EQ(errno, ENOENT) << strerror(errno);
262         }
263         if (unlink(fs_path_.c_str()) < 0) {
264             ASSERT_EQ(errno, ENOENT) << strerror(errno);
265         }
266     }
267 
268     std::string base_name_;
269     std::string mntpoint_;
270     std::string fs_path_;
271     std::optional<LoopDevice> loop_device_;
272     std::string dm_name_;
273     std::string dm_path_;
274     std::unique_ptr<ImageManager> manager_;
275 };
276 
277 // The actual size of the block device should be the requested size. For
278 // example, a 16KB image should be mapped as a 16KB device, even if the
279 // underlying filesystem requires 32KB to be fallocated.
TEST_F(VfatTest,DeviceIsRequestedSize)280 TEST_F(VfatTest, DeviceIsRequestedSize) {
281     manager_ = ImageManager::Open(kMetadataPath, mntpoint_);
282     ASSERT_NE(manager_, nullptr);
283 
284     manager_->set_partition_opener(std::make_unique<TestPartitionOpener>());
285 
286     // Create something not aligned to the backing fs block size.
287     constexpr uint64_t kTestSize = (kBlockSize * 64) - (kBlockSize / 2);
288     ASSERT_TRUE(manager_->CreateBackingImage(base_name_, kTestSize, false, nullptr));
289 
290     std::string path;
291     ASSERT_TRUE(manager_->MapImageDevice(base_name_, 10s, &path));
292 
293     unique_fd fd(open(path.c_str(), O_RDONLY | O_CLOEXEC));
294     ASSERT_GE(fd, 0);
295     ASSERT_EQ(get_block_device_size(fd.get()), kTestSize);
296 }
297 
298 }  // namespace
299 
Mkdir(const std::string & path)300 bool Mkdir(const std::string& path) {
301     if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
302         std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
303         return false;
304     }
305     return true;
306 }
307 
main(int argc,char ** argv)308 int main(int argc, char** argv) {
309     ::testing::InitGoogleTest(&argc, argv);
310 
311     if (argc >= 2) {
312         gDataPath = argv[1];
313     } else {
314         gDataPath = "/data/local/tmp";
315     }
316 
317     if (!Mkdir(gDataPath) || !Mkdir(kMetadataPath) || !Mkdir(kMetadataPath + "/mnt"s)) {
318         return 1;
319     }
320 
321     std::string tempdir = gDataPath + "/XXXXXX";
322     if (!mkdtemp(tempdir.data())) {
323         std::cerr << "unable to create tempdir on " << tempdir << "\n";
324         exit(EXIT_FAILURE);
325     }
326     if (!android::base::Realpath(tempdir, &gTestDir)) {
327         std::cerr << "unable to find realpath for " << tempdir;
328         exit(EXIT_FAILURE);
329     }
330 
331     auto rv = RUN_ALL_TESTS();
332 
333     std::string cmd = "rm -rf " + gTestDir;
334     system(cmd.c_str());
335 
336     return rv;
337 }
338