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