Add MoveFileOrDirectory to move files, directories, or symbolic links

Change-Id: I6eaeef0dc3ec4300b361c1a96d14209aec736ff0
Reviewed-on: https://chromium-review.googlesource.com/727567
Commit-Queue: Joshua Peraza <jperaza@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Joshua Peraza 2017-10-20 09:54:35 -07:00 committed by Commit Bot
parent 419f25eac8
commit ce084d37c8
6 changed files with 182 additions and 5 deletions

View File

@ -35,6 +35,31 @@ bool LoggingCreateDirectory(const base::FilePath& path,
FilePermissions permissions, FilePermissions permissions,
bool may_reuse); bool may_reuse);
//! \brief Moves a file, symbolic link, or directory, logging a message on
//! failure.
//!
//! \a source must exist and refer to a file, symbolic link, or directory.
//!
//! \a source and \a dest must be on the same filesystem.
//!
//! If \a dest exists, it may be overwritten:
//!
//! If \a dest exists and refers to a file or to a live or dangling symbolic
//! link to a file, it will be overwritten if \a source also refers to a file or
//! to a live or dangling symbolic link to a file or directory.
//!
//! On POSIX, if \a dest refers to a directory, it will be overwritten only if
//! it is empty and \a source also refers to a directory.
//!
//! On Windows, if \a dest refers to a directory or to a live or dangling
//! symbolic link to a directory, it will not be overwritten.
//!
//! \param[in] source The path to the file to be moved.
//! \param[in] dest The new path for the file.
//! \return `true` on success. `false` on failure with a message logged.
bool MoveFileOrDirectory(const base::FilePath& source,
const base::FilePath& dest);
//! \brief Determines if a path refers to a regular file, logging a message on //! \brief Determines if a path refers to a regular file, logging a message on
//! failure. //! failure.
//! //!

View File

