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