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