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