Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / tests / nacl_io_test / fuse_fs_test.cc
blobaef35ced2e66e2e155637bc12b736e8de8ce0fe0
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.
5 #include <fcntl.h>
7 #include <gtest/gtest.h>
9 #include <string>
10 #include <vector>
12 #include "nacl_io/fuse.h"
13 #include "nacl_io/fusefs/fuse_fs.h"
14 #include "nacl_io/kernel_handle.h"
15 #include "nacl_io/kernel_intercept.h"
16 #include "nacl_io/kernel_proxy.h"
17 #include "nacl_io/ostime.h"
19 using namespace nacl_io;
21 namespace {
23 class FuseFsForTesting : public FuseFs {
24 public:
25 explicit FuseFsForTesting(fuse_operations* fuse_ops) {
26 FsInitArgs args;
27 args.fuse_ops = fuse_ops;
28 EXPECT_EQ(0, Init(args));
32 // Implementation of a simple flat memory filesystem.
33 struct File {
34 File() : mode(0666) { memset(&times, 0, sizeof(times)); }
36 std::string name;
37 std::vector<uint8_t> data;
38 mode_t mode;
39 timespec times[2];
42 typedef std::vector<File> Files;
43 Files g_files;
45 bool IsValidPath(const char* path) {
46 if (path == NULL)
47 return false;
49 if (strlen(path) <= 1)
50 return false;
52 if (path[0] != '/')
53 return false;
55 return true;
58 File* FindFile(const char* path) {
59 if (!IsValidPath(path))
60 return NULL;
62 for (Files::iterator iter = g_files.begin(); iter != g_files.end(); ++iter) {
63 if (iter->name == &path[1])
64 return &*iter;
67 return NULL;
70 int testfs_getattr(const char* path, struct stat* stbuf) {
71 memset(stbuf, 0, sizeof(struct stat));
73 if (strcmp(path, "/") == 0) {
74 stbuf->st_mode = S_IFDIR | 0755;
75 return 0;
78 if (strcmp(path, "/foo") == 0) {
79 stbuf->st_mode = S_IFDIR | 0755;
80 return 0;
83 File* file = FindFile(path);
84 if (file == NULL)
85 return -ENOENT;
87 stbuf->st_mode = S_IFREG | file->mode;
88 stbuf->st_size = file->data.size();
89 stbuf->st_atime = file->times[0].tv_sec;
90 stbuf->st_atimensec = file->times[0].tv_nsec;
91 stbuf->st_mtime = file->times[1].tv_sec;
92 stbuf->st_mtimensec = file->times[1].tv_nsec;
93 return 0;
96 int testfs_readdir(const char* path,
97 void* buf,
98 fuse_fill_dir_t filler,
99 off_t offset,
100 struct fuse_file_info*) {
101 if (strcmp(path, "/") != 0)
102 return -ENOENT;
104 filler(buf, ".", NULL, 0);
105 filler(buf, "..", NULL, 0);
106 for (Files::iterator iter = g_files.begin(); iter != g_files.end(); ++iter) {
107 filler(buf, iter->name.c_str(), NULL, 0);
109 return 0;
112 int testfs_create(const char* path, mode_t mode, struct fuse_file_info* fi) {
113 if (!IsValidPath(path))
114 return -ENOENT;
116 File* file = FindFile(path);
117 if (file != NULL) {
118 if (fi->flags & O_EXCL)
119 return -EEXIST;
120 } else {
121 g_files.push_back(File());
122 file = &g_files.back();
123 file->name = &path[1]; // Skip initial /
125 file->mode = mode;
127 return 0;
130 int testfs_open(const char* path, struct fuse_file_info*) {
131 // open is only called to open an existing file, otherwise create is
132 // called. We don't need to do any additional work here, the path will be
133 // passed to any other operations.
134 return FindFile(path) != NULL;
137 int testfs_read(const char* path,
138 char* buf,
139 size_t size,
140 off_t offset,
141 struct fuse_file_info* fi) {
142 File* file = FindFile(path);
143 if (file == NULL)
144 return -ENOENT;
146 size_t filesize = file->data.size();
147 // Trying to read past the end of the file.
148 if (offset >= filesize)
149 return 0;
151 if (offset + size > filesize)
152 size = filesize - offset;
154 memcpy(buf, file->data.data() + offset, size);
155 return size;
158 int testfs_write(const char* path,
159 const char* buf,
160 size_t size,
161 off_t offset,
162 struct fuse_file_info*) {
163 File* file = FindFile(path);
164 if (file == NULL)
165 return -ENOENT;
167 size_t filesize = file->data.size();
169 if (offset + size > filesize)
170 file->data.resize(offset + size);
172 memcpy(file->data.data() + offset, buf, size);
173 return size;
176 int testfs_utimens(const char* path, const struct timespec times[2]) {
177 File* file = FindFile(path);
178 if (file == NULL)
179 return -ENOENT;
181 file->times[0] = times[0];
182 file->times[1] = times[1];
183 return 0;
186 int testfs_chmod(const char* path, mode_t mode) {
187 File* file = FindFile(path);
188 if (file == NULL)
189 return -ENOENT;
191 file->mode = mode;
192 return 0;
195 const char hello_world[] = "Hello, World!\n";
197 fuse_operations g_fuse_operations = {
198 0, // flag_nopath
199 0, // flag_reserved
200 testfs_getattr, // getattr
201 NULL, // readlink
202 NULL, // mknod
203 NULL, // mkdir
204 NULL, // unlink
205 NULL, // rmdir
206 NULL, // symlink
207 NULL, // rename
208 NULL, // link
209 testfs_chmod, // chmod
210 NULL, // chown
211 NULL, // truncate
212 testfs_open, // open
213 testfs_read, // read
214 testfs_write, // write
215 NULL, // statfs
216 NULL, // flush
217 NULL, // release
218 NULL, // fsync
219 NULL, // setxattr
220 NULL, // getxattr
221 NULL, // listxattr
222 NULL, // removexattr
223 NULL, // opendir
224 testfs_readdir, // readdir
225 NULL, // releasedir
226 NULL, // fsyncdir
227 NULL, // init
228 NULL, // destroy
229 NULL, // access
230 testfs_create, // create
231 NULL, // ftruncate
232 NULL, // fgetattr
233 NULL, // lock
234 testfs_utimens, // utimens
235 NULL, // bmap
236 NULL, // ioctl
237 NULL, // poll
238 NULL, // write_buf
239 NULL, // read_buf
240 NULL, // flock
241 NULL, // fallocate
244 class FuseFsTest : public ::testing::Test {
245 public:
246 FuseFsTest();
248 void SetUp();
250 protected:
251 FuseFsForTesting fs_;
254 FuseFsTest::FuseFsTest() : fs_(&g_fuse_operations) {}
256 void FuseFsTest::SetUp() {
257 // Reset the filesystem.
258 g_files.clear();
260 // Add a built-in file.
261 size_t hello_len = strlen(hello_world);
263 File hello;
264 hello.name = "hello";
265 hello.data.resize(hello_len);
266 memcpy(hello.data.data(), hello_world, hello_len);
267 g_files.push_back(hello);
270 } // namespace
272 TEST_F(FuseFsTest, OpenAndRead) {
273 ScopedNode node;
274 ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
276 char buffer[15] = {0};
277 int bytes_read = 0;
278 HandleAttr attr;
279 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
280 ASSERT_EQ(strlen(hello_world), bytes_read);
281 ASSERT_STREQ(hello_world, buffer);
283 // Try to read past the end of the file.
284 attr.offs = strlen(hello_world) - 7;
285 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
286 ASSERT_EQ(7, bytes_read);
287 ASSERT_STREQ("World!\n", buffer);
290 TEST_F(FuseFsTest, CreateWithMode) {
291 ScopedNode node;
292 struct stat statbuf;
294 ASSERT_EQ(0, fs_.OpenWithMode(Path("/hello"),
295 O_RDWR | O_CREAT, 0723, &node));
296 EXPECT_EQ(0, node->GetStat(&statbuf));
297 EXPECT_TRUE(S_ISREG(statbuf.st_mode));
298 EXPECT_EQ(0723, statbuf.st_mode & S_MODEBITS);
301 TEST_F(FuseFsTest, CreateAndWrite) {
302 ScopedNode node;
303 ASSERT_EQ(0, fs_.Open(Path("/foobar"), O_RDWR | O_CREAT, &node));
305 HandleAttr attr;
306 const char message[] = "Something interesting";
307 int bytes_written;
308 ASSERT_EQ(0, node->Write(attr, &message[0], strlen(message), &bytes_written));
309 ASSERT_EQ(bytes_written, strlen(message));
311 // Now try to read the data back.
312 char buffer[40] = {0};
313 int bytes_read = 0;
314 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
315 ASSERT_EQ(strlen(message), bytes_read);
316 ASSERT_STREQ(message, buffer);
319 TEST_F(FuseFsTest, GetStat) {
320 struct stat statbuf;
321 ScopedNode node;
323 ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
324 EXPECT_EQ(0, node->GetStat(&statbuf));
325 EXPECT_TRUE(S_ISREG(statbuf.st_mode));
326 EXPECT_EQ(0666, statbuf.st_mode & S_MODEBITS);
327 EXPECT_EQ(strlen(hello_world), statbuf.st_size);
329 ASSERT_EQ(0, fs_.Open(Path("/"), O_RDONLY, &node));
330 EXPECT_EQ(0, node->GetStat(&statbuf));
331 EXPECT_TRUE(S_ISDIR(statbuf.st_mode));
332 EXPECT_EQ(0755, statbuf.st_mode & S_MODEBITS);
334 // Create a file and stat.
335 ASSERT_EQ(0, fs_.Open(Path("/foobar"), O_RDWR | O_CREAT, &node));
336 EXPECT_EQ(0, node->GetStat(&statbuf));
337 EXPECT_TRUE(S_ISREG(statbuf.st_mode));
338 EXPECT_EQ(0666, statbuf.st_mode & S_MODEBITS);
339 EXPECT_EQ(0, statbuf.st_size);
342 TEST_F(FuseFsTest, GetDents) {
343 ScopedNode root;
345 ASSERT_EQ(0, fs_.Open(Path("/"), O_RDONLY, &root));
347 struct dirent entries[4];
348 int bytes_read;
350 // Try reading everything.
351 ASSERT_EQ(0, root->GetDents(0, &entries[0], sizeof(entries), &bytes_read));
352 ASSERT_EQ(3 * sizeof(dirent), bytes_read);
353 EXPECT_STREQ(".", entries[0].d_name);
354 EXPECT_STREQ("..", entries[1].d_name);
355 EXPECT_STREQ("hello", entries[2].d_name);
357 // Try reading from an offset.
358 memset(&entries, 0, sizeof(entries));
359 ASSERT_EQ(0, root->GetDents(sizeof(dirent), &entries[0], 2 * sizeof(dirent),
360 &bytes_read));
361 ASSERT_EQ(2 * sizeof(dirent), bytes_read);
362 EXPECT_STREQ("..", entries[0].d_name);
363 EXPECT_STREQ("hello", entries[1].d_name);
365 // Add a file and read again.
366 ScopedNode node;
367 ASSERT_EQ(0, fs_.Open(Path("/foobar"), O_RDWR | O_CREAT, &node));
368 ASSERT_EQ(0, root->GetDents(0, &entries[0], sizeof(entries), &bytes_read));
369 ASSERT_EQ(4 * sizeof(dirent), bytes_read);
370 EXPECT_STREQ(".", entries[0].d_name);
371 EXPECT_STREQ("..", entries[1].d_name);
372 EXPECT_STREQ("hello", entries[2].d_name);
373 EXPECT_STREQ("foobar", entries[3].d_name);
376 TEST_F(FuseFsTest, Utimens) {
377 struct stat statbuf;
378 ScopedNode node;
380 struct timespec times[2];
381 times[0].tv_sec = 1000;
382 times[0].tv_nsec = 2000;
383 times[1].tv_sec = 3000;
384 times[1].tv_nsec = 4000;
386 ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
387 EXPECT_EQ(0, node->Futimens(times));
389 EXPECT_EQ(0, node->GetStat(&statbuf));
390 EXPECT_EQ(times[0].tv_sec, statbuf.st_atime);
391 EXPECT_EQ(times[0].tv_nsec, statbuf.st_atimensec);
392 EXPECT_EQ(times[1].tv_sec, statbuf.st_mtime);
393 EXPECT_EQ(times[1].tv_nsec, statbuf.st_mtimensec);
396 TEST_F(FuseFsTest, Fchmod) {
397 struct stat statbuf;
398 ScopedNode node;
400 ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
401 ASSERT_EQ(0, node->GetStat(&statbuf));
402 EXPECT_EQ(0666, statbuf.st_mode & S_MODEBITS);
404 ASSERT_EQ(0, node->Fchmod(0777));
406 ASSERT_EQ(0, node->GetStat(&statbuf));
407 EXPECT_EQ(0777, statbuf.st_mode & S_MODEBITS);
410 namespace {
412 class KernelProxyFuseTest : public ::testing::Test {
413 public:
414 KernelProxyFuseTest() {}
416 void SetUp();
417 void TearDown();
419 private:
420 KernelProxy kp_;
423 void KernelProxyFuseTest::SetUp() {
424 ASSERT_EQ(0, ki_push_state_for_testing());
425 ASSERT_EQ(0, ki_init(&kp_));
427 // Register a fuse filesystem.
428 nacl_io_register_fs_type("flatfs", &g_fuse_operations);
430 // Unmount the passthrough FS and mount our fuse filesystem.
431 EXPECT_EQ(0, kp_.umount("/"));
432 EXPECT_EQ(0, kp_.mount("", "/", "flatfs", 0, NULL));
435 void KernelProxyFuseTest::TearDown() {
436 nacl_io_unregister_fs_type("flatfs");
437 ki_uninit();
440 } // namespace
442 TEST_F(KernelProxyFuseTest, Basic) {
443 // Write a file.
444 int fd = ki_open("/hello", O_WRONLY | O_CREAT, 0777);
445 ASSERT_GT(fd, -1);
446 ASSERT_EQ(sizeof(hello_world),
447 ki_write(fd, hello_world, sizeof(hello_world)));
448 EXPECT_EQ(0, ki_close(fd));
450 // Then read it back in.
451 fd = ki_open("/hello", O_RDONLY, 0);
452 ASSERT_GT(fd, -1);
454 char buffer[30];
455 memset(buffer, 0, sizeof(buffer));
456 ASSERT_EQ(sizeof(hello_world), ki_read(fd, buffer, sizeof(buffer)));
457 EXPECT_STREQ(hello_world, buffer);
458 EXPECT_EQ(0, ki_close(fd));
461 TEST_F(KernelProxyFuseTest, Chdir) {
462 ASSERT_EQ(0, ki_chdir("/foo"));