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