// Copyright 2016 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include #include #include #include #include #include "filesystems.h" #include "misc.h" bool TestAccessReadable(void) { BEGIN_TEST; const char* filename = "::alpha"; // Try writing a string to a file int fd = open(filename, O_RDWR | O_CREAT, 0644); ASSERT_GT(fd, 0); const char buf[] = "Hello, World!\n"; ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf)); ASSERT_EQ(close(fd), 0); // Re-open as readonly fd = open(filename, O_RDONLY, 0644); // Reading is allowed char tmp[sizeof(buf)]; ASSERT_EQ(read(fd, tmp, sizeof(tmp)), sizeof(tmp)); ASSERT_EQ(memcmp(buf, tmp, sizeof(tmp)), 0); // Writing is disallowed ASSERT_EQ(write(fd, buf, sizeof(buf)), -1); ASSERT_EQ(errno, EBADF); errno = 0; // Truncating is disallowed ASSERT_EQ(ftruncate(fd, 0), -1); ASSERT_EQ(errno, EBADF); errno = 0; ASSERT_EQ(close(fd), 0); ASSERT_EQ(unlink(filename), 0); END_TEST; } bool TestAccessWritable(void) { BEGIN_TEST; const char* filename = "::alpha"; // Try writing a string to a file int fd = open(filename, O_RDWR | O_CREAT, 0644); ASSERT_GT(fd, 0); const char buf[] = "Hello, World!\n"; ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf)); ASSERT_EQ(close(fd), 0); // Re-open as writable fd = open(filename, O_WRONLY, 0644); // Reading is disallowed char tmp[sizeof(buf)]; ASSERT_EQ(read(fd, tmp, sizeof(tmp)), -1); ASSERT_EQ(errno, EBADF); errno = 0; // Writing is allowed ASSERT_EQ(write(fd, buf, sizeof(buf)), sizeof(buf)); // Truncating is allowed ASSERT_EQ(ftruncate(fd, 0), 0); ASSERT_EQ(close(fd), 0); ASSERT_EQ(unlink(filename), 0); END_TEST; } bool TestAccessBadflags(void) { BEGIN_TEST; const char* filename = "::foobar"; int fd = open(filename, O_RDWR | O_CREAT, 0644); ASSERT_GT(fd, 0); ASSERT_EQ(close(fd), 0); // No read-only truncation ASSERT_LT(open(filename, O_RDONLY | O_TRUNC | O_CREAT, 0644), 0); ASSERT_EQ(unlink(filename), 0); END_TEST; } bool TestAccessDirectory(void) { BEGIN_TEST; const char* filename = "::foobar"; ASSERT_EQ(mkdir(filename, 0666), 0); // Try opening as writable int fd = open(filename, O_RDWR, 0644); ASSERT_LT(fd, 0); ASSERT_EQ(errno, EISDIR); fd = open(filename, O_WRONLY, 0644); ASSERT_LT(fd, 0); ASSERT_EQ(errno, EISDIR); // Directories should only be openable with O_RDONLY fd = open(filename, O_RDONLY, 0644); ASSERT_GT(fd, 0); ASSERT_EQ(close(fd), 0); ASSERT_EQ(rmdir(filename), 0); END_TEST; } bool TestAccessOpath(void) { BEGIN_TEST; const char* dirname = "::foo"; const char* filename = "::foo/bar"; ASSERT_EQ(mkdir(dirname, 0666), 0); int fd; // Cannot create a file as O_PATH fd = open(filename, O_CREAT | O_RDWR | O_PATH); ASSERT_LT(fd, 0); const char* data = "hello"; const size_t datalen = strlen(data); fd = open(filename, O_CREAT | O_RDWR); ASSERT_GE(fd, 0); ASSERT_EQ(write(fd, data, datalen), static_cast(datalen)); ASSERT_EQ(close(fd), 0); // Cannot read to / write from O_PATH fd fd = open(filename, O_RDWR | O_PATH); ASSERT_GE(fd, 0); char buf[128]; ASSERT_LT(read(fd, buf, sizeof(buf)), 0); ASSERT_EQ(errno, EBADF); ASSERT_LT(write(fd, data, datalen), 0); ASSERT_EQ(errno, EBADF); ASSERT_LT(lseek(fd, 1, SEEK_SET), 0); ASSERT_EQ(errno, EBADF); // We can fstat the file, however struct stat st; ASSERT_EQ(fstat(fd, &st), 0); ASSERT_EQ(st.st_size, static_cast(datalen)); ASSERT_EQ(close(fd), 0); // We can pass in a variety of flags to open with O_PATH, and // they'll be ignored. fd = open(filename, O_RDWR | O_CREAT | O_EXCL | O_TRUNC | O_PATH); ASSERT_GE(fd, 0); ASSERT_EQ(fstat(fd, &st), 0); ASSERT_EQ(st.st_size, static_cast(datalen)); // We can use fcntl on the fd int flags = fcntl(fd, F_GETFL); ASSERT_GT(flags, -1); ASSERT_EQ(flags & O_ACCMODE, O_PATH); ASSERT_EQ(flags & ~O_ACCMODE, 0); // We can toggle some flags, even if they don't make much sense ASSERT_EQ(fcntl(fd, F_SETFL, flags | O_APPEND), 0); flags = fcntl(fd, F_GETFL); ASSERT_EQ(flags & O_ACCMODE, O_PATH); ASSERT_EQ(flags & ~O_ACCMODE, O_APPEND); // We still can't write though ASSERT_LT(write(fd, data, datalen), 0); ASSERT_EQ(errno, EBADF); // We cannot update attributes of the file struct timespec ts[2]; ts[0].tv_nsec = UTIME_OMIT; ts[1].tv_sec = 0; ts[1].tv_nsec = 0; ASSERT_LT(futimens(fd, ts), 0); ASSERT_EQ(errno, EBADF); ASSERT_EQ(close(fd), 0); // O_PATH doesn't ignore O_DIRECTORY ASSERT_LT(open(filename, O_PATH | O_DIRECTORY), 0); // We can use O_PATH when opening directories too fd = open(dirname, O_PATH | O_DIRECTORY); ASSERT_GE(fd, 0); // The *at functions are allowed ASSERT_EQ(renameat(fd, "bar", fd, "baz"), 0); if (test_info->supports_hardlinks) { // TODO(smklein): Implement linkat, use it here // ASSERT_EQ(linkat(fd, "baz", fd, "bar", 0), 0); ASSERT_EQ(link("::foo/baz", filename), 0); ASSERT_EQ(unlinkat(fd, "baz", 0), 0); } else { ASSERT_EQ(renameat(fd, "baz", fd, "bar"), 0); } // Readdir is not allowed DIR* dir = fdopendir(fd); ASSERT_NONNULL(dir); struct dirent* de = readdir(dir); ASSERT_NULL(de); ASSERT_EQ(errno, EBADF); ASSERT_EQ(closedir(dir), 0); ASSERT_EQ(unlink(filename), 0); ASSERT_EQ(rmdir(dirname), 0); END_TEST; } // This test case was created to prevent a regression of a // file descriptor refcounting bug: files opened with "O_PATH" // do not cause the underlying object to be opened, and files // opened without "O_PATH" do cause the underlying object to // be opened. Cloning the object should not invalidate the // internal file descriptor count. bool TestOpathFdcount(void) { BEGIN_TEST; const char* dirname = "::foo"; int fd; zx_handle_t handles[FDIO_MAX_HANDLES]; uint32_t types[FDIO_MAX_HANDLES]; zx_status_t r; // Opened with O_PATH, cloned, and closed before clone. ASSERT_EQ(mkdir(dirname, 0666), 0); ASSERT_GE((fd = open(dirname, O_PATH | O_DIRECTORY)), 0); ASSERT_GT((r = fdio_clone_fd(fd, 0, handles, types)), 0); ASSERT_EQ(close(fd), 0); ASSERT_EQ(zx_handle_close_many(handles, r), ZX_OK); ASSERT_EQ(rmdir(dirname), 0); // Opened with O_PATH, cloned, and closed after clone. ASSERT_EQ(mkdir(dirname, 0666), 0); ASSERT_GE((fd = open(dirname, O_PATH | O_DIRECTORY)), 0); ASSERT_GT((r = fdio_clone_fd(fd, 0, handles, types)), 0); ASSERT_EQ(zx_handle_close_many(handles, r), ZX_OK); ASSERT_EQ(close(fd), 0); ASSERT_EQ(rmdir(dirname), 0); END_TEST; } RUN_FOR_ALL_FILESYSTEMS(access_tests, RUN_TEST_MEDIUM(TestAccessReadable) RUN_TEST_MEDIUM(TestAccessWritable) RUN_TEST_MEDIUM(TestAccessBadflags) RUN_TEST_MEDIUM(TestAccessDirectory) RUN_TEST_MEDIUM(TestAccessOpath) RUN_TEST_MEDIUM(TestOpathFdcount) )