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/mount_mem.h"
      6 
      7 #include <errno.h>
      8 #include <fcntl.h>
      9 
     10 #include <string>
     11 
     12 #include "nacl_io/mount.h"
     13 #include "nacl_io/mount_node.h"
     14 #include "nacl_io/mount_node_dir.h"
     15 #include "nacl_io/mount_node_mem.h"
     16 #include "nacl_io/osstat.h"
     17 #include "nacl_io/osunistd.h"
     18 #include "nacl_io/path.h"
     19 #include "sdk_util/auto_lock.h"
     20 #include "sdk_util/ref_object.h"
     21 
     22 namespace nacl_io {
     23 
     24 MountMem::MountMem() : root_(NULL) {}
     25 
     26 Error MountMem::Init(const MountInitArgs& args) {
     27   Error error = Mount::Init(args);
     28   if (error)
     29     return error;
     30 
     31   root_.reset(new MountNodeDir(this));
     32   error = root_->Init(0);
     33   if (error) {
     34     root_.reset(NULL);
     35     return error;
     36   }
     37   return 0;
     38 }
     39 
     40 Error MountMem::FindNode(const Path& path,
     41                          int type,
     42                          ScopedMountNode* out_node) {
     43   out_node->reset(NULL);
     44   ScopedMountNode node = root_;
     45 
     46   // If there is no root there, we have an error.
     47   if (node == NULL)
     48     return ENOTDIR;
     49 
     50   // We are expecting an "absolute" path from this mount point.
     51   if (!path.IsAbsolute())
     52     return EINVAL;
     53 
     54   // Starting at the root, traverse the path parts.
     55   for (size_t index = 1; node && index < path.Size(); index++) {
     56     // If not a directory, then we have an error so return.
     57     if (!node->IsaDir())
     58       return ENOTDIR;
     59 
     60     // Find the child node
     61     Error error = node->FindChild(path.Part(index), &node);
     62     if (error)
     63       return error;
     64   }
     65 
     66   // If a directory is expected, but it's not a directory, then fail.
     67   if ((type & S_IFDIR) && !node->IsaDir())
     68     return ENOTDIR;
     69 
     70   // If a file is expected, but it's not a file, then fail.
     71   if ((type & S_IFREG) && node->IsaDir())
     72     return EISDIR;
     73 
     74   // We now have a valid object of the expected type, so return it.
     75   *out_node = node;
     76   return 0;
     77 }
     78 
     79 Error MountMem::Access(const Path& path, int a_mode) {
     80   ScopedMountNode node;
     81   Error error = FindNode(path, 0, &node);
     82 
     83   if (error)
     84     return error;
     85 
     86   int obj_mode = node->GetMode();
     87   if (((a_mode & R_OK) && !(obj_mode & S_IREAD)) ||
     88       ((a_mode & W_OK) && !(obj_mode & S_IWRITE)) ||
     89       ((a_mode & X_OK) && !(obj_mode & S_IEXEC))) {
     90     return EACCES;
     91   }
     92 
     93   return 0;
     94 }
     95 
     96 Error MountMem::Open(const Path& path, int open_flags,
     97                      ScopedMountNode* out_node) {
     98   out_node->reset(NULL);
     99   ScopedMountNode node;
    100 
    101   Error error = FindNode(path, 0, &node);
    102   if (error) {
    103     // If the node does not exist and we can't create it, fail
    104     if ((open_flags & O_CREAT) == 0)
    105       return ENOENT;
    106 
    107     // Now first find the parent directory to see if we can add it
    108     ScopedMountNode parent;
    109     error = FindNode(path.Parent(), S_IFDIR, &parent);
    110     if (error)
    111       return error;
    112 
    113     node.reset(new MountNodeMem(this));
    114     error = node->Init(open_flags);
    115     if (error)
    116       return error;
    117 
    118     error = parent->AddChild(path.Basename(), node);
    119     if (error)
    120       return error;
    121 
    122   } else {
    123     // Opening an existing file.
    124 
    125     // Directories can only be opened read-only.
    126     if (node->IsaDir() && (open_flags & 3) != O_RDONLY)
    127       return EISDIR;
    128 
    129     // If we were expected to create it exclusively, fail
    130     if (open_flags & O_EXCL)
    131       return EEXIST;
    132 
    133     if (open_flags & O_TRUNC)
    134       static_cast<MountNodeMem*>(node.get())->Resize(0);
    135   }
    136 
    137 
    138   *out_node = node;
    139   return 0;
    140 }
    141 
    142 Error MountMem::Mkdir(const Path& path, int mode) {
    143   // We expect a Mount "absolute" path
    144   if (!path.IsAbsolute())
    145     return ENOENT;
    146 
    147   // The root of the mount is already created by the mount
    148   if (path.Size() == 1)
    149     return EEXIST;
    150 
    151   ScopedMountNode parent;
    152   int error = FindNode(path.Parent(), S_IFDIR, &parent);
    153   if (error)
    154     return error;
    155 
    156   ScopedMountNode node;
    157   error = parent->FindChild(path.Basename(), &node);
    158   if (!error)
    159     return EEXIST;
    160 
    161   if (error != ENOENT)
    162     return error;
    163 
    164   // Allocate a node, with a RefCount of 1.  If added to the parent
    165   // it will get ref counted again.  In either case, release the
    166   // recount we have on exit.
    167   node.reset(new MountNodeDir(this));
    168   error = node->Init(0);
    169   if (error)
    170     return error;
    171 
    172   return parent->AddChild(path.Basename(), node);
    173 }
    174 
    175 Error MountMem::Unlink(const Path& path) {
    176   return RemoveInternal(path, REMOVE_FILE);
    177 }
    178 
    179 Error MountMem::Rmdir(const Path& path) {
    180   return RemoveInternal(path, REMOVE_DIR);
    181 }
    182 
    183 Error MountMem::Remove(const Path& path) {
    184   return RemoveInternal(path, REMOVE_ALL);
    185 }
    186 
    187 Error MountMem::Rename(const Path& src_path, const Path& target_path) {
    188   ScopedMountNode src_node;
    189   ScopedMountNode src_parent;
    190   ScopedMountNode target_node;
    191   ScopedMountNode target_parent;
    192   int error = FindNode(src_path, 0, &src_node);
    193   if (error)
    194     return error;
    195 
    196   // The source must exist
    197   error = FindNode(src_path.Parent(), S_IFDIR, &src_parent);
    198   if (error)
    199     return error;
    200 
    201   // The parent of the target must exist
    202   error = FindNode(target_path.Parent(), 0, &target_parent);
    203   if (error)
    204     return error;
    205 
    206   std::string target_name = target_path.Basename();
    207 
    208   // The target itself need not exist but if it does there are
    209   // certain restrictions
    210   error = FindNode(target_path, 0, &target_node);
    211   bool replacing_target = error == 0;
    212   if (replacing_target) {
    213     if (target_node->IsaDir()) {
    214       // If the target is a direcotry it must be empty
    215       if (target_node->ChildCount()) {
    216         return ENOTEMPTY;
    217       }
    218 
    219       if (src_node->IsaDir()) {
    220         // Replacing an existing directory.
    221         RemoveInternal(target_path, REMOVE_ALL);
    222       } else {
    223         // Renaming into an existing directory.
    224         target_name = src_path.Basename();
    225         target_parent = target_node;
    226       }
    227     } else {
    228       if (src_node->IsaDir())
    229         // Can't replace a file with a direcotory
    230         return EISDIR;
    231 
    232       // Replacing an existing file.
    233       target_parent->RemoveChild(target_path.Basename());
    234     }
    235   }
    236 
    237   // Perform that actual rename. Simply re-parent the original source node
    238   // onto its new parent node.
    239   error = src_parent->RemoveChild(src_path.Basename());
    240   if (error)
    241     return error;
    242 
    243   error = target_parent->AddChild(target_name, src_node);
    244   if (error) {
    245     // Re-parent the old target_node if we failed to add the new one.
    246     if (replacing_target)
    247       target_parent->AddChild(target_path.Basename(), target_node);
    248     // Re-parent the src_node
    249     target_parent->AddChild(target_path.Basename(), src_node);
    250     return error;
    251   }
    252 
    253   return 0;
    254 }
    255 
    256 Error MountMem::RemoveInternal(const Path& path, int remove_type) {
    257   bool dir_only = remove_type == REMOVE_DIR;
    258   bool file_only = remove_type == REMOVE_FILE;
    259   bool remove_dir = (remove_type & REMOVE_DIR) != 0;
    260 
    261   if (dir_only) {
    262     // We expect a Mount "absolute" path
    263     if (!path.IsAbsolute())
    264       return ENOENT;
    265 
    266     // The root of the mount is already created by the mount
    267     if (path.Size() == 1)
    268       return EEXIST;
    269   }
    270 
    271   ScopedMountNode parent;
    272   int error = FindNode(path.Parent(), S_IFDIR, &parent);
    273   if (error)
    274     return error;
    275 
    276   // Verify we find a child which is a directory.
    277   ScopedMountNode child;
    278   error = parent->FindChild(path.Basename(), &child);
    279   if (error)
    280     return error;
    281 
    282   if (dir_only && !child->IsaDir())
    283     return ENOTDIR;
    284 
    285   if (file_only && child->IsaDir())
    286     return EISDIR;
    287 
    288   if (remove_dir && child->ChildCount() > 0)
    289     return ENOTEMPTY;
    290 
    291   return parent->RemoveChild(path.Basename());
    292 }
    293 
    294 }  // namespace nacl_io
    295