Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / native_client_sdk / src / libraries / nacl_io / memfs / mem_fs.cc
1 // Copyright 2013 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/memfs/mem_fs.h"
6
7 #include <errno.h>
8 #include <fcntl.h>
9
10 #include <string>
11
12 #include "nacl_io/dir_node.h"
13 #include "nacl_io/filesystem.h"
14 #include "nacl_io/memfs/mem_fs_node.h"
15 #include "nacl_io/node.h"
16 #include "nacl_io/osstat.h"
17 #include "nacl_io/osunistd.h"
18 #include "nacl_io/path.h"
19 #include "sdk_util/auto_lock.h"
20 #include "sdk_util/ref_object.h"
21
22 namespace nacl_io {
23
24 MemFs::MemFs() : root_(NULL) {
25 }
26
27 Error MemFs::Init(const FsInitArgs& args) {
28   Error error = Filesystem::Init(args);
29   if (error)
30     return error;
31
32   root_.reset(new DirNode(this));
33   error = root_->Init(0);
34   if (error) {
35     root_.reset(NULL);
36     return error;
37   }
38   return 0;
39 }
40
41 Error MemFs::FindNode(const Path& path, int type, ScopedNode* out_node) {
42   out_node->reset(NULL);
43   ScopedNode node = root_;
44
45   // If there is no root there, we have an error.
46   if (node == NULL)
47     return ENOTDIR;
48
49   // We are expecting an "absolute" path from this mount point.
50   if (!path.IsAbsolute())
51     return EINVAL;
52
53   // Starting at the root, traverse the path parts.
54   for (size_t index = 1; node && index < path.Size(); index++) {
55     // If not a directory, then we have an error so return.
56     if (!node->IsaDir())
57       return ENOTDIR;
58
59     // Find the child node
60     Error error = node->FindChild(path.Part(index), &node);
61     if (error)
62       return error;
63   }
64
65   // If a directory is expected, but it's not a directory, then fail.
66   if ((type & S_IFDIR) && !node->IsaDir())
67     return ENOTDIR;
68
69   // If a file is expected, but it's not a file, then fail.
70   if ((type & S_IFREG) && node->IsaDir())
71     return EISDIR;
72
73   // We now have a valid object of the expected type, so return it.
74   *out_node = node;
75   return 0;
76 }
77
78 Error MemFs::OpenWithMode(const Path& path, int open_flags, mode_t mode,
79                           ScopedNode* out_node) {
80   out_node->reset(NULL);
81   ScopedNode node;
82
83   Error error = FindNode(path, 0, &node);
84   if (error) {
85     // If the node does not exist and we can't create it, fail
86     if ((open_flags & O_CREAT) == 0)
87       return ENOENT;
88
89     // Now first find the parent directory to see if we can add it
90     ScopedNode parent;
91     error = FindNode(path.Parent(), S_IFDIR, &parent);
92     if (error)
93       return error;
94
95     node.reset(new MemFsNode(this));
96     error = node->Init(open_flags);
97     if (error)
98       return error;
99     node->SetMode(mode);
100
101     error = parent->AddChild(path.Basename(), node);
102     if (error)
103       return error;
104
105   } else {
106     // Opening an existing file.
107
108     // Directories can only be opened read-only.
109     if (node->IsaDir() && (open_flags & 3) != O_RDONLY)
110       return EISDIR;
111
112     // If we were expected to create it exclusively, fail
113     if (open_flags & O_EXCL)
114       return EEXIST;
115
116     if (open_flags & O_TRUNC)
117       node->FTruncate(0);
118   }
119
120   *out_node = node;
121   return 0;
122 }
123
124 Error MemFs::Mkdir(const Path& path, int mode) {
125   // We expect a Filesystem "absolute" path
126   if (!path.IsAbsolute())
127     return ENOENT;
128
129   // The root of the filesystem is already created by the filesystem
130   if (path.Size() == 1)
131     return EEXIST;
132
133   ScopedNode parent;
134   int error = FindNode(path.Parent(), S_IFDIR, &parent);
135   if (error)
136     return error;
137
138   ScopedNode node;
139   error = parent->FindChild(path.Basename(), &node);
140   if (!error)
141     return EEXIST;
142
143   if (error != ENOENT)
144     return error;
145
146   // Allocate a node, with a RefCount of 1.  If added to the parent
147   // it will get ref counted again.  In either case, release the
148   // recount we have on exit.
149   node.reset(new DirNode(this));
150   error = node->Init(0);
151   if (error)
152     return error;
153
154   return parent->AddChild(path.Basename(), node);
155 }
156
157 Error MemFs::Unlink(const Path& path) {
158   return RemoveInternal(path, REMOVE_FILE);
159 }
160
161 Error MemFs::Rmdir(const Path& path) {
162   return RemoveInternal(path, REMOVE_DIR);
163 }
164
165 Error MemFs::Remove(const Path& path) {
166   return RemoveInternal(path, REMOVE_ALL);
167 }
168
169 Error MemFs::Rename(const Path& src_path, const Path& target_path) {
170   ScopedNode src_node;
171   ScopedNode src_parent;
172   ScopedNode target_node;
173   ScopedNode target_parent;
174   int error = FindNode(src_path, 0, &src_node);
175   if (error)
176     return error;
177
178   // The source must exist
179   error = FindNode(src_path.Parent(), S_IFDIR, &src_parent);
180   if (error)
181     return error;
182
183   // The parent of the target must exist
184   error = FindNode(target_path.Parent(), 0, &target_parent);
185   if (error)
186     return error;
187
188   std::string target_name = target_path.Basename();
189
190   // The target itself need not exist but if it does there are
191   // certain restrictions
192   error = FindNode(target_path, 0, &target_node);
193   bool replacing_target = error == 0;
194   if (replacing_target) {
195     if (target_node->IsaDir()) {
196       // If the target is a direcotry it must be empty
197       if (target_node->ChildCount()) {
198         return ENOTEMPTY;
199       }
200
201       if (src_node->IsaDir()) {
202         // Replacing an existing directory.
203         RemoveInternal(target_path, REMOVE_ALL);
204       } else {
205         // Renaming into an existing directory.
206         target_name = src_path.Basename();
207         target_parent = target_node;
208       }
209     } else {
210       if (src_node->IsaDir())
211         // Can't replace a file with a direcotory
212         return EISDIR;
213
214       // Replacing an existing file.
215       target_parent->RemoveChild(target_path.Basename());
216     }
217   }
218
219   // Perform that actual rename. Simply re-parent the original source node
220   // onto its new parent node.
221   error = src_parent->RemoveChild(src_path.Basename());
222   if (error)
223     return error;
224
225   error = target_parent->AddChild(target_name, src_node);
226   if (error) {
227     // Re-parent the old target_node if we failed to add the new one.
228     if (replacing_target)
229       target_parent->AddChild(target_path.Basename(), target_node);
230     // Re-parent the src_node
231     target_parent->AddChild(target_path.Basename(), src_node);
232     return error;
233   }
234
235   return 0;
236 }
237
238 Error MemFs::RemoveInternal(const Path& path, int remove_type) {
239   bool dir_only = remove_type == REMOVE_DIR;
240   bool file_only = remove_type == REMOVE_FILE;
241   bool remove_dir = (remove_type & REMOVE_DIR) != 0;
242
243   if (dir_only) {
244     // We expect a Filesystem "absolute" path
245     if (!path.IsAbsolute())
246       return ENOENT;
247
248     // The root of the filesystem is already created by the filesystem
249     if (path.Size() == 1)
250       return EEXIST;
251   }
252
253   ScopedNode parent;
254   int error = FindNode(path.Parent(), S_IFDIR, &parent);
255   if (error)
256     return error;
257
258   // Verify we find a child which is a directory.
259   ScopedNode child;
260   error = parent->FindChild(path.Basename(), &child);
261   if (error)
262     return error;
263
264   if (dir_only && !child->IsaDir())
265     return ENOTDIR;
266
267   if (file_only && child->IsaDir())
268     return EISDIR;
269
270   if (remove_dir && child->ChildCount() > 0)
271     return ENOTEMPTY;
272
273   return parent->RemoveChild(path.Basename());
274 }
275
276 }  // namespace nacl_io