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