// Copyright (c) 2011 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 "base/file_util_proxy.h"

#include "base/message_loop_proxy.h"

// TODO(jianli): Move the code from anonymous namespace to base namespace so
// that all of the base:: prefixes would be unnecessary.
namespace {

namespace {

// Performs common checks for move and copy.
// This also removes the destination directory if it's non-empty and all other
// checks are passed (so that the copy/move correctly overwrites the
// destination).
static base::PlatformFileError PerformCommonCheckAndPreparationForMoveAndCopy(
    const FilePath& src_file_path,
    const FilePath& dest_file_path) {
  // Exits earlier if the source path does not exist.
  if (!file_util::PathExists(src_file_path))
    return base::PLATFORM_FILE_ERROR_NOT_FOUND;

  // The parent of the |dest_file_path| does not exist.
  if (!file_util::DirectoryExists(dest_file_path.DirName()))
    return base::PLATFORM_FILE_ERROR_NOT_FOUND;

  // It is an error to try to copy/move an entry into its child.
  if (src_file_path.IsParent(dest_file_path))
    return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;

  // Now it is ok to return if the |dest_file_path| does not exist.
  if (!file_util::PathExists(dest_file_path))
    return base::PLATFORM_FILE_OK;

  // |src_file_path| exists and is a directory.
  // |dest_file_path| exists and is a file.
  bool src_is_directory = file_util::DirectoryExists(src_file_path);
  bool dest_is_directory = file_util::DirectoryExists(dest_file_path);
  if (src_is_directory && !dest_is_directory)
    return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;

  // |src_file_path| exists and is a file.
  // |dest_file_path| exists and is a directory.
  if (!src_is_directory && dest_is_directory)
    return base::PLATFORM_FILE_ERROR_NOT_A_FILE;

  // It is an error to copy/move an entry into the same path.
  if (src_file_path.value() == dest_file_path.value())
    return base::PLATFORM_FILE_ERROR_EXISTS;

  if (dest_is_directory) {
    // It is an error to copy/move an entry to a non-empty directory.
    // Otherwise the copy/move attempt must overwrite the destination, but
    // the file_util's Copy or Move method doesn't perform overwrite
    // on all platforms, so we delete the destination directory here.
    // TODO(kinuko): may be better to change the file_util::{Copy,Move}.
    if (!file_util::Delete(dest_file_path, false /* recursive */)) {
      if (!file_util::IsDirectoryEmpty(dest_file_path))
        return base::PLATFORM_FILE_ERROR_NOT_EMPTY;
      return base::PLATFORM_FILE_ERROR_FAILED;
    }
  }
  return base::PLATFORM_FILE_OK;
}

}  // anonymous namespace

class MessageLoopRelay
    : public base::RefCountedThreadSafe<MessageLoopRelay> {
 public:
  MessageLoopRelay()
      : origin_message_loop_proxy_(
            base::MessageLoopProxy::CreateForCurrentThread()),
        error_code_(base::PLATFORM_FILE_OK) {
  }

  bool Start(scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
             const tracked_objects::Location& from_here) {
    return message_loop_proxy->PostTask(
        from_here,
        NewRunnableMethod(this, &MessageLoopRelay::ProcessOnTargetThread));
  }

 protected:
  friend class base::RefCountedThreadSafe<MessageLoopRelay>;
  virtual ~MessageLoopRelay() {}

  // Called to perform work on the FILE thread.
  virtual void RunWork() = 0;

  // Called to notify the callback on the origin thread.
  virtual void RunCallback() = 0;

  void set_error_code(base::PlatformFileError error_code) {
    error_code_ = error_code;
  }

  base::PlatformFileError error_code() const {
    return error_code_;
  }

 private:
  void ProcessOnTargetThread() {
    RunWork();
    origin_message_loop_proxy_->PostTask(
        FROM_HERE,
        NewRunnableMethod(this, &MessageLoopRelay::RunCallback));
  }

  scoped_refptr<base::MessageLoopProxy> origin_message_loop_proxy_;
  base::PlatformFileError error_code_;
};

class RelayCreateOrOpen : public MessageLoopRelay {
 public:
  RelayCreateOrOpen(
      scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
      const FilePath& file_path,
      int file_flags,
      base::FileUtilProxy::CreateOrOpenCallback* callback)
      : message_loop_proxy_(message_loop_proxy),
        file_path_(file_path),
        file_flags_(file_flags),
        callback_(callback),
        file_handle_(base::kInvalidPlatformFileValue),
        created_(false) {
    DCHECK(callback);
  }

 protected:
  virtual ~RelayCreateOrOpen() {
    if (file_handle_ != base::kInvalidPlatformFileValue)
      base::FileUtilProxy::Close(message_loop_proxy_, file_handle_, NULL);
  }

  virtual void RunWork() {
    if (!file_util::DirectoryExists(file_path_.DirName())) {
      // If its parent does not exist, should return NOT_FOUND error.
      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
      return;
    }
    base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
    file_handle_ = base::CreatePlatformFile(file_path_, file_flags_,
                                            &created_, &error_code);
    set_error_code(error_code);
  }

  virtual void RunCallback() {
    callback_->Run(error_code(), base::PassPlatformFile(&file_handle_),
                   created_);
    delete callback_;
  }

 private:
  scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
  FilePath file_path_;
  int file_flags_;
  base::FileUtilProxy::CreateOrOpenCallback* callback_;
  base::PlatformFile file_handle_;
  bool created_;
};

class RelayCreateTemporary : public MessageLoopRelay {
 public:
  RelayCreateTemporary(
      scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
      base::FileUtilProxy::CreateTemporaryCallback* callback)
      : message_loop_proxy_(message_loop_proxy),
        callback_(callback),
        file_handle_(base::kInvalidPlatformFileValue) {
    DCHECK(callback);
  }

 protected:
  virtual ~RelayCreateTemporary() {
    if (file_handle_ != base::kInvalidPlatformFileValue)
      base::FileUtilProxy::Close(message_loop_proxy_, file_handle_, NULL);
  }

  virtual void RunWork() {
    // TODO(darin): file_util should have a variant of CreateTemporaryFile
    // that returns a FilePath and a PlatformFile.
    file_util::CreateTemporaryFile(&file_path_);

    // Use a fixed set of flags that are appropriate for writing to a temporary
    // file from the IO thread using a net::FileStream.
    int file_flags =
        base::PLATFORM_FILE_CREATE_ALWAYS |
        base::PLATFORM_FILE_WRITE |
        base::PLATFORM_FILE_ASYNC |
        base::PLATFORM_FILE_TEMPORARY;
    base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
    file_handle_ = base::CreatePlatformFile(file_path_, file_flags,
                                            NULL, &error_code);
    set_error_code(error_code);
  }

  virtual void RunCallback() {
    callback_->Run(error_code(), base::PassPlatformFile(&file_handle_),
                   file_path_);
    delete callback_;
  }

 private:
  scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
  base::FileUtilProxy::CreateTemporaryCallback* callback_;
  base::PlatformFile file_handle_;
  FilePath file_path_;
};

class RelayWithStatusCallback : public MessageLoopRelay {
 public:
  explicit RelayWithStatusCallback(
      base::FileUtilProxy::StatusCallback* callback)
      : callback_(callback) {
    // It is OK for callback to be NULL.
  }

 protected:
  virtual void RunCallback() {
    // The caller may not have been interested in the result.
    if (callback_) {
      callback_->Run(error_code());
      delete callback_;
    }
  }

 private:
  base::FileUtilProxy::StatusCallback* callback_;
};

class RelayClose : public RelayWithStatusCallback {
 public:
  RelayClose(base::PlatformFile file_handle,
             base::FileUtilProxy::StatusCallback* callback)
      : RelayWithStatusCallback(callback),
        file_handle_(file_handle) {
  }

 protected:
  virtual void RunWork() {
    if (!base::ClosePlatformFile(file_handle_))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

 private:
  base::PlatformFile file_handle_;
};

class RelayEnsureFileExists : public MessageLoopRelay {
 public:
  RelayEnsureFileExists(
      scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
      const FilePath& file_path,
      base::FileUtilProxy::EnsureFileExistsCallback* callback)
      : message_loop_proxy_(message_loop_proxy),
        file_path_(file_path),
        callback_(callback),
        created_(false) {
    DCHECK(callback);
  }

