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