blob: 70da25ce58fa96593570ca2e0f9f22c82df7f70a [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "nacl_io/memfs/mem_fs.h"
#include <errno.h>
#include <fcntl.h>
#include <string>
#include "nacl_io/dir_node.h"
#include "nacl_io/filesystem.h"
#include "nacl_io/memfs/mem_fs_node.h"
#include "nacl_io/node.h"
#include "nacl_io/osstat.h"
#include "nacl_io/osunistd.h"
#include "nacl_io/path.h"
#include "sdk_util/auto_lock.h"
#include "sdk_util/ref_object.h"
namespace nacl_io {
MemFs::MemFs() : root_(NULL) {
}
Error MemFs::Init(const FsInitArgs& args) {
Error error = Filesystem::Init(args);
if (error)
return error;
root_.reset(new DirNode(this, S_IRALL | S_IWALL | S_IXALL));
error = root_->Init(0);
if (error) {
root_.reset(NULL);
return error;
}
return 0;
}
Error MemFs::FindNode(const Path& path, int type, ScopedNode* out_node) {
out_node->reset(NULL);
ScopedNode node = root_;
// If there is no root there, we have an error.
if (node == NULL)
return ENOTDIR;
// We are expecting an "absolute" path from this mount point.
if (!path.IsAbsolute())
return EINVAL;
// Starting at the root, traverse the path parts.
for (size_t index = 1; node && index < path.Size(); index++) {
// If not a directory, then we have an error so return.
if (!node->IsaDir())
return ENOTDIR;
// Find the child node
Error error = node->FindChild(path.Part(index), &node);
if (error)
return error;
}
// If a directory is expected, but it's not a directory, then fail.
if ((type & S_IFDIR) && !node->IsaDir())
return ENOTDIR;
// If a file is expected, but it's not a file, then fail.
if ((type & S_IFREG) && node->IsaDir())
return EISDIR;
// We now have a valid object of the expected type, so return it.
*out_node = node;
return 0;
}
Error MemFs::OpenWithMode(const Path& path, int open_flags, mode_t mode,
ScopedNode* out_node) {
out_node->reset(NULL);
ScopedNode node;
Error error = FindNode(path, 0, &node);
if (error) {
// If the node does not exist and we can't create it, fail
if ((open_flags & O_CREAT) == 0)
return ENOENT;
// Now first find the parent directory to see if we can add it
ScopedNode parent;
error = FindNode(path.Parent(), S_IFDIR, &parent);
if (error)
return error;
node.reset(new MemFsNode(this));
error = node->Init(open_flags);
if (error)
return error;
node->SetMode(mode);
error = parent->AddChild(path.Basename(), node);
if (error)
return error;
} else {
// Opening an existing file.
// If we were expected to create it exclusively, fail
if (open_flags & O_EXCL)
return EEXIST;
if (open_flags & O_TRUNC)
node->FTruncate(0);
}
*out_node = node;
return 0;
}
Error MemFs::Mkdir(const Path& path, int mode) {
// We expect a Filesystem "absolute" path
if (!path.IsAbsolute())
return ENOENT;
// The root of the filesystem is already created by the filesystem
if (path.Size() == 1)
return EEXIST;
ScopedNode parent;
int error = FindNode(path.Parent(), S_IFDIR, &parent);
if (error)
return error;
ScopedNode node;
error = parent->FindChild(path.Basename(), &node);
if (!error)
return EEXIST;
if (error != ENOENT)
return error;
// Allocate a node, with a RefCount of 1. If added to the parent
// it will get ref counted again. In either case, release the
// recount we have on exit.
node.reset(new DirNode(this, mode));
error = node->Init(0);
if (error)
return error;
return parent->AddChild(path.Basename(), node);
}
Error MemFs::Unlink(const Path& path) {
return RemoveInternal(path, REMOVE_FILE);
}
Error MemFs::Rmdir(const Path& path) {
return RemoveInternal(path, REMOVE_DIR);
}
Error MemFs::Remove(const Path& path) {
return RemoveInternal(path, REMOVE_ALL);
}
Error MemFs::Rename(const Path& src_path, const Path& target_path) {
ScopedNode src_node;
ScopedNode src_parent;
ScopedNode target_node;
ScopedNode target_parent;
int error = FindNode(src_path, 0, &src_node);
if (error)
return error;
// The source must exist
error = FindNode(src_path.Parent(), S_IFDIR, &src_parent);
if (error)
return error;
// The parent of the target must exist
error = FindNode(target_path.Parent(), 0, &target_parent);
if (error)
return error;
std::string target_name = target_path.Basename();
// The target itself need not exist but if it does there are
// certain restrictions
error = FindNode(target_path, 0, &target_node);
bool replacing_target = error == 0;
if (replacing_target) {
if (target_node->IsaDir()) {
// If the target is a direcotry it must be empty
if (target_node->ChildCount()) {
return ENOTEMPTY;
}
if (src_node->IsaDir()) {
// Replacing an existing directory.
RemoveInternal(target_path, REMOVE_ALL);
} else {
// Renaming into an existing directory.
target_name = src_path.Basename();
target_parent = target_node;
}
} else {
if (src_node->IsaDir())
// Can't replace a file with a direcotory
return EISDIR;
// Replacing an existing file.
target_parent->RemoveChild(target_path.Basename());
}
}
// Perform that actual rename. Simply re-parent the original source node
// onto its new parent node.
error = src_parent->RemoveChild(src_path.Basename());
if (error)
return error;
error = target_parent->AddChild(target_name, src_node);
if (error) {
// Re-parent the old target_node if we failed to add the new one.
if (replacing_target)
target_parent->AddChild(target_path.Basename(), target_node);
// Re-parent the src_node
target_parent->AddChild(target_path.Basename(), src_node);
return error;
}
return 0;
}
Error MemFs::RemoveInternal(const Path& path, int remove_type) {
bool dir_only = remove_type == REMOVE_DIR;
bool file_only = remove_type == REMOVE_FILE;
bool remove_dir = (remove_type & REMOVE_DIR) != 0;
if (dir_only) {
// We expect a Filesystem "absolute" path
if (!path.IsAbsolute())
return ENOENT;
// The root of the filesystem is already created by the filesystem
if (path.Size() == 1)
return EEXIST;
}
ScopedNode parent;
int error = FindNode(path.Parent(), S_IFDIR, &parent);
if (error)
return error;
// Verify we find a child which is a directory.
ScopedNode child;
error = parent->FindChild(path.Basename(), &child);
if (error)
return error;
if (dir_only && !child->IsaDir())
return ENOTDIR;
if (file_only && child->IsaDir())
return EISDIR;
if (remove_dir && child->ChildCount() > 0)
return ENOTEMPTY;
return parent->RemoveChild(path.Basename());
}
} // namespace nacl_io