]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-invalid-ranges.m
hfs-522.0.9.tar.gz
[apple/hfs.git] / tests / cases / test-invalid-ranges.m
1 #include <sys/mman.h>
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <sys/param.h>
5 #include <spawn.h>
6 #include <signal.h>
7 #include <sys/stat.h>
8 #include <TargetConditionals.h>
9
10 #import <Foundation/Foundation.h>
11
12 #include "hfs-tests.h"
13 #include "test-utils.h"
14 #include "disk-image.h"
15 #include "systemx.h"
16
17 TEST(invalid_ranges)
18
19 static disk_image_t *di;
20
21 #define DISK_IMAGE "/tmp/invalid-ranges.sparseimage"
22
23 int run_invalid_ranges(__unused test_ctx_t *ctx)
24 {
25 di = disk_image_get();
26
27 char *file;
28 asprintf(&file, "%s/invalid-ranges.data", di->mount_point);
29
30 unlink(file);
31
32 int fd = open(file, O_CREAT | O_RDWR, 0666);
33
34 assert_with_errno(fd >= 0);
35
36 off_t size = 1000000;
37
38 // Make a big file and punch holes in it
39 ftruncate(fd, size);
40
41 void *buf = valloc(65536);
42
43 memset(buf, 0xaf, 65536);
44
45 assert_no_err(fcntl(fd, F_NOCACHE, 1));
46
47 off_t o;
48 for (o = 0; o < size; o += 131072)
49 check_io(pwrite(fd, buf, 65536, o + 65536), 65536);
50
51 // This should cause everything to be flushed
52 assert_no_err(close(fd));
53
54 assert_with_errno((fd = open(file, O_RDWR)) >= 0);
55
56 uint8_t *p;
57 assert_with_errno((p = mmap(NULL, o, PROT_READ | PROT_WRITE, MAP_SHARED,
58 fd, 0)) != MAP_FAILED);
59
60 assert_no_err(msync(p, o, MS_INVALIDATE));
61
62 void *zero = malloc(65536);
63 bzero(zero, 65536);
64
65 off_t n;
66 uint8_t *q;
67
68 for (n = 0, q = p; n < o; n += 131072, q += 131072) {
69 assert(!memcmp(q, zero, 65536));
70 assert(!memcmp(q + 65536, buf, 65536));
71 }
72
73 assert(p[size] == 0xaf);
74
75 assert_no_err(ftruncate(fd, size));
76
77 // Check the tail portion of the page is zero
78 assert(p[size] == 0);
79
80 p[size] = 0xbe;
81
82 msync(p + size - 1, 1, MS_SYNC);
83
84 int ps = getpagesize();
85 int ps_mask = ps - 1;
86
87 // Force the page out
88 assert_no_err(msync((void *)((uintptr_t)(p + size - 1) & ~ps_mask),
89 ps, MS_INVALIDATE));
90
91 // Page back in and check it's zeroed
92 assert(p[size] == 0);
93
94 p[size] = 0x75;
95
96 // Extend the file to include the change we made above
97 assert_no_err(ftruncate(fd, size + 1));
98
99 // That change should have been zeroed out
100 assert(p[size] == 0);
101
102 assert_no_err(munmap(p, o));
103
104 // Extend the file
105 assert_no_err(ftruncate(fd, o + 2 * ps));
106
107 // Write something into the middle of the page following o
108 off_t hello_offset = roundup(o, ps) + 100;
109
110 check_io(pwrite(fd, "hello", 5, hello_offset), 5);
111
112 // Close and re-read
113 assert_no_err(close(fd));
114
115 assert_with_errno((fd = open(file, O_RDWR)) >= 0);
116
117 assert_with_errno((p = mmap(NULL, o + 2 * ps, PROT_READ | PROT_WRITE, MAP_SHARED,
118 fd, 0)) != MAP_FAILED);
119
120 assert_no_err(msync(p, o + 2 * ps, MS_INVALIDATE));
121
122 assert(!memcmp(p + hello_offset, "hello", 5));
123 assert(!memcmp(p + size, zero, hello_offset - size));
124 assert(!memcmp(p + hello_offset + 5, zero, o + ps * 2 - hello_offset - 5));
125
126 assert_no_err(close(fd));
127 assert_no_err(unlink(file));
128
129 // Make a large number of invalid ranges
130 assert_with_errno((fd = open(file,
131 O_RDWR | O_CREAT, 0666)) >= 0);
132 for (int i = 0; i < 1024; ++i) {
133 pwrite(fd, "hello", 5, i * ps * 2);
134 }
135
136 assert_no_err(munmap(p, o + 2 * ps));
137
138 // OK, that should have created 1024 invalid ranges. Sync the data.
139 p = mmap(NULL, 1024 * ps * 2, PROT_READ | PROT_WRITE, MAP_SHARED,
140 fd, 0);
141 assert(p != MAP_FAILED);
142
143 assert_no_err(msync(p, 1024 * ps * 2, MS_SYNC));
144
145 // Now sync the invalid ranges
146 assert_no_err(fcntl(fd, F_FULLFSYNC));
147
148 assert_no_err(close(fd));
149
150 assert_no_err(unlink(file));
151
152 assert_no_err(munmap(p, 1024 * ps * 2));
153
154 #if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
155 disk_image_t *di2 = disk_image_create(DISK_IMAGE, &(disk_image_opts_t){
156 .size = 100 * 1024 * 1024
157 });
158
159 // Find the diskimages_helper process
160 char *dev_device = strdup(di2->disk + 5);
161
162 // Strip off the s bit for the partition
163 char *spos = strrchr(dev_device, 's');
164 assert(spos);
165 *spos = 0;
166
167 io_service_t obj = IOServiceGetMatchingService(kIOMasterPortDefault,
168 IOBSDNameMatching(kIOMasterPortDefault, 0, dev_device));
169
170 assert(obj);
171
172 io_service_t parent;
173
174 // obj should be the IOMedia object. Go up three to the IOHDIXHDDrive object.
175 assert(!IORegistryEntryGetParentEntry(obj, kIOServicePlane, &parent));
176
177 assert(parent);
178
179 IOObjectRelease(obj);
180
181 assert(!IORegistryEntryGetParentEntry(parent, kIOServicePlane, &obj));
182
183 IOObjectRelease(parent);
184
185 assert(!IORegistryEntryGetParentEntry(obj, kIOServicePlane, &parent));
186
187 assert(parent);
188
189 IOObjectRelease(obj);
190
191 NSString *creator = (id)CFBridgingRelease(IORegistryEntrySearchCFProperty(parent, kIOServicePlane,
192 CFSTR("IOUserClientCreator"),
193 kCFAllocatorDefault,
194 kIORegistryIterateRecursively));
195
196 IOObjectRelease(parent);
197
198 assert(creator);
199
200 // Extract the pid of disk_images_helper
201 pid_t disk_images_helper_pid;
202 assert(sscanf([creator UTF8String], "pid %u", &disk_images_helper_pid) == 1);
203
204 // Create a file
205
206 char *path;
207 asprintf(&path, "%s/test-file", di2->mount_point);
208
209 fd = open(path, O_CREAT | O_RDWR, 0666);
210
211 /*
212 * Workaround for <rdar://20688964>: force the journal to
213 * have at least one transaction in it.
214 */
215 assert_no_err(fcntl(fd, F_FULLFSYNC));
216
217 assert(fd >= 0);
218
219 assert_no_err(fcntl(fd, F_NOCACHE, 1));
220
221 int block_size = 65536;
222
223 // Preallocate
224 struct fstore fst = {
225 .fst_posmode = F_PEOFPOSMODE,
226 .fst_length = 2 * block_size,
227 };
228
229 assert_no_err(fcntl(fd, F_PREALLOCATE, &fst));
230
231 assert(fst.fst_bytesalloc >= 2 * block_size);
232
233 // Figure out where that is on the device
234 struct log2phys l2p = { .l2p_contigbytes = block_size, .l2p_devoffset = block_size };
235 assert_no_err(fcntl(fd, F_LOG2PHYS_EXT, &l2p));
236
237 assert(l2p.l2p_contigbytes > 0);
238
239 // Now open the raw device and write some garbage to that location
240
241 assert(!strncmp(di2->disk, "/dev/", 5));
242
243 char *raw_path;
244 asprintf(&raw_path, "/dev/r%s", di2->disk + 5);
245
246 int raw_dev = open(raw_path, O_RDWR);
247
248 assert_with_errno(raw_dev >= 0);
249
250 memset(buf, 0x57, block_size);
251
252 check_io(pwrite(raw_dev, buf, l2p.l2p_contigbytes, l2p.l2p_devoffset),
253 l2p.l2p_contigbytes);
254
255 assert_no_err(close(raw_dev));
256
257 // OK, so now we have some garbage where we want it.
258
259 // Check fcntl F_LOG2PHYS_EXT is doing what we expect
260 off_t file_offset = block_size;
261 do {
262 assert(l2p.l2p_contigbytes > 0);
263 assert(l2p.l2p_contigbytes < 1024 * 1024);
264 file_offset += l2p.l2p_contigbytes;
265 assert(file_offset < 1024 * 1024);
266 l2p.l2p_devoffset = file_offset;
267 l2p.l2p_contigbytes = INT64_MAX;
268 } while (!fcntl(fd, F_LOG2PHYS_EXT, &l2p));
269
270 assert_with_errno(errno == ERANGE);
271
272 // Do some writing to the file normally
273
274 memset(buf, 0xaa, block_size);
275
276 check_io(pwrite(fd, buf, block_size, 0), block_size);
277
278 check_io(pwrite(fd, buf, block_size, block_size * 2), block_size);
279
280 // So now we have a hole that should be zeroed at <block_size, block_size>
281
282 // Touch every page in that hole
283 for (int i = 0; i < block_size / 4096; ++i)
284 check_io(pwrite(fd, "hello", 5, block_size + i * 4096 + 1000), 5);
285
286 // Check what we have in the cache
287 check_io(pread(fd, buf, ps, block_size), ps);
288
289 assert(!memcmp(buf, zero, 1000));
290 assert(!memcmp(buf + 1000, "hello", 5));
291 assert(!memcmp(buf + 1005, zero, ps - 1005));
292
293 /* Write something into the block beyond the hole. This should
294 cause a metadata update. */
295 check_io(pwrite(fd, "hello", 5, block_size * 3), 5);
296
297 /* Create another file so we can do a full fsync to
298 force a journal flush. */
299 char *fsync_path;
300 asprintf(&fsync_path, "%s/fsync", di->mount_point);
301
302 assert_no_err(close(fd));
303 fd = open(fsync_path, O_CREAT | O_RDWR | O_TRUNC, 0666);
304
305 assert_with_errno(fd >= 0);
306
307 assert_no_err(fcntl(fd, F_FULLFSYNC));
308
309 // Kill disk_images_helper to simulate a crash
310 assert_no_err(kill(disk_images_helper_pid, SIGKILL));
311
312 // Wait until it gets unmounted
313 struct stat sb;
314 while (!stat(raw_path, &sb))
315 sleep(1);
316
317 // Wait another second for things to settle down
318 sleep(1);
319
320 // Attach to the disk image again
321 assert(!systemx("/usr/bin/hdiutil", SYSTEMX_QUIET, "attach", DISK_IMAGE, NULL));
322
323 assert_no_err(close(fd));
324 assert_with_errno((fd = open(path, O_RDWR)) >= 0);
325
326 // Either the file should be short, or there should be zeroes
327 ssize_t amount = pread(fd, buf, block_size, block_size);
328 assert_with_errno(amount >= 0);
329
330 assert(!memcmp(buf, zero, (amount > 1000) ? 1000 : amount));
331
332 assert_no_err(close(fd));
333
334 #endif //!(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
335
336 // Test for <rdar://20994239>
337 fd = open(file, O_CREAT | O_RDWR, 0666);
338 assert_with_errno(fd >= 0);
339 assert_no_err(fcntl(fd, F_NOCACHE, 1));
340
341 void *buf2 = malloc(0x100000);
342 memset(buf2, 0x16, 0x100000);
343 check_io(pwrite(fd, buf2, 0x200, 0), 0x200);
344 check_io(pwrite(fd, buf2, 0x100000, 0x00100200), 0x100000);
345 check_io(pwrite(fd, buf2, 0x100000, 0x00300200), 0x100000);
346 check_io(pwrite(fd, buf2, 0x100000, 0x00500200), 0x100000);
347 check_io(pwrite(fd, buf2, 0x200, 0), 0x200);
348 check_io(pwrite(fd, buf2, 0x100000, 0x00700200), 0x100000);
349 check_io(pwrite(fd, buf2, 0x100000, 0x200), 0x100000);
350
351 void *buf3 = malloc(0x100000);
352 check_io(pread(fd, buf3, 0x100000, 0x100000), 0x100000);
353 assert(!memcmp(buf2, buf3, 0x100000));
354
355 free(buf3);
356 free(buf2);
357 free(buf);
358 free(zero);
359 assert_no_err(close(fd));
360 assert_no_err(unlink(file));
361
362 return 0;
363 }