summaryrefslogtreecommitdiffstats
path: root/native_client_sdk
diff options
context:
space:
mode:
authornoelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-08 22:20:40 +0000
committernoelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2012-05-08 22:20:40 +0000
commit754defc88fd112a9a762f9dde3dd7b046b8752c0 (patch)
tree752ee1b330196823d63c64306661c45769277808 /native_client_sdk
parent10951ebcca94646c559f5bd7cf556f4b39514b6d (diff)
downloadchromium_src-754defc88fd112a9a762f9dde3dd7b046b8752c0.zip
chromium_src-754defc88fd112a9a762f9dde3dd7b046b8752c0.tar.gz
chromium_src-754defc88fd112a9a762f9dde3dd7b046b8752c0.tar.bz2
Add "kernel" source nacl_mount
kernel_intercept grabs calls to open, close, getcwd, read, write, etc and routes them to the kernel_proxy. kernel_proxy implements manages mounts, and implements thread safe system calls on top of kernel_object primatives. kernel_object primatives track file descriptors and file handles. BUG=122229 R=binji@chromium.org Review URL: https://chromiumcodereview.appspot.com/10315015 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@135927 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/kernel_handle.cc22
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/kernel_handle.h39
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/kernel_intercept.cc86
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/kernel_intercept.h45
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/kernel_object.cc153
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/kernel_object.h59
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.cc357
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.h93
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts_test/kernel_intercept_test.cc181
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts_test/kernel_object_test.cc123
-rw-r--r--native_client_sdk/src/libraries/nacl_mounts_test/kernel_proxy_test.cc156
11 files changed, 1314 insertions, 0 deletions
diff --git a/native_client_sdk/src/libraries/nacl_mounts/kernel_handle.cc b/native_client_sdk/src/libraries/nacl_mounts/kernel_handle.cc
new file mode 100644
index 0000000..58f5d8b
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/kernel_handle.cc
@@ -0,0 +1,22 @@
+/* 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 <fcntl.h>
+#include <pthread.h>
+
+#include "nacl_mounts/kernel_handle.h"
+#include "nacl_mounts/mount.h"
+#include "nacl_mounts/mount_node.h"
+
+
+// It is only legal to construct a handle while the kernel lock is held.
+KernelHandle::KernelHandle(Mount* mnt, MountNode* node, int mode)
+ : mount_(mnt),
+ node_(node),
+ mode_(mode),
+ offs_(0) {
+ if (mode & O_APPEND) offs_ = node->GetSize();
+}
+
diff --git a/native_client_sdk/src/libraries/nacl_mounts/kernel_handle.h b/native_client_sdk/src/libraries/nacl_mounts/kernel_handle.h
new file mode 100644
index 0000000..a3fb7e0
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/kernel_handle.h
@@ -0,0 +1,39 @@
+/* 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_MOUNTS_KERNEL_HANDLE_H_
+#define LIBRARIES_NACL_MOUNTS_KERNEL_HANDLE_H_
+
+#include <pthread.h>
+
+#include "utils/macros.h"
+#include "utils/ref_object.h"
+
+class Mount;
+class MountNode;
+
+// KernelHandle provides a reference counted container for the open
+// file information, such as it's mount, node, access type and offset.
+// KernelHandle can only be referenced when the KernelProxy lock is held.
+class KernelHandle : public RefObject {
+ public:
+ KernelHandle(Mount* mnt, MountNode* node, int oflags);
+
+ Mount* mount_;
+ MountNode* node_;
+ int mode_;
+ size_t offs_;
+
+ private:
+ // May only be called by the KernelProxy when the Kernel's
+ // lock is held.
+ friend class KernelObject;
+ friend class KernelProxy;
+ void Acquire() { RefObject::Acquire(); }
+ bool Release() { return RefObject::Release(); }
+ DISALLOW_COPY_AND_ASSIGN(KernelHandle);
+};
+
+#endif // LIBRARIES_NACL_MOUNTS_KERNEL_HANDLE_H_
diff --git a/native_client_sdk/src/libraries/nacl_mounts/kernel_intercept.cc b/native_client_sdk/src/libraries/nacl_mounts/kernel_intercept.cc
new file mode 100644
index 0000000..c111d10
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/kernel_intercept.cc
@@ -0,0 +1,86 @@
+/* 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_mounts/kernel_intercept.h"
+#include "nacl_mounts/kernel_proxy.h"
+
+static KernelProxy* s_kp;
+
+void ki_init(void* kp) {
+ if (kp == NULL) kp = new KernelProxy();
+ s_kp = static_cast<KernelProxy*>(kp);
+ s_kp->Init();
+}
+
+int ki_chdir(const char* path) {
+ return s_kp->chdir(path);
+}
+
+char* ki_getcwd(char* buf, size_t size) {
+ return s_kp->getcwd(buf, size);
+}
+
+char* ki_getwd(char* buf) {
+ return s_kp->getwd(buf);
+}
+
+int ki_dup(int oldfd) {
+ return s_kp->dup(oldfd);
+}
+
+int ki_chmod(const char *path, mode_t mode) {
+ return s_kp->chmod(path, mode);
+}
+
+int ki_stat(const char *path, struct stat *buf) {
+ return s_kp->stat(path, buf);
+}
+
+int ki_mkdir(const char *path, mode_t mode) {
+ return s_kp->mkdir(path, mode);
+}
+
+int ki_rmdir(const char *path) {
+ return s_kp->rmdir(path);
+}
+
+int ki_mount(const char *source, const char *target, const char *filesystemtype,
+ unsigned long mountflags, const void *data) {
+ return s_kp->mount(source, target, filesystemtype, mountflags, data);
+}
+int ki_umount(const char *path) {
+ return s_kp->umount(path);
+}
+int ki_open(const char *path, int oflag) {
+ return s_kp->open(path, oflag);
+}
+
+ssize_t ki_read(int fd, void *buf, size_t nbyte) {
+ return s_kp->read(fd, buf, nbyte);
+}
+
+ssize_t ki_write(int fd, const void *buf, size_t nbyte) {
+ return s_kp->write(fd, buf, nbyte);
+}
+
+int ki_fstat(int fd, struct stat *buf){
+ return s_kp->fstat(fd, buf);
+}
+
+int ki_getdents(int fd, void *buf, unsigned int count) {
+ return s_kp->getdents(fd, buf, count);
+}
+
+int ki_fsync(int fd) {
+ return s_kp->fsync(fd);
+}
+
+int ki_isatty(int fd) {
+ return s_kp->isatty(fd);
+}
+
+int ki_close(int fd) {
+ return s_kp->close(fd);
+}
diff --git a/native_client_sdk/src/libraries/nacl_mounts/kernel_intercept.h b/native_client_sdk/src/libraries/nacl_mounts/kernel_intercept.h
new file mode 100644
index 0000000..05d0f51
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/kernel_intercept.h
@@ -0,0 +1,45 @@
+/* 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_MOUNT_KERNEL_INTERCEPT_H_
+#define LIBRARIES_NACL_MOUNT_KERNEL_INTERCEPT_H_
+
+#include <stdint.h>
+
+#include "utils/macros.h"
+
+EXTERN_C_BEGIN
+
+// The kernel intercept module provides a C->C++ thunk between the libc
+// kernel calls and the KernelProxy singleton.
+
+// ki_init must be called with an uninitialized KernelProxy object. Calling
+// with NULL will instantiate a default kernel proxy object. ki_init must
+// be called before any other ki_XXX function can be used.
+void ki_init(void* kernel_proxy);
+
+int ki_chdir(const char* path);
+char* ki_getcwd(char* buf, size_t size);
+char* ki_getwd(char* buf);
+int ki_dup(int oldfd);
+int ki_chmod(const char* path, mode_t mode);
+int ki_stat(const char* path, struct stat* buf);
+int ki_mkdir(const char* path, mode_t mode);
+int ki_rmdir(const char* path);
+int ki_mount(const char* source, const char* target, const char* filesystemtype,
+ unsigned long mountflags, const void *data);
+int ki_umount(const char* path);
+int ki_open(const char* path, int oflag);
+ssize_t ki_read(int fd, void* buf, size_t nbyte);
+ssize_t ki_write(int fd, const void* buf, size_t nbyte);
+int ki_fstat(int fd, struct stat *buf);
+int ki_getdents(int fd, void* buf, unsigned int count);
+int ki_fsync(int fd);
+int ki_isatty(int fd);
+int ki_close(int fd);
+
+EXTERN_C_END
+
+#endif // LIBRARIES_NACL_MOUNT_KERNEL_INTERCEPT_H_
diff --git a/native_client_sdk/src/libraries/nacl_mounts/kernel_object.cc b/native_client_sdk/src/libraries/nacl_mounts/kernel_object.cc
new file mode 100644
index 0000000..7c048d9
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/kernel_object.cc
@@ -0,0 +1,153 @@
+/* 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/stat.h>
+
+#include <algorithm>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "nacl_mounts/kernel_handle.h"
+#include "nacl_mounts/kernel_object.h"
+#include "nacl_mounts/mount.h"
+#include "nacl_mounts/mount_node.h"
+#include "utils/auto_lock.h"
+
+KernelObject::KernelObject() {
+ pthread_mutex_init(&lock_, NULL);
+}
+
+KernelObject::~KernelObject() {
+ pthread_mutex_destroy(&lock_);
+}
+
+// Uses longest prefix to find the mount for the give path, then
+// acquires the mount and returns it with a relative path.
+Mount* KernelObject::AcquireMountAndPath(const std::string& relpath,
+ Path* out_path) {
+ AutoLock lock(&lock_);
+ Mount* mount = NULL;
+ MountNode* node = NULL;
+
+ Path abs_path = GetAbsPathLocked(relpath);
+
+ // Find longest prefix
+ size_t max = abs_path.Size();
+ for (size_t len = 0; len < abs_path.Size(); len++) {
+ MountMap_t::iterator it = mounts_.find(abs_path.Range(0, max - len));
+ if (it != mounts_.end()) {
+ out_path->Set("/");
+ out_path->Append(abs_path.Range(max - len, max));
+ mount = it->second;
+ break;
+ }
+ }
+
+ if (NULL == mount) {
+ errno = ENOTDIR;
+ return NULL;
+ }
+
+ // Acquire the mount while we hold the proxy lock
+ mount->Acquire();
+ return mount;
+}
+
+void KernelObject::ReleaseMount(Mount* mnt) {
+ AutoLock lock(&lock_);
+ mnt->Release();
+}
+
+KernelHandle* KernelObject::AcquireHandle(int fd) {
+ AutoLock lock(&lock_);
+ if (fd < 0 || fd >= static_cast<int>(handle_map_.size())) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ KernelHandle* handle = handle_map_[fd];
+ if (NULL == handle) {
+ errno = EBADF;
+ return NULL;
+ }
+
+ // Ref count while holding parent mutex
+ handle->Acquire();
+
+ lock.Unlock();
+ if (handle->node_) handle->mount_->AcquireNode(handle->node_);
+
+ return handle;
+}
+
+void KernelObject::ReleaseHandle(KernelHandle* handle) {
+ // The handle must already be held before taking the
+ // kernel lock.
+ if (handle->node_) handle->mount_->ReleaseNode(handle->node_);
+
+ AutoLock lock(&lock_);
+ handle->Release();
+}
+
+// Helper function to properly sort FD order in the heap, forcing
+// lower numberd FD to be available first.
+static bool FdOrder(int i, int j) {
+ return i > j;
+}
+
+int KernelObject::AllocateFD(KernelHandle* handle) {
+ AutoLock lock(&lock_);
+ int id;
+
+ // Acquire then handle and it's mount since we are about
+ // to track them with this FD.
+ handle->Acquire();
+ handle->mount_->Acquire();
+
+ // If we can recycle and FD, use that first
+ if (free_fds_.size()) {
+ id = free_fds_.front();
+ std::pop_heap(free_fds_.begin(), free_fds_.end(), FdOrder);
+ free_fds_.pop_back();
+ handle_map_[id] = handle;
+ } else {
+ id = handle_map_.size();
+ handle_map_.push_back(handle);
+ }
+ return id;
+}
+
+void KernelObject::FreeFD(int fd) {
+ AutoLock lock(&lock_);
+
+ // Release the mount and handle since we no longer
+ // track them with this FD.
+ KernelHandle* handle = handle_map_[fd];
+ handle->mount_->Release();
+ handle->Release();
+
+ handle_map_[fd] = NULL;
+ free_fds_.push_back(fd);
+ std::push_heap(free_fds_.begin(), free_fds_.end(), FdOrder);
+}
+
+Path KernelObject::GetAbsPathLocked(const std::string& path) {
+ // Generate absolute path
+ Path abs_path(cwd_);
+ if (path[0] == '/') {
+ abs_path = path;
+ } else {
+ abs_path = cwd_;
+ abs_path.Append(path);
+ }
+
+ return abs_path;
+}
+
diff --git a/native_client_sdk/src/libraries/nacl_mounts/kernel_object.h b/native_client_sdk/src/libraries/nacl_mounts/kernel_object.h
new file mode 100644
index 0000000..3dca46b
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/kernel_object.h
@@ -0,0 +1,59 @@
+/* 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_MOUNTS_KERNEL_OBJECT_H_
+#define LIBRARIES_NACL_MOUNTS_KERNEL_OBJECT_H_
+
+#include <pthread.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "nacl_mounts/path.h"
+
+class KernelHandle;
+class Mount;
+
+// KernelObject provides basic functionality for threadsafe
+// acces to kernel objects such as file descriptors and
+// file handles. It also provides access to the CWD for
+// path resolution.
+class KernelObject {
+ public:
+ typedef std::vector<KernelHandle*> HandleMap_t;
+ typedef std::map<std::string, Mount*> MountMap_t;
+
+ KernelObject();
+ virtual ~KernelObject();
+
+ // Find the mount for the given path, and acquires it
+ Mount* AcquireMountAndPath(const std::string& relpath, Path *pobj);
+ void ReleaseMount(Mount* mnt);
+
+ // Convert from FD to KernelHandle, and aquire the handle.
+ KernelHandle* AcquireHandle(int fd);
+ void ReleaseHandle(KernelHandle* handle);
+
+ // Allocate a new fd and assign the handle to it, while
+ // ref counting the handle and associated mount.
+ int AllocateFD(KernelHandle* handle);
+ void FreeFD(int fd);
+
+ protected:
+ Path GetAbsPathLocked(const std::string& path);
+
+ std::vector<int> free_fds_;
+ std::string cwd_;
+
+ HandleMap_t handle_map_;
+ MountMap_t mounts_;
+ pthread_mutex_t lock_;
+
+ DISALLOW_COPY_AND_ASSIGN(KernelObject);
+};
+
+#endif // LIBRARIES_NACL_MOUNTS_KERNEL_OBJECT_H_
+
diff --git a/native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.cc b/native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.cc
new file mode 100644
index 0000000..8b09e4a
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.cc
@@ -0,0 +1,357 @@
+/* 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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <sys/stat.h>
+
+#include <string>
+
+#include "nacl_mounts/kernel_handle.h"
+#include "nacl_mounts/kernel_proxy.h"
+#include "nacl_mounts/mount.h"
+#include "nacl_mounts/mount_mem.h"
+#include "nacl_mounts/mount_node.h"
+#include "nacl_mounts/mount_url.h"
+#include "nacl_mounts/path.h"
+
+#include "utils/auto_lock.h"
+#include "utils/ref_object.h"
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 256
+#endif
+
+// TODO(noelallen) : Grab/Redefine these in the kernel object once available.
+#define USR_ID 1002
+#define GRP_ID 1003
+
+
+KernelProxy::KernelProxy() : dev_(0) {}
+KernelProxy::~KernelProxy() {}
+
+void KernelProxy::Init() {
+ cwd_ = "/";
+ dev_ = 1;
+
+ factories_["memfs"] = MountMem::Create;
+ factories_["urlfs"] = MountURL::Create;
+
+ // Create memory mount at root
+ StringMap_t smap;
+ mounts_["/"] = MountMem::Create(dev_++, smap);
+}
+
+
+int KernelProxy::open(const char *path, int oflags) {
+ Path rel;
+
+ Mount* mnt = AcquireMountAndPath(path, &rel);
+ if (mnt == NULL) return -1;
+
+ MountNode* node = mnt->Open(rel, oflags);
+ if (node == NULL) {
+ ReleaseMount(mnt);
+ return -1;
+ }
+
+ KernelHandle* handle = new KernelHandle(mnt, node, oflags);
+ int fd = AllocateFD(handle);
+ mnt->AcquireNode(node);
+
+ ReleaseHandle(handle);
+ ReleaseMount(mnt);
+ return fd;
+}
+
+int KernelProxy::close(int fd) {
+ KernelHandle* handle = AcquireHandle(fd);
+
+ if (NULL == handle) return -1;
+ handle->mount_->Close(handle->node_);
+
+ FreeFD(fd);
+ ReleaseHandle(handle);
+ return 0;
+}
+
+int KernelProxy::dup(int oldfd) {
+ KernelHandle* handle = AcquireHandle(oldfd);
+ if (NULL == handle) return -1;
+
+ int newfd = AllocateFD(handle);
+ ReleaseHandle(handle);
+
+ return newfd;
+}
+
+
+char* KernelProxy::getcwd(char* buf, size_t size) {
+ AutoLock lock(&lock_);
+ if (size <= 0) {
+ errno = EINVAL;
+ return NULL;
+ }
+ // If size is 0, allocate as much as we need.
+ if (size == 0) {
+ size = cwd_.size() + 1;
+ }
+
+ // Verify the buffer is large enough
+ if (size <= cwd_.size()) {
+ errno = ERANGE;
+ return NULL;
+ }
+
+ // Allocate the buffer if needed
+ if (buf == NULL) {
+ buf = static_cast<char*>(malloc(size));
+ }
+
+ strcpy(buf, cwd_.c_str());
+ return buf;
+}
+
+char* KernelProxy::getwd(char* buf) {
+ if (NULL == buf) {
+ errno = EFAULT;
+ return NULL;
+ }
+ return getcwd(buf, MAXPATHLEN);
+}
+
+int KernelProxy::chmod(const char *path, mode_t mode) {
+ int fd = KernelProxy::open(path, O_RDWR);
+ if (-1 == fd) return -1;
+
+ int ret = fchmod(fd, mode);
+ close(fd);
+ return ret;
+}
+
+int KernelProxy::mkdir(const char *path, mode_t mode) {
+ Path rel;
+ Mount* mnt = AcquireMountAndPath(path, &rel);
+ if (mnt == NULL) return -1;
+
+ int val = mnt->Mkdir(rel, mode);
+ ReleaseMount(mnt);
+ return val;
+}
+
+int KernelProxy::rmdir(const char *path) {
+ Path rel;
+ Mount* mnt = AcquireMountAndPath(path, &rel);
+ if (mnt == NULL) return -1;
+
+ int val = mnt->Rmdir(rel);
+ ReleaseMount(mnt);
+ return val;
+}
+
+int KernelProxy::stat(const char *path, struct stat *buf) {
+ int fd = KernelProxy::open(path, O_RDONLY);
+ if (-1 == fd) return -1;
+
+ int ret = fstat(fd, buf);
+ close(fd);
+ return ret;
+}
+
+int KernelProxy::chdir(const char* path) {
+ Path rel;
+ Mount* mnt = AcquireMountAndPath(path, &rel);
+
+ MountNode* node = mnt->Open(rel, O_RDONLY);
+ if (NULL == node) {
+ errno = EEXIST;
+ ReleaseMount(mnt);
+ return -1;
+ }
+
+ bool isDir = node->IsaDir();
+ mnt->Close(node);
+ ReleaseMount(mnt);
+
+ if (isDir) {
+ AutoLock lock(&lock_);
+ cwd_ = GetAbsPathLocked(path).Join();
+ return 0;
+ }
+
+ errno = ENOTDIR;
+ return -1;
+}
+
+int KernelProxy::mount(const char *source, const char *target,
+ const char *filesystemtype, unsigned long mountflags,
+ const void *data) {
+ // See if it's already mounted
+ AutoLock lock(&lock_);
+
+ std::string abs_targ = GetAbsPathLocked(target).Join();
+ if (mounts_.find(abs_targ) != mounts_.end()) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ // Find a factory of that type
+ MountFactoryMap_t::iterator factory = factories_.find(filesystemtype);
+ if (factory == factories_.end()) {
+ errno = ENODEV;
+ return -1;
+ }
+
+ StringMap_t smap;
+ smap["SOURCE"] = source;
+ smap["TARGET"] = abs_targ;
+
+ if (data) {
+ char* str = strdup(static_cast<const char *>(data));
+ char* ptr = strtok(str,",");
+ char* val;
+ while (ptr != NULL) {
+ val = strchr(ptr, '=');
+ if (val) {
+ *val = 0;
+ smap[ptr] = val + 1;
+ } else {
+ smap[ptr] = "TRUE";
+ }
+ ptr = strtok(NULL, ",");
+ }
+ free(str);
+ }
+
+ Mount* mnt = factory->second(dev_++, smap);
+ if (mnt) {
+ mounts_[abs_targ] = mnt;
+ return 0;
+ }
+ errno = EINVAL;
+ return -1;
+}
+
+int KernelProxy::umount(const char *path) {
+ AutoLock lock(&lock_);
+
+ Path abs_path = GetAbsPathLocked(path);
+ MountMap_t::iterator it = mounts_.find(abs_path.Join());
+
+ if (mounts_.end() == it) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (it->second->RefCount() != 1) {
+ errno = EBUSY;
+ return -1;
+ }
+
+ it->second->Release();
+ mounts_.erase(it);
+ return 0;
+}
+
+ssize_t KernelProxy::read(int fd, void *buf, size_t nbytes) {
+ KernelHandle* handle = AcquireHandle(fd);
+
+ // check if fd is valid and handle exists
+ if (NULL == handle) return -1;
+
+ AutoLock lock(&handle->lock_);
+ ssize_t cnt = handle->node_->Read(handle->offs_, buf, nbytes);
+ if (cnt > 0) handle->offs_ += cnt;
+
+ ReleaseHandle(handle);
+ return cnt;
+}
+
+ssize_t KernelProxy::write(int fd, const void *buf, size_t nbytes) {
+ KernelHandle* handle = AcquireHandle(fd);
+
+ // check if fd is valid and handle exists
+ if (NULL == handle) return -1;
+
+ AutoLock lock(&handle->lock_);
+ ssize_t cnt = handle->node_->Write(handle->offs_, buf, nbytes);
+ if (cnt > 0) handle->offs_ += cnt;
+
+ ReleaseHandle(handle);
+ return cnt;
+}
+
+int KernelProxy::fstat(int fd, struct stat* buf) {
+ KernelHandle* handle = AcquireHandle(fd);
+
+ // check if fd is valid and handle exists
+ if (NULL == handle) return -1;
+
+ int ret = handle->node_->GetStat(buf);
+ ReleaseHandle(handle);
+ return ret;
+};
+
+int KernelProxy::getdents(int fd, void* buf, unsigned int count) {
+ KernelHandle* handle = AcquireHandle(fd);
+
+ // check if fd is valid and handle exists
+ if (NULL == handle) return -1;
+
+ AutoLock lock(&handle->lock_);
+ int cnt = handle->node_->GetDents(handle->offs_,
+ static_cast<dirent *>(buf), count);
+
+ if (cnt > 0) handle->offs_ += cnt;
+
+ ReleaseHandle(handle);
+ return cnt;
+}
+
+int KernelProxy::fsync(int fd) {
+ KernelHandle* handle = AcquireHandle(fd);
+
+ // check if fd is valid and handle exists
+ if (NULL == handle) return -1;
+ int ret = handle->node_->FSync();
+
+ ReleaseHandle(handle);
+ return ret;
+}
+
+int KernelProxy::isatty(int fd) {
+ KernelHandle* handle = AcquireHandle(fd);
+
+ // check if fd is valid and handle exists
+ if (NULL == handle) return -1;
+ int ret = handle->node_->IsaTTY();
+
+ ReleaseHandle(handle);
+ return ret;
+}
+
+// TODO(noelallen): Needs implementation.
+int KernelProxy::fchmod(int fd, int mode) {
+ errno = EINVAL;
+ return -1;
+}
+int KernelProxy::remove(const char* path) {
+ errno = EINVAL;
+ return -1;
+}
+int KernelProxy::unlink(const char* path) {
+ errno = EINVAL;
+ return -1;
+}
+int KernelProxy::access(const char*path, int amode) {
+ errno = EINVAL;
+ return -1;
+}
+off_t KernelProxy::lseek(int fd, off_t offset, int whence) {
+ errno = EINVAL;
+ return -1;
+}
diff --git a/native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.h b/native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.h
new file mode 100644
index 0000000..befae9e
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts/kernel_proxy.h
@@ -0,0 +1,93 @@
+/* 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_MOUNTS_KERNEL_PROXY_H_
+#define LIBRARIES_NACL_MOUNTS_KERNEL_PROXY_H_
+
+#include <pthread.h>
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "nacl_mounts/path.h"
+#include "nacl_mounts/kernel_object.h"
+#include "nacl_mounts/mount.h"
+
+class KernelHandle;
+class Mount;
+class MountNode;
+
+// KernelProxy provide one-to-one mapping for libc kernel calls. Calls to the
+// proxy will result in IO access to the provided Mount and MountNode objects.
+class KernelProxy : protected KernelObject {
+ public:
+ typedef Mount* (*MountFactory_t)(int, StringMap_t&);
+ typedef std::map<std::string, std::string> StringMap_t;
+ typedef std::map<std::string, MountFactory_t> MountFactoryMap_t;
+
+ KernelProxy();
+ virtual ~KernelProxy();
+ virtual void Init();
+
+ // KernelHandle and FD allocation and manipulation functions.
+ virtual int open(const char *path, int oflag);
+ virtual int close(int fd);
+ virtual int dup(int fd);
+
+ // System calls handled by KernelProxy (not mount-specific)
+ virtual int chdir(const char* path);
+ virtual char* getcwd(char* buf, size_t size);
+ virtual char* getwd(char* buf);
+ virtual int mount(const char *source, const char *target,
+ const char *filesystemtype, unsigned long mountflags, const void *data);
+ virtual int umount(const char *path);
+
+ // System calls that take a path as an argument:
+ // The kernel proxy will look for the Node associated to the path. To
+ // find the node, the kernel proxy calls the corresponding mount's GetNode()
+ // method. The corresponding method will be called. If the node
+ // cannot be found, errno is set and -1 is returned.
+ virtual int chmod(const char *path, mode_t mode);
+ virtual int mkdir(const char *path, mode_t mode);
+ virtual int rmdir(const char *path);
+ virtual int stat(const char *path, struct stat *buf);
+
+ // System calls that take a file descriptor as an argument:
+ // The kernel proxy will determine to which mount the file
+ // descriptor's corresponding file handle belongs. The
+ // associated mount's function will be called.
+ virtual ssize_t read(int fd, void *buf, size_t nbyte);
+ virtual ssize_t write(int fd, const void *buf, size_t nbyte);
+
+ virtual int fchmod(int fd, int prot);
+ virtual int fstat(int fd, struct stat *buf);
+ virtual int getdents(int fd, void *buf, unsigned int count);
+ virtual int fsync(int fd);
+ virtual int isatty(int fd);
+
+ // lseek() relies on the mount's Stat() to determine whether or not the
+ // file handle corresponding to fd is a directory
+ virtual off_t lseek(int fd, off_t offset, int whence);
+
+ // remove() uses the mount's GetNode() and Stat() to determine whether or
+ // not the path corresponds to a directory or a file. The mount's Rmdir()
+ // or Unlink() is called accordingly.
+ virtual int remove(const char* path);
+ // unlink() is a simple wrapper around the mount's Unlink function.
+ virtual int unlink(const char* path);
+ // access() uses the Mount's Stat().
+ int access(const char*path, int amode);
+
+protected:
+ MountFactoryMap_t factories_;
+ int dev_;
+
+ static KernelProxy *s_instance_;
+
+ DISALLOW_COPY_AND_ASSIGN(KernelProxy);
+};
+
+#endif LIBRARIES_NACL_MOUNTS_KERNEL_PROXY_H_ \ No newline at end of file
diff --git a/native_client_sdk/src/libraries/nacl_mounts_test/kernel_intercept_test.cc b/native_client_sdk/src/libraries/nacl_mounts_test/kernel_intercept_test.cc
new file mode 100644
index 0000000..dc0c0fc
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts_test/kernel_intercept_test.cc
@@ -0,0 +1,181 @@
+/* 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 <fcntl.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+
+#include "nacl_mounts/kernel_intercept.h"
+#include "nacl_mounts/kernel_proxy.h"
+#include "nacl_mounts/path.h"
+
+#define __STDC__ 1
+#include "gtest/gtest.h"
+
+class KernelProxyMock : public KernelProxy {
+ public:
+ KernelProxyMock() {}
+ virtual ~KernelProxyMock() {}
+
+ int chdir(const char* path) {
+ events.push_back("chdir");
+ return 0;
+ }
+ char* getcwd(char* buf, size_t size) {
+ events.push_back("getcwd");
+ return buf;
+ }
+ char* getwd(char* buf) {
+ events.push_back("getwd");
+ return buf;
+ }
+ int dup(int oldfd) {
+ events.push_back("dup");
+ return oldfd;
+ }
+ int chmod(const char *path, mode_t mode) {
+ events.push_back("chmod");
+ return 0;
+ }
+ int stat(const char *path, struct stat *buf) {
+ events.push_back("stat");
+ return 0;
+ }
+ int mkdir(const char *path, mode_t mode) {
+ events.push_back("mkdir");
+ return 0;
+ }
+ int rmdir(const char *path) {
+ events.push_back("rmdir");
+ return 0;
+ }
+ int mount(const char *source, const char *target,
+ const char *filesystemtype, unsigned long mountflags, const void *data) {
+ events.push_back("mount");
+ return 0;
+ }
+ int umount(const char *path) {
+ events.push_back("umount");
+ return 0;
+ }
+ int open(const char *path, int oflag) {
+ events.push_back("open");
+ return 0;
+ }
+ ssize_t read(int fd, void *buf, size_t nbyte) {
+ events.push_back("read");
+ return 0;
+ }
+ ssize_t write(int fd, const void *buf, size_t nbyte) {
+ events.push_back("write");
+ return 0;
+ }
+ int fstat(int fd, struct stat *buf) {
+ events.push_back("fstat");
+ return 0;
+ }
+ int getdents(int fd, void *buf, unsigned int count) {
+ events.push_back("getdents");
+ return 0;
+ }
+ int fsync(int fd) {
+ events.push_back("fsync");
+ return 0;
+ }
+ int isatty(int fd) {
+ events.push_back("isatty");
+ return 0;
+ }
+ int close(int fd) {
+ events.push_back("close");
+ return 0;
+ }
+ off_t lseek(int fd, off_t offset, int whence) {
+ events.push_back("lseek");
+ return 0;
+ }
+ int remove(const std::string& path) {
+ events.push_back("remove");
+ return 0;
+ }
+ int unlink(const std::string& path) {
+ events.push_back("unlink");
+ return 0;
+ }
+ int access(const std::string& path, int amode) {
+ events.push_back("access");
+ return 0;
+ }
+
+ std::string& LastStr() {
+ return events.back();
+ }
+
+ std::vector<std::string> events;
+};
+
+
+TEST(KernelIntercept, SanityChecks) {
+ static KernelProxyMock* mock = new KernelProxyMock();
+ ki_init(mock);
+
+ ki_chdir("foo");
+ EXPECT_EQ("chdir", mock->LastStr());
+
+ ki_getcwd("foo", 1);
+ EXPECT_EQ("getcwd", mock->LastStr());
+
+ ki_getwd("foo");
+ EXPECT_EQ("getwd", mock->LastStr());
+
+ ki_dup(1);
+ EXPECT_EQ("dup", mock->LastStr());
+
+ ki_chmod("foo", NULL);
+ EXPECT_EQ("chmod", mock->LastStr());
+
+ ki_stat("foo", NULL);
+ EXPECT_EQ("stat", mock->LastStr());
+
+ ki_mkdir("foo", 0);
+ EXPECT_EQ("mkdir", mock->LastStr());
+
+ ki_rmdir("foo");
+ EXPECT_EQ("rmdir", mock->LastStr());
+
+ ki_mount("foo", "bar", NULL, 0, NULL);
+ EXPECT_EQ("mount", mock->LastStr());
+
+ ki_umount("foo");
+ EXPECT_EQ("umount", mock->LastStr());
+
+ ki_open("foo", 0);
+ EXPECT_EQ("open", mock->LastStr());
+
+ ki_read(1, NULL, 0);
+ EXPECT_EQ("read", mock->LastStr());
+
+ ki_write(1, NULL, 0);
+ EXPECT_EQ("write", mock->LastStr());
+
+ ki_fstat(1, NULL);
+ EXPECT_EQ("fstat", mock->LastStr());
+
+ ki_getdents(1, NULL, 0);
+ EXPECT_EQ("getdents", mock->LastStr());
+
+ ki_fsync(1);
+ EXPECT_EQ("fsync", mock->LastStr());
+
+ ki_isatty(1);
+ EXPECT_EQ("isatty", mock->LastStr());
+
+ ki_close(1);
+ EXPECT_EQ("close", mock->LastStr());
+}
+
diff --git a/native_client_sdk/src/libraries/nacl_mounts_test/kernel_object_test.cc b/native_client_sdk/src/libraries/nacl_mounts_test/kernel_object_test.cc
new file mode 100644
index 0000000..0c0519e
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts_test/kernel_object_test.cc
@@ -0,0 +1,123 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+
+#include "nacl_mounts/kernel_handle.h"
+#include "nacl_mounts/kernel_object.h"
+#include "nacl_mounts/mount.h"
+#include "nacl_mounts/path.h"
+
+#define __STDC__ 1
+#include "gtest/gtest.h"
+
+int g_MountCnt = 0;
+int g_HandleCnt = 0;
+
+class MountRefMock : public Mount {
+ public:
+ MountRefMock() : Mount() { g_MountCnt++; }
+ ~MountRefMock() { g_MountCnt--; }
+
+ public:
+ MountNode* Open(const Path& path, int mode) { return NULL; }
+ int Close(MountNode* node) { return 0; }
+ int Unlink(const Path& path) { return 0; }
+ int Mkdir(const Path& path, int permissions) { return 0; }
+ int Rmdir(const Path& path) { return 0; }
+};
+
+class KernelHandleRefMock : public KernelHandle {
+ public:
+ KernelHandleRefMock(Mount* mnt, MountNode* node, int flags) :
+ KernelHandle(mnt, node, flags) {
+ g_HandleCnt++;
+ }
+ ~KernelHandleRefMock() {
+ g_HandleCnt--;
+ }
+};
+
+TEST(KernelObject, Referencing) {
+ KernelObject* proxy = new KernelObject();
+ Mount* mnt = new MountRefMock();
+ KernelHandle* handle = new KernelHandleRefMock(mnt, NULL, 0);
+ KernelHandle* handle2 = new KernelHandleRefMock(mnt, NULL, 0);
+
+ // Objects should have one ref when we start
+ EXPECT_EQ(1, mnt->RefCount());
+ EXPECT_EQ(1, handle->RefCount());
+
+ // Objects should have two refs when we get here
+ int fd1 = proxy->AllocateFD(handle);
+ EXPECT_EQ(2, mnt->RefCount());
+ EXPECT_EQ(2, handle->RefCount());
+
+ // If we "dup" the handle, we should bump the refs
+ int fd2 = proxy->AllocateFD(handle);
+ EXPECT_EQ(3, mnt->RefCount());
+ EXPECT_EQ(3, handle->RefCount());
+
+ // If use a new handle with the same values... bump the refs
+ int fd3 = proxy->AllocateFD(handle2);
+ EXPECT_EQ(4, mnt->RefCount());
+ EXPECT_EQ(2, handle2->RefCount());
+
+ // Handles are expectd to come out in order
+ EXPECT_EQ(0, fd1);
+ EXPECT_EQ(1, fd2);
+ EXPECT_EQ(2, fd3);
+
+ // We should find the handle by either fd
+ EXPECT_EQ(handle, proxy->AcquireHandle(fd1));
+ EXPECT_EQ(handle, proxy->AcquireHandle(fd2));
+
+ // A non existant fd should fail
+ EXPECT_EQ(NULL, proxy->AcquireHandle(-1));
+ EXPECT_EQ(EBADF, errno);
+ EXPECT_EQ(NULL, proxy->AcquireHandle(100));
+ EXPECT_EQ(EBADF, errno);
+
+ // Acquiring the handle, should have ref'd it
+ EXPECT_EQ(4, mnt->RefCount());
+ EXPECT_EQ(5, handle->RefCount());
+
+ // Release the handle for each call to acquire
+ proxy->ReleaseHandle(handle);
+ proxy->ReleaseHandle(handle);
+
+ // Release the handle for each time we constructed something
+ proxy->ReleaseHandle(handle);
+ proxy->ReleaseHandle(handle2);
+ proxy->ReleaseMount(mnt);
+
+ // We should now only have references used by the KernelProxy
+ EXPECT_EQ(2, handle->RefCount());
+ EXPECT_EQ(1, handle2->RefCount());
+ EXPECT_EQ(3, mnt->RefCount());
+
+ EXPECT_EQ(2, g_HandleCnt);
+ EXPECT_EQ(1, g_MountCnt);
+
+ proxy->FreeFD(fd1);
+ EXPECT_EQ(2, g_HandleCnt);
+ EXPECT_EQ(1, g_MountCnt);
+
+ proxy->FreeFD(fd3);
+ EXPECT_EQ(1, g_HandleCnt);
+ EXPECT_EQ(1, g_MountCnt);
+
+ proxy->FreeFD(fd2);
+ EXPECT_EQ(0, g_HandleCnt);
+ EXPECT_EQ(0, g_MountCnt);
+}
+
diff --git a/native_client_sdk/src/libraries/nacl_mounts_test/kernel_proxy_test.cc b/native_client_sdk/src/libraries/nacl_mounts_test/kernel_proxy_test.cc
new file mode 100644
index 0000000..67a207d
--- /dev/null
+++ b/native_client_sdk/src/libraries/nacl_mounts_test/kernel_proxy_test.cc
@@ -0,0 +1,156 @@
+/* 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 <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <map>
+#include <string>
+
+#include "nacl_mounts/kernel_handle.h"
+#include "nacl_mounts/kernel_intercept.h"
+#include "nacl_mounts/kernel_proxy.h"
+#include "nacl_mounts/mount.h"
+#include "nacl_mounts/mount_mem.h"
+#include "nacl_mounts/path.h"
+
+#define __STDC__ 1
+#include "gtest/gtest.h"
+
+
+TEST(KernelProxy, WorkingDirectory) {
+ char text[1024];
+
+ ki_init(new KernelProxy());
+
+ text[0] = 0;
+ getcwd(text, sizeof(text));
+ EXPECT_STREQ("/", text);
+
+ char* alloc = getwd(NULL);
+ EXPECT_EQ((char *) NULL, alloc);
+ EXPECT_EQ(EFAULT, errno);
+
+ text[0] = 0;
+ alloc = getwd(text);
+ EXPECT_STREQ("/", alloc);
+
+ EXPECT_EQ(-1, chdir("/foo"));
+ EXPECT_EQ(EEXIST, errno);
+
+ EXPECT_EQ(0, chdir("/"));
+
+ EXPECT_EQ(0, mkdir("/foo", S_IREAD | S_IWRITE));
+ EXPECT_EQ(-1, mkdir("/foo", S_IREAD | S_IWRITE));
+ EXPECT_EQ(EEXIST, errno);
+
+ memset(text, 0, sizeof(text));
+ EXPECT_EQ(0, chdir("foo"));
+ EXPECT_EQ(text, getcwd(text, sizeof(text)));
+ EXPECT_STREQ("/foo", text);
+
+ memset(text, 0, sizeof(text));
+ EXPECT_EQ(-1, chdir("foo"));
+ EXPECT_EQ(EEXIST, errno);
+ EXPECT_EQ(0, chdir(".."));
+ EXPECT_EQ(0, chdir("/foo"));
+ EXPECT_EQ(text, getcwd(text, sizeof(text)));
+ EXPECT_STREQ("/foo", text);
+}
+
+TEST(KernelProxy, MemMountIO) {
+ char text[1024];
+ int fd1, fd2, fd3;
+ int len;
+
+ ki_init(new KernelProxy());
+
+ // Create "/foo"
+ EXPECT_EQ(0, mkdir("/foo", S_IREAD | S_IWRITE));
+
+ // Fail to open "/foo/bar"
+ EXPECT_EQ(-1, open("/foo/bar", O_RDONLY));
+ EXPECT_EQ(ENOENT, errno);
+
+ // Create bar "/foo/bar"
+ fd1 = open("/foo/bar", O_RDONLY | O_CREAT);
+ EXPECT_NE(-1, fd1);
+
+ // Open (optionally create) bar "/foo/bar"
+ fd2 = open("/foo/bar", O_RDONLY | O_CREAT);
+ EXPECT_NE(-1, fd2);
+
+ // Fail to exclusively create bar "/foo/bar"
+ EXPECT_EQ(-1, open("/foo/bar", O_RDONLY | O_CREAT | O_EXCL));
+ EXPECT_EQ(EEXIST, errno);
+
+ // Write hello and world to same node with different descriptors
+ // so that we overwrite each other
+ EXPECT_EQ(5, write(fd2, "WORLD", 5));
+ EXPECT_EQ(5, write(fd1, "HELLO", 5));
+
+ fd3 = open("/foo/bar", O_WRONLY);
+ EXPECT_NE(-1, fd3);
+
+ len = read(fd3, text, sizeof(text));
+ if (len > -0) text[len] = 0;
+ EXPECT_EQ(5, len);
+ EXPECT_STREQ("HELLO", text);
+ EXPECT_EQ(0, close(fd1));
+ EXPECT_EQ(0, close(fd2));
+
+ fd1 = open("/foo/bar", O_WRONLY | O_APPEND);
+ EXPECT_NE(-1, fd1);
+ EXPECT_EQ(5, write(fd1, "WORLD", 5));
+
+ len = read(fd3, text, sizeof(text));
+ if (len >= 0) text[len] = 0;
+
+ EXPECT_EQ(5, len);
+ EXPECT_STREQ("WORLD", text);
+
+ fd2 = open("/foo/bar", O_RDONLY);
+ EXPECT_NE(-1, fd2);
+ len = read(fd2, text, sizeof(text));
+ if (len > 0) text[len] = 0;
+ EXPECT_EQ(10, len);
+ EXPECT_STREQ("HELLOWORLD", text);
+}
+
+StringMap_t g_StringMap;
+
+class MountMockInit : public MountFactory<MountMockInit, MountMem> {
+ public:
+ bool Init(int dev, StringMap_t& args) {
+ g_StringMap = args;
+ if (args.find("false") != args.end())
+ return false;
+ return true;
+ };
+};
+
+class KernelProxyMountMock : public KernelProxy {
+ void Init() {
+ KernelProxy::Init();
+ factories_["initfs"] = MountMockInit::Create;
+ }
+};
+
+TEST(KernelProxy, MountInit) {
+ ki_init(new KernelProxyMountMock());
+ int res1 = mount("/", "/mnt1", "initfs", 0, "false,foo=bar");
+
+ EXPECT_EQ("bar", g_StringMap["foo"]);
+ EXPECT_EQ(-1, res1);
+ EXPECT_EQ(EINVAL, errno);
+
+ int res2 = mount("/", "/mnt2", "initfs", 0, "true,bar=foo,x=y");
+ EXPECT_NE(-1, res2);
+ EXPECT_EQ("y", g_StringMap["x"]);
+} \ No newline at end of file