/**
   * internal implementation of directory creation
   *
   * @param path path to file
   * @return boolean file is created
   * @throws IOException if specified path is file instead of directory
   */
  private boolean mkdir(Path path) throws IOException {
    Path absolutePath = makeAbsolute(path);
    //    if (!store.objectExists(absolutePath)) {
    //      store.createDirectory(absolutePath);
    //    }

    // TODO: define a consistent semantic for a directory/subdirectory
    // in the hadoop:swift bridge. Hadoop FS assumes that there
    // are files and directories, whereas SwiftFS assumes
    // that there are just "objects"

    FileStatus fileStatus;
    try {
      fileStatus = getFileStatus(absolutePath);
      if (!SwiftUtils.isDirectory(fileStatus)) {
        throw new SwiftNotDirectoryException(
            path, String.format(": can't mkdir since it is not a directory: %s", fileStatus));
      } else {
        if (LOG.isDebugEnabled()) {
          LOG.debug("skipping mkdir(" + path + ") as it exists already");
        }
      }
    } catch (FileNotFoundException e) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Making dir '" + path + "' in Swift");
      }
      // file is not found: it must be created
      store.createDirectory(absolutePath);
    }
    return true;
  }
  @Override
  public boolean isDirectory(Path f) throws IOException {

    try {
      FileStatus fileStatus = getFileStatus(f);
      return SwiftUtils.isDirectory(fileStatus);
    } catch (FileNotFoundException e) {
      return false; // f does not exist
    }
  }
  /**
   * Delete the entire tree. This is an internal one with slightly different behavior: if an entry
   * is missing, a {@link FileNotFoundException} is raised. This lets the caller distinguish a file
   * not found with other reasons for failure, so handles race conditions in recursive directory
   * deletes better.
   *
   * <p>The problem being addressed is: caller A requests a recursive directory of directory /dir ;
   * caller B requests a delete of a file /dir/file, between caller A enumerating the files
   * contents, and requesting a delete of /dir/file. We want to recognise the special case "directed
   * file is no longer there" and not convert that into a failure
   *
   * @param path the path to delete.
   * @param recursive if path is a directory and set to true, the directory is deleted else throws
   *     an exception if the directory is not empty case of a file the recursive can be set to
   *     either true or false.
   * @return true if the object was deleted
   * @throws IOException IO problems
   * @throws FileNotFoundException if a file/dir being deleted is not there - this includes entries
   *     below the specified path, (if the path is a dir and recursive is true)
   */
  private boolean innerDelete(Path path, boolean recursive) throws IOException {
    Path target = makeAbsolute(path);
    final FileStatus fileStatus;
    fileStatus = getFileStatus(path);
    if (LOG.isDebugEnabled()) {
      LOG.debug("Deleting path '" + path + "'");
    }
    if (!SwiftUtils.isDirectory(fileStatus)) {
      // simple file: delete it
      if (LOG.isDebugEnabled()) {
        LOG.debug("Deleting simple file '" + path + "'");
      }
      store.deleteObject(target);
    } else {
      // it's a directory
      if (LOG.isDebugEnabled()) {
        LOG.debug("Deleting directory '" + path + "'");
      }

      // get all entries
      List<FileStatus> children = store.listDirectory(target, true, true);

      // look to see if there are now any children
      if (!children.isEmpty() && !recursive) {
        // if there are children, unless this is a recursive operation, fail immediately
        throw new SwiftOperationFailedException("Directory " + path + " is not empty.");
      }

      // delete the children
      for (FileStatus child : children) {
        Path childPath = child.getPath();
        try {
          store.deleteObject(childPath);
        } catch (FileNotFoundException e) {
          // the path went away -race conditions.
          // do not fail, as the outcome is still OK.
          LOG.info("Path " + childPath + " is no longer present");
        }
      }
      // here any children that existed have been deleted
      // so rm the directory (which is a no-op for /)
      store.rmdir(target);
    }

    return true;
  }
  /** @param permission Currently ignored. */
  @Override
  public FSDataOutputStream create(
      Path file,
      FsPermission permission,
      boolean overwrite,
      int bufferSize,
      short replication,
      long blockSize,
      Progressable progress)
      throws IOException {

    LOG.debug("SwiftFileSystem.create");

    FileStatus fileStatus = null;
    try {
      fileStatus = getFileStatus(makeAbsolute(file));
    } catch (FileNotFoundException e) {
      // nothing to do
    }

    if (fileStatus != null && !SwiftUtils.isDirectory(fileStatus)) {
      if (overwrite) {
        delete(file, true);
      } else {
        throw new SwiftException("File already exists: " + file);
      }
    } else {
      Path parent = file.getParent();
      if (parent != null) {
        if (!mkdirs(parent)) {
          throw new SwiftException("Mkdirs failed to create " + parent.toString());
        }
      }
    }

    SwiftNativeOutputStream out =
        new SwiftNativeOutputStream(getConf(), store, file.toUri().toString());
    return new FSDataOutputStream(out, statistics);
  }
  /**
   * internal implementation of directory creation
   *
   * @param path path to file
   * @return boolean file is created
   * @throws IOException if specified path is file instead of directory
   */
  private boolean mkdir(Path path) throws IOException {
    Path absolutePath = makeAbsolute(path);

    FileStatus fileStatus;
    try {
      fileStatus = getFileStatus(absolutePath);
      if (!SwiftUtils.isDirectory(fileStatus)) {
        throw new SwiftNotDirectoryException(
            path, String.format(": can't mkdir since it is not a directory: %s", fileStatus));
      } else {
        if (LOG.isDebugEnabled()) {
          LOG.debug("skipping mkdir(" + path + ") as it exists already");
        }
      }
    } catch (FileNotFoundException e) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Making dir '" + path + "' in Swift");
      }
      // file is not found: it must be created
      store.createDirectory(absolutePath);
    }
    return true;
  }
  /**
   * Delete the entire tree. This is an internal one with slightly different behavior: if an entry
   * is missing, a {@link FileNotFoundException} is raised. This lets the caller distinguish a file
   * not found with other reasons for failure, so handles race conditions in recursive directory
   * deletes better.
   *
   * <p>The problem being addressed is: caller A requests a recursive directory of directory /dir ;
   * caller B requests a delete of a file /dir/file, between caller A enumerating the files
   * contents, and requesting a delete of /dir/file. We want to recognise the special case "directed
   * file is no longer there" and not convert that into a failure
   *
   * @param path the path to delete.
   * @param recursive if path is a directory and set to true, the directory is deleted else throws
   *     an exception if the directory is not empty case of a file the recursive can be set to
   *     either true or false.
   * @return true if the object was deleted
   * @throws IOException IO problems
   * @throws FileNotFoundException if a file/dir being deleted is not there - this includes entries
   *     below the specified path, (if the path is a dir and recursive is true)
   */
  private boolean innerDelete(Path path, boolean recursive) throws IOException {
    Path absolutePath = makeAbsolute(path);
    final FileStatus fileStatus;
    fileStatus = getFileStatus(path);
    if (LOG.isDebugEnabled()) {
      LOG.debug("Deleting path '" + path + "'");
    }
    if (!SwiftUtils.isDirectory(fileStatus)) {
      // simple file: delete it
      if (LOG.isDebugEnabled()) {
        LOG.debug("Deleting simple file '" + path + "'");
      }
      store.deleteObject(absolutePath);
    } else {
      // it's a directory
      if (LOG.isDebugEnabled()) {
        LOG.debug("Deleting directory '" + path + "'");
      }
      FileStatus[] contents = listStatus(absolutePath);
      if (contents == null) {
        // the directory went away during the non-atomic stages of the operation.
        // Return false as it was not this thread doing the deletion.
        if (LOG.isDebugEnabled()) {
          LOG.debug("Path '" + path + "' has no status -it has 'gone away'");
        }
        return false;
      }
      // now build a list without ourselves in it
      Path dirPath = fileStatus.getPath();
      if (LOG.isDebugEnabled()) {
        LOG.debug("Found " + contents.length + " child entries under " + dirPath);
      }
      ArrayList<FileStatus> children = new ArrayList<FileStatus>(contents.length);
      for (FileStatus child : contents) {
        if (!(child.getPath().equals(dirPath))) {
          if (LOG.isDebugEnabled()) {
            LOG.debug(child.toString());
          }
          children.add(child);
        } else {
          if (LOG.isDebugEnabled()) {
            LOG.debug("skipping own entry");
          }
        }
      }

      // look to see if there are now any children
      if (!children.isEmpty() && !recursive) {
        // if there are unless this is a recursive operation, fail immediately
        throw new SwiftException("Directory " + path + " is not empty.");
      }
      // delete the children
      for (FileStatus child : children) {
        Path childPath = child.getPath();
        try {
          if (!innerDelete(childPath, true)) {
            if (LOG.isDebugEnabled()) {
              LOG.debug("Failed to  recursively delete '" + childPath + "'");
            }
            return false;
          }
        } catch (FileNotFoundException e) {
          // the path went away -race conditions.
          // do not fail, as the outcome is still OK.
          LOG.info("Path " + childPath + " is no longer present");
        }
      }
      // here any children that existed have been deleted
      // so rm the directory
      store.rmdir(absolutePath);
    }

    return true;
  }