summaryrefslogtreecommitdiffstats
path: root/native_client_sdk
diff options
context:
space:
mode:
authorbinji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-11 20:24:29 +0000
committerbinji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-12-11 20:24:29 +0000
commit2b6fc834dc9c17213eb51dc7685340c7cd65ab14 (patch)
tree226defb51a6f9c6385cb0ed300285fae4264c960 /native_client_sdk
parent726b63668d783e1e53c6a4d6e0102075a8c28143 (diff)
downloadchromium_src-2b6fc834dc9c17213eb51dc7685340c7cd65ab14.zip
chromium_src-2b6fc834dc9c17213eb51dc7685340c7cd65ab14.tar.gz
chromium_src-2b6fc834dc9c17213eb51dc7685340c7cd65ab14.tar.bz2
[NaCl SDK] Add FUSE mount.
This is an initial implementation of the FUSE interface, to allow extending the types of mounts nacl_io supports. BUG=none R=sbc@chromium.org Review URL: https://codereview.chromium.org/108803003 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@240167 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
-rw-r--r--native_client_sdk/src/libraries/nacl_io/fuse.h221
-rw-r--r--native_client_sdk/src/libraries/nacl_io/fuse_mount_factory.cc28
-rw-r--r--native_client_sdk/src/libraries/nacl_io/fuse_mount_factory.h27
-rw-r--r--native_client_sdk/src/libraries/nacl_io/getdents_helper.cc17
-rw-r--r--native_client_sdk/src/libraries/nacl_io/getdents_helper.h3
-rw-r--r--native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc9
-rw-r--r--native_client_sdk/src/libraries/nacl_io/kernel_intercept.h5
-rw-r--r--native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc34
-rw-r--r--native_client_sdk/src/libraries/nacl_io/kernel_proxy.h6
-rw-r--r--native_client_sdk/src/libraries/nacl_io/library.dsc5
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount.cc6
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount.h19
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_dev.cc4
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_dev.h2
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_factory.h10
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_fuse.cc462
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_fuse.h122
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc15
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_html5fs.h2
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_http.cc8
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_http.h2
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_mem.cc4
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_mem.h3
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_node.h1
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc6
-rw-r--r--native_client_sdk/src/libraries/nacl_io/mount_passthrough.h2
-rw-r--r--native_client_sdk/src/libraries/nacl_io/nacl_io.cc8
-rw-r--r--native_client_sdk/src/libraries/nacl_io/nacl_io.h61
-rw-r--r--native_client_sdk/src/libraries/nacl_io/typed_mount_factory.h6
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/example.dsc1
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc18
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/mount_dev_mock.h3
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/mount_fuse_test.cc345
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc7
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc5
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/mount_mock.h3
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc4
-rw-r--r--native_client_sdk/src/tests/nacl_io_test/mount_test.cc4
38 files changed, 1423 insertions, 65 deletions
diff --git a/native_client_sdk/src/libraries/nacl_io/fuse.h b/native_client_sdk/src/libraries/nacl_io/fuse.h
new file mode 100644
index 0000000..b7f52d4
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_io/fuse.h
@@ -0,0 +1,221 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBRARIES_NACL_IO_FUSE_H_
+#define LIBRARIES_NACL_IO_FUSE_H_
+
+#include "osinttypes.h"
+#include "ostypes.h"
+
+// These interfaces are copied from the FUSE library.
+//
+// FUSE has two interfaces that can be implemented: low-level and high-level.
+// In nacl_io, we only support the high-level interface.
+//
+// See http://fuse.sourceforge.net/ for more information.
+
+// This struct is typically passed to functions that would normally use return
+// or receive an fd; that is, operations to open/create a node, or operations
+// that act on an already opened node.
+struct fuse_file_info {
+ // This is filled with the flags passed to open()
+ int flags;
+ // Deprecated in FUSE. Use fh instead.
+ unsigned long fh_old;
+ int writepage;
+ // Currently unsupported
+ unsigned int direct_io : 1;
+ // Currently unsupported
+ unsigned int keep_cache : 1;
+ // Currently unsupported
+ unsigned int flush : 1;
+ // Currently unsupported
+ unsigned int nonseekable : 1;
+ // Currently unsupported
+ unsigned int padding : 27;
+ // This value is not used by nacl_io. It can be filled by the developer when
+ // open() is called, and reused for subsequent calls on the same node.
+ uint64_t fh;
+ // Currently unsupported
+ uint64_t lock_owner;
+ // Currently unsupported
+ uint32_t poll_events;
+};
+
+// A dummy structure that currently exists only to match the FUSE interface.
+struct fuse_conn_info {};
+
+// A function of this type will be passed to readdir (see below). The developer
+// should call this function once for each directory entry.
+//
+// See the documentation for readdir() below for more information on how to use
+// this function.
+typedef int (*fuse_fill_dir_t)(void* buf,
+ const char* name,
+ const struct stat* stbuf,
+ off_t off);
+
+// This structure defines the interface to create a user mount. Pass this to
+// nacl_io_register_mount_type(). (see nacl_io.h)
+//
+// Example:
+//
+// struct fuse_operations g_my_fuse_operations = { ... };
+// ...
+// nacl_io_register_mount_type("myfusefs", &g_my_fuse_operations);
+// ...
+// mount("", "/mnt/fuse", "myfusefs", 0, NULL);
+//
+// It is not necessary to implement every function -- nacl_io will first check
+// if the function pointer is NULL before calling it. If it is NULL and
+// required by the current operation, the call will fail and return ENOSYS in
+// errno.
+//
+// Except where specified below, each function should return one of the
+// following values:
+// == 0: operation completed successfully.
+// < 0: operation failed. The error is a negative errno. e.g. -EACCES, -EPERM,
+// etc. The sign will be flipped when the error is actually set.
+//
+// Some functions (e.g. read, write) also return a positive count, which is the
+// number of bytes read/written.
+//
+struct fuse_operations {
+ // Currently unsupported
+ unsigned int flag_nopath : 1;
+ unsigned int flag_reserved : 31;
+
+ // Called when a mount of this type is initialized.
+ void* (*init)(struct fuse_conn_info* conn);
+ // Called when a mount of this type is unmounted.
+ void (*destroy)(void*);
+ // Called by access()
+ int (*access)(const char* path, int mode);
+ // Called when O_CREAT is passed to open()
+ int (*create)(const char* path, mode_t mode, struct fuse_file_info*);
+ // Called by stat()/fstat(). If this function pointer is non-NULL, it is
+ // called, otherwise fuse_operations.getattr will be called.
+ int (*fgetattr)(const char* path, struct stat*, struct fuse_file_info*);
+ // Called by fsync(). The datasync paramater is not currently supported.
+ int (*fsync)(const char* path, int datasync, struct fuse_file_info*);
+ // Called by ftruncate()
+ int (*ftruncate)(const char* path, off_t, struct fuse_file_info*);
+ // Called by stat()/fstat(), but only when fuse_operations.fgetattr is NULL.
+ // Also called by open() to determine if the path is a directory or a regular
+ // file.
+ int (*getattr)(const char* path, struct stat*);
+ // Called by mkdir()
+ int (*mkdir)(const char* path, mode_t);
+ // Called when O_CREAT is passed to open(), but only if fuse_operations.create
+ // is non-NULL.
+ int (*mknod)(const char* path, mode_t, dev_t);
+ // Called by open()
+ int (*open)(const char* path, struct fuse_file_info*);
+ // Called by getdents(), which is called by the more standard functions
+ // opendir()/readdir().
+ int (*opendir)(const char* path, struct fuse_file_info*);
+ // Called by read(). Note that FUSE specifies that all reads will fill the
+ // entire requested buffer. If this function returns less than that, the
+ // remainder of the buffer is zeroed.
+ int (*read)(const char* path, char* buf, size_t count, off_t,
+ struct fuse_file_info*);
+ // Called by getdents(), which is called by the more standard function
+ // readdir().
+ //
+ // NOTE: it is the responsibility of this function to add the "." and ".."
+ // entries.
+ //
+ // This function can be implemented one of two ways:
+ // 1) Ignore the offset, and always write every entry in a given directory.
+ // In this case, you should always call filler() with an offset of 0. You
+ // can ignore the return value of the filler.
+ //
+ // int my_readdir(const char* path, void* buf, fuse_fill_dir_t filler,
+ // off_t offset, struct fuse_file_info*) {
+ // ...
+ // filler(buf, ".", NULL, 0);
+ // filler(buf, "..", NULL, 0);
+ // filler(buf, "file1", &file1stat, 0);
+ // filler(buf, "file2", &file2stat, 0);
+ // return 0;
+ // }
+ //
+ // 2) Only write entries starting from offset. Always pass the correct offset
+ // to the filler function. When the filler function returns 1, the buffer
+ // is full so you can exit readdir.
+ //
+ // int my_readdir(const char* path, void* buf, fuse_fill_dir_t filler,
+ // off_t offset, struct fuse_file_info*) {
+ // ...
+ // size_t kNumEntries = 4;
+ // const char* my_entries[] = { ".", "..", "file1", "file2" };
+ // int entry_index = offset / sizeof(dirent);
+ // offset = entry_index * sizeof(dirent);
+ // while (entry_index < kNumEntries) {
+ // int result = filler(buf, my_entries[entry_index], NULL, offset);
+ // if (filler == 1) {
+ // // buffer filled, we're done.
+ // return 0;
+ // }
+ // offset += sizeof(dirent);
+ // entry_index++;
+ // }
+ //
+ // // No more entries, we're done.
+ // return 0;
+ // }
+ //
+ int (*readdir)(const char* path, void* buf, fuse_fill_dir_t filldir, off_t,
+ struct fuse_file_info*);
+ // Called when the last reference to this node is released. This is only
+ // called for regular files. For directories, fuse_operations.releasedir is
+ // called instead.
+ int (*release)(const char* path, struct fuse_file_info*);
+ // Called when the last reference to this node is released. This is only
+ // called for directories. For regular files, fuse_operations.release is
+ // called instead.
+ int (*releasedir)(const char* path, struct fuse_file_info*);
+ // Called by rename()
+ int (*rename)(const char* path, const char* new_path);
+ // Called by rmdir()
+ int (*rmdir)(const char* path);
+ // Called by truncate(), as well as open() when O_TRUNC is passed.
+ int (*truncate)(const char* path, off_t);
+ // Called by unlink()
+ int (*unlink)(const char* path);
+ // Called by write(). Note that FUSE specifies that a write should always
+ // return the full count, unless an error occurs.
+ int (*write)(const char* path, const char* buf, size_t count, off_t,
+ struct fuse_file_info*);
+
+ // The following functions are not currently called by the nacl_io
+ // implementation of FUSE.
+ int (*bmap)(const char*, size_t blocksize, uint64_t* idx);
+ int (*chmod)(const char*, mode_t);
+ int (*chown)(const char*, uid_t, gid_t);
+ int (*fallocate)(const char*, int, off_t, off_t, struct fuse_file_info*);
+ int (*flock)(const char*, struct fuse_file_info*, int op);
+ int (*flush)(const char*, struct fuse_file_info*);
+ int (*fsyncdir)(const char*, int, struct fuse_file_info*);
+ int (*getxattr)(const char*, const char*, char*, size_t);
+ int (*ioctl)(const char*, int cmd, void* arg, struct fuse_file_info*,
+ unsigned int flags, void* data);
+ int (*link)(const char*, const char*);
+ int (*listxattr)(const char*, char*, size_t);
+ int (*lock)(const char*, struct fuse_file_info*, int cmd, struct flock*);
+ int (*poll)(const char*, struct fuse_file_info*, struct fuse_pollhandle* ph,
+ unsigned* reventsp);
+ int (*read_buf)(const char*, struct fuse_bufvec** bufp, size_t size,
+ off_t off, struct fuse_file_info*);
+ int (*readlink)(const char*, char*, size_t);
+ int (*removexattr)(const char*, const char*);
+ int (*setxattr)(const char*, const char*, const char*, size_t, int);
+ int (*statfs)(const char*, struct statvfs*);
+ int (*symlink)(const char*, const char*);
+ int (*utimens)(const char*, const struct timespec tv[2]);
+ int (*write_buf)(const char*, struct fuse_bufvec* buf, off_t off,
+ struct fuse_file_info*);
+};
+
+#endif // LIBRARIES_NACL_IO_FUSE_H_
diff --git a/native_client_sdk/src/libraries/nacl_io/fuse_mount_factory.cc b/native_client_sdk/src/libraries/nacl_io/fuse_mount_factory.cc
new file mode 100644
index 0000000..c527883
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_io/fuse_mount_factory.cc
@@ -0,0 +1,28 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "nacl_io/fuse_mount_factory.h"
+
+#include "nacl_io/mount_fuse.h"
+
+namespace nacl_io {
+
+FuseMountFactory::FuseMountFactory(fuse_operations* fuse_ops)
+ : fuse_ops_(fuse_ops) {}
+
+Error FuseMountFactory::CreateMount(const MountInitArgs& args,
+ ScopedMount* out_mount) {
+ MountInitArgs args_copy(args);
+ args_copy.fuse_ops = fuse_ops_;
+
+ sdk_util::ScopedRef<MountFuse> mnt(new MountFuse());
+ Error error = mnt->Init(args_copy);
+ if (error)
+ return error;
+
+ *out_mount = mnt;
+ return 0;
+}
+
+} // namespace nacl_io
diff --git a/native_client_sdk/src/libraries/nacl_io/fuse_mount_factory.h b/native_client_sdk/src/libraries/nacl_io/fuse_mount_factory.h
new file mode 100644
index 0000000..24ab4e4
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_io/fuse_mount_factory.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBRARIES_NACL_IO_FUSE_MOUNT_FACTORY_H_
+#define LIBRARIES_NACL_IO_FUSE_MOUNT_FACTORY_H_
+
+#include "nacl_io/mount.h"
+#include "nacl_io/mount_factory.h"
+
+struct fuse_operations;
+
+namespace nacl_io {
+
+class FuseMountFactory : public MountFactory {
+ public:
+ explicit FuseMountFactory(fuse_operations* fuse_ops);
+ virtual Error CreateMount(const MountInitArgs& args,
+ ScopedMount* out_mount);
+
+ private:
+ fuse_operations* fuse_ops_;
+};
+
+} // namespace nacl_io
+
+#endif // LIBRARIES_NACL_IO_FUSE_MOUNT_FACTORY_H_
diff --git a/native_client_sdk/src/libraries/nacl_io/getdents_helper.cc b/native_client_sdk/src/libraries/nacl_io/getdents_helper.cc
index 6b7098d..87dc32d 100644
--- a/native_client_sdk/src/libraries/nacl_io/getdents_helper.cc
+++ b/native_client_sdk/src/libraries/nacl_io/getdents_helper.cc
@@ -14,8 +14,15 @@
namespace nacl_io {
+GetDentsHelper::GetDentsHelper()
+ : curdir_ino_(0), parentdir_ino_(0), init_defaults_(false) {
+ Initialize();
+}
+
GetDentsHelper::GetDentsHelper(ino_t curdir_ino, ino_t parentdir_ino)
- : curdir_ino_(curdir_ino), parentdir_ino_(parentdir_ino) {
+ : curdir_ino_(curdir_ino),
+ parentdir_ino_(parentdir_ino),
+ init_defaults_(true) {
Initialize();
}
@@ -25,9 +32,11 @@ void GetDentsHelper::Reset() {
}
void GetDentsHelper::Initialize() {
- // Add the default entries: "." and ".."
- AddDirent(curdir_ino_, ".", 1);
- AddDirent(parentdir_ino_, "..", 2);
+ if (init_defaults_) {
+ // Add the default entries: "." and ".."
+ AddDirent(curdir_ino_, ".", 1);
+ AddDirent(parentdir_ino_, "..", 2);
+ }
}
void GetDentsHelper::AddDirent(ino_t ino, const char* name, size_t namelen) {
diff --git a/native_client_sdk/src/libraries/nacl_io/getdents_helper.h b/native_client_sdk/src/libraries/nacl_io/getdents_helper.h
index ca611c6..0d5d34f 100644
--- a/native_client_sdk/src/libraries/nacl_io/getdents_helper.h
+++ b/native_client_sdk/src/libraries/nacl_io/getdents_helper.h
@@ -14,6 +14,8 @@ namespace nacl_io {
class GetDentsHelper {
public:
+ // Initialize the helper without any defaults.
+ GetDentsHelper();
GetDentsHelper(ino_t curdir_ino, ino_t parentdir_ino);
void Reset();
@@ -26,6 +28,7 @@ class GetDentsHelper {
std::vector<dirent> dirents_;
ino_t curdir_ino_;
ino_t parentdir_ino_;
+ bool init_defaults_;
};
} // namespace nacl_io
diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc
index fa661a5..b5f894c 100644
--- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc
+++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.cc
@@ -50,6 +50,15 @@ void ki_init_ppapi(void* kp,
s_kp->Init(ppapi);
}
+int ki_register_mount_type(const char* mount_type,
+ struct fuse_operations* fuse_ops) {
+ return s_kp->RegisterMountType(mount_type, fuse_ops);
+}
+
+int ki_unregister_mount_type(const char* mount_type) {
+ return s_kp->UnregisterMountType(mount_type);
+}
+
int ki_is_initialized() {
return s_kp != NULL;
}
diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h
index 5b987ed..b135213 100644
--- a/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h
+++ b/native_client_sdk/src/libraries/nacl_io/kernel_intercept.h
@@ -21,6 +21,8 @@
EXTERN_C_BEGIN
+struct fuse_operations;
+
// The kernel intercept module provides a C->C++ thunk between the libc
// kernel calls and the KernelProxy singleton.
@@ -31,6 +33,9 @@ void ki_init(void* kernel_proxy);
void ki_init_ppapi(void* kernel_proxy,
PP_Instance instance,
PPB_GetInterface get_browser_interface);
+int ki_register_mount_type(const char* mount_type,
+ struct fuse_operations* fuse_ops);
+int ki_unregister_mount_type(const char* mount_type);
int ki_is_initialized();
void ki_uninit();
diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc
index 83023c0..b18f2ad 100644
--- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc
+++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc
@@ -18,6 +18,7 @@
#include <iterator>
#include <string>
+#include "nacl_io/fuse_mount_factory.h"
#include "nacl_io/host_resolver.h"
#include "nacl_io/kernel_handle.h"
#include "nacl_io/kernel_wrap_real.h"
@@ -111,9 +112,11 @@ Error KernelProxy::Init(PepperInterface* ppapi) {
host_resolver_.Init(ppapi_);
#endif
- StringMap_t args;
+ MountInitArgs args;
+ args.dev = dev_++;
+ args.ppapi = ppapi_;
stream_mount_.reset(new MountStream());
- result = stream_mount_->Init(0, args, ppapi);
+ result = stream_mount_->Init(args);
if (result != 0) {
assert(false);
rtn = result;
@@ -122,6 +125,26 @@ Error KernelProxy::Init(PepperInterface* ppapi) {
return rtn;
}
+bool KernelProxy::RegisterMountType(const char* mount_type,
+ fuse_operations* fuse_ops) {
+ MountFactoryMap_t::iterator iter = factories_.find(mount_type);
+ if (iter != factories_.end())
+ return false;
+
+ factories_[mount_type] = new FuseMountFactory(fuse_ops);
+ return true;
+}
+
+bool KernelProxy::UnregisterMountType(const char* mount_type) {
+ MountFactoryMap_t::iterator iter = factories_.find(mount_type);
+ if (iter == factories_.end())
+ return false;
+
+ delete iter->second;
+ factories_.erase(iter);
+ return true;
+}
+
int KernelProxy::open_resource(const char* path) {
ScopedMount mnt;
Path rel;
@@ -392,8 +415,13 @@ int KernelProxy::mount(const char* source,
}
}
+ MountInitArgs args;
+ args.dev = dev_++;
+ args.string_map = smap;
+ args.ppapi = ppapi_;
+
ScopedMount mnt;
- Error error = factory->second->CreateMount(dev_++, smap, ppapi_, &mnt);
+ Error error = factory->second->CreateMount(args, &mnt);
if (error) {
errno = error;
return -1;
diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h
index 788370a..e037110 100644
--- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h
+++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h
@@ -18,6 +18,7 @@
#include "nacl_io/ostypes.h"
#include "nacl_io/osutime.h"
+struct fuse_operations;
struct timeval;
namespace nacl_io {
@@ -49,6 +50,11 @@ class KernelProxy : protected KernelObject {
// |ppapi| may be NULL. If so, no mount that uses pepper calls can be mounted.
virtual Error Init(PepperInterface* ppapi);
+ // Register/Unregister a new mount type. See the documentation in nacl_io.h
+ // for more info.
+ bool RegisterMountType(const char* mount_type, fuse_operations* fuse_ops);
+ bool UnregisterMountType(const char* mount_type);
+
virtual int pipe(int pipefds[2]);
// NaCl-only function to read resources specified in the NMF file.
diff --git a/native_client_sdk/src/libraries/nacl_io/library.dsc b/native_client_sdk/src/libraries/nacl_io/library.dsc
index ecc298a..abddc64 100644
--- a/native_client_sdk/src/libraries/nacl_io/library.dsc
+++ b/native_client_sdk/src/libraries/nacl_io/library.dsc
@@ -20,6 +20,7 @@
"event_listener.cc",
"fifo_char.cc",
"fifo_packet.cc",
+ "fuse_mount_factory.cc",
"getdents_helper.cc",
"h_errno.cc",
"host_resolver.cc",
@@ -34,6 +35,7 @@
"kernel_wrap_win.cc",
"mount.cc",
"mount_dev.cc",
+ "mount_fuse.cc",
"mount_html5fs.cc",
"mount_http.cc",
"mount_mem.cc",
@@ -162,6 +164,8 @@
"fifo_interface.h",
"fifo_null.h",
"fifo_packet.h",
+ "fuse.h",
+ "fuse_mount_factory.h",
"getdents_helper.h",
"host_resolver.h",
"inode_pool.h",
@@ -175,6 +179,7 @@
"mount_dev.h",
"mount_factory.h",
"mount.h",
+ "mount_fuse.h",
"mount_html5fs.h",
"mount_http.h",
"mount_mem.h",
diff --git a/native_client_sdk/src/libraries/nacl_io/mount.cc b/native_client_sdk/src/libraries/nacl_io/mount.cc
index 4892662..3c0d7a2 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount.cc
+++ b/native_client_sdk/src/libraries/nacl_io/mount.cc
@@ -26,9 +26,9 @@ Mount::Mount() : dev_(0) {}
Mount::~Mount() {}
-Error Mount::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
- dev_ = dev;
- ppapi_ = ppapi;
+Error Mount::Init(const MountInitArgs& args) {
+ dev_ = args.dev;
+ ppapi_ = args.ppapi;
return 0;
}
diff --git a/native_client_sdk/src/libraries/nacl_io/mount.h b/native_client_sdk/src/libraries/nacl_io/mount.h
index b13fc08..a99c1fa 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount.h
@@ -17,6 +17,8 @@
#include "sdk_util/ref_object.h"
#include "sdk_util/scoped_ref.h"
+struct fuse_operations;
+
namespace nacl_io {
class Mount;
@@ -26,6 +28,20 @@ class PepperInterface;
typedef sdk_util::ScopedRef<Mount> ScopedMount;
typedef std::map<std::string, std::string> StringMap_t;
+// This structure is passed to all mounts via the Mount::Init virtual function.
+// With it, we can add or remove initialization values without changing the
+// function signature.
+struct MountInitArgs {
+ MountInitArgs() : dev(0), ppapi(NULL), fuse_ops(NULL) {}
+ explicit MountInitArgs(int dev) : dev(dev), ppapi(NULL), fuse_ops(NULL) {}
+
+ // Device number of the new filesystem.
+ int dev;
+ StringMap_t string_map;
+ PepperInterface* ppapi;
+ fuse_operations* fuse_ops;
+};
+
// NOTE: The KernelProxy is the only class that should be setting errno. All
// other classes should return Error (as defined by nacl_io/error.h).
class Mount : public sdk_util::RefObject {
@@ -36,9 +52,8 @@ class Mount : public sdk_util::RefObject {
virtual ~Mount();
// Init must be called by the factory before the mount is used.
- // This function must assign a root node, or replace FindNode.
// |ppapi| can be NULL. If so, this mount cannot make any pepper calls.
- virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi);
+ virtual Error Init(const MountInitArgs& args);
virtual void Destroy();
public:
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc
index 8fee68a..38eb86b 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc
+++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc
@@ -307,8 +307,8 @@ MountDev::MountDev() {}
if (error) \
return error;
-Error MountDev::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
- Error error = Mount::Init(dev, args, ppapi);
+Error MountDev::Init(const MountInitArgs& args) {
+ Error error = Mount::Init(args);
if (error)
return error;
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_dev.h b/native_client_sdk/src/libraries/nacl_io/mount_dev.h
index 640158b..110c8fd 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_dev.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.h
@@ -27,7 +27,7 @@ class MountDev : public Mount {
protected:
MountDev();
- virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi);
+ virtual Error Init(const MountInitArgs& args);
private:
ScopedMountNode root_;
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_factory.h b/native_client_sdk/src/libraries/nacl_io/mount_factory.h
index fd10014..48842ba 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_factory.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount_factory.h
@@ -5,8 +5,7 @@
#ifndef LIBRARIES_NACL_IO_MOUNT_FACTORY_H_
#define LIBRARIES_NACL_IO_MOUNT_FACTORY_H_
-#include <map>
-#include <string>
+#include <errno.h>
#include "nacl_io/error.h"
#include "sdk_util/scoped_ref.h"
@@ -15,15 +14,12 @@ namespace nacl_io {
class PepperInterface;
class Mount;
-
-typedef std::map<std::string, std::string> StringMap_t;
+struct MountInitArgs;
class MountFactory {
public:
virtual ~MountFactory() {}
- virtual Error CreateMount(int dev,
- StringMap_t& args,
- PepperInterface* ppapi,
+ virtual Error CreateMount(const MountInitArgs& args,
sdk_util::ScopedRef<Mount>* out_mount) = 0;
};
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_fuse.cc b/native_client_sdk/src/libraries/nacl_io/mount_fuse.cc
new file mode 100644
index 0000000..8bf7664
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_io/mount_fuse.cc
@@ -0,0 +1,462 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "nacl_io/mount_fuse.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "nacl_io/getdents_helper.h"
+#include "nacl_io/kernel_handle.h"
+#include "sdk_util/macros.h"
+
+
+namespace nacl_io {
+
+namespace {
+
+struct FillDirInfo {
+ FillDirInfo(GetDentsHelper* getdents, size_t num_bytes)
+ : getdents(getdents), num_bytes(num_bytes), wrote_offset(false) {}
+
+ GetDentsHelper* getdents;
+ size_t num_bytes;
+ bool wrote_offset;
+};
+
+} // namespace
+
+MountFuse::MountFuse() : fuse_ops_(NULL), fuse_user_data_(NULL) {}
+
+Error MountFuse::Init(const MountInitArgs& args) {
+ Error error = Mount::Init(args);
+ if (error)
+ return error;
+
+ fuse_ops_ = args.fuse_ops;
+ if (fuse_ops_ == NULL)
+ return EINVAL;
+
+ if (fuse_ops_->init) {
+ struct fuse_conn_info info;
+ fuse_user_data_ = fuse_ops_->init(&info);
+ }
+
+ return 0;
+}
+
+void MountFuse::Destroy() {
+ if (fuse_ops_ && fuse_ops_->destroy)
+ fuse_ops_->destroy(fuse_user_data_);
+}
+
+Error MountFuse::Access(const Path& path, int a_mode) {
+ if (!fuse_ops_->access)
+ return ENOSYS;
+
+ int result = fuse_ops_->access(path.Join().c_str(), a_mode);
+ if (result < 0)
+ return -result;
+
+ return 0;
+}
+
+Error MountFuse::Open(const Path& path,
+ int open_flags,
+ ScopedMountNode* out_node) {
+ std::string path_str = path.Join();
+ const char* path_cstr = path_str.c_str();
+ int result = 0;
+
+ struct fuse_file_info fi;
+ memset(&fi, 0, sizeof(fi));
+ fi.flags = open_flags;
+
+ if (open_flags & (O_CREAT | O_EXCL)) {
+ // According to the FUSE docs, open() is not called when O_CREAT or O_EXCL
+ // is passed.
+ mode_t mode = S_IRALL | S_IWALL;
+ if (fuse_ops_->create) {
+ result = fuse_ops_->create(path_cstr, mode, &fi);
+ if (result < 0)
+ return -result;
+ } else if (fuse_ops_->mknod) {
+ result = fuse_ops_->mknod(path_cstr, mode, dev_);
+ if (result < 0)
+ return -result;
+ } else {
+ return ENOSYS;
+ }
+ } else {
+ // First determine if this is a regular file or a directory.
+ if (fuse_ops_->getattr) {
+ struct stat statbuf;
+ result = fuse_ops_->getattr(path_cstr, &statbuf);
+ if (result < 0)
+ return -result;
+
+ if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
+ // This is a directory. Don't try to open, just create a new node with
+ // this path.
+ ScopedMountNode node(
+ new MountNodeFuseDir(this, fuse_ops_, fi, path_cstr));
+ Error error = node->Init(open_flags);
+ if (error)
+ return error;
+
+ *out_node = node;
+ return 0;
+ }
+ }
+
+ // Existing file.
+ if (open_flags & O_TRUNC) {
+ // According to the FUSE docs, O_TRUNC does two calls: first truncate()
+ // then open().
+ if (!fuse_ops_->truncate)
+ return ENOSYS;
+ result = fuse_ops_->truncate(path_cstr, 0);
+ if (result < 0)
+ return -result;
+ }
+
+ if (!fuse_ops_->open)
+ return ENOSYS;
+ result = fuse_ops_->open(path_cstr, &fi);
+ if (result < 0)
+ return -result;
+ }
+
+ ScopedMountNode node(new MountNodeFuseFile(this, fuse_ops_, fi, path_cstr));
+ Error error = node->Init(open_flags);
+ if (error)
+ return error;
+
+ *out_node = node;
+ return 0;
+}
+
+Error MountFuse::Unlink(const Path& path) {
+ if (!fuse_ops_->unlink)
+ return ENOSYS;
+
+ int result = fuse_ops_->unlink(path.Join().c_str());
+ if (result < 0)
+ return -result;
+
+ return 0;
+}
+
+Error MountFuse::Mkdir(const Path& path, int perm) {
+ if (!fuse_ops_->mkdir)
+ return ENOSYS;
+
+ int result = fuse_ops_->mkdir(path.Join().c_str(), perm);
+ if (result < 0)
+ return -result;
+
+ return 0;
+}
+
+Error MountFuse::Rmdir(const Path& path) {
+ if (!fuse_ops_->rmdir)
+ return ENOSYS;
+
+ int result = fuse_ops_->rmdir(path.Join().c_str());
+ if (result < 0)
+ return -result;
+
+ return 0;
+}
+
+Error MountFuse::Remove(const Path& path) {
+ ScopedMountNode node;
+ Error error = Open(path, O_RDONLY, &node);
+ if (error)
+ return error;
+
+ struct stat statbuf;
+ error = node->GetStat(&statbuf);
+ if (error)
+ return error;
+
+ node.reset();
+
+ if ((statbuf.st_mode & S_IFMT) == S_IFDIR) {
+ return Rmdir(path);
+ } else {
+ return Unlink(path);
+ }
+}
+
+Error MountFuse::Rename(const Path& path, const Path& newpath) {
+ if (!fuse_ops_->rename)
+ return ENOSYS;
+
+ int result = fuse_ops_->rename(path.Join().c_str(), newpath.Join().c_str());
+ if (result < 0)
+ return -result;
+
+ return 0;
+}
+
+MountNodeFuse::MountNodeFuse(Mount* mount,
+ struct fuse_operations* fuse_ops,
+ struct fuse_file_info& info,
+ const std::string& path)
+ : MountNode(mount),
+ fuse_ops_(fuse_ops),
+ info_(info),
+ path_(path) {}
+
+bool MountNodeFuse::CanOpen(int open_flags) {
+ struct stat statbuf;
+ Error error = GetStat(&statbuf);
+ if (error)
+ return false;
+
+ // GetStat cached the mode in stat_.st_mode. Forward to MountNode::CanOpen,
+ // which will check this mode against open_flags.
+ return MountNode::CanOpen(open_flags);
+}
+
+Error MountNodeFuse::GetStat(struct stat* stat) {
+ int result;
+ if (fuse_ops_->fgetattr) {
+ result = fuse_ops_->fgetattr(path_.c_str(), stat, &info_);
+ if (result < 0)
+ return -result;
+ } else if (fuse_ops_->getattr) {
+ result = fuse_ops_->getattr(path_.c_str(), stat);
+ if (result < 0)
+ return -result;
+ } else {
+ return ENOSYS;
+ }
+
+ // Also update the cached stat values.
+ stat_ = *stat;
+ return 0;
+}
+
+Error MountNodeFuse::VIoctl(int request, va_list args) {
+ // TODO(binji): implement
+ return ENOSYS;
+}
+
+Error MountNodeFuse::Tcflush(int queue_selector) {
+ // TODO(binji): use ioctl for this?
+ return ENOSYS;
+}
+
+Error MountNodeFuse::Tcgetattr(struct termios* termios_p) {
+ // TODO(binji): use ioctl for this?
+ return ENOSYS;
+}
+
+Error MountNodeFuse::Tcsetattr(int optional_actions,
+ const struct termios* termios_p) {
+ // TODO(binji): use ioctl for this?
+ return ENOSYS;
+}
+
+Error MountNodeFuse::GetSize(size_t* out_size) {
+ struct stat statbuf;
+ Error error = GetStat(&statbuf);
+ if (error)
+ return error;
+
+ *out_size = stat_.st_size;
+ return 0;
+}
+
+MountNodeFuseFile::MountNodeFuseFile(Mount* mount,
+ struct fuse_operations* fuse_ops,
+ struct fuse_file_info& info,
+ const std::string& path)
+ : MountNodeFuse(mount, fuse_ops, info, path) {}
+
+void MountNodeFuseFile::Destroy() {
+ if (!fuse_ops_->release)
+ return;
+ fuse_ops_->release(path_.c_str(), &info_);
+}
+
+Error MountNodeFuseFile::FSync() {
+ if (!fuse_ops_->fsync)
+ return ENOSYS;
+
+ int datasync = 0;
+ int result = fuse_ops_->fsync(path_.c_str(), datasync, &info_);
+ if (result < 0)
+ return -result;
+ return 0;
+}
+
+Error MountNodeFuseFile::FTruncate(off_t length) {
+ if (!fuse_ops_->ftruncate)
+ return ENOSYS;
+
+ int result = fuse_ops_->ftruncate(path_.c_str(), length, &info_);
+ if (result < 0)
+ return -result;
+ return 0;
+}
+
+Error MountNodeFuseFile::Read(const HandleAttr& attr,
+ void* buf,
+ size_t count,
+ int* out_bytes) {
+ if (!fuse_ops_->read)
+ return ENOSYS;
+
+ char* cbuf = static_cast<char*>(buf);
+
+ int result = fuse_ops_->read(path_.c_str(), cbuf, count, attr.offs, &info_);
+ if (result < 0)
+ return -result;
+
+ // Fuse docs say that a read() call will always completely fill the buffer
+ // (padding with zeroes) unless the direct_io mount flag is set.
+ // TODO(binji): support the direct_io flag
+ if (static_cast<size_t>(result) < count)
+ memset(&cbuf[result], 0, count - result);
+
+ *out_bytes = count;
+ return 0;
+}
+
+Error MountNodeFuseFile::Write(const HandleAttr& attr,
+ const void* buf,
+ size_t count,
+ int* out_bytes) {
+ if (!fuse_ops_->write)
+ return ENOSYS;
+
+ int result = fuse_ops_->write(
+ path_.c_str(), static_cast<const char*>(buf), count, attr.offs, &info_);
+ if (result < 0)
+ return -result;
+
+ // Fuse docs say that a write() call will always write the entire buffer
+ // unless the direct_io mount flag is set.
+ // TODO(binji): What should we do if the user breaks this contract? Warn?
+ // TODO(binji): support the direct_io flag
+ *out_bytes = result;
+ return 0;
+}
+
+MountNodeFuseDir::MountNodeFuseDir(Mount* mount,
+ struct fuse_operations* fuse_ops,
+ struct fuse_file_info& info,
+ const std::string& path)
+ : MountNodeFuse(mount, fuse_ops, info, path) {}
+
+void MountNodeFuseDir::Destroy() {
+ if (!fuse_ops_->releasedir)
+ return;
+ fuse_ops_->releasedir(path_.c_str(), &info_);
+}
+
+Error MountNodeFuseDir::FSync() {
+ if (!fuse_ops_->fsyncdir)
+ return ENOSYS;
+
+ int datasync = 0;
+ int result = fuse_ops_->fsyncdir(path_.c_str(), datasync, &info_);
+ if (result < 0)
+ return -result;
+ return 0;
+}
+
+Error MountNodeFuseDir::GetDents(size_t offs,
+ struct dirent* pdir,
+ size_t count,
+ int* out_bytes) {
+ if (!fuse_ops_->readdir)
+ return ENOSYS;
+
+ bool opened_dir = false;
+ int result;
+
+ // Opendir is not necessary, only readdir. Call it anyway, if it is defined.
+ if (fuse_ops_->opendir) {
+ result = fuse_ops_->opendir(path_.c_str(), &info_);
+ if (result < 0)
+ return -result;
+
+ opened_dir = true;
+ }
+
+ Error error = 0;
+ GetDentsHelper getdents;
+ FillDirInfo fill_info(&getdents, count);
+ result = fuse_ops_->readdir(path_.c_str(),
+ &fill_info,
+ &MountNodeFuseDir::FillDirCallback,
+ offs,
+ &info_);
+ if (result < 0)
+ goto fail;
+
+ // If the fill function ever wrote an entry with |offs| != 0, then assume it
+ // was not given the full list of entries. In that case, GetDentsHelper's
+ // buffers start with the entry at offset |offs|, so the call to
+ // GetDentsHelpers::GetDents should use an offset of 0.
+ if (fill_info.wrote_offset)
+ offs = 0;
+
+ // The entries have been filled in from the FUSE filesystem, now write them
+ // out to the buffer.
+ error = getdents.GetDents(offs, pdir, count, out_bytes);
+ if (error)
+ goto fail;
+
+ return 0;
+
+fail:
+ if (opened_dir && fuse_ops_->releasedir) {
+ // Ignore this result, we're already failing.
+ fuse_ops_->releasedir(path_.c_str(), &info_);
+ }
+
+ return -result;
+}
+
+int MountNodeFuseDir::FillDirCallback(void* buf,
+ const char* name,
+ const struct stat* stbuf,
+ off_t off) {
+ FillDirInfo* fill_info = static_cast<FillDirInfo*>(buf);
+
+ // It is OK for the FUSE filesystem to pass a NULL stbuf. In that case, just
+ // use a bogus ino.
+ ino_t ino = stbuf ? stbuf->st_ino : 1;
+
+ // The FUSE docs say that the implementor of readdir can choose to ignore the
+ // offset given, and instead return all entries. To do this, they pass
+ // |off| == 0 for each call.
+ if (off) {
+ if (fill_info->num_bytes < sizeof(dirent))
+ return 1; // 1 => buffer is full
+
+ fill_info->wrote_offset = true;
+ fill_info->getdents->AddDirent(ino, name, strlen(name));
+ fill_info->num_bytes -= sizeof(dirent);
+ // return 0 => request more data. return 1 => buffer full.
+ return fill_info->num_bytes > 0 ? 0 : 1;
+ } else {
+ fill_info->getdents->AddDirent(ino, name, strlen(name));
+ fill_info->num_bytes -= sizeof(dirent);
+ // According to the docs, we can never return 1 (buffer full) when the
+ // offset is zero (the user is probably ignoring the result anyway).
+ return 0;
+ }
+}
+
+
+} // namespace nacl_io
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_fuse.h b/native_client_sdk/src/libraries/nacl_io/mount_fuse.h
new file mode 100644
index 0000000..28e4c3f
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_io/mount_fuse.h
@@ -0,0 +1,122 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef LIBRARIES_NACL_IO_MOUNT_FUSE_H_
+#define LIBRARIES_NACL_IO_MOUNT_FUSE_H_
+
+#include <map>
+
+#include "nacl_io/fuse.h"
+#include "nacl_io/mount.h"
+#include "nacl_io/mount_node.h"
+#include "nacl_io/typed_mount_factory.h"
+
+namespace nacl_io {
+
+class MountFuse : public Mount {
+ protected:
+ MountFuse();
+
+ virtual Error Init(const MountInitArgs& args);
+ virtual void Destroy();
+
+ public:
+ virtual Error Access(const Path& path, int a_mode);
+ virtual Error Open(const Path& path, int mode, ScopedMountNode* out_node);
+ virtual Error Unlink(const Path& path);
+ virtual Error Mkdir(const Path& path, int perm);
+ virtual Error Rmdir(const Path& path);
+ virtual Error Remove(const Path& path);
+ virtual Error Rename(const Path& path, const Path& newpath);
+
+private:
+ struct fuse_operations* fuse_ops_;
+ void* fuse_user_data_;
+
+ friend class MountNodeFuse;
+ friend class FuseMountFactory;
+ DISALLOW_COPY_AND_ASSIGN(MountFuse);
+};
+
+class MountNodeFuse : public MountNode {
+ protected:
+ MountNodeFuse(Mount* mount,
+ struct fuse_operations* fuse_ops,
+ struct fuse_file_info& info,
+ const std::string& path);
+
+ public:
+ virtual bool CanOpen(int open_flags);
+ virtual Error GetStat(struct stat* stat);
+ virtual Error VIoctl(int request, va_list args);
+ virtual Error Tcflush(int queue_selector);
+ virtual Error Tcgetattr(struct termios* termios_p);
+ virtual Error Tcsetattr(int optional_actions,
+ const struct termios *termios_p);
+ virtual Error GetSize(size_t* out_size);
+
+ protected:
+ struct fuse_operations* fuse_ops_;
+ struct fuse_file_info info_;
+ std::string path_;
+};
+
+class MountNodeFuseFile : public MountNodeFuse {
+ public:
+ MountNodeFuseFile(Mount* mount,
+ struct fuse_operations* fuse_ops,
+ struct fuse_file_info& info,
+ const std::string& path);
+
+ protected:
+ virtual void Destroy();
+
+ public:
+ virtual Error FSync();
+ virtual Error FTruncate(off_t length);
+ virtual Error Read(const HandleAttr& attr,
+ void* buf,
+ size_t count,
+ int* out_bytes);
+ virtual Error Write(const HandleAttr& attr,
+ const void* buf,
+ size_t count,
+ int* out_bytes);
+
+ private:
+ friend class MountFuse;
+ DISALLOW_COPY_AND_ASSIGN(MountNodeFuseFile);
+};
+
+class MountNodeFuseDir : public MountNodeFuse {
+ public:
+ MountNodeFuseDir(Mount* mount,
+ struct fuse_operations* fuse_ops,
+ struct fuse_file_info& info,
+ const std::string& path);
+
+ protected:
+ virtual void Destroy();
+
+ public:
+ virtual Error FSync();
+ virtual Error GetDents(size_t offs,
+ struct dirent* pdir,
+ size_t count,
+ int* out_bytes);
+
+ private:
+ static int FillDirCallback(void* buf,
+ const char* name,
+ const struct stat* stbuf,
+ off_t off);
+
+ private:
+ friend class MountFuse;
+ DISALLOW_COPY_AND_ASSIGN(MountNodeFuseDir);
+};
+
+} // namespace nacl_io
+
+#endif // LIBRARIES_NACL_IO_MOUNT_FUSE_H_
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc
index 3c9695f..a54c183 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc
+++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc
@@ -123,12 +123,12 @@ MountHtml5Fs::MountHtml5Fs()
filesystem_open_has_result_(false),
filesystem_open_error_(0) {}
-Error MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
- Error error = Mount::Init(dev, args, ppapi);
+Error MountHtml5Fs::Init(const MountInitArgs& args) {
+ Error error = Mount::Init(args);
if (error)
return error;
- if (!ppapi)
+ if (!args.ppapi)
return ENOSYS;
pthread_cond_init(&filesystem_open_cond_, NULL);
@@ -136,7 +136,8 @@ Error MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
// Parse mount args.
PP_FileSystemType filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT;
int64_t expected_size = 0;
- for (StringMap_t::iterator iter = args.begin(), end = args.end(); iter != end;
+ for (StringMap_t::const_iterator iter = args.string_map.begin();
+ iter != args.string_map.end();
++iter) {
if (iter->first == "type") {
if (iter->second == "PERSISTENT") {
@@ -150,7 +151,7 @@ Error MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
}
// Initialize filesystem.
- filesystem_resource_ = ppapi->GetFileSystemInterface()
+ filesystem_resource_ = args.ppapi->GetFileSystemInterface()
->Create(ppapi_->GetInstance(), filesystem_type);
if (filesystem_resource_ == 0)
return ENOSYS;
@@ -158,13 +159,13 @@ Error MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
// We can't block the main thread, so make an asynchronous call if on main
// thread. If we are off-main-thread, then don't make an asynchronous call;
// otherwise we require a message loop.
- bool main_thread = ppapi->GetCoreInterface()->IsMainThread();
+ bool main_thread = args.ppapi->GetCoreInterface()->IsMainThread();
PP_CompletionCallback cc =
main_thread ? PP_MakeCompletionCallback(
&MountHtml5Fs::FilesystemOpenCallbackThunk, this)
: PP_BlockUntilComplete();
- int32_t result = ppapi->GetFileSystemInterface()
+ int32_t result = args.ppapi->GetFileSystemInterface()
->Open(filesystem_resource_, expected_size, cc);
if (!main_thread) {
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h
index 0408c79..06dad78 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h
@@ -31,7 +31,7 @@ class MountHtml5Fs : public Mount {
protected:
MountHtml5Fs();
- virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi);
+ virtual Error Init(const MountInitArgs& args);
virtual void Destroy();
Error BlockUntilFilesystemOpen();
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_http.cc
index dcc1332..4d613e9 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_http.cc
+++ b/native_client_sdk/src/libraries/nacl_io/mount_http.cc
@@ -201,13 +201,15 @@ MountHttp::MountHttp()
cache_stat_(true),
cache_content_(true) {}
-Error MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
- Error error = Mount::Init(dev, args, ppapi);
+Error MountHttp::Init(const MountInitArgs& args) {
+ Error error = Mount::Init(args);
if (error)
return error;
// Parse mount args.
- for (StringMap_t::iterator iter = args.begin(); iter != args.end(); ++iter) {
+ for (StringMap_t::const_iterator iter = args.string_map.begin();
+ iter != args.string_map.end();
+ ++iter) {
if (iter->first == "SOURCE") {
url_root_ = iter->second;
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_http.h b/native_client_sdk/src/libraries/nacl_io/mount_http.h
index 4946d9f..145140d 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_http.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount_http.h
@@ -34,7 +34,7 @@ class MountHttp : public Mount {
protected:
MountHttp();
- virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi);
+ virtual Error Init(const MountInitArgs& args);
virtual void Destroy();
Error FindOrCreateDir(const Path& path, ScopedMountNode* out_node);
Error LoadManifest(const std::string& path, char** out_manifest);
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc
index dd4a402..f00aa15 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc
+++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc
@@ -23,8 +23,8 @@ namespace nacl_io {
MountMem::MountMem() : root_(NULL) {}
-Error MountMem::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
- Error error = Mount::Init(dev, args, ppapi);
+Error MountMem::Init(const MountInitArgs& args) {
+ Error error = Mount::Init(args);
if (error)
return error;
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_mem.h b/native_client_sdk/src/libraries/nacl_io/mount_mem.h
index 4966975..8ad87ca 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_mem.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.h
@@ -14,7 +14,8 @@ class MountMem : public Mount {
protected:
MountMem();
- virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi);
+ using Mount::Init;
+ virtual Error Init(const MountInitArgs& args);
// The protected functions are only used internally and will not
// acquire or release the mount's lock themselves. The caller is
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.h b/native_client_sdk/src/libraries/nacl_io/mount_node.h
index fe5493c..80e27fa 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_node.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount_node.h
@@ -132,6 +132,7 @@ class MountNode : public sdk_util::RefObject {
friend class Mount;
friend class MountDev;
+ friend class MountFuse;
friend class MountHtml5Fs;
friend class MountHttp;
friend class MountMem;
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc
index 33e6675..9f91763 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc
+++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc
@@ -111,10 +111,8 @@ class MountNodePassthrough : public MountNode {
MountPassthrough::MountPassthrough() {}
-Error MountPassthrough::Init(int dev,
- StringMap_t& args,
- PepperInterface* ppapi) {
- return Mount::Init(dev, args, ppapi);
+Error MountPassthrough::Init(const MountInitArgs& args) {
+ return Mount::Init(args);
}
void MountPassthrough::Destroy() {}
diff --git a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h
index 7b9aa61..3cc3285 100644
--- a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h
+++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h
@@ -14,7 +14,7 @@ class MountPassthrough : public Mount {
protected:
MountPassthrough();
- virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi);
+ virtual Error Init(const MountInitArgs& args);
virtual void Destroy();
public:
diff --git a/native_client_sdk/src/libraries/nacl_io/nacl_io.cc b/native_client_sdk/src/libraries/nacl_io/nacl_io.cc
index 5e58a60..2263d1b 100644
--- a/native_client_sdk/src/libraries/nacl_io/nacl_io.cc
+++ b/native_client_sdk/src/libraries/nacl_io/nacl_io.cc
@@ -16,3 +16,11 @@ void nacl_io_init_ppapi(PP_Instance instance,
ki_init_ppapi(NULL, instance, get_interface);
}
+int nacl_io_register_mount_type(const char* mount_type,
+ fuse_operations* fuse_ops) {
+ return ki_register_mount_type(mount_type, fuse_ops);
+}
+
+int nacl_io_unregister_mount_type(const char* mount_type) {
+ return ki_unregister_mount_type(mount_type);
+}
diff --git a/native_client_sdk/src/libraries/nacl_io/nacl_io.h b/native_client_sdk/src/libraries/nacl_io/nacl_io.h
index 71fb928..98490f2 100644
--- a/native_client_sdk/src/libraries/nacl_io/nacl_io.h
+++ b/native_client_sdk/src/libraries/nacl_io/nacl_io.h
@@ -117,6 +117,67 @@ void nacl_io_init_ppapi(PP_Instance instance,
* unsigned long mountflags, const void *data) NOTHROW;
*/
+/**
+ * Register a new mount type, using a FUSE interface to implement it.
+ *
+ * Example:
+ * int my_open(const char* path, struct fuse_file_info*) {
+ * ...
+ * }
+ *
+ * int my_read(const char* path, char* buf, size_t count, off_t offset, struct
+ * fuse_file_info* info) {
+ * ...
+ * }
+ *
+ * struct fuse_operations my_fuse_ops = {
+ * ...
+ * my_open,
+ * NULL, // opendir() not implemented.
+ * my_read,
+ * ...
+ * };
+ *
+ * ...
+ *
+ * const char mount_type[] = "my_mount";
+ * int result = nacl_io_register_mount_type(mount_type, &my_fuse_ops);
+ * if (!result) {
+ * fprintf(stderr, "Error registering mount type %s.\n", mount_type);
+ * exit(1);
+ * }
+ *
+ * ...
+ *
+ * int result = mount("", "/mnt/foo", mount_type, 0, NULL);
+ * if (!result) {
+ * fprintf(stderr, "Error mounting %s.\n", mount_type);
+ * exit(1);
+ * }
+ *
+ * See fuse.h for more information about the FUSE interface.
+ * Also see fuse.sourceforge.net for more information about FUSE in general.
+ *
+ * @param[in] mount_type The name of the new mount type.
+ * @param[in] fuse_ops A pointer to the FUSE interface that will be used to
+ * implement this mount type. This pointer must be valid for the lifetime
+ * of all mounts and nodes that are created with it.
+ * @return 0 on success, -1 on failure (with errno set).
+ */
+struct fuse_operations;
+int nacl_io_register_mount_type(const char* mount_type,
+ struct fuse_operations* fuse_ops);
+
+/**
+ * Unregister a mount type, previously registered by
+ * nacl_io_register_mount_type().
+ *
+ * @param[in] mount_type The name of the mount type; the same identifier that
+ * was passed to nacl_io_register_mount_type().
+ * @return 0 on success, -1 on failure (with errno set).
+ */
+int nacl_io_unregister_mount_type(const char* mount_type);
+
EXTERN_C_END
#endif /* LIBRARIES_NACL_IO_NACL_IO_H_ */
diff --git a/native_client_sdk/src/libraries/nacl_io/typed_mount_factory.h b/native_client_sdk/src/libraries/nacl_io/typed_mount_factory.h
index 824c20e..805429c 100644
--- a/native_client_sdk/src/libraries/nacl_io/typed_mount_factory.h
+++ b/native_client_sdk/src/libraries/nacl_io/typed_mount_factory.h
@@ -13,12 +13,10 @@ namespace nacl_io {
template <typename T>
class TypedMountFactory : public MountFactory {
public:
- virtual Error CreateMount(int dev,
- StringMap_t& args,
- PepperInterface* ppapi,
+ virtual Error CreateMount(const MountInitArgs& args,
ScopedMount* out_mount) {
sdk_util::ScopedRef<T> mnt(new T());
- Error error = mnt->Init(dev, args, ppapi);
+ Error error = mnt->Init(args);
if (error)
return error;
diff --git a/native_client_sdk/src/tests/nacl_io_test/example.dsc b/native_client_sdk/src/tests/nacl_io_test/example.dsc
index d270b0f..f2a73e1 100644
--- a/native_client_sdk/src/tests/nacl_io_test/example.dsc
+++ b/native_client_sdk/src/tests/nacl_io_test/example.dsc
@@ -27,6 +27,7 @@
'main.cc',
'mock_util.h',
'mount_dev_mock.h',
+ 'mount_fuse_test.cc',
'mount_html5fs_test.cc',
'mount_http_test.cc',
'mount_mock.cc',
diff --git a/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc
index 470ad17..2707ad4 100644
--- a/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc
+++ b/native_client_sdk/src/tests/nacl_io_test/kernel_proxy_test.cc
@@ -422,13 +422,15 @@ TEST_F(KernelProxyTest, MemMountDup) {
namespace {
-StringMap_t g_StringMap;
+StringMap_t g_string_map;
class MountMockInit : public MountMem {
public:
- virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
- g_StringMap = args;
- if (args.find("false") != args.end())
+ using MountMem::Init;
+
+ virtual Error Init(const MountInitArgs& args) {
+ g_string_map = args.string_map;
+ if (g_string_map.find("false") != g_string_map.end())
return EINVAL;
return 0;
}
@@ -465,13 +467,13 @@ class KernelProxyMountTest : public ::testing::Test {
TEST_F(KernelProxyMountTest, MountInit) {
int res1 = ki_mount("/", "/mnt1", "initfs", 0, "false,foo=bar");
- EXPECT_EQ("bar", g_StringMap["foo"]);
+ EXPECT_EQ("bar", g_string_map["foo"]);
EXPECT_EQ(-1, res1);
EXPECT_EQ(EINVAL, errno);
int res2 = ki_mount("/", "/mnt2", "initfs", 0, "true,bar=foo,x=y");
EXPECT_NE(-1, res2);
- EXPECT_EQ("y", g_StringMap["x"]);
+ EXPECT_EQ("y", g_string_map["x"]);
}
namespace {
@@ -591,9 +593,7 @@ class SingletonMountFactory : public MountFactory {
public:
SingletonMountFactory(const ScopedMount& mount) : mount_(mount) {}
- virtual Error CreateMount(int dev,
- StringMap_t& args,
- PepperInterface* ppapi,
+ virtual Error CreateMount(const MountInitArgs& args,
ScopedMount* out_mount) {
*out_mount = mount_;
return 0;
diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_dev_mock.h b/native_client_sdk/src/tests/nacl_io_test/mount_dev_mock.h
index 10ee830..72ef88c 100644
--- a/native_client_sdk/src/tests/nacl_io_test/mount_dev_mock.h
+++ b/native_client_sdk/src/tests/nacl_io_test/mount_dev_mock.h
@@ -15,8 +15,7 @@
class MountDevMock : public nacl_io::MountDev {
public:
MountDevMock() {
- nacl_io::StringMap_t map;
- Init(1, map, NULL);
+ Init(nacl_io::MountInitArgs(1));
}
int num_nodes() { return (int) inode_pool_.size(); }
};
diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_fuse_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_fuse_test.cc
new file mode 100644
index 0000000..a0ac553
--- /dev/null
+++ b/native_client_sdk/src/tests/nacl_io_test/mount_fuse_test.cc
@@ -0,0 +1,345 @@
+// Copyright (c) 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <fcntl.h>
+
+#include <gtest/gtest.h>
+
+#include <string>
+#include <vector>
+
+#include "nacl_io/fuse.h"
+#include "nacl_io/kernel_handle.h"
+#include "nacl_io/kernel_intercept.h"
+#include "nacl_io/kernel_proxy.h"
+#include "nacl_io/mount_fuse.h"
+
+using namespace nacl_io;
+
+namespace {
+
+class MountFuseForTesting : public MountFuse {
+ public:
+ explicit MountFuseForTesting(fuse_operations* fuse_ops) {
+ MountInitArgs args;
+ args.fuse_ops = fuse_ops;
+ EXPECT_EQ(0, Init(args));
+ }
+};
+
+// Implementation of a simple flat memory filesystem.
+struct File {
+ std::string name;
+ std::vector<uint8_t> data;
+};
+
+typedef std::vector<File> Files;
+Files g_files;
+
+bool IsValidPath(const char* path) {
+ if (path == NULL)
+ return false;
+
+ if (strlen(path) <= 1)
+ return false;
+
+ if (path[0] != '/')
+ return false;
+
+ return true;
+}
+
+File* FindFile(const char* path) {
+ if (!IsValidPath(path))
+ return NULL;
+
+ for (Files::iterator iter = g_files.begin(); iter != g_files.end(); ++iter) {
+ if (iter->name == &path[1])
+ return &*iter;
+ }
+
+ return NULL;
+}
+
+int testfs_getattr(const char* path, struct stat* stbuf) {
+ memset(stbuf, 0, sizeof(struct stat));
+
+ if (strcmp(path, "/") == 0) {
+ stbuf->st_mode = S_IFDIR | 0755;
+ return 0;
+ }
+
+ File* file = FindFile(path);
+ if (file == NULL)
+ return -ENOENT;
+
+ stbuf->st_mode = S_IFREG | 0666;
+ stbuf->st_size = file->data.size();
+ return 0;
+}
+
+int testfs_readdir(const char* path, void* buf, fuse_fill_dir_t filler,
+ off_t offset, struct fuse_file_info*) {
+ if (strcmp(path, "/") != 0)
+ return -ENOENT;
+
+ filler(buf, ".", NULL, 0);
+ filler(buf, "..", NULL, 0);
+ for (Files::iterator iter = g_files.begin(); iter != g_files.end(); ++iter) {
+ filler(buf, iter->name.c_str(), NULL, 0);
+ }
+ return 0;
+}
+
+int testfs_create(const char* path, mode_t, struct fuse_file_info* fi) {
+ if (!IsValidPath(path))
+ return -ENOENT;
+
+ File* file = FindFile(path);
+ if (file != NULL) {
+ if (fi->flags & O_EXCL)
+ return -EEXIST;
+ } else {
+ g_files.push_back(File());
+ file = &g_files.back();
+ file->name = &path[1]; // Skip initial /
+ }
+
+ return 0;
+}
+
+int testfs_open(const char* path, struct fuse_file_info*) {
+ // open is only called to open an existing file, otherwise create is
+ // called. We don't need to do any additional work here, the path will be
+ // passed to any other operations.
+ return FindFile(path) != NULL;
+}
+
+int testfs_read(const char* path, char* buf, size_t size, off_t offset,
+ struct fuse_file_info* fi) {
+ File* file = FindFile(path);
+ if (file == NULL)
+ return -ENOENT;
+
+ size_t filesize = file->data.size();
+ // Trying to read past the end of the file.
+ if (offset >= filesize)
+ return 0;
+
+ if (offset + size > filesize)
+ size = filesize - offset;
+
+ memcpy(buf, file->data.data() + offset, size);
+ return size;
+}
+
+int testfs_write(const char* path, const char* buf, size_t size,
+ off_t offset, struct fuse_file_info*) {
+ File* file = FindFile(path);
+ if (file == NULL)
+ return -ENOENT;
+
+ size_t filesize = file->data.size();
+
+ if (offset + size > filesize)
+ file->data.resize(offset + size);
+
+ memcpy(file->data.data() + offset, buf, size);
+ return size;
+}
+
+const char hello_world[] = "Hello, World!\n";
+
+fuse_operations g_fuse_operations = {
+ 0, // flag_nopath
+ 0, // flag_reserved
+ NULL, // init
+ NULL, // destroy
+ NULL, // access
+ testfs_create, // create
+ NULL, // fgetattr
+ NULL, // fsync
+ NULL, // ftruncate
+ testfs_getattr, // getattr
+ NULL, // mkdir
+ NULL, // mknod
+ testfs_open, // open
+ NULL, // opendir
+ testfs_read, // read
+ testfs_readdir, // readdir
+ NULL, // release
+ NULL, // releasedir
+ NULL, // rename
+ NULL, // rmdir
+ NULL, // truncate
+ NULL, // unlink
+ testfs_write, // write
+};
+
+class MountFuseTest : public ::testing::Test {
+ public:
+ MountFuseTest();
+
+ void SetUp();
+
+ protected:
+ MountFuseForTesting mnt_;
+};
+
+MountFuseTest::MountFuseTest() : mnt_(&g_fuse_operations) {}
+
+void MountFuseTest::SetUp() {
+ // Reset the filesystem.
+ g_files.clear();
+
+ // Add a built-in file.
+ size_t hello_len = strlen(hello_world);
+
+ File hello;
+ hello.name = "hello";
+ hello.data.resize(hello_len);
+ memcpy(hello.data.data(), hello_world, hello_len);
+ g_files.push_back(hello);
+}
+
+} // namespace
+
+TEST_F(MountFuseTest, OpenAndRead) {
+ ScopedMountNode node;
+ ASSERT_EQ(0, mnt_.Open(Path("/hello"), O_RDONLY, &node));
+
+ char buffer[15] = {0};
+ int bytes_read = 0;
+ HandleAttr attr;
+ ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
+ // FUSE always fills the buffer (padding with \0) unless in direct_io mode.
+ ASSERT_EQ(sizeof(buffer), bytes_read);
+ ASSERT_STREQ(hello_world, buffer);
+}
+
+TEST_F(MountFuseTest, CreateAndWrite) {
+ ScopedMountNode node;
+ ASSERT_EQ(0, mnt_.Open(Path("/foobar"), O_RDWR | O_CREAT, &node));
+
+ HandleAttr attr;
+ const char message[] = "Something interesting";
+ int bytes_written;
+ ASSERT_EQ(0, node->Write(attr, &message[0], strlen(message), &bytes_written));
+ ASSERT_EQ(bytes_written, strlen(message));
+
+ // Now try to read the data back.
+ char buffer[40] = {0};
+ int bytes_read = 0;
+ ASSERT_EQ(0, node->Read(attr, &buffer[0], sizeof(buffer), &bytes_read));
+ // FUSE always fills the buffer (padding with \0) unless in direct_io mode.
+ ASSERT_EQ(sizeof(buffer), bytes_read);
+ ASSERT_STREQ(message, buffer);
+}
+
+TEST_F(MountFuseTest, GetStat) {
+ struct stat statbuf;
+ ScopedMountNode node;
+
+ ASSERT_EQ(0, mnt_.Open(Path("/hello"), O_RDONLY, &node));
+ EXPECT_EQ(0, node->GetStat(&statbuf));
+ EXPECT_EQ(S_IFREG, statbuf.st_mode & S_IFMT);
+ EXPECT_EQ(0666, statbuf.st_mode & ~S_IFMT);
+ EXPECT_EQ(strlen(hello_world), statbuf.st_size);
+
+ ASSERT_EQ(0, mnt_.Open(Path("/"), O_RDONLY, &node));
+ EXPECT_EQ(0, node->GetStat(&statbuf));
+ EXPECT_EQ(S_IFDIR, statbuf.st_mode & S_IFMT);
+ EXPECT_EQ(0755, statbuf.st_mode & ~S_IFMT);
+
+ // Create a file and stat.
+ ASSERT_EQ(0, mnt_.Open(Path("/foobar"), O_RDWR | O_CREAT, &node));
+ EXPECT_EQ(0, node->GetStat(&statbuf));
+ EXPECT_EQ(S_IFREG, statbuf.st_mode & S_IFMT);
+ EXPECT_EQ(0666, statbuf.st_mode & ~S_IFMT);
+ EXPECT_EQ(0, statbuf.st_size);
+}
+
+TEST_F(MountFuseTest, GetDents) {
+ ScopedMountNode root;
+
+ ASSERT_EQ(0, mnt_.Open(Path("/"), O_RDONLY, &root));
+
+ struct dirent entries[4];
+ int bytes_read;
+
+ // Try reading everything.
+ ASSERT_EQ(0, root->GetDents(0, &entries[0], sizeof(entries), &bytes_read));
+ ASSERT_EQ(3 * sizeof(dirent), bytes_read);
+ EXPECT_STREQ(".", entries[0].d_name);
+ EXPECT_STREQ("..", entries[1].d_name);
+ EXPECT_STREQ("hello", entries[2].d_name);
+
+ // Try reading from an offset.
+ memset(&entries, 0, sizeof(entries));
+ ASSERT_EQ(0, root->GetDents(sizeof(dirent), &entries[0], 2 * sizeof(dirent),
+ &bytes_read));
+ ASSERT_EQ(2 * sizeof(dirent), bytes_read);
+ EXPECT_STREQ("..", entries[0].d_name);
+ EXPECT_STREQ("hello", entries[1].d_name);
+
+ // Add a file and read again.
+ ScopedMountNode node;
+ ASSERT_EQ(0, mnt_.Open(Path("/foobar"), O_RDWR | O_CREAT, &node));
+ ASSERT_EQ(0, root->GetDents(0, &entries[0], sizeof(entries), &bytes_read));
+ ASSERT_EQ(4 * sizeof(dirent), bytes_read);
+ EXPECT_STREQ(".", entries[0].d_name);
+ EXPECT_STREQ("..", entries[1].d_name);
+ EXPECT_STREQ("hello", entries[2].d_name);
+ EXPECT_STREQ("foobar", entries[3].d_name);
+}
+
+namespace {
+
+class KernelProxyFuseTest : public ::testing::Test {
+ public:
+ KernelProxyFuseTest() {}
+
+ void SetUp();
+ void TearDown();
+
+ private:
+ KernelProxy kp_;
+};
+
+void KernelProxyFuseTest::SetUp() {
+ ki_init(&kp_);
+
+ // Register a fuse filesystem.
+ ki_register_mount_type("flatfs", &g_fuse_operations);
+
+ // Unmount the passthrough FS and mount our fuse filesystem.
+ EXPECT_EQ(0, kp_.umount("/"));
+ EXPECT_EQ(0, kp_.mount("", "/", "flatfs", 0, NULL));
+}
+
+void KernelProxyFuseTest::TearDown() {
+ ki_unregister_mount_type("flatfs");
+ ki_uninit();
+}
+
+} // namespace
+
+TEST_F(KernelProxyFuseTest, Basic) {
+ // Write a file.
+ int fd = ki_open("/hello", O_WRONLY | O_CREAT);
+ ASSERT_GT(fd, -1);
+ ASSERT_EQ(sizeof(hello_world),
+ ki_write(fd, hello_world, sizeof(hello_world)));
+ EXPECT_EQ(0, ki_close(fd));
+
+ // Then read it back in.
+ fd = ki_open("/hello", O_RDONLY);
+ ASSERT_GT(fd, -1);
+
+ char buffer[30];
+ memset(buffer, 0, sizeof(buffer));
+ ASSERT_EQ(sizeof(buffer), ki_read(fd, buffer, sizeof(buffer)));
+ EXPECT_STREQ(hello_world, buffer);
+ EXPECT_EQ(0, ki_close(fd));
+}
diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc
index 3f1bd11..b770892 100644
--- a/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc
+++ b/native_client_sdk/src/tests/nacl_io_test/mount_html5fs_test.cc
@@ -40,8 +40,11 @@ namespace {
class MountHtml5FsForTesting : public MountHtml5Fs {
public:
- MountHtml5FsForTesting(StringMap_t& args, PepperInterface* ppapi) {
- Error error = Init(1, args, ppapi);
+ MountHtml5FsForTesting(StringMap_t& string_map, PepperInterface* ppapi) {
+ MountInitArgs args;
+ args.string_map = string_map;
+ args.ppapi = ppapi;
+ Error error = Init(args);
EXPECT_EQ(0, error);
}
};
diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc
index 735a17b..a679bbc 100644
--- a/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc
+++ b/native_client_sdk/src/tests/nacl_io_test/mount_http_test.cc
@@ -26,7 +26,10 @@ namespace {
class MountHttpForTesting : public MountHttp {
public:
MountHttpForTesting(StringMap_t map, PepperInterface* ppapi) {
- EXPECT_EQ(0, Init(1, map, ppapi));
+ MountInitArgs args(1);
+ args.string_map = map;
+ args.ppapi = ppapi;
+ EXPECT_EQ(0, Init(args));
}
using MountHttp::GetNodeCacheForTesting;
diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_mock.h b/native_client_sdk/src/tests/nacl_io_test/mount_mock.h
index b0e5700..cde3bbb 100644
--- a/native_client_sdk/src/tests/nacl_io_test/mount_mock.h
+++ b/native_client_sdk/src/tests/nacl_io_test/mount_mock.h
@@ -12,6 +12,7 @@
class MountMock : public nacl_io::Mount {
public:
typedef nacl_io::Error Error;
+ typedef nacl_io::MountInitArgs MountInitArgs;
typedef nacl_io::Path Path;
typedef nacl_io::PepperInterface PepperInterface;
typedef nacl_io::ScopedMountNode ScopedMountNode;
@@ -20,7 +21,7 @@ class MountMock : public nacl_io::Mount {
MountMock();
virtual ~MountMock();
- MOCK_METHOD3(Init, Error(int, StringMap_t&, PepperInterface*));
+ MOCK_METHOD1(Init, Error(const MountInitArgs&));
MOCK_METHOD0(Destroy, void());
MOCK_METHOD2(Access, Error(const Path&, int));
MOCK_METHOD3(Open, Error(const Path&, int, ScopedMountNode*));
diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc
index 751e9fe..efa9c80 100644
--- a/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc
+++ b/native_client_sdk/src/tests/nacl_io_test/mount_node_test.cc
@@ -30,8 +30,8 @@ static int s_AllocNum = 0;
class MockMount : public MountMem {
public:
MockMount() {
- StringMap_t map;
- EXPECT_EQ(0, Init(1, map, NULL));
+ MountInitArgs args(1);
+ EXPECT_EQ(0, Init(args));
}
int num_nodes() { return inode_pool_.size(); }
diff --git a/native_client_sdk/src/tests/nacl_io_test/mount_test.cc b/native_client_sdk/src/tests/nacl_io_test/mount_test.cc
index ed6d674..bbea69e 100644
--- a/native_client_sdk/src/tests/nacl_io_test/mount_test.cc
+++ b/native_client_sdk/src/tests/nacl_io_test/mount_test.cc
@@ -24,8 +24,8 @@ namespace {
class MountMemMock : public MountMem {
public:
MountMemMock() {
- StringMap_t map;
- EXPECT_EQ(0, Init(1, map, NULL));
+ MountInitArgs args(1);
+ EXPECT_EQ(0, Init(args));
}
int num_nodes() { return (int) inode_pool_.size(); }