]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-msync-16k.c
hfs-366.70.1.tar.gz
[apple/hfs.git] / tests / cases / test-msync-16k.c
1 /*
2 * For some background: this unit test was created for a bug where
3 * during page-out, we captured the file size without any locks and
4 * then if a change happened to come in before we took the locks, we
5 * would lose the remainder of the page. The following illustrates
6 * the scenario:
7 *
8 * <- 1 PG ->
9 * a b c
10 * +-------------+--------+----
11 * | |XXXX |
12 * +-------------+--------+----
13 * ^
14 * |
15 * EOF
16 *
17 * A page-out is requested for the page starting at @a and ending at
18 * @c. The end of the file is at @b and is captured before any locks
19 * are taken. After that, another thread writes something between @b
20 * and @c. The page-out then continues, but it thinks the end of the
21 * file is at @b, so it will only write up to @b and then it will mark
22 * the entire page as clean. That will cause us to lose the data that
23 * was written between @b and @c. This would only occur if there is
24 * an extent boundary at @b (which is the reason for the F_PREALLOCATE
25 * calls below). The syncer thread below is simulating what we do in
26 * page-out: we write 1 MB chunks.
27 */
28
29 #include <fcntl.h>
30 #include <assert.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <pthread.h>
35 #include <sys/mman.h>
36 #include <sys/errno.h>
37 #include <sys/stat.h>
38 #include <stdio.h>
39 #include <sched.h>
40
41 #include "hfs-tests.h"
42 #include "test-utils.h"
43 #include "disk-image.h"
44
45 TEST(msync_16k)
46
47 static disk_image_t *di;
48
49 volatile off_t size = 0;
50
51 static int fd;
52
53 void syncer(void)
54 {
55 for (;;) {
56 off_t sz = size;
57
58 if (sz == -1)
59 break;
60
61 sz &= ~0xfffff;
62
63 void *p = mmap(NULL, 0x100000, PROT_READ | PROT_WRITE, MAP_SHARED,
64 fd, sz);
65
66 assert_with_errno(p != MAP_FAILED);
67
68 while ((size & ~0xfffff) == sz) {
69 assert_no_err(msync(p, 0x100000, MS_SYNC));
70 sched_yield();
71 }
72
73 assert_no_err(munmap(p, 0x100000));
74 }
75 }
76
77 int run_msync_16k(__unused test_ctx_t *ctx)
78 {
79 di = disk_image_get();
80
81 char *file;
82 asprintf(&file, "%s/msync-16k.data", di->mount_point);
83
84 char *buf = malloc(1024 * 1024);
85 memset(buf, 0xaf, 1024 * 1024);
86
87 unlink(file);
88
89 fd = open(file, O_CREAT | O_RDWR, 0666);
90 assert_with_errno(fd >= 0);
91
92 pthread_t thr;
93 pthread_create(&thr, NULL, (void *(*)(void *))syncer, NULL);
94
95 assert_no_err(fcntl(fd, F_NOCACHE, 1));
96
97 check_io(write(fd, buf, 8192), 8192);
98
99 size = 8192;
100 while (size < 100 * 1024 * 1024ll) {
101 // Force an extent boundary
102 struct log2phys l2p = {
103 .l2p_contigbytes = 4096,
104 .l2p_devoffset = size - 4096,
105 };
106 assert(!fcntl(fd, F_LOG2PHYS_EXT, &l2p));
107
108 struct fstore fst = {
109 .fst_posmode = F_VOLPOSMODE,
110 .fst_offset = l2p.l2p_devoffset + 4096,
111 .fst_length = size + 16384,
112 };
113 assert(!fcntl(fd, F_PREALLOCATE, &fst));
114
115 check_io(pwrite(fd, buf, 16384, size), 16384);
116 size += 16384;
117 }
118
119 size_t sz = size;
120 size = -1;
121
122 pthread_join(thr, NULL);
123 assert_no_err(close(fd));
124
125 fd = open(file, O_RDWR);
126
127 assert_with_errno(fd >= 0);
128 size_t done = 0;
129 char *cmp_buf = malloc(1024 * 1024);
130
131 while (done < sz) {
132 void *p = mmap(NULL, 1024 * 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, done);
133 assert_with_errno(p != MAP_FAILED);
134 assert_no_err(msync(p, 1024 * 1024, MS_INVALIDATE | MS_SYNC));
135 assert_no_err(munmap(p, 1024 * 1024));
136
137 bzero(cmp_buf, 1024 * 1024);
138 ssize_t amt = read(fd, cmp_buf, 1024 * 1024);
139 assert(amt > 0);
140 assert(!memcmp(cmp_buf, buf, amt));
141 done += amt;
142 }
143
144 assert_no_err(close(fd));
145
146 assert_no_err(unlink(file));
147
148 free(file);
149
150 return 0;
151 }