blob: d534faaf50454686782be497dc0ff444ccb07336 [file] [log] [blame]
// Copyright 2020 The Fuchsia 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 <fcntl.h>
#include <unistd.h>
#include <iostream>
#include <optional>
#include <string>
#include <thread>
#include <vector>
#include <fbl/unique_fd.h>
#include "src/storage/fs_test/fs_test_fixture.h"
namespace fs_test {
namespace {
using DirectoryMaxTest = FilesystemTest;
// Hopefully not pushing against any 'max file length' boundaries, but large enough to fill a
// directory quickly.
constexpr int kLargePathLength = 128;
TEST_P(DirectoryMaxTest, Max) {
// Write the maximum number of files to a directory
const std::string dir = "dir/";
ASSERT_EQ(mkdir(GetPath(dir).c_str(), 0777), 0);
class Context {
public:
explicit Context(std::string prefix) : path_prefix_(std::move(prefix)) {}
void fill() {
for (;; ++cnt_) {
const std::string path = path_prefix_ + std::to_string(cnt_);
fbl::unique_fd fd(open(path.c_str(), O_RDWR | O_CREAT | O_EXCL, 0644));
if (!fd) {
break;
}
}
}
void unlink_all() {
for (cnt_ -= cnt_; cnt_ >= 0; cnt_--) {
const std::string path = path_prefix_ + std::to_string(cnt_);
int r = unlink(path.c_str());
if (r != 0) {
err_ = true;
break;
}
}
}
bool err() const { return err_; }
int count() const { return cnt_; }
private:
std::string path_prefix_;
int cnt_ = 0;
bool err_ = false;
};
const std::string path_prefix = GetPath(dir + std::string(kLargePathLength, '.'));
std::vector<Context> contexts;
for (int i = 0; i < 16; i++) {
contexts.emplace_back(Context(path_prefix + std::to_string(i) + "_"));
}
std::vector<std::thread> fillers;
for (auto& context : contexts) {
fillers.emplace_back(std::thread([&context]() { context.fill(); }));
}
for (auto& thread : fillers) {
thread.join();
}
int total_files = 0;
for (const auto& context : contexts) {
total_files += context.count();
}
std::cerr << "Wrote a total of " << total_files << "." << std::endl;
std::cerr << "Starting unmount." << std::endl;
EXPECT_EQ(fs().Unmount().status_value(), ZX_OK);
std::cerr << "Starting fsck." << std::endl;
EXPECT_EQ(fs().Fsck().status_value(), ZX_OK);
std::cerr << "Starting mount." << std::endl;
EXPECT_EQ(fs().Mount().status_value(), ZX_OK);
std::cerr << "Time to clean up." << std::endl;
std::vector<std::thread> unlinkers;
for (auto& context : contexts) {
unlinkers.emplace_back(std::thread([&context]() { context.unlink_all(); }));
}
for (auto& thread : unlinkers) {
thread.join();
}
for (auto& context : contexts) {
ASSERT_FALSE(context.err());
}
std::cerr << "Done." << std::endl;
}
INSTANTIATE_TEST_SUITE_P(
/*no prefix*/, DirectoryMaxTest,
testing::ValuesIn(MapAndFilterAllTestFilesystems(
[](TestFilesystemOptions options) -> std::optional<TestFilesystemOptions> {
// Filesystems such as memfs cannot run this test because they OOM (as expected, given
// memory is the limiting factor).
if (options.filesystem->GetTraits().in_memory)
return std::nullopt;
if (!options.filesystem->GetTraits().has_directory_size_limit &&
!options.has_min_volume_size) {
// Fatfs is slow and, other than the root directory on FAT12/16, is limited by the size
// of the ram-disk rather than a directory size limit, so use a small ram-disk to keep
// run-time reasonable, and do the same for other filesystems that don't have a
// directory size limit.
options.device_block_count = options.filesystem->GetTraits().is_slow ? 256 : 4096;
}
return options;
})),
testing::PrintToStringParamName());
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DirectoryMaxTest);
} // namespace
} // namespace fs_test