 protected:
  virtual void RunWork() {
    if (!file_util::DirectoryExists(file_path_.DirName())) {
      // If its parent does not exist, should return NOT_FOUND error.
      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
      return;
    }
    base::PlatformFileError error_code = base::PLATFORM_FILE_OK;
    // Tries to create the |file_path_| exclusively.  This should fail
    // with PLATFORM_FILE_ERROR_EXISTS if the path already exists.
    base::PlatformFile handle = base::CreatePlatformFile(
        file_path_,
        base::PLATFORM_FILE_CREATE | base::PLATFORM_FILE_READ,
        &created_, &error_code);
    if (error_code == base::PLATFORM_FILE_ERROR_EXISTS) {
      // Make sure created_ is false.
      created_ = false;
      error_code = base::PLATFORM_FILE_OK;
    }
    if (handle != base::kInvalidPlatformFileValue)
      base::ClosePlatformFile(handle);
    set_error_code(error_code);
  }

  virtual void RunCallback() {
    callback_->Run(error_code(), created_);
    delete callback_;
  }

 private:
  scoped_refptr<base::MessageLoopProxy> message_loop_proxy_;
  FilePath file_path_;
  base::FileUtilProxy::EnsureFileExistsCallback* callback_;
  bool created_;
};

class RelayDelete : public RelayWithStatusCallback {
 public:
  RelayDelete(const FilePath& file_path,
              bool recursive,
              base::FileUtilProxy::StatusCallback* callback)
      : RelayWithStatusCallback(callback),
        file_path_(file_path),
        recursive_(recursive) {
  }

 protected:
  virtual void RunWork() {
    if (!file_util::PathExists(file_path_)) {
      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
      return;
    }
    if (!file_util::Delete(file_path_, recursive_)) {
      if (!recursive_ && !file_util::IsDirectoryEmpty(file_path_)) {
        set_error_code(base::PLATFORM_FILE_ERROR_NOT_EMPTY);
        return;
      }
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
    }
  }

 private:
  FilePath file_path_;
  bool recursive_;
};

class RelayCopy : public RelayWithStatusCallback {
 public:
  RelayCopy(const FilePath& src_file_path,
            const FilePath& dest_file_path,
            base::FileUtilProxy::StatusCallback* callback)
      : RelayWithStatusCallback(callback),
        src_file_path_(src_file_path),
        dest_file_path_(dest_file_path) {
  }

 protected:
  virtual void RunWork() {
    set_error_code(PerformCommonCheckAndPreparationForMoveAndCopy(
        src_file_path_, dest_file_path_));
    if (error_code() != base::PLATFORM_FILE_OK)
      return;
    if (!file_util::CopyDirectory(src_file_path_, dest_file_path_,
        true /* recursive */))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

 private:
  FilePath src_file_path_;
  FilePath dest_file_path_;
};

class RelayMove : public RelayWithStatusCallback {
 public:
  RelayMove(const FilePath& src_file_path,
            const FilePath& dest_file_path,
            base::FileUtilProxy::StatusCallback* callback)
      : RelayWithStatusCallback(callback),
        src_file_path_(src_file_path),
        dest_file_path_(dest_file_path) {
  }

 protected:
  virtual void RunWork() {
    set_error_code(PerformCommonCheckAndPreparationForMoveAndCopy(
        src_file_path_, dest_file_path_));
    if (error_code() != base::PLATFORM_FILE_OK)
      return;
    if (!file_util::Move(src_file_path_, dest_file_path_))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

 private:
  FilePath src_file_path_;
  FilePath dest_file_path_;
};

class RelayCreateDirectory : public RelayWithStatusCallback {
 public:
  RelayCreateDirectory(
      const FilePath& file_path,
      bool exclusive,
      bool recursive,
      base::FileUtilProxy::StatusCallback* callback)
      : RelayWithStatusCallback(callback),
        file_path_(file_path),
        exclusive_(exclusive),
        recursive_(recursive) {
  }

 protected:
  virtual void RunWork() {
    bool path_exists = file_util::PathExists(file_path_);
    // If parent dir of file doesn't exist.
    if (!recursive_ && !file_util::PathExists(file_path_.DirName())) {
      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
      return;
    }
    if (exclusive_ && path_exists) {
      set_error_code(base::PLATFORM_FILE_ERROR_EXISTS);
      return;
    }
    // If file exists at the path.
    if (path_exists && !file_util::DirectoryExists(file_path_)) {
      set_error_code(base::PLATFORM_FILE_ERROR_EXISTS);
      return;
    }
    if (!file_util::CreateDirectory(file_path_))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

 private:
  FilePath file_path_;
  bool exclusive_;
  bool recursive_;
};

class RelayReadDirectory : public MessageLoopRelay {
 public:
  RelayReadDirectory(const FilePath& file_path,
      base::FileUtilProxy::ReadDirectoryCallback* callback)
      : callback_(callback), file_path_(file_path) {
    DCHECK(callback);
  }

 protected:
  virtual void RunWork() {
    // TODO(kkanetkar): Implement directory read in multiple chunks.
    if (!file_util::DirectoryExists(file_path_)) {
      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
      return;
    }

    file_util::FileEnumerator file_enum(
        file_path_, false, static_cast<file_util::FileEnumerator::FILE_TYPE>(
        file_util::FileEnumerator::FILES |
        file_util::FileEnumerator::DIRECTORIES));
    FilePath current;
    while (!(current = file_enum.Next()).empty()) {
      base::FileUtilProxy::Entry entry;
      file_util::FileEnumerator::FindInfo info;
      file_enum.GetFindInfo(&info);
      entry.is_directory = file_enum.IsDirectory(info);
      // This will just give the entry's name instead of entire path
      // if we use current.value().
      entry.name = file_util::FileEnumerator::GetFilename(info).value();
      entries_.push_back(entry);
    }
  }

  virtual void RunCallback() {
    callback_->Run(error_code(), entries_);
    delete callback_;
  }

 private:
  base::FileUtilProxy::ReadDirectoryCallback* callback_;
  FilePath file_path_;
  std::vector<base::FileUtilProxy::Entry> entries_;
};

class RelayGetFileInfo : public MessageLoopRelay {
 public:
  RelayGetFileInfo(const FilePath& file_path,
                   base::FileUtilProxy::GetFileInfoCallback* callback)
      : callback_(callback),
        file_path_(file_path) {
    DCHECK(callback);
  }

 protected:
  virtual void RunWork() {
    if (!file_util::PathExists(file_path_)) {
      set_error_code(base::PLATFORM_FILE_ERROR_NOT_FOUND);
      return;
    }
    if (!file_util::GetFileInfo(file_path_, &file_info_))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

  virtual void RunCallback() {
    callback_->Run(error_code(), file_info_);
    delete callback_;
  }

 private:
  base::FileUtilProxy::GetFileInfoCallback* callback_;
  FilePath file_path_;
  base::PlatformFileInfo file_info_;
};

class RelayGetFileInfoFromPlatformFile : public MessageLoopRelay {
 public:
  RelayGetFileInfoFromPlatformFile(
      base::PlatformFile file,
      base::FileUtilProxy::GetFileInfoCallback* callback)
      : callback_(callback),
        file_(file) {
    DCHECK(callback);
  }

 protected:
  virtual void RunWork() {
    if (!base::GetPlatformFileInfo(file_, &file_info_))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

  virtual void RunCallback() {
    callback_->Run(error_code(), file_info_);
    delete callback_;
  }

 private:
  base::FileUtilProxy::GetFileInfoCallback* callback_;
  base::PlatformFile file_;
  base::PlatformFileInfo file_info_;
};

class RelayRead : public MessageLoopRelay {
 public:
  RelayRead(base::PlatformFile file,
            int64 offset,
            int bytes_to_read,
            base::FileUtilProxy::ReadCallback* callback)
      : file_(file),
        offset_(offset),
        buffer_(new char[bytes_to_read]),
        bytes_to_read_(bytes_to_read),
        callback_(callback),
        bytes_read_(0) {
  }

 protected:
  virtual void RunWork() {
    bytes_read_ = base::ReadPlatformFile(file_, offset_, buffer_.get(),
                                         bytes_to_read_);
    if (bytes_read_ < 0)
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

  virtual void RunCallback() {
    if (callback_) {
      callback_->Run(error_code(), buffer_.get(), bytes_read_);
      delete callback_;
    }
  }

 private:
  base::PlatformFile file_;
  int64 offset_;
  scoped_array<char> buffer_;
  int bytes_to_read_;
  base::FileUtilProxy::ReadCallback* callback_;
  int bytes_read_;
};

class RelayWrite : public MessageLoopRelay {
 public:
  RelayWrite(base::PlatformFile file,
             int64 offset,
             const char* buffer,
             int bytes_to_write,
             base::FileUtilProxy::WriteCallback* callback)
      : file_(file),
        offset_(offset),
        buffer_(new char[bytes_to_write]),
        bytes_to_write_(bytes_to_write),
        callback_(callback),
        bytes_written_(0) {
    memcpy(buffer_.get(), buffer, bytes_to_write);
  }

