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