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