crashpad/util/file/filesystem_test.cc
Bruce Dawson fdf7b9e8e9 Skip tests that create symbol links when not allowed
Several tests in filesystem_test.cc create symbol links. The privilege
needed to do this is not enabled on all Windows systems so several of
the tests check for the privilege and are skipped if it is not
available.

However, two tests that created symbol links were not doing this check
and therefore failed on some Windows machines. This corrects those
failures by adding the checks.

Bug: chromium:1418165
Change-Id: I6621796b462b8db02271ad5a05e0c29ee047f648
Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/4348801
Reviewed-by: Mark Mentovai <mark@chromium.org>
Commit-Queue: Bruce Dawson <brucedawson@chromium.org>
2023-03-18 05:36:06 +00:00

542 lines
17 KiB
C++

// Copyright 2017 The Crashpad Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "util/file/filesystem.h"
#include <sys/time.h>
#include "build/build_config.h"
#include "gtest/gtest.h"
#include "test/errors.h"
#include "test/filesystem.h"
#include "test/scoped_temp_dir.h"
#include "util/file/file_writer.h"
#include "util/misc/time.h"
namespace crashpad {
namespace test {
namespace {
constexpr char kTestFileContent[] = "file_content";
bool CurrentTime(timespec* now) {
#if BUILDFLAG(IS_APPLE)
timeval now_tv;
int res = gettimeofday(&now_tv, nullptr);
if (res != 0) {
EXPECT_EQ(res, 0) << ErrnoMessage("gettimeofday");
return false;
}
TimevalToTimespec(now_tv, now);
return true;
#elif BUILDFLAG(IS_POSIX)
int res = clock_gettime(CLOCK_REALTIME, now);
if (res != 0) {
EXPECT_EQ(res, 0) << ErrnoMessage("clock_gettime");
return false;
}
return true;
#else
int res = timespec_get(now, TIME_UTC);
if (res != TIME_UTC) {
EXPECT_EQ(res, TIME_UTC);
return false;
}
return true;
#endif
}
TEST(Filesystem, FileModificationTime) {
timespec expected_time_start, expected_time_end;
ASSERT_TRUE(CurrentTime(&expected_time_start));
ScopedTempDir temp_dir;
ASSERT_TRUE(CurrentTime(&expected_time_end));
timespec dir_mtime;
ASSERT_TRUE(FileModificationTime(temp_dir.path(), &dir_mtime));
EXPECT_GE(dir_mtime.tv_sec, expected_time_start.tv_sec - 2);
EXPECT_LE(dir_mtime.tv_sec, expected_time_end.tv_sec + 2);
base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
ASSERT_TRUE(CurrentTime(&expected_time_start));
ASSERT_TRUE(CreateFile(file));
ASSERT_TRUE(CurrentTime(&expected_time_end));
timespec file_mtime;
ASSERT_TRUE(FileModificationTime(file, &file_mtime));
EXPECT_GE(file_mtime.tv_sec, expected_time_start.tv_sec - 2);
EXPECT_LE(file_mtime.tv_sec, expected_time_end.tv_sec + 2);
timespec file_mtime_again;
ASSERT_TRUE(FileModificationTime(file, &file_mtime_again));
EXPECT_EQ(file_mtime.tv_sec, file_mtime_again.tv_sec);
EXPECT_EQ(file_mtime.tv_nsec, file_mtime_again.tv_nsec);
timespec mtime;
EXPECT_FALSE(FileModificationTime(base::FilePath(), &mtime));
EXPECT_FALSE(FileModificationTime(
temp_dir.path().Append(FILE_PATH_LITERAL("notafile")), &mtime));
}
#if !BUILDFLAG(IS_FUCHSIA)
TEST(Filesystem, FileModificationTime_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
GTEST_SKIP();
}
ScopedTempDir temp_dir;
const base::FilePath dir_link(
temp_dir.path().Append(FILE_PATH_LITERAL("dir_link")));
timespec expected_time_start, expected_time_end;
ASSERT_TRUE(CurrentTime(&expected_time_start));
ASSERT_TRUE(CreateSymbolicLink(temp_dir.path(), dir_link));
ASSERT_TRUE(CurrentTime(&expected_time_end));
timespec mtime, mtime2;
ASSERT_TRUE(FileModificationTime(temp_dir.path(), &mtime));
mtime.tv_sec -= 100;
ASSERT_TRUE(SetFileModificationTime(temp_dir.path(), mtime));
ASSERT_TRUE(FileModificationTime(temp_dir.path(), &mtime2));
EXPECT_GE(mtime2.tv_sec, mtime.tv_sec - 2);
EXPECT_LE(mtime2.tv_sec, mtime.tv_sec + 2);
ASSERT_TRUE(FileModificationTime(dir_link, &mtime));
EXPECT_GE(mtime.tv_sec, expected_time_start.tv_sec - 2);
EXPECT_LE(mtime.tv_sec, expected_time_end.tv_sec + 2);
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(CurrentTime(&expected_time_start));
ASSERT_TRUE(CreateSymbolicLink(file, link));
ASSERT_TRUE(CurrentTime(&expected_time_end));
ASSERT_TRUE(FileModificationTime(file, &mtime));
mtime.tv_sec -= 100;
ASSERT_TRUE(SetFileModificationTime(file, mtime));
ASSERT_TRUE(FileModificationTime(file, &mtime2));
EXPECT_GE(mtime2.tv_sec, mtime.tv_sec - 2);
EXPECT_LE(mtime2.tv_sec, mtime.tv_sec + 2);
ASSERT_TRUE(FileModificationTime(link, &mtime));
EXPECT_GE(mtime.tv_sec, expected_time_start.tv_sec - 2);
EXPECT_LE(mtime.tv_sec, expected_time_end.tv_sec + 2);
}
#endif // !BUILDFLAG(IS_FUCHSIA)
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, MoveFileOrDirectory) {
ScopedTempDir temp_dir;
base::FilePath file(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
ASSERT_TRUE(CreateFile(file));
// empty paths
EXPECT_FALSE(MoveFileOrDirectory(base::FilePath(), base::FilePath()));
EXPECT_FALSE(MoveFileOrDirectory(base::FilePath(), file));
EXPECT_FALSE(MoveFileOrDirectory(file, base::FilePath()));
EXPECT_TRUE(IsRegularFile(file));
// files
base::FilePath file2(temp_dir.path().Append(FILE_PATH_LITERAL("file2")));
EXPECT_TRUE(MoveFileOrDirectory(file, file2));
EXPECT_TRUE(IsRegularFile(file2));
EXPECT_FALSE(PathExists(file));
EXPECT_FALSE(MoveFileOrDirectory(file, file2));
EXPECT_TRUE(IsRegularFile(file2));
EXPECT_FALSE(PathExists(file));
ASSERT_TRUE(CreateFile(file));
EXPECT_TRUE(MoveFileOrDirectory(file2, file));
EXPECT_TRUE(IsRegularFile(file));
EXPECT_FALSE(PathExists(file2));
// directories
base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir")));
ASSERT_TRUE(
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));
base::FilePath nested(dir.Append(FILE_PATH_LITERAL("nested")));
ASSERT_TRUE(CreateFile(nested));
base::FilePath dir2(temp_dir.path().Append(FILE_PATH_LITERAL("dir2")));
EXPECT_TRUE(MoveFileOrDirectory(dir, dir2));
EXPECT_FALSE(IsDirectory(dir, true));
EXPECT_TRUE(IsDirectory(dir2, false));
EXPECT_TRUE(IsRegularFile(dir2.Append(nested.BaseName())));
ASSERT_TRUE(
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));
EXPECT_FALSE(MoveFileOrDirectory(dir, dir2));
EXPECT_TRUE(IsDirectory(dir, false));
EXPECT_TRUE(IsDirectory(dir2, false));
EXPECT_TRUE(IsRegularFile(dir2.Append(nested.BaseName())));
// files <-> directories
EXPECT_FALSE(MoveFileOrDirectory(file, dir2));
EXPECT_TRUE(IsDirectory(dir2, false));
EXPECT_TRUE(IsRegularFile(file));
EXPECT_FALSE(MoveFileOrDirectory(dir2, file));
EXPECT_TRUE(IsDirectory(dir2, false));
EXPECT_TRUE(IsRegularFile(file));
}
#if !BUILDFLAG(IS_FUCHSIA)
TEST(Filesystem, MoveFileOrDirectory_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
GTEST_SKIP();
}
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")));
ASSERT_TRUE(CreateSymbolicLink(file, link));
base::FilePath link2(temp_dir.path().Append(FILE_PATH_LITERAL("link2")));
EXPECT_TRUE(MoveFileOrDirectory(link, link2));
EXPECT_TRUE(PathExists(link2));
EXPECT_FALSE(PathExists(link));
ASSERT_TRUE(CreateSymbolicLink(file, link));
EXPECT_TRUE(MoveFileOrDirectory(link, link2));
EXPECT_TRUE(PathExists(link2));
EXPECT_FALSE(PathExists(link));
// file links <-> files
EXPECT_TRUE(MoveFileOrDirectory(file, link2));
EXPECT_TRUE(IsRegularFile(link2));
EXPECT_FALSE(PathExists(file));
ASSERT_TRUE(MoveFileOrDirectory(link2, file));
ASSERT_TRUE(CreateSymbolicLink(file, link));
EXPECT_TRUE(MoveFileOrDirectory(link, file));
EXPECT_TRUE(PathExists(file));
EXPECT_FALSE(IsRegularFile(file));
EXPECT_FALSE(PathExists(link));
EXPECT_TRUE(LoggingRemoveFile(file));
// dangling file links
ASSERT_TRUE(CreateSymbolicLink(file, link));
EXPECT_TRUE(MoveFileOrDirectory(link, link2));
EXPECT_TRUE(PathExists(link2));
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));
EXPECT_TRUE(PathExists(link2));
EXPECT_FALSE(PathExists(link));
// dangling directory links
ASSERT_TRUE(LoggingRemoveDirectory(dir));
EXPECT_TRUE(MoveFileOrDirectory(link2, link));
EXPECT_TRUE(PathExists(link));
EXPECT_FALSE(PathExists(link2));
}
#endif // !BUILDFLAG(IS_FUCHSIA)
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));
}
#if !BUILDFLAG(IS_FUCHSIA)
TEST(Filesystem, IsRegularFile_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
GTEST_SKIP();
}
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));
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));
}
#endif // !BUILDFLAG(IS_FUCHSIA)
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));
}
#if !BUILDFLAG(IS_FUCHSIA)
TEST(Filesystem, IsDirectory_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
GTEST_SKIP();
}
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));
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));
}
#endif // !BUILDFLAG(IS_FUCHSIA)
TEST(Filesystem, RemoveFile) {
EXPECT_FALSE(LoggingRemoveFile(base::FilePath()));
ScopedTempDir temp_dir;
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 dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir")));
ASSERT_TRUE(
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));
EXPECT_TRUE(LoggingRemoveFile(file));
EXPECT_FALSE(PathExists(file));
EXPECT_FALSE(LoggingRemoveFile(file));
}
#if !BUILDFLAG(IS_FUCHSIA)
TEST(Filesystem, RemoveFile_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
GTEST_SKIP();
}
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));
ASSERT_TRUE(CreateSymbolicLink(dir, link));
EXPECT_TRUE(LoggingRemoveFile(link));
EXPECT_FALSE(PathExists(link));
EXPECT_TRUE(PathExists(dir));
}
#endif // !BUILDFLAG(IS_FUCHSIA)
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));
#if !BUILDFLAG(IS_FUCHSIA)
// The current implementation of Fuchsia's rmdir() simply calls unlink(), and
// unlink() works on all FS objects. This is incorrect as
// http://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html says
// "The directory shall be removed only if it is an empty directory." and "If
// the directory is not an empty directory, rmdir() shall fail and set errno
// to [EEXIST] or [ENOTEMPTY]." Upstream bug: US-400.
EXPECT_FALSE(LoggingRemoveDirectory(file));
EXPECT_FALSE(LoggingRemoveDirectory(dir));
#endif
ASSERT_TRUE(LoggingRemoveFile(file));
EXPECT_TRUE(LoggingRemoveDirectory(dir));
}
#if !BUILDFLAG(IS_FUCHSIA)
TEST(Filesystem, RemoveDirectory_SymbolicLinks) {
if (!CanCreateSymbolicLinks()) {
GTEST_SKIP();
}
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));
EXPECT_TRUE(LoggingRemoveFile(link));
ASSERT_TRUE(CreateSymbolicLink(dir, link));
EXPECT_FALSE(LoggingRemoveDirectory(link));
EXPECT_TRUE(LoggingRemoveFile(link));
}
#endif // !BUILDFLAG(IS_FUCHSIA)
TEST(Filesystem, GetFileSize) {
ScopedTempDir temp_dir;
base::FilePath filepath(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
FileWriter writer;
bool is_created = writer.Open(
filepath, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly);
ASSERT_TRUE(is_created);
writer.Write(kTestFileContent, sizeof(kTestFileContent));
writer.Close();
uint64_t filesize = GetFileSize(filepath);
EXPECT_EQ(filesize, sizeof(kTestFileContent));
#if !BUILDFLAG(IS_FUCHSIA)
if (!CanCreateSymbolicLinks()) {
GTEST_SKIP();
}
// Create a link to a file.
base::FilePath link(temp_dir.path().Append(FILE_PATH_LITERAL("link")));
ASSERT_TRUE(CreateSymbolicLink(filepath, link));
uint64_t filesize_link = GetFileSize(link);
EXPECT_EQ(filesize_link, 0u);
#endif // !BUILDFLAG(IS_FUCHSIA)
}
TEST(Filesystem, GetDirectorySize) {
ScopedTempDir temp_dir;
base::FilePath dir(temp_dir.path().Append(FILE_PATH_LITERAL("dir")));
ASSERT_TRUE(
LoggingCreateDirectory(dir, FilePermissions::kWorldReadable, false));
base::FilePath filepath1(temp_dir.path().Append(FILE_PATH_LITERAL("file")));
FileWriter writer1;
bool is_created1 = writer1.Open(
filepath1, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly);
ASSERT_TRUE(is_created1);
writer1.Write(kTestFileContent, sizeof(kTestFileContent));
writer1.Close();
base::FilePath filepath2(dir.Append(FILE_PATH_LITERAL("file")));
FileWriter writer2;
bool is_created2 = writer2.Open(
filepath2, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly);
ASSERT_TRUE(is_created2);
writer2.Write(kTestFileContent, sizeof(kTestFileContent));
writer2.Close();
#if !BUILDFLAG(IS_FUCHSIA)
if (CanCreateSymbolicLinks()) {
// Create a link to a file.
base::FilePath link(dir.Append(FILE_PATH_LITERAL("link")));
ASSERT_TRUE(CreateSymbolicLink(filepath2, link));
// Create a link to a dir.
base::FilePath linkdir(temp_dir.path().Append(FILE_PATH_LITERAL("link")));
ASSERT_TRUE(CreateSymbolicLink(dir, linkdir));
}
#endif // !BUILDFLAG(IS_FUCHSIA)
uint64_t filesize = GetDirectorySize(temp_dir.path());
EXPECT_EQ(filesize, 2 * sizeof(kTestFileContent));
}
} // namespace
} // namespace test
} // namespace crashpad