1 /*
2  * Copyright (C) 2024 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 <android-base/logging.h>
18 #include <gtest/gtest.h>
19 
20 #include <libdm/loop_control.h>
21 #include <logwrap/logwrap.h>
22 
23 #include <sys/ioctl.h>
24 #include <sys/mount.h>
25 #include <sys/stat.h>
26 
27 #include <linux/f2fs.h>
28 #include <linux/fs.h>
29 
30 #include <chrono>
31 #include <fstream>
32 
33 using LoopDevice = android::dm::LoopDevice;
34 using namespace std::chrono_literals;
35 
36 static const char* kMkfsPath = "/system/bin/make_f2fs";
37 static const char* kMountPath = "/system/bin/mount";
38 static const char* kUmountPath = "/system/bin/umount";
39 
40 static const char* kTestFilePath = "/data/local/tmp/mnt/test";
41 
42 namespace android {
43 
44 class F2fsTest : public testing::Test {
SetUp()45   void SetUp() override {
46     int fd = open("/data/local/tmp/img", O_RDWR | O_TRUNC | O_CREAT,
47                   (S_IRWXU | S_IRGRP | S_IROTH));
48     int flags = FS_COMPR_FL;
49     int res;
50 
51     ASSERT_NE(fd, -1);
52     res = ftruncate(fd, 100 << 20);  // 100 MB
53     ASSERT_EQ(res, 0);
54     close(fd);
55 
56     const char* make_fs_argv[] = {
57         kMkfsPath,
58         "-f",
59         "-O",
60         "extra_attr",
61         "-O",
62         "project_quota",
63         "-O",
64         "compression",
65         "-g",
66         "android",
67         "/data/local/tmp/img",
68     };
69     res = logwrap_fork_execvp(arraysize(make_fs_argv), make_fs_argv, nullptr,
70                               false, LOG_KLOG, true, nullptr);
71     ASSERT_EQ(res, 0);
72     mkdir("/data/local/tmp/mnt", (S_IRWXU | S_IRGRP | S_IROTH));
73 
74     LoopDevice loop_dev("/data/local/tmp/img", 10s);
75     ASSERT_TRUE(loop_dev.valid());
76 
77     ASSERT_EQ(mount(loop_dev.device().c_str(), "data/local/tmp/mnt", "f2fs", 0,
78                     "compress_mode=user"),
79               0);
80     test_data1 = malloc(4096);
81     ASSERT_NE(test_data1, nullptr);
82     memset(test_data1, 0x41, 4096);
83     test_data2 = malloc(4096);
84     ASSERT_NE(test_data2, nullptr);
85     memset(test_data2, 0x61, 4096);
86   }
TearDown()87   void TearDown() override {
88     ASSERT_EQ(umount2("/data/local/tmp/mnt", MNT_DETACH), 0);
89     ASSERT_EQ(unlink("/data/local/tmp/img"), 0);
90     ASSERT_EQ(rmdir("/data/local/tmp/mnt"), 0);
91     free(test_data1);
92     free(test_data2);
93   }
94 
95  protected:
96   void* test_data1;
97   void* test_data2;
98 };
99 
TEST_F(F2fsTest,test_normal_lseek)100 TEST_F(F2fsTest, test_normal_lseek) {
101   char buf[4096];
102   int fd = open(kTestFilePath, O_RDWR | O_TRUNC | O_CREAT,
103                 (S_IRWXU | S_IRGRP | S_IROTH));
104   ASSERT_NE(fd, -1);
105 
106   ASSERT_EQ(lseek(fd, 1024 * 4096, SEEK_SET), 1024 * 4096);
107   for (int i = 0; i < 1024; i++) {
108     ASSERT_EQ(write(fd, test_data1, 4096), 4096);
109   }
110   fsync(fd);
111   ASSERT_EQ(lseek(fd, 0, SEEK_HOLE), 0);
112   ASSERT_EQ(lseek(fd, 0, SEEK_DATA), 1024 * 4096);
113   lseek(fd, 0, SEEK_SET);
114   write(fd, test_data2, 4096);
115   fsync(fd);
116   ASSERT_EQ(lseek(fd, 0, SEEK_DATA), 0);
117 
118   ASSERT_EQ(lseek(fd, 0, SEEK_HOLE), 4096);
119   ASSERT_EQ(lseek(fd, 5000, SEEK_DATA), 1024 * 4096);
120 }
121 
TEST_F(F2fsTest,test_compressed_lseek)122 TEST_F(F2fsTest, test_compressed_lseek) {
123   char buf[4096];
124 
125   int fd = open(kTestFilePath, O_RDWR | O_TRUNC | O_CREAT,
126                 (S_IRWXU | S_IRGRP | S_IROTH));
127   ASSERT_NE(fd, -1);
128 
129   int flags = FS_COMPR_FL;
130   ASSERT_NE(ioctl(fd, FS_IOC_SETFLAGS, &flags), -1);
131   ASSERT_EQ(lseek(fd, 1024 * 4096, SEEK_SET), 1024 * 4096);
132   for (int i = 0; i < 1024; i++) {
133     ASSERT_EQ(write(fd, test_data1, 4096), 4096);
134   }
135   fsync(fd);
136   ASSERT_EQ(lseek(fd, 0, SEEK_HOLE), 0);
137   ASSERT_EQ(lseek(fd, 0, SEEK_DATA), 1024 * 4096);
138   ASSERT_NE(ioctl(fd, F2FS_IOC_COMPRESS_FILE), -1);
139   lseek(fd, 0, SEEK_SET);
140   write(fd, test_data2, 4096);
141   fsync(fd);
142   ASSERT_EQ(lseek(fd, 0, SEEK_DATA), 0);
143   ASSERT_EQ(lseek(fd, 0, SEEK_HOLE), 4096);
144   ASSERT_EQ(lseek(fd, 5000, SEEK_DATA), 1024 * 4096);
145 }
146 
TEST_F(F2fsTest,test_sparse_decompress)147 TEST_F(F2fsTest, test_sparse_decompress) {
148   char buf[4096];
149   int res;
150 
151   int fd = open(kTestFilePath, O_RDWR | O_TRUNC | O_CREAT,
152                 (S_IRWXU | S_IRGRP | S_IROTH));
153   ASSERT_NE(fd, -1);
154   int flags = FS_COMPR_FL;
155 
156   ASSERT_NE(fd, -1);
157 
158   ASSERT_NE(ioctl(fd, FS_IOC_SETFLAGS, &flags), -1);
159   res = lseek(fd, 1024 * 4096, SEEK_SET);
160   ASSERT_EQ(res, 1024 * 4096);
161   for (int i = 0; i < 1024; i++) {
162     res = write(fd, test_data1, 4096);
163     ASSERT_EQ(res, 4096);
164   }
165   fsync(fd);
166   ASSERT_NE(ioctl(fd, F2FS_IOC_COMPRESS_FILE), -1);
167   lseek(fd, 0, SEEK_SET);
168   write(fd, test_data2, 4096);
169   fsync(fd);
170   int pid = fork();
171   if (pid == 0) {
172     // If this fails, we must reset the device or it will be left in a bad state
173     exit(ioctl(fd, F2FS_IOC_DECOMPRESS_FILE));
174   }
175   int status;
176   int time = 0;
177   while (time < 50) {
178     res = waitpid(pid, &status, WNOHANG);
179     if (res) {
180       ASSERT_EQ(pid, res);
181       ASSERT_EQ(WIFEXITED(status), true);
182       ASSERT_EQ(WEXITSTATUS(status), 0);
183       break;
184     }
185     sleep(5);
186     time += 5;
187   }
188   if (!res) {
189     std::ofstream reboot_trigger("/proc/sysrq-trigger");
190     reboot_trigger << "c";
191     reboot_trigger.close();
192     return;
193   }
194   close(fd);
195   // Check for corruption
196   fd = open(kTestFilePath, O_RDONLY);
197   ASSERT_NE(fd, -1);
198   res = read(fd, buf, 4096);
199   ASSERT_EQ(res, 4096);
200   ASSERT_EQ(memcmp(buf, test_data2, 4096), 0);
201 
202   char empty_buf[4096];
203   memset(empty_buf, 0, 4096);
204   for (int i = 1; i < 1024; i++) {
205     res = read(fd, buf, 4096);
206     ASSERT_EQ(res, 4096);
207     ASSERT_EQ(memcmp(buf, empty_buf, 4096), 0);
208   }
209   for (int i = 0; i < 1024; i++) {
210     res = read(fd, buf, 4096);
211     ASSERT_EQ(res, 4096);
212     ASSERT_EQ(memcmp(buf, test_data1, 4096), 0);
213   }
214   close(fd);
215 }
216 
217 }  // namespace android
218