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