mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 15:32:10 +08:00
Add FileModificationTime
FileModificationTime gets the last write time for files, directories, or symbolic links. Symbolic links may point to files, directories, or be dangling. Bug: crashpad:206 Change-Id: Ic83b5a7d318502ad5db5c01731d06c8624925e15 Reviewed-on: https://chromium-review.googlesource.com/744298 Commit-Queue: Joshua Peraza <jperaza@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
2d077a2c57
commit
6f6f8a144d
@ -15,7 +15,9 @@
|
|||||||
#include "test/filesystem.h"
|
#include "test/filesystem.h"
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
@ -25,6 +27,7 @@
|
|||||||
#include "test/scoped_temp_dir.h"
|
#include "test/scoped_temp_dir.h"
|
||||||
#include "util/file/file_io.h"
|
#include "util/file/file_io.h"
|
||||||
#include "util/file/filesystem.h"
|
#include "util/file/filesystem.h"
|
||||||
|
#include "util/misc/time.h"
|
||||||
|
|
||||||
#if defined(OS_POSIX)
|
#if defined(OS_POSIX)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -116,6 +119,63 @@ bool PathExists(const base::FilePath& path) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SetFileModificationTime(const base::FilePath& path,
|
||||||
|
const timespec& mtime) {
|
||||||
|
#if defined(OS_MACOSX)
|
||||||
|
// utimensat() isn't available on macOS until 10.13, so lutimes() is used
|
||||||
|
// instead.
|
||||||
|
struct stat st;
|
||||||
|
if (lstat(path.value().c_str(), &st) != 0) {
|
||||||
|
PLOG(ERROR) << "lstat " << path.value();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
timeval times[2];
|
||||||
|
EXPECT_TRUE(TimespecToTimeval(st.st_atimespec, ×[0]));
|
||||||
|
EXPECT_TRUE(TimespecToTimeval(mtime, ×[1]));
|
||||||
|
if (lutimes(path.value().c_str(), times) != 0) {
|
||||||
|
PLOG(ERROR) << "lutimes " << path.value();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#elif defined(OS_POSIX)
|
||||||
|
timespec times[2];
|
||||||
|
times[0].tv_sec = 0;
|
||||||
|
times[0].tv_nsec = UTIME_OMIT;
|
||||||
|
times[1] = mtime;
|
||||||
|
if (utimensat(AT_FDCWD, path.value().c_str(), times, AT_SYMLINK_NOFOLLOW) !=
|
||||||
|
0) {
|
||||||
|
PLOG(ERROR) << "utimensat " << path.value();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
DWORD flags = FILE_FLAG_OPEN_REPARSE_POINT;
|
||||||
|
if (IsDirectory(path, true)) {
|
||||||
|
// required for directory handles
|
||||||
|
flags |= FILE_FLAG_BACKUP_SEMANTICS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedFileHandle handle(::CreateFile(path.value().c_str(),
|
||||||
|
GENERIC_WRITE,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
|
nullptr,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
flags,
|
||||||
|
nullptr));
|
||||||
|
if (!handle.is_valid()) {
|
||||||
|
PLOG(ERROR) << "CreateFile " << base::UTF16ToUTF8(path.value());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILETIME filetime = TimespecToFiletimeEpoch(mtime);
|
||||||
|
if (!SetFileTime(handle.get(), nullptr, nullptr, &filetime)) {
|
||||||
|
PLOG(ERROR) << "SetFileTime " << base::UTF16ToUTF8(path.value());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#endif // OS_MACOSX
|
||||||
|
}
|
||||||
|
|
||||||
#if !defined(OS_FUCHSIA)
|
#if !defined(OS_FUCHSIA)
|
||||||
|
|
||||||
bool CanCreateSymbolicLinks() {
|
bool CanCreateSymbolicLinks() {
|
||||||
|
@ -17,15 +17,26 @@
|
|||||||
|
|
||||||
#include "base/files/file_path.h"
|
#include "base/files/file_path.h"
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
bool CreateFile(const base::FilePath& file);
|
//! \brief Creates an empty file at path \a filepath.
|
||||||
|
bool CreateFile(const base::FilePath& filepath);
|
||||||
|
|
||||||
|
//! \brief Returns `true` if a filesystem node exists at path \a path.
|
||||||
bool PathExists(const base::FilePath& path);
|
bool PathExists(const base::FilePath& path);
|
||||||
|
|
||||||
|
//! \brief Sets the modification time for a file, directory, or symbolic link.
|
||||||
|
//!
|
||||||
|
//! \param[in] path The path to the file to set the modification time for.
|
||||||
|
//! \param[in] mtime The new modification time for the file.
|
||||||
|
//! \return `true` on success. Otherwise `false` with a message logged.
|
||||||
|
bool SetFileModificationTime(const base::FilePath& path, const timespec& mtime);
|
||||||
|
|
||||||
#if !defined(OS_FUCHSIA) || DOXYGEN
|
#if !defined(OS_FUCHSIA) || DOXYGEN
|
||||||
// There are no symbolic links on Fuchsia. Don’t bother declaring or defining
|
// There are no symbolic links on Fuchsia. Don’t bother declaring or defining
|
||||||
// symbolic link-related functions at all, because it’s an error to even pretend
|
// symbolic link-related functions at all, because it’s an error to even pretend
|
||||||
@ -48,6 +59,11 @@ bool PathExists(const base::FilePath& path);
|
|||||||
//! in Windows 10!</a>
|
//! in Windows 10!</a>
|
||||||
bool CanCreateSymbolicLinks();
|
bool CanCreateSymbolicLinks();
|
||||||
|
|
||||||
|
//! \brief Creates a new symbolic link.
|
||||||
|
//!
|
||||||
|
//! \param[in] target_path The target for the link.
|
||||||
|
//! \param[in] symlink_path The name for the new link.
|
||||||
|
//! \return `true` on success. Otherwise `false` with a message logged.
|
||||||
bool CreateSymbolicLink(const base::FilePath& target_path,
|
bool CreateSymbolicLink(const base::FilePath& target_path,
|
||||||
const base::FilePath& symlink_path);
|
const base::FilePath& symlink_path);
|
||||||
|
|
||||||
|
@ -15,12 +15,21 @@
|
|||||||
#ifndef CRASHPAD_UTIL_FILE_FILESYSTEM_H_
|
#ifndef CRASHPAD_UTIL_FILE_FILESYSTEM_H_
|
||||||
#define CRASHPAD_UTIL_FILE_FILESYSTEM_H_
|
#define CRASHPAD_UTIL_FILE_FILESYSTEM_H_
|
||||||
|
|
||||||
#include "base/files/file_path.h"
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "base/files/file_path.h"
|
||||||
#include "util/file/file_io.h"
|
#include "util/file/file_io.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
|
//! \brief Determines the modification time for a file, directory, or symbolic
|
||||||
|
//! link, logging a message on failure.
|
||||||
|
//!
|
||||||
|
//! \param[in] path The file to get the modification time for.
|
||||||
|
//! \param[out] mtime The modification time as seconds since the POSIX Epoch.
|
||||||
|
//! \return `true` on success. `false` on failure with a message logged.
|
||||||
|
bool FileModificationTime(const base::FilePath& path, timespec* mtime);
|
||||||
|
|
||||||
//! \brief Creates a directory, logging a message on failure.
|
//! \brief Creates a directory, logging a message on failure.
|
||||||
//!
|
//!
|
||||||
//! \param[in] path The path to the directory to create.
|
//! \param[in] path The path to the directory to create.
|
||||||
|
@ -21,9 +21,25 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
|
bool FileModificationTime(const base::FilePath& path, timespec* mtime) {
|
||||||
|
struct stat st;
|
||||||
|
if (lstat(path.value().c_str(), &st) != 0) {
|
||||||
|
PLOG(ERROR) << "lstat " << path.value();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(OS_MACOSX)
|
||||||
|
*mtime = st.st_mtimespec;
|
||||||
|
#else
|
||||||
|
*mtime = st.st_mtim;
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool LoggingCreateDirectory(const base::FilePath& path,
|
bool LoggingCreateDirectory(const base::FilePath& path,
|
||||||
FilePermissions permissions,
|
FilePermissions permissions,
|
||||||
bool may_reuse) {
|
bool may_reuse) {
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "test/errors.h"
|
||||||
#include "test/filesystem.h"
|
#include "test/filesystem.h"
|
||||||
#include "test/gtest_disabled.h"
|
#include "test/gtest_disabled.h"
|
||||||
#include "test/scoped_temp_dir.h"
|
#include "test/scoped_temp_dir.h"
|
||||||
@ -25,6 +26,109 @@ namespace crashpad {
|
|||||||
namespace test {
|
namespace test {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
bool CurrentTime(timespec* now) {
|
||||||
|
#if defined(OS_POSIX)
|
||||||
|
int res = clock_gettime(CLOCK_REALTIME, now);
|
||||||
|
if (res != 0) {
|
||||||
|
EXPECT_EQ(res, 0) << ErrnoMessage("clock_gettime");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
int res = timespec_get(now, TIME_UTC);
|
||||||
|
if (res != TIME_UTC) {
|
||||||
|
EXPECT_EQ(res, TIME_UTC);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Filesystem, FileModificationTime) {
|
||||||
|
timespec expected_time_start, expected_time_end;
|
||||||
|
|
||||||
|
ASSERT_TRUE(CurrentTime(&expected_time_start));
|
||||||
|
ScopedTempDir temp_dir;
|
||||||
|
ASSERT_TRUE(CurrentTime(&expected_time_end));
|
||||||
|
|
||||||
|
timespec dir_mtime;
|
||||||
|
ASSERT_TRUE(FileModificationTime(temp_dir.path(), &dir_mtime));
|
||||||
|
EXPECT_GE(dir_mtime.tv_sec, expected_time_start.tv_sec - 2);
|
||||||
|
EXPECT_LE(dir_mtime.tv_sec, expected_time_end.tv_sec + 2);
|
||||||
|
|
||||||
|
base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
|
||||||
|
ASSERT_TRUE(CurrentTime(&expected_time_start));
|
||||||
|
ASSERT_TRUE(CreateFile(file));
|
||||||
|
ASSERT_TRUE(CurrentTime(&expected_time_end));
|
||||||
|
|
||||||
|
timespec file_mtime;
|
||||||
|
ASSERT_TRUE(FileModificationTime(file, &file_mtime));
|
||||||
|
EXPECT_GE(file_mtime.tv_sec, expected_time_start.tv_sec - 2);
|
||||||
|
EXPECT_LE(file_mtime.tv_sec, expected_time_end.tv_sec + 2);
|
||||||
|
|
||||||
|
timespec file_mtime_again;
|
||||||
|
ASSERT_TRUE(FileModificationTime(file, &file_mtime_again));
|
||||||
|
EXPECT_EQ(file_mtime.tv_sec, file_mtime_again.tv_sec);
|
||||||
|
EXPECT_EQ(file_mtime.tv_nsec, file_mtime_again.tv_nsec);
|
||||||
|
|
||||||
|
timespec mtime;
|
||||||
|
EXPECT_FALSE(FileModificationTime(base::FilePath(), &mtime));
|
||||||
|
EXPECT_FALSE(FileModificationTime(
|
||||||
|
temp_dir.path().Append(FILE_PATH_LITERAL("notafile")), &mtime));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(OS_FUCHSIA)
|
||||||
|
|
||||||
|
TEST(Filesystem, FileModificationTime_SymbolicLinks) {
|
||||||
|
if (!CanCreateSymbolicLinks()) {
|
||||||
|
DISABLED_TEST();
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedTempDir temp_dir;
|
||||||
|
|
||||||
|
const base::FilePath dir_link(
|
||||||
|
temp_dir.path().Append(FILE_PATH_LITERAL("dir_link")));
|
||||||
|
|
||||||
|
timespec expected_time_start, expected_time_end;
|
||||||
|
ASSERT_TRUE(CurrentTime(&expected_time_start));
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(temp_dir.path(), dir_link));
|
||||||
|
ASSERT_TRUE(CurrentTime(&expected_time_end));
|
||||||
|
|
||||||
|
timespec mtime, mtime2;
|
||||||
|
ASSERT_TRUE(FileModificationTime(temp_dir.path(), &mtime));
|
||||||
|
mtime.tv_sec -= 100;
|
||||||
|
ASSERT_TRUE(SetFileModificationTime(temp_dir.path(), mtime));
|
||||||
|
ASSERT_TRUE(FileModificationTime(temp_dir.path(), &mtime2));
|
||||||
|
EXPECT_GE(mtime2.tv_sec, mtime.tv_sec - 2);
|
||||||
|
EXPECT_LE(mtime2.tv_sec, mtime.tv_sec + 2);
|
||||||
|
|
||||||
|
ASSERT_TRUE(FileModificationTime(dir_link, &mtime));
|
||||||
|
EXPECT_GE(mtime.tv_sec, expected_time_start.tv_sec - 2);
|
||||||
|
EXPECT_LE(mtime.tv_sec, expected_time_end.tv_sec + 2);
|
||||||
|
|
||||||
|
base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
|
||||||
|
ASSERT_TRUE(CreateFile(file));
|
||||||
|
|
||||||
|
base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link")));
|
||||||
|
|
||||||
|
ASSERT_TRUE(CurrentTime(&expected_time_start));
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(file, link));
|
||||||
|
ASSERT_TRUE(CurrentTime(&expected_time_end));
|
||||||
|
|
||||||
|
ASSERT_TRUE(FileModificationTime(file, &mtime));
|
||||||
|
mtime.tv_sec -= 100;
|
||||||
|
ASSERT_TRUE(SetFileModificationTime(file, mtime));
|
||||||
|
ASSERT_TRUE(FileModificationTime(file, &mtime2));
|
||||||
|
EXPECT_GE(mtime2.tv_sec, mtime.tv_sec - 2);
|
||||||
|
EXPECT_LE(mtime2.tv_sec, mtime.tv_sec + 2);
|
||||||
|
|
||||||
|
ASSERT_TRUE(FileModificationTime(link, &mtime));
|
||||||
|
EXPECT_GE(mtime.tv_sec, expected_time_start.tv_sec - 2);
|
||||||
|
EXPECT_LE(mtime.tv_sec, expected_time_end.tv_sec + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // !OS_FUCHSIA
|
||||||
|
|
||||||
TEST(Filesystem, CreateDirectory) {
|
TEST(Filesystem, CreateDirectory) {
|
||||||
ScopedTempDir temp_dir;
|
ScopedTempDir temp_dir;
|
||||||
|
|
||||||
|
@ -14,10 +14,12 @@
|
|||||||
|
|
||||||
#include "util/file/filesystem.h"
|
#include "util/file/filesystem.h"
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
|
#include "util/misc/time.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
@ -50,6 +52,35 @@ bool LoggingRemoveDirectoryImpl(const base::FilePath& path) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
bool FileModificationTime(const base::FilePath& path, timespec* mtime) {
|
||||||
|
DWORD flags = FILE_FLAG_OPEN_REPARSE_POINT;
|
||||||
|
if (IsDirectory(path, true)) {
|
||||||
|
// required for directory handles
|
||||||
|
flags |= FILE_FLAG_BACKUP_SEMANTICS;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopedFileHandle handle(
|
||||||
|
::CreateFile(path.value().c_str(),
|
||||||
|
GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||||||
|
nullptr,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
flags,
|
||||||
|
nullptr));
|
||||||
|
if (!handle.is_valid()) {
|
||||||
|
PLOG(ERROR) << "CreateFile " << base::UTF16ToUTF8(path.value());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILETIME file_mtime;
|
||||||
|
if (!GetFileTime(handle.get(), nullptr, nullptr, &file_mtime)) {
|
||||||
|
PLOG(ERROR) << "GetFileTime " << base::UTF16ToUTF8(path.value());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*mtime = FiletimeToTimespecEpoch(file_mtime);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool LoggingCreateDirectory(const base::FilePath& path,
|
bool LoggingCreateDirectory(const base::FilePath& path,
|
||||||
FilePermissions permissions,
|
FilePermissions permissions,
|
||||||
bool may_reuse) {
|
bool may_reuse) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user