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