blob: 6cb68faad2cd8b97c14b5b20206e9eef3805641b [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "nacl_io/kernel_object.h"
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <algorithm>
#include <map>
#include <string>
#include <vector>
#include "nacl_io/kernel_handle.h"
#include "nacl_io/mount.h"
#include "nacl_io/mount_node.h"
#include "sdk_util/auto_lock.h"
#include "sdk_util/ref_object.h"
#include "sdk_util/scoped_ref.h"
namespace nacl_io {
KernelObject::KernelObject() {
cwd_ = "/";
}
KernelObject::~KernelObject() {};
Error KernelObject::AttachMountAtPath(const ScopedMount& mnt,
const std::string& path) {
std::string abs_path = GetAbsParts(path).Join();
AUTO_LOCK(mount_lock_);
if (mounts_.find(abs_path) != mounts_.end())
return EBUSY;
mounts_[abs_path] = mnt;
return 0;
}
Error KernelObject::DetachMountAtPath(const std::string& path) {
std::string abs_path = GetAbsParts(path).Join();
AUTO_LOCK(mount_lock_);
MountMap_t::iterator it = mounts_.find(abs_path);
if (mounts_.end() == it)
return EINVAL;
// It is only legal to unmount if there are no open references
if (it->second->RefCount() != 1)
return EBUSY;
mounts_.erase(it);
return 0;
}
// Uses longest prefix to find the mount for the give path, then
// acquires the mount and returns it with a relative path.
Error KernelObject::AcquireMountAndRelPath(const std::string& path,
ScopedMount* out_mount,
Path* rel_parts) {
Path abs_parts = GetAbsParts(path);
out_mount->reset(NULL);
*rel_parts = Path();
AUTO_LOCK(mount_lock_);
// Find longest prefix
size_t max = abs_parts.Size();
for (size_t len = 0; len < abs_parts.Size(); len++) {
MountMap_t::iterator it = mounts_.find(abs_parts.Range(0, max - len));
if (it != mounts_.end()) {
rel_parts->Set("/");
rel_parts->Append(abs_parts.Range(max - len, max));
*out_mount = it->second;
return 0;
}
}
return ENOTDIR;
}
// Given a path, acquire the associated mount and node, creating the
// node if needed based on the provided flags.
Error KernelObject::AcquireMountAndNode(const std::string& path,
int oflags,
ScopedMount* out_mount,
ScopedMountNode* out_node) {
Path rel_parts;
out_mount->reset(NULL);
out_node->reset(NULL);
Error error = AcquireMountAndRelPath(path, out_mount, &rel_parts);
if (error)
return error;
error = (*out_mount)->Open(rel_parts, oflags, out_node);
if (error)
return error;
return 0;
}
Path KernelObject::GetAbsParts(const std::string& path) {
AUTO_LOCK(cwd_lock_);
Path abs_parts(cwd_);
if (path[0] == '/') {
abs_parts = path;
} else {
abs_parts = cwd_;
abs_parts.Append(path);
}
return abs_parts;
}
std::string KernelObject::GetCWD() {
AUTO_LOCK(cwd_lock_);
std::string out = cwd_;
return out;
}
Error KernelObject::SetCWD(const std::string& path) {
std::string abs_path = GetAbsParts(path).Join();
ScopedMount mnt;
ScopedMountNode node;
Error error = AcquireMountAndNode(abs_path, O_RDONLY, &mnt, &node);
if (error)
return error;
if ((node->GetType() & S_IFDIR) == 0)
return ENOTDIR;
AUTO_LOCK(cwd_lock_);
cwd_ = abs_path;
return 0;
}
Error KernelObject::AcquireHandle(int fd, ScopedKernelHandle* out_handle) {
out_handle->reset(NULL);
AUTO_LOCK(handle_lock_);
if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
return EBADF;
*out_handle = handle_map_[fd];
if (out_handle) return 0;
return EBADF;
}
int KernelObject::AllocateFD(const ScopedKernelHandle& handle) {
AUTO_LOCK(handle_lock_);
int id;
// If we can recycle and FD, use that first
if (free_fds_.size()) {
id = free_fds_.front();
// 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 {
id = handle_map_.size();
handle_map_.push_back(handle);
}
return id;
}
void KernelObject::FreeAndReassignFD(int fd, const ScopedKernelHandle& handle) {
if (NULL == handle) {
FreeFD(fd);
} else {
AUTO_LOCK(handle_lock_);
// If the required FD is larger than the current set, grow the set
if (fd >= (int)handle_map_.size())
handle_map_.resize(fd + 1);
handle_map_[fd] = handle;
}
}
void KernelObject::FreeFD(int fd) {
AUTO_LOCK(handle_lock_);
handle_map_[fd].reset(NULL);
free_fds_.push_back(fd);
// Force lower numbered FD to be available first.
std::push_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>());
}
} // namespace nacl_io