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