1 // Copyright 2016 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 <fcntl.h>
8 #include <limits.h>
9 #include <stdint.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/stat.h>
14 #include <unistd.h>
15 
16 #include <zircon/compiler.h>
17 #include <fbl/algorithm.h>
18 
19 #include "filesystems.h"
20 
21 // Make some files, then unlink them.
TestUnlinkSimple(void)22 bool TestUnlinkSimple(void) {
23     BEGIN_TEST;
24     const char* const paths[] = {"::abc", "::def", "::ghi", "::jkl", "::mnopqrstuvxyz"};
25     for (size_t i = 0; i < fbl::count_of(paths); i++) {
26         int fd = open(paths[i], O_RDWR | O_CREAT | O_EXCL, 0644);
27         ASSERT_GT(fd, 0);
28         ASSERT_EQ(close(fd), 0);
29     }
30     for (size_t i = 0; i < fbl::count_of(paths); i++) {
31         ASSERT_EQ(unlink(paths[i]), 0);
32     }
33     END_TEST;
34 }
35 
36 const char* const STRING_DATA[] = {
37     "Hello, world",
38     "Foo bar baz blat",
39     "This is yet another sample string",
40 };
41 
simple_read_test(int fd,size_t data_index)42 static bool simple_read_test(int fd, size_t data_index) {
43     ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
44     char buf[1024];
45     memset(buf, 0, sizeof(buf));
46     ssize_t len = strlen(STRING_DATA[data_index]);
47     ASSERT_EQ(read(fd, buf, len), len);
48     ASSERT_EQ(memcmp(STRING_DATA[data_index], buf, strlen(STRING_DATA[data_index])), 0);
49     return true;
50 }
51 
simple_write_test(int fd,size_t data_index)52 static bool simple_write_test(int fd, size_t data_index) {
53     ASSERT_EQ(ftruncate(fd, 0), 0);
54     ASSERT_EQ(lseek(fd, 0, SEEK_SET), 0);
55     ssize_t len = strlen(STRING_DATA[data_index]);
56     ASSERT_EQ(write(fd, STRING_DATA[data_index], len), len);
57     return simple_read_test(fd, data_index);
58 }
59 
TestUnlinkUseAfterwards(void)60 bool TestUnlinkUseAfterwards(void) {
61     BEGIN_TEST;
62 
63     const char* path = "::foobar";
64     int fd = open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
65     ASSERT_GT(fd, 0);
66 
67     ASSERT_TRUE(simple_write_test(fd, 1));
68 
69     // When we unlink path, fd is still open.
70     ASSERT_EQ(unlink(path), 0);
71     ASSERT_TRUE(simple_read_test(fd, 1)); // It should contain the same data as before
72     ASSERT_TRUE(simple_write_test(fd, 2)); // It should still be writable
73     ASSERT_EQ(close(fd), 0);           // This actually releases the file
74 
75     // Now, opening the file should fail without O_CREAT
76     ASSERT_EQ(open(path, O_RDWR, 0644), -1);
77 
78     END_TEST;
79 }
80 
TestUnlinkOpenElsewhere(void)81 bool TestUnlinkOpenElsewhere(void) {
82     BEGIN_TEST;
83 
84     const char* path = "::foobar";
85     int fd1 = open(path, O_RDWR | O_CREAT | O_EXCL, 0644);
86     ASSERT_GT(fd1, 0);
87     int fd2 = open(path, O_RDWR, 0644);
88     ASSERT_GT(fd2, 0);
89 
90     ASSERT_TRUE(simple_write_test(fd1, 0));
91     ASSERT_EQ(close(fd1), 0);
92 
93     // When we unlink path, fd2 is still open.
94     ASSERT_EQ(unlink(path), 0);
95     ASSERT_TRUE(simple_read_test(fd2, 0));  // It should contain the same data as before
96     ASSERT_TRUE(simple_write_test(fd2, 1)); // It should still be writable
97     ASSERT_EQ(close(fd2), 0);           // This actually releases the file
98 
99     // Now, opening the file should fail without O_CREAT
100     ASSERT_EQ(open(path, O_RDWR, 0644), -1);
101 
102     END_TEST;
103 }
104 
test_remove(void)105 bool test_remove(void) {
106     BEGIN_TEST;
107 
108     // Test the trivial cases of removing files and directories
109     const char* filename = "::file";
110     int fd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0644);
111     ASSERT_GT(fd, 0);
112     ASSERT_EQ(remove(filename), 0);
113     ASSERT_EQ(remove(filename), -1);
114     ASSERT_EQ(errno, ENOENT);
115     ASSERT_EQ(close(fd), 0);
116 
117     const char* dirname = "::dir";
118     ASSERT_EQ(mkdir(dirname, 0666), 0);
119     ASSERT_EQ(remove(dirname), 0);
120     ASSERT_EQ(remove(dirname), -1);
121     ASSERT_EQ(errno, ENOENT);
122 
123     // Test that we cannot remove non-empty directories, and that
124     // we see the expected error code too.
125     ASSERT_EQ(mkdir("::dir", 0666), 0);
126     ASSERT_EQ(mkdir("::dir/subdir", 0666), 0);
127     ASSERT_EQ(remove("::dir"), -1);
128     ASSERT_EQ(errno, ENOTEMPTY);
129     ASSERT_EQ(remove("::dir/subdir"), 0);
130     ASSERT_EQ(remove("::dir"), 0);
131     ASSERT_EQ(remove("::dir"), -1);
132     ASSERT_EQ(errno, ENOENT);
133 
134     END_TEST;
135 }
136 
137 RUN_FOR_ALL_FILESYSTEMS(unlink_tests,
138     RUN_TEST_MEDIUM(TestUnlinkSimple)
139     RUN_TEST_MEDIUM(TestUnlinkUseAfterwards)
140     RUN_TEST_MEDIUM(TestUnlinkOpenElsewhere)
141     RUN_TEST_MEDIUM(test_remove);
142 )
143