1 // Copyright 2018 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 <dirent.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <limits.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <threads.h>
12 #include <unistd.h>
13
14 #include <fbl/unique_fd.h>
15 #include <fuchsia/io/c/fidl.h>
16 #include <lib/async-loop/cpp/loop.h>
17 #include <lib/fdio/util.h>
18 #include <lib/fzl/fdio.h>
19 #include <lib/memfs/memfs.h>
20 #include <unittest/unittest.h>
21 #include <zircon/device/vfs.h>
22 #include <zircon/processargs.h>
23 #include <zircon/syscalls.h>
24
25 #include <utility>
26
27 namespace {
28
TestFidlBasic()29 bool TestFidlBasic() {
30 BEGIN_TEST;
31
32 async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
33 ASSERT_EQ(loop.StartThread(), ZX_OK);
34
35 ASSERT_EQ(memfs_install_at(loop.dispatcher(), "/fidltmp"), ZX_OK);
36 fbl::unique_fd fd(open("/fidltmp", O_DIRECTORY | O_RDONLY));
37 ASSERT_GE(fd.get(), 0);
38
39 // Create a file
40 const char* filename = "file-a";
41 fd.reset(openat(fd.get(), filename, O_CREAT | O_RDWR));
42 ASSERT_GE(fd.get(), 0);
43 const char* data = "hello";
44 ssize_t datalen = strlen(data);
45 ASSERT_EQ(write(fd.get(), data, datalen), datalen);
46 fd.reset();
47
48 zx_handle_t h, request;
49 ASSERT_EQ(zx_channel_create(0, &h, &request), ZX_OK);
50 ASSERT_EQ(fdio_service_connect("/fidltmp/file-a", request), ZX_OK);
51
52 fuchsia_io_NodeInfo info = {};
53 ASSERT_EQ(fuchsia_io_FileDescribe(h, &info), ZX_OK);
54 ASSERT_EQ(info.tag, fuchsia_io_NodeInfoTag_file);
55 ASSERT_EQ(info.file.event, ZX_HANDLE_INVALID);
56 zx_handle_close(h);
57
58 loop.Shutdown();
59
60 // No way to clean up the namespace entry. See ZX-2013 for more details.
61
62 END_TEST;
63 }
64
TestFidlOpenReadOnly()65 bool TestFidlOpenReadOnly() {
66 BEGIN_TEST;
67
68 async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
69 ASSERT_EQ(loop.StartThread(), ZX_OK);
70
71 ASSERT_EQ(memfs_install_at(loop.dispatcher(), "/fidltmp-ro"), ZX_OK);
72 fbl::unique_fd fd(open("/fidltmp-ro", O_DIRECTORY | O_RDONLY));
73 ASSERT_GE(fd.get(), 0);
74
75 // Create a file
76 const char* filename = "file-ro";
77 fd.reset(openat(fd.get(), filename, O_CREAT | O_RDWR));
78 ASSERT_GE(fd.get(), 0);
79 fd.reset();
80
81 zx_handle_t h, request;
82 ASSERT_EQ(zx_channel_create(0, &h, &request), ZX_OK);
83 ASSERT_EQ(fdio_open("/fidltmp-ro/file-ro", ZX_FS_RIGHT_READABLE, request), ZX_OK);
84
85 zx_status_t status;
86 uint32_t flags;
87 ASSERT_EQ(fuchsia_io_FileGetFlags(h, &status, &flags), ZX_OK);
88 ASSERT_EQ(status, ZX_OK);
89 ASSERT_EQ(flags, ZX_FS_RIGHT_READABLE);
90 zx_handle_close(h);
91
92 loop.Shutdown();
93
94 // No way to clean up the namespace entry. See ZX-2013 for more details.
95
96 END_TEST;
97 }
98
QueryInfo(const char * path,fuchsia_io_FilesystemInfo * info)99 bool QueryInfo(const char* path, fuchsia_io_FilesystemInfo* info) {
100 BEGIN_HELPER;
101 fbl::unique_fd fd(open(path, O_RDONLY | O_DIRECTORY));
102 ASSERT_TRUE(fd);
103 zx_status_t status;
104 fzl::FdioCaller caller(std::move(fd));
105 ASSERT_EQ(fuchsia_io_DirectoryAdminQueryFilesystem(caller.borrow_channel(), &status, info),
106 ZX_OK);
107 ASSERT_EQ(status, ZX_OK);
108 const char* kFsName = "memfs";
109 const char* name = reinterpret_cast<const char*>(info->name);
110 ASSERT_EQ(strncmp(name, kFsName, strlen(kFsName)), 0, "Unexpected filesystem mounted");
111 ASSERT_EQ(info->block_size, ZX_PAGE_SIZE);
112 ASSERT_EQ(info->max_filename_size, NAME_MAX);
113 ASSERT_EQ(info->fs_type, VFS_TYPE_MEMFS);
114 ASSERT_NE(info->fs_id, 0);
115 ASSERT_EQ(info->used_bytes % info->block_size, 0);
116 END_HELPER;
117 }
118
TestFidlQueryFilesystem()119 bool TestFidlQueryFilesystem() {
120 BEGIN_TEST;
121
122 {
123 async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
124 ASSERT_EQ(loop.StartThread(), ZX_OK);
125
126 ASSERT_EQ(memfs_install_at(loop.dispatcher(), "/fidltmp-basic"), ZX_OK);
127 fbl::unique_fd fd(open("/fidltmp-basic", O_DIRECTORY | O_RDONLY));
128 ASSERT_GE(fd.get(), 0);
129
130 // Sanity checks
131 fuchsia_io_FilesystemInfo info;
132 ASSERT_TRUE(QueryInfo("/fidltmp-basic", &info));
133
134 // Query number of blocks
135 ASSERT_EQ(info.total_bytes, UINT64_MAX);
136 ASSERT_EQ(info.used_bytes, 0);
137
138 loop.Shutdown();
139 }
140
141 // Query disk pressure in a page-limited scenario
142 {
143 async::Loop loop(&kAsyncLoopConfigNoAttachToThread);
144 ASSERT_EQ(loop.StartThread(), ZX_OK);
145
146 size_t max_num_pages = 3;
147 ASSERT_EQ(memfs_install_at_with_page_limit(loop.dispatcher(),
148 max_num_pages, "/fidltmp-limited"), ZX_OK);
149 fbl::unique_fd fd(open("/fidltmp-limited", O_DIRECTORY | O_RDONLY));
150 ASSERT_GE(fd.get(), 0);
151
152 fuchsia_io_FilesystemInfo info;
153 ASSERT_TRUE(QueryInfo("/fidltmp-limited", &info));
154
155 // When space is limited, total_bytes must be a multiple of block_size
156 ASSERT_EQ(info.total_bytes % info.block_size, 0);
157
158 // Query number of blocks
159 ASSERT_EQ(info.total_bytes, 3 * info.block_size);
160 ASSERT_EQ(info.used_bytes, 0);
161
162 // Create a file with a size smaller than ZX_PAGE_SIZE
163 DIR* d = fdopendir(fd.release());
164 const char* filename = "file-a";
165 fd.reset(openat(dirfd(d), filename, O_CREAT | O_RDWR));
166 ASSERT_GE(fd.get(), 0);
167 const char* data = "hello";
168 ssize_t datalen = strlen(data);
169 ASSERT_EQ(write(fd.get(), data, datalen), datalen);
170
171 // Query should now indicate an entire page is used
172 ASSERT_TRUE(QueryInfo("/fidltmp-limited", &info));
173 ASSERT_EQ(info.used_bytes, 1 * info.block_size);
174
175 // Unlink and close the file
176 ASSERT_EQ(unlinkat(dirfd(d), filename, 0), 0);
177 ASSERT_EQ(close(fd.get()), 0);
178 fd.reset();
179
180 // Query should now indicate 0 bytes used
181 ASSERT_TRUE(QueryInfo("/fidltmp-limited", &info));
182 ASSERT_EQ(info.used_bytes, 0);
183
184 closedir(d);
185 loop.Shutdown();
186 }
187
188 // No way to clean up the namespace entry. See ZX-2013 for more details.
189
190 END_TEST;
191 }
192
193 } // namespace
194
195 BEGIN_TEST_CASE(fidl_tests)
196 RUN_TEST(TestFidlBasic)
197 RUN_TEST(TestFidlOpenReadOnly)
198 RUN_TEST(TestFidlQueryFilesystem)
199 END_TEST_CASE(fidl_tests)
200