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