diff options
author | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-10 21:16:10 +0000 |
---|---|---|
committer | binji@chromium.org <binji@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-06-10 21:16:10 +0000 |
commit | 17511f9115d393261971726fc8a2663b5c29c348 (patch) | |
tree | cfcb8037e51cc9a7d7e742cba9a13d5e57808cdc /native_client_sdk | |
parent | 0e863bbdb3f9af2d6912f3ec9f8f86e2f05668bf (diff) | |
download | chromium_src-17511f9115d393261971726fc8a2663b5c29c348.zip chromium_src-17511f9115d393261971726fc8a2663b5c29c348.tar.gz chromium_src-17511f9115d393261971726fc8a2663b5c29c348.tar.bz2 |
[NaCl SDK] nacl_io: big refactor to return error value (errno).
This is done to prevent forgetting to set it. It also isolates the
responsibility of setting errno to kernel_proxy.
BUG=244171
R=noelallen@chromium.org
Review URL: https://codereview.chromium.org/16232016
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@205308 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'native_client_sdk')
39 files changed, 2330 insertions, 1879 deletions
diff --git a/native_client_sdk/src/build_tools/sdk_files.list b/native_client_sdk/src/build_tools/sdk_files.list index 5fc3614..67eafd0 100644 --- a/native_client_sdk/src/build_tools/sdk_files.list +++ b/native_client_sdk/src/build_tools/sdk_files.list @@ -232,6 +232,7 @@ include/json/reader.h include/json/value.h include/json/writer.h include/KHR/khrplatform.h +include/nacl_io/error.h include/nacl_io/inode_pool.h include/nacl_io/kernel_handle.h include/nacl_io/kernel_intercept.h @@ -247,6 +248,7 @@ include/nacl_io/mount_mem.h include/nacl_io/mount_node_dir.h include/nacl_io/mount_node.h include/nacl_io/mount_node_html5fs.h +include/nacl_io/mount_node_http.h include/nacl_io/mount_node_mem.h include/nacl_io/mount_passthrough.h include/nacl_io/nacl_io.h @@ -667,6 +669,7 @@ src/nacl_io/mount_mem.cc src/nacl_io/mount_node.cc src/nacl_io/mount_node_dir.cc src/nacl_io/mount_node_html5fs.cc +src/nacl_io/mount_node_http.cc src/nacl_io/mount_node_mem.cc src/nacl_io/mount_passthrough.cc src/nacl_io/nacl_io.cc diff --git a/native_client_sdk/src/libraries/nacl_io/error.h b/native_client_sdk/src/libraries/nacl_io/error.h new file mode 100644 index 0000000..8f7510e --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/error.h @@ -0,0 +1,17 @@ +/* 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_ERROR_H_ +#define LIBRARIES_NACL_IO_ERROR_H_ + +struct Error { + // TODO(binji): Add debugging constructor w/ __FILE__, __LINE__. + // crbug.com/247816 + Error(int error) : error(error) {} + operator int() const { return error; } + + int error; +}; + +#endif // LIBRARIES_NACL_IO_ERROR_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc b/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc index dcf42fb..a943894 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_handle.cc @@ -17,40 +17,57 @@ #include "nacl_io/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(); +KernelHandle::KernelHandle(Mount* mnt, MountNode* node) + : mount_(mnt), node_(node), offs_(0) {} + +Error KernelHandle::Init(int open_mode) { + if (open_mode & O_APPEND) { + size_t node_size; + Error error = node_->GetSize(&offs_); + if (error) + return error; + } + + return 0; } -off_t KernelHandle::Seek(off_t offset, int whence) { +Error KernelHandle::Seek(off_t offset, int whence, off_t* out_offset) { + // By default, don't move the offset. + *out_offset = offset; + size_t base; - size_t node_size = node_->GetSize(); + size_t node_size; + Error error = node_->GetSize(&node_size); + if (error) + return error; switch (whence) { - default: return -1; - case SEEK_SET: base = 0; break; - case SEEK_CUR: base = offs_; break; - case SEEK_END: base = node_size; break; + default: + return -1; + case SEEK_SET: + base = 0; + break; + case SEEK_CUR: + base = offs_; + break; + case SEEK_END: + base = node_size; + break; } - if (base + offset < 0) { - errno = EINVAL; - return -1; - } + if (base + offset < 0) + return EINVAL; - offs_ = base + offset; + off_t new_offset = base + offset; // Seeking past the end of the file will zero out the space between the old // end and the new end. - if (offs_ > node_size) { - if (node_->FTruncate(offs_) < 0) { - errno = EINVAL; - return -1; - } + if (new_offset > node_size) { + error = node_->FTruncate(new_offset); + if (error) + return EINVAL; } - return offs_; + *out_offset = offs_ = new_offset; + return 0; } diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_handle.h b/native_client_sdk/src/libraries/nacl_io/kernel_handle.h index 4695be3..ab7b7cd 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_handle.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_handle.h @@ -7,6 +7,7 @@ #include <pthread.h> +#include "nacl_io/error.h" #include "nacl_io/ostypes.h" #include "sdk_util/macros.h" #include "sdk_util/ref_object.h" @@ -19,13 +20,15 @@ class MountNode; // KernelHandle can only be referenced when the KernelProxy lock is held. class KernelHandle : public RefObject { public: - KernelHandle(Mount* mnt, MountNode* node, int oflags); + // Assumes |mnt| and |node| are non-NULL. + KernelHandle(Mount* mnt, MountNode* node); - off_t Seek(off_t offset, int whence); + Error Init(int open_flags); + // Assumes |out_offset| is non-NULL. + Error Seek(off_t offset, int whence, off_t* out_offset); Mount* mount_; MountNode* node_; - int mode_; size_t offs_; private: diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_object.cc b/native_client_sdk/src/libraries/nacl_io/kernel_object.cc index 4841864..6be3c97 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_object.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_object.cc @@ -31,8 +31,12 @@ KernelObject::~KernelObject() { // 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) { +Error KernelObject::AcquireMountAndPath(const std::string& relpath, + Mount** out_mount, + Path* out_path) { + *out_mount = NULL; + *out_path = Path(); + Path abs_path; { AutoLock lock(&process_lock_); @@ -42,7 +46,6 @@ Mount* KernelObject::AcquireMountAndPath(const std::string& relpath, AutoLock lock(&kernel_lock_); Mount* mount = NULL; - // Find longest prefix size_t max = abs_path.Size(); for (size_t len = 0; len < abs_path.Size(); len++) { @@ -55,14 +58,13 @@ Mount* KernelObject::AcquireMountAndPath(const std::string& relpath, } } - if (NULL == mount) { - errno = ENOTDIR; - return NULL; - } + if (NULL == mount) + return ENOTDIR; // Acquire the mount while we hold the proxy lock mount->Acquire(); - return mount; + *out_mount = mount; + return 0; } void KernelObject::ReleaseMount(Mount* mnt) { @@ -70,43 +72,38 @@ void KernelObject::ReleaseMount(Mount* mnt) { mnt->Release(); } -KernelHandle* KernelObject::AcquireHandle(int fd) { +Error KernelObject::AcquireHandle(int fd, KernelHandle** out_handle) { + *out_handle = NULL; + AutoLock lock(&process_lock_); - if (fd < 0 || fd >= static_cast<int>(handle_map_.size())) { - errno = EBADF; - return NULL; - } + if (fd < 0 || fd >= static_cast<int>(handle_map_.size())) + return EBADF; KernelHandle* handle = handle_map_[fd]; - if (NULL == handle) { - errno = EBADF; - return NULL; - } + if (NULL == handle) + return EBADF; // Ref count while holding parent mutex handle->Acquire(); lock.Unlock(); - if (handle->node_) handle->mount_->AcquireNode(handle->node_); + if (handle->node_) + handle->mount_->AcquireNode(handle->node_); - return handle; + *out_handle = handle; + return 0; } void KernelObject::ReleaseHandle(KernelHandle* handle) { // The handle must already be held before taking the // kernel lock. - if (handle->node_) handle->mount_->ReleaseNode(handle->node_); + if (handle->node_) + handle->mount_->ReleaseNode(handle->node_); AutoLock lock(&process_lock_); handle->Release(); } -// Helper function to properly sort FD order in the heap, forcing -// lower numbered FD to be available first. -static bool FdOrder(int i, int j) { - return i > j; -} - int KernelObject::AllocateFD(KernelHandle* handle) { AutoLock lock(&process_lock_); int id; @@ -119,7 +116,8 @@ int KernelObject::AllocateFD(KernelHandle* handle) { // 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); + // Force lower numbered FD to be available first. + std::pop_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>()); free_fds_.pop_back(); handle_map_[id] = handle; } else { @@ -166,7 +164,8 @@ void KernelObject::FreeFD(int fd) { handle_map_[fd] = NULL; free_fds_.push_back(fd); - std::push_heap(free_fds_.begin(), free_fds_.end(), FdOrder); + // Force lower numbered FD to be available first. + std::push_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>()); } Path KernelObject::GetAbsPathLocked(const std::string& path) { diff --git a/native_client_sdk/src/libraries/nacl_io/kernel_object.h b/native_client_sdk/src/libraries/nacl_io/kernel_object.h index 97b197f..eb4c8ac 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_object.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_object.h @@ -11,6 +11,7 @@ #include <string> #include <vector> +#include "nacl_io/error.h" #include "nacl_io/path.h" class KernelHandle; @@ -28,17 +29,25 @@ class KernelObject { KernelObject(); virtual ~KernelObject(); - // Find the mount for the given path, and acquires it - Mount* AcquireMountAndPath(const std::string& relpath, Path *pobj); + // Find the mount for the given path, and acquires it. + // Assumes |out_mount| and |out_path| are non-NULL. + Error AcquireMountAndPath(const std::string& relpath, + Mount** out_mount, + Path* out_path); + // Assumes |mnt| is non-NULL. void ReleaseMount(Mount* mnt); // Convert from FD to KernelHandle, and acquire the handle. - KernelHandle* AcquireHandle(int fd); + // Assumes |out_handle| is non-NULL. + Error AcquireHandle(int fd, KernelHandle** out_handle); + // Assumes |handle| is non-NULL. void ReleaseHandle(KernelHandle* handle); // Allocate a new fd and assign the handle to it, while // ref counting the handle and associated mount. + // Assumes |handle| is non-NULL; int AllocateFD(KernelHandle* handle); + // Assumes |handle| is non-NULL; void FreeAndReassignFD(int fd, KernelHandle* handle); void FreeFD(int fd); 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 b0c449f..2f49395 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.cc @@ -36,16 +36,9 @@ #define USR_ID 1002 #define GRP_ID 1003 +KernelProxy::KernelProxy() : dev_(0), ppapi_(NULL) {} - -KernelProxy::KernelProxy() - : dev_(0), - ppapi_(NULL) { -} - -KernelProxy::~KernelProxy() { - delete ppapi_; -} +KernelProxy::~KernelProxy() { delete ppapi_; } void KernelProxy::Init(PepperInterface* ppapi) { ppapi_ = ppapi; @@ -58,11 +51,12 @@ void KernelProxy::Init(PepperInterface* ppapi) { factories_["httpfs"] = MountHttp::Create<MountHttp>; factories_["passthroughfs"] = MountPassthrough::Create<MountPassthrough>; - // Create passthrough mount at root - StringMap_t smap; - mounts_["/"] = MountPassthrough::Create<MountPassthrough>( - dev_++, smap, ppapi_); - mounts_["/dev"] = MountDev::Create<MountDev>(dev_++, smap, ppapi_); + int result; + result = mount("", "/", "passthroughfs", 0, NULL); + assert(result == 0); + + result = mount("", "/dev", "dev", 0, NULL); + assert(result == 0); // Open the first three in order to get STDIN, STDOUT, STDERR open("/dev/stdin", O_RDONLY); @@ -70,19 +64,32 @@ void KernelProxy::Init(PepperInterface* ppapi) { open("/dev/stderr", O_WRONLY); } -int KernelProxy::open(const char *path, int oflags) { +int KernelProxy::open(const char* path, int oflags) { Path rel; - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; + Mount* mnt; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } - MountNode* node = mnt->Open(rel, oflags); - if (node == NULL) { + MountNode* node = NULL; + error = mnt->Open(rel, oflags, &node); + if (error) { + errno = error; + ReleaseMount(mnt); + return -1; + } + + KernelHandle* handle = new KernelHandle(mnt, node); + error = handle->Init(oflags); + if (error) { + errno = error; ReleaseMount(mnt); return -1; } - KernelHandle* handle = new KernelHandle(mnt, node, oflags); int fd = AllocateFD(handle); mnt->AcquireNode(node); @@ -93,9 +100,12 @@ int KernelProxy::open(const char *path, int oflags) { } int KernelProxy::close(int fd) { - KernelHandle* handle = AcquireHandle(fd); - - if (NULL == handle) return -1; + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } Mount* mount = handle->mount_; // Acquire the mount to ensure FreeFD doesn't prematurely destroy it. @@ -115,8 +125,12 @@ int KernelProxy::close(int fd) { } int KernelProxy::dup(int oldfd) { - KernelHandle* handle = AcquireHandle(oldfd); - if (NULL == handle) return -1; + KernelHandle* handle; + Error error = AcquireHandle(oldfd, &handle); + if (error) { + errno = error; + return -1; + } int newfd = AllocateFD(handle); ReleaseHandle(handle); @@ -126,17 +140,21 @@ int KernelProxy::dup(int oldfd) { int KernelProxy::dup2(int oldfd, int newfd) { // If it's the same file handle, just return - if (oldfd == newfd) return newfd; + if (oldfd == newfd) + return newfd; - KernelHandle* old_handle = AcquireHandle(oldfd); - if (NULL == old_handle) return -1; + KernelHandle* old_handle; + Error error = AcquireHandle(oldfd, &old_handle); + if (error) { + errno = error; + return -1; + } FreeAndReassignFD(newfd, old_handle); ReleaseHandle(old_handle); return newfd; } - char* KernelProxy::getcwd(char* buf, size_t size) { AutoLock lock(&process_lock_); if (size <= 0) { @@ -171,42 +189,64 @@ char* KernelProxy::getwd(char* buf) { return getcwd(buf, MAXPATHLEN); } -int KernelProxy::chmod(const char *path, mode_t mode) { +int KernelProxy::chmod(const char* path, mode_t mode) { int fd = KernelProxy::open(path, O_RDWR); - if (-1 == fd) return -1; + if (-1 == fd) + return -1; - int ret = fchmod(fd, mode); + int result = fchmod(fd, mode); close(fd); - return ret; + return result; } -int KernelProxy::mkdir(const char *path, mode_t mode) { +int KernelProxy::mkdir(const char* path, mode_t mode) { + Mount* mnt; Path rel; - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } + + int result = 0; + error = mnt->Mkdir(rel, mode); + if (error) { + errno = error; + result = -1; + } - int val = mnt->Mkdir(rel, mode); ReleaseMount(mnt); - return val; + return result; } -int KernelProxy::rmdir(const char *path) { +int KernelProxy::rmdir(const char* path) { + Mount* mnt; Path rel; - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } + + int result = 0; + error = mnt->Rmdir(rel); + if (error) { + errno = error; + result = -1; + } - int val = mnt->Rmdir(rel); ReleaseMount(mnt); - return val; + return result; } -int KernelProxy::stat(const char *path, struct stat *buf) { +int KernelProxy::stat(const char* path, struct stat* buf) { int fd = open(path, O_RDONLY); - if (-1 == fd) return -1; + if (-1 == fd) + return -1; - int ret = fstat(fd, buf); + int result = fstat(fd, buf); close(fd); - return ret; + return result; } int KernelProxy::chdir(const char* path) { @@ -215,19 +255,21 @@ int KernelProxy::chdir(const char* path) { return -1; bool is_dir = (statbuf.st_mode & S_IFDIR) != 0; - if (is_dir) { - AutoLock lock(&process_lock_); - cwd_ = GetAbsPathLocked(path).Join(); - return 0; + if (!is_dir) { + errno = ENOTDIR; + return -1; } - errno = ENOTDIR; - return -1; + AutoLock lock(&process_lock_); + cwd_ = GetAbsPathLocked(path).Join(); + return 0; } -int KernelProxy::mount(const char *source, const char *target, - const char *filesystemtype, unsigned long mountflags, - const void *data) { +int KernelProxy::mount(const char* source, + const char* target, + const char* filesystemtype, + unsigned long mountflags, + const void* data) { // See if it's already mounted std::string abs_targ; @@ -255,8 +297,8 @@ int KernelProxy::mount(const char *source, const char *target, smap["TARGET"] = abs_targ; if (data) { - char* str = strdup(static_cast<const char *>(data)); - char* ptr = strtok(str,","); + char* str = strdup(static_cast<const char*>(data)); + char* ptr = strtok(str, ","); char* val; while (ptr != NULL) { val = strchr(ptr, '='); @@ -271,16 +313,18 @@ int KernelProxy::mount(const char *source, const char *target, free(str); } - Mount* mnt = factory->second(dev_++, smap, ppapi_); - if (mnt) { - mounts_[abs_targ] = mnt; - return 0; + Mount* mnt = NULL; + Error error = factory->second(dev_++, smap, ppapi_, &mnt); + if (error) { + errno = error; + return -1; } - errno = EINVAL; - return -1; + + mounts_[abs_targ] = mnt; + return 0; } -int KernelProxy::umount(const char *path) { +int KernelProxy::umount(const char* path) { Path abs_path; // Scope this lock to prevent holding both process and kernel locks @@ -307,125 +351,204 @@ int KernelProxy::umount(const char *path) { 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; +ssize_t KernelProxy::read(int fd, void* buf, size_t nbytes) { + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } AutoLock lock(&handle->lock_); - ssize_t cnt = handle->node_->Read(handle->offs_, buf, nbytes); - if (cnt > 0) handle->offs_ += cnt; + int cnt = 0; + error = handle->node_->Read(handle->offs_, buf, nbytes, &cnt); + if (error) + errno = error; + + 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; +ssize_t KernelProxy::write(int fd, const void* buf, size_t nbytes) { + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } AutoLock lock(&handle->lock_); - ssize_t cnt = handle->node_->Write(handle->offs_, buf, nbytes); - if (cnt > 0) handle->offs_ += cnt; + int cnt = 0; + error = handle->node_->Write(handle->offs_, buf, nbytes, &cnt); + if (error) + errno = error; + + if (cnt > 0) + handle->offs_ += cnt; ReleaseHandle(handle); return cnt; } int KernelProxy::fstat(int fd, struct stat* buf) { - KernelHandle* handle = AcquireHandle(fd); + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } - // check if fd is valid and handle exists - if (NULL == handle) return -1; + int result = 0; + error = handle->node_->GetStat(buf); + if (error) { + errno = error; + result = -1; + } - int ret = handle->node_->GetStat(buf); ReleaseHandle(handle); - return ret; + return result; } 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; + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } AutoLock lock(&handle->lock_); - int cnt = handle->node_->GetDents(handle->offs_, - static_cast<dirent *>(buf), count); + int cnt = 0; + error = handle->node_ + ->GetDents(handle->offs_, static_cast<dirent*>(buf), count, &cnt); + if (error) + errno = error; - if (cnt > 0) handle->offs_ += cnt; + if (cnt > 0) + handle->offs_ += cnt; ReleaseHandle(handle); return cnt; } int KernelProxy::ftruncate(int fd, off_t length) { - KernelHandle* handle = AcquireHandle(fd); + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } - // check if fd is valid and handle exists - if (NULL == handle) return -1; - int ret = handle->node_->FTruncate(length); + int result = 0; + error = handle->node_->FTruncate(length); + if (error) { + errno = error; + result = -1; + } ReleaseHandle(handle); - return ret; + return result; } int KernelProxy::fsync(int fd) { - KernelHandle* handle = AcquireHandle(fd); + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } - // check if fd is valid and handle exists - if (NULL == handle) return -1; - int ret = handle->node_->FSync(); + int result = 0; + error = handle->node_->FSync(); + if (error) { + errno = error; + result = -1; + } ReleaseHandle(handle); - return ret; + return result; } int KernelProxy::isatty(int fd) { - KernelHandle* handle = AcquireHandle(fd); + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } - // check if fd is valid and handle exists - if (NULL == handle) return -1; - int ret = handle->node_->IsaTTY(); + int result = 0; + error = handle->node_->IsaTTY(); + if (error) { + errno = error; + result = -1; + } ReleaseHandle(handle); - return ret; + return result; } off_t KernelProxy::lseek(int fd, off_t offset, int whence) { - KernelHandle* handle = AcquireHandle(fd); - - // check if fd is valid and handle exists - if (NULL == handle) return -1; + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; + return -1; + } AutoLock lock(&handle->lock_); - int ret = handle->Seek(offset, whence); + off_t new_offset; + error = handle->Seek(offset, whence, &new_offset); + if (error) { + errno = error; + new_offset = -1; + } ReleaseHandle(handle); - return ret; + return new_offset; } int KernelProxy::unlink(const char* path) { + Mount* mnt; Path rel; - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } + + int result = 0; + error = mnt->Unlink(rel); + if (error) { + errno = error; + result = -1; + } - int val = mnt->Unlink(rel); ReleaseMount(mnt); - return val; + return result; } int KernelProxy::remove(const char* path) { + Mount* mnt; Path rel; - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } + + int result = 0; + error = mnt->Remove(rel); + if (error) { + errno = error; + result = -1; + } - int val = mnt->Remove(rel); ReleaseMount(mnt); - return val; + return result; } // TODO(noelallen): Needs implementation. @@ -449,22 +572,29 @@ int KernelProxy::symlink(const char* oldpath, const char* newpath) { return -1; } -void* KernelProxy::mmap(void* addr, size_t length, int prot, int flags, int fd, +void* KernelProxy::mmap(void* addr, + size_t length, + int prot, + int flags, + int fd, size_t offset) { // We shouldn't be getting anonymous mmaps here. assert((flags & MAP_ANONYMOUS) == 0); assert(fd != -1); - KernelHandle* handle = AcquireHandle(fd); - - if (NULL == handle) + KernelHandle* handle; + Error error = AcquireHandle(fd, &handle); + if (error) { + errno = error; return MAP_FAILED; + } void* new_addr; { AutoLock lock(&handle->lock_); - new_addr = handle->node_->MMap(addr, length, prot, flags, offset); - if (new_addr == MAP_FAILED) { + error = handle->node_->MMap(addr, length, prot, flags, offset, &new_addr); + if (error) { + errno = error; ReleaseHandle(handle); return MAP_FAILED; } @@ -510,23 +640,34 @@ int KernelProxy::munmap(void* addr, size_t length) { } int KernelProxy::open_resource(const char* path) { + Mount* mnt; Path rel; + Error error = AcquireMountAndPath(path, &mnt, &rel); + if (error) { + errno = error; + return -1; + } - Mount* mnt = AcquireMountAndPath(path, &rel); - if (mnt == NULL) return -1; - - MountNode* node = mnt->OpenResource(rel); - if (node == NULL) { - node = mnt->Open(rel, O_RDONLY); - if (node == NULL) { + MountNode* node = NULL; + error = mnt->OpenResource(rel, &node); + if (error) { + // OpenResource failed, try Open(). + error = mnt->Open(rel, O_RDONLY, &node); + if (error) { + errno = error; ReleaseMount(mnt); return -1; } } - // OpenResource failed, try Open(). + KernelHandle* handle = new KernelHandle(mnt, node); + error = handle->Init(O_RDONLY); + if (error) { + errno = error; + ReleaseMount(mnt); + return -1; + } - KernelHandle* handle = new KernelHandle(mnt, node, O_RDONLY); int fd = AllocateFD(handle); mnt->AcquireNode(node); 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 6c7b6b3..e8c251a 100644 --- a/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h +++ b/native_client_sdk/src/libraries/nacl_io/kernel_proxy.h @@ -24,9 +24,12 @@ class PepperInterface; // 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. +// +// 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 KernelProxy : protected KernelObject { public: - typedef Mount* (*MountFactory_t)(int, StringMap_t&, PepperInterface*); + typedef Error (*MountFactory_t)(int, StringMap_t&, PepperInterface*, Mount**); typedef std::map<std::string, std::string> StringMap_t; typedef std::map<std::string, MountFactory_t> MountFactoryMap_t; diff --git a/native_client_sdk/src/libraries/nacl_io/library.dsc b/native_client_sdk/src/libraries/nacl_io/library.dsc index 612af7d..81d621c 100644 --- a/native_client_sdk/src/libraries/nacl_io/library.dsc +++ b/native_client_sdk/src/libraries/nacl_io/library.dsc @@ -24,6 +24,7 @@ "mount_node.cc", "mount_node_dir.cc", "mount_node_html5fs.cc", + "mount_node_http.cc", "mount_node_mem.cc", "mount_passthrough.cc", "nacl_io.cc", @@ -36,6 +37,7 @@ 'HEADERS': [ { 'FILES': [ + "error.h", "inode_pool.h", "kernel_handle.h", "kernel_intercept.h", @@ -51,6 +53,7 @@ "mount_node_dir.h", "mount_node.h", "mount_node_html5fs.h", + "mount_node_http.h", "mount_node_mem.h", "mount_passthrough.h", "nacl_io.h", diff --git a/native_client_sdk/src/libraries/nacl_io/mount.cc b/native_client_sdk/src/libraries/nacl_io/mount.cc index f099aa8..76fe3a7 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount.cc @@ -20,16 +20,14 @@ #include <windows.h> #endif -Mount::Mount() - : dev_(0) { -} +Mount::Mount() : dev_(0) {} Mount::~Mount() {} -bool Mount::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { +Error Mount::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { dev_ = dev; ppapi_ = ppapi; - return true; + return 0; } void Mount::Destroy() {} @@ -44,23 +42,30 @@ void Mount::ReleaseNode(MountNode* node) { node->Release(); } +Error Mount::OpenResource(const Path& path, MountNode** out_node) { + *out_node = NULL; + return EINVAL; +} + int Mount::OpenModeToPermission(int mode) { int out; switch (mode & 3) { - case O_RDONLY: out = S_IREAD; - case O_WRONLY: out = S_IWRITE; - case O_RDWR: out = S_IREAD | S_IWRITE; + case O_RDONLY: + out = S_IREAD; + case O_WRONLY: + out = S_IWRITE; + case O_RDWR: + out = S_IREAD | S_IWRITE; } return out; } - void Mount::OnNodeCreated(MountNode* node) { node->stat_.st_ino = inode_pool_.Acquire(); node->stat_.st_dev = dev_; } void Mount::OnNodeDestroyed(MountNode* node) { - if (node->stat_.st_ino) inode_pool_.Release(node->stat_.st_ino); + if (node->stat_.st_ino) + inode_pool_.Release(node->stat_.st_ino); } - diff --git a/native_client_sdk/src/libraries/nacl_io/mount.h b/native_client_sdk/src/libraries/nacl_io/mount.h index abfe68f..ddf1fd5 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount.h +++ b/native_client_sdk/src/libraries/nacl_io/mount.h @@ -8,6 +8,7 @@ #include <map> #include <string> +#include "nacl_io/error.h" #include "nacl_io/inode_pool.h" #include "nacl_io/mount_node.h" #include "nacl_io/path.h" @@ -19,7 +20,8 @@ class PepperInterface; typedef std::map<std::string, std::string> StringMap_t; - +// 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 RefObject { protected: // The protected functions are only used internally and will not @@ -30,38 +32,49 @@ class Mount : public RefObject { // 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 bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); public: template <class M> - static Mount* Create(int dev, StringMap_t& args, PepperInterface* ppapi); + // Assumes that |out_mount| is non-NULL. + static Error Create(int dev, + StringMap_t& args, + PepperInterface* ppapi, + Mount** out_mount); PepperInterface* ppapi() { return ppapi_; } - // All paths are expected to containing a leading "/" + // Assumes that |node| is non-NULL. void AcquireNode(MountNode* node); + // Assumes that |node| is non-NULL. void ReleaseNode(MountNode* node); + // All paths in functions below are expected to containing a leading "/". + // Open a node at |path| with the specified open flags. The resulting // MountNode is created with a ref count of 1. - virtual MountNode *Open(const Path& path, int o_flags) = 0; + // Assumes that |out_node| is non-NULL. + virtual Error Open(const Path& path, int o_flags, MountNode** out_node) = 0; // OpenResource is only used to read files from the NaCl NMF file. No mount // except MountPassthrough should implement it. - virtual MountNode *OpenResource(const Path& path) { return NULL; } + // Assumes that |out_node| is non-NULL. + virtual Error OpenResource(const Path& path, MountNode** out_node); // Unlink, Mkdir, Rmdir will affect the both the RefCount // and the nlink number in the stat object. - virtual int Unlink(const Path& path) = 0; - virtual int Mkdir(const Path& path, int permissions) = 0; - virtual int Rmdir(const Path& path) = 0; - virtual int Remove(const Path& path) = 0; + virtual Error Unlink(const Path& path) = 0; + virtual Error Mkdir(const Path& path, int permissions) = 0; + virtual Error Rmdir(const Path& path) = 0; + virtual Error Remove(const Path& path) = 0; // Convert from R,W,R/W open flags to STAT permission flags static int OpenModeToPermission(int mode); - void OnNodeCreated(MountNode* node) ; + // Assumes that |node| is non-NULL. + void OnNodeCreated(MountNode* node); + // Assumes that |node| is non-NULL. void OnNodeDestroyed(MountNode* node); protected: @@ -81,16 +94,22 @@ class Mount : public RefObject { DISALLOW_COPY_AND_ASSIGN(Mount); }; - -template <class M> /*static*/ -Mount* Mount::Create(int dev, StringMap_t& args, PepperInterface* ppapi) { +template <class M> +Error Mount::Create(int dev, + StringMap_t& args, + PepperInterface* ppapi, + Mount** out_mount) { Mount* mnt = new M(); - if (mnt->Init(dev, args, ppapi) == false) { + Error error = mnt->Init(dev, args, ppapi); + if (error) { delete mnt; - return NULL; + *out_mount = NULL; + return error; } - return mnt; + + *out_mount = mnt; + return 0; } #endif // LIBRARIES_NACL_IO_MOUNT_H_ 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 9190a92..11893ea 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_dev.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.cc @@ -17,28 +17,23 @@ #include "sdk_util/auto_lock.h" #if defined(__native_client__) -# include <irt.h> +#include <irt.h> #elif defined(WIN32) -# include <stdlib.h> +#include <stdlib.h> #endif - namespace { -void ReleaseAndNullNode(MountNode** node) { - if (*node) - (*node)->Release(); - *node = NULL; -} - - class RealNode : public MountNode { public: RealNode(Mount* mount, int fd); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); - virtual int GetStat(struct stat* stat); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + virtual Error GetStat(struct stat* stat); protected: int fd_; @@ -48,43 +43,56 @@ class NullNode : public MountNode { public: explicit NullNode(Mount* mount); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); }; class ConsoleNode : public NullNode { public: ConsoleNode(Mount* mount, PP_LogLevel level); - virtual int Write(size_t offs, const void* buf, size_t count); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); -private: + private: PP_LogLevel level_; }; - class TtyNode : public NullNode { public: explicit TtyNode(Mount* mount); - virtual int Write(size_t offs, const void* buf, size_t count); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); }; - class ZeroNode : public MountNode { public: explicit ZeroNode(Mount* mount); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); }; class UrandomNode : public MountNode { public: explicit UrandomNode(Mount* mount); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); private: #if defined(__native_client__) @@ -93,132 +101,141 @@ class UrandomNode : public MountNode { #endif }; -RealNode::RealNode(Mount* mount, int fd) - : MountNode(mount), - fd_(fd) { +RealNode::RealNode(Mount* mount, int fd) : MountNode(mount), fd_(fd) { stat_.st_mode = S_IFCHR; } -int RealNode::Read(size_t offs, void* buf, size_t count) { +Error RealNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; + size_t readcnt; int err = _real_read(fd_, buf, count, &readcnt); - if (err) { - errno = err; - return -1; - } - return static_cast<int>(readcnt); + if (err) + return err; + + *out_bytes = static_cast<int>(readcnt); + return 0; } -int RealNode::Write(size_t offs, const void* buf, size_t count) { +Error RealNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + size_t writecnt; int err = _real_write(fd_, buf, count, &writecnt); - if (err) { - errno = err; - return -1; - } - return static_cast<int>(writecnt); -} + if (err) + return err; -int RealNode::GetStat(struct stat* stat) { - int err = _real_fstat(fd_, stat); - if (err) { - errno = err; - return -1; - } + *out_bytes = static_cast<int>(writecnt); return 0; } -NullNode::NullNode(Mount* mount) - : MountNode(mount) { - stat_.st_mode = S_IFCHR; -} +Error RealNode::GetStat(struct stat* stat) { return _real_fstat(fd_, stat); } -int NullNode::Read(size_t offs, void* buf, size_t count) { +NullNode::NullNode(Mount* mount) : MountNode(mount) { stat_.st_mode = S_IFCHR; } + +Error NullNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; return 0; } -int NullNode::Write(size_t offs, const void* buf, size_t count) { - return count; +Error NullNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = count; + return 0; } ConsoleNode::ConsoleNode(Mount* mount, PP_LogLevel level) - : NullNode(mount), - level_(level) { + : NullNode(mount), level_(level) { stat_.st_mode = S_IFCHR; } -int ConsoleNode::Write(size_t offs, const void* buf, size_t count) { +Error ConsoleNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + ConsoleInterface* con_intr = mount_->ppapi()->GetConsoleInterface(); VarInterface* var_intr = mount_->ppapi()->GetVarInterface(); - if (var_intr && con_intr) { - const char* data = static_cast<const char *>(buf); - uint32_t len = static_cast<uint32_t>(count); - struct PP_Var val = var_intr->VarFromUtf8(data, len); - con_intr->Log(mount_->ppapi()->GetInstance(), level_, val); - return count; - } + if (!(var_intr && con_intr)) + return ENOSYS; + + const char* data = static_cast<const char*>(buf); + uint32_t len = static_cast<uint32_t>(count); + struct PP_Var val = var_intr->VarFromUtf8(data, len); + con_intr->Log(mount_->ppapi()->GetInstance(), level_, val); + + *out_bytes = count; return 0; } +TtyNode::TtyNode(Mount* mount) : NullNode(mount) {} -TtyNode::TtyNode(Mount* mount) - : NullNode(mount) { -} +Error TtyNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; -int TtyNode::Write(size_t offs, const void* buf, size_t count) { MessagingInterface* msg_intr = mount_->ppapi()->GetMessagingInterface(); VarInterface* var_intr = mount_->ppapi()->GetVarInterface(); - if (var_intr && msg_intr) { - const char* data = static_cast<const char *>(buf); - uint32_t len = static_cast<uint32_t>(count); - struct PP_Var val = var_intr->VarFromUtf8(data, len); - msg_intr->PostMessage(mount_->ppapi()->GetInstance(), val); - return count; - } - return 0; -} + if (!(var_intr && msg_intr)) + return ENOSYS; + const char* data = static_cast<const char*>(buf); + uint32_t len = static_cast<uint32_t>(count); + struct PP_Var val = var_intr->VarFromUtf8(data, len); + msg_intr->PostMessage(mount_->ppapi()->GetInstance(), val); -ZeroNode::ZeroNode(Mount* mount) - : MountNode(mount) { - stat_.st_mode = S_IFCHR; + *out_bytes = count; + return 0; } -int ZeroNode::Read(size_t offs, void* buf, size_t count) { +ZeroNode::ZeroNode(Mount* mount) : MountNode(mount) { stat_.st_mode = S_IFCHR; } + +Error ZeroNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { memset(buf, 0, count); - return count; + *out_bytes = count; + return 0; } -int ZeroNode::Write(size_t offs, const void* buf, size_t count) { - return count; +Error ZeroNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = count; + return 0; } -UrandomNode::UrandomNode(Mount* mount) - : MountNode(mount) { +UrandomNode::UrandomNode(Mount* mount) : MountNode(mount) { stat_.st_mode = S_IFCHR; #if defined(__native_client__) - size_t result = nacl_interface_query(NACL_IRT_RANDOM_v0_1, &random_interface_, - sizeof(random_interface_)); + size_t result = nacl_interface_query( + NACL_IRT_RANDOM_v0_1, &random_interface_, sizeof(random_interface_)); interface_ok_ = result != 0; #endif } -int UrandomNode::Read(size_t offs, void* buf, size_t count) { +Error UrandomNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; + #if defined(__native_client__) - if (interface_ok_) { - size_t nread; - int result = (*random_interface_.get_random_bytes)(buf, count, &nread); - if (result != 0) { - errno = result; - return 0; - } + if (!interface_ok_) + return EBADF; - return count; - } + size_t nread; + int error = (*random_interface_.get_random_bytes)(buf, count, &nread); + if (error) + return error; - errno = EBADF; + *out_bytes = count; return 0; #elif defined(WIN32) char* out = static_cast<char*>(buf); @@ -227,8 +244,8 @@ int UrandomNode::Read(size_t offs, void* buf, size_t count) { unsigned int random_int; errno_t err = rand_s(&random_int); if (err) { - errno = err; - return count - bytes_left; + *out_bytes = count - bytes_left; + return err; } int bytes_to_copy = std::min(bytes_left, sizeof(random_int)); @@ -237,107 +254,84 @@ int UrandomNode::Read(size_t offs, void* buf, size_t count) { bytes_left -= bytes_to_copy; } - return count; + *out_bytes = count; + return 0; #endif } -int UrandomNode::Write(size_t offs, const void* buf, size_t count) { - return count; +Error UrandomNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = count; + return 0; } - - } // namespace -MountNode *MountDev::Open(const Path& path, int mode) { +Error MountDev::Open(const Path& path, int mode, MountNode** out_node) { + *out_node = NULL; + AutoLock lock(&lock_); // Don't allow creating any files. if (mode & O_CREAT) - return NULL; + return EINVAL; - MountNode* node = root_->FindChild(path.Join()); - if (node) - node->Acquire(); - return node; -} + MountNode* node = NULL; + int error = root_->FindChild(path.Join(), &node); + if (error) + return error; -int MountDev::Unlink(const Path& path) { - errno = EINVAL; - return -1; + node->Acquire(); + *out_node = node; + return 0; } -int MountDev::Mkdir(const Path& path, int permissions) { - errno = EINVAL; - return -1; -} +Error MountDev::Unlink(const Path& path) { return EINVAL; } -int MountDev::Rmdir(const Path& path) { - errno = EINVAL; - return -1; -} +Error MountDev::Mkdir(const Path& path, int permissions) { return EINVAL; } -int MountDev::Remove(const Path& path) { - errno = EINVAL; - return -1; -} +Error MountDev::Rmdir(const Path& path) { return EINVAL; } -MountDev::MountDev() - : null_node_(NULL), - zero_node_(NULL), - random_node_(NULL), - console0_node_(NULL), - console1_node_(NULL), - console2_node_(NULL), - console3_node_(NULL), - tty_node_(NULL) { -} +Error MountDev::Remove(const Path& path) { return EINVAL; } -bool MountDev::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { - if (!Mount::Init(dev, args, ppapi)) - return false; +MountDev::MountDev() {} + +#define INITIALIZE_DEV_NODE(path, klass) \ + error = root_->AddChild(path, new klass(this)); \ + if (error) \ + return error; + +#define INITIALIZE_DEV_NODE_1(path, klass, arg) \ + error = root_->AddChild(path, new klass(this, arg)); \ + if (error) \ + return error; + +Error MountDev::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { + Error error = Mount::Init(dev, args, ppapi); + if (error) + return error; root_ = new MountNodeDir(this); - null_node_ = new NullNode(this); - root_->AddChild("/null", null_node_); - zero_node_ = new ZeroNode(this); - root_->AddChild("/zero", zero_node_); - random_node_ = new UrandomNode(this); - root_->AddChild("/urandom", random_node_); - - console0_node_ = new ConsoleNode(this, PP_LOGLEVEL_TIP); - root_->AddChild("/console0", console0_node_); - console1_node_ = new ConsoleNode(this, PP_LOGLEVEL_LOG); - root_->AddChild("/console1", console1_node_); - console2_node_ = new ConsoleNode(this, PP_LOGLEVEL_WARNING); - root_->AddChild("/console2", console2_node_); - console3_node_ = new ConsoleNode(this, PP_LOGLEVEL_ERROR); - root_->AddChild("/console3", console3_node_); - - tty_node_ = new TtyNode(this); - root_->AddChild("/tty", tty_node_); - - stdin_node_ = new RealNode(this, 0); - root_->AddChild("/stdin", stdin_node_); - stdout_node_ = new RealNode(this, 1); - root_->AddChild("/stdout", stdout_node_); - stderr_node_ = new RealNode(this, 2); - root_->AddChild("/stderr", stderr_node_); - - return true; + + INITIALIZE_DEV_NODE("/null", NullNode); + INITIALIZE_DEV_NODE("/zero", ZeroNode); + INITIALIZE_DEV_NODE("/urandom", UrandomNode); + INITIALIZE_DEV_NODE_1("/console0", ConsoleNode, PP_LOGLEVEL_TIP); + INITIALIZE_DEV_NODE_1("/console1", ConsoleNode, PP_LOGLEVEL_LOG); + INITIALIZE_DEV_NODE_1("/console2", ConsoleNode, PP_LOGLEVEL_WARNING); + INITIALIZE_DEV_NODE_1("/console3", ConsoleNode, PP_LOGLEVEL_ERROR); + INITIALIZE_DEV_NODE("/tty", TtyNode); + INITIALIZE_DEV_NODE_1("/stdin", RealNode, 0); + INITIALIZE_DEV_NODE_1("/stdout", RealNode, 1); + INITIALIZE_DEV_NODE_1("/stderr", RealNode, 2); + + return 0; } void MountDev::Destroy() { - ReleaseAndNullNode(&stdin_node_); - ReleaseAndNullNode(&stdout_node_); - ReleaseAndNullNode(&stderr_node_); - ReleaseAndNullNode(&tty_node_); - ReleaseAndNullNode(&console3_node_); - ReleaseAndNullNode(&console2_node_); - ReleaseAndNullNode(&console1_node_); - ReleaseAndNullNode(&console0_node_); - ReleaseAndNullNode(&random_node_); - ReleaseAndNullNode(&zero_node_); - ReleaseAndNullNode(&null_node_); - ReleaseAndNullNode(&root_); + if (root_) + root_->Release(); + root_ = NULL; } 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 ff3d99c1..7ad7daa 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_dev.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_dev.h @@ -11,32 +11,21 @@ class MountNode; class MountDev : public Mount { public: - virtual MountNode *Open(const Path& path, int mode); + virtual Error Open(const Path& path, int mode, MountNode** out_node); - virtual int Unlink(const Path& path); - virtual int Mkdir(const Path& path, int permissions); - virtual int Rmdir(const Path& path); - virtual int Remove(const Path& path); + virtual Error Unlink(const Path& path); + virtual Error Mkdir(const Path& path, int permissions); + virtual Error Rmdir(const Path& path); + virtual Error Remove(const Path& path); protected: MountDev(); - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); private: MountNode* root_; - MountNode* null_node_; - MountNode* zero_node_; - MountNode* random_node_; - MountNode* console0_node_; - MountNode* console1_node_; - MountNode* console2_node_; - MountNode* console3_node_; - MountNode* tty_node_; - MountNode* stderr_node_; - MountNode* stdin_node_; - MountNode* stdout_node_; friend class Mount; }; 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 c49acae..0008284 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.cc @@ -24,96 +24,85 @@ int64_t strtoull(const char* nptr, char** endptr, int base) { } // namespace -MountNode *MountHtml5Fs::Open(const Path& path, int mode) { - if (BlockUntilFilesystemOpen() != PP_OK) { - errno = ENODEV; - return NULL; - } +Error MountHtml5Fs::Open(const Path& path, int mode, MountNode** out_node) { + *out_node = NULL; + + Error error = BlockUntilFilesystemOpen(); + if (error) + return error; - PP_Resource fileref = ppapi()->GetFileRefInterface()->Create( - filesystem_resource_, path.Join().c_str()); + PP_Resource fileref = ppapi()->GetFileRefInterface() + ->Create(filesystem_resource_, path.Join().c_str()); if (!fileref) - return NULL; + return ENOSYS; MountNodeHtml5Fs* node = new MountNodeHtml5Fs(this, fileref); - if (!node->Init(mode)) { + error = node->Init(mode); + if (error) { node->Release(); - return NULL; + return error; } - return node; + *out_node = node; + return 0; } -int MountHtml5Fs::Unlink(const Path& path) { - return Remove(path); -} +Error MountHtml5Fs::Unlink(const Path& path) { return Remove(path); } -int MountHtml5Fs::Mkdir(const Path& path, int permissions) { - if (BlockUntilFilesystemOpen() != PP_OK) { - errno = ENODEV; - return -1; - } +Error MountHtml5Fs::Mkdir(const Path& path, int permissions) { + Error error = BlockUntilFilesystemOpen(); + if (error) + return error; ScopedResource fileref_resource( - ppapi(), ppapi()->GetFileRefInterface()->Create(filesystem_resource_, - path.Join().c_str())); - if (!fileref_resource.pp_resource()) { - errno = EINVAL; - return -1; - } + ppapi(), + ppapi()->GetFileRefInterface()->Create(filesystem_resource_, + path.Join().c_str())); + if (!fileref_resource.pp_resource()) + return EIO; int32_t result = ppapi()->GetFileRefInterface()->MakeDirectory( fileref_resource.pp_resource(), PP_FALSE, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } + if (result != PP_OK) + return PPErrorToErrno(result); return 0; } -int MountHtml5Fs::Rmdir(const Path& path) { - return Remove(path); -} +Error MountHtml5Fs::Rmdir(const Path& path) { return Remove(path); } -int MountHtml5Fs::Remove(const Path& path) { - if (BlockUntilFilesystemOpen() != PP_OK) { - errno = ENODEV; - return -1; - } +Error MountHtml5Fs::Remove(const Path& path) { + Error error = BlockUntilFilesystemOpen(); + if (error) + return error; ScopedResource fileref_resource( - ppapi(), ppapi()->GetFileRefInterface()->Create(filesystem_resource_, - path.Join().c_str())); - if (!fileref_resource.pp_resource()) { - errno = EINVAL; - return -1; - } + ppapi(), + ppapi()->GetFileRefInterface()->Create(filesystem_resource_, + path.Join().c_str())); + if (!fileref_resource.pp_resource()) + return ENOSYS; - int32_t result = ppapi()->GetFileRefInterface()->Delete( - fileref_resource.pp_resource(), - PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } + int32_t result = ppapi()->GetFileRefInterface() + ->Delete(fileref_resource.pp_resource(), PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); return 0; } - MountHtml5Fs::MountHtml5Fs() : filesystem_resource_(0), filesystem_open_has_result_(false), - filesystem_open_result_(PP_OK) { -} + filesystem_open_error_(0) {} -bool MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { - if (!Mount::Init(dev, args, ppapi)) - return false; +Error MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { + Error error = Mount::Init(dev, args, ppapi); + if (error) + return error; if (!ppapi) - return false; + return ENOSYS; pthread_cond_init(&filesystem_open_cond_, NULL); @@ -121,7 +110,7 @@ bool MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { PP_FileSystemType filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT; int64_t expected_size = 0; for (StringMap_t::iterator iter = args.begin(), end = args.end(); iter != end; - ++iter) { + ++iter) { if (iter->first == "type") { if (iter->second == "PERSISTENT") { filesystem_type = PP_FILESYSTEMTYPE_LOCALPERSISTENT; @@ -134,33 +123,32 @@ bool MountHtml5Fs::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { } // Initialize filesystem. - filesystem_resource_ = ppapi->GetFileSystemInterface()->Create( - ppapi_->GetInstance(), filesystem_type); - + filesystem_resource_ = ppapi->GetFileSystemInterface() + ->Create(ppapi_->GetInstance(), filesystem_type); if (filesystem_resource_ == 0) - return false; + return ENOSYS; // 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->IsMainThread(); - PP_CompletionCallback cc = main_thread ? - PP_MakeCompletionCallback(&MountHtml5Fs::FilesystemOpenCallbackThunk, - this) : - PP_BlockUntilComplete(); + PP_CompletionCallback cc = + main_thread ? PP_MakeCompletionCallback( + &MountHtml5Fs::FilesystemOpenCallbackThunk, this) + : PP_BlockUntilComplete(); - int32_t result = ppapi->GetFileSystemInterface()->Open( - filesystem_resource_, expected_size, cc); + int32_t result = ppapi->GetFileSystemInterface() + ->Open(filesystem_resource_, expected_size, cc); if (!main_thread) { filesystem_open_has_result_ = true; - filesystem_open_result_ = result; + filesystem_open_error_ = PPErrorToErrno(result); - return filesystem_open_result_ == PP_OK; + return filesystem_open_error_; } else { // We have to assume the call to Open will succeed; there is no better // result to return here. - return true; + return 0; } } @@ -169,12 +157,12 @@ void MountHtml5Fs::Destroy() { pthread_cond_destroy(&filesystem_open_cond_); } -int32_t MountHtml5Fs::BlockUntilFilesystemOpen() { +Error MountHtml5Fs::BlockUntilFilesystemOpen() { AutoLock lock(&lock_); while (!filesystem_open_has_result_) { pthread_cond_wait(&filesystem_open_cond_, &lock_); } - return filesystem_open_result_; + return filesystem_open_error_; } // static @@ -187,6 +175,6 @@ void MountHtml5Fs::FilesystemOpenCallbackThunk(void* user_data, void MountHtml5Fs::FilesystemOpenCallback(int32_t result) { AutoLock lock(&lock_); filesystem_open_has_result_ = true; - filesystem_open_result_ = result; + filesystem_open_error_ = PPErrorToErrno(result); pthread_cond_signal(&filesystem_open_cond_); } 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 a6633be..e872dae 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_html5fs.h @@ -11,23 +11,23 @@ class MountNode; -class MountHtml5Fs: public Mount { +class MountHtml5Fs : public Mount { public: - virtual MountNode *Open(const Path& path, int mode); - virtual int Unlink(const Path& path); - virtual int Mkdir(const Path& path, int permissions); - virtual int Rmdir(const Path& path); - virtual int Remove(const Path& path); + virtual Error Open(const Path& path, int mode, MountNode** out_node); + virtual Error Unlink(const Path& path); + virtual Error Mkdir(const Path& path, int permissions); + virtual Error Rmdir(const Path& path); + virtual Error Remove(const Path& path); PP_Resource filesystem_resource() { return filesystem_resource_; } protected: MountHtml5Fs(); - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); - int32_t BlockUntilFilesystemOpen(); + Error BlockUntilFilesystemOpen(); private: static void FilesystemOpenCallbackThunk(void* user_data, int32_t result); @@ -35,7 +35,7 @@ class MountHtml5Fs: public Mount { PP_Resource filesystem_resource_; bool filesystem_open_has_result_; // protected by lock_. - int32_t filesystem_open_result_; // protected by lock_. + Error filesystem_open_error_; // protected by lock_. pthread_cond_t filesystem_open_cond_; friend class Mount; 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 9e6155f..7b4316e 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_http.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_http.cc @@ -4,30 +4,30 @@ */ #include "nacl_io/mount_http.h" + #include <assert.h> #include <ctype.h> #include <errno.h> #include <fcntl.h> -#include <ppapi/c/pp_errors.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/types.h> + #include <vector> + +#include <ppapi/c/pp_errors.h> + #include "nacl_io/mount_node_dir.h" +#include "nacl_io/mount_node_http.h" #include "nacl_io/osinttypes.h" #include "sdk_util/auto_lock.h" -#if defined(WIN32) -#define snprintf _snprintf -#endif - - namespace { -typedef std::vector<char *> StringList_t; -size_t SplitString(char *str, const char *delim, StringList_t* list) { - char *item = strtok(str, delim); +typedef std::vector<char*> StringList_t; +size_t SplitString(char* str, const char* delim, StringList_t* list) { + char* item = strtok(str, delim); list->clear(); while (item) { @@ -38,13 +38,7 @@ size_t SplitString(char *str, const char *delim, StringList_t* list) { return list->size(); } - -// If we're attempting to read a partial request, but the server returns a full -// request, we need to read all of the data up to the start of our partial -// request into a dummy buffer. This is the maximum size of that buffer. -const size_t MAX_READ_BUFFER_SIZE = 64 * 1024; -const int32_t STATUSCODE_OK = 200; -const int32_t STATUSCODE_PARTIAL_CONTENT = 206; +} // namespace std::string NormalizeHeaderKey(const std::string& s) { // Capitalize the first letter and any letter following a hyphen: @@ -60,557 +54,64 @@ std::string NormalizeHeaderKey(const std::string& s) { return result; } -StringMap_t ParseHeaders(const char* headers, int32_t headers_length) { - enum State { - FINDING_KEY, - SKIPPING_WHITESPACE, - FINDING_VALUE, - }; - - StringMap_t result; - std::string key; - std::string value; - - State state = FINDING_KEY; - const char* start = headers; - for (int i = 0; i < headers_length; ++i) { - switch (state) { - case FINDING_KEY: - if (headers[i] == ':') { - // Found key. - key.assign(start, &headers[i] - start); - key = NormalizeHeaderKey(key); - state = SKIPPING_WHITESPACE; - } - break; - - case SKIPPING_WHITESPACE: - if (headers[i] == ' ') { - // Found whitespace, keep going... - break; - } - - // Found a non-whitespace, mark this as the start of the value. - start = &headers[i]; - state = FINDING_VALUE; - // Fallthrough to start processing value without incrementing i. - - case FINDING_VALUE: - if (headers[i] == '\n') { - // Found value. - value.assign(start, &headers[i] - start); - result[key] = value; - start = &headers[i + 1]; - state = FINDING_KEY; - } - break; - } - } - - return result; -} - -bool ParseContentLength(const StringMap_t& headers, size_t* content_length) { - StringMap_t::const_iterator iter = headers.find("Content-Length"); - if (iter == headers.end()) - return false; - - *content_length = strtoul(iter->second.c_str(), NULL, 10); - return true; -} - -bool ParseContentRange(const StringMap_t& headers, size_t* read_start, - size_t* read_end, size_t* entity_length) { - StringMap_t::const_iterator iter = headers.find("Content-Range"); - if (iter == headers.end()) - return false; - - // The key should look like "bytes ##-##/##" or "bytes ##-##/*". The last - // value is the entity length, which can potentially be * (i.e. unknown). - int read_start_int; - int read_end_int; - int entity_length_int; - int result = sscanf(iter->second.c_str(), "bytes %"SCNuS"-%"SCNuS"/%"SCNuS, - &read_start_int, &read_end_int, &entity_length_int); - - // The Content-Range header specifies an inclusive range: e.g. the first ten - // bytes is "bytes 0-9/*". Convert it to a half-open range by incrementing - // read_end. - if (result == 2) { - *read_start = read_start_int; - *read_end = read_end_int + 1; - *entity_length = 0; - return true; - } else if (result == 3) { - *read_start = read_start_int; - *read_end = read_end_int + 1; - *entity_length = entity_length_int; - return true; - } - - return false; -} - -} // namespace - - -class MountNodeHttp : public MountNode { - public: - virtual int FSync(); - virtual int GetDents(size_t offs, struct dirent* pdir, size_t count); - virtual int GetStat(struct stat* stat); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int FTruncate(off_t size); - virtual int Write(size_t offs, const void* buf, size_t count); - virtual size_t GetSize(); - - void SetCachedSize(off_t size); - - protected: - MountNodeHttp(Mount* mount, const std::string& url, bool cache_content); - - private: - bool OpenUrl(const char* method, - StringMap_t* request_headers, - PP_Resource* out_loader, - PP_Resource* out_request, - PP_Resource* out_response, - int32_t* out_statuscode, - StringMap_t* out_response_headers); - - int DownloadToCache(); - int ReadPartialFromCache(size_t offs, void* buf, size_t count); - int DownloadPartial(size_t offs, void* buf, size_t count); - int DownloadToBuffer(PP_Resource loader, void* buf, size_t count); - - std::string url_; - std::vector<char> buffer_; - - bool cache_content_; - bool has_cached_size_; - std::vector<char> cached_data_; - - friend class MountHttp; -}; - -void MountNodeHttp::SetCachedSize(off_t size) { - has_cached_size_ = true; - stat_.st_size = size; -} - -int MountNodeHttp::FSync() { - errno = ENOSYS; - return -1; -} - -int MountNodeHttp::GetDents(size_t offs, struct dirent* pdir, size_t count) { - errno = ENOSYS; - return -1; -} - -int MountNodeHttp::GetStat(struct stat* stat) { - AutoLock lock(&lock_); - - // Assume we need to 'HEAD' if we do not know the size, otherwise, assume - // that the information is constant. We can add a timeout if needed. - MountHttp* mount = static_cast<MountHttp*>(mount_); - if (stat_.st_size == 0 || !mount->cache_stat_) { - StringMap_t headers; - PP_Resource loader; - PP_Resource request; - PP_Resource response; - int32_t statuscode; - StringMap_t response_headers; - if (!OpenUrl("HEAD", &headers, &loader, &request, &response, &statuscode, - &response_headers)) { - // errno is already set by OpenUrl. - return -1; - } - - ScopedResource scoped_loader(mount_->ppapi(), loader); - ScopedResource scoped_request(mount_->ppapi(), request); - ScopedResource scoped_response(mount_->ppapi(), response); - - - size_t entity_length; - if (ParseContentLength(response_headers, &entity_length)) { - SetCachedSize(static_cast<off_t>(entity_length)); - } else if (cache_content_ && !has_cached_size_) { - DownloadToCache(); - } else { - // Don't use SetCachedSize here -- it is actually unknown. - stat_.st_size = 0; - } - - stat_.st_atime = 0; // TODO(binji): Use "Last-Modified". - stat_.st_mtime = 0; - stat_.st_ctime = 0; - } - - // Fill the stat structure if provided - if (stat) { - memcpy(stat, &stat_, sizeof(stat_)); - } - return 0; -} - -int MountNodeHttp::Read(size_t offs, void* buf, size_t count) { - AutoLock lock(&lock_); - if (cache_content_) { - if (cached_data_.empty()) { - if (DownloadToCache() < 0) - return -1; - } - - return ReadPartialFromCache(offs, buf, count); - } - - return DownloadPartial(offs, buf, count); -} - -int MountNodeHttp::FTruncate(off_t size) { - errno = ENOSYS; - return -1; -} - -int MountNodeHttp::Write(size_t offs, const void* buf, size_t count) { - // TODO(binji): support POST? - errno = ENOSYS; - return -1; -} - -size_t MountNodeHttp::GetSize() { - // TODO(binji): This value should be cached properly; i.e. obey the caching - // headers returned by the server. - AutoLock lock(&lock_); - if (!has_cached_size_) { - // Even if DownloadToCache fails, the best result we can return is what - // was written to stat_.st_size. - if (cache_content_) - DownloadToCache(); - } - - return stat_.st_size; -} - -MountNodeHttp::MountNodeHttp(Mount* mount, const std::string& url, - bool cache_content) - : MountNode(mount), - url_(url), - cache_content_(cache_content), - has_cached_size_(false) { -} - -bool MountNodeHttp::OpenUrl(const char* method, - StringMap_t* request_headers, - PP_Resource* out_loader, - PP_Resource* out_request, - PP_Resource* out_response, - int32_t* out_statuscode, - StringMap_t* out_response_headers) { - // Assume lock_ is already held. - - PepperInterface* ppapi = mount_->ppapi(); - - MountHttp* mount_http = static_cast<MountHttp*>(mount_); - ScopedResource request(ppapi, - mount_http->MakeUrlRequestInfo(url_, method, - request_headers)); - if (!request.pp_resource()) { - errno = EINVAL; - return false; - } - - URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); - URLResponseInfoInterface* response_interface = - ppapi->GetURLResponseInfoInterface(); - VarInterface* var_interface = ppapi->GetVarInterface(); - - ScopedResource loader(ppapi, loader_interface->Create(ppapi->GetInstance())); - if (!loader.pp_resource()) { - errno = EINVAL; - return false; - } - - int32_t result = loader_interface->Open( - loader.pp_resource(), request.pp_resource(), PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return false; - } - - ScopedResource response( - ppapi, - loader_interface->GetResponseInfo(loader.pp_resource())); - if (!response.pp_resource()) { - errno = EINVAL; - return false; - } - - // Get response statuscode. - PP_Var statuscode = response_interface->GetProperty( - response.pp_resource(), - PP_URLRESPONSEPROPERTY_STATUSCODE); - - if (statuscode.type != PP_VARTYPE_INT32) { - errno = EINVAL; - return false; - } - - *out_statuscode = statuscode.value.as_int; - - // Only accept OK or Partial Content. - if (*out_statuscode != STATUSCODE_OK && - *out_statuscode != STATUSCODE_PARTIAL_CONTENT) { - errno = EINVAL; - return false; - } - - // Get response headers. - PP_Var response_headers_var = response_interface->GetProperty( - response.pp_resource(), - PP_URLRESPONSEPROPERTY_HEADERS); - - uint32_t response_headers_length; - const char* response_headers_str = var_interface->VarToUtf8( - response_headers_var, - &response_headers_length); - - *out_loader = loader.Release(); - *out_request = request.Release(); - *out_response = response.Release(); - *out_response_headers = ParseHeaders(response_headers_str, - response_headers_length); - - return true; -} - -int MountNodeHttp::DownloadToCache() { - StringMap_t headers; - PP_Resource loader; - PP_Resource request; - PP_Resource response; - int32_t statuscode; - StringMap_t response_headers; - if (!OpenUrl("GET", &headers, &loader, &request, &response, &statuscode, - &response_headers)) { - // errno is already set by OpenUrl. - return -1; - } - - PepperInterface* ppapi = mount_->ppapi(); - ScopedResource scoped_loader(ppapi, loader); - ScopedResource scoped_request(ppapi, request); - ScopedResource scoped_response(ppapi, response); - - size_t content_length = 0; - if (ParseContentLength(response_headers, &content_length)) { - cached_data_.resize(content_length); - int real_size = DownloadToBuffer(loader, cached_data_.data(), - content_length); - if (real_size < 0) - return -1; - - SetCachedSize(real_size); - cached_data_.resize(real_size); - return real_size; - } - - // We don't know how big the file is. Read in chunks. - cached_data_.resize(MAX_READ_BUFFER_SIZE); - size_t total_bytes_read = 0; - size_t bytes_to_read = MAX_READ_BUFFER_SIZE; - while (true) { - char* buf = cached_data_.data() + total_bytes_read; - int bytes_read = DownloadToBuffer(loader, buf, bytes_to_read); - if (bytes_read < 0) - return -1; - - total_bytes_read += bytes_read; - - if (bytes_read < bytes_to_read) { - SetCachedSize(total_bytes_read); - cached_data_.resize(total_bytes_read); - return total_bytes_read; - } - - cached_data_.resize(total_bytes_read + bytes_to_read); - } -} - -int MountNodeHttp::ReadPartialFromCache(size_t offs, void* buf, size_t count) { - if (offs > cached_data_.size()) { - errno = EINVAL; - return -1; - } - - count = std::min(count, cached_data_.size() - offs); - memcpy(buf, &cached_data_.data()[offs], count); - return count; -} - -int MountNodeHttp::DownloadPartial(size_t offs, void* buf, size_t count) { - StringMap_t headers; - - char buffer[100]; - // Range request is inclusive: 0-99 returns 100 bytes. - snprintf(&buffer[0], sizeof(buffer), "bytes=%"PRIuS"-%"PRIuS, - offs, offs + count - 1); - headers["Range"] = buffer; - - PP_Resource loader; - PP_Resource request; - PP_Resource response; - int32_t statuscode; - StringMap_t response_headers; - if (!OpenUrl("GET", &headers, &loader, &request, &response, &statuscode, - &response_headers)) { - // errno is already set by OpenUrl. - return -1; - } - - PepperInterface* ppapi = mount_->ppapi(); - ScopedResource scoped_loader(ppapi, loader); - ScopedResource scoped_request(ppapi, request); - ScopedResource scoped_response(ppapi, response); - - size_t read_start = 0; - if (statuscode == STATUSCODE_OK) { - // No partial result, read everything starting from the part we care about. - size_t content_length; - if (ParseContentLength(response_headers, &content_length)) { - if (offs >= content_length) { - errno = EINVAL; - return 0; - } +Error MountHttp::Open(const Path& path, int mode, MountNode** out_node) { + *out_node = NULL; - // Clamp count, if trying to read past the end of the file. - if (offs + count > content_length) { - count = content_length - offs; - } - } - } else if (statuscode == STATUSCODE_PARTIAL_CONTENT) { - // Determine from the headers where we are reading. - size_t read_end; - size_t entity_length; - if (ParseContentRange(response_headers, &read_start, &read_end, - &entity_length)) { - if (read_start > offs || read_start > read_end) { - // If this error occurs, the server is returning bogus values. - errno = EINVAL; - return -1; - } + assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/'); - // Clamp count, if trying to read past the end of the file. - count = std::min(read_end - read_start, count); - } else { - // Partial Content without Content-Range. Assume that the server gave us - // exactly what we asked for. This can happen even when the server - // returns 200 -- the cache may return 206 in this case, but not modify - // the headers. - read_start = offs; - } + NodeMap_t::iterator iter = node_cache_.find(path.Join()); + if (iter != node_cache_.end()) { + *out_node = iter->second; + return 0; } - if (read_start < offs) { - // We aren't yet at the location where we want to start reading. Read into - // our dummy buffer until then. - size_t bytes_to_read = offs - read_start; - if (buffer_.size() < bytes_to_read) - buffer_.resize(std::min(bytes_to_read, MAX_READ_BUFFER_SIZE)); - - while (bytes_to_read > 0) { - int32_t bytes_read = DownloadToBuffer(loader, buffer_.data(), - buffer_.size()); - if (bytes_read < 0) - return -1; + // If we can't find the node in the cache, create it + std::string url = url_root_ + (path.IsAbsolute() ? path.Range(1, path.Size()) + : path.Join()); - bytes_to_read -= bytes_read; - } + MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); + Error error = node->Init(mode); + if (error) { + node->Release(); + return error; } - return DownloadToBuffer(loader, buf, count); -} - -int MountNodeHttp::DownloadToBuffer(PP_Resource loader, void* buf, - size_t count) { - PepperInterface* ppapi = mount_->ppapi(); - URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); - - char* out_buffer = static_cast<char*>(buf); - size_t bytes_to_read = count; - while (bytes_to_read > 0) { - int32_t bytes_read = loader_interface->ReadResponseBody( - loader, out_buffer, bytes_to_read, PP_BlockUntilComplete()); - - if (bytes_read == 0) { - // This is not an error -- it may just be that we were trying to read - // more data than exists. - return count - bytes_to_read; - } - - if (bytes_read < 0) { - errno = PPErrorToErrno(bytes_read); - return -1; - } - - assert(bytes_read <= bytes_to_read); - bytes_to_read -= bytes_read; - out_buffer += bytes_read; + error = node->GetStat(NULL); + if (error) { + node->Release(); + return error; } - return count; -} - -MountNode *MountHttp::Open(const Path& path, int mode) { - assert(url_root_.empty() || url_root_[url_root_.length() - 1] == '/'); - - NodeMap_t::iterator iter = node_cache_.find(path.Join()); - if (iter != node_cache_.end()) { - return iter->second; + MountNodeDir* parent; + error = FindOrCreateDir(path.Parent(), &parent); + if (error) { + node->Release(); + return error; } - // If we can't find the node in the cache, create it - std::string url = url_root_ + (path.IsAbsolute() ? - path.Range(1, path.Size()) : - path.Join()); - - MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); - if (!node->Init(mode) || (0 != node->GetStat(NULL))) { + error = parent->AddChild(path.Basename(), node); + if (error) { node->Release(); - return NULL; + return error; } - MountNodeDir* parent = FindOrCreateDir(path.Parent()); node_cache_[path.Join()] = node; - parent->AddChild(path.Basename(), node); - return node; -} -int MountHttp::Unlink(const Path& path) { - errno = ENOSYS; - return -1; + *out_node = node; + return 0; } -int MountHttp::Mkdir(const Path& path, int permissions) { - errno = ENOSYS; - return -1; -} +Error MountHttp::Unlink(const Path& path) { return ENOSYS; } -int MountHttp::Rmdir(const Path& path) { - errno = ENOSYS; - return -1; -} +Error MountHttp::Mkdir(const Path& path, int permissions) { return ENOSYS; } -int MountHttp::Remove(const Path& path) { - errno = ENOSYS; - return -1; -} +Error MountHttp::Rmdir(const Path& path) { return ENOSYS; } + +Error MountHttp::Remove(const Path& path) { return ENOSYS; } -PP_Resource MountHttp::MakeUrlRequestInfo( - const std::string& url, - const char* method, - StringMap_t* additional_headers) { +PP_Resource MountHttp::MakeUrlRequestInfo(const std::string& url, + const char* method, + StringMap_t* additional_headers) { URLRequestInfoInterface* interface = ppapi_->GetURLRequestInfoInterface(); VarInterface* var_interface = ppapi_->GetVarInterface(); @@ -618,21 +119,23 @@ PP_Resource MountHttp::MakeUrlRequestInfo( if (!request_info) return 0; - interface->SetProperty( - request_info, PP_URLREQUESTPROPERTY_URL, - var_interface->VarFromUtf8(url.c_str(), url.length())); - interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_METHOD, + interface->SetProperty(request_info, + PP_URLREQUESTPROPERTY_URL, + var_interface->VarFromUtf8(url.c_str(), url.length())); + interface->SetProperty(request_info, + PP_URLREQUESTPROPERTY_METHOD, var_interface->VarFromUtf8(method, strlen(method))); interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_ALLOWCROSSORIGINREQUESTS, PP_MakeBool(allow_cors_ ? PP_TRUE : PP_FALSE)); - interface->SetProperty(request_info, PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS, + interface->SetProperty(request_info, + PP_URLREQUESTPROPERTY_ALLOWCREDENTIALS, PP_MakeBool(allow_credentials_ ? PP_TRUE : PP_FALSE)); // Merge the mount headers with the request headers. If the field is already // set it |additional_headers|, don't use the one from headers_. for (StringMap_t::iterator iter = headers_.begin(); iter != headers_.end(); - ++iter) { + ++iter) { const std::string& key = NormalizeHeaderKey(iter->first); if (additional_headers->find(key) == additional_headers->end()) { additional_headers->insert(std::make_pair(key, iter->second)); @@ -642,12 +145,14 @@ PP_Resource MountHttp::MakeUrlRequestInfo( // Join the headers into one string. std::string headers; for (StringMap_t::iterator iter = additional_headers->begin(); - iter != additional_headers->end(); ++iter) { + iter != additional_headers->end(); + ++iter) { headers += iter->first + ": " + iter->second + '\n'; } interface->SetProperty( - request_info, PP_URLREQUESTPROPERTY_HEADERS, + request_info, + PP_URLREQUESTPROPERTY_HEADERS, var_interface->VarFromUtf8(headers.c_str(), headers.length())); return request_info; @@ -657,12 +162,12 @@ MountHttp::MountHttp() : allow_cors_(false), allow_credentials_(false), cache_stat_(true), - cache_content_(true) { -} + cache_content_(true) {} -bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { - if (!Mount::Init(dev, args, ppapi)) - return false; +Error MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { + Error error = Mount::Init(dev, args, ppapi); + if (error) + return error; // Parse mount args. for (StringMap_t::iterator iter = args.begin(); iter != args.end(); ++iter) { @@ -674,11 +179,18 @@ bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { url_root_ += '/'; } } else if (iter->first == "manifest") { - char *text = LoadManifest(iter->second); - if (text != NULL) { - ParseManifest(text); + char* text; + error = LoadManifest(iter->second, &text); + if (error) + return error; + + error = ParseManifest(text); + if (error) { delete[] text; + return error; } + + delete[] text; } else if (iter->first == "allow_cross_origin_requests") { allow_cors_ = iter->second == "true"; } else if (iter->first == "allow_credentials") { @@ -693,34 +205,53 @@ bool MountHttp::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { } } - return true; + return 0; } -void MountHttp::Destroy() { -} +void MountHttp::Destroy() {} + +Error MountHttp::FindOrCreateDir(const Path& path, MountNodeDir** out_node) { + *out_node = NULL; -MountNodeDir* MountHttp::FindOrCreateDir(const Path& path) { std::string strpath = path.Join(); NodeMap_t::iterator iter = node_cache_.find(strpath); if (iter != node_cache_.end()) { - return static_cast<MountNodeDir*>(iter->second); + *out_node = static_cast<MountNodeDir*>(iter->second); + return 0; } - // If the node does not exist, create it, and add it to the node cache + // If the node does not exist, create it. MountNodeDir* node = new MountNodeDir(this); - node->Init(S_IREAD); - node_cache_[strpath] = node; + Error error = node->Init(S_IREAD); + if (error) { + node->Release(); + return error; + } // If not the root node, find the parent node and add it to the parent if (!path.Top()) { - MountNodeDir* parent = FindOrCreateDir(path.Parent()); - parent->AddChild(path.Basename(), node); + MountNodeDir* parent; + error = FindOrCreateDir(path.Parent(), &parent); + if (error) { + node->Release(); + return error; + } + + error = parent->AddChild(path.Basename(), node); + if (error) { + node->Release(); + return error; + } } - return node; + // Add it to the node cache. + node_cache_[strpath] = node; + + *out_node = node; + return 0; } -bool MountHttp::ParseManifest(char *text) { +Error MountHttp::ParseManifest(char* text) { StringList_t lines; SplitString(text, "\n", &lines); @@ -741,62 +272,102 @@ bool MountHttp::ParseManifest(char *text) { // Ignore EXEC bit int mode = S_IFREG; switch (modestr[0]) { - case '-': mode = S_IFREG; break; - case 'c': mode = S_IFCHR; break; + case '-': + mode = S_IFREG; + break; + case 'c': + mode = S_IFCHR; + break; default: fprintf(stderr, "Unable to parse type %s for %s.\n", modestr, name); - return false; + return EINVAL; } switch (modestr[1]) { - case '-': break; - case 'r': mode |= S_IREAD; break; + case '-': + break; + case 'r': + mode |= S_IREAD; + break; default: fprintf(stderr, "Unable to parse read %s for %s.\n", modestr, name); - return false; + return EINVAL; } switch (modestr[2]) { - case '-': break; - case 'w': mode |= S_IWRITE; break; + case '-': + break; + case 'w': + mode |= S_IWRITE; + break; default: fprintf(stderr, "Unable to parse write %s for %s.\n", modestr, name); - return false; + return EINVAL; } Path path(name); - std::string url = url_root_ + (path.IsAbsolute() ? - path.Range(1, path.Size()) : - path.Join()); + std::string url = + url_root_ + + (path.IsAbsolute() ? path.Range(1, path.Size()) : path.Join()); MountNodeHttp* node = new MountNodeHttp(this, url, cache_content_); - node->Init(mode); + Error error = node->Init(mode); + if (error) { + node->Release(); + return error; + } + node->SetCachedSize(atoi(lenstr)); - MountNodeDir* dir_node = FindOrCreateDir(path.Parent()); - dir_node->AddChild(path.Basename(), node); + MountNodeDir* dir_node; + error = FindOrCreateDir(path.Parent(), &dir_node); + if (error) { + node->Release(); + return error; + } + + error = dir_node->AddChild(path.Basename(), node); + if (error) { + node->Release(); + return error; + } std::string pname = path.Join(); node_cache_[pname] = node; } } - return true; + return 0; } -char *MountHttp::LoadManifest(const std::string& manifest_name) { +Error MountHttp::LoadManifest(const std::string& manifest_name, + char** out_manifest) { Path manifest_path(manifest_name); - MountNode* manifest_node = Open(manifest_path, O_RDONLY); + MountNode* manifest_node = NULL; + *out_manifest = NULL; - if (manifest_node) { - char *text = new char[manifest_node->GetSize() + 1]; - off_t len = manifest_node->Read(0, text, manifest_node->GetSize()); + int error = Open(manifest_path, O_RDONLY, &manifest_node); + if (error) + return error; + + size_t size; + error = manifest_node->GetSize(&size); + if (error) { manifest_node->Release(); + return error; + } - text[len] = 0; - return text; + char* text = new char[size + 1]; + int len; + error = manifest_node->Read(0, text, size, &len); + if (error) { + manifest_node->Release(); + return error; } - fprintf(stderr, "Could not open manifest: %s\n", manifest_name.c_str()); - return NULL; + manifest_node->Release(); + text[len] = 0; + + *out_manifest = text; + return 0; } 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 3a842a04..e10f1af 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_http.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_http.h @@ -15,15 +15,17 @@ class MountNodeDir; class MountNodeHttp; class MountHttpMock; +std::string NormalizeHeaderKey(const std::string& s); + class MountHttp : public Mount { public: typedef std::map<std::string, MountNode*> NodeMap_t; - virtual MountNode *Open(const Path& path, int mode); - virtual int Unlink(const Path& path); - virtual int Mkdir(const Path& path, int permissions); - virtual int Rmdir(const Path& path); - virtual int Remove(const Path& path); + virtual Error Open(const Path& path, int mode, MountNode** out_node); + virtual Error Unlink(const Path& path); + virtual Error Mkdir(const Path& path, int permissions); + virtual Error Rmdir(const Path& path); + virtual Error Remove(const Path& path); PP_Resource MakeUrlRequestInfo(const std::string& url, const char* method, @@ -32,11 +34,11 @@ class MountHttp : public Mount { protected: MountHttp(); - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); - MountNodeDir* FindOrCreateDir(const Path& path); - char *LoadManifest(const std::string& path); - bool ParseManifest(char *text); + Error FindOrCreateDir(const Path& path, MountNodeDir** out_node); + Error LoadManifest(const std::string& path, char** out_manifest); + Error ParseManifest(char *text); private: std::string url_root_; 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 541f58c..356600b 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_mem.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.cc @@ -17,19 +17,21 @@ #include "sdk_util/auto_lock.h" #include "sdk_util/ref_object.h" -// TODO(noelallen) : Grab/Redefine these in the kernel object once available. -#define USR_ID 1002 -#define GRP_ID 1003 +MountMem::MountMem() : root_(NULL), max_ino_(0) {} -MountMem::MountMem() - : root_(NULL), - max_ino_(0) { -} +Error MountMem::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { + Error error = Mount::Init(dev, args, ppapi); + if (error) + return error; + + root_ = new MountNodeDir(this); + error = root_->Init(S_IREAD | S_IWRITE); + if (error) { + root_->Release(); + return error; + } -bool MountMem::Init(int dev, StringMap_t& args, PepperInterface* ppapi) { - Mount::Init(dev, args, ppapi); - root_ = AllocatePath(S_IREAD | S_IWRITE); - return (bool) (root_ != NULL); + return 0; } void MountMem::Destroy() { @@ -38,173 +40,156 @@ void MountMem::Destroy() { root_ = NULL; } -MountNode* MountMem::AllocatePath(int mode) { - MountNode *ptr = new MountNodeDir(this); - if (!ptr->Init(mode)) { - ptr->Release(); - return NULL; - } - return ptr; -} - -MountNode* MountMem::AllocateData(int mode) { - MountNode* ptr = new MountNodeMem(this); - if (!ptr->Init(mode)) { - ptr->Release(); - return NULL; - } - return ptr; -} - -MountNode* MountMem::FindNode(const Path& path, int type) { +Error MountMem::FindNode(const Path& path, int type, MountNode** out_node) { MountNode* node = root_; // If there is no root there, we have an error. - if (node == NULL) { - errno = ENOTDIR; - return NULL; - } + if (node == NULL) + return ENOTDIR; // We are expecting an "absolute" path from this mount point. - if (!path.IsAbsolute()) { - errno = EINVAL; - return NULL; - } + if (!path.IsAbsolute()) + return EINVAL; // Starting at the root, traverse the path parts. for (size_t index = 1; node && index < path.Size(); index++) { // If not a directory, then we have an error so return. - if (!node->IsaDir()) { - errno = ENOTDIR; - return NULL; - } + if (!node->IsaDir()) + return ENOTDIR; // Find the child node - node = node->FindChild(path.Part(index)); + Error error = node->FindChild(path.Part(index), &node); + if (error) + return error; } - // node should be root, a found child, or a failed 'FindChild' - // which already has the correct errno set. - if (NULL == node) return NULL; - // If a directory is expected, but it's not a directory, then fail. - if ((type & S_IFDIR) && !node->IsaDir()) { - errno = ENOTDIR; - return NULL; - } + if ((type & S_IFDIR) && !node->IsaDir()) + return ENOTDIR; // If a file is expected, but it's not a file, then fail. - if ((type & S_IFREG) && node->IsaDir()) { - errno = EISDIR; - return NULL; - } + if ((type & S_IFREG) && node->IsaDir()) + return EISDIR; // We now have a valid object of the expected type, so return it. - return node; + *out_node = node; + return 0; } -MountNode* MountMem::Open(const Path& path, int mode) { +Error MountMem::Open(const Path& path, int mode, MountNode** out_node) { AutoLock lock(&lock_); - MountNode* node = FindNode(path); + MountNode* node = NULL; + *out_node = NULL; - if (NULL == node) { - // Now first find the parent directory to see if we can add it - MountNode* parent = FindNode(path.Parent(), S_IFDIR); - if (NULL == parent) return NULL; + Error error = FindNode(path, 0, &node); + if (error) { // If the node does not exist and we can't create it, fail - if ((mode & O_CREAT) == 0) return NULL; + if ((mode & O_CREAT) == 0) + return ENOENT; - // Otherwise, create it with a single reference - mode = OpenModeToPermission(mode); - node = AllocateData(mode); - if (NULL == node) return NULL; + // Now first find the parent directory to see if we can add it + MountNode* parent = NULL; + error = FindNode(path.Parent(), S_IFDIR, &parent); + if (error) + return error; + + // Create it with a single reference + node = new MountNodeMem(this); + error = node->Init(OpenModeToPermission(mode)); + if (error) { + node->Release(); + return error; + } - if (parent->AddChild(path.Basename(), node) == -1) { + error = parent->AddChild(path.Basename(), node); + if (error) { // Or if it fails, release it node->Release(); - return NULL; + return error; } - return node; + + *out_node = node; + return 0; } + // Directories can only be opened read-only. + if (node->IsaDir() && (mode & 3) != O_RDONLY) + return EISDIR; + // If we were expected to create it exclusively, fail - if (mode & O_EXCL) { - errno = EEXIST; - return NULL; - } + if (mode & O_EXCL) + return EEXIST; // Verify we got the requested permissions. int req_mode = OpenModeToPermission(mode); int obj_mode = node->GetMode() & OpenModeToPermission(O_RDWR); - if ((obj_mode & req_mode) != req_mode) { - errno = EACCES; - return NULL; - } + if ((obj_mode & req_mode) != req_mode) + return EACCES; // We opened it, so ref count it before passing it back. node->Acquire(); - return node; + *out_node = node; + return 0; } -int MountMem::Mkdir(const Path& path, int mode) { +Error MountMem::Mkdir(const Path& path, int mode) { AutoLock lock(&lock_); // We expect a Mount "absolute" path - if (!path.IsAbsolute()) { - errno = ENOENT; - return -1; - } + if (!path.IsAbsolute()) + return ENOENT; // The root of the mount is already created by the mount - if (path.Size() == 1) { - errno = EEXIST; - return -1; - } - - MountNode* parent = FindNode(path.Parent(), S_IFDIR); - MountNode* node; + if (path.Size() == 1) + return EEXIST; - // If we failed to find the parent, the error code is already set. - if (NULL == parent) return -1; + MountNode* parent = NULL; + int error = FindNode(path.Parent(), S_IFDIR, &parent); + if (error) + return error; - node = parent->FindChild(path.Basename()); - if (NULL != node) { - errno = EEXIST; - return -1; - } + MountNode* node = NULL; + error = parent->FindChild(path.Basename(), &node); + if (!error) + return EEXIST; - // Otherwise, create a new node and attempt to add it - mode = OpenModeToPermission(mode); + if (error != ENOENT) + return error; // Allocate a node, with a RefCount of 1. If added to the parent // it will get ref counted again. In either case, release the // recount we have on exit. - node = AllocatePath(S_IREAD | S_IWRITE); - if (NULL == node) return -1; + node = new MountNodeDir(this); + error = node->Init(S_IREAD | S_IWRITE); + if (error) { + node->Release(); + return error; + } - if (parent->AddChild(path.Basename(), node) == -1) { + error = parent->AddChild(path.Basename(), node); + if (error) { node->Release(); - return -1; + return error; } node->Release(); return 0; } -int MountMem::Unlink(const Path& path) { +Error MountMem::Unlink(const Path& path) { return RemoveInternal(path, REMOVE_FILE); } -int MountMem::Rmdir(const Path& path) { +Error MountMem::Rmdir(const Path& path) { return RemoveInternal(path, REMOVE_DIR); } -int MountMem::Remove(const Path& path) { +Error MountMem::Remove(const Path& path) { return RemoveInternal(path, REMOVE_ALL); } -int MountMem::RemoveInternal(const Path& path, int remove_type) { +Error MountMem::RemoveInternal(const Path& path, int remove_type) { AutoLock lock(&lock_); bool dir_only = remove_type == REMOVE_DIR; bool file_only = remove_type == REMOVE_FILE; @@ -212,40 +197,33 @@ int MountMem::RemoveInternal(const Path& path, int remove_type) { if (dir_only) { // We expect a Mount "absolute" path - if (!path.IsAbsolute()) { - errno = ENOENT; - return -1; - } + if (!path.IsAbsolute()) + return ENOENT; // The root of the mount is already created by the mount - if (path.Size() == 1) { - errno = EEXIST; - return -1; - } + if (path.Size() == 1) + return EEXIST; } - MountNode* parent = FindNode(path.Parent(), S_IFDIR); - - // If we failed to find the parent, the error code is already set. - if (NULL == parent) return -1; + MountNode* parent = NULL; + int error = FindNode(path.Parent(), S_IFDIR, &parent); + if (error) + return error; // Verify we find a child which is a directory. - MountNode* child = parent->FindChild(path.Basename()); - if (NULL == child) { - errno = ENOENT; - return -1; - } - if (dir_only && !child->IsaDir()) { - errno = ENOTDIR; - return -1; - } - if (file_only && child->IsaDir()) { - errno = EISDIR; - return -1; - } - if (remove_dir && child->ChildCount() > 0) { - errno = ENOTEMPTY; - return -1; - } + MountNode* child = NULL; + error = parent->FindChild(path.Basename(), &child); + if (error) + return error; + + if (dir_only && !child->IsaDir()) + return ENOTDIR; + + if (file_only && child->IsaDir()) + return EISDIR; + + if (remove_dir && child->ChildCount() > 0) + return ENOTEMPTY; + return parent->RemoveChild(path.Basename()); } 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 dc75e81..930e9c7 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_mem.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_mem.h @@ -14,35 +14,33 @@ class MountMem : public Mount { protected: MountMem(); - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); // The protected functions are only used internally and will not // acquire or release the mount's lock themselves. The caller is // required to use correct locking as needed. - MountNode *AllocateData(int mode); - MountNode *AllocatePath(int mode); // Allocate or free an INODE number. int AllocateINO(); void FreeINO(int ino); // Find a Node specified node optionally failing if type does not match. - virtual MountNode* FindNode(const Path& path, int type = 0); + virtual Error FindNode(const Path& path, int type, MountNode** out_node); public: - virtual MountNode *Open(const Path& path, int mode); - virtual int Unlink(const Path& path); - virtual int Mkdir(const Path& path, int perm); - virtual int Rmdir(const Path& path); - virtual int Remove(const Path& path); + virtual Error Open(const Path& path, int mode, MountNode** 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); private: static const int REMOVE_DIR = 1; static const int REMOVE_FILE = 2; static const int REMOVE_ALL = REMOVE_DIR | REMOVE_FILE; - int RemoveInternal(const Path& path, int remove_type); + Error RemoveInternal(const Path& path, int remove_type); MountNode* root_; size_t max_ino_; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node.cc b/native_client_sdk/src/libraries/nacl_io/mount_node.cc index 97dc799..34de7a8 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.cc @@ -18,8 +18,7 @@ static const int USR_ID = 1001; static const int GRP_ID = 1002; -MountNode::MountNode(Mount* mount) - : mount_(mount) { +MountNode::MountNode(Mount* mount) : mount_(mount) { memset(&stat_, 0, sizeof(stat_)); stat_.st_gid = GRP_ID; stat_.st_uid = USR_ID; @@ -32,12 +31,11 @@ MountNode::MountNode(Mount* mount) stat_.st_ino = 1; } -MountNode::~MountNode() { -} +MountNode::~MountNode() {} -bool MountNode::Init(int perm) { +Error MountNode::Init(int perm) { stat_.st_mode |= perm; - return true; + return 0; } void MountNode::Destroy() { @@ -46,119 +44,110 @@ void MountNode::Destroy() { } } -int MountNode::FSync() { - return 0; -} +Error MountNode::FSync() { return 0; } -int MountNode::FTruncate(off_t length) { - errno = EINVAL; - return -1; +Error MountNode::FTruncate(off_t length) { + return EINVAL; } -int MountNode::GetDents(size_t offs, struct dirent* pdir, size_t count) { - errno = ENOTDIR; - return -1; +Error MountNode::GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes) { + *out_bytes = 0; + return ENOTDIR; } -int MountNode::GetStat(struct stat* pstat) { +Error MountNode::GetStat(struct stat* pstat) { AutoLock lock(&lock_); memcpy(pstat, &stat_, sizeof(stat_)); return 0; } -int MountNode::Ioctl(int request, char* arg) { - errno = EINVAL; - return -1; +Error MountNode::Ioctl(int request, char* arg) { + return EINVAL; } -int MountNode::Read(size_t offs, void* buf, size_t count) { - errno = EINVAL; - return -1; +Error MountNode::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; + return EINVAL; } -int MountNode::Write(size_t offs, const void* buf, size_t count) { - errno = EINVAL; - return -1; +Error MountNode::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + return EINVAL; } -void* MountNode::MMap(void* addr, size_t length, int prot, int flags, - size_t offset) { +Error MountNode::MMap(void* addr, + size_t length, + int prot, + int flags, + size_t offset, + void** out_addr) { + *out_addr = NULL; + // Never allow mmap'ing PROT_EXEC. The passthrough node supports this, but we // don't. Fortunately, glibc will fallback if this fails, so dlopen will // continue to work. - if (prot & PROT_EXEC) { - errno = EPERM; - return MAP_FAILED; - } + if (prot & PROT_EXEC) + return EPERM; // This default mmap support is just enough to make dlopen work. // This implementation just reads from the mount into the mmap'd memory area. void* new_addr = addr; - int err = _real_mmap(&new_addr, length, prot | PROT_WRITE, flags | - MAP_ANONYMOUS, -1, 0); + int mmap_error = _real_mmap( + &new_addr, length, prot | PROT_WRITE, flags | MAP_ANONYMOUS, -1, 0); if (new_addr == MAP_FAILED) { _real_munmap(new_addr, length); - errno = err; - return MAP_FAILED; + return mmap_error; } - ssize_t cnt = Read(offset, new_addr, length); - if (cnt == -1) { + int bytes_read; + Error read_error = Read(offset, new_addr, length, &bytes_read); + if (read_error) { _real_munmap(new_addr, length); - errno = ENOSYS; - return MAP_FAILED; + return read_error; } - return new_addr; -} - -int MountNode::GetLinks() { - return stat_.st_nlink; + *out_addr = new_addr; + return 0; } -int MountNode::GetMode() { - return stat_.st_mode & ~S_IFMT; -} +int MountNode::GetLinks() { return stat_.st_nlink; } -size_t MountNode::GetSize() { - return stat_.st_size; -} +int MountNode::GetMode() { return stat_.st_mode & ~S_IFMT; } -int MountNode::GetType() { - return stat_.st_mode & S_IFMT; +Error MountNode::GetSize(size_t* out_size) { + *out_size = stat_.st_size; + return 0; } -bool MountNode::IsaDir() { - return (stat_.st_mode & S_IFDIR) != 0; -} +int MountNode::GetType() { return stat_.st_mode & S_IFMT; } -bool MountNode::IsaFile() { - return (stat_.st_mode & S_IFREG) != 0; -} +bool MountNode::IsaDir() { return (stat_.st_mode & S_IFDIR) != 0; } -bool MountNode::IsaTTY() { - return (stat_.st_mode & S_IFCHR) != 0; -} +bool MountNode::IsaFile() { return (stat_.st_mode & S_IFREG) != 0; } +bool MountNode::IsaTTY() { return (stat_.st_mode & S_IFCHR) != 0; } -int MountNode:: AddChild(const std::string& name, MountNode* node) { - errno = ENOTDIR; - return -1; +Error MountNode::AddChild(const std::string& name, MountNode* node) { + return ENOTDIR; } -int MountNode::RemoveChild(const std::string& name) { - errno = ENOTDIR; - return -1; +Error MountNode::RemoveChild(const std::string& name) { + return ENOTDIR; } -MountNode* MountNode::FindChild(const std::string& name) { - errno = ENOTDIR; - return NULL; +Error MountNode::FindChild(const std::string& name, MountNode** out_node) { + *out_node = NULL; + return ENOTDIR; } int MountNode::ChildCount() { - errno = ENOTDIR; - return -1; + return 0; } void MountNode::Link() { 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 6837548..0acce66 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node.h @@ -7,6 +7,7 @@ #include <string> +#include "nacl_io/error.h" #include "nacl_io/osstat.h" #include "sdk_util/ref_object.h" @@ -14,6 +15,8 @@ struct dirent; struct stat; class Mount; +// 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 MountNode : public RefObject { protected: explicit MountNode(Mount* mount); @@ -21,27 +24,46 @@ class MountNode : public RefObject { protected: // Initialize with node specific flags, in this case stat permissions. - virtual bool Init(int flags); + virtual Error Init(int flags); virtual void Destroy(); public: // Normal OS operations on a node (file), can be called by the kernel // directly so it must lock and unlock appropriately. These functions // must not be called by the mount. - virtual int FSync(); - virtual int FTruncate(off_t length); - virtual int GetDents(size_t offs, struct dirent* pdir, size_t count); - virtual int GetStat(struct stat* stat); - virtual int Ioctl(int request, char* arg); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); - virtual void* MMap(void* addr, size_t length, int prot, int flags, - size_t offset); + virtual Error FSync(); + // It is expected that the derived MountNode will fill with 0 when growing + // the file. + virtual Error FTruncate(off_t length); + // Assume that |out_bytes| is non-NULL. + virtual Error GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes); + // Assume that |stat| is non-NULL. + virtual Error GetStat(struct stat* stat); + // Assume that |arg| is non-NULL. + virtual Error Ioctl(int request, char* arg); + // Assume that |buf| and |out_bytes| are non-NULL. + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + // Assume that |buf| and |out_bytes| are non-NULL. + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + // Assume that |addr| and |out_addr| are non-NULL. + virtual Error MMap(void* addr, + size_t length, + int prot, + int flags, + size_t offset, + void** out_addr); virtual int GetLinks(); virtual int GetMode(); virtual int GetType(); - virtual size_t GetSize(); + // Assume that |out_size| is non-NULL. + virtual Error GetSize(size_t *out_size); virtual bool IsaDir(); virtual bool IsaFile(); virtual bool IsaTTY(); @@ -51,18 +73,20 @@ class MountNode : public RefObject { // must be held while these calls are made. // Adds or removes a directory entry updating the link numbers and refcount - virtual int AddChild(const std::string& name, MountNode *node); - virtual int RemoveChild(const std::string& name); + // Assumes that |node| is non-NULL. + virtual Error AddChild(const std::string& name, MountNode* node); + virtual Error RemoveChild(const std::string& name); // Find a child and return it without updating the refcount - virtual MountNode* FindChild(const std::string& name); + // Assumes that |out_node| is non-NULL. + virtual Error FindChild(const std::string& name, MountNode** out_node); virtual int ChildCount(); // Update the link count virtual void Link(); virtual void Unlink(); -protected: + protected: struct stat stat_; Mount* mount_; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc index aa4b019..6f7feab 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.cc @@ -12,75 +12,79 @@ #include "sdk_util/auto_lock.h" #include "sdk_util/macros.h" -MountNodeDir::MountNodeDir(Mount* mount) - : MountNode(mount), - cache_(NULL) { +MountNodeDir::MountNodeDir(Mount* mount) : MountNode(mount), cache_(NULL) { stat_.st_mode |= S_IFDIR; } MountNodeDir::~MountNodeDir() { + for (MountNodeMap_t::iterator it = map_.begin(); it != map_.end(); ++it) { + it->second->Unlink(); + } free(cache_); } -int MountNodeDir::Read(size_t offs, void *buf, size_t count) { - errno = EISDIR; - return -1; +Error MountNodeDir::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; + return EISDIR; } -int MountNodeDir::FTruncate(off_t size) { - errno = EISDIR; - return -1; -} +Error MountNodeDir::FTruncate(off_t size) { return EISDIR; } -int MountNodeDir::Write(size_t offs, void *buf, size_t count) { - errno = EISDIR; - return -1; +Error MountNodeDir::Write(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + return EISDIR; } -int MountNodeDir::GetDents(size_t offs, struct dirent* pdir, size_t size) { +Error MountNodeDir::GetDents(size_t offs, + struct dirent* pdir, + size_t size, + int* out_bytes) { + *out_bytes = 0; + AutoLock lock(&lock_); // If the buffer pointer is invalid, fail - if (NULL == pdir) { - errno = EINVAL; - return -1; - } + if (NULL == pdir) + return EINVAL; // If the buffer is too small, fail - if (size < sizeof(struct dirent)) { - errno = EINVAL; - return -1; - } + if (size < sizeof(struct dirent)) + return EINVAL; // Force size to a multiple of dirent size -= size % sizeof(struct dirent); size_t max = map_.size() * sizeof(struct dirent); - if (cache_ == NULL) BuildCache(); + if (cache_ == NULL) + BuildCache(); - if (offs >= max) return 0; - if (offs + size >= max) size = max - offs; + if (offs >= max) { + // OK, trying to read past the end. + return 0; + } - memcpy(pdir, ((char *) cache_) + offs, size); - return size; + if (offs + size >= max) + size = max - offs; + + memcpy(pdir, ((char*)cache_) + offs, size); + *out_bytes = size; + return 0; } -int MountNodeDir::AddChild(const std::string& name, MountNode* node) { +Error MountNodeDir::AddChild(const std::string& name, MountNode* node) { AutoLock lock(&lock_); - if (name.empty()) { - errno = ENOENT; - return -1; - } - if (name.length() >= MEMBER_SIZE(struct dirent, d_name)) { - errno = ENAMETOOLONG; - return -1; - } + if (name.empty()) + return ENOENT; + + if (name.length() >= MEMBER_SIZE(struct dirent, d_name)) + return ENAMETOOLONG; MountNodeMap_t::iterator it = map_.find(name); - if (it != map_.end()) { - errno = EEXIST; - return -1; - } + if (it != map_.end()) + return EEXIST; node->Link(); map_[name] = node; @@ -88,7 +92,7 @@ int MountNodeDir::AddChild(const std::string& name, MountNode* node) { return 0; } -int MountNodeDir::RemoveChild(const std::string& name) { +Error MountNodeDir::RemoveChild(const std::string& name) { AutoLock lock(&lock_); MountNodeMap_t::iterator it = map_.find(name); if (it != map_.end()) { @@ -97,18 +101,19 @@ int MountNodeDir::RemoveChild(const std::string& name) { ClearCache(); return 0; } - errno = ENOENT; - return -1; + return ENOENT; } -MountNode* MountNodeDir::FindChild(const std::string& name) { +Error MountNodeDir::FindChild(const std::string& name, MountNode** out_node) { + *out_node = NULL; + AutoLock lock(&lock_); MountNodeMap_t::iterator it = map_.find(name); - if (it != map_.end()) { - return it->second; - } - errno = ENOENT; - return NULL; + if (it == map_.end()) + return ENOENT; + + *out_node = it->second; + return 0; } int MountNodeDir::ChildCount() { @@ -123,7 +128,7 @@ void MountNodeDir::ClearCache() { void MountNodeDir::BuildCache() { if (map_.size()) { - cache_ = (struct dirent *) malloc(sizeof(struct dirent) * map_.size()); + cache_ = (struct dirent*)malloc(sizeof(struct dirent) * map_.size()); MountNodeMap_t::iterator it = map_.begin(); for (size_t index = 0; it != map_.end(); it++, index++) { MountNode* node = it->second; diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h index b6af58d..a2ab67e 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_dir.h @@ -25,15 +25,18 @@ class MountNodeDir : public MountNode { public: typedef std::map<std::string, MountNode*> MountNodeMap_t; - virtual int FTruncate(off_t size); - virtual int GetDents(size_t offs, struct dirent* pdir, size_t count); - virtual int Read(size_t offs, void *buf, size_t count); - virtual int Write(size_t offs, void *buf, size_t count); + virtual Error FTruncate(off_t size); + virtual Error GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes); + virtual Error Read(size_t offs, void *buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, void *buf, size_t count, int* out_bytes); // Adds a finds or adds a directory entry as an INO, updating the refcount - virtual int AddChild(const std::string& name, MountNode *node); - virtual int RemoveChild(const std::string& name); - virtual MountNode* FindChild(const std::string& name); + virtual Error AddChild(const std::string& name, MountNode *node); + virtual Error RemoveChild(const std::string& name); + virtual Error FindChild(const std::string& name, MountNode** out_node); virtual int ChildCount(); diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc index 63c6ab8..8be2a4f 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.cc @@ -55,66 +55,61 @@ int32_t ModeToOpenFlags(int mode) { break; } - if (mode & O_CREAT) open_flags |= PP_FILEOPENFLAG_CREATE; - if (mode & O_TRUNC) open_flags |= PP_FILEOPENFLAG_TRUNCATE; - if (mode & O_EXCL) open_flags |= PP_FILEOPENFLAG_EXCLUSIVE; + if (mode & O_CREAT) + open_flags |= PP_FILEOPENFLAG_CREATE; + if (mode & O_TRUNC) + open_flags |= PP_FILEOPENFLAG_TRUNCATE; + if (mode & O_EXCL) + open_flags |= PP_FILEOPENFLAG_EXCLUSIVE; return open_flags; } } // namespace -int MountNodeHtml5Fs::FSync() { +Error MountNodeHtml5Fs::FSync() { // Cannot call Flush on a directory; simply do nothing. if (IsDirectory()) return 0; - int32_t result = mount_->ppapi()->GetFileIoInterface()->Flush( - fileio_resource_, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } - + int32_t result = mount_->ppapi()->GetFileIoInterface() + ->Flush(fileio_resource_, PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); return 0; } -int MountNodeHtml5Fs::GetDents(size_t offs, struct dirent* pdir, size_t size) { +Error MountNodeHtml5Fs::GetDents(size_t offs, + struct dirent* pdir, + size_t size, + int* out_bytes) { + *out_bytes = 0; + // If the buffer pointer is invalid, fail - if (NULL == pdir) { - errno = EINVAL; - return -1; - } + if (NULL == pdir) + return EINVAL; // If the buffer is too small, fail - if (size < sizeof(struct dirent)) { - errno = EINVAL; - return -1; - } + if (size < sizeof(struct dirent)) + return EINVAL; // If this is not a directory, fail - if (!IsDirectory()) { - errno = ENOTDIR; - return -1; - } + if (!IsDirectory()) + return ENOTDIR; - OutputBuffer output_buf = { NULL, 0 }; - PP_ArrayOutput output = { &GetOutputBuffer, &output_buf }; - int32_t result = - mount_->ppapi()->GetFileRefInterface()->ReadDirectoryEntries( - fileref_resource_, output, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } + OutputBuffer output_buf = {NULL, 0}; + PP_ArrayOutput output = {&GetOutputBuffer, &output_buf}; + int32_t result = mount_->ppapi()->GetFileRefInterface()->ReadDirectoryEntries( + fileref_resource_, output, PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); std::vector<struct dirent> dirents; - PP_DirectoryEntry* entries = - static_cast<PP_DirectoryEntry*>(output_buf.data); + PP_DirectoryEntry* entries = static_cast<PP_DirectoryEntry*>(output_buf.data); for (int i = 0; i < output_buf.element_count; ++i) { - PP_Var file_name_var = mount_->ppapi()->GetFileRefInterface()->GetName( - entries[i].file_ref); + PP_Var file_name_var = + mount_->ppapi()->GetFileRefInterface()->GetName(entries[i].file_ref); // Release the file reference. mount_->ppapi()->ReleaseResource(entries[i].file_ref); @@ -123,14 +118,14 @@ int MountNodeHtml5Fs::GetDents(size_t offs, struct dirent* pdir, size_t size) { continue; uint32_t file_name_length; - const char* file_name = mount_->ppapi()->GetVarInterface()->VarToUtf8( - file_name_var, &file_name_length); + const char* file_name = mount_->ppapi()->GetVarInterface() + ->VarToUtf8(file_name_var, &file_name_length); if (!file_name) continue; file_name_length = std::min( static_cast<size_t>(file_name_length), - sizeof(static_cast<struct dirent*>(0)->d_name) - 1); // -1 for NULL. + sizeof(static_cast<struct dirent*>(0)->d_name) - 1); // -1 for NULL. dirents.push_back(dirent()); struct dirent& direntry = dirents.back(); @@ -148,33 +143,40 @@ int MountNodeHtml5Fs::GetDents(size_t offs, struct dirent* pdir, size_t size) { size -= size % sizeof(struct dirent); size_t max = dirents.size() * sizeof(struct dirent); - if (offs >= max) return 0; - if (offs + size >= max) size = max - offs; + if (offs >= max) + return 0; + + if (offs + size >= max) + size = max - offs; memcpy(pdir, reinterpret_cast<char*>(dirents.data()) + offs, size); - return size; + *out_bytes = size; + return 0; } -int MountNodeHtml5Fs::GetStat(struct stat* stat) { +Error MountNodeHtml5Fs::GetStat(struct stat* stat) { AutoLock lock(&lock_); PP_FileInfo info; int32_t result = mount_->ppapi()->GetFileRefInterface()->Query( fileref_resource_, &info, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } + if (result != PP_OK) + return PPErrorToErrno(result); // Fill in known info here. memcpy(stat, &stat_, sizeof(stat_)); // Fill in the additional info from ppapi. switch (info.type) { - case PP_FILETYPE_REGULAR: stat->st_mode |= S_IFREG; break; - case PP_FILETYPE_DIRECTORY: stat->st_mode |= S_IFDIR; break; + case PP_FILETYPE_REGULAR: + stat->st_mode |= S_IFREG; + break; + case PP_FILETYPE_DIRECTORY: + stat->st_mode |= S_IFDIR; + break; case PP_FILETYPE_OTHER: - default: break; + default: + break; } stat->st_size = static_cast<off_t>(info.size); stat->st_atime = info.last_access_time; @@ -184,80 +186,85 @@ int MountNodeHtml5Fs::GetStat(struct stat* stat) { return 0; } -int MountNodeHtml5Fs::Read(size_t offs, void* buf, size_t count) { - if (IsDirectory()) { - errno = EISDIR; - return -1; - } +Error MountNodeHtml5Fs::Read(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; - int32_t result = mount_->ppapi()->GetFileIoInterface()->Read( - fileio_resource_, offs, static_cast<char*>(buf), - static_cast<int32_t>(count), - PP_BlockUntilComplete()); - if (result < 0) { - errno = PPErrorToErrno(result); - return -1; - } + if (IsDirectory()) + return EISDIR; - return result; + int32_t result = + mount_->ppapi()->GetFileIoInterface()->Read(fileio_resource_, + offs, + static_cast<char*>(buf), + static_cast<int32_t>(count), + PP_BlockUntilComplete()); + if (result < 0) + return PPErrorToErrno(result); + + *out_bytes = result; + return 0; } -int MountNodeHtml5Fs::FTruncate(off_t size) { - if (IsDirectory()) { - errno = EISDIR; - return -1; - } - - int32_t result = mount_->ppapi()->GetFileIoInterface()->SetLength( - fileio_resource_, size, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } +Error MountNodeHtml5Fs::FTruncate(off_t size) { + if (IsDirectory()) + return EISDIR; + int32_t result = mount_->ppapi()->GetFileIoInterface() + ->SetLength(fileio_resource_, size, PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); return 0; } -int MountNodeHtml5Fs::Write(size_t offs, const void* buf, size_t count) { - if (IsDirectory()) { - errno = EISDIR; - return -1; - } - - int32_t result = mount_->ppapi()->GetFileIoInterface()->Write( - fileio_resource_, offs, static_cast<const char*>(buf), - static_cast<int32_t>(count), PP_BlockUntilComplete()); - if (result < 0) { - errno = PPErrorToErrno(result); - return -1; - } +Error MountNodeHtml5Fs::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; - return result; + if (IsDirectory()) + return EISDIR; + + int32_t result = mount_->ppapi()->GetFileIoInterface() + ->Write(fileio_resource_, + offs, + static_cast<const char*>(buf), + static_cast<int32_t>(count), + PP_BlockUntilComplete()); + if (result < 0) + return PPErrorToErrno(result); + + *out_bytes = result; + return 0; } -size_t MountNodeHtml5Fs::GetSize() { +Error MountNodeHtml5Fs::GetSize(size_t* out_size) { + *out_size = 0; + AutoLock lock(&lock_); PP_FileInfo info; - int32_t result = mount_->ppapi()->GetFileRefInterface()->Query( - fileref_resource_, &info, PP_BlockUntilComplete()); - if (result != PP_OK) { - errno = PPErrorToErrno(result); - return -1; - } + int32_t result = mount_->ppapi()->GetFileIoInterface() + ->Query(fileio_resource_, &info, PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); - return static_cast<size_t>(info.size); + *out_size = static_cast<size_t>(info.size); + return 0; } MountNodeHtml5Fs::MountNodeHtml5Fs(Mount* mount, PP_Resource fileref_resource) : MountNode(mount), fileref_resource_(fileref_resource), - fileio_resource_(0) { -} + fileio_resource_(0) {} -bool MountNodeHtml5Fs::Init(int perm) { - if (!MountNode::Init(Mount::OpenModeToPermission(perm))) - return false; +Error MountNodeHtml5Fs::Init(int perm) { + Error error = MountNode::Init(Mount::OpenModeToPermission(perm)); + if (error) + return error; // First query the FileRef to see if it is a file or directory. PP_FileInfo file_info; @@ -265,20 +272,21 @@ bool MountNodeHtml5Fs::Init(int perm) { PP_BlockUntilComplete()); // If this is a directory, do not get a FileIO. if (file_info.type == PP_FILETYPE_DIRECTORY) - return true; + return 0; - fileio_resource_= mount_->ppapi()->GetFileIoInterface()->Create( - mount_->ppapi()->GetInstance()); + fileio_resource_ = mount_->ppapi()->GetFileIoInterface() + ->Create(mount_->ppapi()->GetInstance()); if (!fileio_resource_) - return false; + return ENOSYS; - int32_t open_result = mount_->ppapi()->GetFileIoInterface()->Open( - fileio_resource_, fileref_resource_, ModeToOpenFlags(perm), - PP_BlockUntilComplete()); + int32_t open_result = + mount_->ppapi()->GetFileIoInterface()->Open(fileio_resource_, + fileref_resource_, + ModeToOpenFlags(perm), + PP_BlockUntilComplete()); if (open_result != PP_OK) - return false; - - return true; + return PPErrorToErrno(open_result); + return 0; } void MountNodeHtml5Fs::Destroy() { diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h index e472208..832d3d4 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_html5fs.h @@ -15,20 +15,26 @@ class MountNodeHtml5Fs : public MountNode { // Normal OS operations on a node (file), can be called by the kernel // directly so it must lock and unlock appropriately. These functions // must not be called by the mount. - virtual int FSync(); - virtual int GetDents(size_t offs, struct dirent* pdir, size_t count); - virtual int GetStat(struct stat* stat); - virtual int Read(size_t offs, void* buf, size_t count); - virtual int FTruncate(off_t size); - virtual int Write(size_t offs, const void* buf, size_t count); - - virtual size_t GetSize(); + virtual Error FSync(); + virtual Error GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes); + virtual Error GetStat(struct stat* stat); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error FTruncate(off_t size); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + + virtual Error GetSize(size_t *out_size); protected: MountNodeHtml5Fs(Mount* mount, PP_Resource fileref); // Init with standard open flags - virtual bool Init(int o_mode); + virtual Error Init(int o_mode); virtual void Destroy(); private: diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc new file mode 100644 index 0000000..5005ff4 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.cc @@ -0,0 +1,523 @@ +/* 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/mount_node_http.h" + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include <ppapi/c/pp_errors.h> + +#include "nacl_io/mount_http.h" +#include "nacl_io/osinttypes.h" + +#if defined(WIN32) +#define snprintf _snprintf +#endif + +namespace { + +// If we're attempting to read a partial request, but the server returns a full +// request, we need to read all of the data up to the start of our partial +// request into a dummy buffer. This is the maximum size of that buffer. +const size_t MAX_READ_BUFFER_SIZE = 64 * 1024; +const int32_t STATUSCODE_OK = 200; +const int32_t STATUSCODE_PARTIAL_CONTENT = 206; + +StringMap_t ParseHeaders(const char* headers, int32_t headers_length) { + enum State { + FINDING_KEY, + SKIPPING_WHITESPACE, + FINDING_VALUE, + }; + + StringMap_t result; + std::string key; + std::string value; + + State state = FINDING_KEY; + const char* start = headers; + for (int i = 0; i < headers_length; ++i) { + switch (state) { + case FINDING_KEY: + if (headers[i] == ':') { + // Found key. + key.assign(start, &headers[i] - start); + key = NormalizeHeaderKey(key); + state = SKIPPING_WHITESPACE; + } + break; + + case SKIPPING_WHITESPACE: + if (headers[i] == ' ') { + // Found whitespace, keep going... + break; + } + + // Found a non-whitespace, mark this as the start of the value. + start = &headers[i]; + state = FINDING_VALUE; + // Fallthrough to start processing value without incrementing i. + + case FINDING_VALUE: + if (headers[i] == '\n') { + // Found value. + value.assign(start, &headers[i] - start); + result[key] = value; + start = &headers[i + 1]; + state = FINDING_KEY; + } + break; + } + } + + return result; +} + +bool ParseContentLength(const StringMap_t& headers, size_t* content_length) { + StringMap_t::const_iterator iter = headers.find("Content-Length"); + if (iter == headers.end()) + return false; + + *content_length = strtoul(iter->second.c_str(), NULL, 10); + return true; +} + +bool ParseContentRange(const StringMap_t& headers, + size_t* read_start, + size_t* read_end, + size_t* entity_length) { + StringMap_t::const_iterator iter = headers.find("Content-Range"); + if (iter == headers.end()) + return false; + + // The key should look like "bytes ##-##/##" or "bytes ##-##/*". The last + // value is the entity length, which can potentially be * (i.e. unknown). + int read_start_int; + int read_end_int; + int entity_length_int; + int result = sscanf(iter->second.c_str(), + "bytes %" SCNuS "-%" SCNuS "/%" SCNuS, + &read_start_int, + &read_end_int, + &entity_length_int); + + // The Content-Range header specifies an inclusive range: e.g. the first ten + // bytes is "bytes 0-9/*". Convert it to a half-open range by incrementing + // read_end. + if (result == 2) { + *read_start = read_start_int; + *read_end = read_end_int + 1; + *entity_length = 0; + return true; + } else if (result == 3) { + *read_start = read_start_int; + *read_end = read_end_int + 1; + *entity_length = entity_length_int; + return true; + } + + return false; +} + +} // namespace + +void MountNodeHttp::SetCachedSize(off_t size) { + has_cached_size_ = true; + stat_.st_size = size; +} + +Error MountNodeHttp::FSync() { return ENOSYS; } + +Error MountNodeHttp::GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes) { + *out_bytes = 0; + return ENOSYS; +} + +Error MountNodeHttp::GetStat(struct stat* stat) { + AutoLock lock(&lock_); + + // Assume we need to 'HEAD' if we do not know the size, otherwise, assume + // that the information is constant. We can add a timeout if needed. + MountHttp* mount = static_cast<MountHttp*>(mount_); + if (stat_.st_size == 0 || !mount->cache_stat_) { + StringMap_t headers; + PP_Resource loader; + PP_Resource request; + PP_Resource response; + int32_t statuscode; + StringMap_t response_headers; + Error error = OpenUrl("HEAD", + &headers, + &loader, + &request, + &response, + &statuscode, + &response_headers); + if (error) + return error; + + ScopedResource scoped_loader(mount_->ppapi(), loader); + ScopedResource scoped_request(mount_->ppapi(), request); + ScopedResource scoped_response(mount_->ppapi(), response); + + size_t entity_length; + if (ParseContentLength(response_headers, &entity_length)) { + SetCachedSize(static_cast<off_t>(entity_length)); + } else if (cache_content_ && !has_cached_size_) { + error = DownloadToCache(); + // TODO(binji): this error should not be dropped, but it requires a bit + // of a refactor of the tests. See crbug.com/245431 + // if (error) + // return error; + } else { + // Don't use SetCachedSize here -- it is actually unknown. + stat_.st_size = 0; + } + + stat_.st_atime = 0; // TODO(binji): Use "Last-Modified". + stat_.st_mtime = 0; + stat_.st_ctime = 0; + } + + // Fill the stat structure if provided + if (stat) + memcpy(stat, &stat_, sizeof(stat_)); + + return 0; +} + +Error MountNodeHttp::Read(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + + AutoLock lock(&lock_); + if (cache_content_) { + if (cached_data_.empty()) { + Error error = DownloadToCache(); + if (error) + return error; + } + + return ReadPartialFromCache(offs, buf, count, out_bytes); + } + + return DownloadPartial(offs, buf, count, out_bytes); +} + +Error MountNodeHttp::FTruncate(off_t size) { return ENOSYS; } + +Error MountNodeHttp::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + // TODO(binji): support POST? + *out_bytes = 0; + return ENOSYS; +} + +Error MountNodeHttp::GetSize(size_t* out_size) { + *out_size = 0; + + // TODO(binji): This value should be cached properly; i.e. obey the caching + // headers returned by the server. + AutoLock lock(&lock_); + if (!has_cached_size_) { + // Even if DownloadToCache fails, the best result we can return is what + // was written to stat_.st_size. + if (cache_content_) { + Error error = DownloadToCache(); + if (error) + return error; + } + } + + *out_size = stat_.st_size; + return 0; +} + +MountNodeHttp::MountNodeHttp(Mount* mount, + const std::string& url, + bool cache_content) + : MountNode(mount), + url_(url), + cache_content_(cache_content), + has_cached_size_(false) {} + +Error MountNodeHttp::OpenUrl(const char* method, + StringMap_t* request_headers, + PP_Resource* out_loader, + PP_Resource* out_request, + PP_Resource* out_response, + int32_t* out_statuscode, + StringMap_t* out_response_headers) { + // Assume lock_ is already held. + PepperInterface* ppapi = mount_->ppapi(); + + MountHttp* mount_http = static_cast<MountHttp*>(mount_); + ScopedResource request( + ppapi, mount_http->MakeUrlRequestInfo(url_, method, request_headers)); + if (!request.pp_resource()) + return EINVAL; + + URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); + URLResponseInfoInterface* response_interface = + ppapi->GetURLResponseInfoInterface(); + VarInterface* var_interface = ppapi->GetVarInterface(); + + ScopedResource loader(ppapi, loader_interface->Create(ppapi->GetInstance())); + if (!loader.pp_resource()) + return EINVAL; + + int32_t result = loader_interface->Open( + loader.pp_resource(), request.pp_resource(), PP_BlockUntilComplete()); + if (result != PP_OK) + return PPErrorToErrno(result); + + ScopedResource response( + ppapi, loader_interface->GetResponseInfo(loader.pp_resource())); + if (!response.pp_resource()) + return EINVAL; + + // Get response statuscode. + PP_Var statuscode = response_interface->GetProperty( + response.pp_resource(), PP_URLRESPONSEPROPERTY_STATUSCODE); + + if (statuscode.type != PP_VARTYPE_INT32) + return EINVAL; + + *out_statuscode = statuscode.value.as_int; + + // Only accept OK or Partial Content. + if (*out_statuscode != STATUSCODE_OK && + *out_statuscode != STATUSCODE_PARTIAL_CONTENT) { + return EINVAL; + } + + // Get response headers. + PP_Var response_headers_var = response_interface->GetProperty( + response.pp_resource(), PP_URLRESPONSEPROPERTY_HEADERS); + + uint32_t response_headers_length; + const char* response_headers_str = + var_interface->VarToUtf8(response_headers_var, &response_headers_length); + + *out_loader = loader.Release(); + *out_request = request.Release(); + *out_response = response.Release(); + *out_response_headers = + ParseHeaders(response_headers_str, response_headers_length); + + return 0; +} + +Error MountNodeHttp::DownloadToCache() { + StringMap_t headers; + PP_Resource loader; + PP_Resource request; + PP_Resource response; + int32_t statuscode; + StringMap_t response_headers; + Error error = OpenUrl("GET", + &headers, + &loader, + &request, + &response, + &statuscode, + &response_headers); + if (error) + return error; + + PepperInterface* ppapi = mount_->ppapi(); + ScopedResource scoped_loader(ppapi, loader); + ScopedResource scoped_request(ppapi, request); + ScopedResource scoped_response(ppapi, response); + + size_t content_length = 0; + if (ParseContentLength(response_headers, &content_length)) { + cached_data_.resize(content_length); + int real_size; + error = DownloadToBuffer( + loader, cached_data_.data(), content_length, &real_size); + if (error) + return error; + + SetCachedSize(real_size); + cached_data_.resize(real_size); + return 0; + } + + // We don't know how big the file is. Read in chunks. + cached_data_.resize(MAX_READ_BUFFER_SIZE); + size_t total_bytes_read = 0; + size_t bytes_to_read = MAX_READ_BUFFER_SIZE; + while (true) { + char* buf = cached_data_.data() + total_bytes_read; + int bytes_read; + error = DownloadToBuffer(loader, buf, bytes_to_read, &bytes_read); + if (error) + return error; + + total_bytes_read += bytes_read; + + if (bytes_read < bytes_to_read) { + SetCachedSize(total_bytes_read); + cached_data_.resize(total_bytes_read); + return 0; + } + + cached_data_.resize(total_bytes_read + bytes_to_read); + } +} + +Error MountNodeHttp::ReadPartialFromCache(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + + if (offs > cached_data_.size()) + return EINVAL; + + count = std::min(count, cached_data_.size() - offs); + memcpy(buf, &cached_data_.data()[offs], count); + + *out_bytes = count; + return 0; +} + +Error MountNodeHttp::DownloadPartial(size_t offs, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + + StringMap_t headers; + + char buffer[100]; + // Range request is inclusive: 0-99 returns 100 bytes. + snprintf(&buffer[0], + sizeof(buffer), + "bytes=%" PRIuS "-%" PRIuS, + offs, + offs + count - 1); + headers["Range"] = buffer; + + PP_Resource loader; + PP_Resource request; + PP_Resource response; + int32_t statuscode; + StringMap_t response_headers; + Error error = OpenUrl("GET", + &headers, + &loader, + &request, + &response, + &statuscode, + &response_headers); + if (error) + return error; + + PepperInterface* ppapi = mount_->ppapi(); + ScopedResource scoped_loader(ppapi, loader); + ScopedResource scoped_request(ppapi, request); + ScopedResource scoped_response(ppapi, response); + + size_t read_start = 0; + if (statuscode == STATUSCODE_OK) { + // No partial result, read everything starting from the part we care about. + size_t content_length; + if (ParseContentLength(response_headers, &content_length)) { + if (offs >= content_length) + return EINVAL; + + // Clamp count, if trying to read past the end of the file. + if (offs + count > content_length) { + count = content_length - offs; + } + } + } else if (statuscode == STATUSCODE_PARTIAL_CONTENT) { + // Determine from the headers where we are reading. + size_t read_end; + size_t entity_length; + if (ParseContentRange( + response_headers, &read_start, &read_end, &entity_length)) { + if (read_start > offs || read_start > read_end) { + // If this error occurs, the server is returning bogus values. + return EINVAL; + } + + // Clamp count, if trying to read past the end of the file. + count = std::min(read_end - read_start, count); + } else { + // Partial Content without Content-Range. Assume that the server gave us + // exactly what we asked for. This can happen even when the server + // returns 200 -- the cache may return 206 in this case, but not modify + // the headers. + read_start = offs; + } + } + + if (read_start < offs) { + // We aren't yet at the location where we want to start reading. Read into + // our dummy buffer until then. + size_t bytes_to_read = offs - read_start; + if (buffer_.size() < bytes_to_read) + buffer_.resize(std::min(bytes_to_read, MAX_READ_BUFFER_SIZE)); + + while (bytes_to_read > 0) { + int32_t bytes_read; + Error error = + DownloadToBuffer(loader, buffer_.data(), buffer_.size(), &bytes_read); + if (error) + return error; + + bytes_to_read -= bytes_read; + } + } + + return DownloadToBuffer(loader, buf, count, out_bytes); +} + +Error MountNodeHttp::DownloadToBuffer(PP_Resource loader, + void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + + PepperInterface* ppapi = mount_->ppapi(); + URLLoaderInterface* loader_interface = ppapi->GetURLLoaderInterface(); + + char* out_buffer = static_cast<char*>(buf); + size_t bytes_to_read = count; + while (bytes_to_read > 0) { + int32_t bytes_read = loader_interface->ReadResponseBody( + loader, out_buffer, bytes_to_read, PP_BlockUntilComplete()); + + if (bytes_read == 0) { + // This is not an error -- it may just be that we were trying to read + // more data than exists. + *out_bytes = count - bytes_to_read; + return 0; + } + + if (bytes_read < 0) + return PPErrorToErrno(bytes_read); + + assert(bytes_read <= bytes_to_read); + bytes_to_read -= bytes_read; + out_buffer += bytes_read; + } + + *out_bytes = count; + return 0; +} diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_http.h b/native_client_sdk/src/libraries/nacl_io/mount_node_http.h new file mode 100644 index 0000000..6b17468 --- /dev/null +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_http.h @@ -0,0 +1,70 @@ +/* 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_MOUNT_NODE_HTTP_H_ +#define LIBRARIES_NACL_IO_MOUNT_NODE_HTTP_H_ + +#include <map> +#include <string> +#include <vector> + +#include "nacl_io/error.h" +#include "nacl_io/mount_node.h" +#include "nacl_io/pepper_interface.h" + +typedef std::map<std::string, std::string> StringMap_t; + +class MountNodeHttp : public MountNode { + public: + virtual Error FSync(); + virtual Error GetDents(size_t offs, + struct dirent* pdir, + size_t count, + int* out_bytes); + virtual Error GetStat(struct stat* stat); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error FTruncate(off_t size); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + virtual Error GetSize(size_t* out_size); + + void SetCachedSize(off_t size); + + protected: + MountNodeHttp(Mount* mount, const std::string& url, bool cache_content); + + private: + Error OpenUrl(const char* method, + StringMap_t* request_headers, + PP_Resource* out_loader, + PP_Resource* out_request, + PP_Resource* out_response, + int32_t* out_statuscode, + StringMap_t* out_response_headers); + + Error DownloadToCache(); + Error ReadPartialFromCache(size_t offs, + void* buf, + size_t count, + int* out_bytes); + Error DownloadPartial(size_t offs, void* buf, size_t count, int* out_bytes); + Error DownloadToBuffer(PP_Resource loader, + void* buf, + size_t count, + int* out_bytes); + + std::string url_; + std::vector<char> buffer_; + + bool cache_content_; + bool has_cached_size_; + std::vector<char> cached_data_; + + friend class MountHttp; +}; + +#endif // LIBRARIES_NACL_IO_MOUNT_NODE_HTTP_H_ diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc index 13f0d21..d324079 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.cc @@ -13,69 +13,83 @@ #define BLOCK_SIZE (1 << 16) #define BLOCK_MASK (BLOCK_SIZE - 1) -MountNodeMem::MountNodeMem(Mount *mount) - : MountNode(mount), - data_(NULL), - capacity_(0) { +MountNodeMem::MountNodeMem(Mount* mount) + : MountNode(mount), data_(NULL), capacity_(0) { stat_.st_mode |= S_IFREG; } -MountNodeMem::~MountNodeMem() { - free(data_); -} +MountNodeMem::~MountNodeMem() { free(data_); } + +Error MountNodeMem::Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; -int MountNodeMem::Read(size_t offs, void *buf, size_t count) { AutoLock lock(&lock_); - if (count == 0) return 0; - if (offs + count > GetSize()) { - count = GetSize() - offs; + if (count == 0) + return 0; + + size_t size = stat_.st_size; + + if (offs + count > size) { + count = size - offs; } memcpy(buf, &data_[offs], count); - return static_cast<int>(count); + *out_bytes = static_cast<int>(count); + return 0; } -int MountNodeMem::Write(size_t offs, const void *buf, size_t count) { +Error MountNodeMem::Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + AutoLock lock(&lock_); - if (count == 0) return 0; + if (count == 0) + return 0; + + if (count + offs > stat_.st_size) { + Error error = FTruncate(count + offs); + if (error) + return error; - if (count + offs > GetSize()) { - FTruncate(count + offs); - count = GetSize() - offs; + count = stat_.st_size - offs; } memcpy(&data_[offs], buf, count); - return static_cast<int>(count); + *out_bytes = static_cast<int>(count); + return 0; } -int MountNodeMem::FTruncate(off_t size) { - size_t need = (size + BLOCK_MASK) & ~BLOCK_MASK; +Error MountNodeMem::FTruncate(off_t new_size) { + size_t need = (new_size + BLOCK_MASK) & ~BLOCK_MASK; + size_t old_size = stat_.st_size; // If the current capacity is correct, just adjust and return if (need == capacity_) { - stat_.st_size = static_cast<off_t>(size); + stat_.st_size = static_cast<off_t>(new_size); return 0; } // Attempt to realloc the block - char *newdata = static_cast<char *>(realloc(data_, need)); + char* newdata = static_cast<char*>(realloc(data_, need)); if (newdata != NULL) { // Zero out new space. - if (size > GetSize()) - memset(newdata + GetSize(), 0, size - GetSize()); + if (new_size > old_size) + memset(newdata + old_size, 0, new_size - old_size); data_ = newdata; capacity_ = need; - stat_.st_size = static_cast<off_t>(size); + stat_.st_size = static_cast<off_t>(new_size); return 0; } // If we failed, then adjust size according to what we keep - if (size > capacity_) size = capacity_; + if (new_size > capacity_) + new_size = capacity_; // Update the size and return the new size - stat_.st_size = static_cast<off_t>(size); - errno = EIO; - return -1; + stat_.st_size = static_cast<off_t>(new_size); + return EIO; } diff --git a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h index 07acfe1..6457014 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_node_mem.h @@ -16,9 +16,12 @@ class MountNodeMem : public MountNode { public: // Normal read/write operations on a file - virtual int Read(size_t offs, void* buf, size_t count); - virtual int Write(size_t offs, const void* buf, size_t count); - virtual int FTruncate(off_t size); + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes); + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes); + virtual Error FTruncate(off_t size); private: char* data_; 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 82660d6..268cbea 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc +++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.cc @@ -9,14 +9,10 @@ class MountNodePassthrough : public MountNode { public: explicit MountNodePassthrough(Mount* mount, int real_fd) - : MountNode(mount), - real_fd_(real_fd) { - } + : MountNode(mount), real_fd_(real_fd) {} protected: - virtual bool Init(int flags) { - return true; - } + virtual Error Init(int flags) { return 0; } virtual void Destroy() { if (real_fd_) @@ -26,77 +22,74 @@ class MountNodePassthrough : public MountNode { public: // Normal read/write operations on a file - virtual int Read(size_t offs, void* buf, size_t count) { + virtual Error Read(size_t offs, void* buf, size_t count, int* out_bytes) { + *out_bytes = 0; + off_t new_offset; int err = _real_lseek(real_fd_, offs, 0, &new_offset); - if (err) { - errno = err; - return -1; - } + if (err) + return err; size_t nread; err = _real_read(real_fd_, buf, count, &nread); - if (err) { - errno = err; - return -1; - } + if (err) + return err; - return static_cast<int>(nread); + *out_bytes = static_cast<int>(nread); + return 0; } - virtual int Write(size_t offs, const void* buf, size_t count) { + virtual Error Write(size_t offs, + const void* buf, + size_t count, + int* out_bytes) { + *out_bytes = 0; + off_t new_offset; int err = _real_lseek(real_fd_, offs, 0, &new_offset); - if (err) { - errno = err; - return -1; - } + if (err) + return err; size_t nwrote; err = _real_write(real_fd_, buf, count, &nwrote); - if (err) { - errno = err; - return -1; - } + if (err) + return err; - return static_cast<int>(nwrote); + *out_bytes = static_cast<int>(nwrote); + return 0; } - virtual int FTruncate(off_t size) { + virtual Error FTruncate(off_t size) { // TODO(binji): what to do here? - return -1; + return ENOSYS; } - virtual int GetDents(size_t offs, struct dirent* pdir, size_t count) { + virtual Error GetDents(size_t offs, struct dirent* pdir, size_t count) { size_t nread; int err = _real_getdents(real_fd_, pdir, count, &nread); - if (err) { - errno = err; - return -1; - } - + if (err) + return err; return nread; } - virtual int GetStat(struct stat* stat) { + virtual Error GetStat(struct stat* stat) { int err = _real_fstat(real_fd_, stat); - if (err) { - errno = err; - return -1; - } - + if (err) + return err; return 0; } - void* MMap(void* addr, size_t length, int prot, int flags, size_t offset) { - void* new_addr = addr; - int err = _real_mmap(&new_addr, length, prot, flags, real_fd_, offset); - if (err) { - errno = err; - return (void*)-1; - } - - return new_addr; + Error MMap(void* addr, + size_t length, + int prot, + int flags, + size_t offset, + void** out_addr) { + *out_addr = addr; + int err = _real_mmap(out_addr, length, prot, flags, real_fd_, offset); + if (err) + return err; + return 0; } private: @@ -105,70 +98,56 @@ class MountNodePassthrough : public MountNode { int real_fd_; }; -MountPassthrough::MountPassthrough() { -} +MountPassthrough::MountPassthrough() {} -bool MountPassthrough::Init(int dev, StringMap_t& args, - PepperInterface* ppapi) { - Mount::Init(dev, args, ppapi); - return true; +Error MountPassthrough::Init(int dev, + StringMap_t& args, + PepperInterface* ppapi) { + return Mount::Init(dev, args, ppapi); } -void MountPassthrough::Destroy() { -} +void MountPassthrough::Destroy() {} + +Error MountPassthrough::Open(const Path& path, int mode, MountNode** out_node) { + *out_node = NULL; -MountNode *MountPassthrough::Open(const Path& path, int mode) { int real_fd; - int err = _real_open(path.Join().c_str(), mode, 0666, &real_fd); - if (err) { - errno = err; - return NULL; - } + int error = _real_open(path.Join().c_str(), mode, 0666, &real_fd); + if (error) + return error; MountNodePassthrough* node = new MountNodePassthrough(this, real_fd); - return node; + *out_node = node; + return 0; } -MountNode *MountPassthrough::OpenResource(const Path& path) { +Error MountPassthrough::OpenResource(const Path& path, MountNode** out_node) { + *out_node = NULL; + int real_fd; - int err = _real_open_resource(path.Join().c_str(), &real_fd); - if (err) { - errno = err; - return NULL; - } + int error = _real_open_resource(path.Join().c_str(), &real_fd); + if (error) + return error; MountNodePassthrough* node = new MountNodePassthrough(this, real_fd); - return node; + *out_node = node; + return 0; } -int MountPassthrough::Unlink(const Path& path) { +Error MountPassthrough::Unlink(const Path& path) { // Not implemented by NaCl. - errno = ENOSYS; - return -1; + return ENOSYS; } -int MountPassthrough::Mkdir(const Path& path, int perm) { - int err = _real_mkdir(path.Join().c_str(), perm); - if (err) { - errno = err; - return -1; - } - - return 0; +Error MountPassthrough::Mkdir(const Path& path, int perm) { + return _real_mkdir(path.Join().c_str(), perm); } -int MountPassthrough::Rmdir(const Path& path) { - int err = _real_rmdir(path.Join().c_str()); - if (err) { - errno = err; - return -1; - } - - return 0; +Error MountPassthrough::Rmdir(const Path& path) { + return _real_rmdir(path.Join().c_str()); } -int MountPassthrough::Remove(const Path& path) { +Error MountPassthrough::Remove(const Path& path) { // Not implemented by NaCl. - errno = ENOSYS; - return -1; + return ENOSYS; } 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 00a0eec..b143abf 100644 --- a/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h +++ b/native_client_sdk/src/libraries/nacl_io/mount_passthrough.h @@ -11,16 +11,16 @@ class MountPassthrough : public Mount { protected: MountPassthrough(); - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi); + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi); virtual void Destroy(); public: - virtual MountNode *Open(const Path& path, int mode); - virtual MountNode *OpenResource(const Path& path); - virtual int Unlink(const Path& path); - virtual int Mkdir(const Path& path, int perm); - virtual int Rmdir(const Path& path); - virtual int Remove(const Path& path); + virtual Error Open(const Path& path, int mode, MountNode** out_node); + virtual Error OpenResource(const Path& path, MountNode** 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); private: friend class Mount; diff --git a/native_client_sdk/src/libraries/nacl_io_test/example.dsc b/native_client_sdk/src/libraries/nacl_io_test/example.dsc index a47ed63..df934a5 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/example.dsc +++ b/native_client_sdk/src/libraries/nacl_io_test/example.dsc @@ -1,5 +1,4 @@ { - # TODO(binji): pnacl doesn't build right now because gtest doesn't build yet. 'TOOLS': ['newlib', 'glibc', 'pnacl', 'win'], # Need to add ../../examples for common.js diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc index 268c6f5..9e6f483 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_object_test.cc @@ -23,22 +23,21 @@ namespace { class MountRefMock : public Mount { public: MountRefMock(int* mount_count, int* handle_count) - : mount_count(mount_count), - handle_count(handle_count) { + : mount_count(mount_count), handle_count(handle_count) { (*mount_count)++; } - ~MountRefMock() { - (*mount_count)--; - } + ~MountRefMock() { (*mount_count)--; } 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; } - int Remove(const Path& path) { return 0; } + Error Open(const Path& path, int mode, MountNode** out_node) { + *out_node = NULL; + return ENOSYS; + } + Error Unlink(const Path& path) { return 0; } + Error Mkdir(const Path& path, int permissions) { return 0; } + Error Rmdir(const Path& path) { return 0; } + Error Remove(const Path& path) { return 0; } public: int* mount_count; @@ -47,8 +46,8 @@ class MountRefMock : public Mount { class KernelHandleRefMock : public KernelHandle { public: - KernelHandleRefMock(Mount* mnt, MountNode* node, int flags) - : KernelHandle(mnt, node, flags) { + KernelHandleRefMock(Mount* mnt, MountNode* node) + : KernelHandle(mnt, node) { MountRefMock* mock_mount = static_cast<MountRefMock*>(mnt); (*mock_mount->handle_count)++; } @@ -61,9 +60,7 @@ class KernelHandleRefMock : public KernelHandle { class KernelObjectTest : public ::testing::Test { public: - KernelObjectTest() - : mount_count(0), - handle_count(0) { + KernelObjectTest() : mount_count(0), handle_count(0) { proxy = new KernelObject; mnt = new MountRefMock(&mount_count, &handle_count); } @@ -81,10 +78,10 @@ class KernelObjectTest : public ::testing::Test { } // namespace - TEST_F(KernelObjectTest, Referencing) { - KernelHandle* handle = new KernelHandleRefMock(mnt, NULL, 0); - KernelHandle* handle2 = new KernelHandleRefMock(mnt, NULL, 0); + KernelHandle* handle = new KernelHandleRefMock(mnt, NULL); + KernelHandle* handle2 = new KernelHandleRefMock(mnt, NULL); + KernelHandle* result_handle = NULL; // Objects should have one ref when we start EXPECT_EQ(1, mnt->RefCount()); @@ -111,14 +108,16 @@ TEST_F(KernelObjectTest, Referencing) { EXPECT_EQ(2, fd3); // We should find the handle by either fd - EXPECT_EQ(handle, proxy->AcquireHandle(fd1)); - EXPECT_EQ(handle, proxy->AcquireHandle(fd2)); + EXPECT_EQ(0, proxy->AcquireHandle(fd1, &result_handle)); + EXPECT_EQ(handle, result_handle); + EXPECT_EQ(0, proxy->AcquireHandle(fd2, &result_handle)); + EXPECT_EQ(handle, result_handle); // A non existent fd should fail - EXPECT_EQ(NULL, proxy->AcquireHandle(-1)); - EXPECT_EQ(EBADF, errno); - EXPECT_EQ(NULL, proxy->AcquireHandle(100)); - EXPECT_EQ(EBADF, errno); + EXPECT_EQ(EBADF, proxy->AcquireHandle(-1, &result_handle)); + EXPECT_EQ(NULL, result_handle); + EXPECT_EQ(EBADF, proxy->AcquireHandle(100, &result_handle)); + EXPECT_EQ(NULL, result_handle); // Acquiring the handle, should have ref'd it EXPECT_EQ(4, mnt->RefCount()); @@ -155,27 +154,35 @@ TEST_F(KernelObjectTest, Referencing) { } TEST_F(KernelObjectTest, FreeAndReassignFD) { + KernelHandle* result_handle = NULL; + EXPECT_EQ(0, handle_count); - KernelHandle* handle = new KernelHandleRefMock(mnt, NULL, 0); + KernelHandle* handle = new KernelHandleRefMock(mnt, NULL); EXPECT_EQ(1, handle_count); EXPECT_EQ(1, handle->RefCount()); // Assign to a non-existent FD proxy->FreeAndReassignFD(2, handle); - EXPECT_EQ((KernelHandle*)NULL, proxy->AcquireHandle(0)); - EXPECT_EQ((KernelHandle*)NULL, proxy->AcquireHandle(1)); - EXPECT_EQ(handle, proxy->AcquireHandle(2)); + EXPECT_EQ(EBADF, proxy->AcquireHandle(0, &result_handle)); + EXPECT_EQ(NULL, result_handle); + EXPECT_EQ(EBADF, proxy->AcquireHandle(1, &result_handle)); + EXPECT_EQ(NULL, result_handle); + EXPECT_EQ(0, proxy->AcquireHandle(2, &result_handle)); + EXPECT_EQ(handle, result_handle); proxy->ReleaseHandle(handle); EXPECT_EQ(1, handle_count); EXPECT_EQ(2, handle->RefCount()); proxy->FreeAndReassignFD(0, handle); - EXPECT_EQ(handle, proxy->AcquireHandle(0)); - EXPECT_EQ((KernelHandle*)NULL, proxy->AcquireHandle(1)); - EXPECT_EQ(handle, proxy->AcquireHandle(2)); + EXPECT_EQ(0, proxy->AcquireHandle(0, &result_handle)); + EXPECT_EQ(handle, result_handle); + EXPECT_EQ(EBADF, proxy->AcquireHandle(1, &result_handle)); + EXPECT_EQ(NULL, result_handle); + EXPECT_EQ(0, proxy->AcquireHandle(2, &result_handle)); + EXPECT_EQ(handle, result_handle); proxy->ReleaseHandle(handle); proxy->ReleaseHandle(handle); diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc index d681653..40f4ef7 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_proxy_test.cc @@ -21,11 +21,9 @@ #include "gtest/gtest.h" - class KernelProxyTest : public ::testing::Test { public: - KernelProxyTest() - : kp_(new KernelProxy) { + KernelProxyTest() : kp_(new KernelProxy) { ki_init(kp_); // Unmount the passthrough FS and mount a memfs. EXPECT_EQ(0, kp_->umount("/")); @@ -41,7 +39,6 @@ class KernelProxyTest : public ::testing::Test { KernelProxy* kp_; }; - TEST_F(KernelProxyTest, WorkingDirectory) { char text[1024]; @@ -50,7 +47,7 @@ TEST_F(KernelProxyTest, WorkingDirectory) { EXPECT_STREQ("/", text); char* alloc = ki_getwd(NULL); - EXPECT_EQ((char *) NULL, alloc); + EXPECT_EQ((char*)NULL, alloc); EXPECT_EQ(EFAULT, errno); text[0] = 0; @@ -113,7 +110,8 @@ TEST_F(KernelProxyTest, MemMountIO) { EXPECT_NE(-1, fd3); len = ki_read(fd3, text, sizeof(text)); - if (len > -0) text[len] = 0; + if (len > 0) + text[len] = 0; EXPECT_EQ(5, len); EXPECT_STREQ("HELLO", text); EXPECT_EQ(0, ki_close(fd1)); @@ -124,7 +122,8 @@ TEST_F(KernelProxyTest, MemMountIO) { EXPECT_EQ(5, ki_write(fd1, "WORLD", 5)); len = ki_read(fd3, text, sizeof(text)); - if (len >= 0) text[len] = 0; + if (len >= 0) + text[len] = 0; EXPECT_EQ(5, len); EXPECT_STREQ("WORLD", text); @@ -132,7 +131,8 @@ TEST_F(KernelProxyTest, MemMountIO) { fd2 = ki_open("/foo/bar", O_RDONLY); EXPECT_NE(-1, fd2); len = ki_read(fd2, text, sizeof(text)); - if (len > 0) text[len] = 0; + if (len > 0) + text[len] = 0; EXPECT_EQ(10, len); EXPECT_STREQ("HELLOWORLD", text); } @@ -197,17 +197,17 @@ TEST_F(KernelProxyTest, MemMountDup) { // fd, new_fd, dup_fd -> "/bar" } - StringMap_t g_StringMap; class MountMockInit : public MountMem { public: - virtual bool Init(int dev, StringMap_t& args, PepperInterface* ppapi) { + virtual Error Init(int dev, StringMap_t& args, PepperInterface* ppapi) { g_StringMap = args; if (args.find("false") != args.end()) - return false; - return true; - }; + return EINVAL; + return 0; + } + ; }; class KernelProxyMountMock : public KernelProxy { @@ -219,10 +219,7 @@ class KernelProxyMountMock : public KernelProxy { class KernelProxyMountTest : public ::testing::Test { public: - KernelProxyMountTest() - : kp_(new KernelProxyMountMock) { - ki_init(kp_); - } + KernelProxyMountTest() : kp_(new KernelProxyMountMock) { ki_init(kp_); } ~KernelProxyMountTest() { ki_uninit(); @@ -245,28 +242,38 @@ TEST_F(KernelProxyMountTest, MountInit) { EXPECT_EQ("y", g_StringMap["x"]); } - namespace { int g_MMapCount = 0; class MountNodeMockMMap : public MountNode { public: - MountNodeMockMMap(Mount* mount) - : MountNode(mount), - node_mmap_count_(0) { - Init(0); + MountNodeMockMMap(Mount* mount) : MountNode(mount), node_mmap_count_(0) { + EXPECT_EQ(0, Init(0)); } - virtual void* MMap(void* addr, size_t length, int prot, int flags, - size_t offset) { + virtual Error MMap(void* addr, + size_t length, + int prot, + int flags, + size_t offset, + void** out_addr) { node_mmap_count_++; switch (g_MMapCount++) { - case 0: return reinterpret_cast<void*>(0x1000); - case 1: return reinterpret_cast<void*>(0x2000); - case 2: return reinterpret_cast<void*>(0x3000); - default: return MAP_FAILED; + case 0: + *out_addr = reinterpret_cast<void*>(0x1000); + break; + case 1: + *out_addr = reinterpret_cast<void*>(0x2000); + break; + case 2: + *out_addr = reinterpret_cast<void*>(0x3000); + break; + default: + return EPERM; } + + return 0; } private: @@ -275,16 +282,20 @@ class MountNodeMockMMap : public MountNode { class MountMockMMap : public Mount { public: - virtual MountNode* Open(const Path& path, int mode) { + virtual Error Open(const Path& path, int mode, MountNode** out_node) { MountNodeMockMMap* node = new MountNodeMockMMap(this); - return node; + *out_node = node; + return 0; } - virtual MountNode* OpenResource(const Path& path) { return NULL; } - virtual int Unlink(const Path& path) { return -1; } - virtual int Mkdir(const Path& path, int permissions) { return -1; } - virtual int Rmdir(const Path& path) { return -1; } - virtual int Remove(const Path& path) { return -1; } + virtual Error OpenResource(const Path& path, MountNode** out_node) { + *out_node = NULL; + return ENOSYS; + } + virtual Error Unlink(const Path& path) { return ENOSYS; } + virtual Error Mkdir(const Path& path, int permissions) { return ENOSYS; } + virtual Error Rmdir(const Path& path) { return ENOSYS; } + virtual Error Remove(const Path& path) { return ENOSYS; } }; class KernelProxyMockMMap : public KernelProxy { @@ -296,10 +307,7 @@ class KernelProxyMockMMap : public KernelProxy { class KernelProxyMMapTest : public ::testing::Test { public: - KernelProxyMMapTest() - : kp_(new KernelProxyMockMMap) { - ki_init(kp_); - } + KernelProxyMMapTest() : kp_(new KernelProxyMockMMap) { ki_init(kp_); } ~KernelProxyMMapTest() { ki_uninit(); diff --git a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc b/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc index f6f010c..34d8472 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/kernel_wrap_test.cc @@ -81,6 +81,10 @@ class KernelWrapTest : public ::testing::Test { .WillOnce(Return(0)) .WillOnce(Return(1)) .WillOnce(Return(2)); + // And will call mount / and /dev. + EXPECT_CALL(mock, mount(_, _, _, _, _)) + .WillOnce(Return(0)) + .WillOnce(Return(0)); ki_init(&mock); } diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc index 0ca210b..e92a20a 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_html5fs_test.cc @@ -166,7 +166,7 @@ void MountHtml5FsNodeTest::InitFilesystem() { } void MountHtml5FsNodeTest::InitNode() { - node_ = mnt_->Open(Path(path_), O_CREAT | O_RDWR); + ASSERT_EQ(0, mnt_->Open(Path(path_), O_CREAT | O_RDWR, &node_)); ASSERT_NE((MountNode*)NULL, node_); } @@ -378,7 +378,8 @@ TEST_F(MountHtml5FsNodeSyncTest, Write) { EXPECT_CALL(*fileio_, Write(fileio_resource_, offset, &buffer[0], count, _)) .WillOnce(Return(count)); - int result = node_->Write(offset, &buffer, count); + int result = 0; + EXPECT_EQ(0, node_->Write(offset, &buffer, count, &result)); EXPECT_EQ(count, result); } @@ -390,7 +391,8 @@ TEST_F(MountHtml5FsNodeSyncTest, Read) { EXPECT_CALL(*fileio_, Read(fileio_resource_, offset, &buffer[0], count, _)) .WillOnce(Return(count)); - int result = node_->Read(offset, &buffer, count); + int result = 0; + EXPECT_EQ(0, node_->Read(offset, &buffer, count, &result)); EXPECT_EQ(count, result); } @@ -437,9 +439,10 @@ TEST_F(MountHtml5FsNodeSyncTest, GetDents) { memset(&dirents[0], 0, sizeof(dirents)); // Should fail for regular files. - int result = node_->GetDents(0, &dirents[0], sizeof(dirent) * 2); - ASSERT_EQ(-1, result); - ASSERT_EQ(ENOTDIR, errno); + int result_bytes = 0; + EXPECT_EQ(ENOTDIR, node_->GetDents(0, &dirents[0], sizeof(dirent) * 2, + &result_bytes)); + ASSERT_EQ(0, result_bytes); } TEST_F(MountHtml5FsNodeSyncDirTest, OpenAndClose) { @@ -451,9 +454,9 @@ TEST_F(MountHtml5FsNodeSyncDirTest, Write) { const char buffer[30] = {0}; // Should fail for directories. - int result = node_->Write(offset, &buffer, count); - ASSERT_EQ(-1, result); - EXPECT_EQ(EISDIR, errno); + int result_bytes = 0; + EXPECT_EQ(EISDIR, node_->Write(offset, &buffer, count, &result_bytes)); + ASSERT_EQ(0, result_bytes); } TEST_F(MountHtml5FsNodeSyncDirTest, Read) { @@ -462,9 +465,9 @@ TEST_F(MountHtml5FsNodeSyncDirTest, Read) { char buffer[30] = {0}; // Should fail for directories. - int result = node_->Read(offset, &buffer, count); - ASSERT_EQ(-1, result); - EXPECT_EQ(EISDIR, errno); + int result_bytes = 0; + EXPECT_EQ(EISDIR, node_->Read(offset, &buffer, count, &result_bytes)); + ASSERT_EQ(0, result_bytes); } TEST_F(MountHtml5FsNodeSyncDirTest, GetStat) { @@ -498,9 +501,7 @@ TEST_F(MountHtml5FsNodeSyncDirTest, GetStat) { TEST_F(MountHtml5FsNodeSyncDirTest, FTruncate) { const int size = 123; // Should fail for directories. - int result = node_->FTruncate(size); - ASSERT_EQ(-1, result); - EXPECT_EQ(EISDIR, errno); + EXPECT_EQ(EISDIR, node_->FTruncate(size)); } TEST_F(MountHtml5FsNodeSyncDirTest, GetDents) { @@ -542,9 +543,12 @@ TEST_F(MountHtml5FsNodeSyncDirTest, GetDents) { memset(&dirents[0], 0, sizeof(dirents)); // +2 to test a size that is not a multiple of sizeof(dirent). // Expect it to round down. - int result = node_->GetDents(0, &dirents[0], sizeof(dirent) * 2 + 2); + int result_bytes = 0; + EXPECT_EQ( + 0, + node_->GetDents(0, &dirents[0], sizeof(dirent) * 2 + 2, &result_bytes)); - ASSERT_EQ(sizeof(dirent) * 2, result); + ASSERT_EQ(sizeof(dirent) * 2, result_bytes); EXPECT_LT(0, dirents[0].d_ino); // 0 is an invalid inode number. EXPECT_EQ(sizeof(dirent), dirents[0].d_off); EXPECT_EQ(sizeof(dirent), dirents[0].d_reclen); diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc index 350874d..ced8d06 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_http_test.cc @@ -29,7 +29,7 @@ using ::testing::StrEq; class MountHttpMock : public MountHttp { public: MountHttpMock(StringMap_t map, PepperInterfaceMock* ppapi) { - EXPECT_TRUE(Init(1, map, ppapi)); + EXPECT_EQ(0, Init(1, map, ppapi)); } ~MountHttpMock() { @@ -71,25 +71,34 @@ TEST_F(MountHttpTest, MountEmpty) { TEST_F(MountHttpTest, ParseManifest) { StringMap_t args; + size_t result_size = 0; + mnt_ = new MountHttpMock(args, &ppapi_); char manifest[] = "-r-- 123 /mydir/foo\n-rw- 234 /thatdir/bar\n"; - EXPECT_TRUE(mnt_->ParseManifest(manifest)); + EXPECT_EQ(0, mnt_->ParseManifest(manifest)); - MountNodeDir* root = mnt_->FindOrCreateDir(Path("/")); + MountNodeDir* root = NULL; + EXPECT_EQ(0, mnt_->FindOrCreateDir(Path("/"), &root)); + ASSERT_NE((MountNode*)NULL, root); EXPECT_EQ(2, root->ChildCount()); - MountNodeDir* dir = mnt_->FindOrCreateDir(Path("/mydir")); + MountNodeDir* dir = NULL; + EXPECT_EQ(0, mnt_->FindOrCreateDir(Path("/mydir"), &dir)); + ASSERT_NE((MountNode*)NULL, dir); EXPECT_EQ(1, dir->ChildCount()); MountNode* node = mnt_->GetMap()["/mydir/foo"]; - EXPECT_TRUE(node); - EXPECT_EQ(123, node->GetSize()); + EXPECT_NE((MountNode*)NULL, node); + EXPECT_EQ(0, node->GetSize(&result_size)); + EXPECT_EQ(123, result_size); // Since these files are cached thanks to the manifest, we can open them // without accessing the PPAPI URL API. - MountNode* foo = mnt_->Open(Path("/mydir/foo"), O_RDONLY); - MountNode* bar = mnt_->Open(Path("/thatdir/bar"), O_RDWR); + MountNode* foo = NULL; + EXPECT_EQ(0, mnt_->Open(Path("/mydir/foo"), O_RDONLY, &foo)); + MountNode* bar = NULL; + EXPECT_EQ(0, mnt_->Open(Path("/thatdir/bar"), O_RDWR, &bar)); struct stat sfoo; struct stat sbar; @@ -248,7 +257,7 @@ void MountHttpNodeTest::SetResponseBody(const char* body) { } void MountHttpNodeTest::OpenNode() { - node_ = mnt_->Open(Path(path_), O_RDONLY); + ASSERT_EQ(0, mnt_->Open(Path(path_), O_RDONLY, &node_)); ASSERT_NE((MountNode*)NULL, node_); } @@ -266,7 +275,9 @@ void MountHttpNodeTest::TearDown() { delete mnt_; } -TEST_F(MountHttpNodeTest, OpenAndClose) { +TEST_F(MountHttpNodeTest, OpenAndCloseNoCache) { + StringMap_t smap; + smap["cache_content"] = "false"; SetMountArgs(StringMap_t()); ExpectOpen("HEAD"); ExpectHeaders(""); @@ -275,6 +286,9 @@ TEST_F(MountHttpNodeTest, OpenAndClose) { } TEST_F(MountHttpNodeTest, ReadCached) { + size_t result_size = 0; + int result_bytes = 0; + SetMountArgs(StringMap_t()); ExpectOpen("HEAD"); ExpectHeaders(""); @@ -282,7 +296,8 @@ TEST_F(MountHttpNodeTest, ReadCached) { OpenNode(); ResetMocks(); - EXPECT_EQ(42, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(42, result_size); char buf[10]; memset(&buf[0], 0, sizeof(buf)); @@ -291,20 +306,24 @@ TEST_F(MountHttpNodeTest, ReadCached) { ExpectHeaders(""); SetResponse(200, "Content-Length: 42\n"); SetResponseBody("Here is some response text. And some more."); - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("Here is s", &buf[0]); ResetMocks(); // Further reads should be cached. - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("Here is s", &buf[0]); - node_->Read(10, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(10, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("me respon", &buf[0]); - EXPECT_EQ(42, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(42, result_size); } TEST_F(MountHttpNodeTest, ReadCachedNoContentLength) { + size_t result_size = 0; + int result_bytes = 0; + SetMountArgs(StringMap_t()); ExpectOpen("HEAD"); ExpectHeaders(""); @@ -319,25 +338,30 @@ TEST_F(MountHttpNodeTest, ReadCachedNoContentLength) { // GetSize will Read() because it didn't get the content length from the HEAD // request. - EXPECT_EQ(42, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(42, result_size); char buf[10]; memset(&buf[0], 0, sizeof(buf)); - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("Here is s", &buf[0]); ResetMocks(); // Further reads should be cached. - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("Here is s", &buf[0]); - node_->Read(10, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(10, buf, sizeof(buf) - 1, &result_bytes)); EXPECT_STREQ("me respon", &buf[0]); - EXPECT_EQ(42, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(42, result_size); } TEST_F(MountHttpNodeTest, ReadCachedUnderrun) { + size_t result_size = 0; + int result_bytes = 0; + SetMountArgs(StringMap_t()); ExpectOpen("HEAD"); ExpectHeaders(""); @@ -345,7 +369,8 @@ TEST_F(MountHttpNodeTest, ReadCachedUnderrun) { OpenNode(); ResetMocks(); - EXPECT_EQ(100, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(100, result_size); char buf[10]; memset(&buf[0], 0, sizeof(buf)); @@ -354,14 +379,19 @@ TEST_F(MountHttpNodeTest, ReadCachedUnderrun) { ExpectHeaders(""); SetResponse(200, "Content-Length: 100\n"); SetResponseBody("abcdefghijklmnopqrstuvwxyz"); - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("abcdefghi", &buf[0]); ResetMocks(); - EXPECT_EQ(26, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(26, result_size); } TEST_F(MountHttpNodeTest, ReadCachedOverrun) { + size_t result_size = 0; + int result_bytes = 0; + SetMountArgs(StringMap_t()); ExpectOpen("HEAD"); ExpectHeaders(""); @@ -369,7 +399,8 @@ TEST_F(MountHttpNodeTest, ReadCachedOverrun) { OpenNode(); ResetMocks(); - EXPECT_EQ(15, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(15, result_size); char buf[10]; memset(&buf[0], 0, sizeof(buf)); @@ -378,14 +409,18 @@ TEST_F(MountHttpNodeTest, ReadCachedOverrun) { ExpectHeaders(""); SetResponse(200, "Content-Length: 15\n"); SetResponseBody("01234567890123456789"); - node_->Read(10, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(10, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(5, result_bytes); EXPECT_STREQ("01234", &buf[0]); ResetMocks(); - EXPECT_EQ(15, node_->GetSize()); + EXPECT_EQ(0, node_->GetSize(&result_size)); + EXPECT_EQ(15, result_size); } TEST_F(MountHttpNodeTest, ReadPartial) { + int result_bytes = 0; + StringMap_t args; args["cache_content"] = "false"; SetMountArgs(args); @@ -402,7 +437,8 @@ TEST_F(MountHttpNodeTest, ReadPartial) { ExpectHeaders("Range: bytes=0-8\n"); SetResponse(206, "Content-Length: 9\nContent-Range: bytes=0-8\n"); SetResponseBody("012345678"); - node_->Read(0, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(0, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("012345678", &buf[0]); ResetMocks(); @@ -411,11 +447,14 @@ TEST_F(MountHttpNodeTest, ReadPartial) { ExpectHeaders("Range: bytes=10-18\n"); SetResponse(206, "Content-Length: 9\nContent-Range: bytes=10-18\n"); SetResponseBody("abcdefghi"); - node_->Read(10, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(10, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("abcdefghi", &buf[0]); } TEST_F(MountHttpNodeTest, ReadPartialNoServerSupport) { + int result_bytes = 0; + StringMap_t args; args["cache_content"] = "false"; SetMountArgs(args); @@ -432,6 +471,7 @@ TEST_F(MountHttpNodeTest, ReadPartialNoServerSupport) { ExpectHeaders("Range: bytes=10-18\n"); SetResponse(200, "Content-Length: 20\n"); SetResponseBody("0123456789abcdefghij"); - node_->Read(10, buf, sizeof(buf) - 1); + EXPECT_EQ(0, node_->Read(10, buf, sizeof(buf) - 1, &result_bytes)); + EXPECT_EQ(sizeof(buf) - 1, result_bytes); EXPECT_STREQ("abcdefghi", &buf[0]); } diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc index 2de13cd..bc6fa65 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_node_test.cc @@ -6,6 +6,7 @@ #include <errno.h> #include <fcntl.h> +#include "nacl_io/error.h" #include "nacl_io/kernel_proxy.h" #include "nacl_io/mount_node.h" #include "nacl_io/mount_node_dir.h" @@ -14,31 +15,25 @@ #include "gtest/gtest.h" -#define NULL_NODE ((MountNode *) NULL) +#define NULL_NODE ((MountNode*) NULL) static int s_AllocNum = 0; class MockMemory : public MountNodeMem { public: - MockMemory() : MountNodeMem(NULL) { - s_AllocNum++; - } + MockMemory() : MountNodeMem(NULL) { s_AllocNum++; } - ~MockMemory() { - s_AllocNum--; - } + ~MockMemory() { s_AllocNum--; } - bool Init(int mode) { - return MountNodeMem::Init(mode); - } - int AddChild(const std::string& name, MountNode *node) { + Error Init(int mode) { return MountNodeMem::Init(mode); } + Error AddChild(const std::string& name, MountNode* node) { return MountNodeMem::AddChild(name, node); } - int RemoveChild(const std::string& name) { + Error RemoveChild(const std::string& name) { return MountNodeMem::RemoveChild(name); } - MountNode* FindChild(const std::string& name) { - return MountNodeMem::FindChild(name); + Error FindChild(const std::string& name, MountNode** out_node) { + return MountNodeMem::FindChild(name, out_node); } void Link() { MountNodeMem::Link(); } void Unlink() { MountNodeMem::Unlink(); } @@ -49,25 +44,19 @@ class MockMemory : public MountNodeMem { class MockDir : public MountNodeDir { public: - MockDir() : MountNodeDir(NULL) { - s_AllocNum++; - } + MockDir() : MountNodeDir(NULL) { s_AllocNum++; } - ~MockDir() { - s_AllocNum--; - } + ~MockDir() { s_AllocNum--; } - bool Init(int mode) { - return MountNodeDir::Init(mode); - } - int AddChild(const std::string& name, MountNode *node) { + Error Init(int mode) { return MountNodeDir::Init(mode); } + Error AddChild(const std::string& name, MountNode* node) { return MountNodeDir::AddChild(name, node); } - int RemoveChild(const std::string& name) { + Error RemoveChild(const std::string& name) { return MountNodeDir::RemoveChild(name); } - MountNode* FindChild(const std::string& name) { - return MountNodeDir::FindChild(name); + Error FindChild(const std::string& name, MountNode** out_node) { + return MountNodeDir::FindChild(name, out_node); } void Link() { MountNodeDir::Link(); } void Unlink() { MountNodeDir::Unlink(); } @@ -77,9 +66,12 @@ class MockDir : public MountNodeDir { }; TEST(MountNodeTest, File) { - MockMemory *file = new MockMemory; + MockMemory* file = new MockMemory; + MountNode* result_node = NULL; + size_t result_size = 0; + int result_bytes = 0; - EXPECT_TRUE(file->Init(S_IREAD | S_IWRITE)); + EXPECT_EQ(0, file->Init(S_IREAD | S_IWRITE)); // Test properties EXPECT_EQ(0, file->GetLinks()); @@ -97,12 +89,18 @@ TEST(MountNodeTest, File) { buf1[a] = a; memset(buf2, 0, sizeof(buf2)); - EXPECT_EQ(0, file->GetSize()); - EXPECT_EQ(0, file->Read(0, buf2, sizeof(buf2))); - EXPECT_EQ(0, file->GetSize()); - EXPECT_EQ(sizeof(buf1), file->Write(0, buf1, sizeof(buf1))); - EXPECT_EQ(sizeof(buf1), file->GetSize()); - EXPECT_EQ(sizeof(buf1), file->Read(0, buf2, sizeof(buf2))); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(0, result_size); + EXPECT_EQ(0, file->Read(0, buf2, sizeof(buf2), &result_bytes)); + EXPECT_EQ(0, result_bytes); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(0, result_size); + EXPECT_EQ(0, file->Write(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(sizeof(buf1), result_bytes); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(sizeof(buf1), result_size); + EXPECT_EQ(0, file->Read(0, buf2, sizeof(buf2), &result_bytes)); + EXPECT_EQ(sizeof(buf1), result_bytes); EXPECT_EQ(0, memcmp(buf1, buf2, sizeof(buf1))); struct stat s; @@ -112,20 +110,21 @@ TEST(MountNodeTest, File) { // Directory operations should fail struct dirent d; - EXPECT_EQ(-1, file->GetDents(0, &d, sizeof(d))); - EXPECT_EQ(errno, ENOTDIR); - EXPECT_EQ(-1, file->AddChild("", file)); - EXPECT_EQ(errno, ENOTDIR); - EXPECT_EQ(-1, file->RemoveChild("")); - EXPECT_EQ(errno, ENOTDIR); - EXPECT_EQ(NULL_NODE, file->FindChild("")); - EXPECT_EQ(errno, ENOTDIR); + EXPECT_EQ(ENOTDIR, file->GetDents(0, &d, sizeof(d), &result_bytes)); + EXPECT_EQ(ENOTDIR, file->AddChild("", file)); + EXPECT_EQ(ENOTDIR, file->RemoveChild("")); + EXPECT_EQ(ENOTDIR, file->FindChild("", &result_node)); + EXPECT_EQ(NULL_NODE, result_node); delete file; } TEST(MountNodeTest, Directory) { - MockDir *root = new MockDir(); + MockDir* root = new MockDir(); + MountNode* result_node = NULL; + size_t result_size = 0; + int result_bytes = 0; + root->Init(S_IREAD | S_IWRITE); // Test properties @@ -139,15 +138,14 @@ TEST(MountNodeTest, Directory) { // IO operations should fail char buf1[1024]; - EXPECT_EQ(0, root->GetSize()); - EXPECT_EQ(-1, root->Read(0, buf1, sizeof(buf1))); - EXPECT_EQ(errno, EISDIR); - EXPECT_EQ(-1, root->Write(0, buf1, sizeof(buf1))); - EXPECT_EQ(errno, EISDIR); + EXPECT_EQ(0, root->GetSize(&result_size)); + EXPECT_EQ(0, result_size); + EXPECT_EQ(EISDIR, root->Read(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(EISDIR, root->Write(0, buf1, sizeof(buf1), &result_bytes)); // Test directory operations MockMemory* file = new MockMemory; - EXPECT_TRUE(file->Init(S_IREAD | S_IWRITE)); + EXPECT_EQ(0, file->Init(S_IREAD | S_IWRITE)); EXPECT_EQ(1, root->RefCount()); EXPECT_EQ(1, file->RefCount()); @@ -157,25 +155,28 @@ TEST(MountNodeTest, Directory) { // Test that the directory is there struct dirent d; - EXPECT_EQ(sizeof(d), root->GetDents(0, &d, sizeof(d))); + EXPECT_EQ(0, root->GetDents(0, &d, sizeof(d), &result_bytes)); + EXPECT_EQ(sizeof(d), result_bytes); EXPECT_LT(0, d.d_ino); // 0 is an invalid inode number. EXPECT_EQ(sizeof(d), d.d_off); EXPECT_EQ(sizeof(d), d.d_reclen); EXPECT_EQ(0, strcmp("F1", d.d_name)); - EXPECT_EQ(0, root->GetDents(sizeof(d), &d, sizeof(d))); + EXPECT_EQ(0, root->GetDents(sizeof(d), &d, sizeof(d), &result_bytes)); + EXPECT_EQ(0, result_bytes); EXPECT_EQ(0, root->AddChild("F2", file)); EXPECT_EQ(2, file->GetLinks()); EXPECT_EQ(3, file->RefCount()); - EXPECT_EQ(-1, root->AddChild("F1", file)); - EXPECT_EQ(EEXIST, errno); + EXPECT_EQ(EEXIST, root->AddChild("F1", file)); EXPECT_EQ(2, file->GetLinks()); EXPECT_EQ(2, s_AllocNum); - EXPECT_NE(NULL_NODE, root->FindChild("F1")); - EXPECT_NE(NULL_NODE, root->FindChild("F2")); - EXPECT_EQ(NULL_NODE, root->FindChild("F3")); - EXPECT_EQ(errno, ENOENT); + EXPECT_EQ(0, root->FindChild("F1", &result_node)); + EXPECT_NE(NULL_NODE, result_node); + EXPECT_EQ(0, root->FindChild("F2", &result_node)); + EXPECT_NE(NULL_NODE, result_node); + EXPECT_EQ(ENOENT, root->FindChild("F3", &result_node)); + EXPECT_EQ(NULL_NODE, result_node); EXPECT_EQ(2, s_AllocNum); EXPECT_EQ(0, root->RemoveChild("F1")); diff --git a/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc b/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc index 1604cca..245d13d 100644 --- a/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc +++ b/native_client_sdk/src/libraries/nacl_io_test/mount_test.cc @@ -22,12 +22,10 @@ class MountMemMock : public MountMem { public: MountMemMock() { StringMap_t map; - Init(1, map, NULL); - }; - - int num_nodes() { - return (int) inode_pool_.size(); + EXPECT_EQ(0, Init(1, map, NULL)); } + + int num_nodes() { return (int) inode_pool_.size(); } }; class MountDevMock : public MountDev { @@ -36,20 +34,20 @@ class MountDevMock : public MountDev { StringMap_t map; Init(1, map, NULL); } - int num_nodes() { - return (int) inode_pool_.size(); - } + int num_nodes() { return (int) inode_pool_.size(); } }; } // namespace - -#define NULL_NODE ((MountNode *) NULL) +#define NULL_NODE ((MountNode*) NULL) TEST(MountTest, Sanity) { MountMemMock* mnt = new MountMemMock(); MountNode* file; MountNode* root; + MountNode* result_node; + size_t result_size = 0; + int result_bytes = 0; char buf1[1024]; @@ -57,43 +55,54 @@ TEST(MountTest, Sanity) { EXPECT_EQ(1, mnt->num_nodes()); // Fail to open non existent file - EXPECT_EQ(NULL_NODE, mnt->Open(Path("/foo"), O_RDWR)); - EXPECT_EQ(errno, ENOENT); + EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &result_node)); + EXPECT_EQ(NULL, result_node); // Create a file - file = mnt->Open(Path("/foo"), O_RDWR | O_CREAT); + EXPECT_EQ(0, mnt->Open(Path("/foo"), O_RDWR | O_CREAT, &file)); EXPECT_NE(NULL_NODE, file); - if (file == NULL) return; + if (file == NULL) + return; EXPECT_EQ(2, file->RefCount()); EXPECT_EQ(2, mnt->num_nodes()); + // Open the root directory for write should fail. + EXPECT_EQ(EISDIR, mnt->Open(Path("/"), O_RDWR, &root)); + // Open the root directory - root = mnt->Open(Path("/"), O_RDWR); + EXPECT_EQ(0, mnt->Open(Path("/"), O_RDONLY, &root)); EXPECT_NE(NULL_NODE, root); if (NULL != root) { struct dirent dirs[2]; - int len = root->GetDents(0, dirs, sizeof(dirs)); + int len; + EXPECT_EQ(0, root->GetDents(0, dirs, sizeof(dirs), &len)); EXPECT_EQ(sizeof(struct dirent), len); } // Fail to re-create the same file - EXPECT_EQ(NULL_NODE, mnt->Open(Path("/foo"), O_RDWR | O_CREAT | O_EXCL)); - EXPECT_EQ(errno, EEXIST); + EXPECT_EQ(EEXIST, + mnt->Open(Path("/foo"), O_RDWR | O_CREAT | O_EXCL, &result_node)); + EXPECT_EQ(NULL_NODE, result_node); EXPECT_EQ(2, mnt->num_nodes()); // Fail to create a directory with the same name - EXPECT_EQ(-1, mnt->Mkdir(Path("/foo"), O_RDWR)); - EXPECT_EQ(errno, EEXIST); + EXPECT_EQ(EEXIST, mnt->Mkdir(Path("/foo"), O_RDWR)); // Attempt to READ/WRITE - EXPECT_EQ(0, file->GetSize()); - EXPECT_EQ(sizeof(buf1), file->Write(0, buf1, sizeof(buf1))); - EXPECT_EQ(sizeof(buf1), file->GetSize()); - EXPECT_EQ(sizeof(buf1), file->Read(0, buf1, sizeof(buf1))); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(0, result_size); + EXPECT_EQ(0, file->Write(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(sizeof(buf1), result_bytes); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(sizeof(buf1), result_size); + EXPECT_EQ(0, file->Read(0, buf1, sizeof(buf1), &result_bytes)); + EXPECT_EQ(sizeof(buf1), result_bytes); // Attempt to open the same file - EXPECT_EQ(file, mnt->Open(Path("/foo"), O_RDWR | O_CREAT)); - EXPECT_EQ(sizeof(buf1), file->GetSize()); + EXPECT_EQ(0, mnt->Open(Path("/foo"), O_RDWR | O_CREAT, &result_node)); + EXPECT_EQ(file, result_node); + EXPECT_EQ(0, file->GetSize(&result_size)); + EXPECT_EQ(sizeof(buf1), result_size); EXPECT_EQ(3, file->RefCount()); EXPECT_EQ(2, mnt->num_nodes()); @@ -102,9 +111,8 @@ TEST(MountTest, Sanity) { EXPECT_EQ(2, mnt->num_nodes()); EXPECT_EQ(0, mnt->Unlink(Path("/foo"))); EXPECT_EQ(2, mnt->num_nodes()); - EXPECT_EQ(-1, mnt->Unlink(Path("/foo"))); + EXPECT_EQ(ENOENT, mnt->Unlink(Path("/foo"))); EXPECT_EQ(2, mnt->num_nodes()); - EXPECT_EQ(errno, ENOENT); mnt->ReleaseNode(file); EXPECT_EQ(1, mnt->num_nodes()); @@ -112,13 +120,13 @@ TEST(MountTest, Sanity) { EXPECT_EQ(0, mnt->Mkdir(Path("/foo"), O_RDWR)); // Create a file (exclusively) - file = mnt->Open(Path("/foo/bar"), O_RDWR | O_CREAT | O_EXCL); + EXPECT_EQ(0, mnt->Open(Path("/foo/bar"), O_RDWR | O_CREAT | O_EXCL, &file)); EXPECT_NE(NULL_NODE, file); - if (NULL == file) return; + if (NULL == file) + return; // Attempt to delete the directory - EXPECT_EQ(-1, mnt->Rmdir(Path("/foo"))); - EXPECT_EQ(errno, ENOTEMPTY); + EXPECT_EQ(ENOTEMPTY, mnt->Rmdir(Path("/foo"))); // Unlink the file, then delete the directory EXPECT_EQ(0, mnt->Unlink(Path("/foo/bar"))); @@ -128,60 +136,71 @@ TEST(MountTest, Sanity) { EXPECT_EQ(1, mnt->num_nodes()); // Verify the directory is gone - file = mnt->Open(Path("/foo"), O_RDWR); + EXPECT_EQ(ENOENT, mnt->Open(Path("/foo"), O_RDWR, &file)); EXPECT_EQ(NULL_NODE, file); - EXPECT_EQ(errno, ENOENT); } TEST(MountTest, MemMountRemove) { MountMemMock* mnt = new MountMemMock(); MountNode* file; + MountNode* result_node; EXPECT_EQ(0, mnt->Mkdir(Path("/dir"), O_RDWR)); - file = mnt->Open(Path("/file"), O_RDWR | O_CREAT | O_EXCL); + EXPECT_EQ(0, mnt->Open(Path("/file"), O_RDWR | O_CREAT | O_EXCL, &file)); EXPECT_NE(NULL_NODE, file); mnt->ReleaseNode(file); EXPECT_EQ(0, mnt->Remove(Path("/dir"))); EXPECT_EQ(0, mnt->Remove(Path("/file"))); - EXPECT_EQ(NULL_NODE, mnt->Open(Path("/dir/foo"), O_CREAT | O_RDWR)); - EXPECT_EQ(ENOENT, errno); - EXPECT_EQ(NULL_NODE, mnt->Open(Path("/file"), O_RDONLY)); - EXPECT_EQ(ENOENT, errno); + EXPECT_EQ(ENOENT, + mnt->Open(Path("/dir/foo"), O_CREAT | O_RDWR, &result_node)); + EXPECT_EQ(NULL_NODE, result_node); + EXPECT_EQ(ENOENT, mnt->Open(Path("/file"), O_RDONLY, &result_node)); + EXPECT_EQ(NULL_NODE, result_node); } TEST(MountTest, DevNull) { MountDevMock* mnt = new MountDevMock(); - MountNode* dev_null = mnt->Open(Path("/null"), O_RDWR); + MountNode* dev_null = NULL; + int result_bytes = 0; + + ASSERT_EQ(0, mnt->Open(Path("/null"), O_RDWR, &dev_null)); ASSERT_NE(NULL_NODE, dev_null); // Writing to /dev/null should write everything. const char msg[] = "Dummy test message."; - EXPECT_EQ(strlen(msg), dev_null->Write(0, &msg[0], strlen(msg))); + EXPECT_EQ(0, dev_null->Write(0, &msg[0], strlen(msg), &result_bytes)); + EXPECT_EQ(strlen(msg), result_bytes); // Reading from /dev/null should read nothing. const int kBufferLength = 100; char buffer[kBufferLength]; - EXPECT_EQ(0, dev_null->Read(0, &buffer[0], kBufferLength)); + EXPECT_EQ(0, dev_null->Read(0, &buffer[0], kBufferLength, &result_bytes)); + EXPECT_EQ(0, result_bytes); mnt->ReleaseNode(dev_null); } TEST(MountTest, DevZero) { MountDevMock* mnt = new MountDevMock(); - MountNode* dev_zero = mnt->Open(Path("/zero"), O_RDWR); + MountNode* dev_zero = NULL; + int result_bytes = 0; + + ASSERT_EQ(0, mnt->Open(Path("/zero"), O_RDWR, &dev_zero)); ASSERT_NE(NULL_NODE, dev_zero); // Writing to /dev/zero should write everything. const char msg[] = "Dummy test message."; - EXPECT_EQ(strlen(msg), dev_zero->Write(0, &msg[0], strlen(msg))); + EXPECT_EQ(0, dev_zero->Write(0, &msg[0], strlen(msg), &result_bytes)); + EXPECT_EQ(strlen(msg), result_bytes); // Reading from /dev/zero should read all zeroes. const int kBufferLength = 100; char buffer[kBufferLength]; // First fill with all 1s. memset(&buffer[0], 0x1, kBufferLength); - EXPECT_EQ(kBufferLength, dev_zero->Read(0, &buffer[0], kBufferLength)); + EXPECT_EQ(0, dev_zero->Read(0, &buffer[0], kBufferLength, &result_bytes)); + EXPECT_EQ(kBufferLength, result_bytes); char zero_buffer[kBufferLength]; memset(&zero_buffer[0], 0, kBufferLength); @@ -191,12 +210,16 @@ TEST(MountTest, DevZero) { TEST(MountTest, DevUrandom) { MountDevMock* mnt = new MountDevMock(); - MountNode* dev_urandom = mnt->Open(Path("/urandom"), O_RDWR); + MountNode* dev_urandom = NULL; + int result_bytes = 0; + + ASSERT_EQ(0, mnt->Open(Path("/urandom"), O_RDWR, &dev_urandom)); ASSERT_NE(NULL_NODE, dev_urandom); - // Writing to /dev/zero should write everything. + // Writing to /dev/urandom should write everything. const char msg[] = "Dummy test message."; - EXPECT_EQ(strlen(msg), dev_urandom->Write(0, &msg[0], strlen(msg))); + EXPECT_EQ(0, dev_urandom->Write(0, &msg[0], strlen(msg), &result_bytes)); + EXPECT_EQ(strlen(msg), result_bytes); // Reading from /dev/urandom should read random bytes. const int kSampleBatches = 1000; @@ -207,7 +230,9 @@ TEST(MountTest, DevUrandom) { unsigned char buffer[kSampleBatchSize]; for (int batch = 0; batch < kSampleBatches; ++batch) { - int bytes_read = dev_urandom->Read(0, &buffer[0], kSampleBatchSize); + int bytes_read = 0; + EXPECT_EQ(0, + dev_urandom->Read(0, &buffer[0], kSampleBatchSize, &bytes_read)); EXPECT_EQ(kSampleBatchSize, bytes_read); for (int i = 0; i < bytes_read; ++i) { |