Skip to content

Instantly share code, notes, and snippets.

@jbosboom
Created September 4, 2025 08:09
Show Gist options
  • Select an option

  • Save jbosboom/a6dc8def084d87ed128e8a5f74c270c2 to your computer and use it in GitHub Desktop.

Select an option

Save jbosboom/a6dc8def084d87ed128e8a5f74c270c2 to your computer and use it in GitHub Desktop.
call statx with controlled parameters
// compile with g++ -std=c++23
// use strace --fault=statx to simulate being on an old kernel
#include <cstdio>
#include <cstring>
#include <ctime>
#include <vector>
#include <string>
#include <string_view>
#include <format>
#include <print>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysmacros.h>
#include <unistd.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
using std::string_view;
using namespace std::literals::string_view_literals;
const char* has(unsigned int mask, const struct statx& stx, unsigned int flag) {
const char* letters[] = {" ", " V", "R ", "RV"};
bool r = mask & flag, v = stx.stx_mask & flag;
return letters[(r << 1) | v];
}
const char* name_for_type(int type) {
switch (type) {
case S_IFIFO: return "fifo";
case S_IFCHR: return "character device";
case S_IFDIR: return "directory";
case S_IFBLK: return "block device";
case S_IFREG: return "regular";
case S_IFLNK: return "symlink";
case S_IFSOCK: return "socket";
default: return "unknown type";
}
}
std::string name_for_uid(uid_t uid) {
errno = 0;
auto* p = getpwuid(uid);
if (p) return p->pw_name;
if (errno) return strerrorname_np(errno);
return "(unknown)";
}
std::string name_for_gid(gid_t gid) {
errno = 0;
auto* p = getgrgid(gid);
if (p) return p->gr_name;
if (errno) return strerrorname_np(errno);
return "(unknown)";
}
std::string format_time(statx_timestamp t) {
std::time_t s = t.tv_sec;
std::tm tm;
localtime_r(&s, &tm);
std::string buf(' ', 80);
const char* format = "%F %T.@ %z";
buf.erase(std::strftime(buf.data(), buf.size()-1, format, &tm));
buf.replace(buf.find('@'), 1, std::format("{:09d}", t.tv_nsec));
return buf;
}
std::string decode_attrs(uint64_t attrs) {
std::string r;
auto process = [&attrs, &r](uint64_t bit, std::string_view name) {
if (attrs & bit) {
if (!r.empty()) r.append(" ");
r.append(name);
attrs &= ~bit;
}
};
process(STATX_ATTR_COMPRESSED, "compressed"sv);
process(STATX_ATTR_IMMUTABLE, "immutable"sv);
process(STATX_ATTR_APPEND, "append-only"sv);
process(STATX_ATTR_NODUMP, "nodump"sv);
process(STATX_ATTR_ENCRYPTED, "encrypted"sv);
process(STATX_ATTR_AUTOMOUNT, "automount"sv);
process(STATX_ATTR_VERITY, "verity"sv);
process(STATX_ATTR_WRITE_ATOMIC, "write-atomic"sv);
process(STATX_ATTR_DAX, "dax"sv);
process(STATX_ATTR_MOUNT_ROOT, "mountpoint"sv);
for (uint64_t i = 0; i < 64; ++i)
process(1 << i, std::format("[{}]", i));
return r;
}
int main(int argc, char* argv[]) {
std::vector<std::string_view> paths;
int dirfd = AT_FDCWD;
int flags = AT_NO_AUTOMOUNT;
std::optional<unsigned int> mask;
bool raw_syscall = false;
for (int i = 1; i < argc; ++i) {
if (argv[i] == "--automount"sv)
flags &= ~AT_NO_AUTOMOUNT;
else if (argv[i] == "--nofollow"sv)
flags |= AT_SYMLINK_NOFOLLOW;
else if (argv[i] == "--sync"sv)
flags |= AT_STATX_FORCE_SYNC;
else if (argv[i] == "--cached"sv)
flags |= AT_STATX_DONT_SYNC;
else if (argv[i] == "--type"sv)
mask = mask.value_or(0) | STATX_TYPE;
else if (argv[i] == "--mode"sv)
mask = mask.value_or(0) | STATX_MODE;
else if (argv[i] == "--nlink"sv)
mask = mask.value_or(0) | STATX_NLINK;
else if (argv[i] == "--uid"sv)
mask = mask.value_or(0) | STATX_UID;
else if (argv[i] == "--gid"sv)
mask = mask.value_or(0) | STATX_GID;
else if (argv[i] == "--atime"sv)
mask = mask.value_or(0) | STATX_ATIME;
else if (argv[i] == "--mtime"sv)
mask = mask.value_or(0) | STATX_MTIME;
else if (argv[i] == "--ctime"sv)
mask = mask.value_or(0) | STATX_CTIME;
else if (argv[i] == "--ino"sv)
mask = mask.value_or(0) | STATX_INO;
else if (argv[i] == "--size"sv)
mask = mask.value_or(0) | STATX_SIZE;
else if (argv[i] == "--blocks"sv)
mask = mask.value_or(0) | STATX_BLOCKS;
else if (argv[i] == "--basic-stats"sv || argv[i] == "--basic"sv)
mask = mask.value_or(0) | STATX_BASIC_STATS;
else if (argv[i] == "--btime"sv)
mask = mask.value_or(0) | STATX_BTIME;
else if (argv[i] == "--mnt-id"sv || argv[i] == "--mntid"sv)
mask = mask.value_or(0) | STATX_MNT_ID;
else if (argv[i] == "--dioalign"sv || argv[i] == "--dioalign"sv)
mask = mask.value_or(0) | STATX_DIOALIGN;
else if (argv[i] == "--mnt-id-unique"sv)
mask = mask.value_or(0) | STATX_MNT_ID_UNIQUE;
else if (argv[i] == "--subvol"sv)
mask = mask.value_or(0) | STATX_SUBVOL;
else if (argv[i] == "--write-atomic"sv)
mask = mask.value_or(0) | STATX_WRITE_ATOMIC;
else if (argv[i] == "--dio-read-align"sv || argv[i] == "--dio-readalign"sv ||
argv[i] == "--dio-read-align"sv)
mask = mask.value_or(0) | STATX_DIO_READ_ALIGN;
else if (argv[i] == "--zero-mask"sv)
mask = 0;
else if (argv[i] == "--raw-syscall"sv)
raw_syscall = true;
else if (!std::strncmp(argv[i], "--", 2)) {
std::println(stderr, "unrecognized option {}", argv[i]);
// (to operate on a file --foo, pass ./--foo)
return 2;
} else
paths.push_back(argv[i]);
}
if (!mask)
mask = STATX_BASIC_STATS | STATX_BTIME | STATX_MNT_ID | STATX_DIOALIGN | STATX_MNT_ID_UNIQUE | STATX_SUBVOL | STATX_WRITE_ATOMIC | STATX_DIO_READ_ALIGN;
for (string_view path : paths) {
struct statx stx = {};
if (raw_syscall) {
if (syscall(SYS_statx, dirfd, path.data(), flags, *mask, &stx) == -1) {
perror("statx (raw syscall)");
return 1;
}
} else {
if (statx(dirfd, path.data(), flags, *mask, &stx) == -1) {
perror("statx (wrapper)");
return 1;
}
}
std::println("-- path {}", path);
std::println("{} {} 0o{:o} {}", has(*mask, stx, STATX_TYPE), "type",
stx.stx_mode & S_IFMT, name_for_type(stx.stx_mode & S_IFMT));
std::println("{} {} 0o{:o}", has(*mask, stx, STATX_MODE), "mode",
stx.stx_mode & ~S_IFMT);
std::println("-V containing device {}:{} {}", stx.stx_dev_major, stx.stx_dev_minor,
makedev(stx.stx_dev_major, stx.stx_dev_minor));
std::println("{} {} {}", has(*mask, stx, STATX_INO), "inode", stx.stx_ino);
std::println("{} {} {}", has(*mask, stx, STATX_NLINK), "link count", stx.stx_nlink);
std::println("{} {} {}", has(*mask, stx, STATX_SIZE), "size", stx.stx_size);
std::println("{} {} {}", has(*mask, stx, STATX_BLOCKS), "blocks", stx.stx_blocks);
std::println("{} {} {} {}", has(*mask, stx, STATX_UID), "uid", stx.stx_uid, name_for_uid(stx.stx_uid));
std::println("{} {} {} {}", has(*mask, stx, STATX_GID), "gid", stx.stx_gid, name_for_gid(stx.stx_gid));
std::println("{} {} {} {} {}", has(*mask, stx, STATX_ATIME), "atime",
stx.stx_atime.tv_sec, stx.stx_atime.tv_nsec, format_time(stx.stx_atime));
std::println("{} {} {} {} {}", has(*mask, stx, STATX_MTIME), "mtime",
stx.stx_mtime.tv_sec, stx.stx_mtime.tv_nsec, format_time(stx.stx_mtime));
std::println("{} {} {} {} {}", has(*mask, stx, STATX_CTIME), "ctime",
stx.stx_ctime.tv_sec, stx.stx_ctime.tv_nsec, format_time(stx.stx_ctime));
std::println("{} {} {} {} {}", has(*mask, stx, STATX_BTIME), "btime",
stx.stx_btime.tv_sec, stx.stx_btime.tv_nsec, format_time(stx.stx_btime));
std::println("-{} represents device {}:{} {}",
(S_ISCHR(stx.stx_mode) || S_ISBLK(stx.stx_mode)) ? "V" : " ",
stx.stx_rdev_major, stx.stx_rdev_minor,
makedev(stx.stx_rdev_major, stx.stx_rdev_minor));
std::println("{} {} {}", has(*mask, stx, STATX_MNT_ID), "mount id", stx.stx_mnt_id);
std::println("{} {} {}", has(*mask, stx, STATX_MNT_ID_UNIQUE), "mount id (unique)", stx.stx_mnt_id);
std::println("{} {} {}", has(*mask, stx, STATX_SUBVOL), "subvol", stx.stx_subvol);
std::println("-V io block size {}", stx.stx_blksize);
std::println("{} {} {} {}", has(*mask, stx, STATX_DIOALIGN), "DIO alignment", stx.stx_dio_mem_align, stx.stx_dio_offset_align);
std::println("{} {} {}", has(*mask, stx, STATX_DIO_READ_ALIGN), "DIO read alignment", stx.stx_dio_read_offset_align);
std::println("{} {} {} {} {}", has(*mask, stx, STATX_WRITE_ATOMIC), "atomic write alignment",
stx.stx_atomic_write_unit_min, stx.stx_atomic_write_unit_max_opt,
stx.stx_atomic_write_unit_max, stx.stx_atomic_write_segments_max);
std::println("-V {} {:0x} {}", "attrs", stx.stx_attributes,
decode_attrs(stx.stx_attributes));
std::println("-V {} {:0x} {}", "supported attrs", stx.stx_attributes_mask,
decode_attrs(stx.stx_attributes_mask));
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment