diff options
author | noelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-08 22:20:40 +0000 |
---|---|---|
committer | noelallen@google.com <noelallen@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-05-08 22:20:40 +0000 |
commit | 754defc88fd112a9a762f9dde3dd7b046b8752c0 (patch) | |
tree | 752ee1b330196823d63c64306661c45769277808 /native_client_sdk | |
parent | 10951ebcca94646c559f5bd7cf556f4b39514b6d (diff) | |
download | chromium_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')
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 |