Skip to content

Commit

Permalink
feat: allow uid/gid override in filesystem and FUSE driver (see gh #244)
Browse files Browse the repository at this point in the history
  • Loading branch information
mhx committed Nov 21, 2024
1 parent 1ecec97 commit a9a1e8a
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 2 deletions.
8 changes: 8 additions & 0 deletions doc/dwarfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ options:
If you have a lot of CPUs, increasing this number can help
speed up access to files in the filesystem.

- `-o uid=`*num*:
Override the user ID for the whole file system. This option
is not supported on Windows.

- `-o gid=`*num*:
Override the group ID for the whole file system. This option
is not supported on Windows.

- `-o decratio=`*value*:
The ratio over which a block is fully decompressed. Blocks
are only decompressed partially, so each block has to carry
Expand Down
7 changes: 7 additions & 0 deletions include/dwarfs/reader/metadata_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,20 @@

#pragma once

#include <cstddef>
#include <optional>

#include <dwarfs/file_stat.h>

namespace dwarfs::reader {

struct metadata_options {
bool enable_nlink{false};
bool readonly{false};
bool check_consistency{false};
size_t block_size{512};
std::optional<file_stat::uid_type> fs_uid{};
std::optional<file_stat::gid_type> fs_gid{};
};

} // namespace dwarfs::reader
4 changes: 2 additions & 2 deletions src/reader/internal/metadata_v2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1800,8 +1800,8 @@ metadata_<LoggerPolicy>::getattr_impl(inode_view iv,

stbuf.set_ino(inode + inode_offset_);
stbuf.set_blksize(options_.block_size);
stbuf.set_uid(iv.getuid());
stbuf.set_gid(iv.getgid());
stbuf.set_uid(options_.fs_uid.value_or(iv.getuid()));
stbuf.set_gid(options_.fs_gid.value_or(iv.getgid()));
stbuf.set_mtime(resolution * (timebase + ivr.mtime_offset()));

if (mtime_only) {
Expand Down
57 changes: 57 additions & 0 deletions test/dwarfs_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1229,6 +1229,63 @@ TEST(filesystem, uid_gid_count) {
EXPECT_EQ(349999, st99999.gid());
}

TEST(filesystem, uid_gid_override) {
test::test_logger lgr;

auto input = std::make_shared<test::os_access_mock>();

input->add("", {1, 040755, 1, 0, 0, 10, 42, 0, 0, 0});
input->add("foo16.txt", {2, 0100755, 1, 60000, 65535, 5, 42, 0, 0, 0},
"hello");
input->add("foo32.txt", {3, 0100755, 1, 65536, 4294967295, 5, 42, 0, 0, 0},
"world");

auto fsimage = build_dwarfs(lgr, input, "null");

auto mm = std::make_shared<test::mmap_mock>(std::move(fsimage));

{
reader::filesystem_options opts{.metadata = {
.fs_uid = 99999,
.fs_gid = 80000,
}};

reader::filesystem_v2 fs(lgr, *input, mm, opts);

auto dev16 = fs.find("/foo16.txt");
auto dev32 = fs.find("/foo32.txt");

ASSERT_TRUE(dev16);
ASSERT_TRUE(dev32);

auto st16 = fs.getattr(dev16->inode());
auto st32 = fs.getattr(dev32->inode());

EXPECT_EQ(99999, st16.uid());
EXPECT_EQ(80000, st16.gid());
EXPECT_EQ(99999, st32.uid());
EXPECT_EQ(80000, st32.gid());
}

{
reader::filesystem_v2 fs(lgr, *input, mm);

auto dev16 = fs.find("/foo16.txt");
auto dev32 = fs.find("/foo32.txt");

ASSERT_TRUE(dev16);
ASSERT_TRUE(dev32);

auto st16 = fs.getattr(dev16->inode());
auto st32 = fs.getattr(dev32->inode());

EXPECT_EQ(60000, st16.uid());
EXPECT_EQ(65535, st16.gid());
EXPECT_EQ(65536, st32.uid());
EXPECT_EQ(4294967295, st32.gid());
}
}

TEST(section_index_regression, github183) {
static constexpr uint64_t section_offset_mask{(UINT64_C(1) << 48) - 1};

Expand Down
4 changes: 4 additions & 0 deletions test/manpage_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ TEST_P(manpage_coverage_test, options) {
if (tool.is_fuse) {
man_opts.erase("allow_root");
man_opts.erase("allow_other");
#ifdef _WIN32
man_opts.erase("uid");
man_opts.erase("gid");
#endif
} else {
EXPECT_TRUE(help_opts.contains("help"))
<< tool_name << " missing help option";
Expand Down
18 changes: 18 additions & 0 deletions test/tools_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
#ifndef _WIN32
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef __APPLE__
#include <sys/mount.h>
Expand Down Expand Up @@ -1055,6 +1056,7 @@ TEST_P(tools_test, end_to_end) {
#ifndef _WIN32
"-oenable_nlink",
"-oreadonly",
"-ouid=2345,gid=3456",
#endif
};

Expand All @@ -1075,6 +1077,7 @@ TEST_P(tools_test, end_to_end) {
#ifndef _WIN32
bool enable_nlink{false};
bool readonly{false};
bool uid_gid_override{false};
#endif

for (size_t i = 0; i < all_options.size(); ++i) {
Expand All @@ -1087,6 +1090,9 @@ TEST_P(tools_test, end_to_end) {
if (opt == "-oenable_nlink") {
enable_nlink = true;
}
if (opt.find("-ouid=") != std::string::npos) {
uid_gid_override = true;
}
#endif
args.push_back(opt);
}
Expand Down Expand Up @@ -1120,6 +1126,18 @@ TEST_P(tools_test, end_to_end) {
// This doesn't really work on Windows (yet)
EXPECT_TRUE(check_readonly(mountpoint / "format.sh", readonly))
<< runner.cmdline();
if (uid_gid_override) {
struct ::stat st;
ASSERT_EQ(0, ::lstat(mountpoint.string().c_str(), &st))
<< runner.cmdline();
EXPECT_EQ(st.st_uid, 2345) << runner.cmdline();
EXPECT_EQ(st.st_gid, 3456) << runner.cmdline();
ASSERT_EQ(0,
::lstat((mountpoint / "format.sh").string().c_str(), &st))
<< runner.cmdline();
EXPECT_EQ(st.st_uid, 2345) << runner.cmdline();
EXPECT_EQ(st.st_gid, 3456) << runner.cmdline();
}
#endif
auto perfmon =
dwarfs::getxattr(mountpoint, "user.dwarfs.driver.perfmon");
Expand Down
30 changes: 30 additions & 0 deletions tools/src/dwarfs_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,10 @@ struct options {
char const* cache_tidy_interval_str{nullptr}; // TODO: const?? -> use string?
char const* cache_tidy_max_age_str{nullptr}; // TODO: const?? -> use string?
char const* seq_detector_thresh_str{nullptr}; // TODO: const?? -> use string?
#ifndef _WIN32
char const* uid_str{nullptr}; // TODO: const?? -> use string?
char const* gid_str{nullptr}; // TODO: const?? -> use string?
#endif
#if DWARFS_PERFMON_ENABLED
char const* perfmon_enabled_str{nullptr}; // TODO: const?? -> use string?
char const* perfmon_trace_file_str{nullptr}; // TODO: const?? -> use string?
Expand All @@ -189,6 +193,10 @@ struct options {
std::chrono::milliseconds block_cache_tidy_interval{std::chrono::minutes(5)};
std::chrono::milliseconds block_cache_tidy_max_age{std::chrono::minutes{10}};
size_t seq_detector_threshold{kDefaultSeqDetectorThreshold};
#ifndef _WIN32
std::optional<file_stat::uid_type> fs_uid;
std::optional<file_stat::gid_type> fs_gid;
#endif
bool is_help{false};
#ifdef DWARFS_BUILTIN_MANPAGE
bool is_man{false};
Expand Down Expand Up @@ -236,6 +244,10 @@ constexpr struct ::fuse_opt dwarfs_opts[] = {
DWARFS_OPT("readahead=%s", readahead_str, 0),
DWARFS_OPT("debuglevel=%s", debuglevel_str, 0),
DWARFS_OPT("workers=%s", workers_str, 0),
#ifndef _WIN32
DWARFS_OPT("uid=%s", uid_str, 0),
DWARFS_OPT("gid=%s", gid_str, 0),
#endif
DWARFS_OPT("mlock=%s", mlock_str, 0),
DWARFS_OPT("decratio=%s", decompress_ratio_str, 0),
DWARFS_OPT("offset=%s", image_offset_str, 0),
Expand Down Expand Up @@ -1202,6 +1214,10 @@ void usage(std::ostream& os, std::filesystem::path const& progname) {
<< " -o blocksize=SIZE set file I/O block size (512K)\n"
<< " -o readahead=SIZE set readahead size (0)\n"
<< " -o workers=NUM number of worker threads (2)\n"
#ifndef _WIN32
<< " -o uid=NUM override user ID for file system\n"
<< " -o gid=NUM override group ID for file system\n"
#endif
<< " -o mlock=NAME mlock mode: (none), try, must\n"
<< " -o decratio=NUM ratio for full decompression (0.8)\n"
<< " -o offset=NUM|auto filesystem image offset in bytes (0)\n"
Expand Down Expand Up @@ -1449,6 +1465,10 @@ void load_filesystem(dwarfs_userdata& userdata) {
fsopts.metadata.enable_nlink = bool(opts.enable_nlink);
fsopts.metadata.readonly = bool(opts.readonly);
fsopts.metadata.block_size = opts.blocksize;
#ifndef _WIN32
fsopts.metadata.fs_uid = opts.fs_uid;
fsopts.metadata.fs_gid = opts.fs_gid;
#endif
fsopts.inode_offset = inode_offset;

if (opts.image_offset_str) {
Expand Down Expand Up @@ -1603,6 +1623,16 @@ int dwarfs_main(int argc, sys_char** argv, iolayer const& iol) {
opts.decompress_ratio =
opts.decompress_ratio_str ? to<double>(opts.decompress_ratio_str) : 0.8;

#ifndef _WIN32
if (opts.uid_str) {
opts.fs_uid = to<file_stat::uid_type>(opts.uid_str);
}

if (opts.gid_str) {
opts.fs_gid = to<file_stat::gid_type>(opts.gid_str);
}
#endif

if (opts.cache_tidy_strategy_str) {
if (auto it = cache_tidy_strategy_map.find(opts.cache_tidy_strategy_str);
it != cache_tidy_strategy_map.end()) {
Expand Down

0 comments on commit a9a1e8a

Please sign in to comment.