8 #include <System/sys/fsgetpath.h>
9 #include <TargetConditionals.h>
11 #import <Foundation/Foundation.h>
12 #include <sys/socket.h>
15 #include "hfs-tests.h"
16 #include "test-utils.h"
17 #include "disk-image.h"
22 static disk_image_t *di;
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"
34 void *do_link(__unused void *arg)
36 assert_no_err(link(moved, file));
43 di = disk_image_get();
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);
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.
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));
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));
80 pthread_create(&thread, NULL, do_link, NULL);
81 pthread_join(thread, NULL);
83 fd = open(file, O_CREAT | O_RDWR, 0666);
84 assert_with_errno(fd >= 0);
85 assert_no_err(close(fd));
87 assert_no_err(unlink(lnk));
88 assert_no_err(unlink(file));
89 assert_no_err(rmdir(dir));
90 assert_no_err(unlink(moved));
92 // This is another sequence that causes problems...
94 fd = open(file2, O_RDWR | O_CREAT, 0666);
95 assert_with_errno(fd >= 0);
96 assert(!fstat(fd, &sb));
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));
106 .fid_objno = (uint32_t)sb.st_ino,
109 fd = openbyid_np(&sfs.f_fsid, &fsobj, O_RDWR);
111 assert_with_errno(fd >= 0);
113 assert_no_err(unlink(file2));
115 #if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
116 // Same as the last sequence but with directory hard links
118 assert_no_err(mkdir(dir1, 0777));
119 assert(!stat(dir1, &sb));
120 assert_no_err(mkdir(dir, 0777));
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));
127 fsobj.fid_objno = (uint32_t)sb.st_ino;
129 fd = openbyid_np(&sfs.f_fsid, &fsobj, O_RDONLY);
131 assert_with_errno(fd >= 0);
133 assert_no_err(rmdir(dir1));
135 // Test for leaked iocounts
137 disk_image_t *di2 = disk_image_create(DISK_IMAGE, &(disk_image_opts_t){
138 .size = 100 * 1024 * 1024
143 asprintf(&path1, "%s/dir1", di2->mount_point);
144 assert_no_err(mkdir(path1, 0777));
146 asprintf(&path2, "%s/dir2", di2->mount_point);
147 assert_no_err(mkdir(path2, 0777));
150 asprintf(&path1, "%s/dir1/hard-links-test-file", di2->mount_point);
151 asprintf(&path2, "%s/dir2/link", di2->mount_point);
153 dispatch_group_t grp = dispatch_group_create();
154 dispatch_queue_t q = dispatch_get_global_queue(0, 0);
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));
165 dispatch_group_async(grp, q, ^{
169 res = fsgetpath(path, sizeof(path), &sfs.f_fsid, (uint64_t)sb.st_ino);
171 assert_with_errno(errno == ENOENT);
174 dispatch_group_async(grp, q, ^{
175 assert_no_err(unlink(path1));
178 dispatch_group_async(grp, q, ^{
179 assert_no_err(unlink(path2));
182 dispatch_group_wait(grp, DISPATCH_TIME_FOREVER);
185 // Test hard links to sockets and fifos
187 assert_with_errno((fd = socket(PF_UNIX, SOCK_STREAM, 0)) >= 0);
189 struct sockaddr_un addr = {
190 .sun_family = AF_UNIX
193 sprintf(addr.sun_path, "%s/socket", di2->mount_point);
195 assert_no_err(bind(fd, (struct sockaddr *)&addr, sizeof(addr)));
197 asprintf(&path1, "%s/socket-link", di2->mount_point);
198 assert_no_err(link(addr.sun_path, path1));
199 assert_no_err(unlink(path1));
203 asprintf(&fifo_path, "%s/fifo", di2->mount_point);
204 asprintf(&path2, "%s/fifo-link", di2->mount_point);
206 assert_no_err(mkfifo(fifo_path, 0666));
207 assert_no_err(link(fifo_path, path2));
208 assert_no_err(unlink(path2));
210 char *mut_vol_device = strdup(di2->disk);
211 assert(!systemx("/sbin/fsck_hfs", SYSTEMX_QUIET, "-ld", mut_vol_device, NULL));
213 assert_no_err(close(fd));
216 assert(!systemx("/usr/sbin/diskutil", SYSTEMX_QUIET, "unmount", mut_vol_device, NULL));
219 assert(!systemx("/usr/sbin/diskutil", SYSTEMX_QUIET, "mount", mut_vol_device, NULL));
221 // We assume the same mount_point
222 assert_no_err(unlink(addr.sun_path));
223 assert_no_err(unlink(fifo_path));
225 assert(!systemx("/sbin/fsck_hfs", SYSTEMX_QUIET, "-ld", mut_vol_device, NULL));
227 #endif //!(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR)
232 int run_hard_links(__unused test_ctx_t *ctx)