// 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 <sbrivio@redhat.com>
*/
/*
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 <asm-generic/unistd.h>
#include <sys/syscall.h>
#define _GNU_SOURCE
#include <sys/stat.h>
#include <sys/xattr.h>
#include <fcntl.h>
#include <linux/limits.h>
#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 },
};