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