]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-move-data-extents.c
hfs-407.1.3.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 if (fcntl(fd, F_LOG2PHYS_EXT, &l2p)) {
64 if (errno == ERANGE)
65 break;
66 assert_fail("fcntl failed: %s", strerror(errno));
67 }
68
69 fstore.fst_posmode = F_VOLPOSMODE;
70 fstore.fst_offset = l2p.l2p_devoffset + 1 + sfs.f_bsize;
71 }
72
73 len += blocks_per_extent * sfs.f_bsize;
74
75 fstore.fst_length = len;
76
77 assert_no_err(fcntl(fd, F_PREALLOCATE, &fstore));
78 }
79
80 assert_no_err(ftruncate(fd, len));
81
82 return fd;
83 }
84
85 int run_move_data_extents(__unused test_ctx_t *ctx)
86 {
87 di = disk_image_get();
88
89 int frag_fd = make_frag_file(1);
90 int fd;
91 int fd2;
92
93 struct stat sb;
94 fstat(frag_fd, &sb);
95
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(frag_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 close(frag_fd);
134
135 /*
136 * Make sure that the extents from move_data_extents.1 got deleted
137 * properly. To test this, we do another F_MOVEDATAEXTENTS and
138 * this time if it's broken it will move the bad extents.
139 */
140
141 fd = make_frag_file(2);
142
143 assert_with_errno((fd2 = open(file2,
144 O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
145
146 assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
147
148 close(fd);
149 close(fd2);
150
151 // And this time it should fail if there's a bug
152 fd = make_frag_file(1);
153
154 assert_with_errno((fd2 = open(file2,
155 O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
156
157 assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
158
159 assert_no_err(unlink(file1));
160 assert_no_err(unlink(file2));
161 assert_no_err(close(fd));
162 assert_no_err(close(fd2));
163
164 assert_no_err(copyfile("/bin/bash", file1, NULL, COPYFILE_ALL));
165
166 assert_no_err(chmod(file1, 0777));
167
168 assert_no_err(stat(file1, &sb));
169
170 assert(sb.st_flags & UF_COMPRESSED);
171
172 assert_with_errno((fd = open(file1, O_RDONLY | O_EVTONLY)) >= 0);
173
174 assert_with_errno((fd2 = open(file2,
175 O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
176
177 int fd3;
178 char *file3;
179
180 asprintf(&file3, "%s/..namedfork/rsrc", file2);
181 assert_with_errno((fd3 = open(file3,
182 O_RDWR | O_TRUNC | O_CREAT, 0666)) >= 0);
183
184 assert_no_err(fcntl(fd, F_MOVEDATAEXTENTS, fd2));
185
186 assert_no_err(unlink(file1));
187 assert_no_err(close(fd));
188
189 assert_no_err(chflags(file2, 0));
190
191 assert_with_errno((fd = open(file3,
192 O_RDWR)) >= 0);
193
194 check_io(pwrite(fd, "append", 6, sb.st_size), 6);
195
196 assert_no_err(unlink(file2));
197
198 assert_no_err (close(fd));
199 assert_no_err (close(fd2));
200 assert_no_err (close(fd3));
201
202 free(file1);
203 free(file2);
204 free(file3);
205
206 return 0;
207 }
208
209 #endif