kernel_proxy_test.cc revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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 <stdio.h>
9#include <sys/stat.h>
10
11#include <map>
12#include <string>
13
14#include "gmock/gmock.h"
15#include "gtest/gtest.h"
16
17#include "mock_fs.h"
18#include "mock_node.h"
19
20#include "nacl_io/filesystem.h"
21#include "nacl_io/kernel_intercept.h"
22#include "nacl_io/kernel_proxy.h"
23#include "nacl_io/memfs/mem_fs.h"
24#include "nacl_io/osmman.h"
25#include "nacl_io/path.h"
26#include "nacl_io/typed_fs_factory.h"
27
28using namespace nacl_io;
29using namespace sdk_util;
30
31using ::testing::_;
32using ::testing::DoAll;
33using ::testing::Invoke;
34using ::testing::Return;
35using ::testing::SaveArg;
36using ::testing::SetArgPointee;
37using ::testing::StrEq;
38using ::testing::WithArgs;
39
40namespace {
41
42class KernelProxyTest_KernelProxy : public KernelProxy {
43 public:
44  Filesystem* RootFs() {
45    ScopedFilesystem fs;
46    Path path;
47
48    AcquireFsAndRelPath("/", &fs, &path);
49    return fs.get();
50  }
51};
52
53class KernelProxyTest : public ::testing::Test {
54 public:
55  KernelProxyTest() {}
56
57  void SetUp() {
58    ASSERT_EQ(0, ki_push_state_for_testing());
59    ASSERT_EQ(0, ki_init(&kp_));
60    // Unmount the passthrough FS and mount a memfs.
61    EXPECT_EQ(0, kp_.umount("/"));
62    EXPECT_EQ(0, kp_.mount("", "/", "memfs", 0, NULL));
63  }
64
65  void TearDown() { ki_uninit(); }
66
67 protected:
68  KernelProxyTest_KernelProxy kp_;
69};
70
71}  // namespace
72
73static int ki_fcntl_wrapper(int fd, int request, ...) {
74  va_list ap;
75  va_start(ap, request);
76  int rtn = ki_fcntl(fd, request, ap);
77  va_end(ap);
78  return rtn;
79}
80
81/**
82 * Test for fcntl commands F_SETFD and F_GETFD.  This
83 * is tested here rather than in the mount_node tests
84 * since the fd flags are not stored in the kernel_handle
85 * or the filesystem node but directly in the FD mapping.
86 */
87TEST_F(KernelProxyTest, Fcntl_GETFD) {
88  int fd = ki_open("/test", O_RDWR | O_CREAT, 0777);
89  ASSERT_NE(-1, fd);
90
91  // FD flags should start as zero.
92  ASSERT_EQ(0, ki_fcntl_wrapper(fd, F_GETFD));
93
94  // Check that setting FD_CLOEXEC works
95  int flags = FD_CLOEXEC;
96  ASSERT_EQ(0, ki_fcntl_wrapper(fd, F_SETFD, flags))
97      << "fcntl failed with: " << strerror(errno);
98  ASSERT_EQ(FD_CLOEXEC, ki_fcntl_wrapper(fd, F_GETFD));
99
100  // Check that setting invalid flag causes EINVAL
101  flags = FD_CLOEXEC + 1;
102  ASSERT_EQ(-1, ki_fcntl_wrapper(fd, F_SETFD, flags));
103  ASSERT_EQ(EINVAL, errno);
104}
105
106TEST_F(KernelProxyTest, FileLeak) {
107  const size_t buffer_size = 1024;
108  char filename[128];
109  int garbage[buffer_size];
110
111  MemFs* filesystem = (MemFs*)kp_.RootFs();
112  ScopedNode root;
113
114  ASSERT_EQ(0, filesystem->Open(Path("/"), O_RDONLY, &root));
115  ASSERT_EQ(0, root->ChildCount());
116
117  for (int file_num = 0; file_num < 4096; file_num++) {
118    sprintf(filename, "/foo%i.tmp", file_num++);
119    int fd = ki_open(filename, O_WRONLY | O_CREAT, 0777);
120    ASSERT_GT(fd, -1);
121    ASSERT_EQ(1, root->ChildCount());
122    ASSERT_EQ(buffer_size, ki_write(fd, garbage, buffer_size));
123    ki_close(fd);
124    ASSERT_EQ(0, ki_remove(filename));
125  }
126  ASSERT_EQ(0, root->ChildCount());
127}
128
129static bool g_handler_called = false;
130static void sighandler(int) { g_handler_called = true; }
131
132TEST_F(KernelProxyTest, Sigaction) {
133  struct sigaction action;
134  struct sigaction oaction;
135  memset(&action, 0, sizeof(action));
136
137  // Invalid signum
138  ASSERT_EQ(-1, ki_sigaction(-1, NULL, &oaction));
139  ASSERT_EQ(-1, ki_sigaction(SIGSTOP, NULL, &oaction));
140  ASSERT_EQ(EINVAL, errno);
141
142  // Get existing handler
143  memset(&oaction, 0, sizeof(oaction));
144  ASSERT_EQ(0, ki_sigaction(SIGINT, NULL, &oaction));
145  ASSERT_EQ(SIG_DFL, oaction.sa_handler);
146
147  // Attempt to set handler for unsupported signum
148  action.sa_handler = sighandler;
149  ASSERT_EQ(-1, ki_sigaction(SIGINT, &action, NULL));
150  ASSERT_EQ(EINVAL, errno);
151
152  // Attempt to set handler for supported signum
153  action.sa_handler = sighandler;
154  ASSERT_EQ(0, ki_sigaction(SIGWINCH, &action, NULL));
155
156  memset(&oaction, 0, sizeof(oaction));
157  ASSERT_EQ(0, ki_sigaction(SIGWINCH, NULL, &oaction));
158  ASSERT_EQ((sighandler_t*)sighandler, (sighandler_t*)oaction.sa_handler);
159}
160
161TEST_F(KernelProxyTest, KillSignals) {
162  // SIGSEGV can't be sent via kill(2)
163  ASSERT_EQ(-1, ki_kill(0, SIGSEGV)) << "kill(SEGV) failed to return an error";
164  ASSERT_EQ(EINVAL, errno) << "kill(SEGV) failed to set errno to EINVAL";
165
166  // Our implemenation should understand SIGWINCH
167  ASSERT_EQ(0, ki_kill(0, SIGWINCH)) << "kill(SIGWINCH) failed: " << errno;
168
169  // And USR1/USR2
170  ASSERT_EQ(0, ki_kill(0, SIGUSR1)) << "kill(SIGUSR1) failed: " << errno;
171  ASSERT_EQ(0, ki_kill(0, SIGUSR2)) << "kill(SIGUSR2) failed: " << errno;
172}
173
174TEST_F(KernelProxyTest, KillPIDValues) {
175  // Any PID other than 0, -1 and getpid() should yield ESRCH
176  // since there is only one valid process under NaCl
177  int mypid = getpid();
178  ASSERT_EQ(0, ki_kill(0, SIGWINCH));
179  ASSERT_EQ(0, ki_kill(-1, SIGWINCH));
180  ASSERT_EQ(0, ki_kill(mypid, SIGWINCH));
181
182  // Don't use mypid + 1 since getpid() actually returns -1
183  // when the IRT interface is missing (e.g. within chrome),
184  // and 0 is always a valid PID when calling kill().
185  int invalid_pid = mypid + 10;
186  ASSERT_EQ(-1, ki_kill(invalid_pid, SIGWINCH));
187  ASSERT_EQ(ESRCH, errno);
188}
189
190TEST_F(KernelProxyTest, SignalValues) {
191  ASSERT_EQ(ki_signal(SIGSEGV, sighandler), SIG_ERR)
192      << "registering SEGV handler didn't fail";
193  ASSERT_EQ(errno, EINVAL) << "signal(SEGV) failed to set errno to EINVAL";
194
195  ASSERT_EQ(ki_signal(-1, sighandler), SIG_ERR)
196      << "registering handler for invalid signal didn't fail";
197  ASSERT_EQ(errno, EINVAL) << "signal(-1) failed to set errno to EINVAL";
198}
199
200TEST_F(KernelProxyTest, SignalHandlerValues) {
201  // Unsupported signal.
202  ASSERT_NE(SIG_ERR, ki_signal(SIGSEGV, SIG_DFL));
203  ASSERT_EQ(SIG_ERR, ki_signal(SIGSEGV, SIG_IGN));
204  ASSERT_EQ(SIG_ERR, ki_signal(SIGSEGV, sighandler));
205
206  // Supported signal.
207  ASSERT_NE(SIG_ERR, ki_signal(SIGWINCH, SIG_DFL));
208  ASSERT_NE(SIG_ERR, ki_signal(SIGWINCH, SIG_IGN));
209  ASSERT_NE(SIG_ERR, ki_signal(SIGWINCH, sighandler));
210}
211
212TEST_F(KernelProxyTest, SignalSigwinch) {
213  g_handler_called = false;
214
215  // Register WINCH handler
216  sighandler_t newsig = sighandler;
217  sighandler_t oldsig = ki_signal(SIGWINCH, newsig);
218  ASSERT_NE(oldsig, SIG_ERR);
219
220  // Send signal.
221  ki_kill(0, SIGWINCH);
222
223  // Verify that handler was called
224  EXPECT_TRUE(g_handler_called);
225
226  // Restore existing handler
227  oldsig = ki_signal(SIGWINCH, oldsig);
228
229  // Verify the our newsig was returned as previous handler
230  ASSERT_EQ(oldsig, newsig);
231}
232
233TEST_F(KernelProxyTest, Rename) {
234  // Create a dummy file
235  int file1 = ki_open("/test1.txt", O_RDWR | O_CREAT, 0777);
236  ASSERT_GT(file1, -1);
237  ASSERT_EQ(0, ki_close(file1));
238
239  // Test the renaming works
240  ASSERT_EQ(0, ki_rename("/test1.txt", "/test2.txt"));
241
242  // Test that renaming across mount points fails
243  ASSERT_EQ(0, ki_mount("", "/foo", "memfs", 0, ""));
244  ASSERT_EQ(-1, ki_rename("/test2.txt", "/foo/test2.txt"));
245  ASSERT_EQ(EXDEV, errno);
246}
247
248TEST_F(KernelProxyTest, WorkingDirectory) {
249  char text[1024];
250
251  text[0] = 0;
252  ki_getcwd(text, sizeof(text));
253  EXPECT_STREQ("/", text);
254
255  char* alloc = ki_getwd(NULL);
256  EXPECT_EQ((char*)NULL, alloc);
257  EXPECT_EQ(EFAULT, errno);
258
259  text[0] = 0;
260  alloc = ki_getwd(text);
261  EXPECT_STREQ("/", alloc);
262
263  EXPECT_EQ(-1, ki_chdir("/foo"));
264  EXPECT_EQ(ENOENT, errno);
265
266  EXPECT_EQ(0, ki_chdir("/"));
267
268  EXPECT_EQ(0, ki_mkdir("/foo", S_IREAD | S_IWRITE));
269  EXPECT_EQ(-1, ki_mkdir("/foo", S_IREAD | S_IWRITE));
270  EXPECT_EQ(EEXIST, errno);
271
272  memset(text, 0, sizeof(text));
273  EXPECT_EQ(0, ki_chdir("foo"));
274  EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
275  EXPECT_STREQ("/foo", text);
276
277  memset(text, 0, sizeof(text));
278  EXPECT_EQ(-1, ki_chdir("foo"));
279  EXPECT_EQ(ENOENT, errno);
280  EXPECT_EQ(0, ki_chdir(".."));
281  EXPECT_EQ(0, ki_chdir("/foo"));
282  EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
283  EXPECT_STREQ("/foo", text);
284}
285
286TEST_F(KernelProxyTest, FDPathMapping) {
287  char text[1024];
288
289  int fd1, fd2, fd3, fd4, fd5;
290
291  EXPECT_EQ(0, ki_mkdir("/foo", S_IREAD | S_IWRITE));
292  EXPECT_EQ(0, ki_mkdir("/foo/bar", S_IREAD | S_IWRITE));
293  EXPECT_EQ(0, ki_mkdir("/example", S_IREAD | S_IWRITE));
294  ki_chdir("/foo");
295
296  fd1 = ki_open("/example", O_RDONLY, 0);
297  EXPECT_NE(-1, fd1);
298  EXPECT_EQ(ki_fchdir(fd1), 0);
299  EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
300  EXPECT_STREQ("/example", text);
301
302  EXPECT_EQ(0, ki_chdir("/foo"));
303  fd2 = ki_open("../example", O_RDONLY, 0);
304  EXPECT_NE(-1, fd2);
305  EXPECT_EQ(0, ki_fchdir(fd2));
306  EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
307  EXPECT_STREQ("/example", text);
308
309  EXPECT_EQ(0, ki_chdir("/foo"));
310  fd3 = ki_open("../test", O_CREAT | O_RDWR, 0777);
311  EXPECT_NE(-1, fd3);
312  EXPECT_EQ(-1, ki_fchdir(fd3));
313  EXPECT_EQ(ENOTDIR, errno);
314
315  EXPECT_EQ(0, ki_chdir("/foo"));
316  fd4 = ki_open("bar", O_RDONLY, 0);
317  EXPECT_EQ(0, ki_fchdir(fd4));
318  EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
319  EXPECT_STREQ("/foo/bar", text);
320  EXPECT_EQ(0, ki_chdir("/example"));
321  EXPECT_EQ(0, ki_fchdir(fd4));
322  EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
323  EXPECT_STREQ("/foo/bar", text);
324
325  EXPECT_EQ(0, ki_chdir("/example"));
326  fd5 = ki_dup(fd4);
327  ASSERT_GT(fd5, -1);
328  ASSERT_NE(fd4, fd5);
329  EXPECT_EQ(0, ki_fchdir(fd5));
330  EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
331  EXPECT_STREQ("/foo/bar", text);
332
333  fd5 = 123;
334
335  EXPECT_EQ(0, ki_chdir("/example"));
336  EXPECT_EQ(fd5, ki_dup2(fd4, fd5));
337  EXPECT_EQ(0, ki_fchdir(fd5));
338  EXPECT_EQ(text, ki_getcwd(text, sizeof(text)));
339  EXPECT_STREQ("/foo/bar", text);
340}
341
342TEST_F(KernelProxyTest, MemMountIO) {
343  char text[1024];
344  int fd1, fd2, fd3;
345  int len;
346
347  // Fail to delete non existant "/foo"
348  EXPECT_EQ(-1, ki_rmdir("/foo"));
349  EXPECT_EQ(ENOENT, errno);
350
351  // Create "/foo"
352  EXPECT_EQ(0, ki_mkdir("/foo", S_IREAD | S_IWRITE));
353  EXPECT_EQ(-1, ki_mkdir("/foo", S_IREAD | S_IWRITE));
354  EXPECT_EQ(EEXIST, errno);
355
356  // Delete "/foo"
357  EXPECT_EQ(0, ki_rmdir("/foo"));
358
359  // Recreate "/foo"
360  EXPECT_EQ(0, ki_mkdir("/foo", S_IREAD | S_IWRITE));
361
362  // Fail to open "/foo/bar"
363  EXPECT_EQ(-1, ki_open("/foo/bar", O_RDONLY, 0));
364  EXPECT_EQ(ENOENT, errno);
365
366  // Create bar "/foo/bar"
367  fd1 = ki_open("/foo/bar", O_RDWR | O_CREAT, 0777);
368  ASSERT_NE(-1, fd1);
369
370  // Open (optionally create) bar "/foo/bar"
371  fd2 = ki_open("/foo/bar", O_RDWR | O_CREAT, 0777);
372  ASSERT_NE(-1, fd2);
373
374  // Fail to exclusively create bar "/foo/bar"
375  EXPECT_EQ(-1, ki_open("/foo/bar", O_RDONLY | O_CREAT | O_EXCL, 0777));
376  EXPECT_EQ(EEXIST, errno);
377
378  // Write hello and world to same node with different descriptors
379  // so that we overwrite each other
380  EXPECT_EQ(5, ki_write(fd2, "WORLD", 5));
381  EXPECT_EQ(5, ki_write(fd1, "HELLO", 5));
382
383  fd3 = ki_open("/foo/bar", O_RDONLY, 0);
384  ASSERT_NE(-1, fd3);
385
386  len = ki_read(fd3, text, sizeof(text));
387  ASSERT_EQ(5, len);
388  text[len] = 0;
389  EXPECT_STREQ("HELLO", text);
390  EXPECT_EQ(0, ki_close(fd1));
391  EXPECT_EQ(0, ki_close(fd2));
392
393  fd1 = ki_open("/foo/bar", O_WRONLY | O_APPEND, 0);
394  ASSERT_NE(-1, fd1);
395  EXPECT_EQ(5, ki_write(fd1, "WORLD", 5));
396
397  len = ki_read(fd3, text, sizeof(text));
398  ASSERT_EQ(5, len);
399  text[len] = 0;
400  EXPECT_STREQ("WORLD", text);
401
402  fd2 = ki_open("/foo/bar", O_RDONLY, 0);
403  ASSERT_NE(-1, fd2);
404  len = ki_read(fd2, text, sizeof(text));
405  if (len > 0)
406    text[len] = 0;
407  EXPECT_EQ(10, len);
408  EXPECT_STREQ("HELLOWORLD", text);
409}
410
411TEST_F(KernelProxyTest, MemMountFTruncate) {
412  char text[1024];
413  int fd1, fd2;
414
415  // Open a file write only, write some text, then test that using a
416  // separate file descriptor pointing to it that it is correctly
417  // truncated at a specified number of bytes (2).
418  fd1 = ki_open("/trunc", O_WRONLY | O_CREAT, 0777);
419  ASSERT_NE(-1, fd1);
420  fd2 = ki_open("/trunc", O_RDONLY, 0);
421  ASSERT_NE(-1, fd2);
422  EXPECT_EQ(5, ki_write(fd1, "HELLO", 5));
423  EXPECT_EQ(0, ki_ftruncate(fd1, 2));
424  // Verify the remaining file (using fd2, opened pre-truncation) is
425  // only 2 bytes in length.
426  EXPECT_EQ(2, ki_read(fd2, text, sizeof(text)));
427  EXPECT_EQ(0, ki_close(fd1));
428  EXPECT_EQ(0, ki_close(fd2));
429}
430
431TEST_F(KernelProxyTest, MemMountTruncate) {
432  char text[1024];
433  int fd1;
434
435  // Open a file write only, write some text, then test that by
436  // referring to it by its path and truncating it we correctly truncate
437  // it at a specified number of bytes (2).
438  fd1 = ki_open("/trunc", O_WRONLY | O_CREAT, 0777);
439  ASSERT_NE(-1, fd1);
440  EXPECT_EQ(5, ki_write(fd1, "HELLO", 5));
441  EXPECT_EQ(0, ki_close(fd1));
442  EXPECT_EQ(0, ki_truncate("/trunc", 2));
443  // Verify the text is only 2 bytes long with new file descriptor.
444  fd1 = ki_open("/trunc", O_RDONLY, 0);
445  ASSERT_NE(-1, fd1);
446  EXPECT_EQ(2, ki_read(fd1, text, sizeof(text)));
447  EXPECT_EQ(0, ki_close(fd1));
448}
449
450TEST_F(KernelProxyTest, MemMountLseek) {
451  int fd = ki_open("/foo", O_CREAT | O_RDWR, 0777);
452  ASSERT_GT(fd, -1);
453  ASSERT_EQ(9, ki_write(fd, "Some text", 9));
454
455  ASSERT_EQ(9, ki_lseek(fd, 0, SEEK_CUR));
456  ASSERT_EQ(9, ki_lseek(fd, 0, SEEK_END));
457  ASSERT_EQ(-1, ki_lseek(fd, -1, SEEK_SET));
458  ASSERT_EQ(EINVAL, errno);
459
460  // Seek past end of file.
461  ASSERT_EQ(13, ki_lseek(fd, 13, SEEK_SET));
462  char buffer[4];
463  memset(&buffer[0], 0xfe, 4);
464  ASSERT_EQ(9, ki_lseek(fd, -4, SEEK_END));
465  ASSERT_EQ(9, ki_lseek(fd, 0, SEEK_CUR));
466  ASSERT_EQ(4, ki_read(fd, &buffer[0], 4));
467  ASSERT_EQ(0, memcmp("\0\0\0\0", buffer, 4));
468}
469
470TEST_F(KernelProxyTest, CloseTwice) {
471  int fd = ki_open("/foo", O_CREAT | O_RDWR, 0777);
472  ASSERT_GT(fd, -1);
473
474  EXPECT_EQ(9, ki_write(fd, "Some text", 9));
475
476  int fd2 = ki_dup(fd);
477  ASSERT_GT(fd2, -1);
478
479  EXPECT_EQ(0, ki_close(fd));
480  EXPECT_EQ(0, ki_close(fd2));
481}
482
483TEST_F(KernelProxyTest, MemMountDup) {
484  int fd = ki_open("/foo", O_CREAT | O_RDWR, 0777);
485  ASSERT_GT(fd, -1);
486
487  int dup_fd = ki_dup(fd);
488  ASSERT_NE(-1, dup_fd);
489
490  ASSERT_EQ(9, ki_write(fd, "Some text", 9));
491  ASSERT_EQ(9, ki_lseek(fd, 0, SEEK_CUR));
492  ASSERT_EQ(9, ki_lseek(dup_fd, 0, SEEK_CUR));
493
494  int dup2_fd = 123;
495  ASSERT_EQ(dup2_fd, ki_dup2(fd, dup2_fd));
496  ASSERT_EQ(9, ki_lseek(dup2_fd, 0, SEEK_CUR));
497
498  int new_fd = ki_open("/bar", O_CREAT | O_RDWR, 0777);
499
500  ASSERT_EQ(fd, ki_dup2(new_fd, fd));
501  // fd, new_fd -> "/bar"
502  // dup_fd, dup2_fd -> "/foo"
503
504  // We should still be able to write to dup_fd (i.e. it should not be closed).
505  ASSERT_EQ(4, ki_write(dup_fd, "more", 4));
506
507  ASSERT_EQ(0, ki_close(dup2_fd));
508  // fd, new_fd -> "/bar"
509  // dup_fd -> "/foo"
510
511  ASSERT_EQ(dup_fd, ki_dup2(fd, dup_fd));
512  // fd, new_fd, dup_fd -> "/bar"
513}
514
515TEST_F(KernelProxyTest, Lstat) {
516  int fd = ki_open("/foo", O_CREAT | O_RDWR, 0777);
517  ASSERT_GT(fd, -1);
518  ASSERT_EQ(0, ki_mkdir("/bar", S_IREAD | S_IWRITE));
519
520  struct stat buf;
521  EXPECT_EQ(0, ki_lstat("/foo", &buf));
522  EXPECT_EQ(0, buf.st_size);
523  EXPECT_TRUE(S_ISREG(buf.st_mode));
524
525  EXPECT_EQ(0, ki_lstat("/bar", &buf));
526  EXPECT_EQ(0, buf.st_size);
527  EXPECT_TRUE(S_ISDIR(buf.st_mode));
528
529  EXPECT_EQ(-1, ki_lstat("/no-such-file", &buf));
530  EXPECT_EQ(ENOENT, errno);
531}
532
533TEST_F(KernelProxyTest, OpenWithMode) {
534  int fd = ki_open("/foo", O_CREAT | O_RDWR, 0723);
535  ASSERT_GT(fd, -1);
536
537  struct stat buf;
538  EXPECT_EQ(0, ki_lstat("/foo", &buf));
539  EXPECT_EQ(0723, buf.st_mode & ~S_IFMT);
540}
541
542TEST_F(KernelProxyTest, UseAfterClose) {
543  int fd = ki_open("/dummy", O_CREAT | O_WRONLY, 0777);
544  ASSERT_GT(fd, -1);
545  EXPECT_EQ(5, ki_write(fd, "hello", 5));
546  EXPECT_EQ(0, ki_close(fd));
547  EXPECT_EQ(-1, ki_write(fd, "hello", 5));
548  EXPECT_EQ(EBADF, errno);
549}
550
551namespace {
552
553StringMap_t g_string_map;
554bool g_fs_ioctl_called;
555int g_fs_dev;
556
557class KernelProxyMountTest_Filesystem : public MemFs {
558 public:
559  virtual Error Init(const FsInitArgs& args) {
560    MemFs::Init(args);
561
562    g_string_map = args.string_map;
563    g_fs_dev = args.dev;
564
565    if (g_string_map.find("false") != g_string_map.end())
566      return EINVAL;
567    return 0;
568  }
569
570  virtual Error Filesystem_VIoctl(int request, va_list arglist) {
571    g_fs_ioctl_called = true;
572    return 0;
573  }
574
575  friend class TypedFsFactory<KernelProxyMountTest_Filesystem>;
576};
577
578class KernelProxyMountTest_KernelProxy : public KernelProxy {
579  virtual Error Init(PepperInterface* ppapi) {
580    KernelProxy::Init(NULL);
581    factories_["initfs"] = new TypedFsFactory<KernelProxyMountTest_Filesystem>;
582    return 0;
583  }
584};
585
586class KernelProxyMountTest : public ::testing::Test {
587 public:
588  KernelProxyMountTest() {}
589
590  void SetUp() {
591    g_string_map.clear();
592    g_fs_dev = -1;
593    g_fs_ioctl_called = false;
594
595    ASSERT_EQ(0, ki_push_state_for_testing());
596    ASSERT_EQ(0, ki_init(&kp_));
597  }
598
599  void TearDown() {
600    g_string_map.clear();
601    ki_uninit();
602  }
603
604 protected:
605  KernelProxyMountTest_KernelProxy kp_;
606};
607
608// Helper function for calling ki_ioctl without having
609// to construct a va_list.
610int ki_ioctl_wrapper(int fd, int request, ...) {
611  va_list ap;
612  va_start(ap, request);
613  int rtn = ki_ioctl(fd, request, ap);
614  va_end(ap);
615  return rtn;
616}
617
618}  // namespace
619
620TEST_F(KernelProxyMountTest, MountInit) {
621  int res1 = ki_mount("/", "/mnt1", "initfs", 0, "false,foo=bar");
622
623  EXPECT_EQ("bar", g_string_map["foo"]);
624  EXPECT_EQ(-1, res1);
625  EXPECT_EQ(EINVAL, errno);
626
627  int res2 = ki_mount("/", "/mnt2", "initfs", 0, "true,bar=foo,x=y");
628  EXPECT_NE(-1, res2);
629  EXPECT_EQ("y", g_string_map["x"]);
630}
631
632TEST_F(KernelProxyMountTest, MountAndIoctl) {
633  ASSERT_EQ(0, ki_mount("/", "/mnt1", "initfs", 0, ""));
634  ASSERT_NE(-1, g_fs_dev);
635
636  char path[100];
637  snprintf(path, 100, "dev/fs/%d", g_fs_dev);
638
639  int fd = ki_open(path, O_RDONLY, 0);
640  ASSERT_GT(fd, -1);
641
642  EXPECT_EQ(0, ki_ioctl_wrapper(fd, 0xdeadbeef));
643  EXPECT_EQ(true, g_fs_ioctl_called);
644}
645
646static void mount_callback(const char* source,
647                           const char* target,
648                           const char* filesystemtype,
649                           unsigned long mountflags,
650                           const void* data,
651                           dev_t dev,
652                           void* user_data) {
653  EXPECT_STREQ("/", source);
654  EXPECT_STREQ("/mnt1", target);
655  EXPECT_STREQ("initfs", filesystemtype);
656  EXPECT_EQ(0, mountflags);
657  EXPECT_STREQ("", (const char*) data);
658  EXPECT_EQ(g_fs_dev, dev);
659
660  bool* callback_called = static_cast<bool*>(user_data);
661  *callback_called = true;
662}
663
664TEST_F(KernelProxyMountTest, MountCallback) {
665  bool callback_called = false;
666  kp_.SetMountCallback(&mount_callback, &callback_called);
667  ASSERT_EQ(0, ki_mount("/", "/mnt1", "initfs", 0, ""));
668  ASSERT_NE(-1, g_fs_dev);
669  EXPECT_EQ(true, callback_called);
670}
671
672namespace {
673
674int g_MMapCount = 0;
675
676class KernelProxyMMapTest_Node : public Node {
677 public:
678  KernelProxyMMapTest_Node(Filesystem* filesystem)
679      : Node(filesystem), node_mmap_count_(0) {
680    EXPECT_EQ(0, Init(0));
681  }
682
683  virtual Error MMap(void* addr,
684                     size_t length,
685                     int prot,
686                     int flags,
687                     size_t offset,
688                     void** out_addr) {
689    node_mmap_count_++;
690    switch (g_MMapCount++) {
691      case 0:
692        *out_addr = reinterpret_cast<void*>(0x1000);
693        break;
694      case 1:
695        *out_addr = reinterpret_cast<void*>(0x2000);
696        break;
697      case 2:
698        *out_addr = reinterpret_cast<void*>(0x3000);
699        break;
700      default:
701        return EPERM;
702    }
703
704    return 0;
705  }
706
707 private:
708  int node_mmap_count_;
709};
710
711class KernelProxyMMapTest_Filesystem : public Filesystem {
712 public:
713  virtual Error OpenWithMode(const Path& path, int open_flags,
714                             mode_t mode, ScopedNode* out_node) {
715    out_node->reset(new KernelProxyMMapTest_Node(this));
716    return 0;
717  }
718
719  virtual Error OpenResource(const Path& path, ScopedNode* out_node) {
720    out_node->reset(NULL);
721    return ENOSYS;
722  }
723  virtual Error Unlink(const Path& path) { return ENOSYS; }
724  virtual Error Mkdir(const Path& path, int permissions) { return ENOSYS; }
725  virtual Error Rmdir(const Path& path) { return ENOSYS; }
726  virtual Error Remove(const Path& path) { return ENOSYS; }
727  virtual Error Rename(const Path& path, const Path& newpath) { return ENOSYS; }
728
729  friend class TypedFsFactory<KernelProxyMMapTest_Filesystem>;
730};
731
732class KernelProxyMMapTest_KernelProxy : public KernelProxy {
733  virtual Error Init(PepperInterface* ppapi) {
734    KernelProxy::Init(NULL);
735    factories_["mmapfs"] = new TypedFsFactory<KernelProxyMMapTest_Filesystem>;
736    return 0;
737  }
738};
739
740class KernelProxyMMapTest : public ::testing::Test {
741 public:
742  KernelProxyMMapTest() {}
743
744  void SetUp() {
745    ASSERT_EQ(0, ki_push_state_for_testing());
746    ASSERT_EQ(0, ki_init(&kp_));
747  }
748
749  void TearDown() { ki_uninit(); }
750
751 private:
752  KernelProxyMMapTest_KernelProxy kp_;
753};
754
755}  // namespace
756
757TEST_F(KernelProxyMMapTest, MMap) {
758  ASSERT_EQ(0, ki_umount("/"));
759  ASSERT_EQ(0, ki_mount("", "/", "mmapfs", 0, NULL));
760  int fd = ki_open("/file", O_RDWR | O_CREAT, 0777);
761  ASSERT_NE(-1, fd);
762
763  void* addr1 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0);
764  ASSERT_EQ(reinterpret_cast<void*>(0x1000), addr1);
765  ASSERT_EQ(1, g_MMapCount);
766
767  void* addr2 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0);
768  ASSERT_EQ(reinterpret_cast<void*>(0x2000), addr2);
769  ASSERT_EQ(2, g_MMapCount);
770
771  void* addr3 = ki_mmap(NULL, 0x800, PROT_READ, MAP_PRIVATE, fd, 0);
772  ASSERT_EQ(reinterpret_cast<void*>(0x3000), addr3);
773  ASSERT_EQ(3, g_MMapCount);
774
775  ki_close(fd);
776
777  // We no longer track mmap'd regions, so munmap is a no-op.
778  ASSERT_EQ(0, ki_munmap(reinterpret_cast<void*>(0x1000), 0x2800));
779  // We don't track regions, so the mmap count hasn't changed.
780  ASSERT_EQ(3, g_MMapCount);
781}
782
783namespace {
784
785class SingletonFsFactory : public FsFactory {
786 public:
787  SingletonFsFactory(const ScopedFilesystem& filesystem) : mount_(filesystem) {}
788
789  virtual Error CreateFilesystem(const FsInitArgs& args,
790                                 ScopedFilesystem* out_fs) {
791    *out_fs = mount_;
792    return 0;
793  }
794
795 private:
796  ScopedFilesystem mount_;
797};
798
799class KernelProxyErrorTest_KernelProxy : public KernelProxy {
800 public:
801  KernelProxyErrorTest_KernelProxy() : fs_(new MockFs) {}
802
803  virtual Error Init(PepperInterface* ppapi) {
804    KernelProxy::Init(ppapi);
805    factories_["testfs"] = new SingletonFsFactory(fs_);
806
807    EXPECT_CALL(*fs_, Destroy()).Times(1);
808    return 0;
809  }
810
811  ScopedRef<MockFs> fs() { return fs_; }
812
813 private:
814  ScopedRef<MockFs> fs_;
815};
816
817class KernelProxyErrorTest : public ::testing::Test {
818 public:
819  KernelProxyErrorTest() {}
820
821  void SetUp() {
822    ASSERT_EQ(0, ki_push_state_for_testing());
823    ASSERT_EQ(0, ki_init(&kp_));
824    // Unmount the passthrough FS and mount a testfs.
825    EXPECT_EQ(0, kp_.umount("/"));
826    EXPECT_EQ(0, kp_.mount("", "/", "testfs", 0, NULL));
827  }
828
829  void TearDown() { ki_uninit(); }
830
831  ScopedRef<MockFs> fs() { return kp_.fs(); }
832
833 private:
834  KernelProxyErrorTest_KernelProxy kp_;
835};
836
837}  // namespace
838
839TEST_F(KernelProxyErrorTest, WriteError) {
840  ScopedRef<MockFs> mock_fs(fs());
841  ScopedRef<MockNode> mock_node(new MockNode(&*mock_fs));
842  EXPECT_CALL(*mock_fs, OpenWithMode(_, _, _, _))
843      .WillOnce(DoAll(SetArgPointee<3>(mock_node), Return(0)));
844
845  EXPECT_CALL(*mock_node, Write(_, _, _, _))
846      .WillOnce(DoAll(SetArgPointee<3>(0),  // Wrote 0 bytes.
847                      Return(1234)));       // Returned error 1234.
848
849  EXPECT_CALL(*mock_node, Destroy()).Times(1);
850
851  int fd = ki_open("/dummy", O_WRONLY, 0);
852  EXPECT_NE(0, fd);
853
854  char buf[20];
855  EXPECT_EQ(-1, ki_write(fd, &buf[0], 20));
856  // The Filesystem should be able to return whatever error it wants and have it
857  // propagate through.
858  EXPECT_EQ(1234, errno);
859}
860
861TEST_F(KernelProxyErrorTest, ReadError) {
862  ScopedRef<MockFs> mock_fs(fs());
863  ScopedRef<MockNode> mock_node(new MockNode(&*mock_fs));
864  EXPECT_CALL(*mock_fs, OpenWithMode(_, _, _, _))
865      .WillOnce(DoAll(SetArgPointee<3>(mock_node), Return(0)));
866
867  EXPECT_CALL(*mock_node, Read(_, _, _, _))
868      .WillOnce(DoAll(SetArgPointee<3>(0),  // Read 0 bytes.
869                      Return(1234)));       // Returned error 1234.
870
871  EXPECT_CALL(*mock_node, Destroy()).Times(1);
872
873  int fd = ki_open("/dummy", O_RDONLY, 0);
874  EXPECT_NE(0, fd);
875
876  char buf[20];
877  EXPECT_EQ(-1, ki_read(fd, &buf[0], 20));
878  // The Filesystem should be able to return whatever error it wants and have it
879  // propagate through.
880  EXPECT_EQ(1234, errno);
881}
882