1 // Copyright 2017 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <assert.h>
6 #include <errno.h>
7 #include <dirent.h>
8 #include <fcntl.h>
9 #include <limits.h>
10 #include <stdbool.h>
11 #include <stdint.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 
18 #include <zircon/compiler.h>
19 
20 #include "filesystems.h"
21 #include "misc.h"
22 
check_link_count(const char * path,unsigned count)23 bool check_link_count(const char* path, unsigned count) {
24     struct stat s;
25     ASSERT_EQ(stat(path, &s), 0, "");
26     ASSERT_EQ(s.st_nlink, count, "");
27     return true;
28 }
29 
test_link_basic(void)30 bool test_link_basic(void) {
31     BEGIN_TEST;
32 
33     if (!test_info->supports_hardlinks) {
34         return true;
35     }
36 
37     const char* oldpath = "::a";
38     const char* newpath = "::b";
39 
40     // Make a file, fill it with content
41     int fd = open(oldpath, O_RDWR | O_CREAT | O_EXCL, 0644);
42     ASSERT_GT(fd, 0, "");
43     uint8_t buf[100];
44     for (size_t i = 0; i < sizeof(buf); i++) {
45         buf[i] = (uint8_t) rand();
46     }
47     ASSERT_STREAM_ALL(write, fd, buf, sizeof(buf));
48     ASSERT_TRUE(check_file_contents(fd, buf, sizeof(buf)), "");
49     ASSERT_TRUE(check_link_count(oldpath, 1), "");
50 
51     ASSERT_EQ(link(oldpath, newpath), 0, "");
52     ASSERT_TRUE(check_link_count(oldpath, 2), "");
53     ASSERT_TRUE(check_link_count(newpath, 2), "");
54 
55     // Confirm that both the old link and the new links exist
56     int fd2 = open(newpath, O_RDONLY, 0644);
57     ASSERT_GT(fd2, 0, "");
58     ASSERT_TRUE(check_file_contents(fd2, buf, sizeof(buf)), "");
59     ASSERT_TRUE(check_file_contents(fd, buf, sizeof(buf)), "");
60 
61     // Remove the old link
62     ASSERT_EQ(close(fd), 0, "");
63     ASSERT_EQ(close(fd2), 0, "");
64     ASSERT_EQ(unlink(oldpath), 0, "");
65     ASSERT_TRUE(check_link_count(newpath, 1), "");
66 
67     // Open the link by its new name, and verify that the contents have
68     // not been altered by the removal of the old link.
69     fd = open(newpath, O_RDONLY, 0644);
70     ASSERT_GT(fd, 0, "");
71     ASSERT_TRUE(check_file_contents(fd, buf, sizeof(buf)), "");
72 
73     ASSERT_EQ(close(fd), 0, "");
74     ASSERT_EQ(unlink(newpath), 0, "");
75 
76     END_TEST;
77 }
78 
test_link_count_dirs(void)79 bool test_link_count_dirs(void) {
80     BEGIN_TEST;
81 
82     if (!test_info->supports_hardlinks) {
83         return true;
84     }
85 
86     ASSERT_EQ(mkdir("::dira", 0755), 0, "");
87     // New directories should have two links:
88     // Parent --> newdir
89     // newdir ('.') --> newdir
90     ASSERT_TRUE(check_link_count("::dira", 2), "");
91 
92     // Adding a file won't change the parent link count...
93     int fd = open("::dira/file", O_RDWR | O_CREAT | O_EXCL, 0644);
94     ASSERT_GT(fd, 0, "");
95     ASSERT_EQ(close(fd), 0, "");
96     ASSERT_TRUE(check_link_count("::dira", 2), "");
97     ASSERT_TRUE(check_link_count("::dira/file", 1), "");
98 
99     // But adding a directory WILL change the parent link count.
100     ASSERT_EQ(mkdir("::dira/dirb", 0755), 0, "");
101     ASSERT_TRUE(check_link_count("::dira", 3), "");
102     ASSERT_TRUE(check_link_count("::dira/dirb", 2), "");
103 
104     // Test that adding "depth" increases the dir count as we expect.
105     ASSERT_EQ(mkdir("::dira/dirb/dirc", 0755), 0, "");
106     ASSERT_TRUE(check_link_count("::dira", 3), "");
107     ASSERT_TRUE(check_link_count("::dira/dirb", 3), "");
108     ASSERT_TRUE(check_link_count("::dira/dirb/dirc", 2), "");
109 
110     // Demonstrate that unwinding also reduces the link count.
111     ASSERT_EQ(unlink("::dira/dirb/dirc"), 0, "");
112     ASSERT_TRUE(check_link_count("::dira", 3), "");
113     ASSERT_TRUE(check_link_count("::dira/dirb", 2), "");
114 
115     ASSERT_EQ(unlink("::dira/dirb"), 0, "");
116     ASSERT_TRUE(check_link_count("::dira", 2), "");
117 
118     // Test that adding "width" increases the dir count too.
119     ASSERT_EQ(mkdir("::dira/dirb", 0755), 0, "");
120     ASSERT_TRUE(check_link_count("::dira", 3), "");
121     ASSERT_TRUE(check_link_count("::dira/dirb", 2), "");
122 
123     ASSERT_EQ(mkdir("::dira/dirc", 0755), 0, "");
124     ASSERT_TRUE(check_link_count("::dira", 4), "");
125     ASSERT_TRUE(check_link_count("::dira/dirb", 2), "");
126     ASSERT_TRUE(check_link_count("::dira/dirc", 2), "");
127 
128     // Demonstrate that unwinding also reduces the link count.
129     ASSERT_EQ(unlink("::dira/dirc"), 0, "");
130     ASSERT_TRUE(check_link_count("::dira", 3), "");
131     ASSERT_TRUE(check_link_count("::dira/dirb", 2), "");
132 
133     ASSERT_EQ(unlink("::dira/dirb"), 0, "");
134     ASSERT_TRUE(check_link_count("::dira", 2), "");
135 
136     ASSERT_EQ(unlink("::dira/file"), 0, "");
137     ASSERT_EQ(unlink("::dira"), 0, "");
138 
139     END_TEST;
140 }
141 
test_link_count_rename(void)142 bool test_link_count_rename(void) {
143     BEGIN_TEST;
144 
145     if (!test_info->supports_hardlinks) {
146         return true;
147     }
148 
149     // Check that link count does not change with simple rename
150     ASSERT_EQ(mkdir("::dir", 0755), 0, "");
151     ASSERT_TRUE(check_link_count("::dir", 2), "");
152     ASSERT_EQ(rename("::dir", "::dir_parent"), 0, "");
153     ASSERT_TRUE(check_link_count("::dir_parent", 2), "");
154 
155     // Set up parent directory with child directories
156     ASSERT_EQ(mkdir("::dir_parent/dir_child_a", 0755), 0, "");
157     ASSERT_EQ(mkdir("::dir_parent/dir_child_b", 0755), 0, "");
158     ASSERT_TRUE(check_link_count("::dir_parent", 4), "");
159     ASSERT_TRUE(check_link_count("::dir_parent/dir_child_a", 2), "");
160     ASSERT_TRUE(check_link_count("::dir_parent/dir_child_b", 2), "");
161 
162     // Rename a child directory out of its parent directory
163     ASSERT_EQ(rename("::dir_parent/dir_child_b", "::dir_parent_alt"), 0, "");
164     ASSERT_TRUE(check_link_count("::dir_parent", 3), "");
165     ASSERT_TRUE(check_link_count("::dir_parent/dir_child_a", 2), "");
166     ASSERT_TRUE(check_link_count("::dir_parent_alt", 2), "");
167 
168     // Rename a parent directory into another directory
169     ASSERT_EQ(rename("::dir_parent", "::dir_parent_alt/dir_semi_parent"), 0, "");
170     ASSERT_TRUE(check_link_count("::dir_parent_alt", 3), "");
171     ASSERT_TRUE(check_link_count("::dir_parent_alt/dir_semi_parent", 3), "");
172     ASSERT_TRUE(check_link_count("::dir_parent_alt/dir_semi_parent/dir_child_a", 2), "");
173 
174     // Rename a directory on top of an empty directory
175     ASSERT_EQ(mkdir("::dir_child", 0755), 0, "");
176     ASSERT_EQ(rename("::dir_child", "::dir_parent_alt/dir_semi_parent/dir_child_a"), 0, "");
177     ASSERT_TRUE(check_link_count("::dir_parent_alt", 3), "");
178     ASSERT_TRUE(check_link_count("::dir_parent_alt/dir_semi_parent", 3), "");
179     ASSERT_TRUE(check_link_count("::dir_parent_alt/dir_semi_parent/dir_child_a", 2), "");
180 
181     // Rename a directory on top of an empty directory from a non-root directory
182     ASSERT_EQ(mkdir("::dir", 0755), 0, "");
183     ASSERT_EQ(mkdir("::dir/dir_child", 0755), 0, "");
184     ASSERT_TRUE(check_link_count("::dir", 3), "");
185     ASSERT_TRUE(check_link_count("::dir/dir_child", 2), "");
186     ASSERT_EQ(rename("::dir/dir_child", "::dir_parent_alt/dir_semi_parent/dir_child_a"), 0, "");
187     ASSERT_TRUE(check_link_count("::dir", 2), "");
188     ASSERT_TRUE(check_link_count("::dir_parent_alt", 3), "");
189     ASSERT_TRUE(check_link_count("::dir_parent_alt/dir_semi_parent", 3), "");
190     ASSERT_TRUE(check_link_count("::dir_parent_alt/dir_semi_parent/dir_child_a", 2), "");
191 
192     // Rename a file on top of a file from a non-root directory
193     ASSERT_EQ(unlink("::dir_parent_alt/dir_semi_parent/dir_child_a"), 0, "");
194     int fd = open("::dir/dir_child", O_RDWR | O_CREAT | O_EXCL, 0644);
195     ASSERT_GT(fd, 0, "");
196     ASSERT_TRUE(check_link_count("::dir", 2), "");
197     ASSERT_TRUE(check_link_count("::dir/dir_child", 1), "");
198     int fd2 = open("::dir_parent_alt/dir_semi_parent/dir_child_a", O_RDWR | O_CREAT | O_EXCL, 0644);
199     ASSERT_GT(fd2, 0, "");
200     ASSERT_EQ(rename("::dir/dir_child", "::dir_parent_alt/dir_semi_parent/dir_child_a"), 0, "");
201     ASSERT_TRUE(check_link_count("::dir", 2), "");
202     ASSERT_TRUE(check_link_count("::dir_parent_alt", 3), "");
203     ASSERT_TRUE(check_link_count("::dir_parent_alt/dir_semi_parent", 2), "");
204     ASSERT_TRUE(check_link_count("::dir_parent_alt/dir_semi_parent/dir_child_a", 1), "");
205     ASSERT_EQ(close(fd), 0, "");
206     ASSERT_EQ(close(fd2), 0, "");
207 
208     // Clean up
209     ASSERT_EQ(unlink("::dir_parent_alt/dir_semi_parent/dir_child_a"), 0, "");
210     ASSERT_TRUE(check_link_count("::dir_parent_alt", 3), "");
211     ASSERT_TRUE(check_link_count("::dir_parent_alt/dir_semi_parent", 2), "");
212     ASSERT_EQ(unlink("::dir_parent_alt/dir_semi_parent"), 0, "");
213     ASSERT_TRUE(check_link_count("::dir_parent_alt", 2), "");
214     ASSERT_EQ(unlink("::dir_parent_alt"), 0, "");
215     ASSERT_EQ(unlink("::dir"), 0, "");
216 
217     END_TEST;
218 }
219 
test_link_between_dirs(void)220 bool test_link_between_dirs(void) {
221     BEGIN_TEST;
222 
223     if (!test_info->supports_hardlinks) {
224         return true;
225     }
226 
227     ASSERT_EQ(mkdir("::dira", 0755), 0, "");
228     // New directories should have two links:
229     // Parent --> newdir
230     // newdir ('.') --> newdir
231     ASSERT_TRUE(check_link_count("::dira", 2), "");
232 
233     ASSERT_EQ(mkdir("::dirb", 0755), 0, "");
234     ASSERT_TRUE(check_link_count("::dirb", 2), "");
235 
236     const char* oldpath = "::dira/a";
237     const char* newpath = "::dirb/b";
238 
239     // Make a file, fill it with content
240     int fd = open(oldpath, O_RDWR | O_CREAT | O_EXCL, 0644);
241     ASSERT_GT(fd, 0, "");
242     uint8_t buf[100];
243     for (size_t i = 0; i < sizeof(buf); i++) {
244         buf[i] = (uint8_t) rand();
245     }
246     ASSERT_STREAM_ALL(write, fd, buf, sizeof(buf));
247     ASSERT_TRUE(check_file_contents(fd, buf, sizeof(buf)), "");
248 
249     ASSERT_EQ(link(oldpath, newpath), 0, "");
250 
251     // Confirm that both the old link and the new links exist
252     int fd2 = open(newpath, O_RDWR, 0644);
253     ASSERT_GT(fd2, 0, "");
254     ASSERT_TRUE(check_file_contents(fd2, buf, sizeof(buf)), "");
255     ASSERT_TRUE(check_file_contents(fd, buf, sizeof(buf)), "");
256 
257     // Remove the old link
258     ASSERT_EQ(close(fd), 0, "");
259     ASSERT_EQ(close(fd2), 0, "");
260     ASSERT_EQ(unlink(oldpath), 0, "");
261 
262     // Open the link by its new name
263     fd = open(newpath, O_RDWR, 0644);
264     ASSERT_GT(fd, 0, "");
265     ASSERT_TRUE(check_file_contents(fd, buf, sizeof(buf)), "");
266 
267     ASSERT_EQ(close(fd), 0, "");
268     ASSERT_EQ(unlink(newpath), 0, "");
269     ASSERT_EQ(unlink("::dira"), 0, "");
270     ASSERT_EQ(unlink("::dirb"), 0, "");
271 
272     END_TEST;
273 }
274 
test_link_errors(void)275 bool test_link_errors(void) {
276     BEGIN_TEST;
277 
278     if (!test_info->supports_hardlinks) {
279         return true;
280     }
281 
282     const char* dirpath = "::dir";
283     const char* oldpath = "::a";
284     const char* newpath = "::b";
285     const char* newpathdir = "::b/";
286 
287     // We should not be able to create hard links to directories
288     ASSERT_EQ(mkdir(dirpath, 0755), 0, "");
289     ASSERT_EQ(link(dirpath, newpath), -1, "");
290     ASSERT_EQ(unlink(dirpath), 0, "");
291 
292     // We should not be able to create hard links to non-existent files
293     ASSERT_EQ(link(oldpath, newpath), -1, "");
294     ASSERT_EQ(errno, ENOENT, "");
295 
296     int fd = open(oldpath, O_RDWR | O_CREAT | O_EXCL, 0644);
297     ASSERT_GT(fd, 0, "");
298     ASSERT_EQ(close(fd), 0, "");
299 
300     // We should not be able to link to or from . or ..
301     ASSERT_EQ(link(oldpath, "::."), -1, "");
302     ASSERT_EQ(link(oldpath, "::.."), -1, "");
303     ASSERT_EQ(link("::.", newpath), -1, "");
304     ASSERT_EQ(link("::..", newpath), -1, "");
305 
306     // We should not be able to link a file to itself
307     ASSERT_EQ(link(oldpath, oldpath), -1, "");
308     ASSERT_EQ(errno, EEXIST, "");
309 
310     // We should not be able to link a file to a path that implies it must be a directory
311     ASSERT_EQ(link(oldpath, newpathdir), -1, "");
312 
313     // After linking, we shouldn't be able to link again
314     ASSERT_EQ(link(oldpath, newpath), 0, "");
315     ASSERT_EQ(link(oldpath, newpath), -1, "");
316     ASSERT_EQ(errno, EEXIST, "");
317     // In either order
318     ASSERT_EQ(link(newpath, oldpath), -1, "");
319     ASSERT_EQ(errno, EEXIST, "");
320 
321     ASSERT_EQ(unlink(newpath), 0, "");
322     ASSERT_EQ(unlink(oldpath), 0, "");
323 
324     END_TEST;
325 }
326 
327 RUN_FOR_ALL_FILESYSTEMS(hard_link_tests,
328     RUN_TEST_MEDIUM(test_link_basic)
329     RUN_TEST_MEDIUM(test_link_count_dirs)
330     RUN_TEST_MEDIUM(test_link_count_rename)
331     RUN_TEST_MEDIUM(test_link_between_dirs)
332     RUN_TEST_MEDIUM(test_link_errors)
333 )
334