]> git.saurik.com Git - apple/hfs.git/blob - tests/cases/test-hard-links.m
hfs-366.50.19.tar.gz
[apple/hfs.git] / tests / cases / test-hard-links.m
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);
170 } while (res != -1);
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 }