I've attached a test program 'fsmount.c'. This can be used along with the test script below to show a kernel bug when calling fsconfig(2) with security options on a btrfs filesystem. This problem only occurs using fsconfig(2) when attempting to add security options. Setting a native btrfs option (e.g. flushoncommit) works. Copy the statements below into test.sh and run with the fs name. Other fs will work such as ext4, xfs. Only btrfs will fail. #!/bin/sh fs_name=$1 mkdir -p /mnt/selinux-testsuite dd if=/dev/zero of=./fstest bs=4096 count=27904 dev=`losetup -f` losetup $dev ./fstest mkfs.$fs_name $dev /usr/bin/systemctl stop udisks2 # Stops crap appearing in journal log # mount(2) works: #mount -t $fs_name -o "rootcontext=system_u:object_r:unconfined_t:s0" $dev /mnt/selinux-testsuite # This native btrfs "flushoncommit" option will work with fsconfig(2): #./fsmount $fs_name $dev /mnt/selinux-testsuite "flushoncommit" # This will not: ./fsmount $fs_name $dev /mnt/selinux-testsuite "rootcontext=system_u:object_r:unconfined_t:s0" # rootcontext fails with journal entry: SELinux: mount invalid. # Same superblock, different security settings for (dev loop0, type btrfs) umount /mnt/selinux-testsuite losetup -d $dev /usr/bin/systemctl start udisks2 rm -f ./fstest
/* cc fsmount.c -o fsmount -Wall */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <sys/prctl.h> #include <linux/mount.h> #include <linux/unistd.h> int fsopen(const char *fs_name, unsigned int flags) { return syscall(__NR_fsopen, fs_name, flags); } int fsconfig(int fsfd, unsigned int cmd, const char *key, const void *val, int aux) { return syscall(__NR_fsconfig, fsfd, cmd, key, val, aux); } int fsmount(int fsfd, unsigned int flags, unsigned int ms_flags) { return syscall(__NR_fsmount, fsfd, flags, ms_flags); } int move_mount(int from_dfd, const char *from_pathname, int to_dfd, const char *to_pathname, unsigned int flags) { return syscall(__NR_move_mount, from_dfd, from_pathname, to_dfd, to_pathname, flags); } #define MAX_OPS 10 int fsconfig_opts(int fd, char *src, char *opts) { int ret, i, max_entries = 0; int cmd[MAX_OPS]; char *key[MAX_OPS], *value[MAX_OPS]; char *src_str = "source"; cmd[0] = FSCONFIG_SET_STRING; key[0] = src_str; value[0] = src; for (i = 1; i < MAX_OPS; i++) { value[i] = strsep(&opts, ","); if (!value[i]) { max_entries = i + 1; break; } cmd[i] = FSCONFIG_SET_STRING; } for (i = 1; value[i] != NULL; i++) { key[i] = strsep(&value[i], "="); if (!value[i]) cmd[i] = FSCONFIG_SET_FLAG; } cmd[i] = FSCONFIG_CMD_CREATE; key[i] = NULL; value[i] = NULL; for (i = 0; i != max_entries; i++) { printf("fsconfig(0x%x, %s, %s, 0)\n", cmd[i], key[i], value[i]); ret = fsconfig(fd, cmd[i], key[i], value[i], 0); if (ret < 0) { fprintf(stderr, "Failed fsconfig(2): %s\n", strerror(errno)); return -1; } } return 0; } int main(int argc, char *argv[]) { int ret, fsfd, mfd; unsigned int mount_attrs = 0; char *opts; if (argc != 5) { fprintf(stderr, "usage: %s <type> <src> <tgt> <opts>\n", argv[0]); return 1; } fsfd = fsopen(argv[1], 0); if (fsfd < 0) { fprintf(stderr, "Failed fsopen(2): %s\n", strerror(errno)); return -1; } if (!strncmp (argv[1], "nfs", 3)) mount_attrs = MS_NODEV; opts = strdup(argv[4]); ret = fsconfig_opts(fsfd, argv[2], opts); if (ret < 0) { fprintf(stderr, "Failed to add options: %s\n", argv[4]); close(fsfd); return -1; } printf("Successfully added options: %s\n", argv[4]); mfd = fsmount(fsfd, 0, mount_attrs); if (mfd < 0) { fprintf(stderr, "Failed fsmount(2): %s\n", strerror(errno)); return -1; } close(fsfd); ret = move_mount(mfd, "", AT_FDCWD, argv[3], MOVE_MOUNT_F_EMPTY_PATH); if (ret < 0) { fprintf(stderr, "Failed move_mount(2): %s\n", strerror(errno)); return -1; } close(mfd); printf("Successfully mounted on: %s\n", argv[3]); return 0; }