Home | History | Annotate | Download | only in nacl_io
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "nacl_io/kernel_object.h"
      6 
      7 #include <assert.h>
      8 #include <errno.h>
      9 #include <fcntl.h>
     10 #include <pthread.h>
     11 
     12 #include <algorithm>
     13 #include <map>
     14 #include <string>
     15 #include <vector>
     16 
     17 #include "nacl_io/filesystem.h"
     18 #include "nacl_io/kernel_handle.h"
     19 #include "nacl_io/log.h"
     20 #include "nacl_io/node.h"
     21 
     22 #include "sdk_util/auto_lock.h"
     23 #include "sdk_util/ref_object.h"
     24 #include "sdk_util/scoped_ref.h"
     25 
     26 namespace nacl_io {
     27 
     28 KernelObject::KernelObject() {
     29   cwd_ = "/";
     30 }
     31 
     32 KernelObject::~KernelObject() {};
     33 
     34 Error KernelObject::AttachFsAtPath(const ScopedFilesystem& fs,
     35                                    const std::string& path) {
     36   std::string abs_path = GetAbsParts(path).Join();
     37 
     38   AUTO_LOCK(fs_lock_);
     39   if (filesystems_.find(abs_path) != filesystems_.end()) {
     40     LOG_ERROR("Can't mount at %s, it is already mounted.", path.c_str());
     41     return EBUSY;
     42   }
     43 
     44   filesystems_[abs_path] = fs;
     45   return 0;
     46 }
     47 
     48 Error KernelObject::DetachFsAtPath(const std::string& path,
     49                                    ScopedFilesystem* out_fs) {
     50   std::string abs_path = GetAbsParts(path).Join();
     51 
     52   AUTO_LOCK(fs_lock_);
     53   FsMap_t::iterator it = filesystems_.find(abs_path);
     54   if (filesystems_.end() == it) {
     55     LOG_TRACE("Can't unmount at %s, nothing is mounted.", path.c_str());
     56     return EINVAL;
     57   }
     58 
     59   // It is only legal to unmount if there are no open references
     60   if (it->second->RefCount() != 1) {
     61     LOG_TRACE("Can't unmount at %s, refcount is != 1.", path.c_str());
     62     return EBUSY;
     63   }
     64 
     65   *out_fs = it->second;
     66 
     67   filesystems_.erase(it);
     68   return 0;
     69 }
     70 
     71 // Uses longest prefix to find the filesystem for the give path, then
     72 // acquires the filesystem and returns it with a relative path.
     73 Error KernelObject::AcquireFsAndRelPath(const std::string& path,
     74                                         ScopedFilesystem* out_fs,
     75                                         Path* rel_parts) {
     76   Path abs_parts = GetAbsParts(path);
     77 
     78   out_fs->reset(NULL);
     79   *rel_parts = Path();
     80 
     81   AUTO_LOCK(fs_lock_);
     82 
     83   // Find longest prefix
     84   size_t max = abs_parts.Size();
     85   for (size_t len = 0; len < abs_parts.Size(); len++) {
     86     FsMap_t::iterator it = filesystems_.find(abs_parts.Range(0, max - len));
     87     if (it != filesystems_.end()) {
     88       rel_parts->Set("/");
     89       rel_parts->Append(abs_parts.Range(max - len, max));
     90 
     91       *out_fs = it->second;
     92       return 0;
     93     }
     94   }
     95 
     96   return ENOTDIR;
     97 }
     98 
     99 // Given a path, acquire the associated filesystem and node, creating the
    100 // node if needed based on the provided flags.
    101 Error KernelObject::AcquireFsAndNode(const std::string& path,
    102                                      int oflags, mode_t mflags,
    103                                      ScopedFilesystem* out_fs,
    104                                      ScopedNode* out_node) {
    105   Path rel_parts;
    106   out_fs->reset(NULL);
    107   out_node->reset(NULL);
    108   Error error = AcquireFsAndRelPath(path, out_fs, &rel_parts);
    109   if (error)
    110     return error;
    111 
    112   error = (*out_fs)->OpenWithMode(rel_parts, oflags, mflags, out_node);
    113   if (error)
    114     return error;
    115 
    116   return 0;
    117 }
    118 
    119 Path KernelObject::GetAbsParts(const std::string& path) {
    120   AUTO_LOCK(cwd_lock_);
    121 
    122   Path abs_parts;
    123   if (path[0] == '/') {
    124     abs_parts = path;
    125   } else {
    126     abs_parts = cwd_;
    127     abs_parts.Append(path);
    128   }
    129 
    130   return abs_parts;
    131 }
    132 
    133 std::string KernelObject::GetCWD() {
    134   AUTO_LOCK(cwd_lock_);
    135   std::string out = cwd_;
    136 
    137   return out;
    138 }
    139 
    140 Error KernelObject::SetCWD(const std::string& path) {
    141   std::string abs_path = GetAbsParts(path).Join();
    142 
    143   ScopedFilesystem fs;
    144   ScopedNode node;
    145 
    146   Error error = AcquireFsAndNode(abs_path, O_RDONLY, 0, &fs, &node);
    147   if (error)
    148     return error;
    149 
    150   if ((node->GetType() & S_IFDIR) == 0)
    151     return ENOTDIR;
    152 
    153   AUTO_LOCK(cwd_lock_);
    154   cwd_ = abs_path;
    155   return 0;
    156 }
    157 
    158 Error KernelObject::GetFDFlags(int fd, int* out_flags) {
    159   AUTO_LOCK(handle_lock_);
    160   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
    161     return EBADF;
    162 
    163   *out_flags = handle_map_[fd].flags;
    164   return 0;
    165 }
    166 
    167 Error KernelObject::SetFDFlags(int fd, int flags) {
    168   AUTO_LOCK(handle_lock_);
    169   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
    170     return EBADF;
    171 
    172   // Only setting of FD_CLOEXEC is supported.
    173   if (flags & ~FD_CLOEXEC)
    174     return EINVAL;
    175 
    176   handle_map_[fd].flags = flags;
    177   return 0;
    178 }
    179 
    180 Error KernelObject::AcquireHandle(int fd, ScopedKernelHandle* out_handle) {
    181   out_handle->reset(NULL);
    182 
    183   AUTO_LOCK(handle_lock_);
    184   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
    185     return EBADF;
    186 
    187   Descriptor_t& desc = handle_map_[fd];
    188   if (!desc.handle)
    189     return EBADF;
    190 
    191   *out_handle = desc.handle;
    192   return 0;
    193 }
    194 
    195 Error KernelObject::AcquireHandleAndPath(int fd,
    196                                          ScopedKernelHandle* out_handle,
    197                                          std::string* out_path) {
    198   out_handle->reset(NULL);
    199 
    200   AUTO_LOCK(handle_lock_);
    201   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
    202     return EBADF;
    203 
    204   Descriptor_t& desc = handle_map_[fd];
    205   if (!desc.handle)
    206     return EBADF;
    207 
    208   *out_handle = desc.handle;
    209   *out_path = desc.path;
    210   return 0;
    211 }
    212 
    213 int KernelObject::AllocateFD(const ScopedKernelHandle& handle,
    214                              const std::string& path) {
    215   AUTO_LOCK(handle_lock_);
    216   int id;
    217 
    218   std::string abs_path = GetAbsParts(path).Join();
    219   Descriptor_t descriptor(handle, abs_path);
    220 
    221   // If we can recycle and FD, use that first
    222   if (free_fds_.size()) {
    223     id = free_fds_.front();
    224     // Force lower numbered FD to be available first.
    225     std::pop_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>());
    226     free_fds_.pop_back();
    227     handle_map_[id] = descriptor;
    228   } else {
    229     id = handle_map_.size();
    230     handle_map_.push_back(descriptor);
    231   }
    232 
    233   return id;
    234 }
    235 
    236 void KernelObject::FreeAndReassignFD(int fd,
    237                                      const ScopedKernelHandle& handle,
    238                                      const std::string& path) {
    239   if (NULL == handle) {
    240     FreeFD(fd);
    241   } else {
    242     AUTO_LOCK(handle_lock_);
    243 
    244     // If the required FD is larger than the current set, grow the set
    245     if (fd >= (int)handle_map_.size())
    246       handle_map_.resize(fd + 1);
    247 
    248     // This path will be from an existing handle, and absolute.
    249     handle_map_[fd] = Descriptor_t(handle, path);
    250   }
    251 }
    252 
    253 void KernelObject::FreeFD(int fd) {
    254   AUTO_LOCK(handle_lock_);
    255 
    256   handle_map_[fd].handle.reset(NULL);
    257   free_fds_.push_back(fd);
    258 
    259   // Force lower numbered FD to be available first.
    260   std::push_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>());
    261 }
    262 
    263 }  // namespace nacl_io
    264