]>
Commit | Line | Data |
---|---|---|
558d2836 A |
1 | #include <TargetConditionals.h> |
2 | ||
3 | #if !TARGET_OS_EMBEDDED | |
4 | ||
5 | #include <sys/mman.h> | |
6 | #include <unistd.h> | |
7 | #include <spawn.h> | |
8 | #include <fcntl.h> | |
9 | #include <sys/stat.h> | |
10 | #include <sys/param.h> | |
11 | #include <sys/mount.h> | |
12 | ||
13 | #include <Foundation/Foundation.h> | |
14 | ||
15 | #include "hfs-tests.h" | |
16 | #include "test-utils.h" | |
17 | #include "systemx.h" | |
18 | #include "disk-image.h" | |
19 | ||
20 | TEST(map_private) | |
21 | ||
22 | #define DISK_IMAGE "/tmp/map-private.sparseimage" | |
23 | ||
24 | static char zero[65536]; | |
25 | ||
26 | static jmp_buf jmp_env; | |
27 | ||
28 | static void handle_sigbus(int signal) | |
29 | { | |
30 | assert(signal == SIGBUS); | |
31 | ||
32 | longjmp(jmp_env, 1); | |
33 | } | |
34 | ||
35 | int run_map_private(__unused test_ctx_t *ctx) | |
36 | { | |
37 | disk_image_t *di = disk_image_create(DISK_IMAGE, &(disk_image_opts_t){ | |
38 | .size = 64 * 1024 * 1024 | |
39 | }); | |
40 | ||
41 | char *path; | |
42 | asprintf(&path, "%s/map-private.data", di->mount_point); | |
43 | ||
44 | int fd; | |
45 | void *p; | |
46 | ||
47 | assert_with_errno((fd = open(path, O_RDWR | O_CREAT, 0666)) >= 0); | |
48 | ||
49 | assert_no_err(ftruncate(fd, 65536)); | |
50 | ||
51 | assert_no_err(fcntl(fd, F_FULLFSYNC)); | |
52 | ||
53 | assert_with_errno((p = mmap(NULL, 65536, PROT_READ | PROT_WRITE, | |
54 | MAP_PRIVATE, fd, 0)) != MAP_FAILED); | |
55 | ||
56 | assert_no_err(close(fd)); | |
57 | ||
58 | assert_no_err(unlink(path)); | |
59 | ||
60 | // Create a second file that we keep open via a file descriptor | |
61 | char *path2; | |
62 | asprintf(&path2, "%s/map-private-2.data", di->mount_point); | |
63 | ||
64 | assert_with_errno((fd = open(path2, O_RDWR | O_CREAT, 0666)) >= 0); | |
65 | ||
66 | assert_no_err(ftruncate(fd, 65536)); | |
67 | ||
68 | assert_no_err(fcntl(fd, F_FULLFSYNC)); | |
69 | ||
70 | assert_no_err(unlink(path2)); | |
71 | ||
72 | assert(!systemx("/usr/sbin/diskutil", SYSTEMX_QUIET, "unmount", "force", di->mount_point, NULL)); | |
73 | ||
74 | /* | |
75 | * Forcibly unmounting should not have caused all the data | |
76 | * to be paged in so this should result in a fault. | |
77 | */ | |
78 | ||
79 | // Set things up to catch the SIGBUS | |
80 | sigset_t set; | |
81 | sigemptyset(&set); | |
82 | sigaddset(&set, SIGBUS); | |
83 | ||
84 | sigprocmask(SIG_BLOCK, &set, NULL); | |
85 | ||
86 | struct sigaction sa = { | |
87 | .sa_handler = handle_sigbus, | |
88 | .sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO, | |
89 | }; | |
90 | ||
91 | sigaction(SIGBUS, &sa, NULL); | |
92 | pthread_sigmask(SIG_UNBLOCK, &set, NULL); | |
93 | ||
94 | if (!setjmp(jmp_env)) { | |
95 | if(memcmp(p, zero, 65536)) // Need this if statement so the memcmp isn't optimized out | |
96 | assert_fail("memcmp should have faulted"); | |
97 | else | |
98 | assert_fail("... memcmp should have faulted"); | |
99 | } | |
100 | ||
101 | // Now remount the volume and make sure file has been deleted | |
102 | assert(!systemx("/usr/sbin/diskutil", SYSTEMX_QUIET, "mount", di->disk, NULL)); | |
103 | ||
104 | /* | |
105 | * We assume that it will get mounted at the same place, which | |
106 | * is reasonable given the environment this should be running | |
107 | * in. | |
108 | */ | |
109 | struct stat sb; | |
110 | assert(stat(path, &sb) == -1 && errno == ENOENT); | |
111 | ||
112 | // Now check that common open unlink behaviour isn't broken | |
113 | ||
114 | // First check disk space | |
115 | struct statfs sfs; | |
116 | assert_no_err(statfs(di->mount_point, &sfs)); | |
117 | ||
118 | // Should be at least 7 MB | |
119 | uint64_t space = sfs.f_bfree * sfs.f_bsize; | |
120 | ||
121 | #define MB * 1024 * 1024 | |
122 | ||
123 | assert(space > 7 MB); | |
124 | ||
125 | assert_with_errno((fd = open(path, O_RDWR | O_CREAT, 0666)) >= 0); | |
126 | ||
127 | assert_no_err(ftruncate(fd, 7 MB)); | |
128 | ||
129 | assert_no_err(statfs(di->mount_point, &sfs)); | |
130 | ||
131 | // Space should have dropped by at least 5 MB | |
132 | assert(sfs.f_bfree * sfs.f_bsize < space - 5 MB); | |
133 | ||
134 | assert_no_err(unlink(path)); | |
135 | ||
136 | // Map the file | |
137 | assert_with_errno((p = mmap(NULL, 65536, PROT_READ | PROT_WRITE, | |
138 | MAP_PRIVATE, fd, 0)) != MAP_FAILED); | |
139 | ||
140 | assert_no_err(close(fd)); | |
141 | ||
142 | // File is still in use, so space should not have changed | |
143 | assert_no_err(statfs(di->mount_point, &sfs)); | |
144 | ||
145 | assert(sfs.f_bfree * sfs.f_bsize < space - 5 MB); | |
146 | ||
147 | // Get rid of the last reference | |
148 | assert_no_err(munmap(p, 65536)); | |
149 | ||
150 | // Just in case we collide with sync | |
151 | sleep(1); | |
152 | ||
153 | // Space should be back up to at least 7 MB free | |
154 | assert_no_err(statfs(di->mount_point, &sfs)); | |
155 | ||
156 | assert(sfs.f_bfree * sfs.f_bsize > 7 MB); | |
157 | ||
158 | return 0; | |
159 | } | |
160 | ||
161 | #endif |