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