Created
September 4, 2025 08:09
-
-
Save jbosboom/a6dc8def084d87ed128e8a5f74c270c2 to your computer and use it in GitHub Desktop.
call statx with controlled parameters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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