blob: 9b32cc1ade7cced35d28da24e00bcac8328ec2e5 [file] [log] [blame]
// Copyright (c) 2012 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/mount_mem.h"
#include <errno.h>
#include <fcntl.h>
#include <string>
#include "nacl_io/mount.h"
#include "nacl_io/mount_node.h"
#include "nacl_io/mount_node_dir.h"
#include "nacl_io/mount_node_mem.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 {
MountMem::MountMem() : root_(NULL) {}
Error MountMem::Init(int dev, StringMap_t& args, PepperInterface* ppapi) {
Error error = Mount::Init(dev, args, ppapi);
if (error)
return error;
root_.reset(new MountNodeDir(this));
error = root_->Init(0);
if (error) {
root_.reset(NULL);
return error;
}
return 0;
}
Error MountMem::FindNode(const Path& path,
int type,
ScopedMountNode* out_node) {
out_node->reset(NULL);
ScopedMountNode 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 MountMem::Access(const Path& path, int a_mode) {
ScopedMountNode node;
Error error = FindNode(path, 0, &node);
if (error)
return error;
int obj_mode = node->GetMode();
if (((a_mode & R_OK) && !(obj_mode & S_IREAD)) ||
((a_mode & W_OK) && !(obj_mode & S_IWRITE)) ||
((a_mode & X_OK) && !(obj_mode & S_IEXEC))) {
return EACCES;
}
return 0;
}
Error MountMem::Open(const Path& path, int open_flags,
ScopedMountNode* out_node) {
out_node->reset(NULL);
ScopedMountNode 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
ScopedMountNode parent;
error = FindNode(path.Parent(), S_IFDIR, &parent);
if (error)
return error;
node.reset(new MountNodeMem(this));
error = node->Init(open_flags);
if (error)
return error;
error = parent->AddChild(path.Basename(), node);
if (error)
return error;
*out_node = node;
return 0;
}
// Directories can only be opened read-only.
if (node->IsaDir() && (open_flags & 3) != O_RDONLY)
return EISDIR;
// If we were expected to create it exclusively, fail
if (open_flags & O_EXCL)
return EEXIST;
*out_node = node;
return 0;
}
Error MountMem::Mkdir(const Path& path, int mode) {
// We expect a Mount "absolute" path
if (!path.IsAbsolute())
return ENOENT;
// The root of the mount is already created by the mount
if (path.Size() == 1)
return EEXIST;
ScopedMountNode parent;
int error = FindNode(path.Parent(), S_IFDIR, &parent);
if (error)
return error;
ScopedMountNode 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 MountNodeDir(this));
error = node->Init(0);
if (error)
return error;
return parent->AddChild(path.Basename(), node);
}
Error MountMem::Unlink(const Path& path) {
return RemoveInternal(path, REMOVE_FILE);
}
Error MountMem::Rmdir(const Path& path) {
return RemoveInternal(path, REMOVE_DIR);
}
Error MountMem::Remove(const Path& path) {
return RemoveInternal(path, REMOVE_ALL);
}
Error MountMem::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 Mount "absolute" path
if (!path.IsAbsolute())
return ENOENT;
// The root of the mount is already created by the mount
if (path.Size() == 1)
return EEXIST;
}
ScopedMountNode parent;
int error = FindNode(path.Parent(), S_IFDIR, &parent);
if (error)
return error;
// Verify we find a child which is a directory.
ScopedMountNode 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