From c49da9caef302ad03f7161c58edf34825fd0171e Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Thu, 26 Oct 2017 18:37:49 -0400 Subject: [PATCH] win: Expect uneven symbolic link support As mentioned at https://chromium-review.googlesource.com/c/chromium/src/+/735820#message-e8b199498d8b850f2612c46648069d819dd47517, the typical Windows behavior for symbolic links requires administrative privileges. Symbolic links are available to non-administrators in Windows 10.0.15063 and later (1703, Creators Update), provided that developer mode has been enabled and SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE is passed to CreateSymbolicLink(). See https://blogs.windows.com/buildingapps/2016/12/02/symlinks-windows-10/. This adds SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE to uses of CreateSymbolicLink(), and creates test::CanCreateSymbolicLinks() to determine whether symbolic link creation is possible. Tests that exercise symbolic links are adapted to gate all symbolic link operations on this test. Test: crashpad_util_test DirectoryReader.*:Filesystem.* Change-Id: I8250cadd974ffcc7abe32701a0d5bc487061baf0 Bug: crashpad: Reviewed-on: https://chromium-review.googlesource.com/739472 Commit-Queue: Mark Mentovai Reviewed-by: Joshua Peraza Reviewed-by: Scott Graham --- compat/compat.gyp | 1 + compat/win/winbase.h | 27 +++++++ test/gtest_disabled.cc | 2 +- test/gtest_disabled.h | 4 +- util/file/directory_reader_test.cc | 121 +++++++++++++++++++---------- util/file/filesystem_posix.cc | 10 +-- util/file/filesystem_test.cc | 114 +++++++++++++++++++++++---- util/file/filesystem_test_util.cc | 112 +++++++++++++++++++++----- util/file/filesystem_test_util.h | 28 ++++++- util/file/filesystem_win.cc | 3 +- 10 files changed, 334 insertions(+), 88 deletions(-) create mode 100644 compat/win/winbase.h diff --git a/compat/compat.gyp b/compat/compat.gyp index 52cca468..b0aec0af 100644 --- a/compat/compat.gyp +++ b/compat/compat.gyp @@ -54,6 +54,7 @@ 'win/sys/types.h', 'win/time.cc', 'win/time.h', + 'win/winbase.h', 'win/winnt.h', 'win/winternl.h', ], diff --git a/compat/win/winbase.h b/compat/win/winbase.h new file mode 100644 index 00000000..ffd850bf --- /dev/null +++ b/compat/win/winbase.h @@ -0,0 +1,27 @@ +// 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_COMPAT_WIN_WINBASE_H_ +#define CRASHPAD_COMPAT_WIN_WINBASE_H_ + +// include_next +#include <../um/winbase.h> + +// 10.0.15063.0 SDK + +#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2) +#endif + +#endif // CRASHPAD_COMPAT_WIN_WINBASE_H_ diff --git a/test/gtest_disabled.cc b/test/gtest_disabled.cc index 36da4403..fab6802a 100644 --- a/test/gtest_disabled.cc +++ b/test/gtest_disabled.cc @@ -51,7 +51,7 @@ void DisabledTestGtestEnvironment::DisabledTest() { "[ DISABLED ] %s\n", disabled_test.c_str()); - disabled_tests_.insert(disabled_test); + disabled_tests_.push_back(disabled_test); } DisabledTestGtestEnvironment::DisabledTestGtestEnvironment() diff --git a/test/gtest_disabled.h b/test/gtest_disabled.h index 06da72ce..9415cba2 100644 --- a/test/gtest_disabled.h +++ b/test/gtest_disabled.h @@ -15,8 +15,8 @@ #ifndef CRASHPAD_TEST_GTEST_DISABLED_H_ #define CRASHPAD_TEST_GTEST_DISABLED_H_ -#include #include +#include #include "base/macros.h" #include "gtest/gtest.h" @@ -54,7 +54,7 @@ class DisabledTestGtestEnvironment final : public testing::Environment { // testing::Environment: void TearDown() override; - std::set disabled_tests_; + std::vector disabled_tests_; DISALLOW_COPY_AND_ASSIGN(DisabledTestGtestEnvironment); }; diff --git a/util/file/directory_reader_test.cc b/util/file/directory_reader_test.cc index da4bfb73..640b850f 100644 --- a/util/file/directory_reader_test.cc +++ b/util/file/directory_reader_test.cc @@ -21,6 +21,7 @@ #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "test/scoped_temp_dir.h" #include "util/file/file_io.h" #include "util/file/filesystem.h" @@ -30,6 +31,51 @@ namespace crashpad { namespace test { namespace { +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)); + + EXPECT_FALSE( + reader.Open(temp_dir.path().Append(FILE_PATH_LITERAL("doesntexist")))); +} + +#if !defined(OS_FUCHSIA) + +TEST(DirectoryReader, BadPaths_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file"))); + ASSERT_TRUE(CreateFile(file)); + + base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); + ASSERT_TRUE(CreateSymbolicLink(file, link)); + + DirectoryReader reader; + EXPECT_FALSE(reader.Open(link)); + + ASSERT_TRUE(LoggingRemoveFile(file)); + EXPECT_FALSE(reader.Open(link)); +} + +#endif // !OS_FUCHSIA + +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); +} + void ExpectFiles(const std::set& files, const std::set& expected) { EXPECT_EQ(files.size(), expected.size()); @@ -41,36 +87,7 @@ void ExpectFiles(const std::set& files, } } -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) { +void TestFilesAndDirectories(bool symbolic_links) { ScopedTempDir temp_dir; std::set expected_files; @@ -78,17 +95,6 @@ TEST(DirectoryReader, FilesAndDirectories) { 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, @@ -99,6 +105,23 @@ TEST(DirectoryReader, FilesAndDirectories) { ASSERT_TRUE( CreateFile(temp_dir.path().Append(directory).Append(nested_file))); +#if !defined(OS_FUCHSIA) + + if (symbolic_links) { + 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); + } + +#endif // !OS_FUCHSIA + std::set files; DirectoryReader reader; ASSERT_TRUE(reader.Open(temp_dir.path())); @@ -113,6 +136,22 @@ TEST(DirectoryReader, FilesAndDirectories) { ExpectFiles(files, expected_files); } +TEST(DirectoryReader, FilesAndDirectories) { + TestFilesAndDirectories(false); +} + +#if !defined(OS_FUCHSIA) + +TEST(DirectoryReader, FilesAndDirectories_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + TestFilesAndDirectories(true); +} + +#endif // !OS_FUCHSIA + } // namespace } // namespace test } // namespace crashpad diff --git a/util/file/filesystem_posix.cc b/util/file/filesystem_posix.cc index 802c3fc4..a35db1cd 100644 --- a/util/file/filesystem_posix.cc +++ b/util/file/filesystem_posix.cc @@ -56,7 +56,7 @@ bool MoveFileOrDirectory(const base::FilePath& source, 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(); + PLOG(ERROR) << "stat " << path.value(); return false; } return S_ISREG(st.st_mode); @@ -69,11 +69,9 @@ bool IsDirectory(const base::FilePath& path, bool allow_symlinks) { PLOG(ERROR) << "stat " << path.value(); return false; } - } else { - if (lstat(path.value().c_str(), &st) != 0) { - PLOG(ERROR) << "lstat " << 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); } diff --git a/util/file/filesystem_test.cc b/util/file/filesystem_test.cc index 4fc856a1..d83172d6 100644 --- a/util/file/filesystem_test.cc +++ b/util/file/filesystem_test.cc @@ -16,6 +16,7 @@ #include "base/logging.h" #include "gtest/gtest.h" +#include "test/gtest_disabled.h" #include "test/scoped_temp_dir.h" #include "util/file/filesystem_test_util.h" @@ -60,16 +61,16 @@ TEST(Filesystem, MoveFileOrDirectory) { base::FilePath file2(temp_dir.path().Append(FILE_PATH_LITERAL("file2"))); EXPECT_TRUE(MoveFileOrDirectory(file, file2)); EXPECT_TRUE(IsRegularFile(file2)); - EXPECT_FALSE(IsRegularFile(file)); + EXPECT_FALSE(PathExists(file)); EXPECT_FALSE(MoveFileOrDirectory(file, file2)); EXPECT_TRUE(IsRegularFile(file2)); - EXPECT_FALSE(IsRegularFile(file)); + EXPECT_FALSE(PathExists(file)); ASSERT_TRUE(CreateFile(file)); EXPECT_TRUE(MoveFileOrDirectory(file2, file)); EXPECT_TRUE(IsRegularFile(file)); - EXPECT_FALSE(IsRegularFile(file2)); + EXPECT_FALSE(PathExists(file2)); // directories base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); @@ -100,6 +101,19 @@ TEST(Filesystem, MoveFileOrDirectory) { EXPECT_FALSE(MoveFileOrDirectory(dir2, file)); EXPECT_TRUE(IsDirectory(dir2, false)); EXPECT_TRUE(IsRegularFile(file)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, MoveFileOrDirectory_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + + base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file"))); + ASSERT_TRUE(CreateFile(file)); // file links base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); @@ -135,6 +149,10 @@ TEST(Filesystem, MoveFileOrDirectory) { EXPECT_FALSE(PathExists(link)); // directory links + base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); + ASSERT_TRUE( + LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false)); + ASSERT_TRUE(CreateSymbolicLink(dir, link)); EXPECT_TRUE(MoveFileOrDirectory(link, link2)); @@ -148,6 +166,8 @@ TEST(Filesystem, MoveFileOrDirectory) { EXPECT_FALSE(PathExists(link2)); } +#endif // !OS_FUCHSIA + TEST(Filesystem, IsRegularFile) { EXPECT_FALSE(IsRegularFile(base::FilePath())); @@ -159,6 +179,18 @@ TEST(Filesystem, IsRegularFile) { ASSERT_TRUE(CreateFile(file)); EXPECT_TRUE(IsRegularFile(file)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, IsRegularFile_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file"))); + ASSERT_TRUE(CreateFile(file)); base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); ASSERT_TRUE(CreateSymbolicLink(file, link)); @@ -173,6 +205,8 @@ TEST(Filesystem, IsRegularFile) { EXPECT_FALSE(IsRegularFile(dir_link)); } +#endif // !OS_FUCHSIA + TEST(Filesystem, IsDirectory) { EXPECT_FALSE(IsDirectory(base::FilePath(), false)); EXPECT_FALSE(IsDirectory(base::FilePath(), true)); @@ -187,6 +221,18 @@ TEST(Filesystem, IsDirectory) { ASSERT_TRUE(CreateFile(file)); EXPECT_FALSE(IsDirectory(file, false)); EXPECT_FALSE(IsDirectory(file, true)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, IsDirectory_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file"))); + ASSERT_TRUE(CreateFile(file)); base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); ASSERT_TRUE(CreateSymbolicLink(file, link)); @@ -204,6 +250,8 @@ TEST(Filesystem, IsDirectory) { EXPECT_TRUE(IsDirectory(dir_link, true)); } +#endif // !OS_FUCHSIA + TEST(Filesystem, RemoveFile) { EXPECT_FALSE(LoggingRemoveFile(base::FilePath())); @@ -216,27 +264,45 @@ TEST(Filesystem, RemoveFile) { ASSERT_TRUE(CreateFile(file)); EXPECT_TRUE(IsRegularFile(file)); + base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); + ASSERT_TRUE( + LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false)); + EXPECT_FALSE(LoggingRemoveFile(dir)); + + EXPECT_TRUE(LoggingRemoveFile(file)); + EXPECT_FALSE(PathExists(file)); + EXPECT_FALSE(LoggingRemoveFile(file)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, RemoveFile_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + ScopedTempDir temp_dir; + base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file"))); + ASSERT_TRUE(CreateFile(file)); + + base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir"))); + ASSERT_TRUE( + LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false)); + base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); ASSERT_TRUE(CreateSymbolicLink(file, link)); EXPECT_TRUE(LoggingRemoveFile(link)); EXPECT_FALSE(PathExists(link)); EXPECT_TRUE(PathExists(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(PathExists(link)); EXPECT_TRUE(PathExists(dir)); - - EXPECT_TRUE(LoggingRemoveFile(file)); - EXPECT_FALSE(IsRegularFile(file)); - EXPECT_FALSE(LoggingRemoveFile(file)); } +#endif // !OS_FUCHSIA + TEST(Filesystem, RemoveDirectory) { EXPECT_FALSE(LoggingRemoveDirectory(base::FilePath())); @@ -253,6 +319,25 @@ TEST(Filesystem, RemoveDirectory) { EXPECT_FALSE(LoggingRemoveDirectory(file)); EXPECT_FALSE(LoggingRemoveDirectory(dir)); + ASSERT_TRUE(LoggingRemoveFile(file)); + EXPECT_TRUE(LoggingRemoveDirectory(dir)); +} + +#if !defined(OS_FUCHSIA) + +TEST(Filesystem, RemoveDirectory_SymbolicLinks) { + if (!CanCreateSymbolicLinks()) { + DISABLED_TEST(); + } + + 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)); + base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link"))); ASSERT_TRUE(CreateSymbolicLink(file, link)); EXPECT_FALSE(LoggingRemoveDirectory(link)); @@ -261,11 +346,10 @@ TEST(Filesystem, RemoveDirectory) { ASSERT_TRUE(CreateSymbolicLink(dir, link)); EXPECT_FALSE(LoggingRemoveDirectory(link)); EXPECT_TRUE(LoggingRemoveFile(link)); - - ASSERT_TRUE(LoggingRemoveFile(file)); - EXPECT_TRUE(LoggingRemoveDirectory(dir)); } +#endif // !OS_FUCHSIA + } // namespace } // namespace test } // namespace crashpad diff --git a/util/file/filesystem_test_util.cc b/util/file/filesystem_test_util.cc index 49e13ecb..42cc8eb3 100644 --- a/util/file/filesystem_test_util.cc +++ b/util/file/filesystem_test_util.cc @@ -20,9 +20,9 @@ #include "base/logging.h" #include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" #include "gtest/gtest.h" #include "test/errors.h" +#include "test/scoped_temp_dir.h" #include "util/file/file_io.h" #include "util/file/filesystem.h" @@ -37,6 +37,60 @@ namespace crashpad { namespace test { +namespace { + +#if defined(OS_WIN) + +// Detects the flags necessary to create symbolic links and stores them in +// |flags| if non-nullptr, and returns true on success. If symbolic links can’t +// be created, returns false. +bool SymbolicLinkFlags(DWORD* flags) { + static DWORD symbolic_link_flags = []() { + ScopedTempDir temp_dir_; + + base::FilePath target_path = temp_dir_.path().Append(L"target"); + base::FilePath symlink_path = temp_dir_.path().Append(L"symlink"); + if (::CreateSymbolicLink( + symlink_path.value().c_str(), target_path.value().c_str(), 0)) { + return 0; + } + + DWORD error = GetLastError(); + if (error == ERROR_PRIVILEGE_NOT_HELD) { + if (::CreateSymbolicLink(symlink_path.value().c_str(), + target_path.value().c_str(), + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) { + return SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + } + + // This may fail with ERROR_INVALID_PARAMETER if the OS is too old to + // understand SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, so keep + // ERROR_PRIVILEGE_NOT_HELD for |error|. + } + + // Don’t use ErrorMessage() here because the second CreateSymbolicLink() may + // have scrambled it. Use the saved |error| value instead. + EXPECT_EQ(error, ERROR_PRIVILEGE_NOT_HELD) + << "CreateSymbolicLink: " + << logging::SystemErrorCodeToString(GetLastError()); + return -1; + }(); + + if (symbolic_link_flags == -1) { + return false; + } + + if (flags) { + *flags = symbolic_link_flags; + } + + return true; +} + +#endif // OS_WIN + +} // namespace + bool CreateFile(const base::FilePath& file) { ScopedFileHandle fd(LoggingOpenFileForWrite( file, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)); @@ -44,6 +98,35 @@ bool CreateFile(const base::FilePath& file) { return fd.is_valid(); } +bool PathExists(const base::FilePath& path) { +#if defined(OS_POSIX) + struct stat st; + if (lstat(path.value().c_str(), &st) != 0) { + EXPECT_EQ(errno, ENOENT) << ErrnoMessage("lstat ") << path.value(); + return false; + } + return true; +#elif defined(OS_WIN) + if (GetFileAttributes(path.value().c_str()) == INVALID_FILE_ATTRIBUTES) { + EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND) + << ErrorMessage("GetFileAttributes ") + << base::UTF16ToUTF8(path.value()); + return false; + } + return true; +#endif +} + +#if !defined(OS_FUCHSIA) + +bool CanCreateSymbolicLinks() { +#if defined(OS_POSIX) + return true; +#elif defined(OS_WIN) + return SymbolicLinkFlags(nullptr); +#endif // OS_POSIX +} + bool CreateSymbolicLink(const base::FilePath& target_path, const base::FilePath& symlink_path) { #if defined(OS_POSIX) @@ -53,35 +136,24 @@ bool CreateSymbolicLink(const base::FilePath& target_path, PLOG(ERROR) << "symlink"; return false; } + return true; #elif defined(OS_WIN) + DWORD symbolic_link_flags = 0; + SymbolicLinkFlags(&symbolic_link_flags); if (!::CreateSymbolicLink( symlink_path.value().c_str(), target_path.value().c_str(), - IsDirectory(target_path, true) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0)) { + symbolic_link_flags | + (IsDirectory(target_path, true) ? SYMBOLIC_LINK_FLAG_DIRECTORY + : 0))) { PLOG(ERROR) << "CreateSymbolicLink"; return false; } -#endif // OS_POSIX return true; +#endif // OS_POSIX } -bool PathExists(const base::FilePath& path) { -#if defined(OS_POSIX) - struct stat st; - if (lstat(path.value().c_str(), &st) != 0) { - EXPECT_EQ(errno, ENOENT) << ErrnoMessage("lstat ") << path.value(); - return false; - } -#elif defined(OS_WIN) - if (GetFileAttributes(path.value().c_str()) == INVALID_FILE_ATTRIBUTES) { - EXPECT_EQ(GetLastError(), ERROR_FILE_NOT_FOUND) - << ErrorMessage("GetFileAttributes ") - << base::UTF16ToUTF8(path.value()); - return false; - } -#endif - return true; -} +#endif // !OS_FUCHSIA } // namespace test } // namespace crashpad diff --git a/util/file/filesystem_test_util.h b/util/file/filesystem_test_util.h index 393bc8b5..b74653f1 100644 --- a/util/file/filesystem_test_util.h +++ b/util/file/filesystem_test_util.h @@ -17,15 +17,41 @@ #include "base/files/file_path.h" +#include "build/build_config.h" + namespace crashpad { namespace test { bool CreateFile(const base::FilePath& file); +bool PathExists(const base::FilePath& path); + +#if !defined(OS_FUCHSIA) || DOXYGEN +// There are no symbolic links on Fuchsia. Don’t bother declaring or defining +// symbolic link-related functions at all, because it’s an error to even pretend +// that symbolic links might be available on Fuchsia. + +//! \brief Determines whether it should be possible to create symbolic links. +//! +//! It is always possible to create symbolic links on POSIX. +//! +//! On Windows, it is only possible to create symbolic links when running as an +//! administrator, or as a non-administrator when running Windows 10 build 15063 +//! (1703, Creators Update) or later, provided that developer mode is enabled +//! and `SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE` is used. This function +//! tests the creation of a symbolic link and returns true on success, and false +//! on failure. If the symbolic link could not be created for a reason other +//! than the expected lack of privilege, a message is logged. +//! +//! Additional background: Symlinks +//! in Windows 10! +bool CanCreateSymbolicLinks(); + bool CreateSymbolicLink(const base::FilePath& target_path, const base::FilePath& symlink_path); -bool PathExists(const base::FilePath& path); +#endif // !OS_FUCHSIA || DOXYGEN } // namespace test } // namespace crashpad diff --git a/util/file/filesystem_win.cc b/util/file/filesystem_win.cc index 5a1242ec..b874ed8c 100644 --- a/util/file/filesystem_win.cc +++ b/util/file/filesystem_win.cc @@ -82,8 +82,7 @@ bool MoveFileOrDirectory(const base::FilePath& source, 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()); + PLOG(ERROR) << "GetFileAttributes " << base::UTF16ToUTF8(path.value()); return false; } if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0 ||