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