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 <fbl/vector.h>
6 #include <fs/lazy-dir.h>
7 #include <fs/pseudo-file.h>
8 #include <unittest/unittest.h>
9
10 #include <utility>
11
12 namespace {
13
14 class DirentChecker {
15 public:
DirentChecker(const void * buffer,size_t length)16 DirentChecker(const void* buffer, size_t length)
17 : current_(reinterpret_cast<const uint8_t*>(buffer)), remaining_(length) {}
18
ExpectEnd()19 bool ExpectEnd() {
20 BEGIN_HELPER;
21
22 EXPECT_EQ(0u, remaining_);
23
24 END_HELPER;
25 }
26
ExpectEntry(const char * name,uint32_t vtype)27 bool ExpectEntry(const char* name, uint32_t vtype) {
28 BEGIN_HELPER;
29
30 ASSERT_NE(0u, remaining_);
31 auto entry = reinterpret_cast<const vdirent_t*>(current_);
32 size_t entry_size = entry->size + sizeof(vdirent_t);
33 ASSERT_GE(remaining_, entry_size);
34 current_ += entry_size;
35 remaining_ -= entry_size;
36 EXPECT_BYTES_EQ(reinterpret_cast<const uint8_t*>(name),
37 reinterpret_cast<const uint8_t*>(entry->name), strlen(name), "name");
38 EXPECT_EQ(VTYPE_TO_DTYPE(vtype), entry->type);
39
40 END_HELPER;
41 }
42
43 private:
44 const uint8_t* current_;
45 size_t remaining_;
46 };
47
48 class TestLazyDirHelper : public fs::LazyDir {
49 public:
50 struct TestContent {
51 uint64_t id;
52 fbl::String name;
53 };
54
GetContents(LazyEntryVector * out_vector)55 void GetContents(LazyEntryVector* out_vector) override {
56 out_vector->reserve(contents_.size());
57 for (const auto& content : contents_) {
58 out_vector->push_back({content.id, content.name, V_TYPE_FILE});
59 }
60 }
61
GetFile(fbl::RefPtr<fs::Vnode> * out,uint64_t id,fbl::String name)62 zx_status_t GetFile(fbl::RefPtr<fs::Vnode>* out, uint64_t id, fbl::String name) override {
63 last_output_file = fbl::AdoptRef(new fs::UnbufferedPseudoFile());
64 *out = last_output_file;
65 last_id = id;
66 last_name = name;
67 return ZX_OK;
68 }
69
AddContent(TestContent content)70 void AddContent(TestContent content) {
71 contents_.push_back(std::move(content));
72 }
73
74 fbl::RefPtr<fs::Vnode> last_output_file;
75 uint64_t last_id;
76 fbl::String last_name;
77
78 private:
79 fbl::Vector<TestContent> contents_;
80 };
81
TestLazyDir()82 bool TestLazyDir() {
83 BEGIN_TEST;
84
85 TestLazyDirHelper test;
86
87 {
88 fs::vdircookie_t cookie = {};
89 uint8_t buffer[4096];
90 size_t len;
91
92 EXPECT_EQ(test.Readdir(&cookie, buffer, sizeof(buffer), &len), ZX_OK);
93 DirentChecker dc(buffer, len);
94 EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
95 EXPECT_TRUE(dc.ExpectEnd());
96 }
97
98 test.AddContent({1, "test"});
99 {
100 fs::vdircookie_t cookie = {};
101 uint8_t buffer[4096];
102 size_t len;
103
104 EXPECT_EQ(test.Readdir(&cookie, buffer, sizeof(buffer), &len), ZX_OK);
105 DirentChecker dc(buffer, len);
106 EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
107 EXPECT_TRUE(dc.ExpectEntry("test", V_TYPE_FILE));
108 EXPECT_TRUE(dc.ExpectEnd());
109
110 fbl::RefPtr<fs::Vnode> out;
111 EXPECT_EQ(test.Lookup(&out, "test"), ZX_OK);
112 EXPECT_EQ(1, test.last_id);
113 EXPECT_TRUE(strcmp("test", test.last_name.c_str()) == 0);
114 EXPECT_EQ(out.get(), test.last_output_file.get());
115
116 EXPECT_EQ(test.Lookup(&out, "test2"), ZX_ERR_NOT_FOUND);
117 }
118 test.AddContent({33, "aaaa"});
119 {
120 fs::vdircookie_t cookie = {};
121 uint8_t buffer[4096];
122 size_t len;
123
124 EXPECT_EQ(test.Readdir(&cookie, buffer, sizeof(buffer), &len), ZX_OK);
125 DirentChecker dc(buffer, len);
126 EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
127 EXPECT_TRUE(dc.ExpectEntry("test", V_TYPE_FILE));
128 EXPECT_TRUE(dc.ExpectEntry("aaaa", V_TYPE_FILE));
129 EXPECT_TRUE(dc.ExpectEnd());
130
131 fbl::RefPtr<fs::Vnode> out;
132 EXPECT_EQ(test.Lookup(&out, "aaaa"), ZX_OK);
133 EXPECT_EQ(33, test.last_id);
134 EXPECT_TRUE(strcmp("aaaa", test.last_name.c_str()) == 0);
135 EXPECT_EQ(out.get(), test.last_output_file.get());
136 }
137 {
138 // Ensure manually setting cookie past entries excludes them, but leaves "."
139 fs::vdircookie_t cookie = {};
140 cookie.n = 30;
141 uint8_t buffer[4096];
142 size_t len;
143
144 EXPECT_EQ(test.Readdir(&cookie, buffer, sizeof(buffer), &len), ZX_OK);
145 DirentChecker dc(buffer, len);
146 EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR));
147 EXPECT_TRUE(dc.ExpectEntry("aaaa", V_TYPE_FILE));
148 EXPECT_TRUE(dc.ExpectEnd());
149
150 // Expect that "." is missing when reusing the cookie.
151 EXPECT_EQ(test.Readdir(&cookie, buffer, sizeof(buffer), &len), ZX_OK);
152 dc = DirentChecker(buffer, len);
153 EXPECT_TRUE(dc.ExpectEnd());
154 }
155
156 END_TEST;
157 }
158
159 } // namespace
160
161 BEGIN_TEST_CASE(lazy_dir_tests)
162 RUN_TEST(TestLazyDir)
163 END_TEST_CASE(lazy_dir_tests)
164