mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-27 15:32:10 +08: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 <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <sched.h>
|
||||
#include <stdio.h>
|
||||
@ -27,8 +26,9 @@
|
||||
#include "base/logging.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
#include "util/file/directory_reader.h"
|
||||
#include "util/linux/proc_stat_reader.h"
|
||||
#include "util/posix/scoped_dir.h"
|
||||
#include "util/misc/as_underlying_type.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -264,15 +264,6 @@ void ProcessReader::InitializeThreads() {
|
||||
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;
|
||||
main_thread.tid = pid;
|
||||
if (main_thread.InitializePtrace(connection_)) {
|
||||
@ -282,15 +273,19 @@ void ProcessReader::InitializeThreads() {
|
||||
LOG(WARNING) << "Couldn't initialize main thread.";
|
||||
}
|
||||
|
||||
char path[32];
|
||||
snprintf(path, arraysize(path), "/proc/%d/task", pid);
|
||||
bool main_thread_found = false;
|
||||
dirent* dir_entry;
|
||||
while ((dir_entry = readdir(scoped_dir.get()))) {
|
||||
if (strncmp(dir_entry->d_name, ".", arraysize(dir_entry->d_name)) == 0 ||
|
||||
strncmp(dir_entry->d_name, "..", arraysize(dir_entry->d_name)) == 0) {
|
||||
continue;
|
||||
}
|
||||
DirectoryReader reader;
|
||||
if (!reader.Open(base::FilePath(path))) {
|
||||
return;
|
||||
}
|
||||
base::FilePath tid_str;
|
||||
DirectoryReader::Result result;
|
||||
while ((result = reader.NextFile(&tid_str)) ==
|
||||
DirectoryReader::Result::kSuccess) {
|
||||
pid_t tid;
|
||||
if (!base::StringToInt(dir_entry->d_name, &tid)) {
|
||||
if (!base::StringToInt(tid_str.value(), &tid)) {
|
||||
LOG(ERROR) << "format error";
|
||||
continue;
|
||||
}
|
||||
@ -308,6 +303,8 @@ void ProcessReader::InitializeThreads() {
|
||||
threads_.push_back(thread);
|
||||
}
|
||||
}
|
||||
DCHECK_EQ(AsUnderlyingType(result),
|
||||
AsUnderlyingType(DirectoryReader::Result::kNoMoreFiles));
|
||||
DCHECK(main_thread_found);
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,11 @@
|
||||
|
||||
#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 test {
|
||||
|
||||
@ -24,5 +29,25 @@ ScopedTempDir::~ScopedTempDir() {
|
||||
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 crashpad
|
||||
|
@ -56,32 +56,5 @@ base::FilePath ScopedTempDir::CreateTemporaryDirectory() {
|
||||
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 crashpad
|
||||
|
@ -72,33 +72,5 @@ base::FilePath ScopedTempDir::CreateTemporaryDirectory() {
|
||||
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 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 <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
@ -28,10 +25,10 @@
|
||||
#include "base/files/scoped_file.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "build/build_config.h"
|
||||
#include "util/file/directory_reader.h"
|
||||
#include "util/misc/implicit_cast.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
#include "util/posix/scoped_dir.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#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.
|
||||
// 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.
|
||||
bool CloseMultipleNowOrOnExecUsingFDDir(int fd, int preserve_fd) {
|
||||
bool CloseMultipleNowOrOnExecUsingFDDir(int min_fd, int preserve_fd) {
|
||||
#if defined(OS_MACOSX)
|
||||
static constexpr char kFDDir[] = "/dev/fd";
|
||||
#elif defined(OS_LINUX) || defined(OS_ANDROID)
|
||||
static constexpr char kFDDir[] = "/proc/self/fd";
|
||||
#endif
|
||||
|
||||
DIR* dir = opendir(kFDDir);
|
||||
if (!dir) {
|
||||
PLOG(WARNING) << "opendir";
|
||||
DirectoryReader reader;
|
||||
if (!reader.Open(base::FilePath(kFDDir))) {
|
||||
return false;
|
||||
}
|
||||
int directory_fd = reader.DirectoryFD();
|
||||
DCHECK_GE(directory_fd, 0);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
base::FilePath entry_fd_path;
|
||||
DirectoryReader::Result result;
|
||||
while ((result = reader.NextFile(&entry_fd_path)) ==
|
||||
DirectoryReader::Result::kSuccess) {
|
||||
int entry_fd;
|
||||
if (!AssignIfInRange(&entry_fd, entry_fd_long)) {
|
||||
LOG(ERROR) << "out-of-range fd " << entry_name;
|
||||
if (!base::StringToInt(entry_fd_path.value(), &entry_fd)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
if (errno != 0) {
|
||||
PLOG(WARNING) << "readdir";
|
||||
if (result == DirectoryReader::Result::kError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
void ScopedDIRCloser::operator()(DIR* dir) const {
|
||||
void ScopedDIRCloseTraits::Free(DIR* dir) {
|
||||
if (dir && IGNORE_EINTR(closedir(dir)) != 0) {
|
||||
PLOG(ERROR) << "closedir";
|
||||
}
|
||||
|
@ -17,13 +17,14 @@
|
||||
|
||||
#include <dirent.h>
|
||||
|
||||
#include <memory>
|
||||
#include "base/scoped_generic.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
struct ScopedDIRCloser {
|
||||
void operator()(DIR* dir) const;
|
||||
struct ScopedDIRCloseTraits {
|
||||
static DIR* InvalidValue() { return nullptr; }
|
||||
static void Free(DIR* dir);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
@ -31,7 +32,7 @@ struct ScopedDIRCloser {
|
||||
//! \brief Maintains a directory opened by `opendir`.
|
||||
//!
|
||||
//! 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
|
||||
|
||||
|
@ -32,6 +32,9 @@
|
||||
'sources': [
|
||||
'file/delimited_file_reader.cc',
|
||||
'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.h',
|
||||
'file/file_io_posix.cc',
|
||||
@ -40,6 +43,9 @@
|
||||
'file/file_reader.h',
|
||||
'file/file_seeker.cc',
|
||||
'file/file_seeker.h',
|
||||
'file/filesystem.h',
|
||||
'file/filesystem_posix.cc',
|
||||
'file/filesystem_win.cc',
|
||||
'file/file_writer.cc',
|
||||
'file/file_writer.h',
|
||||
'file/string_file.cc',
|
||||
@ -108,6 +114,7 @@
|
||||
'misc/address_sanitizer.h',
|
||||
'misc/address_types.h',
|
||||
'misc/arraysize_unsafe.h',
|
||||
'misc/as_underlying_type.h',
|
||||
'misc/clock.h',
|
||||
'misc/clock_mac.cc',
|
||||
'misc/clock_posix.cc',
|
||||
|
@ -36,8 +36,12 @@
|
||||
],
|
||||
'sources': [
|
||||
'file/delimited_file_reader_test.cc',
|
||||
'file/directory_reader_test.cc',
|
||||
'file/file_io_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',
|
||||
'linux/auxiliary_vector_test.cc',
|
||||
'linux/memory_map_test.cc',
|
||||
|
@ -28,5 +28,9 @@ void ScopedKernelHANDLECloseTraits::Free(HANDLE handle) {
|
||||
PCHECK(CloseHandle(handle)) << "CloseHandle";
|
||||
}
|
||||
|
||||
void ScopedSearchHANDLECloseTraits::Free(HANDLE handle) {
|
||||
PCHECK(FindClose(handle)) << "FindClose";
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace crashpad
|
||||
|
@ -24,16 +24,17 @@ namespace crashpad {
|
||||
namespace internal {
|
||||
|
||||
struct ScopedFileHANDLECloseTraits {
|
||||
static HANDLE InvalidValue() {
|
||||
return INVALID_HANDLE_VALUE;
|
||||
}
|
||||
static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; }
|
||||
static void Free(HANDLE handle);
|
||||
};
|
||||
|
||||
struct ScopedKernelHANDLECloseTraits {
|
||||
static HANDLE InvalidValue() {
|
||||
return nullptr;
|
||||
}
|
||||
static HANDLE InvalidValue() { return nullptr; }
|
||||
static void Free(HANDLE handle);
|
||||
};
|
||||
|
||||
struct ScopedSearchHANDLECloseTraits {
|
||||
static HANDLE InvalidValue() { return INVALID_HANDLE_VALUE; }
|
||||
static void Free(HANDLE handle);
|
||||
};
|
||||
|
||||
@ -43,6 +44,8 @@ using ScopedFileHANDLE =
|
||||
base::ScopedGeneric<HANDLE, internal::ScopedFileHANDLECloseTraits>;
|
||||
using ScopedKernelHANDLE =
|
||||
base::ScopedGeneric<HANDLE, internal::ScopedKernelHANDLECloseTraits>;
|
||||
using ScopedSearchHANDLE =
|
||||
base::ScopedGeneric<HANDLE, internal::ScopedSearchHANDLECloseTraits>;
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user