mem_fs_node_test.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
1// Copyright 2013 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
8#include <set>
9#include <string>
10
11#include "gtest/gtest.h"
12
13#include "nacl_io/devfs/dev_fs.h"
14#include "nacl_io/dir_node.h"
15#include "nacl_io/error.h"
16#include "nacl_io/ioctl.h"
17#include "nacl_io/kernel_handle.h"
18#include "nacl_io/kernel_proxy.h"
19#include "nacl_io/memfs/mem_fs.h"
20#include "nacl_io/memfs/mem_fs_node.h"
21#include "nacl_io/node.h"
22#include "nacl_io/osdirent.h"
23
24#define NULL_NODE ((Node*)NULL)
25
26using namespace nacl_io;
27
28static int s_alloc_num = 0;
29
30namespace {
31
32class MemFsForTesting : public MemFs {
33 public:
34  MemFsForTesting() {
35    FsInitArgs args(1);
36    EXPECT_EQ(0, Init(args));
37  }
38
39  int num_nodes() { return inode_pool_.size(); }
40};
41
42class MemFsNodeForTesting : public MemFsNode {
43 public:
44  MemFsNodeForTesting() : MemFsNode(NULL) { s_alloc_num++; }
45
46  ~MemFsNodeForTesting() { s_alloc_num--; }
47
48  using MemFsNode::Init;
49  using MemFsNode::AddChild;
50  using MemFsNode::RemoveChild;
51  using MemFsNode::FindChild;
52};
53
54class DirNodeForTesting : public DirNode {
55 public:
56  DirNodeForTesting() : DirNode(NULL) { s_alloc_num++; }
57
58  ~DirNodeForTesting() { s_alloc_num--; }
59
60  using DirNode::Init;
61  using DirNode::AddChild;
62  using DirNode::RemoveChild;
63  using DirNode::FindChild;
64};
65
66}  // namespace
67
68TEST(MemFsNodeTest, File) {
69  MemFsNodeForTesting file;
70  ScopedNode result_node;
71  size_t result_size = 0;
72  int result_bytes = 0;
73
74  EXPECT_EQ(0, file.Init(0));
75
76  // Test properties
77  EXPECT_EQ(0, file.GetLinks());
78  EXPECT_EQ(S_IRALL | S_IWALL, file.GetMode());
79  EXPECT_EQ(S_IFREG, file.GetType());
80  EXPECT_FALSE(file.IsaDir());
81  EXPECT_TRUE(file.IsaFile());
82  EXPECT_FALSE(file.IsaTTY());
83  EXPECT_EQ(0, file.RefCount());
84
85  // Test IO
86  char buf1[1024];
87  char buf2[1024 * 2];
88  for (size_t a = 0; a < sizeof(buf1); a++)
89    buf1[a] = a;
90  memset(buf2, 0, sizeof(buf2));
91  HandleAttr attr;
92
93  EXPECT_EQ(0, file.GetSize(&result_size));
94  EXPECT_EQ(0, result_size);
95  EXPECT_EQ(0, file.Read(attr, buf2, sizeof(buf2), &result_bytes));
96  EXPECT_EQ(0, result_bytes);
97  EXPECT_EQ(0, file.GetSize(&result_size));
98  EXPECT_EQ(0, result_size);
99  EXPECT_EQ(0, file.Write(attr, buf1, sizeof(buf1), &result_bytes));
100  EXPECT_EQ(sizeof(buf1), result_bytes);
101  EXPECT_EQ(0, file.GetSize(&result_size));
102  EXPECT_EQ(sizeof(buf1), result_size);
103  EXPECT_EQ(0, file.Read(attr, buf2, sizeof(buf2), &result_bytes));
104  EXPECT_EQ(sizeof(buf1), result_bytes);
105  EXPECT_EQ(0, memcmp(buf1, buf2, sizeof(buf1)));
106
107  struct stat s;
108  EXPECT_EQ(0, file.GetStat(&s));
109  EXPECT_LT(0, s.st_ino);  // 0 is an invalid inode number.
110  EXPECT_EQ(sizeof(buf1), s.st_size);
111
112  // Directory operations should fail
113  struct dirent d;
114  EXPECT_EQ(ENOTDIR, file.GetDents(0, &d, sizeof(d), &result_bytes));
115  EXPECT_EQ(ENOTDIR, file.AddChild("", result_node));
116  EXPECT_EQ(ENOTDIR, file.RemoveChild(""));
117  EXPECT_EQ(ENOTDIR, file.FindChild("", &result_node));
118  EXPECT_EQ(NULL_NODE, result_node.get());
119}
120
121TEST(MemFsNodeTest, FTruncate) {
122  MemFsNodeForTesting file;
123  size_t result_size = 0;
124  int result_bytes = 0;
125
126  char data[1024];
127  char buffer[1024];
128  char zero[1024];
129
130  for (size_t a = 0; a < sizeof(data); a++)
131    data[a] = a;
132  memset(buffer, 0, sizeof(buffer));
133  memset(zero, 0, sizeof(zero));
134  HandleAttr attr;
135
136  // Write the data to the file.
137  ASSERT_EQ(0, file.Write(attr, data, sizeof(data), &result_bytes));
138  ASSERT_EQ(sizeof(data), result_bytes);
139
140  // Double the size of the file.
141  EXPECT_EQ(0, file.FTruncate(sizeof(data) * 2));
142  EXPECT_EQ(0, file.GetSize(&result_size));
143  EXPECT_EQ(sizeof(data) * 2, result_size);
144
145  // Read the first half of the file, it shouldn't have changed.
146  EXPECT_EQ(0, file.Read(attr, buffer, sizeof(buffer), &result_bytes));
147  EXPECT_EQ(sizeof(buffer), result_bytes);
148  EXPECT_EQ(0, memcmp(buffer, data, sizeof(buffer)));
149
150  // Read the second half of the file, it should be all zeroes.
151  attr.offs = sizeof(data);
152  EXPECT_EQ(0, file.Read(attr, buffer, sizeof(buffer), &result_bytes));
153  EXPECT_EQ(sizeof(buffer), result_bytes);
154  EXPECT_EQ(0, memcmp(buffer, zero, sizeof(buffer)));
155
156  // Decrease the size of the file.
157  EXPECT_EQ(0, file.FTruncate(100));
158  EXPECT_EQ(0, file.GetSize(&result_size));
159  EXPECT_EQ(100, result_size);
160
161  // Data should still be there.
162  attr.offs = 0;
163  EXPECT_EQ(0, file.Read(attr, buffer, sizeof(buffer), &result_bytes));
164  EXPECT_EQ(100, result_bytes);
165  EXPECT_EQ(0, memcmp(buffer, data, 100));
166}
167
168TEST(MemFsNodeTest, Fcntl_GETFL) {
169  MemFsNodeForTesting* node = new MemFsNodeForTesting();
170  ScopedFilesystem fs(new MemFsForTesting());
171  ScopedNode file(node);
172  KernelHandle handle(fs, file);
173  ASSERT_EQ(0, handle.Init(O_CREAT | O_APPEND));
174
175  // Test invalid fcntl command.
176  ASSERT_EQ(ENOSYS, handle.Fcntl(-1, NULL));
177
178  // Test F_GETFL
179  ASSERT_EQ(0, node->Init(0));
180  int flags = 0;
181  ASSERT_EQ(0, handle.Fcntl(F_GETFL, &flags));
182  ASSERT_EQ(O_CREAT | O_APPEND, flags);
183
184  // Test F_SETFL
185  // Test adding of O_NONBLOCK
186  flags = O_NONBLOCK | O_APPEND;
187  ASSERT_EQ(0, handle.Fcntl(F_SETFL, NULL, flags));
188  ASSERT_EQ(0, handle.Fcntl(F_GETFL, &flags));
189  ASSERT_EQ(O_CREAT | O_APPEND | O_NONBLOCK, flags);
190
191  // Clearing of O_APPEND should generate EPERM;
192  flags = O_NONBLOCK;
193  ASSERT_EQ(EPERM, handle.Fcntl(F_SETFL, NULL, flags));
194}
195
196TEST(MemFsNodeTest, Directory) {
197  s_alloc_num = 0;
198  DirNodeForTesting root;
199  ScopedNode result_node;
200  size_t result_size = 0;
201  int result_bytes = 0;
202
203  root.Init(0);
204
205  // Test properties
206  EXPECT_EQ(0, root.GetLinks());
207  // Directories are always executable.
208  EXPECT_EQ(S_IRALL | S_IWALL | S_IXALL, root.GetMode());
209  EXPECT_EQ(S_IFDIR, root.GetType());
210  EXPECT_TRUE(root.IsaDir());
211  EXPECT_FALSE(root.IsaFile());
212  EXPECT_FALSE(root.IsaTTY());
213  EXPECT_EQ(0, root.RefCount());
214
215  // IO operations should fail
216  char buf1[1024];
217  HandleAttr attr;
218  EXPECT_EQ(0, root.GetSize(&result_size));
219  EXPECT_EQ(0, result_size);
220  EXPECT_EQ(EISDIR, root.Read(attr, buf1, sizeof(buf1), &result_bytes));
221  EXPECT_EQ(EISDIR, root.Write(attr, buf1, sizeof(buf1), &result_bytes));
222
223  // Test directory operations
224  MemFsNodeForTesting* raw_file = new MemFsNodeForTesting;
225  EXPECT_EQ(0, raw_file->Init(0));
226  ScopedNode file(raw_file);
227
228  EXPECT_EQ(0, root.RefCount());
229  EXPECT_EQ(1, file->RefCount());
230  EXPECT_EQ(0, root.AddChild("F1", file));
231  EXPECT_EQ(1, file->GetLinks());
232  EXPECT_EQ(2, file->RefCount());
233
234  // Test that the directory is there
235  const size_t kMaxDirents = 4;
236  struct dirent d[kMaxDirents];
237  EXPECT_EQ(0, root.GetDents(0, &d[0], sizeof(d), &result_bytes));
238
239  {
240    size_t num_dirents = result_bytes / sizeof(dirent);
241    EXPECT_EQ(3, num_dirents);
242    EXPECT_EQ(sizeof(dirent) * num_dirents, result_bytes);
243
244    std::multiset<std::string> dirnames;
245    for (int i = 0; i < num_dirents; ++i) {
246      EXPECT_LT(0, d[i].d_ino);  // 0 is an invalid inode number.
247      EXPECT_EQ(sizeof(dirent), d[i].d_off);
248      EXPECT_EQ(sizeof(dirent), d[i].d_reclen);
249      dirnames.insert(d[i].d_name);
250    }
251
252    EXPECT_EQ(1, dirnames.count("F1"));
253    EXPECT_EQ(1, dirnames.count("."));
254    EXPECT_EQ(1, dirnames.count(".."));
255  }
256
257  // There should only be 3 entries. Reading past that will return 0 bytes read.
258  EXPECT_EQ(0, root.GetDents(sizeof(d), &d[0], sizeof(d), &result_bytes));
259  EXPECT_EQ(0, result_bytes);
260
261  EXPECT_EQ(0, root.AddChild("F2", file));
262  EXPECT_EQ(2, file->GetLinks());
263  EXPECT_EQ(3, file->RefCount());
264  EXPECT_EQ(EEXIST, root.AddChild("F1", file));
265  EXPECT_EQ(2, file->GetLinks());
266  EXPECT_EQ(3, file->RefCount());
267
268  EXPECT_EQ(2, s_alloc_num);
269  EXPECT_EQ(0, root.FindChild("F1", &result_node));
270  EXPECT_NE(NULL_NODE, result_node.get());
271  EXPECT_EQ(0, root.FindChild("F2", &result_node));
272  EXPECT_NE(NULL_NODE, result_node.get());
273  EXPECT_EQ(ENOENT, root.FindChild("F3", &result_node));
274  EXPECT_EQ(NULL_NODE, result_node.get());
275
276  EXPECT_EQ(2, s_alloc_num);
277  EXPECT_EQ(0, root.RemoveChild("F1"));
278  EXPECT_EQ(1, file->GetLinks());
279  EXPECT_EQ(2, file->RefCount());
280  EXPECT_EQ(0, root.RemoveChild("F2"));
281  EXPECT_EQ(0, file->GetLinks());
282  EXPECT_EQ(1, file->RefCount());
283  EXPECT_EQ(2, s_alloc_num);
284
285  file.reset();
286  EXPECT_EQ(1, s_alloc_num);
287}
288
289TEST(MemFsNodeTest, OpenMode) {
290  MemFsNodeForTesting* node = new MemFsNodeForTesting();
291  ScopedFilesystem fs(new MemFsForTesting());
292  ScopedNode file(node);
293
294  const char write_buf[] = "hello world";
295  char read_buf[10];
296  int byte_count = 0;
297
298  // Write some data to the file
299  {
300    KernelHandle handle(fs, file);
301    ASSERT_EQ(0, handle.Init(O_CREAT | O_WRONLY));
302    ASSERT_EQ(0, handle.Write(write_buf, strlen(write_buf), &byte_count));
303    ASSERT_EQ(byte_count, strlen(write_buf));
304  }
305
306  // Reading from the O_WRONLY handle should be impossible
307  {
308    byte_count = 0;
309    KernelHandle handle(fs, file);
310    ASSERT_EQ(0, handle.Init(O_WRONLY));
311    ASSERT_EQ(EACCES, handle.Read(read_buf, 10, &byte_count));
312    ASSERT_EQ(0, handle.Write(write_buf, strlen(write_buf), &byte_count));
313    ASSERT_EQ(byte_count, strlen(write_buf));
314  }
315
316  // Writing to a O_RDONLY handle should fail
317  {
318    byte_count = 0;
319    KernelHandle handle(fs, file);
320    ASSERT_EQ(0, handle.Init(O_RDONLY));
321    ASSERT_EQ(EACCES, handle.Write(write_buf, strlen(write_buf), &byte_count));
322    ASSERT_EQ(0, handle.Read(read_buf, sizeof(read_buf), &byte_count));
323    ASSERT_EQ(byte_count, sizeof(read_buf));
324  }
325}
326