// Copyright 2017 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include namespace { class DirentChecker { public: DirentChecker(const void* buffer, size_t length) : current_(reinterpret_cast(buffer)), remaining_(length) {} bool ExpectEnd() { BEGIN_HELPER; EXPECT_EQ(0u, remaining_); END_HELPER; } bool ExpectEntry(const char* name, uint32_t vtype) { BEGIN_HELPER; ASSERT_NE(0u, remaining_); auto entry = reinterpret_cast(current_); size_t entry_size = entry->size + sizeof(vdirent_t); ASSERT_GE(remaining_, entry_size); current_ += entry_size; remaining_ -= entry_size; EXPECT_BYTES_EQ(reinterpret_cast(name), reinterpret_cast(entry->name), strlen(name), "name"); EXPECT_EQ(VTYPE_TO_DTYPE(vtype), entry->type); END_HELPER; } private: const uint8_t* current_; size_t remaining_; }; bool TestPseudoDir() { BEGIN_TEST; auto dir = fbl::AdoptRef(new fs::PseudoDir()); auto subdir = fbl::AdoptRef(new fs::PseudoDir()); auto file1 = fbl::AdoptRef(new fs::UnbufferedPseudoFile()); auto file2 = fbl::AdoptRef(new fs::UnbufferedPseudoFile()); // add entries EXPECT_EQ(ZX_OK, dir->AddEntry("subdir", subdir)); EXPECT_EQ(ZX_OK, dir->AddEntry("file1", file1)); EXPECT_EQ(ZX_OK, dir->AddEntry("file2", file2)); EXPECT_EQ(ZX_OK, dir->AddEntry("file2b", file2)); // try to add duplicates EXPECT_EQ(ZX_ERR_ALREADY_EXISTS, dir->AddEntry("subdir", subdir)); EXPECT_EQ(ZX_ERR_ALREADY_EXISTS, dir->AddEntry("file1", subdir)); // remove entries EXPECT_EQ(ZX_OK, dir->RemoveEntry("file2")); EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->RemoveEntry("file2")); // open as directory fbl::RefPtr redirect; EXPECT_EQ(ZX_OK, dir->ValidateFlags(ZX_FS_FLAG_DIRECTORY)); EXPECT_EQ(ZX_OK, dir->Open(ZX_FS_FLAG_DIRECTORY, &redirect)); EXPECT_NULL(redirect); // get attributes vnattr_t attr; EXPECT_EQ(ZX_OK, dir->Getattr(&attr)); EXPECT_EQ(V_TYPE_DIR | V_IRUSR, attr.mode); EXPECT_EQ(1, attr.nlink); // lookup entries fbl::RefPtr node; EXPECT_EQ(ZX_OK, dir->Lookup(&node, "subdir")); EXPECT_EQ(subdir.get(), node.get()); EXPECT_EQ(ZX_OK, dir->Lookup(&node, "file1")); EXPECT_EQ(file1.get(), node.get()); EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, "file2")); EXPECT_EQ(ZX_OK, dir->Lookup(&node, "file2b")); EXPECT_EQ(file2.get(), node.get()); // readdir { fs::vdircookie_t cookie = {}; uint8_t buffer[4096]; size_t length; EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK); DirentChecker dc(buffer, length); EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR)); EXPECT_TRUE(dc.ExpectEntry("subdir", V_TYPE_DIR)); EXPECT_TRUE(dc.ExpectEntry("file1", V_TYPE_FILE)); EXPECT_TRUE(dc.ExpectEntry("file2b", V_TYPE_FILE)); EXPECT_TRUE(dc.ExpectEnd()); } // readdir with small buffer { fs::vdircookie_t cookie = {}; uint8_t buffer[2*sizeof(vdirent) + 13]; size_t length; EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK); DirentChecker dc(buffer, length); EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR)); EXPECT_TRUE(dc.ExpectEntry("subdir", V_TYPE_DIR)); EXPECT_TRUE(dc.ExpectEnd()); // readdir again EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK); DirentChecker dc1(buffer, length); EXPECT_TRUE(dc1.ExpectEntry("file1", V_TYPE_FILE)); EXPECT_TRUE(dc1.ExpectEntry("file2b", V_TYPE_FILE)); EXPECT_TRUE(dc1.ExpectEnd()); } // test removed entries do not appear in readdir or lookup dir->RemoveEntry("file1"); { fs::vdircookie_t cookie = {}; uint8_t buffer[4096]; size_t length; EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK); DirentChecker dc(buffer, length); EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR)); EXPECT_TRUE(dc.ExpectEntry("subdir", V_TYPE_DIR)); EXPECT_TRUE(dc.ExpectEntry("file2b", V_TYPE_FILE)); EXPECT_TRUE(dc.ExpectEnd()); } EXPECT_EQ(ZX_ERR_NOT_FOUND, dir->Lookup(&node, "file1")); // remove all entries dir->RemoveAllEntries(); // readdir again { fs::vdircookie_t cookie = {}; uint8_t buffer[4096]; size_t length; EXPECT_EQ(dir->Readdir(&cookie, buffer, sizeof(buffer), &length), ZX_OK); DirentChecker dc(buffer, length); EXPECT_TRUE(dc.ExpectEntry(".", V_TYPE_DIR)); EXPECT_TRUE(dc.ExpectEnd()); } // FIXME(ZX-1186): Can't unittest watch/notify (hard to isolate right now). END_TEST; } } // namespace BEGIN_TEST_CASE(pseudo_dir_tests) RUN_TEST(TestPseudoDir) END_TEST_CASE(pseudo_dir_tests)