@ -15,6 +15,7 @@
#include "util/file/filesystem.h" #include "util/file/filesystem.h"
#include <errno.h> #include <errno.h>
#include <stdio.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
@ -42,6 +43,16 @@ bool LoggingCreateDirectory(const base::FilePath& path,
return false; return false;
} }
bool MoveFileOrDirectory(const base::FilePath& source,
const base::FilePath& dest) {
if (rename(source.value().c_str(), dest.value().c_str()) != 0) {
PLOG(ERROR) << "rename " << source.value().c_str() << ", "
<< dest.value().c_str();
return false;
}
return true;
}
bool IsRegularFile(const base::FilePath& path) { bool IsRegularFile(const base::FilePath& path) {
struct stat st; struct stat st;
if (lstat(path.value().c_str(), &st) != 0) { if (lstat(path.value().c_str(), &st) != 0) {

View File

@ -16,7 +16,6 @@
#include "base/logging.h" #include "base/logging.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "test/file.h"
#include "test/scoped_temp_dir.h" #include "test/scoped_temp_dir.h"
#include "util/file/filesystem_test_util.h" #include "util/file/filesystem_test_util.h"
@ -45,6 +44,110 @@ TEST(Filesystem, CreateDirectory) {
EXPECT_TRUE(IsRegularFile(file)); EXPECT_TRUE(IsRegularFile(file));
} }
TEST(Filesystem, MoveFileOrDirectory) {
ScopedTempDir temp_dir;
base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
ASSERT_TRUE(CreateFile(file));
// empty paths
EXPECT_FALSE(MoveFileOrDirectory(base::FilePath(), base::FilePath()));
EXPECT_FALSE(MoveFileOrDirectory(base::FilePath(), file));
EXPECT_FALSE(MoveFileOrDirectory(file, base::FilePath()));
EXPECT_TRUE(IsRegularFile(file));
// files
base::FilePath file2(temp_dir.path().Append(FILE_PATH_LITERAL("file2")));
EXPECT_TRUE(MoveFileOrDirectory(file, file2));
EXPECT_TRUE(IsRegularFile(file2));
EXPECT_FALSE(IsRegularFile(file));
EXPECT_FALSE(MoveFileOrDirectory(file, file2));
EXPECT_TRUE(IsRegularFile(file2));
EXPECT_FALSE(IsRegularFile(file));
ASSERT_TRUE(CreateFile(file));
EXPECT_TRUE(MoveFileOrDirectory(file2, file));
EXPECT_TRUE(IsRegularFile(file));
EXPECT_FALSE(IsRegularFile(file2));
// directories
base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir")));
ASSERT_TRUE(
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));
base::FilePath nested(dir.Append(FILE_PATH_LITERAL("nested")));
ASSERT_TRUE(CreateFile(nested));
base::FilePath dir2(temp_dir.path().Append(FILE_PATH_LITERAL("dir2")));
EXPECT_TRUE(MoveFileOrDirectory(dir, dir2));
EXPECT_FALSE(IsDirectory(dir, true));
EXPECT_TRUE(IsDirectory(dir2, false));
EXPECT_TRUE(IsRegularFile(dir2.Append(nested.BaseName())));
ASSERT_TRUE(
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));
EXPECT_FALSE(MoveFileOrDirectory(dir, dir2));
EXPECT_TRUE(IsDirectory(dir, false));
EXPECT_TRUE(IsDirectory(dir2, false));
EXPECT_TRUE(IsRegularFile(dir2.Append(nested.BaseName())));
// files <-> directories
EXPECT_FALSE(MoveFileOrDirectory(file, dir2));
EXPECT_TRUE(IsDirectory(dir2, false));
EXPECT_TRUE(IsRegularFile(file));
EXPECT_FALSE(MoveFileOrDirectory(dir2, file));
EXPECT_TRUE(IsDirectory(dir2, false));
EXPECT_TRUE(IsRegularFile(file));
// file links
base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link")));
ASSERT_TRUE(CreateSymbolicLink(file, link));
base::FilePath link2(temp_dir.path().Append(FILE_PATH_LITERAL("link2")));
EXPECT_TRUE(MoveFileOrDirectory(link, link2));
EXPECT_TRUE(PathExists(link2));
EXPECT_FALSE(PathExists(link));
ASSERT_TRUE(CreateSymbolicLink(file, link));
EXPECT_TRUE(MoveFileOrDirectory(link, link2));
EXPECT_TRUE(PathExists(link2));
EXPECT_FALSE(PathExists(link));
// file links <-> files
EXPECT_TRUE(MoveFileOrDirectory(file, link2));
EXPECT_TRUE(IsRegularFile(link2));
EXPECT_FALSE(PathExists(file));
ASSERT_TRUE(MoveFileOrDirectory(link2, file));
ASSERT_TRUE(CreateSymbolicLink(file, link));
EXPECT_TRUE(MoveFileOrDirectory(link, file));
EXPECT_TRUE(PathExists(file));
EXPECT_FALSE(IsRegularFile(file));
EXPECT_FALSE(PathExists(link));
EXPECT_TRUE(LoggingRemoveFile(file));
// dangling file links
ASSERT_TRUE(CreateSymbolicLink(file, link));
EXPECT_TRUE(MoveFileOrDirectory(link, link2));
EXPECT_TRUE(PathExists(link2));
EXPECT_FALSE(PathExists(link));
// directory links
ASSERT_TRUE(CreateSymbolicLink(dir, link));
EXPECT_TRUE(MoveFileOrDirectory(link, link2));
EXPECT_TRUE(PathExists(link2));
EXPECT_FALSE(PathExists(link));
// dangling directory links
ASSERT_TRUE(LoggingRemoveDirectory(dir));
EXPECT_TRUE(MoveFileOrDirectory(link2, link));
EXPECT_TRUE(PathExists(link));
EXPECT_FALSE(PathExists(link2));
}
TEST(Filesystem, IsRegularFile) { TEST(Filesystem, IsRegularFile) {
EXPECT_FALSE(IsRegularFile(base::FilePath())); EXPECT_FALSE(IsRegularFile(base::FilePath()));
@ -116,8 +219,8 @@ TEST(Filesystem, RemoveFile) {
base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link")));
ASSERT_TRUE(CreateSymbolicLink(file, link)); ASSERT_TRUE(CreateSymbolicLink(file, link));
EXPECT_TRUE(LoggingRemoveFile(link)); EXPECT_TRUE(LoggingRemoveFile(link));
EXPECT_FALSE(FileExists(link)); EXPECT_FALSE(PathExists(link));
EXPECT_TRUE(FileExists(file)); EXPECT_TRUE(PathExists(file));
base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir")));
ASSERT_TRUE( ASSERT_TRUE(
@ -126,8 +229,8 @@ TEST(Filesystem, RemoveFile) {
ASSERT_TRUE(CreateSymbolicLink(dir, link)); ASSERT_TRUE(CreateSymbolicLink(dir, link));
EXPECT_TRUE(LoggingRemoveFile(link)); EXPECT_TRUE(LoggingRemoveFile(link));
EXPECT_FALSE(FileExists(link)); EXPECT_FALSE(PathExists(link));
EXPECT_TRUE(FileExists(dir)); EXPECT_TRUE(PathExists(dir));
EXPECT_TRUE(LoggingRemoveFile(file)); EXPECT_TRUE(LoggingRemoveFile(file));
EXPECT_FALSE(IsRegularFile(file)); EXPECT_FALSE(IsRegularFile(file));

View File

@ -14,9 +14,15 @@
#include "util/file/filesystem_test_util.h" #include "util/file/filesystem_test_util.h"
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h" #include "build/build_config.h"
#include "gtest/gtest.h" #include "gtest/gtest.h"
#include "test/errors.h"
#include "util/file/file_io.h" #include "util/file/file_io.h"
#include "util/file/filesystem.h" #include "util/file/filesystem.h"
@ -59,5 +65,23 @@ bool CreateSymbolicLink(const base::FilePath& target_path,
return true; return true;
} }
bool PathExists(const base::FilePath& path) {
#if defined(OS_POSIX)
struct stat st;
if (lstat(path.value().c_str(), &st) != 0) {
EXPECT_EQ(errno, ENOENT) << ErrnoMessage("lstat ") << path.value();
return false;
}
#elif defined(OS_WIN)
if (GetFileAttributes(path.value().c_str()) == INVALID_FILE_ATTRIBUTES) {
EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND)
<< ErrorMessage("GetFileAttributes ")
<< base::UTF16ToUTF8(path.value());
return false;
}
#endif
return true;
}
} // namespace test } // namespace test
} // namespace crashpad } // namespace crashpad

View File

@ -25,6 +25,8 @@ bool CreateFile(const base::FilePath& file);
bool CreateSymbolicLink(const base::FilePath& target_path, bool CreateSymbolicLink(const base::FilePath& target_path,
const base::FilePath& symlink_path); const base::FilePath& symlink_path);
bool PathExists(const base::FilePath& path);
} // namespace test } // namespace test
} // namespace crashpad } // namespace crashpad

View File

@ -67,6 +67,18 @@ bool LoggingCreateDirectory(const base::FilePath& path,
return false; return false;
} }
bool MoveFileOrDirectory(const base::FilePath& source,
const base::FilePath& dest) {
if (!MoveFileEx(source.value().c_str(),
dest.value().c_str(),
IsDirectory(source, false) ? 0 : MOVEFILE_REPLACE_EXISTING)) {
PLOG(ERROR) << "MoveFileEx" << base::UTF16ToUTF8(source.value()) << ", "
<< base::UTF16ToUTF8(dest.value());
return false;
}
return true;
}
bool IsRegularFile(const base::FilePath& path) { bool IsRegularFile(const base::FilePath& path) {
DWORD fileattr = GetFileAttributes(path.value().c_str()); DWORD fileattr = GetFileAttributes(path.value().c_str());
if (fileattr == INVALID_FILE_ATTRIBUTES) { if (fileattr == INVALID_FILE_ATTRIBUTES) {