Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / libraries / nacl_io / kernel_object.cc
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
28 KernelObject::KernelObject() {
29   cwd_ = "/";
30 }
31
32 KernelObject::~KernelObject() {};
33
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
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.
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(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.
101 Error KernelObject::AcquireFsAndNode(const std::string& path,
102                                      int oflags,
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)->Open(rel_parts, oflags, out_node);
113   if (error)
114     return error;
115
116   return 0;
117 }
118
119 Path KernelObject::GetAbsParts(const std::string& path) {
120   AUTO_LOCK(cwd_lock_);
121
122   Path abs_parts(cwd_);
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
133 std::string KernelObject::GetCWD() {
134   AUTO_LOCK(cwd_lock_);
135   std::string out = cwd_;
136
137   return out;
138 }
139
140 Error 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, &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
158 Error 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
167 Error 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
180 Error 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
195 Error 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
213 int 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
236 void 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
253 void 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