// SPDX-License-Identifier: GPL-2.0-or-later /* seitan - Syscall Expressive Interpreter, Transformer and Notifier * * cooker/calls/fs.c - Description of known filesystem-related system calls * * Copyright 2023 Red Hat GmbH * Author: Stefano Brivio */ /* stat ? fstat ? lstat ? lseek ? fcntl ? flock ~ fsync fdatasync truncate ftruncate getdents getcwd chdir fchdir mkdir rmdir rename creat link unlink symlink readlink chmod fchmod chown fchown fchownat lchown umask mknod mknodat mount umount2 swapon swapoff */ #include #include #define _GNU_SOURCE #include #include #include #include #include "../cooker.h" #include "../calls.h" static struct num modes[] = { { "S_ISUID", S_ISUID }, { "S_ISGID", S_ISGID }, { "S_IRWXU", S_IRWXU }, { "S_IRUSR", S_IRUSR }, { "S_IWUSR", S_IWUSR }, { "S_IXUSR", S_IXUSR }, { "S_IRWXG", S_IRWXG }, { "S_IRGRP", S_IRGRP }, { "S_IWGRP", S_IWGRP }, { "S_IXGRP", S_IXGRP }, { "S_IRWXO", S_IRWXO }, { "S_IROTH", S_IROTH }, { "S_IWOTH", S_IWOTH }, { "S_IXOTH", S_IXOTH }, { "S_ISVTX", S_ISVTX }, { 0 }, }; static struct num types[] = { { "S_IFSOCK", S_IFSOCK }, { "S_IFLNK", S_IFLNK }, { "S_IFREG", S_IFREG }, { "S_IFBLK", S_IFBLK }, { "S_IFDIR", S_IFDIR }, { "S_IFCHR", S_IFCHR }, { "S_IFIFO", S_IFIFO }, { 0 }, }; static struct arg mknod_args[] = { { 0, { "path", STRING, 0, 0, PATH_MAX, { 0 } } }, { 1, { "mode", INT, FLAGS | MASK, 0, 0, { .d_num = modes }, } }, { 1, { "type", INT, FLAGS | MASK, 0, 0, { .d_num = types }, } }, { 2, { "major", GNU_DEV_MAJOR, 0, 0, 0, { 0 }, } }, { 2, { "minor", GNU_DEV_MINOR, 0, 0, 0, { 0 }, } }, { 0 } }; static struct arg mknodat_args[] = { /* No dirfd: we only support absolute paths at the moment */ { 1, { "path", STRING, 0, 0, PATH_MAX, { 0 } } }, { 2, { "mode", INT, FLAGS | MASK, 0, 0, { .d_num = modes }, } }, { 2, { "type", INT, FLAGS | MASK, 0, 0, { .d_num = types }, } }, { 3, { "major", GNU_DEV_MAJOR, 0, 0, 0, { 0 }, } }, { 3, { "minor", GNU_DEV_MINOR, 0, 0, 0, { 0 }, } }, { 0 } }; static struct arg chown_args[] = { { 0, { "path", STRING, 0, 0, PATH_MAX, { 0 } } }, { 1, { "uid", UID_T, 0, 0, 0, { .d_num = modes }, } }, { 2, { "gid", GID_T, 0, 0, 0, { 0 }, } }, { 0 } }; static struct num xattr_flags[] = { { "create", XATTR_CREATE }, { "replace", XATTR_REPLACE }, { 0 }, }; static struct arg fsetxattr_args[] = { { 0, { "fd", INT, FD, 0, 0, { 0 } } }, { 0, { "path", FDPATH, FD, 0, 0, { 0 } } }, { 1, { "name", STRING, 0, 0, XATTR_NAME_MAX, { 0 } } }, { 2, { "value", STRING, WBUF, 0, XATTR_SIZE_MAX, { 0 } } }, { 3, { "size", LONG, 0, 0, 0, { 0 } } }, { 4, { "flags", INT, FLAGS, 0, 0, { .d_num = xattr_flags } } }, { 0 }, }; static struct arg fremovexattr_args[] = { { 0, { "fd", INT, FD, 0, 0, { 0 } } }, { 0, { "path", FDPATH, FD, 0, 0, { 0 } } }, { 1, { "name", STRING, 0, 0, XATTR_NAME_MAX, { 0 } } }, { 0 }, }; /* enum fid_type copied (without comments) from include/linux/exportfs.h of * Linux 6.9. Just copied and pasted like that? Is that an acceptable practice? * Well then, let's say I _vendored_ it. */ enum fid_type { FILEID_ROOT = 0, FILEID_INO32_GEN = 1, FILEID_INO32_GEN_PARENT = 2, FILEID_BTRFS_WITHOUT_PARENT = 0x4d, FILEID_BTRFS_WITH_PARENT = 0x4e, FILEID_BTRFS_WITH_PARENT_ROOT = 0x4f, FILEID_UDF_WITHOUT_PARENT = 0x51, FILEID_UDF_WITH_PARENT = 0x52, FILEID_NILFS_WITHOUT_PARENT = 0x61, FILEID_NILFS_WITH_PARENT = 0x62, FILEID_FAT_WITHOUT_PARENT = 0x71, FILEID_FAT_WITH_PARENT = 0x72, FILEID_INO64_GEN = 0x81, FILEID_INO64_GEN_PARENT = 0x82, FILEID_LUSTRE = 0x97, FILEID_BCACHEFS_WITHOUT_PARENT = 0xb1, FILEID_BCACHEFS_WITH_PARENT = 0xb2, FILEID_KERNFS = 0xfe, FILEID_INVALID = 0xff, }; static struct num handle_types[] = { { "root", FILEID_ROOT }, { "ino32_gen", FILEID_INO32_GEN }, { "ino32_gen_parent", FILEID_INO32_GEN_PARENT }, { "btrfs_with_parent", FILEID_BTRFS_WITH_PARENT }, { "btrfs_without_parent", FILEID_BTRFS_WITHOUT_PARENT }, { "udf_with_parent", FILEID_UDF_WITH_PARENT }, { "udf_without_parent", FILEID_UDF_WITHOUT_PARENT }, { "fat_with_parent", FILEID_FAT_WITH_PARENT }, { "fat_without_parent", FILEID_FAT_WITHOUT_PARENT }, { "ino64_gen", FILEID_INO64_GEN }, { "ino64_gen_parent", FILEID_INO64_GEN_PARENT }, { "lustre", FILEID_LUSTRE }, { "bcachefs_with_parent", FILEID_BCACHEFS_WITH_PARENT }, { "bcachefs_without_parent", FILEID_BCACHEFS_WITHOUT_PARENT }, { "kernfs", FILEID_KERNFS }, { "invalid", FILEID_INVALID }, { 0 }, }; static struct field file_handle[] = { { "handle_bytes", LONG, SIZE, offsetof(struct file_handle, handle_bytes), 0, { .d_size = (intptr_t)&file_handle[2] }, }, { "handle_type", INT, FLAGS, offsetof(struct file_handle, handle_type), 0, { .d_num = handle_types }, }, { "f_handle", STRING, 0, offsetof(struct file_handle, f_handle), MAX_HANDLE_SZ, { 0 }, }, { 0 } }; static struct num open_flags[] = { { "rdonly", O_RDONLY }, { "wronly", O_WRONLY }, { "rdwr", O_RDWR }, { 0 }, }; static struct arg open_by_handle_at_args[] = { { 0, { "mount", FDMOUNT, 0, 0, 0, { 0 } } }, { 0, { "mount_fd", INT, 0, 0, 0, { 0 } } }, { 1, { "handle", STRUCT, 0, 0, sizeof(struct file_handle) + MAX_HANDLE_SZ, { .d_struct = file_handle } } }, { 2, { "flags", INT, MASK | FLAGS, 0, 0, { .d_num = open_flags } } }, { 0 }, }; struct call syscalls_fs[] = { { __NR_mknod, "mknod", mknod_args }, { __NR_mknodat, "mknodat", mknodat_args }, { __NR_chown, "chown", chown_args }, { __NR_lchown, "lchown", chown_args }, { __NR_fsetxattr, "fsetxattr", fsetxattr_args }, /* fd, name, value, size, flags */ { __NR_fremovexattr, "fremovexattr", fremovexattr_args }, /* fd, name */ { __NR_open_by_handle_at, "open_by_handle_at", open_by_handle_at_args }, { 0 }, };