]>
Commit | Line | Data |
---|---|---|
558d2836 A |
1 | #include <unistd.h> |
2 | #include <sys/fcntl.h> | |
3 | #include <errno.h> | |
4 | #include <sys/stat.h> | |
5 | #include <stdio.h> | |
6 | #include <pthread.h> | |
7 | #include <sys/mount.h> | |
8 | #include <System/sys/fsgetpath.h> | |
9 | #include <TargetConditionals.h> | |
10 | #include <spawn.h> | |
11 | #import <Foundation/Foundation.h> | |
12 | #include <sys/socket.h> | |
13 | #include <sys/un.h> | |
14 | ||
15 | #include "hfs-tests.h" | |
16 | #include "test-utils.h" | |
17 | #include "disk-image.h" | |
18 | #include "systemx.h" | |
19 | ||
20 | TEST(hard_links) | |
21 | ||
22 | static disk_image_t *di; | |
23 | ||
24 | char *dir, *file, *lnk, *moved, *file2, *dir1, *dir2; | |
25 | #define DIR "/tmp/hard-links" | |
26 | #define FILE "/tmp/hard-links/file" | |
27 | #define LINK "/tmp/hard-links.link" | |
28 | #define MOVED "/tmp/hardlinks.moved" | |
29 | #define FILE2 "/tmp/hard-links.file2" | |
30 | #define DIR1 "/tmp/hard-links.dir1" | |
31 | #define DIR2 "/tmp/hard-links/dir2" | |
32 | #define DISK_IMAGE "/tmp/hard-links.sparseimage" | |
33 | ||
34 | void *do_link(__unused void *arg) | |
35 | { | |
36 | assert_no_err(link(moved, file)); | |
37 | return NULL; | |
38 | } | |
39 | ||
40 | static void run(void) | |
41 | { | |
42 | char *tstdir; | |
43 | di = disk_image_get(); | |
44 | ||
45 | asprintf(&tstdir, "%s/tmp", di->mount_point); | |
46 | assert(!mkdir(tstdir, 0777) || errno == EEXIST); | |
47 | asprintf(&dir, "%s/hard-links", tstdir); | |
48 | asprintf(&file, "%s/file", dir); | |
49 | asprintf(&lnk, "%s/hard-links.link", tstdir); | |
50 | asprintf(&moved, "%s/harlinks.moved", tstdir); | |
51 | asprintf(&file2, "%s/hard-links.file2", tstdir); | |
52 | asprintf(&dir1, "%s/hard-links.dir1", tstdir); | |
53 | asprintf(&dir2, "%s/dir2", dir); | |
54 | ||
55 | unlink(lnk); | |
56 | unlink(file); | |
57 | unlink(file2); | |
58 | unlink(moved); | |
59 | rmdir(dir2); | |
60 | rmdir(dir); | |
61 | rmdir(dir1); | |
62 | ||
63 | /* | |
64 | * The sequence that follows used to cause a loop in the kernel on | |
65 | * iOS. For some reason, it doesn't on OS X. | |
66 | */ | |
67 | assert(!mkdir(dir, 0777)); | |
68 | int fd = open(file, O_CREAT | O_RDWR, 0666); | |
69 | assert_with_errno(fd >= 0); | |
70 | assert_no_err(close(fd)); | |
71 | assert_no_err(link(file, lnk)); | |
72 | struct stat sb; | |
73 | assert_no_err(stat(lnk, &sb)); | |
74 | assert_no_err(stat(file, &sb)); | |
75 | assert_no_err(rename(file, moved)); | |
76 | assert_no_err(rmdir(dir)); | |
77 | assert_no_err(mkdir(dir, 0777)); | |
78 | ||
79 | pthread_t thread; | |
80 | pthread_create(&thread, NULL, do_link, NULL); | |
81 | pthread_join(thread, NULL); | |
82 | ||
83 | fd = open(file, O_CREAT | O_RDWR, 0666); | |
84 | assert_with_errno(fd >= 0); | |
85 | assert_no_err(close(fd)); | |
86 | ||
87 | assert_no_err(unlink(lnk)); | |
88 | assert_no_err(unlink(file)); | |
89 | assert_no_err(rmdir(dir)); | |
90 | assert_no_err(unlink(moved)); | |
91 | ||
92 | // This is another sequence that causes problems... | |
93 | ||
94 | fd = open(file2, O_RDWR | O_CREAT, 0666); | |
95 | assert_with_errno(fd >= 0); | |
96 | assert(!fstat(fd, &sb)); | |
97 | struct statfs sfs; | |
98 | assert_no_err(fstatfs(fd, &sfs)); | |
99 | assert_no_err(close(fd)); | |
100 | assert_no_err(mkdir(dir, 0777)); | |
101 | assert_no_err(link(file2, file)); | |
102 | assert_no_err(unlink(file)); | |
103 | assert_no_err(rmdir(dir)); | |
104 | ||
105 | fsobj_id_t fsobj = { | |
106 | .fid_objno = (uint32_t)sb.st_ino, | |
107 | }; | |
108 | ||
109 | fd = openbyid_np(&sfs.f_fsid, &fsobj, O_RDWR); | |
110 | ||
111 | assert_with_errno(fd >= 0); | |
112 | ||
113 | assert_no_err(unlink(file2)); | |
114 | ||
115 | #if !TARGET_OS_EMBEDDED | |
116 | // Same as the last sequence but with directory hard links | |
117 | ||
118 | assert_no_err(mkdir(dir1, 0777)); | |
119 | assert(!stat(dir1, &sb)); | |
120 | assert_no_err(mkdir(dir, 0777)); | |
121 | ||
122 | //printf("dir1: %s, dir2: %s\n", dir1, dir2); | |
123 | assert_no_err(link(dir1, dir2)); | |
124 | assert_no_err(rmdir(dir2)); | |
125 | assert_no_err(rmdir(dir)); | |
126 | ||
127 | fsobj.fid_objno = (uint32_t)sb.st_ino; | |
128 | ||
129 | fd = openbyid_np(&sfs.f_fsid, &fsobj, O_RDONLY); | |
130 | ||
131 | assert_with_errno(fd >= 0); | |
132 | ||
133 | assert_no_err(rmdir(dir1)); | |
134 | ||
135 | // Test for leaked iocounts | |
136 | ||
137 | disk_image_t *di2 = disk_image_create(DISK_IMAGE, &(disk_image_opts_t){ | |
138 | .size = 100 * 1024 * 1024 | |
139 | }); | |
140 | ||
141 | char *path1, *path2; | |
142 | ||
143 | asprintf(&path1, "%s/dir1", di2->mount_point); | |
144 | assert_no_err(mkdir(path1, 0777)); | |
145 | free(path1); | |
146 | asprintf(&path2, "%s/dir2", di2->mount_point); | |
147 | assert_no_err(mkdir(path2, 0777)); | |
148 | free(path2); | |
149 | ||
150 | asprintf(&path1, "%s/dir1/hard-links-test-file", di2->mount_point); | |
151 | asprintf(&path2, "%s/dir2/link", di2->mount_point); | |
152 | ||
153 | dispatch_group_t grp = dispatch_group_create(); | |
154 | dispatch_queue_t q = dispatch_get_global_queue(0, 0); | |
155 | ||
156 | //printf("path1: %s, path2: %s\n", path1, path2); | |
157 | for (int i = 0; i < 10000; ++i) { | |
158 | fd = open(path1, O_CREAT | O_RDWR, 0666); | |
159 | assert_with_errno(fd >= 0); | |
160 | assert_no_err(link(path1, path2)); | |
161 | assert_no_err(fstat(fd, &sb)); | |
162 | assert_no_err(fstatfs(fd, &sfs)); | |
163 | assert_no_err(close(fd)); | |
164 | ||
165 | dispatch_group_async(grp, q, ^{ | |
166 | char path[PATH_MAX]; | |
167 | ssize_t res; | |
168 | do { | |
169 | res = fsgetpath(path, sizeof(path), &sfs.f_fsid, (uint64_t)sb.st_ino); | |
7adaf79d | 170 | } while (res != -1); |
558d2836 A |
171 | assert_with_errno(errno == ENOENT); |
172 | }); | |
173 | ||
174 | dispatch_group_async(grp, q, ^{ | |
175 | assert_no_err(unlink(path1)); | |
176 | }); | |
177 | ||
178 | dispatch_group_async(grp, q, ^{ | |
179 | assert_no_err(unlink(path2)); | |
180 | }); | |
181 | ||
182 | dispatch_group_wait(grp, DISPATCH_TIME_FOREVER); | |
183 | } | |
184 | ||
185 | // Test hard links to sockets and fifos | |
186 | ||
187 | assert_with_errno((fd = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0); | |
188 | ||
189 | struct sockaddr_un addr = { | |
190 | .sun_family = AF_UNIX | |
191 | }; | |
192 | ||
193 | sprintf(addr.sun_path, "%s/socket", di2->mount_point); | |
194 | ||
195 | assert_no_err(bind(fd, (struct sockaddr *)&addr, sizeof(addr))); | |
196 | ||
197 | asprintf(&path1, "%s/socket-link", di2->mount_point); | |
198 | assert_no_err(link(addr.sun_path, path1)); | |
199 | assert_no_err(unlink(path1)); | |
200 | ||
201 | // And a fifo | |
202 | char *fifo_path; | |
203 | asprintf(&fifo_path, "%s/fifo", di2->mount_point); | |
204 | asprintf(&path2, "%s/fifo-link", di2->mount_point); | |
205 | ||
206 | assert_no_err(mkfifo(fifo_path, 0666)); | |
207 | assert_no_err(link(fifo_path, path2)); | |
208 | assert_no_err(unlink(path2)); | |
209 | ||
210 | char *mut_vol_device = strdup(di2->disk); | |
211 | assert(!systemx("/sbin/fsck_hfs", SYSTEMX_QUIET, "-ld", mut_vol_device, NULL)); | |
212 | ||
213 | assert_no_err(close(fd)); | |
214 | ||
215 | // Unmount | |
216 | assert(!systemx("/usr/sbin/diskutil", SYSTEMX_QUIET, "unmount", mut_vol_device, NULL)); | |
217 | ||
218 | // Remount | |
219 | assert(!systemx("/usr/sbin/diskutil", SYSTEMX_QUIET, "mount", mut_vol_device, NULL)); | |
220 | ||
221 | // We assume the same mount_point | |
222 | assert_no_err(unlink(addr.sun_path)); | |
223 | assert_no_err(unlink(fifo_path)); | |
224 | ||
225 | assert(!systemx("/sbin/fsck_hfs", SYSTEMX_QUIET, "-ld", mut_vol_device, NULL)); | |
226 | ||
227 | #endif | |
228 | ||
229 | return; | |
230 | } | |
231 | ||
232 | int run_hard_links(__unused test_ctx_t *ctx) | |
233 | { | |
234 | run(); | |
235 | ||
236 | return 0; | |
237 | } |