• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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,    "-f",          "-O",
58         "extra_attr", "-O",          "project_quota",
59         "-O",         "compression", "-O",
60         "casefold",   "-C",          "utf8",
61         "-g",         "android",     "/data/local/tmp/img",
62     };
63     res = logwrap_fork_execvp(arraysize(make_fs_argv), make_fs_argv, nullptr,
64                               false, LOG_KLOG, true, nullptr);
65     ASSERT_EQ(res, 0);
66     mkdir("/data/local/tmp/mnt", (S_IRWXU | S_IRGRP | S_IROTH));
67 
68     LoopDevice loop_dev("/data/local/tmp/img", 10s);
69     ASSERT_TRUE(loop_dev.valid());
70 
71     ASSERT_EQ(mount(loop_dev.device().c_str(), "data/local/tmp/mnt", "f2fs", 0,
72                     "compress_mode=user"),
73               0);
74     test_data1 = malloc(4096);
75     ASSERT_NE(test_data1, nullptr);
76     memset(test_data1, 0x41, 4096);
77     test_data2 = malloc(4096);
78     ASSERT_NE(test_data2, nullptr);
79     memset(test_data2, 0x61, 4096);
80   }
TearDown()81   void TearDown() override {
82     ASSERT_EQ(umount2("/data/local/tmp/mnt", MNT_DETACH), 0);
83     ASSERT_EQ(unlink("/data/local/tmp/img"), 0);
84     ASSERT_EQ(rmdir("/data/local/tmp/mnt"), 0);
85     free(test_data1);
86     free(test_data2);
87   }
88 
89  protected:
90   void* test_data1;
91   void* test_data2;
92 };
93 
TEST_F(F2fsTest,test_normal_lseek)94 TEST_F(F2fsTest, test_normal_lseek) {
95   char buf[4096];
96   int fd = open(kTestFilePath, O_RDWR | O_TRUNC | O_CREAT,
97                 (S_IRWXU | S_IRGRP | S_IROTH));
98   ASSERT_NE(fd, -1);
99 
100   ASSERT_EQ(lseek(fd, 1024 * 4096, SEEK_SET), 1024 * 4096);
101   for (int i = 0; i < 1024; i++) {
102     ASSERT_EQ(write(fd, test_data1, 4096), 4096);
103   }
104   fsync(fd);
105   ASSERT_EQ(lseek(fd, 0, SEEK_HOLE), 0);
106   ASSERT_EQ(lseek(fd, 0, SEEK_DATA), 1024 * 4096);
107   lseek(fd, 0, SEEK_SET);
108   write(fd, test_data2, 4096);
109   fsync(fd);
110   ASSERT_EQ(lseek(fd, 0, SEEK_DATA), 0);
111 
112   ASSERT_EQ(lseek(fd, 0, SEEK_HOLE), 4096);
113   ASSERT_EQ(lseek(fd, 5000, SEEK_DATA), 1024 * 4096);
114 }
115 
TEST_F(F2fsTest,test_compressed_lseek)116 TEST_F(F2fsTest, test_compressed_lseek) {
117   char buf[4096];
118 
119   int fd = open(kTestFilePath, O_RDWR | O_TRUNC | O_CREAT,
120                 (S_IRWXU | S_IRGRP | S_IROTH));
121   ASSERT_NE(fd, -1);
122 
123   int flags = FS_COMPR_FL;
124   ASSERT_NE(ioctl(fd, FS_IOC_SETFLAGS, &flags), -1);
125   ASSERT_EQ(lseek(fd, 1024 * 4096, SEEK_SET), 1024 * 4096);
126   for (int i = 0; i < 1024; i++) {
127     ASSERT_EQ(write(fd, test_data1, 4096), 4096);
128   }
129   fsync(fd);
130   ASSERT_EQ(lseek(fd, 0, SEEK_HOLE), 0);
131   ASSERT_EQ(lseek(fd, 0, SEEK_DATA), 1024 * 4096);
132   ASSERT_NE(ioctl(fd, F2FS_IOC_COMPRESS_FILE), -1);
133   lseek(fd, 0, SEEK_SET);
134   write(fd, test_data2, 4096);
135   fsync(fd);
136   ASSERT_EQ(lseek(fd, 0, SEEK_DATA), 0);
137   ASSERT_EQ(lseek(fd, 0, SEEK_HOLE), 4096);
138   ASSERT_EQ(lseek(fd, 5000, SEEK_DATA), 1024 * 4096);
139 }
140 
TEST_F(F2fsTest,test_sparse_decompress)141 TEST_F(F2fsTest, test_sparse_decompress) {
142   char buf[4096];
143   int res;
144 
145   int fd = open(kTestFilePath, O_RDWR | O_TRUNC | O_CREAT,
146                 (S_IRWXU | S_IRGRP | S_IROTH));
147   ASSERT_NE(fd, -1);
148   int flags = FS_COMPR_FL;
149 
150   ASSERT_NE(fd, -1);
151 
152   ASSERT_NE(ioctl(fd, FS_IOC_SETFLAGS, &flags), -1);
153   res = lseek(fd, 1024 * 4096, SEEK_SET);
154   ASSERT_EQ(res, 1024 * 4096);
155   for (int i = 0; i < 1024; i++) {
156     res = write(fd, test_data1, 4096);
157     ASSERT_EQ(res, 4096);
158   }
159   fsync(fd);
160   ASSERT_NE(ioctl(fd, F2FS_IOC_COMPRESS_FILE), -1);
161   lseek(fd, 0, SEEK_SET);
162   write(fd, test_data2, 4096);
163   fsync(fd);
164   int pid = fork();
165   if (pid == 0) {
166     // If this fails, we must reset the device or it will be left in a bad state
167     exit(ioctl(fd, F2FS_IOC_DECOMPRESS_FILE));
168   }
169   int status;
170   int time = 0;
171   while (time < 50) {
172     res = waitpid(pid, &status, WNOHANG);
173     if (res) {
174       ASSERT_EQ(pid, res);
175       ASSERT_EQ(WIFEXITED(status), true);
176       ASSERT_EQ(WEXITSTATUS(status), 0);
177       break;
178     }
179     sleep(5);
180     time += 5;
181   }
182   if (!res) {
183     std::ofstream reboot_trigger("/proc/sysrq-trigger");
184     reboot_trigger << "c";
185     reboot_trigger.close();
186     return;
187   }
188   close(fd);
189   // Check for corruption
190   fd = open(kTestFilePath, O_RDONLY);
191   ASSERT_NE(fd, -1);
192   res = read(fd, buf, 4096);
193   ASSERT_EQ(res, 4096);
194   ASSERT_EQ(memcmp(buf, test_data2, 4096), 0);
195 
196   char empty_buf[4096];
197   memset(empty_buf, 0, 4096);
198   for (int i = 1; i < 1024; i++) {
199     res = read(fd, buf, 4096);
200     ASSERT_EQ(res, 4096);
201     ASSERT_EQ(memcmp(buf, empty_buf, 4096), 0);
202   }
203   for (int i = 0; i < 1024; i++) {
204     res = read(fd, buf, 4096);
205     ASSERT_EQ(res, 4096);
206     ASSERT_EQ(memcmp(buf, test_data1, 4096), 0);
207   }
208   close(fd);
209 }
210 
TEST_F(F2fsTest,test_casefolding_ignorable_codepoint)211 TEST_F(F2fsTest, test_casefolding_ignorable_codepoint) {
212   const char* kTestFolder = "/data/local/tmp/mnt/cf/";
213   const char* kTestFileCase1Path = "/data/local/tmp/mnt/cf/❤";  // u2764
214   const char* kTestFileCase2Path = "/data/local/tmp/mnt/cf/❤️";  // u2764 + ufe0f
215   struct stat stat1;
216   struct stat stat2;
217 
218   ASSERT_EQ(mkdir(kTestFolder, (S_IRWXU | S_IRGRP | S_IROTH)), 0);
219   int fd = open(kTestFolder, O_RDONLY | O_DIRECTORY);
220   ASSERT_NE(fd, -1);
221   int flag = FS_CASEFOLD_FL;
222   ASSERT_EQ(ioctl(fd, FS_IOC_SETFLAGS, &flag), 0);
223   close(fd);
224 
225   fd = open(kTestFileCase1Path, O_RDWR | O_TRUNC | O_CREAT,
226             (S_IRWXU | S_IRGRP | S_IROTH));
227   ASSERT_NE(fd, -1);
228   ASSERT_NE(fstat(fd, &stat1), -1);
229   close(fd);
230 
231   fd = open(kTestFileCase2Path, O_RDWR | O_TRUNC | O_CREAT,
232             (S_IRWXU | S_IRGRP | S_IROTH));
233   ASSERT_NE(fd, -1);
234   ASSERT_NE(fstat(fd, &stat2), -1);
235   close(fd);
236 
237   ASSERT_EQ(stat1.st_ino, stat2.st_ino);
238 }
239 
240 }  // namespace android
241