mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-19 18:03:47 +00:00
Add DirectoryReader to iterate over files in a directory
This change also adds functions to create directories, remove files and directories, and check for the existence of files and directories. Change-Id: I62b78219ae2b277d6976d2d90ec86fcabd0ef073 Reviewed-on: https://chromium-review.googlesource.com/696132 Commit-Queue: Joshua Peraza <jperaza@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
906fce1d01
commit
474c7331a6
@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
#include "snapshot/linux/process_reader.h"
|
#include "snapshot/linux/process_reader.h"
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -27,8 +26,9 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/strings/string_number_conversions.h"
|
#include "base/strings/string_number_conversions.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
#include "util/file/directory_reader.h"
|
||||||
#include "util/linux/proc_stat_reader.h"
|
#include "util/linux/proc_stat_reader.h"
|
||||||
#include "util/posix/scoped_dir.h"
|
#include "util/misc/as_underlying_type.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
@ -264,15 +264,6 @@ void ProcessReader::InitializeThreads() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
char path[32];
|
|
||||||
snprintf(path, arraysize(path), "/proc/%d/task", pid);
|
|
||||||
DIR* dir = opendir(path);
|
|
||||||
if (!dir) {
|
|
||||||
PLOG(ERROR) << "opendir";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ScopedDIR scoped_dir(dir);
|
|
||||||
|
|
||||||
Thread main_thread;
|
Thread main_thread;
|
||||||
main_thread.tid = pid;
|
main_thread.tid = pid;
|
||||||
if (main_thread.InitializePtrace(connection_)) {
|
if (main_thread.InitializePtrace(connection_)) {
|
||||||
@ -282,15 +273,19 @@ void ProcessReader::InitializeThreads() {
|
|||||||
LOG(WARNING) << "Couldn't initialize main thread.";
|
LOG(WARNING) << "Couldn't initialize main thread.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char path[32];
|
||||||
|
snprintf(path, arraysize(path), "/proc/%d/task", pid);
|
||||||
bool main_thread_found = false;
|
bool main_thread_found = false;
|
||||||
dirent* dir_entry;
|
DirectoryReader reader;
|
||||||
while ((dir_entry = readdir(scoped_dir.get()))) {
|
if (!reader.Open(base::FilePath(path))) {
|
||||||
if (strncmp(dir_entry->d_name, ".", arraysize(dir_entry->d_name)) == 0 ||
|
return;
|
||||||
strncmp(dir_entry->d_name, "..", arraysize(dir_entry->d_name)) == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
base::FilePath tid_str;
|
||||||
|
DirectoryReader::Result result;
|
||||||
|
while ((result = reader.NextFile(&tid_str)) ==
|
||||||
|
DirectoryReader::Result::kSuccess) {
|
||||||
pid_t tid;
|
pid_t tid;
|
||||||
if (!base::StringToInt(dir_entry->d_name, &tid)) {
|
if (!base::StringToInt(tid_str.value(), &tid)) {
|
||||||
LOG(ERROR) << "format error";
|
LOG(ERROR) << "format error";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -308,6 +303,8 @@ void ProcessReader::InitializeThreads() {
|
|||||||
threads_.push_back(thread);
|
threads_.push_back(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
DCHECK_EQ(AsUnderlyingType(result),
|
||||||
|
AsUnderlyingType(DirectoryReader::Result::kNoMoreFiles));
|
||||||
DCHECK(main_thread_found);
|
DCHECK(main_thread_found);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,6 +14,11 @@
|
|||||||
|
|
||||||
#include "test/scoped_temp_dir.h"
|
#include "test/scoped_temp_dir.h"
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "util/file/directory_reader.h"
|
||||||
|
#include "util/file/file_io.h"
|
||||||
|
#include "util/file/filesystem.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace test {
|
namespace test {
|
||||||
|
|
||||||
@ -24,5 +29,25 @@ ScopedTempDir::~ScopedTempDir() {
|
|||||||
RecursivelyDeleteTemporaryDirectory(path());
|
RecursivelyDeleteTemporaryDirectory(path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void ScopedTempDir::RecursivelyDeleteTemporaryDirectory(
|
||||||
|
const base::FilePath& path) {
|
||||||
|
DirectoryReader reader;
|
||||||
|
ASSERT_TRUE(reader.Open(path));
|
||||||
|
DirectoryReader::Result result;
|
||||||
|
base::FilePath entry;
|
||||||
|
while ((result = reader.NextFile(&entry)) ==
|
||||||
|
DirectoryReader::Result::kSuccess) {
|
||||||
|
const base::FilePath entry_path(path.Append(entry));
|
||||||
|
if (IsDirectory(entry_path, false)) {
|
||||||
|
RecursivelyDeleteTemporaryDirectory(entry_path);
|
||||||
|
} else {
|
||||||
|
EXPECT_TRUE(LoggingRemoveFile(entry_path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_EQ(result, DirectoryReader::Result::kNoMoreFiles);
|
||||||
|
EXPECT_TRUE(LoggingRemoveDirectory(path));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -56,32 +56,5 @@ base::FilePath ScopedTempDir::CreateTemporaryDirectory() {
|
|||||||
return base::FilePath(dir);
|
return base::FilePath(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
void ScopedTempDir::RecursivelyDeleteTemporaryDirectory(
|
|
||||||
const base::FilePath& path) {
|
|
||||||
DIR* dir = opendir(path.value().c_str());
|
|
||||||
ASSERT_TRUE(dir) << ErrnoMessage("opendir") << " " << path.value();
|
|
||||||
|
|
||||||
dirent* entry;
|
|
||||||
while ((entry = readdir(dir))) {
|
|
||||||
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
base::FilePath entry_path = path.Append(entry->d_name);
|
|
||||||
if (entry->d_type == DT_DIR) {
|
|
||||||
RecursivelyDeleteTemporaryDirectory(entry_path);
|
|
||||||
} else {
|
|
||||||
EXPECT_EQ(unlink(entry_path.value().c_str()), 0)
|
|
||||||
<< ErrnoMessage("unlink") << " " << entry_path.value();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
EXPECT_EQ(closedir(dir), 0) << ErrnoMessage("closedir") << " "
|
|
||||||
<< path.value();
|
|
||||||
EXPECT_EQ(rmdir(path.value().c_str()), 0) << ErrnoMessage("rmdir") << " "
|
|
||||||
<< path.value();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -72,33 +72,5 @@ base::FilePath ScopedTempDir::CreateTemporaryDirectory() {
|
|||||||
return base::FilePath();
|
return base::FilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
|
||||||
void ScopedTempDir::RecursivelyDeleteTemporaryDirectory(
|
|
||||||
const base::FilePath& path) {
|
|
||||||
const base::string16 all_files_mask(L"\\*");
|
|
||||||
|
|
||||||
base::string16 search_mask = path.value() + all_files_mask;
|
|
||||||
WIN32_FIND_DATA find_data;
|
|
||||||
HANDLE search_handle = FindFirstFile(search_mask.c_str(), &find_data);
|
|
||||||
if (search_handle == INVALID_HANDLE_VALUE)
|
|
||||||
ASSERT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND);
|
|
||||||
do {
|
|
||||||
if (wcscmp(find_data.cFileName, L".") == 0 ||
|
|
||||||
wcscmp(find_data.cFileName, L"..") == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
base::FilePath entry_path = path.Append(find_data.cFileName);
|
|
||||||
ASSERT_FALSE(find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT);
|
|
||||||
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
||||||
RecursivelyDeleteTemporaryDirectory(entry_path);
|
|
||||||
else
|
|
||||||
EXPECT_TRUE(DeleteFile(entry_path.value().c_str()));
|
|
||||||
} while (FindNextFile(search_handle, &find_data));
|
|
||||||
EXPECT_EQ(GetLastError(), ERROR_NO_MORE_FILES);
|
|
||||||
|
|
||||||
EXPECT_TRUE(FindClose(search_handle));
|
|
||||||
EXPECT_TRUE(RemoveDirectory(path.value().c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
87
util/file/directory_reader.h
Normal file
87
util/file/directory_reader.h
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef CRASHPAD_UTIL_FILE_DIRECTORY_READER_H_
|
||||||
|
#define CRASHPAD_UTIL_FILE_DIRECTORY_READER_H_
|
||||||
|
|
||||||
|
#include "base/files/file_path.h"
|
||||||
|
#include "base/macros.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
|
||||||
|
#if defined(OS_POSIX)
|
||||||
|
#include "util/posix/scoped_dir.h"
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#include "util/win/scoped_handle.h"
|
||||||
|
#endif // OS_POSIX
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
//! \brief Iterates over the file and directory names in a directory.
|
||||||
|
//!
|
||||||
|
//! The names enumerated are relative to the specified directory and do not
|
||||||
|
//! include ".", "..", or files and directories in subdirectories.
|
||||||
|
class DirectoryReader {
|
||||||
|
public:
|
||||||
|
//! \brief The result of a call to NextFile().
|
||||||
|
enum class Result {
|
||||||
|
//! \brief An error occurred and a message was logged.
|
||||||
|
kError = -1,
|
||||||
|
|
||||||
|
//! \brief A file was found.
|
||||||
|
kSuccess,
|
||||||
|
|
||||||
|
//! \brief No more files were found.
|
||||||
|
kNoMoreFiles,
|
||||||
|
};
|
||||||
|
|
||||||
|
DirectoryReader();
|
||||||
|
~DirectoryReader();
|
||||||
|
|
||||||
|
//! \brief Opens the directory specified by \a path for reading.
|
||||||
|
//!
|
||||||
|
//! \param[in] path The path to the directory to read.
|
||||||
|
//! \return `true` on success. `false` on failure with a message logged.
|
||||||
|
bool Open(const base::FilePath& path);
|
||||||
|
|
||||||
|
//! \brief Advances the reader to the next file in the directory.
|
||||||
|
//!
|
||||||
|
//! \param[out] filename The filename of the next file.
|
||||||
|
//! \return a #Result value. \a filename is only valid when Result::kSuccess
|
||||||
|
//! is returned. If Result::kError is returned, a message will be
|
||||||
|
//! logged.
|
||||||
|
Result NextFile(base::FilePath* filename);
|
||||||
|
|
||||||
|
#if defined(OS_POSIX) || DOXYGEN
|
||||||
|
//! \brief Returns the file descriptor associated with this reader, logging a
|
||||||
|
//! message and returning -1 on error.
|
||||||
|
int DirectoryFD();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
#if defined(OS_POSIX)
|
||||||
|
ScopedDIR dir_;
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
WIN32_FIND_DATA find_data_;
|
||||||
|
ScopedSearchHANDLE handle_;
|
||||||
|
bool first_entry_;
|
||||||
|
#endif // OS_POSIX
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(DirectoryReader);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_FILE_DIRECTORY_READER_H_
|
79
util/file/directory_reader_posix.cc
Normal file
79
util/file/directory_reader_posix.cc
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
// 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/directory_reader.h"
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
#define HANDLE_EINTR_IF_EQ(x, val) \
|
||||||
|
({ \
|
||||||
|
decltype(x) eintr_wrapper_result; \
|
||||||
|
do { \
|
||||||
|
eintr_wrapper_result = (x); \
|
||||||
|
} while (eintr_wrapper_result == (val) && errno == EINTR); \
|
||||||
|
eintr_wrapper_result; \
|
||||||
|
})
|
||||||
|
|
||||||
|
DirectoryReader::DirectoryReader() : dir_() {}
|
||||||
|
|
||||||
|
DirectoryReader::~DirectoryReader() {}
|
||||||
|
|
||||||
|
bool DirectoryReader::Open(const base::FilePath& path) {
|
||||||
|
dir_.reset(HANDLE_EINTR_IF_EQ(opendir(path.value().c_str()), nullptr));
|
||||||
|
if (!dir_.is_valid()) {
|
||||||
|
PLOG(ERROR) << "opendir";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryReader::Result DirectoryReader::NextFile(base::FilePath* filename) {
|
||||||
|
DCHECK(dir_.is_valid());
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
dirent* entry = HANDLE_EINTR_IF_EQ(readdir(dir_.get()), nullptr);
|
||||||
|
if (!entry) {
|
||||||
|
if (errno) {
|
||||||
|
PLOG(ERROR) << "readdir";
|
||||||
|
return Result::kError;
|
||||||
|
} else {
|
||||||
|
return Result::kNoMoreFiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
|
||||||
|
return NextFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
*filename = base::FilePath(entry->d_name);
|
||||||
|
return Result::kSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
int DirectoryReader::DirectoryFD() {
|
||||||
|
DCHECK(dir_.is_valid());
|
||||||
|
int rv = dirfd(dir_.get());
|
||||||
|
if (rv < 0) {
|
||||||
|
PLOG(ERROR) << "dirfd";
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crashpad
|
118
util/file/directory_reader_test.cc
Normal file
118
util/file/directory_reader_test.cc
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
// 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/directory_reader.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "base/files/file_path.h"
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/strings/stringprintf.h"
|
||||||
|
#include "base/strings/utf_string_conversions.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "test/scoped_temp_dir.h"
|
||||||
|
#include "util/file/file_io.h"
|
||||||
|
#include "util/file/filesystem.h"
|
||||||
|
#include "util/file/filesystem_test_util.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void ExpectFiles(const std::set<base::FilePath>& files,
|
||||||
|
const std::set<base::FilePath>& expected) {
|
||||||
|
EXPECT_EQ(files.size(), expected.size());
|
||||||
|
|
||||||
|
for (const auto& filename : expected) {
|
||||||
|
SCOPED_TRACE(
|
||||||
|
base::StringPrintf("Filename: %" PRFilePath, filename.value().c_str()));
|
||||||
|
EXPECT_NE(files.find(filename), files.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DirectoryReader, BadPaths) {
|
||||||
|
DirectoryReader reader;
|
||||||
|
EXPECT_FALSE(reader.Open(base::FilePath()));
|
||||||
|
|
||||||
|
ScopedTempDir temp_dir;
|
||||||
|
base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
|
||||||
|
ASSERT_TRUE(CreateFile(file));
|
||||||
|
EXPECT_FALSE(reader.Open(file));
|
||||||
|
|
||||||
|
base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link")));
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(file, link));
|
||||||
|
EXPECT_FALSE(reader.Open(link));
|
||||||
|
|
||||||
|
ASSERT_TRUE(LoggingRemoveFile(file));
|
||||||
|
EXPECT_FALSE(reader.Open(link));
|
||||||
|
|
||||||
|
EXPECT_FALSE(
|
||||||
|
reader.Open(temp_dir.path().Append(FILE_PATH_LITERAL("doesntexist"))));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DirectoryReader, EmptyDirectory) {
|
||||||
|
ScopedTempDir temp_dir;
|
||||||
|
DirectoryReader reader;
|
||||||
|
|
||||||
|
ASSERT_TRUE(reader.Open(temp_dir.path()));
|
||||||
|
base::FilePath filename;
|
||||||
|
EXPECT_EQ(reader.NextFile(&filename), DirectoryReader::Result::kNoMoreFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DirectoryReader, FilesAndDirectories) {
|
||||||
|
ScopedTempDir temp_dir;
|
||||||
|
std::set<base::FilePath> expected_files;
|
||||||
|
|
||||||
|
base::FilePath file(FILE_PATH_LITERAL("file"));
|
||||||
|
ASSERT_TRUE(CreateFile(temp_dir.path().Append(file)));
|
||||||
|
EXPECT_TRUE(expected_files.insert(file).second);
|
||||||
|
|
||||||
|
base::FilePath link(FILE_PATH_LITERAL("link"));
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(temp_dir.path().Append(file),
|
||||||
|
temp_dir.path().Append(link)));
|
||||||
|
EXPECT_TRUE(expected_files.insert(link).second);
|
||||||
|
|
||||||
|
base::FilePath dangling(FILE_PATH_LITERAL("dangling"));
|
||||||
|
ASSERT_TRUE(
|
||||||
|
CreateSymbolicLink(base::FilePath(FILE_PATH_LITERAL("not_a_file")),
|
||||||
|
temp_dir.path().Append(dangling)));
|
||||||
|
EXPECT_TRUE(expected_files.insert(dangling).second);
|
||||||
|
|
||||||
|
base::FilePath directory(FILE_PATH_LITERAL("directory"));
|
||||||
|
ASSERT_TRUE(LoggingCreateDirectory(temp_dir.path().Append(directory),
|
||||||
|
FilePermissions::kWorldReadable,
|
||||||
|
false));
|
||||||
|
EXPECT_TRUE(expected_files.insert(directory).second);
|
||||||
|
|
||||||
|
base::FilePath nested_file(FILE_PATH_LITERAL("nested_file"));
|
||||||
|
ASSERT_TRUE(
|
||||||
|
CreateFile(temp_dir.path().Append(directory).Append(nested_file)));
|
||||||
|
|
||||||
|
std::set<base::FilePath> files;
|
||||||
|
DirectoryReader reader;
|
||||||
|
ASSERT_TRUE(reader.Open(temp_dir.path()));
|
||||||
|
DirectoryReader::Result result;
|
||||||
|
base::FilePath filename;
|
||||||
|
while ((result = reader.NextFile(&filename)) ==
|
||||||
|
DirectoryReader::Result::kSuccess) {
|
||||||
|
EXPECT_TRUE(files.insert(filename).second);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(result, DirectoryReader::Result::kNoMoreFiles);
|
||||||
|
EXPECT_EQ(reader.NextFile(&filename), DirectoryReader::Result::kNoMoreFiles);
|
||||||
|
ExpectFiles(files, expected_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
76
util/file/directory_reader_win.cc
Normal file
76
util/file/directory_reader_win.cc
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// 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/directory_reader.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
DirectoryReader::DirectoryReader()
|
||||||
|
: find_data_(), handle_(), first_entry_(false) {}
|
||||||
|
|
||||||
|
DirectoryReader::~DirectoryReader() {}
|
||||||
|
|
||||||
|
bool DirectoryReader::Open(const base::FilePath& path) {
|
||||||
|
if (path.empty()) {
|
||||||
|
LOG(ERROR) << "Empty directory path";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_.reset(
|
||||||
|
FindFirstFileEx(path.Append(FILE_PATH_LITERAL("*")).value().c_str(),
|
||||||
|
FindExInfoBasic,
|
||||||
|
&find_data_,
|
||||||
|
FindExSearchNameMatch,
|
||||||
|
nullptr,
|
||||||
|
FIND_FIRST_EX_LARGE_FETCH));
|
||||||
|
|
||||||
|
if (!handle_.is_valid()) {
|
||||||
|
PLOG(ERROR) << "FindFirstFile";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
first_entry_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryReader::Result DirectoryReader::NextFile(base::FilePath* filename) {
|
||||||
|
DCHECK(handle_.is_valid());
|
||||||
|
|
||||||
|
if (!first_entry_) {
|
||||||
|
if (!FindNextFile(handle_.get(), &find_data_)) {
|
||||||
|
if (GetLastError() != ERROR_NO_MORE_FILES) {
|
||||||
|
PLOG(ERROR) << "FindNextFile";
|
||||||
|
return Result::kError;
|
||||||
|
} else {
|
||||||
|
return Result::kNoMoreFiles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
first_entry_ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wcscmp(find_data_.cFileName, L".") == 0 ||
|
||||||
|
wcscmp(find_data_.cFileName, L"..") == 0) {
|
||||||
|
return NextFile(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
*filename = base::FilePath(find_data_.cFileName);
|
||||||
|
return Result::kSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crashpad
|
78
util/file/filesystem.h
Normal file
78
util/file/filesystem.h
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef CRASHPAD_UTIL_FILE_FILESYSTEM_H_
|
||||||
|
#define CRASHPAD_UTIL_FILE_FILESYSTEM_H_
|
||||||
|
|
||||||
|
#include "base/files/file_path.h"
|
||||||
|
|
||||||
|
#include "util/file/file_io.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
//! \brief Creates a directory, logging a message on failure.
|
||||||
|
//!
|
||||||
|
//! \param[in] path The path to the directory to create.
|
||||||
|
//! \param[in] permissions The permissions to use if the directory is created.
|
||||||
|
//! \param[in] may_reuse If `true`, this function will return `true` if a
|
||||||
|
//! directory or symbolic link to a directory with path \a path already
|
||||||
|
//! exists. If the directory already exists, it's permissions may differ
|
||||||
|
//! from \a permissions.
|
||||||
|
//! \return `true` if the directory is successfully created or it already
|
||||||
|
//! existed and \a may_reuse is `true`. Otherwise, `false`.
|
||||||
|
bool LoggingCreateDirectory(const base::FilePath& path,
|
||||||
|
FilePermissions permissions,
|
||||||
|
bool may_reuse);
|
||||||
|
|
||||||
|
//! \brief Determines if a path refers to a regular file, logging a message on
|
||||||
|
//! failure.
|
||||||
|
//!
|
||||||
|
//! On POSIX, this function returns `true` if \a path refers to a file that is
|
||||||
|
//! not a symbolic link, directory, or other kind of special file.
|
||||||
|
//!
|
||||||
|
//! On Windows, this function returns `true` if \a path refers to a file that
|
||||||
|
//! is not a symbolic link or directory.
|
||||||
|
//!
|
||||||
|
//! \param[in] path The path to the file to check.
|
||||||
|
//! \return `true` if the file exists and is a regular file. Otherwise `false`.
|
||||||
|
bool IsRegularFile(const base::FilePath& path);
|
||||||
|
|
||||||
|
//! \brief Determines if a path refers to a directory, logging a message on
|
||||||
|
//! failure.
|
||||||
|
//!
|
||||||
|
//! \param[in] path The path to check.
|
||||||
|
//! \param[in] allow_symlinks Whether to allow the final component in the path
|
||||||
|
//! to be a symbolic link to a directory.
|
||||||
|
//! \return `true` if the path exists and is a directory. Otherwise `false`.
|
||||||
|
bool IsDirectory(const base::FilePath& path, bool allow_symlinks);
|
||||||
|
|
||||||
|
//! \brief Removes a file or a symbolic link to a file or directory, logging a
|
||||||
|
//! message on failure.
|
||||||
|
//!
|
||||||
|
//! \param[in] path The path to the file to remove.
|
||||||
|
//! \return `true` on success. `false` on failure with a message logged.
|
||||||
|
bool LoggingRemoveFile(const base::FilePath& path);
|
||||||
|
|
||||||
|
//! \brief Non-recurseively removes an empty directory, logging a message on
|
||||||
|
//! failure.
|
||||||
|
//!
|
||||||
|
//! This function will not remove symbolic links to directories.
|
||||||
|
//!
|
||||||
|
//! \param[in] path The to the directory to remove.
|
||||||
|
//! \return `true` if the directory was removed. Otherwise, `false`.
|
||||||
|
bool LoggingRemoveDirectory(const base::FilePath& path);
|
||||||
|
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_FILE_FILESYSTEM_H_
|
86
util/file/filesystem_posix.cc
Normal file
86
util/file/filesystem_posix.cc
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
// 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 <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
bool LoggingCreateDirectory(const base::FilePath& path,
|
||||||
|
FilePermissions permissions,
|
||||||
|
bool may_reuse) {
|
||||||
|
if (mkdir(path.value().c_str(),
|
||||||
|
permissions == FilePermissions::kWorldReadable ? 0755 : 0700) ==
|
||||||
|
0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (may_reuse && errno == EEXIST) {
|
||||||
|
if (!IsDirectory(path, true)) {
|
||||||
|
LOG(ERROR) << path.value() << " not a directory";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
PLOG(ERROR) << "mkdir " << path.value();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRegularFile(const base::FilePath& path) {
|
||||||
|
struct stat st;
|
||||||
|
if (lstat(path.value().c_str(), &st) != 0) {
|
||||||
|
PLOG_IF(ERROR, errno != ENOENT) << "stat " << path.value();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return S_ISREG(st.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsDirectory(const base::FilePath& path, bool allow_symlinks) {
|
||||||
|
struct stat st;
|
||||||
|
if (allow_symlinks) {
|
||||||
|
if (stat(path.value().c_str(), &st) != 0) {
|
||||||
|
PLOG(ERROR) << "stat " << path.value();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (lstat(path.value().c_str(), &st) != 0) {
|
||||||
|
PLOG(ERROR) << "lstat " << path.value();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return S_ISDIR(st.st_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoggingRemoveFile(const base::FilePath& path) {
|
||||||
|
if (unlink(path.value().c_str()) != 0) {
|
||||||
|
PLOG(ERROR) << "unlink " << path.value();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LoggingRemoveDirectory(const base::FilePath& path) {
|
||||||
|
if (rmdir(path.value().c_str()) != 0) {
|
||||||
|
PLOG(ERROR) << "rmdir " << path.value();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crashpad
|
168
util/file/filesystem_test.cc
Normal file
168
util/file/filesystem_test.cc
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
// 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 "base/logging.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "test/file.h"
|
||||||
|
#include "test/scoped_temp_dir.h"
|
||||||
|
#include "util/file/filesystem_test_util.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
TEST(Filesystem, CreateDirectory) {
|
||||||
|
ScopedTempDir temp_dir;
|
||||||
|
|
||||||
|
base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir")));
|
||||||
|
EXPECT_FALSE(IsDirectory(dir, false));
|
||||||
|
|
||||||
|
ASSERT_TRUE(
|
||||||
|
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));
|
||||||
|
EXPECT_TRUE(IsDirectory(dir, false));
|
||||||
|
|
||||||
|
EXPECT_FALSE(
|
||||||
|
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));
|
||||||
|
|
||||||
|
base::FilePath file(dir.Append(FILE_PATH_LITERAL("file")));
|
||||||
|
ASSERT_TRUE(CreateFile(file));
|
||||||
|
|
||||||
|
EXPECT_TRUE(
|
||||||
|
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, true));
|
||||||
|
EXPECT_TRUE(IsRegularFile(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Filesystem, IsRegularFile) {
|
||||||
|
EXPECT_FALSE(IsRegularFile(base::FilePath()));
|
||||||
|
|
||||||
|
ScopedTempDir temp_dir;
|
||||||
|
EXPECT_FALSE(IsRegularFile(temp_dir.path()));
|
||||||
|
|
||||||
|
base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
|
||||||
|
EXPECT_FALSE(IsRegularFile(file));
|
||||||
|
|
||||||
|
ASSERT_TRUE(CreateFile(file));
|
||||||
|
EXPECT_TRUE(IsRegularFile(file));
|
||||||
|
|
||||||
|
base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link")));
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(file, link));
|
||||||
|
EXPECT_FALSE(IsRegularFile(link));
|
||||||
|
|
||||||
|
ASSERT_TRUE(LoggingRemoveFile(file));
|
||||||
|
EXPECT_FALSE(IsRegularFile(link));
|
||||||
|
|
||||||
|
base::FilePath dir_link(
|
||||||
|
temp_dir.path().Append((FILE_PATH_LITERAL("dir_link"))));
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(temp_dir.path(), dir_link));
|
||||||
|
EXPECT_FALSE(IsRegularFile(dir_link));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Filesystem, IsDirectory) {
|
||||||
|
EXPECT_FALSE(IsDirectory(base::FilePath(), false));
|
||||||
|
EXPECT_FALSE(IsDirectory(base::FilePath(), true));
|
||||||
|
|
||||||
|
ScopedTempDir temp_dir;
|
||||||
|
EXPECT_TRUE(IsDirectory(temp_dir.path(), false));
|
||||||
|
|
||||||
|
base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
|
||||||
|
EXPECT_FALSE(IsDirectory(file, false));
|
||||||
|
EXPECT_FALSE(IsDirectory(file, true));
|
||||||
|
|
||||||
|
ASSERT_TRUE(CreateFile(file));
|
||||||
|
EXPECT_FALSE(IsDirectory(file, false));
|
||||||
|
EXPECT_FALSE(IsDirectory(file, true));
|
||||||
|
|
||||||
|
base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link")));
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(file, link));
|
||||||
|
EXPECT_FALSE(IsDirectory(link, false));
|
||||||
|
EXPECT_FALSE(IsDirectory(link, true));
|
||||||
|
|
||||||
|
ASSERT_TRUE(LoggingRemoveFile(file));
|
||||||
|
EXPECT_FALSE(IsDirectory(link, false));
|
||||||
|
EXPECT_FALSE(IsDirectory(link, true));
|
||||||
|
|
||||||
|
base::FilePath dir_link(
|
||||||
|
temp_dir.path().Append(FILE_PATH_LITERAL("dir_link")));
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(temp_dir.path(), dir_link));
|
||||||
|
EXPECT_FALSE(IsDirectory(dir_link, false));
|
||||||
|
EXPECT_TRUE(IsDirectory(dir_link, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Filesystem, RemoveFile) {
|
||||||
|
EXPECT_FALSE(LoggingRemoveFile(base::FilePath()));
|
||||||
|
|
||||||
|
ScopedTempDir temp_dir;
|
||||||
|
EXPECT_FALSE(LoggingRemoveFile(temp_dir.path()));
|
||||||
|
|
||||||
|
base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
|
||||||
|
EXPECT_FALSE(LoggingRemoveFile(file));
|
||||||
|
|
||||||
|
ASSERT_TRUE(CreateFile(file));
|
||||||
|
EXPECT_TRUE(IsRegularFile(file));
|
||||||
|
|
||||||
|
base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link")));
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(file, link));
|
||||||
|
EXPECT_TRUE(LoggingRemoveFile(link));
|
||||||
|
EXPECT_FALSE(FileExists(link));
|
||||||
|
EXPECT_TRUE(FileExists(file));
|
||||||
|
|
||||||
|
base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir")));
|
||||||
|
ASSERT_TRUE(
|
||||||
|
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));
|
||||||
|
EXPECT_FALSE(LoggingRemoveFile(dir));
|
||||||
|
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(dir, link));
|
||||||
|
EXPECT_TRUE(LoggingRemoveFile(link));
|
||||||
|
EXPECT_FALSE(FileExists(link));
|
||||||
|
EXPECT_TRUE(FileExists(dir));
|
||||||
|
|
||||||
|
EXPECT_TRUE(LoggingRemoveFile(file));
|
||||||
|
EXPECT_FALSE(IsRegularFile(file));
|
||||||
|
EXPECT_FALSE(LoggingRemoveFile(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(Filesystem, RemoveDirectory) {
|
||||||
|
EXPECT_FALSE(LoggingRemoveDirectory(base::FilePath()));
|
||||||
|
|
||||||
|
ScopedTempDir temp_dir;
|
||||||
|
|
||||||
|
base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir")));
|
||||||
|
ASSERT_TRUE(
|
||||||
|
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));
|
||||||
|
|
||||||
|
base::FilePath file(dir.Append(FILE_PATH_LITERAL("file")));
|
||||||
|
EXPECT_FALSE(LoggingRemoveDirectory(file));
|
||||||
|
|
||||||
|
ASSERT_TRUE(CreateFile(file));
|
||||||
|
EXPECT_FALSE(LoggingRemoveDirectory(file));
|
||||||
|
EXPECT_FALSE(LoggingRemoveDirectory(dir));
|
||||||
|
|
||||||
|
base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link")));
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(file, link));
|
||||||
|
EXPECT_FALSE(LoggingRemoveDirectory(link));
|
||||||
|
EXPECT_TRUE(LoggingRemoveFile(link));
|
||||||
|
|
||||||
|
ASSERT_TRUE(CreateSymbolicLink(dir, link));
|
||||||
|
EXPECT_FALSE(LoggingRemoveDirectory(link));
|
||||||
|
EXPECT_TRUE(LoggingRemoveFile(link));
|
||||||
|
|
||||||
|
ASSERT_TRUE(LoggingRemoveFile(file));
|
||||||
|
EXPECT_TRUE(LoggingRemoveDirectory(dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
63
util/file/filesystem_test_util.cc
Normal file
63
util/file/filesystem_test_util.cc
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// 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_test_util.h"
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "util/file/file_io.h"
|
||||||
|
#include "util/file/filesystem.h"
|
||||||
|
|
||||||
|
#if defined(OS_POSIX)
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "base/posix/eintr_wrapper.h"
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
#include <windows.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
bool CreateFile(const base::FilePath& file) {
|
||||||
|
ScopedFileHandle fd(LoggingOpenFileForWrite(
|
||||||
|
file, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly));
|
||||||
|
EXPECT_TRUE(fd.is_valid());
|
||||||
|
return fd.is_valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CreateSymbolicLink(const base::FilePath& target_path,
|
||||||
|
const base::FilePath& symlink_path) {
|
||||||
|
#if defined(OS_POSIX)
|
||||||
|
int rv = HANDLE_EINTR(
|
||||||
|
symlink(target_path.value().c_str(), symlink_path.value().c_str()));
|
||||||
|
if (rv != 0) {
|
||||||
|
PLOG(ERROR) << "symlink";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#elif defined(OS_WIN)
|
||||||
|
if (!::CreateSymbolicLink(
|
||||||
|
symlink_path.value().c_str(),
|
||||||
|
target_path.value().c_str(),
|
||||||
|
IsDirectory(target_path, true) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0)) {
|
||||||
|
PLOG(ERROR) << "CreateSymbolicLink";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif // OS_POSIX
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
31
util/file/filesystem_test_util.h
Normal file
31
util/file/filesystem_test_util.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef CRASHPAD_UTIL_FILE_FILESYSTEM_TEST_UTIL_H_
|
||||||
|
#define CRASHPAD_UTIL_FILE_FILESYSTEM_TEST_UTIL_H_
|
||||||
|
|
||||||
|
#include "base/files/file_path.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
|
||||||
|
bool CreateFile(const base::FilePath& file);
|
||||||
|
|
||||||
|
bool CreateSymbolicLink(const base::FilePath& target_path,
|
||||||
|
const base::FilePath& symlink_path);
|
||||||
|
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_FILE_FILESYSTEM_TEST_UTIL_H_
|
120
util/file/filesystem_win.cc
Normal file
120
util/file/filesystem_win.cc
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// 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 <windows.h>
|
||||||
|
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/strings/utf_string_conversions.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 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 IsRegularFile(const base::FilePath& path) {
|
||||||
|
DWORD fileattr = GetFileAttributes(path.value().c_str());
|
||||||
|
if (fileattr == INVALID_FILE_ATTRIBUTES) {
|
||||||
|
PLOG_IF(ERROR, GetLastError() != ERROR_FILE_NOT_FOUND)
|
||||||
|
<< "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
|
33
util/misc/as_underlying_type.h
Normal file
33
util/misc/as_underlying_type.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
#ifndef CRASHPAD_UTIL_MISC_AS_UNDERLYING_TYPE_H_
|
||||||
|
#define CRASHPAD_UTIL_MISC_AS_UNDERLYING_TYPE_H_
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
//! \brief Casts a value to its underlying type.
|
||||||
|
//!
|
||||||
|
//! \param[in] from The value to be casted.
|
||||||
|
//! \return \a from casted to its underlying type.
|
||||||
|
template <typename From>
|
||||||
|
typename std::underlying_type<From>::type AsUnderlyingType(From from) {
|
||||||
|
return static_cast<typename std::underlying_type<From>::type>(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_MISC_AS_UNDERLYING_TYPE_H_
|
@ -14,13 +14,10 @@
|
|||||||
|
|
||||||
#include "util/posix/close_multiple.h"
|
#include "util/posix/close_multiple.h"
|
||||||
|
|
||||||
#include <dirent.h>
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
@ -28,10 +25,10 @@
|
|||||||
#include "base/files/scoped_file.h"
|
#include "base/files/scoped_file.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "base/posix/eintr_wrapper.h"
|
#include "base/posix/eintr_wrapper.h"
|
||||||
|
#include "base/strings/string_number_conversions.h"
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
#include "util/file/directory_reader.h"
|
||||||
#include "util/misc/implicit_cast.h"
|
#include "util/misc/implicit_cast.h"
|
||||||
#include "util/numeric/safe_assignment.h"
|
|
||||||
#include "util/posix/scoped_dir.h"
|
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
@ -73,54 +70,35 @@ void CloseNowOrOnExec(int fd, bool ebadf_ok) {
|
|||||||
// system-specific FD directory to determine which file descriptors are open.
|
// system-specific FD directory to determine which file descriptors are open.
|
||||||
// This is an advantage over looping over all possible file descriptors, because
|
// This is an advantage over looping over all possible file descriptors, because
|
||||||
// no attempt needs to be made to close file descriptors that are not open.
|
// no attempt needs to be made to close file descriptors that are not open.
|
||||||
bool CloseMultipleNowOrOnExecUsingFDDir(int fd, int preserve_fd) {
|
bool CloseMultipleNowOrOnExecUsingFDDir(int min_fd, int preserve_fd) {
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
static constexpr char kFDDir[] = "/dev/fd";
|
static constexpr char kFDDir[] = "/dev/fd";
|
||||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||||
static constexpr char kFDDir[] = "/proc/self/fd";
|
static constexpr char kFDDir[] = "/proc/self/fd";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DIR* dir = opendir(kFDDir);
|
DirectoryReader reader;
|
||||||
if (!dir) {
|
if (!reader.Open(base::FilePath(kFDDir))) {
|
||||||
PLOG(WARNING) << "opendir";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScopedDIR dir_owner(dir);
|
|
||||||
|
|
||||||
int dir_fd = dirfd(dir);
|
|
||||||
if (dir_fd == -1) {
|
|
||||||
PLOG(WARNING) << "dirfd";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dirent* entry;
|
|
||||||
while ((errno = 0, entry = readdir(dir)) != nullptr) {
|
|
||||||
const char* entry_name = entry->d_name;
|
|
||||||
if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* end;
|
|
||||||
long entry_fd_long = strtol(entry_name, &end, 10);
|
|
||||||
if (entry_name[0] == '\0' || *end) {
|
|
||||||
LOG(ERROR) << "unexpected entry " << entry_name;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
int directory_fd = reader.DirectoryFD();
|
||||||
|
DCHECK_GE(directory_fd, 0);
|
||||||
|
|
||||||
|
base::FilePath entry_fd_path;
|
||||||
|
DirectoryReader::Result result;
|
||||||
|
while ((result = reader.NextFile(&entry_fd_path)) ==
|
||||||
|
DirectoryReader::Result::kSuccess) {
|
||||||
int entry_fd;
|
int entry_fd;
|
||||||
if (!AssignIfInRange(&entry_fd, entry_fd_long)) {
|
if (!base::StringToInt(entry_fd_path.value(), &entry_fd)) {
|
||||||
LOG(ERROR) << "out-of-range fd " << entry_name;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entry_fd >= fd && entry_fd != preserve_fd && entry_fd != dir_fd) {
|
if (entry_fd >= min_fd && entry_fd != preserve_fd &&
|
||||||
|
entry_fd != directory_fd) {
|
||||||
CloseNowOrOnExec(entry_fd, false);
|
CloseNowOrOnExec(entry_fd, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (result == DirectoryReader::Result::kError) {
|
||||||
if (errno != 0) {
|
|
||||||
PLOG(WARNING) << "readdir";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
void ScopedDIRCloser::operator()(DIR* dir) const {
|
void ScopedDIRCloseTraits::Free(DIR* dir) {
|
||||||
if (dir && IGNORE_EINTR(closedir(dir)) != 0) {
|
if (dir && IGNORE_EINTR(closedir(dir)) != 0) {
|
||||||
PLOG(ERROR) << "closedir";
|
PLOG(ERROR) << "closedir";
|
||||||
}
|
}
|
||||||
|
@ -17,13 +17,14 @@
|
|||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
|
||||||
#include <memory>
|
#include "base/scoped_generic.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
struct ScopedDIRCloser {
|
struct ScopedDIRCloseTraits {
|
||||||
void operator()(DIR* dir) const;
|
static DIR* InvalidValue() { return nullptr; }
|
||||||
|
static void Free(DIR* dir);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
@ -31,7 +32,7 @@ struct ScopedDIRCloser {
|
|||||||
//! \brief Maintains a directory opened by `opendir`.
|
//! \brief Maintains a directory opened by `opendir`.
|
||||||
//!
|
//!
|
||||||
//! On destruction, the directory will be closed by calling `closedir`.
|
//! On destruction, the directory will be closed by calling `closedir`.
|
||||||
using ScopedDIR = std::unique_ptr<DIR, internal::ScopedDIRCloser>;
|
using ScopedDIR = base::ScopedGeneric<DIR*, internal::ScopedDIRCloseTraits>;
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
|
||||||
|
@ -32,6 +32,9 @@
|
|||||||
'sources': [
|
'sources': [
|
||||||
'file/delimited_file_reader.cc',
|
'file/delimited_file_reader.cc',
|
||||||
'file/delimited_file_reader.h',
|
'file/delimited_file_reader.h',
|
||||||
|
'file/directory_reader.h',
|
||||||
|
'file/directory_reader_posix.cc',
|
||||||
|
'file/directory_reader_win.cc',
|
||||||
'file/file_io.cc',
|
'file/file_io.cc',
|
||||||
'file/file_io.h',
|
'file/file_io.h',
|
||||||
'file/file_io_posix.cc',
|
'file/file_io_posix.cc',
|
||||||
@ -40,6 +43,9 @@
|
|||||||
'file/file_reader.h',
|
'file/file_reader.h',
|
||||||
'file/file_seeker.cc',
|
'file/file_seeker.cc',
|
||||||
'file/file_seeker.h',
|
'file/file_seeker.h',
|
||||||
|
'file/filesystem.h',
|
||||||
|
'file/filesystem_posix.cc',
|
||||||
|
'file/filesystem_win.cc',
|
||||||
'file/file_writer.cc',
|
'file/file_writer.cc',
|
||||||
'file/file_writer.h',
|
'file/file_writer.h',
|
||||||
'file/string_file.cc',
|
'file/string_file.cc',
|
||||||
@ -108,6 +114,7 @@
|
|||||||
'misc/address_sanitizer.h',
|
'misc/address_sanitizer.h',
|
||||||
'misc/address_types.h',
|
'misc/address_types.h',
|
||||||
'misc/arraysize_unsafe.h',
|
'misc/arraysize_unsafe.h',
|
||||||
|
'misc/as_underlying_type.h',
|
||||||
'misc/clock.h',
|
'misc/clock.h',
|
||||||
'misc/clock_mac.cc',
|
'misc/clock_mac.cc',
|
||||||
'misc/clock_posix.cc',
|
'misc/clock_posix.cc',
|
||||||
|
@ -36,8 +36,12 @@
|
|||||||
],
|
],
|
||||||
'sources': [
|
'sources': [
|
||||||
'file/delimited_file_reader_test.cc',
|
'file/delimited_file_reader_test.cc',
|
||||||
|
'file/directory_reader_test.cc',
|
||||||
'file/file_io_test.cc',
|
'file/file_io_test.cc',
|
||||||
'file/file_reader_test.cc',
|
'file/file_reader_test.cc',
|
||||||
|
'file/filesystem_test.cc',
|
||||||
|
'file/filesystem_test_util.cc',
|
||||||
|
'file/filesystem_test_util.h',
|
||||||
'file/string_file_test.cc',
|
'file/string_file_test.cc',
|
||||||
'linux/auxiliary_vector_test.cc',
|
'linux/auxiliary_vector_test.cc',
|
||||||
'linux/memory_map_test.cc',
|
'linux/memory_map_test.cc',
|
||||||
|
@ -28,5 +28,9 @@ void ScopedKernelHANDLECloseTraits::Free(HANDLE handle) {
|
|||||||
PCHECK(CloseHandle(handle)) << "CloseHandle";
|
PCHECK(CloseHandle(handle)) << "CloseHandle";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScopedSearchHANDLECloseTraits::Free(HANDLE handle) {
|
||||||
|
PCHECK(FindClose(handle)) << "FindClose";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -24,16 +24,17 @@ namespace crashpad {
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
struct ScopedFileHANDLECloseTraits {
|
struct ScopedFileHANDLECloseTraits {
|
||||||
static HANDLE InvalidValue() {
|
static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; }
|
||||||
return INVALID_HANDLE_VALUE;
|
|
||||||
}
|
|
||||||
static void Free(HANDLE handle);
|
static void Free(HANDLE handle);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScopedKernelHANDLECloseTraits {
|
struct ScopedKernelHANDLECloseTraits {
|
||||||
static HANDLE InvalidValue() {
|
static HANDLE InvalidValue() { return nullptr; }
|
||||||
return nullptr;
|
static void Free(HANDLE handle);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
struct ScopedSearchHANDLECloseTraits {
|
||||||
|
static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; }
|
||||||
static void Free(HANDLE handle);
|
static void Free(HANDLE handle);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -43,6 +44,8 @@ using ScopedFileHANDLE =
|
|||||||
base::ScopedGeneric<HANDLE, internal::ScopedFileHANDLECloseTraits>;
|
base::ScopedGeneric<HANDLE, internal::ScopedFileHANDLECloseTraits>;
|
||||||
using ScopedKernelHANDLE =
|
using ScopedKernelHANDLE =
|
||||||
base::ScopedGeneric<HANDLE, internal::ScopedKernelHANDLECloseTraits>;
|
base::ScopedGeneric<HANDLE, internal::ScopedKernelHANDLECloseTraits>;
|
||||||
|
using ScopedSearchHANDLE =
|
||||||
|
base::ScopedGeneric<HANDLE, internal::ScopedSearchHANDLECloseTraits>;
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user