crashpad/util/file/filesystem_win.cc
Joshua Peraza 6f6f8a144d 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>
2017-11-09 06:27:46 +00:00

163 lines
5.2 KiB
C++

// Copyright 2017 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/file/filesystem.h"
#include <sys/time.h>
#include <windows.h>
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "util/misc/time.h"
namespace crashpad {
namespace {
bool IsSymbolicLink(const base::FilePath& path) {
WIN32_FIND_DATA find_data;
ScopedSearchHANDLE handle(FindFirstFileEx(path.value().c_str(),
FindExInfoBasic,
&find_data,
FindExSearchNameMatch,
nullptr,
0));
if (!handle.is_valid()) {
PLOG(ERROR) << "FindFirstFileEx " << base::UTF16ToUTF8(path.value());
return false;
}
return (find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0 &&
find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK;
}
bool LoggingRemoveDirectoryImpl(const base::FilePath& path) {
if (!RemoveDirectory(path.value().c_str())) {
PLOG(ERROR) << "RemoveDirectory " << base::UTF16ToUTF8(path.value());
return false;
}
return true;
}
} // 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,
FilePermissions permissions,
bool may_reuse) {
if (CreateDirectory(path.value().c_str(), nullptr)) {
return true;
}
if (may_reuse && GetLastError() == ERROR_ALREADY_EXISTS) {
if (!IsDirectory(path, true)) {
LOG(ERROR) << base::UTF16ToUTF8(path.value()) << " not a directory";
return false;
}
return true;
}
PLOG(ERROR) << "CreateDirectory " << base::UTF16ToUTF8(path.value());
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) {
DWORD fileattr = GetFileAttributes(path.value().c_str());
if (fileattr == INVALID_FILE_ATTRIBUTES) {
PLOG(ERROR) << "GetFileAttributes " << base::UTF16ToUTF8(path.value());
return false;
}
if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0 ||
(fileattr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
return false;
}
return true;
}
bool IsDirectory(const base::FilePath& path, bool allow_symlinks) {
DWORD fileattr = GetFileAttributes(path.value().c_str());
if (fileattr == INVALID_FILE_ATTRIBUTES) {
PLOG(ERROR) << "GetFileAttributes " << base::UTF16ToUTF8(path.value());
return false;
}
if (!allow_symlinks && (fileattr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
return false;
}
return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
bool LoggingRemoveFile(const base::FilePath& path) {
// RemoveDirectory is used if the file is a symbolic link to a directory.
DWORD fileattr = GetFileAttributes(path.value().c_str());
if (fileattr != INVALID_FILE_ATTRIBUTES &&
(fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0 &&
(fileattr & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
return LoggingRemoveDirectoryImpl(path);
}
if (!DeleteFile(path.value().c_str())) {
PLOG(ERROR) << "DeleteFile " << base::UTF16ToUTF8(path.value());
return false;
}
return true;
}
bool LoggingRemoveDirectory(const base::FilePath& path) {
if (IsSymbolicLink(path)) {
LOG(ERROR) << "Not a directory " << base::UTF16ToUTF8(path.value());
return false;
}
return LoggingRemoveDirectoryImpl(path);
}
} // namespace crashpad