Home | History | Annotate | Download | only in nacl_io_test
      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 
     26 using namespace nacl_io;
     27 
     28 static int s_alloc_num = 0;
     29 
     30 namespace {
     31 
     32 class 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 
     42 class 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 
     54 class 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 
     68 TEST(MemFsNodeTest, File) {
     69   MemFsNodeForTesting file;
     70   ScopedNode result_node;
     71   off_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_EQ(ENOTTY, 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 
    121 TEST(MemFsNodeTest, FTruncate) {
    122   MemFsNodeForTesting file;
    123   off_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 
    168 TEST(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 
    196 TEST(MemFsNodeTest, Directory) {
    197   s_alloc_num = 0;
    198   DirNodeForTesting root;
    199   ScopedNode result_node;
    200   off_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_EQ(ENOTTY, 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 
    289 TEST(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