blob: 35c410e76c503e6ff5f99b2dea061e9cb3963585 [file] [log] [blame]
// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "crash-reporter/crash_collector_test.h"
#include <unistd.h>
#include <utility>
#include <base/files/file_util.h>
#include <base/files/scoped_temp_dir.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <brillo/syslog_logging.h>
#include <gtest/gtest.h>
#include "crash-reporter/crash_collector.h"
#include "crash-reporter/test_util.h"
using base::FilePath;
using base::StringPrintf;
using brillo::FindLog;
using ::testing::Return;
namespace {
void CountCrash() {
ADD_FAILURE();
}
bool IsMetrics() {
ADD_FAILURE();
return false;
}
} // namespace
class CrashCollectorTest : public ::testing::Test {
public:
void SetUp() {
EXPECT_CALL(collector_, SetUpDBus()).WillRepeatedly(Return());
collector_.Initialize(CountCrash, IsMetrics);
ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
test_dir_ = scoped_temp_dir_.GetPath();
brillo::ClearLog();
}
bool CheckHasCapacity();
protected:
CrashCollectorMock collector_;
FilePath test_dir_;
base::ScopedTempDir scoped_temp_dir_;
};
TEST_F(CrashCollectorTest, Initialize) {
ASSERT_TRUE(CountCrash == collector_.count_crash_function_);
ASSERT_TRUE(IsMetrics == collector_.is_feedback_allowed_function_);
}
TEST_F(CrashCollectorTest, WriteNewFile) {
FilePath test_file = test_dir_.Append("test_new");
const char kBuffer[] = "buffer";
EXPECT_EQ(strlen(kBuffer),
collector_.WriteNewFile(test_file, kBuffer, strlen(kBuffer)));
EXPECT_LT(collector_.WriteNewFile(test_file, kBuffer, strlen(kBuffer)), 0);
}
TEST_F(CrashCollectorTest, Sanitize) {
EXPECT_EQ("chrome", collector_.Sanitize("chrome"));
EXPECT_EQ("CHROME", collector_.Sanitize("CHROME"));
EXPECT_EQ("1chrome2", collector_.Sanitize("1chrome2"));
EXPECT_EQ("chrome__deleted_", collector_.Sanitize("chrome (deleted)"));
EXPECT_EQ("foo_bar", collector_.Sanitize("foo.bar"));
EXPECT_EQ("", collector_.Sanitize(""));
EXPECT_EQ("_", collector_.Sanitize(" "));
}
TEST_F(CrashCollectorTest, StripMacAddressesBasic) {
// Basic tests of StripSensitiveData...
// Make sure we work OK with a string w/ no MAC addresses.
const std::string kCrashWithNoMacsOrig =
"<7>[111566.131728] PM: Entering mem sleep\n";
std::string crash_with_no_macs(kCrashWithNoMacsOrig);
collector_.StripSensitiveData(&crash_with_no_macs);
EXPECT_EQ(kCrashWithNoMacsOrig, crash_with_no_macs);
// Make sure that we handle the case where there's nothing before/after the
// MAC address.
const std::string kJustAMacOrig = "11:22:33:44:55:66";
const std::string kJustAMacStripped = "00:00:00:00:00:01";
std::string just_a_mac(kJustAMacOrig);
collector_.StripSensitiveData(&just_a_mac);
EXPECT_EQ(kJustAMacStripped, just_a_mac);
// Test MAC addresses crammed together to make sure it gets both of them.
//
// I'm not sure that the code does ideal on these two test cases (they don't
// look like two MAC addresses to me), but since we don't see them I think
// it's OK to behave as shown here.
const std::string kCrammedMacs1Orig = "11:22:33:44:55:66:11:22:33:44:55:66";
const std::string kCrammedMacs1Stripped =
"00:00:00:00:00:01:00:00:00:00:00:01";
std::string crammed_macs_1(kCrammedMacs1Orig);
collector_.StripSensitiveData(&crammed_macs_1);
EXPECT_EQ(kCrammedMacs1Stripped, crammed_macs_1);
const std::string kCrammedMacs2Orig = "11:22:33:44:55:6611:22:33:44:55:66";
const std::string kCrammedMacs2Stripped =
"00:00:00:00:00:0100:00:00:00:00:01";
std::string crammed_macs_2(kCrammedMacs2Orig);
collector_.StripSensitiveData(&crammed_macs_2);
EXPECT_EQ(kCrammedMacs2Stripped, crammed_macs_2);
// Test case-sensitiveness (we shouldn't be case-senstive).
const std::string kCapsMacOrig = "AA:BB:CC:DD:EE:FF";
const std::string kCapsMacStripped = "00:00:00:00:00:01";
std::string caps_mac(kCapsMacOrig);
collector_.StripSensitiveData(&caps_mac);
EXPECT_EQ(kCapsMacStripped, caps_mac);
const std::string kLowerMacOrig = "aa:bb:cc:dd:ee:ff";
const std::string kLowerMacStripped = "00:00:00:00:00:01";
std::string lower_mac(kLowerMacOrig);
collector_.StripSensitiveData(&lower_mac);
EXPECT_EQ(kLowerMacStripped, lower_mac);
}
TEST_F(CrashCollectorTest, StripMacAddressesBulk) {
// Test calling StripSensitiveData w/ lots of MAC addresses in the "log".
// Test that stripping code handles more than 256 unique MAC addresses, since
// that overflows past the last byte...
// We'll write up some code that generates 258 unique MAC addresses. Sorta
// cheating since the code is very similar to the current code in
// StripSensitiveData(), but would catch if someone changed that later.
std::string lotsa_macs_orig;
std::string lotsa_macs_stripped;
int i;
for (i = 0; i < 258; i++) {
lotsa_macs_orig +=
StringPrintf(" 11:11:11:11:%02X:%02x", (i & 0xff00) >> 8, i & 0x00ff);
lotsa_macs_stripped += StringPrintf(
" 00:00:00:00:%02X:%02x", ((i + 1) & 0xff00) >> 8, (i + 1) & 0x00ff);
}
std::string lotsa_macs(lotsa_macs_orig);
collector_.StripMacAddresses(&lotsa_macs);
EXPECT_EQ(lotsa_macs_stripped, lotsa_macs);
}
TEST_F(CrashCollectorTest, StripSensitiveDataSample) {
// Test calling StripSensitiveData w/ some actual lines from a real crash;
// included two MAC addresses (though replaced them with some bogusness).
const std::string kCrashWithMacsOrig =
"<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)"
" filtered out\n"
"<7>[108539.540144] wlan0: authenticate with 11:22:33:44:55:66 (try 1)\n"
"<7>[108539.554973] wlan0: associate with 11:22:33:44:55:66 (try 1)\n"
"<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
" QCUSBNet Ethernet Device, 99:88:77:66:55:44\n"
"<7>[110964.314648] wlan0: deauthenticated from 11:22:33:44:55:66"
" (Reason: 6)\n"
"<7>[110964.325057] phy0: Removed STA 11:22:33:44:55:66\n"
"<7>[110964.325115] phy0: Destroyed STA 11:22:33:44:55:66\n"
"<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
" QCUSBNet Ethernet Device, 99:88:77:66:55:44\n"
"<7>[111566.131728] PM: Entering mem sleep\n";
const std::string kCrashWithMacsStripped =
"<6>[111567.195339] ata1.00: ACPI cmd ef/10:03:00:00:00:a0 (SET FEATURES)"
" filtered out\n"
"<7>[108539.540144] wlan0: authenticate with 00:00:00:00:00:01 (try 1)\n"
"<7>[108539.554973] wlan0: associate with 00:00:00:00:00:01 (try 1)\n"
"<6>[110136.587583] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
" QCUSBNet Ethernet Device, 00:00:00:00:00:02\n"
"<7>[110964.314648] wlan0: deauthenticated from 00:00:00:00:00:01"
" (Reason: 6)\n"
"<7>[110964.325057] phy0: Removed STA 00:00:00:00:00:01\n"
"<7>[110964.325115] phy0: Destroyed STA 00:00:00:00:00:01\n"
"<6>[110969.219172] usb0: register 'QCUSBNet2k' at usb-0000:00:1d.7-2,"
" QCUSBNet Ethernet Device, 00:00:00:00:00:02\n"
"<7>[111566.131728] PM: Entering mem sleep\n";
std::string crash_with_macs(kCrashWithMacsOrig);
collector_.StripMacAddresses(&crash_with_macs);
EXPECT_EQ(kCrashWithMacsStripped, crash_with_macs);
}
TEST_F(CrashCollectorTest, StripEmailAddresses) {
std::string logs =
"Lorem ipsum dolor sit amet, consectetur adipiscing elit,"
" sed do eiusmod tempor incididunt ut labore et dolore \n"
"magna aliqua. Ut enim ad minim veniam, quis nostrud "
"exercitation ullamco foo.bar+baz@secret.com laboris \n"
"nisi ut aliquip ex ea commodo consequat. Duis aute "
"irure dolor in reprehenderit (support@example.com) in \n"
"voluptate velit esse cillum dolore eu fugiat nulla "
"pariatur. Excepteur sint occaecat:abuse@dev.reallylong,\n"
"cupidatat non proident, sunt in culpa qui officia "
"deserunt mollit anim id est laborum.";
collector_.StripEmailAddresses(&logs);
EXPECT_EQ(0, logs.find("Lorem ipsum"));
EXPECT_EQ(std::string::npos, logs.find("foo.bar"));
EXPECT_EQ(std::string::npos, logs.find("secret"));
EXPECT_EQ(std::string::npos, logs.find("support"));
EXPECT_EQ(std::string::npos, logs.find("example.com"));
EXPECT_EQ(std::string::npos, logs.find("abuse"));
EXPECT_EQ(std::string::npos, logs.find("dev.reallylong"));
}
TEST_F(CrashCollectorTest, GetCrashDirectoryInfo) {
FilePath path;
const int kRootUid = 0;
const int kRootGid = 0;
const int kNtpUid = 5;
const int kChronosUid = 1000;
const int kChronosGid = 1001;
const mode_t kExpectedSystemMode = 01755;
const mode_t kExpectedUserMode = 0755;
mode_t directory_mode;
uid_t directory_owner;
gid_t directory_group;
path = collector_.GetCrashDirectoryInfo(kRootUid, kChronosUid, kChronosGid,
&directory_mode, &directory_owner,
&directory_group);
EXPECT_EQ("/var/spool/crash", path.value());
EXPECT_EQ(kExpectedSystemMode, directory_mode);
EXPECT_EQ(kRootUid, directory_owner);
EXPECT_EQ(kRootGid, directory_group);
path = collector_.GetCrashDirectoryInfo(kNtpUid, kChronosUid, kChronosGid,
&directory_mode, &directory_owner,
&directory_group);
EXPECT_EQ("/var/spool/crash", path.value());
EXPECT_EQ(kExpectedSystemMode, directory_mode);
EXPECT_EQ(kRootUid, directory_owner);
EXPECT_EQ(kRootGid, directory_group);
auto* mock = new org::chromium::SessionManagerInterfaceProxyMock;
test_util::SetActiveSessions(mock, {{"user", "hashcakes"}});
collector_.session_manager_proxy_.reset(mock);
EXPECT_EQ(collector_.IsUserSpecificDirectoryEnabled(), true);
path = collector_.GetCrashDirectoryInfo(kChronosUid, kChronosUid, kChronosGid,
&directory_mode, &directory_owner,
&directory_group);
EXPECT_EQ("/home/user/hashcakes/crash", path.value());
EXPECT_EQ(kExpectedUserMode, directory_mode);
EXPECT_EQ(kChronosUid, directory_owner);
EXPECT_EQ(kChronosGid, directory_group);
}
TEST_F(CrashCollectorTest, FormatDumpBasename) {
struct tm tm = {};
tm.tm_sec = 15;
tm.tm_min = 50;
tm.tm_hour = 13;
tm.tm_mday = 23;
tm.tm_mon = 4;
tm.tm_year = 110;
tm.tm_isdst = -1;
std::string basename = collector_.FormatDumpBasename("foo", mktime(&tm), 100);
ASSERT_EQ("foo.20100523.135015.100", basename);
}
TEST_F(CrashCollectorTest, GetCrashPath) {
EXPECT_EQ("/var/spool/crash/myprog.20100101.1200.1234.core",
collector_
.GetCrashPath(FilePath("/var/spool/crash"),
"myprog.20100101.1200.1234", "core")
.value());
EXPECT_EQ("/home/chronos/user/crash/chrome.20100101.1200.1234.dmp",
collector_
.GetCrashPath(FilePath("/home/chronos/user/crash"),
"chrome.20100101.1200.1234", "dmp")
.value());
}
TEST_F(CrashCollectorTest, ParseProcessTicksFromStat) {
uint64_t ticks;
EXPECT_FALSE(CrashCollector::ParseProcessTicksFromStat("", &ticks));
EXPECT_FALSE(CrashCollector::ParseProcessTicksFromStat("123 (foo)", &ticks));
constexpr char kTruncatedStat[] =
"234641 (cat) R 234581 234641 234581 34821 234641 4194304 117 0 0 0 0 0 "
"0 0 20 0 1 0";
EXPECT_FALSE(
CrashCollector::ParseProcessTicksFromStat(kTruncatedStat, &ticks));
constexpr char kInvalidStat[] =
"234641 (cat) R 234581 234641 234581 34821 234641 4194304 117 0 0 0 0 0 "
"0 0 20 0 1 0 foo";
EXPECT_FALSE(CrashCollector::ParseProcessTicksFromStat(kInvalidStat, &ticks));
// Executable name is ") (".
constexpr char kStat[] =
"234641 () () R 234581 234641 234581 34821 234641 4194304 117 0 0 0 0 0 "
"0 0 20 0 1 0 2092891 6090752 182 18446744073709551615 94720364494848 "
"94720364525584 140735323062016 0 0 0 0 0 0 0 0 0 17 32 0 0 0 0 0 "
"94720366623824 94720366625440 94720371765248 140735323070153 "
"140735323070173 140735323070173 140735323074543 0";
EXPECT_TRUE(CrashCollector::ParseProcessTicksFromStat(kStat, &ticks));
EXPECT_EQ(2092891, ticks);
}
TEST_F(CrashCollectorTest, GetUptime) {
base::TimeDelta uptime_at_process_start;
EXPECT_TRUE(CrashCollector::GetUptimeAtProcessStart(
getpid(), &uptime_at_process_start));
base::TimeDelta uptime;
EXPECT_TRUE(CrashCollector::GetUptime(&uptime));
EXPECT_GT(uptime, uptime_at_process_start);
}
bool CrashCollectorTest::CheckHasCapacity() {
std::string full_message = StringPrintf("Crash directory %s already full",
test_dir_.value().c_str());
bool has_capacity = collector_.CheckHasCapacity(test_dir_);
bool has_message = FindLog(full_message.c_str());
EXPECT_EQ(has_message, !has_capacity);
return has_capacity;
}
TEST_F(CrashCollectorTest, CheckHasCapacityUsual) {
// Test kMaxCrashDirectorySize - 1 non-meta files can be added.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
ASSERT_TRUE(test_util::CreateFile(
test_dir_.Append(StringPrintf("file%d.core", i)), ""));
EXPECT_TRUE(CheckHasCapacity());
}
// Test supplemental files fit with longer names.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
ASSERT_TRUE(test_util::CreateFile(
test_dir_.Append(StringPrintf("file%d.log.gz", i)), ""));
EXPECT_TRUE(CheckHasCapacity());
}
// Test an additional kMaxCrashDirectorySize - 1 meta files fit.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
ASSERT_TRUE(test_util::CreateFile(
test_dir_.Append(StringPrintf("file%d.meta", i)), ""));
EXPECT_TRUE(CheckHasCapacity());
}
// Test an additional kMaxCrashDirectorySize meta files don't fit.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize; ++i) {
ASSERT_TRUE(test_util::CreateFile(
test_dir_.Append(StringPrintf("overage%d.meta", i)), ""));
EXPECT_FALSE(CheckHasCapacity());
}
}
TEST_F(CrashCollectorTest, CheckHasCapacityCorrectBasename) {
// Test kMaxCrashDirectorySize - 1 files can be added.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 1; ++i) {
ASSERT_TRUE(test_util::CreateFile(
test_dir_.Append(StringPrintf("file.%d.core", i)), ""));
EXPECT_TRUE(CheckHasCapacity());
}
ASSERT_TRUE(test_util::CreateFile(test_dir_.Append("file.last.core"), ""));
EXPECT_FALSE(CheckHasCapacity());
}
TEST_F(CrashCollectorTest, CheckHasCapacityStrangeNames) {
// Test many files with different extensions and same base fit.
for (int i = 0; i < 5 * CrashCollector::kMaxCrashDirectorySize; ++i) {
ASSERT_TRUE(
test_util::CreateFile(test_dir_.Append(StringPrintf("a.%d", i)), ""));
EXPECT_TRUE(CheckHasCapacity());
}
// Test dot files are treated as individual files.
for (int i = 0; i < CrashCollector::kMaxCrashDirectorySize - 2; ++i) {
ASSERT_TRUE(test_util::CreateFile(
test_dir_.Append(StringPrintf(".file%d", i)), ""));
EXPECT_TRUE(CheckHasCapacity());
}
ASSERT_TRUE(test_util::CreateFile(test_dir_.Append("normal.meta"), ""));
EXPECT_TRUE(CheckHasCapacity());
}
TEST_F(CrashCollectorTest, MetaData) {
const char kMetaFileBasename[] = "generated.meta";
FilePath meta_file = test_dir_.Append(kMetaFileBasename);
FilePath lsb_release = test_dir_.Append("lsb-release");
FilePath payload_file = test_dir_.Append("payload-file");
FilePath payload_full_path;
std::string contents;
collector_.set_lsb_release_for_test(lsb_release);
const char kLsbContents[] =
"CHROMEOS_RELEASE_BOARD=lumpy\n"
"CHROMEOS_RELEASE_VERSION=6727.0.2015_01_26_0853\n"
"CHROMEOS_RELEASE_NAME=Chromium OS\n";
ASSERT_TRUE(test_util::CreateFile(lsb_release, kLsbContents));
const char kPayload[] = "foo";
ASSERT_TRUE(test_util::CreateFile(payload_file, kPayload));
collector_.AddCrashMetaData("foo", "bar");
ASSERT_TRUE(NormalizeFilePath(payload_file, &payload_full_path));
collector_.WriteCrashMetaData(meta_file, "kernel", payload_file.value());
EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
std::string expected_meta = StringPrintf(
"foo=bar\n"
"exec_name=kernel\n"
"ver=6727.0.2015_01_26_0853\n"
"payload=%s\n"
"payload_size=3\n"
"done=1\n",
payload_full_path.value().c_str());
EXPECT_EQ(expected_meta, contents);
// Test target of symlink is not overwritten.
payload_file = test_dir_.Append("payload2-file");
ASSERT_TRUE(test_util::CreateFile(payload_file, kPayload));
FilePath meta_symlink_path = test_dir_.Append("symlink.meta");
ASSERT_EQ(0, symlink(kMetaFileBasename, meta_symlink_path.value().c_str()));
ASSERT_TRUE(base::PathExists(meta_symlink_path));
brillo::ClearLog();
collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
payload_file.value());
// Target metadata contents should have stayed the same.
contents.clear();
EXPECT_TRUE(base::ReadFileToString(meta_file, &contents));
EXPECT_EQ(expected_meta, contents);
EXPECT_TRUE(FindLog("Unable to write"));
// Test target of dangling symlink is not created.
base::DeleteFile(meta_file, false);
ASSERT_FALSE(base::PathExists(meta_file));
brillo::ClearLog();
collector_.WriteCrashMetaData(meta_symlink_path, "kernel",
payload_file.value());
EXPECT_FALSE(base::PathExists(meta_file));
EXPECT_TRUE(FindLog("Unable to write"));
}
TEST_F(CrashCollectorTest, GetLogContents) {
FilePath config_file = test_dir_.Append("crash_config");
FilePath output_file = test_dir_.Append("crash_log");
const char kConfigContents[] =
"foobar=echo hello there | \\\n sed -e \"s/there/world/\"";
ASSERT_TRUE(test_util::CreateFile(config_file, kConfigContents));
base::DeleteFile(FilePath(output_file), false);
EXPECT_FALSE(collector_.GetLogContents(config_file, "barfoo", output_file));
EXPECT_FALSE(base::PathExists(output_file));
base::DeleteFile(FilePath(output_file), false);
EXPECT_TRUE(collector_.GetLogContents(config_file, "foobar", output_file));
ASSERT_TRUE(base::PathExists(output_file));
std::string contents;
EXPECT_TRUE(base::ReadFileToString(output_file, &contents));
EXPECT_EQ("hello world\n", contents);
}
TEST_F(CrashCollectorTest, GetProcessTree) {
const FilePath output_file = test_dir_.Append("log");
std::string contents;
ASSERT_TRUE(collector_.GetProcessTree(getpid(), output_file));
ASSERT_TRUE(base::PathExists(output_file));
EXPECT_TRUE(base::ReadFileToString(output_file, &contents));
EXPECT_LT(300, contents.size());
base::DeleteFile(FilePath(output_file), false);
ASSERT_TRUE(collector_.GetProcessTree(0, output_file));
ASSERT_TRUE(base::PathExists(output_file));
EXPECT_TRUE(base::ReadFileToString(output_file, &contents));
EXPECT_GT(100, contents.size());
}
TEST_F(CrashCollectorTest, TruncatedLog) {
FilePath config_file = test_dir_.Append("crash_config");
FilePath output_file = test_dir_.Append("crash_log");
const char kConfigContents[] = "foobar=echo These are log contents.";
ASSERT_TRUE(test_util::CreateFile(config_file, kConfigContents));
base::DeleteFile(FilePath(output_file), false);
collector_.max_log_size_ = 10;
EXPECT_TRUE(collector_.GetLogContents(config_file, "foobar", output_file));
ASSERT_TRUE(base::PathExists(output_file));
std::string contents;
EXPECT_TRUE(base::ReadFileToString(output_file, &contents));
EXPECT_EQ("These are \n<TRUNCATED>\n", contents);
}
// Check that the mode is reset properly.
TEST_F(CrashCollectorTest, CreateDirectoryWithSettingsMode) {
int mode;
EXPECT_TRUE(base::SetPosixFilePermissions(test_dir_, 0700));
EXPECT_TRUE(CrashCollector::CreateDirectoryWithSettings(
test_dir_, 0755, getuid(), getgid(), nullptr));
EXPECT_TRUE(base::GetPosixFilePermissions(test_dir_, &mode));
EXPECT_EQ(0755, mode);
}
// Check non-dir handling.
TEST_F(CrashCollectorTest, CreateDirectoryWithSettingsNonDir) {
const base::FilePath file = test_dir_.Append("file");
// Do not walk past a non-dir.
ASSERT_TRUE(test_util::CreateFile(file, ""));
EXPECT_FALSE(CrashCollector::CreateDirectoryWithSettings(
file.Append("subdir"), 0755, getuid(), getgid(), nullptr));
EXPECT_TRUE(base::PathExists(file));
EXPECT_FALSE(base::DirectoryExists(file));
// Remove files and create dirs.
EXPECT_TRUE(CrashCollector::CreateDirectoryWithSettings(file, 0755, getuid(),
getgid(), nullptr));
EXPECT_TRUE(base::DirectoryExists(file));
}
// Check we only create a single subdir.
TEST_F(CrashCollectorTest, CreateDirectoryWithSettingsSubdir) {
const base::FilePath subdir = test_dir_.Append("sub");
const base::FilePath subsubdir = subdir.Append("subsub");
// Accessing sub/subsub/ should fail.
EXPECT_FALSE(CrashCollector::CreateDirectoryWithSettings(
subsubdir, 0755, getuid(), getgid(), nullptr));
EXPECT_FALSE(base::PathExists(subdir));
// Accessing sub/ should work.
EXPECT_TRUE(CrashCollector::CreateDirectoryWithSettings(
subdir, 0755, getuid(), getgid(), nullptr));
EXPECT_TRUE(base::DirectoryExists(subdir));
// Accessing sub/subsub/ should now work.
EXPECT_TRUE(CrashCollector::CreateDirectoryWithSettings(
subsubdir, 0755, getuid(), getgid(), nullptr));
EXPECT_TRUE(base::DirectoryExists(subsubdir));
}
// Check symlink handling.
TEST_F(CrashCollectorTest, CreateDirectoryWithSettingsSymlinks) {
base::FilePath td;
// Do not walk an intermediate symlink (final target doesn't exist).
// test/sub/
// test/sym -> sub
// Then access test/sym/subsub/.
td = test_dir_.Append("1");
EXPECT_TRUE(base::CreateDirectory(td.Append("sub")));
EXPECT_TRUE(
base::CreateSymbolicLink(base::FilePath("sub"), td.Append("sym")));
EXPECT_FALSE(CrashCollector::CreateDirectoryWithSettings(
td.Append("sym1/subsub"), 0755, getuid(), getgid(), nullptr));
EXPECT_TRUE(base::IsLink(td.Append("sym")));
EXPECT_FALSE(base::PathExists(td.Append("sub/subsub")));
// Do not walk an intermediate symlink (final target exists).
// test/sub/subsub/
// test/sym -> sub
// Then access test/sym/subsub/.
td = test_dir_.Append("2");
EXPECT_TRUE(base::CreateDirectory(td.Append("sub/subsub")));
EXPECT_TRUE(
base::CreateSymbolicLink(base::FilePath("sub"), td.Append("sym")));
EXPECT_FALSE(CrashCollector::CreateDirectoryWithSettings(
td.Append("sym/subsub"), 0755, getuid(), getgid(), nullptr));
EXPECT_TRUE(base::IsLink(td.Append("sym")));
// If the final path is a symlink, we should remove it and make a dir.
// test/sub/
// test/sub/sym -> subsub
td = test_dir_.Append("3");
EXPECT_TRUE(base::CreateDirectory(td.Append("sub/subsub")));
EXPECT_TRUE(
base::CreateSymbolicLink(base::FilePath("subsub"), td.Append("sub/sym")));
EXPECT_TRUE(CrashCollector::CreateDirectoryWithSettings(
td.Append("sub/sym"), 0755, getuid(), getgid(), nullptr));
EXPECT_FALSE(base::IsLink(td.Append("sub/sym")));
EXPECT_TRUE(base::DirectoryExists(td.Append("sub/sym")));
// If the final path is a symlink, we should remove it and make a dir.
// test/sub/subsub
// test/sub/sym -> subsub
td = test_dir_.Append("4");
EXPECT_TRUE(base::CreateDirectory(td.Append("sub")));
EXPECT_TRUE(
base::CreateSymbolicLink(base::FilePath("subsub"), td.Append("sub/sym")));
EXPECT_TRUE(CrashCollector::CreateDirectoryWithSettings(
td.Append("sub/sym"), 0755, getuid(), getgid(), nullptr));
EXPECT_FALSE(base::IsLink(td.Append("sub/sym")));
EXPECT_TRUE(base::DirectoryExists(td.Append("sub/sym")));
EXPECT_FALSE(base::PathExists(td.Append("sub/subsub")));
}