 protected:
  virtual void RunWork() {
    bytes_written_ = base::WritePlatformFile(file_, offset_, buffer_.get(),
                                             bytes_to_write_);
    if (bytes_written_ < 0)
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

  virtual void RunCallback() {
    if (callback_) {
      callback_->Run(error_code(), bytes_written_);
      delete callback_;
    }
  }

 private:
  base::PlatformFile file_;
  int64 offset_;
  scoped_array<char> buffer_;
  int bytes_to_write_;
  base::FileUtilProxy::WriteCallback* callback_;
  int bytes_written_;
};

class RelayTouch : public RelayWithStatusCallback {
 public:
  RelayTouch(base::PlatformFile file,
             const base::Time& last_access_time,
             const base::Time& last_modified_time,
             base::FileUtilProxy::StatusCallback* callback)
      : RelayWithStatusCallback(callback),
        file_(file),
        last_access_time_(last_access_time),
        last_modified_time_(last_modified_time) {
  }

 protected:
  virtual void RunWork() {
    if (!base::TouchPlatformFile(file_, last_access_time_, last_modified_time_))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

 private:
  base::PlatformFile file_;
  base::Time last_access_time_;
  base::Time last_modified_time_;
};

class RelayTouchFilePath : public RelayWithStatusCallback {
 public:
  RelayTouchFilePath(const FilePath& file_path,
                     const base::Time& last_access_time,
                     const base::Time& last_modified_time,
                     base::FileUtilProxy::StatusCallback* callback)
      : RelayWithStatusCallback(callback),
        file_path_(file_path),
        last_access_time_(last_access_time),
        last_modified_time_(last_modified_time) {
  }

 protected:
  virtual void RunWork() {
    if (!file_util::TouchFile(
            file_path_, last_access_time_, last_modified_time_))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

 private:
  FilePath file_path_;
  base::Time last_access_time_;
  base::Time last_modified_time_;
};

class RelayTruncatePlatformFile : public RelayWithStatusCallback {
 public:
  RelayTruncatePlatformFile(base::PlatformFile file,
                            int64 length,
                            base::FileUtilProxy::StatusCallback* callback)
      : RelayWithStatusCallback(callback),
        file_(file),
        length_(length) {
  }

 protected:
  virtual void RunWork() {
    if (!base::TruncatePlatformFile(file_, length_))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

 private:
  base::PlatformFile file_;
  int64 length_;
};

class RelayTruncate : public RelayWithStatusCallback {
 public:
  RelayTruncate(const FilePath& path,
                int64 length,
                base::FileUtilProxy::StatusCallback* callback)
      : RelayWithStatusCallback(callback),
        path_(path),
        length_(length) {
  }

 protected:
  virtual void RunWork() {
    base::PlatformFileError error_code(base::PLATFORM_FILE_ERROR_FAILED);
    base::PlatformFile file =
        base::CreatePlatformFile(
            path_,
            base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_WRITE,
            NULL,
            &error_code);
    if (error_code != base::PLATFORM_FILE_OK) {
      set_error_code(error_code);
      return;
    }
    if (!base::TruncatePlatformFile(file, length_))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
    base::ClosePlatformFile(file);
  }

 private:
  FilePath path_;
  int64 length_;
};

class RelayFlush : public RelayWithStatusCallback {
 public:
  RelayFlush(base::PlatformFile file,
             base::FileUtilProxy::StatusCallback* callback)
      : RelayWithStatusCallback(callback),
        file_(file) {
  }

 protected:
  virtual void RunWork() {
    if (!base::FlushPlatformFile(file_))
      set_error_code(base::PLATFORM_FILE_ERROR_FAILED);
  }

