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 
26 namespace nacl_io {
27 
KernelObject()28 KernelObject::KernelObject() : umask_(0) {
29   cwd_ = "/";
30 }
31 
~KernelObject()32 KernelObject::~KernelObject() {};
33 
AttachFsAtPath(const ScopedFilesystem & fs,const std::string & path)34 Error 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 
DetachFsAtPath(const std::string & path,ScopedFilesystem * out_fs)48 Error 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.
AcquireFsAndRelPath(const std::string & path,ScopedFilesystem * out_fs,Path * rel_parts)73 Error 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(Path(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.
AcquireFsAndNode(const std::string & path,int oflags,mode_t mflags,ScopedFilesystem * out_fs,ScopedNode * out_node)101 Error 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 
GetAbsParts(const std::string & path)119 Path KernelObject::GetAbsParts(const std::string& path) {
120   AUTO_LOCK(cwd_lock_);
121   Path abs_parts(cwd_);
122   return abs_parts.Append(Path(path));
123 }
124 
GetCWD()125 std::string KernelObject::GetCWD() {
126   AUTO_LOCK(cwd_lock_);
127   std::string out = cwd_;
128   return out;
129 }
130 
SetCWD(const std::string & path)131 Error KernelObject::SetCWD(const std::string& path) {
132   std::string abs_path = GetAbsParts(path).Join();
133 
134   ScopedFilesystem fs;
135   ScopedNode node;
136 
137   Error error = AcquireFsAndNode(abs_path, O_RDONLY, 0, &fs, &node);
138   if (error)
139     return error;
140 
141   if ((node->GetType() & S_IFDIR) == 0)
142     return ENOTDIR;
143 
144   AUTO_LOCK(cwd_lock_);
145   cwd_ = abs_path;
146   return 0;
147 }
148 
GetUmask()149 mode_t KernelObject::GetUmask() {
150   return umask_;
151 }
152 
SetUmask(mode_t newmask)153 mode_t KernelObject::SetUmask(mode_t newmask) {
154   AUTO_LOCK(umask_lock_);
155   mode_t oldmask = umask_;
156   umask_ = newmask & S_MODEBITS;
157   return oldmask;
158 }
159 
GetFDFlags(int fd,int * out_flags)160 Error KernelObject::GetFDFlags(int fd, int* out_flags) {
161   AUTO_LOCK(handle_lock_);
162   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
163     return EBADF;
164 
165   *out_flags = handle_map_[fd].flags;
166   return 0;
167 }
168 
SetFDFlags(int fd,int flags)169 Error KernelObject::SetFDFlags(int fd, int flags) {
170   AUTO_LOCK(handle_lock_);
171   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
172     return EBADF;
173 
174   // Only setting of FD_CLOEXEC is supported.
175   if (flags & ~FD_CLOEXEC)
176     return EINVAL;
177 
178   handle_map_[fd].flags = flags;
179   return 0;
180 }
181 
AcquireHandle(int fd,ScopedKernelHandle * out_handle)182 Error KernelObject::AcquireHandle(int fd, ScopedKernelHandle* out_handle) {
183   out_handle->reset(NULL);
184 
185   AUTO_LOCK(handle_lock_);
186   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
187     return EBADF;
188 
189   Descriptor_t& desc = handle_map_[fd];
190   if (!desc.handle)
191     return EBADF;
192 
193   *out_handle = desc.handle;
194   return 0;
195 }
196 
AcquireHandleAndPath(int fd,ScopedKernelHandle * out_handle,std::string * out_path)197 Error KernelObject::AcquireHandleAndPath(int fd,
198                                          ScopedKernelHandle* out_handle,
199                                          std::string* out_path) {
200   out_handle->reset(NULL);
201 
202   AUTO_LOCK(handle_lock_);
203   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
204     return EBADF;
205 
206   Descriptor_t& desc = handle_map_[fd];
207   if (!desc.handle)
208     return EBADF;
209 
210   *out_handle = desc.handle;
211   *out_path = desc.path;
212   return 0;
213 }
214 
AllocateFD(const ScopedKernelHandle & handle,const std::string & path)215 int KernelObject::AllocateFD(const ScopedKernelHandle& handle,
216                              const std::string& path) {
217   AUTO_LOCK(handle_lock_);
218   int id;
219 
220   std::string abs_path = GetAbsParts(path).Join();
221   Descriptor_t descriptor(handle, abs_path);
222 
223   // If we can recycle an FD, use that first
224   if (free_fds_.size()) {
225     // Force lower numbered FD to be available first.
226     // std::set is ordered, so begin() returns the lowest value.
227     id = *free_fds_.begin();
228     free_fds_.erase(id);
229     handle_map_[id] = descriptor;
230   } else {
231     id = handle_map_.size();
232     handle_map_.push_back(descriptor);
233   }
234 
235   return id;
236 }
237 
FreeAndReassignFD(int fd,const ScopedKernelHandle & handle,const std::string & path)238 void KernelObject::FreeAndReassignFD(int fd,
239                                      const ScopedKernelHandle& handle,
240                                      const std::string& path) {
241   AUTO_LOCK(handle_lock_);
242 
243   // If the required FD is larger than the current set, grow the set.
244   int sz = static_cast<int>(handle_map_.size());
245   if (fd >= sz) {
246     // Expand the handle map to include all the extra descriptors
247     // up to and including fd.
248     handle_map_.resize(fd + 1);
249     // Add all the new descriptors, except fd, to the free list.
250     for (; sz < fd; ++sz) {
251       free_fds_.insert(sz);
252     }
253   }
254 
255   assert(handle != NULL);
256 
257   free_fds_.erase(fd);
258 
259   // This path will be from an existing handle, and absolute.
260   handle_map_[fd] = Descriptor_t(handle, path);
261 }
262 
FreeFD(int fd)263 void KernelObject::FreeFD(int fd) {
264   AUTO_LOCK(handle_lock_);
265 
266   handle_map_[fd].handle.reset(NULL);
267   free_fds_.insert(fd);
268 }
269 
270 }  // namespace nacl_io
271