root/native_client_sdk/src/libraries/nacl_io/memfs/mem_fs.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Init
  2. FindNode
  3. Access
  4. Open
  5. Mkdir
  6. Unlink
  7. Rmdir
  8. Remove
  9. Rename
  10. RemoveInternal

// 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));
  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::Access(const Path& path, int a_mode) {
  ScopedNode 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 MemFs::Open(const Path& path, int open_flags, 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;

    error = parent->AddChild(path.Basename(), node);
    if (error)
      return error;

  } else {
    // Opening an existing file.

    // 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;

    if (open_flags & O_TRUNC)
      static_cast<MemFsNode*>(node.get())->Resize(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));
  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

/* [<][>][^][v][top][bottom][index][help] */