1 // Copyright 2017 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 <fs/pseudo-dir.h>
6
7 #include <fs/pseudo-file.h>
8 #include <unittest/unittest.h>
9
10 namespace {
11
12 class DirentChecker {
13 public:
DirentChecker(const void * buffer,size_t length)14 DirentChecker(const void* buffer, size_t length)
15 : current_(reinterpret_cast<const uint8_t*>(buffer)), remaining_(length) {}
16
ExpectEnd()17 bool ExpectEnd() {
18 BEGIN_HELPER;
19
20 EXPECT_EQ(0u, remaining_);
21
22 END_HELPER;
23 }
24
ExpectEntry(const char * name,uint32_t vtype)25 bool ExpectEntry(const char* name, uint32_t vtype) {
26 BEGIN_HELPER;
27
28 ASSERT_NE(0u, remaining_);
29 auto entry = reinterpret_cast<const vdirent_t*>(current_);
30 size_t entry_size = entry->size + sizeof(vdirent_t);
31 ASSERT_GE(remaining_, entry_size);
32 current_ += entry_size;
33 remaining_ -= entry_size;
34 EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(name),
35 reinterpret_cast<const uint8_t*>(entry->name), strlen(name), "name");
36 EXPECT_EQ(VTYPE_TO_DTYPE(vtype), entry->type);
37
38 END_HELPER;
39 }
40
41 private:
42 const uint8_t* current_;
43 size_t remaining_;
44 };
45
TestPseudoDir()46 bool TestPseudoDir() {
47 BEGIN_TEST;
48
49 auto dir = fbl::AdoptRef<fs::PseudoDir>(new fs::PseudoDir());
50 auto subdir = fbl::AdoptRef<fs::Vnode>(new fs::PseudoDir());
51 auto file1 = fbl::AdoptRef<fs::Vnode>(new fs::UnbufferedPseudoFile());
52 auto file2 = fbl::AdoptRef<fs::Vnode>(new fs::UnbufferedPseudoFile());
53
54 // add entries
55 EXPECT_EQ(ZX_OK, dir->AddEntry("subdir", subdir));
56 EXPECT_EQ(ZX_OK, dir->AddEntry("file1", file1));
57 EXPECT_EQ(ZX_OK, dir->AddEntry("file2", file2));
58 EXPECT_EQ(ZX_OK, dir->AddEntry("file2b", file2));
59
60 // try to add duplicates
61 EXPECT_EQ(ZX_ERR_ALREADY_EXISTS, dir->AddEntry("subdir", subdir));
62 EXPECT_EQ(ZX_ERR_ALREADY_EXISTS, dir->AddEntry("file1", subdir));
63
64 // remove entries
65 EXPECT_EQ(ZX_OK, dir->RemoveEntry("file2"));
66 EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->RemoveEntry("file2"));
67
68 // open as directory
69 fbl::RefPtr<fs::Vnode> redirect;
70 EXPECT_EQ(ZX_OK, dir->ValidateFlags(ZX_FS_FLAG_DIRECTORY));
71 EXPECT_EQ(ZX_OK, dir->Open(ZX_FS_FLAG_DIRECTORY, &redirect));
72 EXPECT_NULL(redirect);
73
74 // get attributes
75 vnattr_t attr;
76 EXPECT_EQ(ZX_OK, dir->Getattr(&attr));
77 EXPECT_EQ(V_TYPE_DIR | V_IRUSR, attr.mode);
78 EXPECT_EQ(1, attr.nlink);
79
80 // lookup entries
81 fbl::RefPtr<fs::Vnode> node;
82 EXPECT_EQ(ZX_OK, dir->Lookup(&node, "subdir"));
83 EXPECT_EQ(subdir.get(), node.get());
84 EXPECT_EQ(ZX_OK, dir->Lookup(&node, "file1"));
85 EXPECT_EQ(file1.get(), node.get());
86 EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, "file2"));
87 EXPECT_EQ(ZX_OK, dir->Lookup(&node, "file2b"));
88 EXPECT_EQ(file2.get(), node.get());
89
90 // readdir
91 {
92 fs::vdircookie_t cookie = {};
93 uint8_t buffer[4096];
94 size_t length;
95 EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK);
96 DirentChecker dc(buffer, length);
97 EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
98 EXPECT_TRUE(dc.ExpectEntry("subdir", V_TYPE_DIR));
99 EXPECT_TRUE(dc.ExpectEntry("file1", V_TYPE_FILE));
100 EXPECT_TRUE(dc.ExpectEntry("file2b", V_TYPE_FILE));
101 EXPECT_TRUE(dc.ExpectEnd());
102 }
103
104 // readdir with small buffer
105 {
106 fs::vdircookie_t cookie = {};
107 uint8_t buffer[2*sizeof(vdirent) + 13];
108 size_t length;
109 EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK);
110 DirentChecker dc(buffer, length);
111 EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
112 EXPECT_TRUE(dc.ExpectEntry("subdir", V_TYPE_DIR));
113 EXPECT_TRUE(dc.ExpectEnd());
114
115 // readdir again
116 EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK);
117 DirentChecker dc1(buffer, length);
118 EXPECT_TRUE(dc1.ExpectEntry("file1", V_TYPE_FILE));
119 EXPECT_TRUE(dc1.ExpectEntry("file2b", V_TYPE_FILE));
120 EXPECT_TRUE(dc1.ExpectEnd());
121 }
122
123 // test removed entries do not appear in readdir or lookup
124 dir->RemoveEntry("file1");
125 {
126 fs::vdircookie_t cookie = {};
127 uint8_t buffer[4096];
128 size_t length;
129 EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK);
130 DirentChecker dc(buffer, length);
131 EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
132 EXPECT_TRUE(dc.ExpectEntry("subdir", V_TYPE_DIR));
133 EXPECT_TRUE(dc.ExpectEntry("file2b", V_TYPE_FILE));
134 EXPECT_TRUE(dc.ExpectEnd());
135 }
136 EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, "file1"));
137
138 // remove all entries
139 dir->RemoveAllEntries();
140
141 // readdir again
142 {
143 fs::vdircookie_t cookie = {};
144 uint8_t buffer[4096];
145 size_t length;
146 EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK);
147 DirentChecker dc(buffer, length);
148 EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
149 EXPECT_TRUE(dc.ExpectEnd());
150 }
151
152 // FIXME(ZX-1186): Can't unittest watch/notify (hard to isolate right now).
153
154 END_TEST;
155 }
156
157 } // namespace
158
159 BEGIN_TEST_CASE(pseudo_dir_tests)
160 RUN_TEST(TestPseudoDir)
161 END_TEST_CASE(pseudo_dir_tests)
162