]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-move-data-extents.c
hfs-366.70.1.tar.gz
[apple/hfs.git] / tests / cases / test-move-data-extents.c
1 #include <TargetConditionals.h>
2
3 #if !TARGET_OS_EMBEDDED
4
5 #include <sys/fcntl.h>
6 #include <sys/types.h>
7 #include <limits.h>
8 #include <unistd.h>
9 #include <sys/stat.h>
10 #include <pthread.h>
11 #include <sys/mount.h>
12 #include <sys/param.h>
13 #include <copyfile.h>
14
15 #include "hfs-tests.h"
16 #include "test-utils.h"
17 #include "disk-image.h"
18
19 TEST(move_data_extents)
20
21 static disk_image_t *di;
22 static char *file1, *file2;
23
24 #define TEST_FILE_1 "/tmp/move_data_extents.1"
25 #define TEST_FILE_2 "/tmp/move_data_extents.2"
26
27 static volatile bool run_thread;
28
29 static void *write_thread(void *param)
30 {
31 int fd = (int)(uintptr_t)param;
32
33 while (run_thread)
34 write(fd, &fd, 4);
35
36 return NULL;
37 }
38
39 static int make_frag_file(int blocks_per_extent)
40 {
41 int fd;
42
43 asprintf(&file1, "%s/move_data_extents.1", di->mount_point);
44 assert_with_errno((fd = open(file1,
45 O_CREAT | O_RDWR | O_TRUNC | O_EVTONLY, 0666)) >= 0);
46
47 struct statfs sfs;
48 assert_no_err(statfs(file1, &sfs));
49
50 fstore_t fstore = {
51 .fst_flags = F_ALLOCATECONTIG | F_ALLOCATEALL,
52 .fst_posmode = F_PEOFPOSMODE,
53 };
54
55 off_t len = 0;
56
57 for (int i = 0; i < 9; ++i) {
58 if (len) {
59 struct log2phys l2p = {
60 .l2p_contigbytes = 1024 * 1024,
61 .l2p_devoffset = len - 1,
62 };
63
64 if (fcntl(fd, F_LOG2PHYS_EXT, &l2p)) {
65 if (errno == ERANGE)
66 break;
67 assert_fail("fcntl failed: %s", strerror(errno));
68 }
69
70 fstore.fst_posmode = F_VOLPOSMODE;
71 fstore.fst_offset = l2p.l2p_devoffset + 1 + sfs.f_bsize;
72 }
73
74 len += blocks_per_extent * sfs.f_bsize;
75
76 fstore.fst_length = len;
77
78 assert_no_err(fcntl(fd, F_PREALLOCATE, &fstore));
79 }
80
81 assert_no_err(ftruncate(fd, len));
82
83 return fd;
84 }
85
86 int run_move_data_extents(__unused test_ctx_t *ctx)
87 {
88 di = disk_image_get();
89
90 int fd = make_frag_file(1);
91
92 struct stat sb;
93 fstat(fd, &sb);
94
95 int fd2;
96
97 asprintf(&file2, "%s/move_data_extents.2", di->mount_point);
98 assert_with_errno((fd2 = open(file2,
99 O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
100
101 #define F_MOVEDATAEXTENTS 69
102
103 assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
104
105 char buf[4096];
106 check_io(pread(fd2, buf, 4096, sb.st_size - 4096), 4096);
107
108 check_io(pwrite(fd2, buf, 100, sb.st_size), 100);
109
110 close(fd2);
111
112 assert_with_errno((fd = open(file1,
113 O_APPEND | O_RDWR)) >= 0);
114
115 run_thread = true;
116
117 pthread_t thread;
118 pthread_create(&thread, NULL, write_thread, (void *)(uintptr_t)fd);
119
120 for (int i = 0; i < 500; ++i) {
121 assert_with_errno((fd2 = open(file2,
122 O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
123
124 assert(fcntl(fd, F_MOVEDATAEXTENTS, fd2) == -1 && errno == EBUSY);
125
126 assert_no_err(close(fd2));
127 }
128
129 run_thread = false;
130 pthread_join(thread, NULL);
131
132 close(fd);
133
134 /*
135 * Make sure that the extents from move_data_extents.1 got deleted
136 * properly. To test this, we do another F_MOVEDATAEXTENTS and
137 * this time if it's broken it will move the bad extents.
138 */
139
140 fd = make_frag_file(2);
141
142 assert_with_errno((fd2 = open(file2,
143 O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
144
145 assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
146
147 close(fd);
148 close(fd2);
149
150 // And this time it should fail if there's a bug
151 fd = make_frag_file(1);
152
153 assert_with_errno((fd2 = open(file2,
154 O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
155
156 assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
157
158 assert_no_err(unlink(file1));
159 assert_no_err(unlink(file2));
160 assert_no_err(close(fd));
161 assert_no_err(close(fd2));
162
163 assert_no_err(copyfile("/bin/bash", file1, NULL, COPYFILE_ALL));
164
165 assert_no_err(chmod(file1, 0777));
166
167 assert_no_err(stat(file1, &sb));
168
169 assert(sb.st_flags & UF_COMPRESSED);
170
171 assert_with_errno((fd = open(file1, O_RDONLY | O_EVTONLY)) >= 0);
172
173 assert_with_errno((fd2 = open(file2,
174 O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
175
176 int fd3;
177 char *file3;
178
179 asprintf(&file3, "%s/..namedfork/rsrc", file2);
180 assert_with_errno((fd3 = open(file3,
181 O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
182
183 assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
184
185 assert_no_err(unlink(file1));
186 assert_no_err(close(fd));
187
188 assert_no_err(chflags(file2, 0));
189
190 assert_with_errno((fd = open(file3,
191 O_RDWR)) >= 0);
192
193 check_io(pwrite(fd, "append", 6, sb.st_size), 6);
194
195 assert_no_err(unlink(file2));
196
197 free(file1);
198 free(file2);
199 free(file3);
200
201 return 0;
202 }
203
204 #endif