[Do not revert] Roll-back V8 to version 4.4.63.
[chromium-blink-merge.git] / native_client_sdk / src / tests / nacl_io_test / fuse_fs_test.cc
blobca56602ec13aef476b33b21fa6fc2cb81968fbbc
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 File* file = FindFile(path);
79 if (file == NULL)
80 return -ENOENT;
82 stbuf->st_mode = S_IFREG | file->mode;
83 stbuf->st_size = file->data.size();
84 stbuf->st_atime = file->times[0].tv_sec;
85 stbuf->st_atimensec = file->times[0].tv_nsec;
86 stbuf->st_mtime = file->times[1].tv_sec;
87 stbuf->st_mtimensec = file->times[1].tv_nsec;
88 return 0;
91 int testfs_readdir(const char* path,
92 void* buf,
93 fuse_fill_dir_t filler,
94 off_t offset,
95 struct fuse_file_info*) {
96 if (strcmp(path, "/") != 0)
97 return -ENOENT;
99 filler(buf, ".", NULL, 0);
100 filler(buf, "..", NULL, 0);
101 for (Files::iterator iter = g_files.begin(); iter != g_files.end(); ++iter) {
102 filler(buf, iter->name.c_str(), NULL, 0);
104 return 0;
107 int testfs_create(const char* path, mode_t mode, struct fuse_file_info* fi) {
108 if (!IsValidPath(path))
109 return -ENOENT;
111 File* file = FindFile(path);
112 if (file != NULL) {
113 if (fi->flags & O_EXCL)
114 return -EEXIST;
115 } else {
116 g_files.push_back(File());
117 file = &g_files.back();
118 file->name = &path[1]; // Skip initial /
120 file->mode = mode;
122 return 0;
125 int testfs_open(const char* path, struct fuse_file_info*) {
126 // open is only called to open an existing file, otherwise create is
127 // called. We don't need to do any additional work here, the path will be
128 // passed to any other operations.
129 return FindFile(path) != NULL;
132 int testfs_read(const char* path,
133 char* buf,
134 size_t size,
135 off_t offset,
136 struct fuse_file_info* fi) {
137 File* file = FindFile(path);
138 if (file == NULL)
139 return -ENOENT;
141 size_t filesize = file->data.size();
142 // Trying to read past the end of the file.
143 if (offset >= filesize)
144 return 0;
146 if (offset + size > filesize)
147 size = filesize - offset;
149 memcpy(buf, file->data.data() + offset, size);
150 return size;
153 int testfs_write(const char* path,
154 const char* buf,
155 size_t size,
156 off_t offset,
157 struct fuse_file_info*) {
158 File* file = FindFile(path);
159 if (file == NULL)
160 return -ENOENT;
162 size_t filesize = file->data.size();
164 if (offset + size > filesize)
165 file->data.resize(offset + size);
167 memcpy(file->data.data() + offset, buf, size);
168 return size;
171 int testfs_utimens(const char* path, const struct timespec times[2]) {
172 File* file = FindFile(path);
173 if (file == NULL)
174 return -ENOENT;
176 file->times[0] = times[0];
177 file->times[1] = times[1];
178 return 0;
181 int testfs_chmod(const char* path, mode_t mode) {
182 File* file = FindFile(path);
183 if (file == NULL)
184 return -ENOENT;
186 file->mode = mode;
187 return 0;
190 const char hello_world[] = "Hello, World!\n";
192 fuse_operations g_fuse_operations = {
193 0, // flag_nopath
194 0, // flag_reserved
195 testfs_getattr, // getattr
196 NULL, // readlink
197 NULL, // mknod
198 NULL, // mkdir
199 NULL, // unlink
200 NULL, // rmdir
201 NULL, // symlink
202 NULL, // rename
203 NULL, // link
204 testfs_chmod, // chmod
205 NULL, // chown
206 NULL, // truncate
207 testfs_open, // open
208 testfs_read, // read
209 testfs_write, // write
210 NULL, // statfs
211 NULL, // flush
212 NULL, // release
213 NULL, // fsync
214 NULL, // setxattr
215 NULL, // getxattr
216 NULL, // listxattr
217 NULL, // removexattr
218 NULL, // opendir
219 testfs_readdir, // readdir
220 NULL, // releasedir
221 NULL, // fsyncdir
222 NULL, // init
223 NULL, // destroy
224 NULL, // access
225 testfs_create, // create
226 NULL, // ftruncate
227 NULL, // fgetattr
228 NULL, // lock
229 testfs_utimens, // utimens
230 NULL, // bmap
231 NULL, // ioctl
232 NULL, // poll
233 NULL, // write_buf
234 NULL, // read_buf
235 NULL, // flock
236 NULL, // fallocate
239 class FuseFsTest : public ::testing::Test {
240 public:
241 FuseFsTest();
243 void SetUp();
245 protected:
246 FuseFsForTesting fs_;
249 FuseFsTest::FuseFsTest() : fs_(&g_fuse_operations) {}
251 void FuseFsTest::SetUp() {
252 // Reset the filesystem.
253 g_files.clear();
255 // Add a built-in file.
256 size_t hello_len = strlen(hello_world);
258 File hello;
259 hello.name = "hello";
260 hello.data.resize(hello_len);
261 memcpy(hello.data.data(), hello_world, hello_len);
262 g_files.push_back(hello);
265 } // namespace
267 TEST_F(FuseFsTest, OpenAndRead) {
268 ScopedNode node;
269 ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
271 char buffer[15] = {0};
272 int bytes_read = 0;
273 HandleAttr attr;
274 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
275 ASSERT_EQ(strlen(hello_world), bytes_read);
276 ASSERT_STREQ(hello_world, buffer);
278 // Try to read past the end of the file.
279 attr.offs = strlen(hello_world) - 7;
280 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
281 ASSERT_EQ(7, bytes_read);
282 ASSERT_STREQ("World!\n", buffer);
285 TEST_F(FuseFsTest, CreateWithMode) {
286 ScopedNode node;
287 struct stat statbuf;
289 ASSERT_EQ(0, fs_.OpenWithMode(Path("/hello"),
290 O_RDWR | O_CREAT, 0723, &node));
291 EXPECT_EQ(0, node->GetStat(&statbuf));
292 EXPECT_TRUE(S_ISREG(statbuf.st_mode));
293 EXPECT_EQ(0723, statbuf.st_mode & S_MODEBITS);
296 TEST_F(FuseFsTest, CreateAndWrite) {
297 ScopedNode node;
298 ASSERT_EQ(0, fs_.Open(Path("/foobar"), O_RDWR | O_CREAT, &node));
300 HandleAttr attr;
301 const char message[] = "Something interesting";
302 int bytes_written;
303 ASSERT_EQ(0, node->Write(attr, &message[0], strlen(message), &bytes_written));
304 ASSERT_EQ(bytes_written, strlen(message));
306 // Now try to read the data back.
307 char buffer[40] = {0};
308 int bytes_read = 0;
309 ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
310 ASSERT_EQ(strlen(message), bytes_read);
311 ASSERT_STREQ(message, buffer);
314 TEST_F(FuseFsTest, GetStat) {
315 struct stat statbuf;
316 ScopedNode node;
318 ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
319 EXPECT_EQ(0, node->GetStat(&statbuf));
320 EXPECT_TRUE(S_ISREG(statbuf.st_mode));
321 EXPECT_EQ(0666, statbuf.st_mode & S_MODEBITS);
322 EXPECT_EQ(strlen(hello_world), statbuf.st_size);
324 ASSERT_EQ(0, fs_.Open(Path("/"), O_RDONLY, &node));
325 EXPECT_EQ(0, node->GetStat(&statbuf));
326 EXPECT_TRUE(S_ISDIR(statbuf.st_mode));
327 EXPECT_EQ(0755, statbuf.st_mode & S_MODEBITS);
329 // Create a file and stat.
330 ASSERT_EQ(0, fs_.Open(Path("/foobar"), O_RDWR | O_CREAT, &node));
331 EXPECT_EQ(0, node->GetStat(&statbuf));
332 EXPECT_TRUE(S_ISREG(statbuf.st_mode));
333 EXPECT_EQ(0666, statbuf.st_mode & S_MODEBITS);
334 EXPECT_EQ(0, statbuf.st_size);
337 TEST_F(FuseFsTest, GetDents) {
338 ScopedNode root;
340 ASSERT_EQ(0, fs_.Open(Path("/"), O_RDONLY, &root));
342 struct dirent entries[4];
343 int bytes_read;
345 // Try reading everything.
346 ASSERT_EQ(0, root->GetDents(0, &entries[0], sizeof(entries), &bytes_read));
347 ASSERT_EQ(3 * sizeof(dirent), bytes_read);
348 EXPECT_STREQ(".", entries[0].d_name);
349 EXPECT_STREQ("..", entries[1].d_name);
350 EXPECT_STREQ("hello", entries[2].d_name);
352 // Try reading from an offset.
353 memset(&entries, 0, sizeof(entries));
354 ASSERT_EQ(0, root->GetDents(sizeof(dirent), &entries[0], 2 * sizeof(dirent),
355 &bytes_read));
356 ASSERT_EQ(2 * sizeof(dirent), bytes_read);
357 EXPECT_STREQ("..", entries[0].d_name);
358 EXPECT_STREQ("hello", entries[1].d_name);
360 // Add a file and read again.
361 ScopedNode node;
362 ASSERT_EQ(0, fs_.Open(Path("/foobar"), O_RDWR | O_CREAT, &node));
363 ASSERT_EQ(0, root->GetDents(0, &entries[0], sizeof(entries), &bytes_read));
364 ASSERT_EQ(4 * sizeof(dirent), bytes_read);
365 EXPECT_STREQ(".", entries[0].d_name);
366 EXPECT_STREQ("..", entries[1].d_name);
367 EXPECT_STREQ("hello", entries[2].d_name);
368 EXPECT_STREQ("foobar", entries[3].d_name);
371 TEST_F(FuseFsTest, Utimens) {
372 struct stat statbuf;
373 ScopedNode node;
375 struct timespec times[2];
376 times[0].tv_sec = 1000;
377 times[0].tv_nsec = 2000;
378 times[1].tv_sec = 3000;
379 times[1].tv_nsec = 4000;
381 ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
382 EXPECT_EQ(0, node->Futimens(times));
384 EXPECT_EQ(0, node->GetStat(&statbuf));
385 EXPECT_EQ(times[0].tv_sec, statbuf.st_atime);
386 EXPECT_EQ(times[0].tv_nsec, statbuf.st_atimensec);
387 EXPECT_EQ(times[1].tv_sec, statbuf.st_mtime);
388 EXPECT_EQ(times[1].tv_nsec, statbuf.st_mtimensec);
391 TEST_F(FuseFsTest, Fchmod) {
392 struct stat statbuf;
393 ScopedNode node;
395 ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
396 ASSERT_EQ(0, node->GetStat(&statbuf));
397 EXPECT_EQ(0666, statbuf.st_mode & S_MODEBITS);
399 ASSERT_EQ(0, node->Fchmod(0777));
401 ASSERT_EQ(0, node->GetStat(&statbuf));
402 EXPECT_EQ(0777, statbuf.st_mode & S_MODEBITS);
405 namespace {
407 class KernelProxyFuseTest : public ::testing::Test {
408 public:
409 KernelProxyFuseTest() {}
411 void SetUp();
412 void TearDown();
414 private:
415 KernelProxy kp_;
418 void KernelProxyFuseTest::SetUp() {
419 ASSERT_EQ(0, ki_push_state_for_testing());
420 ASSERT_EQ(0, ki_init(&kp_));
422 // Register a fuse filesystem.
423 nacl_io_register_fs_type("flatfs", &g_fuse_operations);
425 // Unmount the passthrough FS and mount our fuse filesystem.
426 EXPECT_EQ(0, kp_.umount("/"));
427 EXPECT_EQ(0, kp_.mount("", "/", "flatfs", 0, NULL));
430 void KernelProxyFuseTest::TearDown() {
431 nacl_io_unregister_fs_type("flatfs");
432 ki_uninit();
435 } // namespace
437 TEST_F(KernelProxyFuseTest, Basic) {
438 // Write a file.
439 int fd = ki_open("/hello", O_WRONLY | O_CREAT, 0777);
440 ASSERT_GT(fd, -1);
441 ASSERT_EQ(sizeof(hello_world),
442 ki_write(fd, hello_world, sizeof(hello_world)));
443 EXPECT_EQ(0, ki_close(fd));
445 // Then read it back in.
446 fd = ki_open("/hello", O_RDONLY, 0);
447 ASSERT_GT(fd, -1);
449 char buffer[30];
450 memset(buffer, 0, sizeof(buffer));
451 ASSERT_EQ(sizeof(hello_world), ki_read(fd, buffer, sizeof(buffer)));
452 EXPECT_STREQ(hello_world, buffer);
453 EXPECT_EQ(0, ki_close(fd));