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