1 // Copyright (c) 2012 The Chromium 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 <errno.h> 6 #include <fcntl.h> 7 #include <pthread.h> 8 #include <sys/stat.h> 9 10 #include <map> 11 #include <string> 12 13 #include "gtest/gtest.h" 14 15 #include "nacl_io/filesystem.h" 16 #include "nacl_io/kernel_handle.h" 17 #include "nacl_io/kernel_object.h" 18 #include "nacl_io/path.h" 19 20 using namespace nacl_io; 21 22 namespace { 23 24 class NodeForTesting : public Node { 25 public: 26 explicit NodeForTesting(Filesystem* fs) : Node(fs) {} 27 }; 28 29 class FilesystemForTesting : public Filesystem { 30 public: 31 FilesystemForTesting() {} 32 33 public: 34 Error Access(const Path& path, int a_mode) { return ENOSYS; } 35 Error Open(const Path& path, int mode, ScopedNode* out_node) { 36 out_node->reset(NULL); 37 return ENOSYS; 38 } 39 Error Unlink(const Path& path) { return 0; } 40 Error Mkdir(const Path& path, int permissions) { return 0; } 41 Error Rmdir(const Path& path) { return 0; } 42 Error Remove(const Path& path) { return 0; } 43 Error Rename(const Path& path, const Path& newpath) { return 0; } 44 }; 45 46 class KernelHandleForTesting : public KernelHandle { 47 public: 48 KernelHandleForTesting(const ScopedFilesystem& fs, const ScopedNode& node) 49 : KernelHandle(fs, node) {} 50 }; 51 52 class KernelObjectTest : public ::testing::Test { 53 public: 54 void SetUp() { 55 fs.reset(new FilesystemForTesting()); 56 node.reset(new NodeForTesting(fs.get())); 57 } 58 59 void TearDown() { 60 // fs is ref-counted, it doesn't need to be explicitly deleted. 61 node.reset(NULL); 62 fs.reset(NULL); 63 } 64 65 KernelObject proxy; 66 ScopedFilesystem fs; 67 ScopedNode node; 68 }; 69 70 } // namespace 71 72 TEST_F(KernelObjectTest, Referencing) { 73 // The filesystem and node should have 1 ref count at this point 74 EXPECT_EQ(1, fs->RefCount()); 75 EXPECT_EQ(1, node->RefCount()); 76 77 // Pass the filesystem and node into a KernelHandle 78 KernelHandle* raw_handle = new KernelHandleForTesting(fs, node); 79 ScopedKernelHandle handle_a(raw_handle); 80 81 // The filesystem and node should have 1 ref count at this point 82 EXPECT_EQ(1, handle_a->RefCount()); 83 EXPECT_EQ(2, fs->RefCount()); 84 EXPECT_EQ(2, node->RefCount()); 85 86 ScopedKernelHandle handle_b = handle_a; 87 88 // There should be two references to the KernelHandle, the filesystem and node 89 // should be unchanged. 90 EXPECT_EQ(2, handle_a->RefCount()); 91 EXPECT_EQ(2, handle_b->RefCount()); 92 EXPECT_EQ(handle_a.get(), handle_b.get()); 93 EXPECT_EQ(2, fs->RefCount()); 94 EXPECT_EQ(2, node->RefCount()); 95 96 // Allocating an FD should cause the KernelProxy to ref the handle and 97 // the node and filesystem should be unchanged. 98 int fd1 = proxy.AllocateFD(handle_a, "/example"); 99 EXPECT_EQ(3, handle_a->RefCount()); 100 EXPECT_EQ(2, fs->RefCount()); 101 EXPECT_EQ(2, node->RefCount()); 102 103 // If we "dup" the handle, we should bump the ref count on the handle 104 int fd2 = proxy.AllocateFD(handle_b, ""); 105 EXPECT_EQ(4, handle_a->RefCount()); 106 EXPECT_EQ(2, fs->RefCount()); 107 EXPECT_EQ(2, node->RefCount()); 108 109 // Handles are expected to come out in order 110 EXPECT_EQ(0, fd1); 111 EXPECT_EQ(1, fd2); 112 113 // Now we "free" the handles, since the proxy should hold them. 114 handle_a.reset(NULL); 115 handle_b.reset(NULL); 116 EXPECT_EQ(2, fs->RefCount()); 117 EXPECT_EQ(2, node->RefCount()); 118 119 // We should find the handle by either fd 120 EXPECT_EQ(0, proxy.AcquireHandle(fd1, &handle_a)); 121 EXPECT_EQ(0, proxy.AcquireHandle(fd2, &handle_b)); 122 EXPECT_EQ(raw_handle, handle_a.get()); 123 EXPECT_EQ(raw_handle, handle_b.get()); 124 125 EXPECT_EQ(4, handle_a->RefCount()); 126 EXPECT_EQ(2, fs->RefCount()); 127 EXPECT_EQ(2, node->RefCount()); 128 129 // A non existent fd should fail, and handleA should decrement as handleB 130 // is released by the call. 131 EXPECT_EQ(EBADF, proxy.AcquireHandle(-1, &handle_b)); 132 EXPECT_EQ(NULL, handle_b.get()); 133 EXPECT_EQ(3, handle_a->RefCount()); 134 135 EXPECT_EQ(EBADF, proxy.AcquireHandle(100, &handle_b)); 136 EXPECT_EQ(NULL, handle_b.get()); 137 138 // Now only the KernelProxy should reference the KernelHandle in the 139 // FD to KernelHandle Map. 140 handle_a.reset(); 141 handle_b.reset(); 142 143 EXPECT_EQ(2, raw_handle->RefCount()); 144 EXPECT_EQ(2, fs->RefCount()); 145 EXPECT_EQ(2, node->RefCount()); 146 proxy.FreeFD(fd2); 147 EXPECT_EQ(1, raw_handle->RefCount()); 148 EXPECT_EQ(2, fs->RefCount()); 149 EXPECT_EQ(2, node->RefCount()); 150 151 proxy.FreeFD(fd1); 152 EXPECT_EQ(1, fs->RefCount()); 153 EXPECT_EQ(1, node->RefCount()); 154 } 155 156 TEST_F(KernelObjectTest, FreeAndReassignFD) { 157 // The filesystem and node should have 1 ref count at this point 158 EXPECT_EQ(1, fs->RefCount()); 159 EXPECT_EQ(1, node->RefCount()); 160 161 KernelHandle* raw_handle = new KernelHandleForTesting(fs, node); 162 ScopedKernelHandle handle(raw_handle); 163 164 EXPECT_EQ(2, fs->RefCount()); 165 EXPECT_EQ(2, node->RefCount()); 166 EXPECT_EQ(1, raw_handle->RefCount()); 167 168 proxy.AllocateFD(handle, "/example"); 169 EXPECT_EQ(2, fs->RefCount()); 170 EXPECT_EQ(2, node->RefCount()); 171 EXPECT_EQ(2, raw_handle->RefCount()); 172 173 proxy.FreeAndReassignFD(5, handle, "/example"); 174 EXPECT_EQ(2, fs->RefCount()); 175 EXPECT_EQ(2, node->RefCount()); 176 EXPECT_EQ(3, raw_handle->RefCount()); 177 178 179 handle.reset(); 180 EXPECT_EQ(2, raw_handle->RefCount()); 181 182 proxy.AcquireHandle(5, &handle); 183 EXPECT_EQ(3, raw_handle->RefCount()); 184 EXPECT_EQ(raw_handle, handle.get()); 185 } 186