2023-01-28 04:54:20 +00:00
|
|
|
// Copyright 2017 The Crashpad Authors
|
2022-04-02 01:21:55 +00:00
|
|
|
//
|
|
|
|
// 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/file/directory_reader.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::WideToUTF8(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::WideToUTF8(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::WideToUTF8(path.value());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
FILETIME file_mtime;
|
|
|
|
if (!GetFileTime(handle.get(), nullptr, nullptr, &file_mtime)) {
|
|
|
|
PLOG(ERROR) << "GetFileTime " << base::WideToUTF8(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::WideToUTF8(path.value()) << " not a directory";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
PLOG(ERROR) << "CreateDirectory " << base::WideToUTF8(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::WideToUTF8(source.value()) << ", "
|
|
|
|
<< base::WideToUTF8(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::WideToUTF8(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::WideToUTF8(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::WideToUTF8(path.value());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LoggingRemoveDirectory(const base::FilePath& path) {
|
|
|
|
if (IsSymbolicLink(path)) {
|
|
|
|
LOG(ERROR) << "Not a directory " << base::WideToUTF8(path.value());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return LoggingRemoveDirectoryImpl(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t GetFileSize(const base::FilePath& filepath) {
|
|
|
|
struct _stati64 statbuf;
|
|
|
|
if (!IsRegularFile(filepath)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int ret_value = _wstat64(filepath.value().c_str(), &statbuf);
|
|
|
|
if (ret_value == 0) {
|
|
|
|
return statbuf.st_size;
|
|
|
|
}
|
|
|
|
PLOG(ERROR) << "stat " << filepath.value().c_str();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t GetDirectorySize(const base::FilePath& dirpath) {
|
|
|
|
if (!IsDirectory(dirpath, /*allow_symlinks=*/false)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
DirectoryReader reader;
|
|
|
|
if (!reader.Open(dirpath)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
base::FilePath filename;
|
|
|
|
DirectoryReader::Result result;
|
|
|
|
uint64_t size = 0;
|
|
|
|
while ((result = reader.NextFile(&filename)) ==
|
|
|
|
DirectoryReader::Result::kSuccess) {
|
|
|
|
const base::FilePath filepath(dirpath.Append(filename));
|
|
|
|
if (IsDirectory(filepath, /*allow_symlinks=*/false)) {
|
|
|
|
size += GetDirectorySize(filepath);
|
|
|
|
} else {
|
|
|
|
size += GetFileSize(filepath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace crashpad
|