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 <stdint.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <sys/stat.h>
13 #include <unistd.h>
14 
15 #include <lib/fdio/limits.h>
16 #include <lib/fdio/util.h>
17 #include <unittest/unittest.h>
18 #include <zircon/syscalls.h>
19 
20 #include "filesystems.h"
21 #include "misc.h"
22 
TestAccessReadable(void)23 bool TestAccessReadable(void) {
24     BEGIN_TEST;
25 
26     const char* filename = "::alpha";
27 
28     // Try writing a string to a file
29     int fd = open(filename, O_RDWR | O_CREAT, 0644);
30     ASSERT_GT(fd, 0);
31     const char buf[] = "Hello, World!\n";
32     ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf));
33     ASSERT_EQ(close(fd), 0);
34 
35     // Re-open as readonly
36     fd = open(filename, O_RDONLY, 0644);
37 
38     // Reading is allowed
39     char tmp[sizeof(buf)];
40     ASSERT_EQ(read(fd, tmp, sizeof(tmp)), sizeof(tmp));
41     ASSERT_EQ(memcmp(buf, tmp, sizeof(tmp)), 0);
42 
43     // Writing is disallowed
44     ASSERT_EQ(write(fd, buf, sizeof(buf)), -1);
45     ASSERT_EQ(errno, EBADF);
46     errno = 0;
47 
48     // Truncating is disallowed
49     ASSERT_EQ(ftruncate(fd, 0), -1);
50     ASSERT_EQ(errno, EBADF);
51     errno = 0;
52 
53     ASSERT_EQ(close(fd), 0);
54     ASSERT_EQ(unlink(filename), 0);
55 
56     END_TEST;
57 }
58 
TestAccessWritable(void)59 bool TestAccessWritable(void) {
60     BEGIN_TEST;
61 
62     const char* filename = "::alpha";
63 
64     // Try writing a string to a file
65     int fd = open(filename, O_RDWR | O_CREAT, 0644);
66     ASSERT_GT(fd, 0);
67     const char buf[] = "Hello, World!\n";
68     ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf));
69     ASSERT_EQ(close(fd), 0);
70 
71     // Re-open as writable
72     fd = open(filename, O_WRONLY, 0644);
73 
74     // Reading is disallowed
75     char tmp[sizeof(buf)];
76     ASSERT_EQ(read(fd, tmp, sizeof(tmp)), -1);
77     ASSERT_EQ(errno, EBADF);
78     errno = 0;
79 
80     // Writing is allowed
81     ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf));
82 
83     // Truncating is allowed
84     ASSERT_EQ(ftruncate(fd, 0), 0);
85 
86     ASSERT_EQ(close(fd), 0);
87     ASSERT_EQ(unlink(filename), 0);
88 
89     END_TEST;
90 }
91 
TestAccessBadflags(void)92 bool TestAccessBadflags(void) {
93     BEGIN_TEST;
94 
95     const char* filename = "::foobar";
96 
97     int fd = open(filename, O_RDWR | O_CREAT, 0644);
98     ASSERT_GT(fd, 0);
99     ASSERT_EQ(close(fd), 0);
100 
101     // No read-only truncation
102     ASSERT_LT(open(filename, O_RDONLY | O_TRUNC | O_CREAT, 0644), 0);
103 
104     ASSERT_EQ(unlink(filename), 0);
105 
106     END_TEST;
107 }
108 
TestAccessDirectory(void)109 bool TestAccessDirectory(void) {
110     BEGIN_TEST;
111 
112     const char* filename = "::foobar";
113 
114     ASSERT_EQ(mkdir(filename, 0666), 0);
115 
116     // Try opening as writable
117     int fd = open(filename, O_RDWR, 0644);
118     ASSERT_LT(fd, 0);
119     ASSERT_EQ(errno, EISDIR);
120     fd = open(filename, O_WRONLY, 0644);
121     ASSERT_LT(fd, 0);
122     ASSERT_EQ(errno, EISDIR);
123 
124     // Directories should only be openable with O_RDONLY
125     fd = open(filename, O_RDONLY, 0644);
126     ASSERT_GT(fd, 0);
127     ASSERT_EQ(close(fd), 0);
128     ASSERT_EQ(rmdir(filename), 0);
129 
130     END_TEST;
131 }
132 
TestAccessOpath(void)133 bool TestAccessOpath(void) {
134     BEGIN_TEST;
135 
136     const char* dirname = "::foo";
137     const char* filename = "::foo/bar";
138 
139     ASSERT_EQ(mkdir(dirname, 0666), 0);
140     int fd;
141 
142     // Cannot create a file as O_PATH
143     fd = open(filename, O_CREAT | O_RDWR | O_PATH);
144     ASSERT_LT(fd, 0);
145 
146     const char* data = "hello";
147     const size_t datalen = strlen(data);
148 
149     fd = open(filename, O_CREAT | O_RDWR);
150     ASSERT_GE(fd, 0);
151     ASSERT_EQ(write(fd, data, datalen), static_cast<ssize_t>(datalen));
152     ASSERT_EQ(close(fd), 0);
153 
154     // Cannot read to / write from O_PATH fd
155     fd = open(filename, O_RDWR | O_PATH);
156     ASSERT_GE(fd, 0);
157 
158     char buf[128];
159     ASSERT_LT(read(fd, buf, sizeof(buf)), 0);
160     ASSERT_EQ(errno, EBADF);
161     ASSERT_LT(write(fd, data, datalen), 0);
162     ASSERT_EQ(errno, EBADF);
163     ASSERT_LT(lseek(fd, 1, SEEK_SET), 0);
164     ASSERT_EQ(errno, EBADF);
165 
166     // We can fstat the file, however
167     struct stat st;
168     ASSERT_EQ(fstat(fd, &st), 0);
169     ASSERT_EQ(st.st_size, static_cast<ssize_t>(datalen));
170     ASSERT_EQ(close(fd), 0);
171 
172     // We can pass in a variety of flags to open with O_PATH, and
173     // they'll be ignored.
174     fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_TRUNC | O_PATH);
175     ASSERT_GE(fd, 0);
176     ASSERT_EQ(fstat(fd, &st), 0);
177     ASSERT_EQ(st.st_size, static_cast<ssize_t>(datalen));
178 
179     // We can use fcntl on the fd
180     int flags = fcntl(fd, F_GETFL);
181     ASSERT_GT(flags, -1);
182     ASSERT_EQ(flags & O_ACCMODE, O_PATH);
183     ASSERT_EQ(flags & ~O_ACCMODE, 0);
184 
185     // We can toggle some flags, even if they don't make much sense
186     ASSERT_EQ(fcntl(fd, F_SETFL, flags | O_APPEND), 0);
187     flags = fcntl(fd, F_GETFL);
188     ASSERT_EQ(flags & O_ACCMODE, O_PATH);
189     ASSERT_EQ(flags & ~O_ACCMODE, O_APPEND);
190     // We still can't write though
191     ASSERT_LT(write(fd, data, datalen), 0);
192     ASSERT_EQ(errno, EBADF);
193 
194     // We cannot update attributes of the file
195     struct timespec ts[2];
196     ts[0].tv_nsec = UTIME_OMIT;
197     ts[1].tv_sec = 0;
198     ts[1].tv_nsec = 0;
199     ASSERT_LT(futimens(fd, ts), 0);
200     ASSERT_EQ(errno, EBADF);
201     ASSERT_EQ(close(fd), 0);
202 
203     // O_PATH doesn't ignore O_DIRECTORY
204     ASSERT_LT(open(filename, O_PATH | O_DIRECTORY), 0);
205 
206     // We can use O_PATH when opening directories too
207     fd = open(dirname, O_PATH | O_DIRECTORY);
208     ASSERT_GE(fd, 0);
209 
210     // The *at functions are allowed
211     ASSERT_EQ(renameat(fd, "bar", fd, "baz"), 0);
212     if (test_info->supports_hardlinks) {
213         // TODO(smklein): Implement linkat, use it here
214         // ASSERT_EQ(linkat(fd, "baz", fd, "bar", 0), 0);
215         ASSERT_EQ(link("::foo/baz", filename), 0);
216         ASSERT_EQ(unlinkat(fd, "baz", 0), 0);
217     } else {
218         ASSERT_EQ(renameat(fd, "baz", fd, "bar"), 0);
219     }
220 
221     // Readdir is not allowed
222     DIR* dir = fdopendir(fd);
223     ASSERT_NONNULL(dir);
224     struct dirent* de = readdir(dir);
225     ASSERT_NULL(de);
226     ASSERT_EQ(errno, EBADF);
227     ASSERT_EQ(closedir(dir), 0);
228 
229     ASSERT_EQ(unlink(filename), 0);
230     ASSERT_EQ(rmdir(dirname), 0);
231 
232     END_TEST;
233 }
234 
235 // This test case was created to prevent a regression of a
236 // file descriptor refcounting bug: files opened with "O_PATH"
237 // do not cause the underlying object to be opened, and files
238 // opened without "O_PATH" do cause the underlying object to
239 // be opened. Cloning the object should not invalidate the
240 // internal file descriptor count.
TestOpathFdcount(void)241 bool TestOpathFdcount(void) {
242     BEGIN_TEST;
243 
244     const char* dirname = "::foo";
245     int fd;
246     zx_handle_t handles[FDIO_MAX_HANDLES];
247     uint32_t types[FDIO_MAX_HANDLES];
248     zx_status_t r;
249 
250     // Opened with O_PATH, cloned, and closed before clone.
251     ASSERT_EQ(mkdir(dirname, 0666), 0);
252     ASSERT_GE((fd = open(dirname, O_PATH | O_DIRECTORY)), 0);
253     ASSERT_GT((r = fdio_clone_fd(fd, 0, handles, types)), 0);
254     ASSERT_EQ(close(fd), 0);
255     ASSERT_EQ(zx_handle_close_many(handles, r), ZX_OK);
256     ASSERT_EQ(rmdir(dirname), 0);
257 
258     // Opened with O_PATH, cloned, and closed after clone.
259     ASSERT_EQ(mkdir(dirname, 0666), 0);
260     ASSERT_GE((fd = open(dirname, O_PATH | O_DIRECTORY)), 0);
261     ASSERT_GT((r = fdio_clone_fd(fd, 0, handles, types)), 0);
262     ASSERT_EQ(zx_handle_close_many(handles, r), ZX_OK);
263     ASSERT_EQ(close(fd), 0);
264     ASSERT_EQ(rmdir(dirname), 0);
265 
266     END_TEST;
267 }
268 
269 RUN_FOR_ALL_FILESYSTEMS(access_tests,
270     RUN_TEST_MEDIUM(TestAccessReadable)
271     RUN_TEST_MEDIUM(TestAccessWritable)
272     RUN_TEST_MEDIUM(TestAccessBadflags)
273     RUN_TEST_MEDIUM(TestAccessDirectory)
274     RUN_TEST_MEDIUM(TestAccessOpath)
275     RUN_TEST_MEDIUM(TestOpathFdcount)
276 )
277