Update To 11.40.268.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() : umask_(0) {
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, 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
119 Path 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
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, 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
158 mode_t KernelObject::GetUmask() {
159   return umask_;
160 }
161
162 mode_t KernelObject::SetUmask(mode_t newmask) {
163   AUTO_LOCK(umask_lock_);
164   mode_t oldmask = umask_;
165   umask_ = newmask & S_MODEBITS;
166   return oldmask;
167 }
168
169 Error KernelObject::GetFDFlags(int fd, int* out_flags) {
170   AUTO_LOCK(handle_lock_);
171   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
172     return EBADF;
173
174   *out_flags = handle_map_[fd].flags;
175   return 0;
176 }
177
178 Error KernelObject::SetFDFlags(int fd, int flags) {
179   AUTO_LOCK(handle_lock_);
180   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
181     return EBADF;
182
183   // Only setting of FD_CLOEXEC is supported.
184   if (flags & ~FD_CLOEXEC)
185     return EINVAL;
186
187   handle_map_[fd].flags = flags;
188   return 0;
189 }
190
191 Error KernelObject::AcquireHandle(int fd, ScopedKernelHandle* out_handle) {
192   out_handle->reset(NULL);
193
194   AUTO_LOCK(handle_lock_);
195   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
196     return EBADF;
197
198   Descriptor_t& desc = handle_map_[fd];
199   if (!desc.handle)
200     return EBADF;
201
202   *out_handle = desc.handle;
203   return 0;
204 }
205
206 Error KernelObject::AcquireHandleAndPath(int fd,
207                                          ScopedKernelHandle* out_handle,
208                                          std::string* out_path) {
209   out_handle->reset(NULL);
210
211   AUTO_LOCK(handle_lock_);
212   if (fd < 0 || fd >= static_cast<int>(handle_map_.size()))
213     return EBADF;
214
215   Descriptor_t& desc = handle_map_[fd];
216   if (!desc.handle)
217     return EBADF;
218
219   *out_handle = desc.handle;
220   *out_path = desc.path;
221   return 0;
222 }
223
224 int KernelObject::AllocateFD(const ScopedKernelHandle& handle,
225                              const std::string& path) {
226   AUTO_LOCK(handle_lock_);
227   int id;
228
229   std::string abs_path = GetAbsParts(path).Join();
230   Descriptor_t descriptor(handle, abs_path);
231
232   // If we can recycle and FD, use that first
233   if (free_fds_.size()) {
234     id = free_fds_.front();
235     // Force lower numbered FD to be available first.
236     std::pop_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>());
237     free_fds_.pop_back();
238     handle_map_[id] = descriptor;
239   } else {
240     id = handle_map_.size();
241     handle_map_.push_back(descriptor);
242   }
243
244   return id;
245 }
246
247 void KernelObject::FreeAndReassignFD(int fd,
248                                      const ScopedKernelHandle& handle,
249                                      const std::string& path) {
250   if (NULL == handle) {
251     FreeFD(fd);
252   } else {
253     AUTO_LOCK(handle_lock_);
254
255     // If the required FD is larger than the current set, grow the set
256     if (fd >= (int)handle_map_.size())
257       handle_map_.resize(fd + 1);
258
259     // This path will be from an existing handle, and absolute.
260     handle_map_[fd] = Descriptor_t(handle, path);
261   }
262 }
263
264 void KernelObject::FreeFD(int fd) {
265   AUTO_LOCK(handle_lock_);
266
267   handle_map_[fd].handle.reset(NULL);
268   free_fds_.push_back(fd);
269
270   // Force lower numbered FD to be available first.
271   std::push_heap(free_fds_.begin(), free_fds_.end(), std::greater<int>());
272 }
273
274 }  // namespace nacl_io