 private:
  base::PlatformFile file_;
};

bool Start(const tracked_objects::Location& from_here,
           scoped_refptr<base::MessageLoopProxy> message_loop_proxy,
           scoped_refptr<MessageLoopRelay> relay) {
  return relay->Start(message_loop_proxy, from_here);
}

}  // namespace

namespace base {

// static
bool FileUtilProxy::CreateOrOpen(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    const FilePath& file_path, int file_flags,
    CreateOrOpenCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy, new RelayCreateOrOpen(
      message_loop_proxy, file_path, file_flags, callback));
}

// static
bool FileUtilProxy::CreateTemporary(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    CreateTemporaryCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayCreateTemporary(message_loop_proxy, callback));
}

// static
bool FileUtilProxy::Close(scoped_refptr<MessageLoopProxy> message_loop_proxy,
                          base::PlatformFile file_handle,
                          StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayClose(file_handle, callback));
}

// static
bool FileUtilProxy::EnsureFileExists(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    const FilePath& file_path,
    EnsureFileExistsCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy, new RelayEnsureFileExists(
      message_loop_proxy, file_path, callback));
}

// Retrieves the information about a file. It is invalid to pass NULL for the
// callback.
bool FileUtilProxy::GetFileInfo(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    const FilePath& file_path,
    GetFileInfoCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy, new RelayGetFileInfo(
               file_path, callback));
}

// static
bool FileUtilProxy::GetFileInfoFromPlatformFile(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    PlatformFile file,
    GetFileInfoCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayGetFileInfoFromPlatformFile(file, callback));
}

// static
bool FileUtilProxy::ReadDirectory(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    const FilePath& file_path,
    ReadDirectoryCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy, new RelayReadDirectory(
               file_path, callback));
}

// static
bool FileUtilProxy::CreateDirectory(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    const FilePath& file_path,
    bool exclusive,
    bool recursive,
    StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy, new RelayCreateDirectory(
      file_path, exclusive, recursive, callback));
}

// static
bool FileUtilProxy::Copy(scoped_refptr<MessageLoopProxy> message_loop_proxy,
                         const FilePath& src_file_path,
                         const FilePath& dest_file_path,
                         StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayCopy(src_file_path, dest_file_path, callback));
}

// static
bool FileUtilProxy::Move(scoped_refptr<MessageLoopProxy> message_loop_proxy,
                         const FilePath& src_file_path,
                         const FilePath& dest_file_path,
                         StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayMove(src_file_path, dest_file_path, callback));
}

// static
bool FileUtilProxy::Delete(scoped_refptr<MessageLoopProxy> message_loop_proxy,
                           const FilePath& file_path,
                           bool recursive,
                           StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayDelete(file_path, recursive, callback));
}

// static
bool FileUtilProxy::RecursiveDelete(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    const FilePath& file_path,
    StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayDelete(file_path, true, callback));
}

// static
bool FileUtilProxy::Read(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    PlatformFile file,
    int64 offset,
    int bytes_to_read,
    ReadCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayRead(file, offset, bytes_to_read, callback));
}

// static
bool FileUtilProxy::Write(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    PlatformFile file,
    int64 offset,
    const char* buffer,
    int bytes_to_write,
    WriteCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayWrite(file, offset, buffer, bytes_to_write, callback));
}

// static
bool FileUtilProxy::Touch(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    PlatformFile file,
    const base::Time& last_access_time,
    const base::Time& last_modified_time,
    StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayTouch(file, last_access_time, last_modified_time,
                              callback));
}

// static
bool FileUtilProxy::Touch(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    const FilePath& file_path,
    const base::Time& last_access_time,
    const base::Time& last_modified_time,
    StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayTouchFilePath(file_path, last_access_time,
                                      last_modified_time, callback));
}

// static
bool FileUtilProxy::Truncate(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    PlatformFile file,
    int64 length,
    StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayTruncatePlatformFile(file, length, callback));
}

// static
bool FileUtilProxy::Truncate(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    const FilePath& path,
    int64 length,
    StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy,
               new RelayTruncate(path, length, callback));
}

// static
bool FileUtilProxy::Flush(
    scoped_refptr<MessageLoopProxy> message_loop_proxy,
    PlatformFile file,
    StatusCallback* callback) {
  return Start(FROM_HERE, message_loop_proxy, new RelayFlush(file, callback));
}

}  // namespace base