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