Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / tests / nacl_io_test / fuse_fs_test.cc
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 <fcntl.h>
6
7 #include <gtest/gtest.h>
8
9 #include <string>
10 #include <vector>
11
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"
18
19 using namespace nacl_io;
20
21 namespace {
22
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));
29   }
30 };
31
32 // Implementation of a simple flat memory filesystem.
33 struct File {
34   File() : mode(0666) { memset(&times, 0, sizeof(times)); }
35
36   std::string name;
37   std::vector<uint8_t> data;
38   mode_t mode;
39   timespec times[2];
40 };
41
42 typedef std::vector<File> Files;
43 Files g_files;
44
45 bool IsValidPath(const char* path) {
46   if (path == NULL)
47     return false;
48
49   if (strlen(path) <= 1)
50     return false;
51
52   if (path[0] != '/')
53     return false;
54
55   return true;
56 }
57
58 File* FindFile(const char* path) {
59   if (!IsValidPath(path))
60     return NULL;
61
62   for (Files::iterator iter = g_files.begin(); iter != g_files.end(); ++iter) {
63     if (iter->name == &path[1])
64       return &*iter;
65   }
66
67   return NULL;
68 }
69
70 int testfs_getattr(const char* path, struct stat* stbuf) {
71   memset(stbuf, 0, sizeof(struct stat));
72
73   if (strcmp(path, "/") == 0) {
74     stbuf->st_mode = S_IFDIR | 0755;
75     return 0;
76   }
77
78   File* file = FindFile(path);
79   if (file == NULL)
80     return -ENOENT;
81
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;
89 }
90
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;
98
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);
103   }
104   return 0;
105 }
106
107 int testfs_create(const char* path, mode_t mode, struct fuse_file_info* fi) {
108   if (!IsValidPath(path))
109     return -ENOENT;
110
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 /
119   }
120   file->mode = mode;
121
122   return 0;
123 }
124
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;
130 }
131
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;
140
141   size_t filesize = file->data.size();
142   // Trying to read past the end of the file.
143   if (offset >= filesize)
144     return 0;
145
146   if (offset + size > filesize)
147     size = filesize - offset;
148
149   memcpy(buf, file->data.data() + offset, size);
150   return size;
151 }
152
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;
161
162   size_t filesize = file->data.size();
163
164   if (offset + size > filesize)
165     file->data.resize(offset + size);
166
167   memcpy(file->data.data() + offset, buf, size);
168   return size;
169 }
170
171 int testfs_utimens(const char* path, const struct timespec times[2]) {
172   File* file = FindFile(path);
173   if (file == NULL)
174     return -ENOENT;
175
176   file->times[0] = times[0];
177   file->times[1] = times[1];
178   return 0;
179 }
180
181 int testfs_chmod(const char* path, mode_t mode) {
182   File* file = FindFile(path);
183   if (file == NULL)
184     return -ENOENT;
185
186   file->mode = mode;
187   return 0;
188 }
189
190 const char hello_world[] = "Hello, World!\n";
191
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
237 };
238
239 class FuseFsTest : public ::testing::Test {
240  public:
241   FuseFsTest();
242
243   void SetUp();
244
245  protected:
246   FuseFsForTesting fs_;
247 };
248
249 FuseFsTest::FuseFsTest() : fs_(&g_fuse_operations) {}
250
251 void FuseFsTest::SetUp() {
252   // Reset the filesystem.
253   g_files.clear();
254
255   // Add a built-in file.
256   size_t hello_len = strlen(hello_world);
257
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);
263 }
264
265 }  // namespace
266
267 TEST_F(FuseFsTest, OpenAndRead) {
268   ScopedNode node;
269   ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
270
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);
277
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);
283 }
284
285 TEST_F(FuseFsTest, CreateWithMode) {
286   ScopedNode node;
287   struct stat statbuf;
288
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);
294 }
295
296 TEST_F(FuseFsTest, CreateAndWrite) {
297   ScopedNode node;
298   ASSERT_EQ(0, fs_.Open(Path("/foobar"), O_RDWR | O_CREAT, &node));
299
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));
305
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);
312 }
313
314 TEST_F(FuseFsTest, GetStat) {
315   struct stat statbuf;
316   ScopedNode node;
317
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);
323
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);
328
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);
335 }
336
337 TEST_F(FuseFsTest, GetDents) {
338   ScopedNode root;
339
340   ASSERT_EQ(0, fs_.Open(Path("/"), O_RDONLY, &root));
341
342   struct dirent entries[4];
343   int bytes_read;
344
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);
351
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);
359
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);
369 }
370
371 TEST_F(FuseFsTest, Utimens) {
372   struct stat statbuf;
373   ScopedNode node;
374
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;
380
381   ASSERT_EQ(0, fs_.Open(Path("/hello"), O_RDONLY, &node));
382   EXPECT_EQ(0, node->Futimens(times));
383
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);
389 }
390
391 TEST_F(FuseFsTest, Fchmod) {
392   struct stat statbuf;
393   ScopedNode node;
394
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);
398
399   ASSERT_EQ(0, node->Fchmod(0777));
400
401   ASSERT_EQ(0, node->GetStat(&statbuf));
402   EXPECT_EQ(0777, statbuf.st_mode & S_MODEBITS);
403 }
404
405 namespace {
406
407 class KernelProxyFuseTest : public ::testing::Test {
408  public:
409   KernelProxyFuseTest() {}
410
411   void SetUp();
412   void TearDown();
413
414  private:
415   KernelProxy kp_;
416 };
417
418 void KernelProxyFuseTest::SetUp() {
419   ASSERT_EQ(0, ki_push_state_for_testing());
420   ASSERT_EQ(0, ki_init(&kp_));
421
422   // Register a fuse filesystem.
423   nacl_io_register_fs_type("flatfs", &g_fuse_operations);
424
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));
428 }
429
430 void KernelProxyFuseTest::TearDown() {
431   nacl_io_unregister_fs_type("flatfs");
432   ki_uninit();
433 }
434
435 }  // namespace
436
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));
444
445   // Then read it back in.
446   fd = ki_open("/hello", O_RDONLY, 0);
447   ASSERT_GT(fd, -1);
448
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));
454 }