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
22namespace nacl_io {
23
24MemFs::MemFs() : root_(NULL) {
25}
26
27Error 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
41Error 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
78Error 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
124Error 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
157Error MemFs::Unlink(const Path& path) {
158  return RemoveInternal(path, REMOVE_FILE);
159}
160
161Error MemFs::Rmdir(const Path& path) {
162  return RemoveInternal(path, REMOVE_DIR);
163}
164
165Error MemFs::Remove(const Path& path) {
166  return RemoveInternal(path, REMOVE_ALL);
167}
168
169Error 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
238Error 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
277