mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 04:33:26 +02:00
Merge tag 'landlock-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux
Pull landlock updates from Mickaël Salaün: "Add support for Landlock to UML. To do this, this fixes the way hostfs manages inodes according to the underlying filesystem [1]. They are now properly handled as for other filesystems, which enables Landlock support (and probably other features). This also extends Landlock's tests with 6 pseudo filesystems, including hostfs" [1] https://lore.kernel.org/all/20230612191430.339153-1-mic@digikod.net/ * tag 'landlock-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux: selftests/landlock: Add hostfs tests selftests/landlock: Add tests for pseudo filesystems selftests/landlock: Make mounts configurable selftests/landlock: Add supports_filesystem() helper selftests/landlock: Don't create useless file layouts hostfs: Fix ephemeral inodes
This commit is contained in:
@@ -1214,13 +1214,6 @@ config COMPAT_32BIT_TIME
|
|||||||
config ARCH_NO_PREEMPT
|
config ARCH_NO_PREEMPT
|
||||||
bool
|
bool
|
||||||
|
|
||||||
config ARCH_EPHEMERAL_INODES
|
|
||||||
def_bool n
|
|
||||||
help
|
|
||||||
An arch should select this symbol if it doesn't keep track of inode
|
|
||||||
instances on its own, but instead relies on something else (e.g. the
|
|
||||||
host kernel for an UML kernel).
|
|
||||||
|
|
||||||
config ARCH_SUPPORTS_RT
|
config ARCH_SUPPORTS_RT
|
||||||
bool
|
bool
|
||||||
|
|
||||||
|
@@ -5,7 +5,6 @@ menu "UML-specific options"
|
|||||||
config UML
|
config UML
|
||||||
bool
|
bool
|
||||||
default y
|
default y
|
||||||
select ARCH_EPHEMERAL_INODES
|
|
||||||
select ARCH_HAS_CPU_FINALIZE_INIT
|
select ARCH_HAS_CPU_FINALIZE_INIT
|
||||||
select ARCH_HAS_FORTIFY_SOURCE
|
select ARCH_HAS_FORTIFY_SOURCE
|
||||||
select ARCH_HAS_GCOV_PROFILE_ALL
|
select ARCH_HAS_GCOV_PROFILE_ALL
|
||||||
|
@@ -65,6 +65,7 @@ struct hostfs_stat {
|
|||||||
unsigned long long blocks;
|
unsigned long long blocks;
|
||||||
unsigned int maj;
|
unsigned int maj;
|
||||||
unsigned int min;
|
unsigned int min;
|
||||||
|
dev_t dev;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern int stat_file(const char *path, struct hostfs_stat *p, int fd);
|
extern int stat_file(const char *path, struct hostfs_stat *p, int fd);
|
||||||
|
@@ -26,6 +26,7 @@ struct hostfs_inode_info {
|
|||||||
fmode_t mode;
|
fmode_t mode;
|
||||||
struct inode vfs_inode;
|
struct inode vfs_inode;
|
||||||
struct mutex open_mutex;
|
struct mutex open_mutex;
|
||||||
|
dev_t dev;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
|
static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
|
||||||
@@ -182,14 +183,6 @@ static char *follow_link(char *link)
|
|||||||
return ERR_PTR(n);
|
return ERR_PTR(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct inode *hostfs_iget(struct super_block *sb)
|
|
||||||
{
|
|
||||||
struct inode *inode = new_inode(sb);
|
|
||||||
if (!inode)
|
|
||||||
return ERR_PTR(-ENOMEM);
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf)
|
static int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -228,6 +221,7 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb)
|
|||||||
return NULL;
|
return NULL;
|
||||||
hi->fd = -1;
|
hi->fd = -1;
|
||||||
hi->mode = 0;
|
hi->mode = 0;
|
||||||
|
hi->dev = 0;
|
||||||
inode_init_once(&hi->vfs_inode);
|
inode_init_once(&hi->vfs_inode);
|
||||||
mutex_init(&hi->open_mutex);
|
mutex_init(&hi->open_mutex);
|
||||||
return &hi->vfs_inode;
|
return &hi->vfs_inode;
|
||||||
@@ -240,6 +234,7 @@ static void hostfs_evict_inode(struct inode *inode)
|
|||||||
if (HOSTFS_I(inode)->fd != -1) {
|
if (HOSTFS_I(inode)->fd != -1) {
|
||||||
close_file(&HOSTFS_I(inode)->fd);
|
close_file(&HOSTFS_I(inode)->fd);
|
||||||
HOSTFS_I(inode)->fd = -1;
|
HOSTFS_I(inode)->fd = -1;
|
||||||
|
HOSTFS_I(inode)->dev = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,6 +260,7 @@ static int hostfs_show_options(struct seq_file *seq, struct dentry *root)
|
|||||||
static const struct super_operations hostfs_sbops = {
|
static const struct super_operations hostfs_sbops = {
|
||||||
.alloc_inode = hostfs_alloc_inode,
|
.alloc_inode = hostfs_alloc_inode,
|
||||||
.free_inode = hostfs_free_inode,
|
.free_inode = hostfs_free_inode,
|
||||||
|
.drop_inode = generic_delete_inode,
|
||||||
.evict_inode = hostfs_evict_inode,
|
.evict_inode = hostfs_evict_inode,
|
||||||
.statfs = hostfs_statfs,
|
.statfs = hostfs_statfs,
|
||||||
.show_options = hostfs_show_options,
|
.show_options = hostfs_show_options,
|
||||||
@@ -512,18 +508,31 @@ static const struct address_space_operations hostfs_aops = {
|
|||||||
.write_end = hostfs_write_end,
|
.write_end = hostfs_write_end,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int read_name(struct inode *ino, char *name)
|
static int hostfs_inode_update(struct inode *ino, const struct hostfs_stat *st)
|
||||||
{
|
{
|
||||||
|
set_nlink(ino, st->nlink);
|
||||||
|
i_uid_write(ino, st->uid);
|
||||||
|
i_gid_write(ino, st->gid);
|
||||||
|
ino->i_atime =
|
||||||
|
(struct timespec64){ st->atime.tv_sec, st->atime.tv_nsec };
|
||||||
|
ino->i_mtime =
|
||||||
|
(struct timespec64){ st->mtime.tv_sec, st->mtime.tv_nsec };
|
||||||
|
ino->i_ctime =
|
||||||
|
(struct timespec64){ st->ctime.tv_sec, st->ctime.tv_nsec };
|
||||||
|
ino->i_size = st->size;
|
||||||
|
ino->i_blocks = st->blocks;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hostfs_inode_set(struct inode *ino, void *data)
|
||||||
|
{
|
||||||
|
struct hostfs_stat *st = data;
|
||||||
dev_t rdev;
|
dev_t rdev;
|
||||||
struct hostfs_stat st;
|
|
||||||
int err = stat_file(name, &st, -1);
|
|
||||||
if (err)
|
|
||||||
return err;
|
|
||||||
|
|
||||||
/* Reencode maj and min with the kernel encoding.*/
|
/* Reencode maj and min with the kernel encoding.*/
|
||||||
rdev = MKDEV(st.maj, st.min);
|
rdev = MKDEV(st->maj, st->min);
|
||||||
|
|
||||||
switch (st.mode & S_IFMT) {
|
switch (st->mode & S_IFMT) {
|
||||||
case S_IFLNK:
|
case S_IFLNK:
|
||||||
ino->i_op = &hostfs_link_iops;
|
ino->i_op = &hostfs_link_iops;
|
||||||
break;
|
break;
|
||||||
@@ -535,7 +544,7 @@ static int read_name(struct inode *ino, char *name)
|
|||||||
case S_IFBLK:
|
case S_IFBLK:
|
||||||
case S_IFIFO:
|
case S_IFIFO:
|
||||||
case S_IFSOCK:
|
case S_IFSOCK:
|
||||||
init_special_inode(ino, st.mode & S_IFMT, rdev);
|
init_special_inode(ino, st->mode & S_IFMT, rdev);
|
||||||
ino->i_op = &hostfs_iops;
|
ino->i_op = &hostfs_iops;
|
||||||
break;
|
break;
|
||||||
case S_IFREG:
|
case S_IFREG:
|
||||||
@@ -547,17 +556,42 @@ static int read_name(struct inode *ino, char *name)
|
|||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
ino->i_ino = st.ino;
|
HOSTFS_I(ino)->dev = st->dev;
|
||||||
ino->i_mode = st.mode;
|
ino->i_ino = st->ino;
|
||||||
set_nlink(ino, st.nlink);
|
ino->i_mode = st->mode;
|
||||||
i_uid_write(ino, st.uid);
|
return hostfs_inode_update(ino, st);
|
||||||
i_gid_write(ino, st.gid);
|
}
|
||||||
ino->i_atime = (struct timespec64){ st.atime.tv_sec, st.atime.tv_nsec };
|
|
||||||
ino->i_mtime = (struct timespec64){ st.mtime.tv_sec, st.mtime.tv_nsec };
|
static int hostfs_inode_test(struct inode *inode, void *data)
|
||||||
ino->i_ctime = (struct timespec64){ st.ctime.tv_sec, st.ctime.tv_nsec };
|
{
|
||||||
ino->i_size = st.size;
|
const struct hostfs_stat *st = data;
|
||||||
ino->i_blocks = st.blocks;
|
|
||||||
return 0;
|
return inode->i_ino == st->ino && HOSTFS_I(inode)->dev == st->dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct inode *hostfs_iget(struct super_block *sb, char *name)
|
||||||
|
{
|
||||||
|
struct inode *inode;
|
||||||
|
struct hostfs_stat st;
|
||||||
|
int err = stat_file(name, &st, -1);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return ERR_PTR(err);
|
||||||
|
|
||||||
|
inode = iget5_locked(sb, st.ino, hostfs_inode_test, hostfs_inode_set,
|
||||||
|
&st);
|
||||||
|
if (!inode)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
if (inode->i_state & I_NEW) {
|
||||||
|
unlock_new_inode(inode);
|
||||||
|
} else {
|
||||||
|
spin_lock(&inode->i_lock);
|
||||||
|
hostfs_inode_update(inode, &st);
|
||||||
|
spin_unlock(&inode->i_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return inode;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hostfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
static int hostfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
||||||
@@ -565,62 +599,48 @@ static int hostfs_create(struct mnt_idmap *idmap, struct inode *dir,
|
|||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
char *name;
|
char *name;
|
||||||
int error, fd;
|
int fd;
|
||||||
|
|
||||||
inode = hostfs_iget(dir->i_sb);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
error = PTR_ERR(inode);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
error = -ENOMEM;
|
|
||||||
name = dentry_name(dentry);
|
name = dentry_name(dentry);
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
goto out_put;
|
return -ENOMEM;
|
||||||
|
|
||||||
fd = file_create(name, mode & 0777);
|
fd = file_create(name, mode & 0777);
|
||||||
if (fd < 0)
|
if (fd < 0) {
|
||||||
error = fd;
|
__putname(name);
|
||||||
else
|
return fd;
|
||||||
error = read_name(inode, name);
|
}
|
||||||
|
|
||||||
|
inode = hostfs_iget(dir->i_sb, name);
|
||||||
__putname(name);
|
__putname(name);
|
||||||
if (error)
|
if (IS_ERR(inode))
|
||||||
goto out_put;
|
return PTR_ERR(inode);
|
||||||
|
|
||||||
HOSTFS_I(inode)->fd = fd;
|
HOSTFS_I(inode)->fd = fd;
|
||||||
HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE;
|
HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE;
|
||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_put:
|
|
||||||
iput(inode);
|
|
||||||
out:
|
|
||||||
return error;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry,
|
static struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode = NULL;
|
||||||
char *name;
|
char *name;
|
||||||
int err;
|
|
||||||
|
|
||||||
inode = hostfs_iget(ino->i_sb);
|
|
||||||
if (IS_ERR(inode))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
err = -ENOMEM;
|
|
||||||
name = dentry_name(dentry);
|
name = dentry_name(dentry);
|
||||||
if (name) {
|
if (name == NULL)
|
||||||
err = read_name(inode, name);
|
return ERR_PTR(-ENOMEM);
|
||||||
__putname(name);
|
|
||||||
|
inode = hostfs_iget(ino->i_sb, name);
|
||||||
|
__putname(name);
|
||||||
|
if (IS_ERR(inode)) {
|
||||||
|
if (PTR_ERR(inode) == -ENOENT)
|
||||||
|
inode = NULL;
|
||||||
|
else
|
||||||
|
return ERR_CAST(inode);
|
||||||
}
|
}
|
||||||
if (err) {
|
|
||||||
iput(inode);
|
|
||||||
inode = (err == -ENOENT) ? NULL : ERR_PTR(err);
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
return d_splice_alias(inode, dentry);
|
return d_splice_alias(inode, dentry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -704,35 +724,23 @@ static int hostfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
|
|||||||
char *name;
|
char *name;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
inode = hostfs_iget(dir->i_sb);
|
|
||||||
if (IS_ERR(inode)) {
|
|
||||||
err = PTR_ERR(inode);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = -ENOMEM;
|
|
||||||
name = dentry_name(dentry);
|
name = dentry_name(dentry);
|
||||||
if (name == NULL)
|
if (name == NULL)
|
||||||
goto out_put;
|
return -ENOMEM;
|
||||||
|
|
||||||
err = do_mknod(name, mode, MAJOR(dev), MINOR(dev));
|
err = do_mknod(name, mode, MAJOR(dev), MINOR(dev));
|
||||||
if (err)
|
if (err) {
|
||||||
goto out_free;
|
__putname(name);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
err = read_name(inode, name);
|
inode = hostfs_iget(dir->i_sb, name);
|
||||||
__putname(name);
|
__putname(name);
|
||||||
if (err)
|
if (IS_ERR(inode))
|
||||||
goto out_put;
|
return PTR_ERR(inode);
|
||||||
|
|
||||||
d_instantiate(dentry, inode);
|
d_instantiate(dentry, inode);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_free:
|
|
||||||
__putname(name);
|
|
||||||
out_put:
|
|
||||||
iput(inode);
|
|
||||||
out:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hostfs_rename2(struct mnt_idmap *idmap,
|
static int hostfs_rename2(struct mnt_idmap *idmap,
|
||||||
@@ -929,49 +937,40 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent)
|
|||||||
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
||||||
err = super_setup_bdi(sb);
|
err = super_setup_bdi(sb);
|
||||||
if (err)
|
if (err)
|
||||||
goto out;
|
return err;
|
||||||
|
|
||||||
/* NULL is printed as '(null)' by printf(): avoid that. */
|
/* NULL is printed as '(null)' by printf(): avoid that. */
|
||||||
if (req_root == NULL)
|
if (req_root == NULL)
|
||||||
req_root = "";
|
req_root = "";
|
||||||
|
|
||||||
err = -ENOMEM;
|
|
||||||
sb->s_fs_info = host_root_path =
|
sb->s_fs_info = host_root_path =
|
||||||
kasprintf(GFP_KERNEL, "%s/%s", root_ino, req_root);
|
kasprintf(GFP_KERNEL, "%s/%s", root_ino, req_root);
|
||||||
if (host_root_path == NULL)
|
if (host_root_path == NULL)
|
||||||
goto out;
|
return -ENOMEM;
|
||||||
|
|
||||||
root_inode = new_inode(sb);
|
root_inode = hostfs_iget(sb, host_root_path);
|
||||||
if (!root_inode)
|
if (IS_ERR(root_inode))
|
||||||
goto out;
|
return PTR_ERR(root_inode);
|
||||||
|
|
||||||
err = read_name(root_inode, host_root_path);
|
|
||||||
if (err)
|
|
||||||
goto out_put;
|
|
||||||
|
|
||||||
if (S_ISLNK(root_inode->i_mode)) {
|
if (S_ISLNK(root_inode->i_mode)) {
|
||||||
char *name = follow_link(host_root_path);
|
char *name;
|
||||||
if (IS_ERR(name)) {
|
|
||||||
err = PTR_ERR(name);
|
iput(root_inode);
|
||||||
goto out_put;
|
name = follow_link(host_root_path);
|
||||||
}
|
if (IS_ERR(name))
|
||||||
err = read_name(root_inode, name);
|
return PTR_ERR(name);
|
||||||
|
|
||||||
|
root_inode = hostfs_iget(sb, name);
|
||||||
kfree(name);
|
kfree(name);
|
||||||
if (err)
|
if (IS_ERR(root_inode))
|
||||||
goto out_put;
|
return PTR_ERR(root_inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
err = -ENOMEM;
|
|
||||||
sb->s_root = d_make_root(root_inode);
|
sb->s_root = d_make_root(root_inode);
|
||||||
if (sb->s_root == NULL)
|
if (sb->s_root == NULL)
|
||||||
goto out;
|
return -ENOMEM;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_put:
|
|
||||||
iput(root_inode);
|
|
||||||
out:
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct dentry *hostfs_read_sb(struct file_system_type *type,
|
static struct dentry *hostfs_read_sb(struct file_system_type *type,
|
||||||
|
@@ -36,6 +36,7 @@ static void stat64_to_hostfs(const struct stat64 *buf, struct hostfs_stat *p)
|
|||||||
p->blocks = buf->st_blocks;
|
p->blocks = buf->st_blocks;
|
||||||
p->maj = os_major(buf->st_rdev);
|
p->maj = os_major(buf->st_rdev);
|
||||||
p->min = os_minor(buf->st_rdev);
|
p->min = os_minor(buf->st_rdev);
|
||||||
|
p->dev = buf->st_dev;
|
||||||
}
|
}
|
||||||
|
|
||||||
int stat_file(const char *path, struct hostfs_stat *p, int fd)
|
int stat_file(const char *path, struct hostfs_stat *p, int fd)
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
config SECURITY_LANDLOCK
|
config SECURITY_LANDLOCK
|
||||||
bool "Landlock support"
|
bool "Landlock support"
|
||||||
depends on SECURITY && !ARCH_EPHEMERAL_INODES
|
depends on SECURITY
|
||||||
select SECURITY_PATH
|
select SECURITY_PATH
|
||||||
help
|
help
|
||||||
Landlock is a sandboxing mechanism that enables processes to restrict
|
Landlock is a sandboxing mechanism that enables processes to restrict
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
|
CONFIG_CGROUPS=y
|
||||||
|
CONFIG_CGROUP_SCHED=y
|
||||||
CONFIG_OVERLAY_FS=y
|
CONFIG_OVERLAY_FS=y
|
||||||
CONFIG_SECURITY_LANDLOCK=y
|
CONFIG_PROC_FS=y
|
||||||
CONFIG_SECURITY_PATH=y
|
|
||||||
CONFIG_SECURITY=y
|
CONFIG_SECURITY=y
|
||||||
|
CONFIG_SECURITY_LANDLOCK=y
|
||||||
CONFIG_SHMEM=y
|
CONFIG_SHMEM=y
|
||||||
CONFIG_TMPFS_XATTR=y
|
CONFIG_SYSFS=y
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
|
CONFIG_TMPFS_XATTR=y
|
||||||
|
1
tools/testing/selftests/landlock/config.um
Normal file
1
tools/testing/selftests/landlock/config.um
Normal file
@@ -0,0 +1 @@
|
|||||||
|
CONFIG_HOSTFS=y
|
@@ -10,6 +10,7 @@
|
|||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <linux/landlock.h>
|
#include <linux/landlock.h>
|
||||||
|
#include <linux/magic.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
#include <sys/sendfile.h>
|
#include <sys/sendfile.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/sysmacros.h>
|
#include <sys/sysmacros.h>
|
||||||
|
#include <sys/vfs.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
@@ -107,8 +109,10 @@ static bool fgrep(FILE *const inf, const char *const str)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool supports_overlayfs(void)
|
static bool supports_filesystem(const char *const filesystem)
|
||||||
{
|
{
|
||||||
|
char str[32];
|
||||||
|
int len;
|
||||||
bool res;
|
bool res;
|
||||||
FILE *const inf = fopen("/proc/filesystems", "r");
|
FILE *const inf = fopen("/proc/filesystems", "r");
|
||||||
|
|
||||||
@@ -119,11 +123,33 @@ static bool supports_overlayfs(void)
|
|||||||
if (!inf)
|
if (!inf)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
res = fgrep(inf, "nodev\toverlay\n");
|
/* filesystem can be null for bind mounts. */
|
||||||
|
if (!filesystem)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
len = snprintf(str, sizeof(str), "nodev\t%s\n", filesystem);
|
||||||
|
if (len >= sizeof(str))
|
||||||
|
/* Ignores too-long filesystem names. */
|
||||||
|
return true;
|
||||||
|
|
||||||
|
res = fgrep(inf, str);
|
||||||
fclose(inf);
|
fclose(inf);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool cwd_matches_fs(unsigned int fs_magic)
|
||||||
|
{
|
||||||
|
struct statfs statfs_buf;
|
||||||
|
|
||||||
|
if (!fs_magic)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (statfs(".", &statfs_buf))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return statfs_buf.f_type == fs_magic;
|
||||||
|
}
|
||||||
|
|
||||||
static void mkdir_parents(struct __test_metadata *const _metadata,
|
static void mkdir_parents(struct __test_metadata *const _metadata,
|
||||||
const char *const path)
|
const char *const path)
|
||||||
{
|
{
|
||||||
@@ -206,7 +232,26 @@ out:
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void prepare_layout(struct __test_metadata *const _metadata)
|
struct mnt_opt {
|
||||||
|
const char *const source;
|
||||||
|
const char *const type;
|
||||||
|
const unsigned long flags;
|
||||||
|
const char *const data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct mnt_opt mnt_tmp = {
|
||||||
|
.type = "tmpfs",
|
||||||
|
.data = "size=4m,mode=700",
|
||||||
|
};
|
||||||
|
|
||||||
|
static int mount_opt(const struct mnt_opt *const mnt, const char *const target)
|
||||||
|
{
|
||||||
|
return mount(mnt->source ?: mnt->type, target, mnt->type, mnt->flags,
|
||||||
|
mnt->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prepare_layout_opt(struct __test_metadata *const _metadata,
|
||||||
|
const struct mnt_opt *const mnt)
|
||||||
{
|
{
|
||||||
disable_caps(_metadata);
|
disable_caps(_metadata);
|
||||||
umask(0077);
|
umask(0077);
|
||||||
@@ -217,12 +262,28 @@ static void prepare_layout(struct __test_metadata *const _metadata)
|
|||||||
* for tests relying on pivot_root(2) and move_mount(2).
|
* for tests relying on pivot_root(2) and move_mount(2).
|
||||||
*/
|
*/
|
||||||
set_cap(_metadata, CAP_SYS_ADMIN);
|
set_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
ASSERT_EQ(0, unshare(CLONE_NEWNS));
|
ASSERT_EQ(0, unshare(CLONE_NEWNS | CLONE_NEWCGROUP));
|
||||||
ASSERT_EQ(0, mount("tmp", TMP_DIR, "tmpfs", 0, "size=4m,mode=700"));
|
ASSERT_EQ(0, mount_opt(mnt, TMP_DIR))
|
||||||
|
{
|
||||||
|
TH_LOG("Failed to mount the %s filesystem: %s", mnt->type,
|
||||||
|
strerror(errno));
|
||||||
|
/*
|
||||||
|
* FIXTURE_TEARDOWN() is not called when FIXTURE_SETUP()
|
||||||
|
* failed, so we need to explicitly do a minimal cleanup to
|
||||||
|
* avoid cascading errors with other tests that don't depend on
|
||||||
|
* the same filesystem.
|
||||||
|
*/
|
||||||
|
remove_path(TMP_DIR);
|
||||||
|
}
|
||||||
ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL));
|
ASSERT_EQ(0, mount(NULL, TMP_DIR, NULL, MS_PRIVATE | MS_REC, NULL));
|
||||||
clear_cap(_metadata, CAP_SYS_ADMIN);
|
clear_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void prepare_layout(struct __test_metadata *const _metadata)
|
||||||
|
{
|
||||||
|
prepare_layout_opt(_metadata, &mnt_tmp);
|
||||||
|
}
|
||||||
|
|
||||||
static void cleanup_layout(struct __test_metadata *const _metadata)
|
static void cleanup_layout(struct __test_metadata *const _metadata)
|
||||||
{
|
{
|
||||||
set_cap(_metadata, CAP_SYS_ADMIN);
|
set_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
@@ -231,6 +292,20 @@ static void cleanup_layout(struct __test_metadata *const _metadata)
|
|||||||
EXPECT_EQ(0, remove_path(TMP_DIR));
|
EXPECT_EQ(0, remove_path(TMP_DIR));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
FIXTURE(layout0) {};
|
||||||
|
/* clang-format on */
|
||||||
|
|
||||||
|
FIXTURE_SETUP(layout0)
|
||||||
|
{
|
||||||
|
prepare_layout(_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
FIXTURE_TEARDOWN(layout0)
|
||||||
|
{
|
||||||
|
cleanup_layout(_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
static void create_layout1(struct __test_metadata *const _metadata)
|
static void create_layout1(struct __test_metadata *const _metadata)
|
||||||
{
|
{
|
||||||
create_file(_metadata, file1_s1d1);
|
create_file(_metadata, file1_s1d1);
|
||||||
@@ -248,7 +323,7 @@ static void create_layout1(struct __test_metadata *const _metadata)
|
|||||||
create_file(_metadata, file1_s3d1);
|
create_file(_metadata, file1_s3d1);
|
||||||
create_directory(_metadata, dir_s3d2);
|
create_directory(_metadata, dir_s3d2);
|
||||||
set_cap(_metadata, CAP_SYS_ADMIN);
|
set_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
ASSERT_EQ(0, mount("tmp", dir_s3d2, "tmpfs", 0, "size=4m,mode=700"));
|
ASSERT_EQ(0, mount_opt(&mnt_tmp, dir_s3d2));
|
||||||
clear_cap(_metadata, CAP_SYS_ADMIN);
|
clear_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
|
|
||||||
ASSERT_EQ(0, mkdir(dir_s3d3, 0700));
|
ASSERT_EQ(0, mkdir(dir_s3d3, 0700));
|
||||||
@@ -262,11 +337,13 @@ static void remove_layout1(struct __test_metadata *const _metadata)
|
|||||||
EXPECT_EQ(0, remove_path(file1_s1d3));
|
EXPECT_EQ(0, remove_path(file1_s1d3));
|
||||||
EXPECT_EQ(0, remove_path(file1_s1d2));
|
EXPECT_EQ(0, remove_path(file1_s1d2));
|
||||||
EXPECT_EQ(0, remove_path(file1_s1d1));
|
EXPECT_EQ(0, remove_path(file1_s1d1));
|
||||||
|
EXPECT_EQ(0, remove_path(dir_s1d3));
|
||||||
|
|
||||||
EXPECT_EQ(0, remove_path(file2_s2d3));
|
EXPECT_EQ(0, remove_path(file2_s2d3));
|
||||||
EXPECT_EQ(0, remove_path(file1_s2d3));
|
EXPECT_EQ(0, remove_path(file1_s2d3));
|
||||||
EXPECT_EQ(0, remove_path(file1_s2d2));
|
EXPECT_EQ(0, remove_path(file1_s2d2));
|
||||||
EXPECT_EQ(0, remove_path(file1_s2d1));
|
EXPECT_EQ(0, remove_path(file1_s2d1));
|
||||||
|
EXPECT_EQ(0, remove_path(dir_s2d2));
|
||||||
|
|
||||||
EXPECT_EQ(0, remove_path(file1_s3d1));
|
EXPECT_EQ(0, remove_path(file1_s3d1));
|
||||||
EXPECT_EQ(0, remove_path(dir_s3d3));
|
EXPECT_EQ(0, remove_path(dir_s3d3));
|
||||||
@@ -510,7 +587,7 @@ TEST_F_FORK(layout1, file_and_dir_access_rights)
|
|||||||
ASSERT_EQ(0, close(ruleset_fd));
|
ASSERT_EQ(0, close(ruleset_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F_FORK(layout1, unknown_access_rights)
|
TEST_F_FORK(layout0, unknown_access_rights)
|
||||||
{
|
{
|
||||||
__u64 access_mask;
|
__u64 access_mask;
|
||||||
|
|
||||||
@@ -608,7 +685,7 @@ static void enforce_ruleset(struct __test_metadata *const _metadata,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F_FORK(layout1, proc_nsfs)
|
TEST_F_FORK(layout0, proc_nsfs)
|
||||||
{
|
{
|
||||||
const struct rule rules[] = {
|
const struct rule rules[] = {
|
||||||
{
|
{
|
||||||
@@ -657,11 +734,11 @@ TEST_F_FORK(layout1, proc_nsfs)
|
|||||||
ASSERT_EQ(0, close(path_beneath.parent_fd));
|
ASSERT_EQ(0, close(path_beneath.parent_fd));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F_FORK(layout1, unpriv)
|
TEST_F_FORK(layout0, unpriv)
|
||||||
{
|
{
|
||||||
const struct rule rules[] = {
|
const struct rule rules[] = {
|
||||||
{
|
{
|
||||||
.path = dir_s1d2,
|
.path = TMP_DIR,
|
||||||
.access = ACCESS_RO,
|
.access = ACCESS_RO,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
@@ -1301,12 +1378,12 @@ TEST_F_FORK(layout1, inherit_superset)
|
|||||||
ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
|
ASSERT_EQ(0, test_open(file1_s1d3, O_RDONLY));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F_FORK(layout1, max_layers)
|
TEST_F_FORK(layout0, max_layers)
|
||||||
{
|
{
|
||||||
int i, err;
|
int i, err;
|
||||||
const struct rule rules[] = {
|
const struct rule rules[] = {
|
||||||
{
|
{
|
||||||
.path = dir_s1d2,
|
.path = TMP_DIR,
|
||||||
.access = ACCESS_RO,
|
.access = ACCESS_RO,
|
||||||
},
|
},
|
||||||
{},
|
{},
|
||||||
@@ -4030,21 +4107,24 @@ static const char (*merge_sub_files[])[] = {
|
|||||||
* └── work
|
* └── work
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* clang-format off */
|
FIXTURE(layout2_overlay)
|
||||||
FIXTURE(layout2_overlay) {};
|
{
|
||||||
/* clang-format on */
|
bool skip_test;
|
||||||
|
};
|
||||||
|
|
||||||
FIXTURE_SETUP(layout2_overlay)
|
FIXTURE_SETUP(layout2_overlay)
|
||||||
{
|
{
|
||||||
if (!supports_overlayfs())
|
if (!supports_filesystem("overlay")) {
|
||||||
SKIP(return, "overlayfs is not supported");
|
self->skip_test = true;
|
||||||
|
SKIP(return, "overlayfs is not supported (setup)");
|
||||||
|
}
|
||||||
|
|
||||||
prepare_layout(_metadata);
|
prepare_layout(_metadata);
|
||||||
|
|
||||||
create_directory(_metadata, LOWER_BASE);
|
create_directory(_metadata, LOWER_BASE);
|
||||||
set_cap(_metadata, CAP_SYS_ADMIN);
|
set_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
/* Creates tmpfs mount points to get deterministic overlayfs. */
|
/* Creates tmpfs mount points to get deterministic overlayfs. */
|
||||||
ASSERT_EQ(0, mount("tmp", LOWER_BASE, "tmpfs", 0, "size=4m,mode=700"));
|
ASSERT_EQ(0, mount_opt(&mnt_tmp, LOWER_BASE));
|
||||||
clear_cap(_metadata, CAP_SYS_ADMIN);
|
clear_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
create_file(_metadata, lower_fl1);
|
create_file(_metadata, lower_fl1);
|
||||||
create_file(_metadata, lower_dl1_fl2);
|
create_file(_metadata, lower_dl1_fl2);
|
||||||
@@ -4054,7 +4134,7 @@ FIXTURE_SETUP(layout2_overlay)
|
|||||||
|
|
||||||
create_directory(_metadata, UPPER_BASE);
|
create_directory(_metadata, UPPER_BASE);
|
||||||
set_cap(_metadata, CAP_SYS_ADMIN);
|
set_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
ASSERT_EQ(0, mount("tmp", UPPER_BASE, "tmpfs", 0, "size=4m,mode=700"));
|
ASSERT_EQ(0, mount_opt(&mnt_tmp, UPPER_BASE));
|
||||||
clear_cap(_metadata, CAP_SYS_ADMIN);
|
clear_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
create_file(_metadata, upper_fu1);
|
create_file(_metadata, upper_fu1);
|
||||||
create_file(_metadata, upper_du1_fu2);
|
create_file(_metadata, upper_du1_fu2);
|
||||||
@@ -4075,8 +4155,8 @@ FIXTURE_SETUP(layout2_overlay)
|
|||||||
|
|
||||||
FIXTURE_TEARDOWN(layout2_overlay)
|
FIXTURE_TEARDOWN(layout2_overlay)
|
||||||
{
|
{
|
||||||
if (!supports_overlayfs())
|
if (self->skip_test)
|
||||||
SKIP(return, "overlayfs is not supported");
|
SKIP(return, "overlayfs is not supported (teardown)");
|
||||||
|
|
||||||
EXPECT_EQ(0, remove_path(lower_do1_fl3));
|
EXPECT_EQ(0, remove_path(lower_do1_fl3));
|
||||||
EXPECT_EQ(0, remove_path(lower_dl1_fl2));
|
EXPECT_EQ(0, remove_path(lower_dl1_fl2));
|
||||||
@@ -4109,8 +4189,8 @@ FIXTURE_TEARDOWN(layout2_overlay)
|
|||||||
|
|
||||||
TEST_F_FORK(layout2_overlay, no_restriction)
|
TEST_F_FORK(layout2_overlay, no_restriction)
|
||||||
{
|
{
|
||||||
if (!supports_overlayfs())
|
if (self->skip_test)
|
||||||
SKIP(return, "overlayfs is not supported");
|
SKIP(return, "overlayfs is not supported (test)");
|
||||||
|
|
||||||
ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));
|
ASSERT_EQ(0, test_open(lower_fl1, O_RDONLY));
|
||||||
ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));
|
ASSERT_EQ(0, test_open(lower_dl1, O_RDONLY));
|
||||||
@@ -4275,8 +4355,8 @@ TEST_F_FORK(layout2_overlay, same_content_different_file)
|
|||||||
size_t i;
|
size_t i;
|
||||||
const char *path_entry;
|
const char *path_entry;
|
||||||
|
|
||||||
if (!supports_overlayfs())
|
if (self->skip_test)
|
||||||
SKIP(return, "overlayfs is not supported");
|
SKIP(return, "overlayfs is not supported (test)");
|
||||||
|
|
||||||
/* Sets rules on base directories (i.e. outside overlay scope). */
|
/* Sets rules on base directories (i.e. outside overlay scope). */
|
||||||
ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
|
ruleset_fd = create_ruleset(_metadata, ACCESS_RW, layer1_base);
|
||||||
@@ -4423,4 +4503,261 @@ TEST_F_FORK(layout2_overlay, same_content_different_file)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FIXTURE(layout3_fs)
|
||||||
|
{
|
||||||
|
bool has_created_dir;
|
||||||
|
bool has_created_file;
|
||||||
|
char *dir_path;
|
||||||
|
bool skip_test;
|
||||||
|
};
|
||||||
|
|
||||||
|
FIXTURE_VARIANT(layout3_fs)
|
||||||
|
{
|
||||||
|
const struct mnt_opt mnt;
|
||||||
|
const char *const file_path;
|
||||||
|
unsigned int cwd_fs_magic;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* clang-format off */
|
||||||
|
FIXTURE_VARIANT_ADD(layout3_fs, tmpfs) {
|
||||||
|
/* clang-format on */
|
||||||
|
.mnt = mnt_tmp,
|
||||||
|
.file_path = file1_s1d1,
|
||||||
|
};
|
||||||
|
|
||||||
|
FIXTURE_VARIANT_ADD(layout3_fs, ramfs) {
|
||||||
|
.mnt = {
|
||||||
|
.type = "ramfs",
|
||||||
|
.data = "mode=700",
|
||||||
|
},
|
||||||
|
.file_path = TMP_DIR "/dir/file",
|
||||||
|
};
|
||||||
|
|
||||||
|
FIXTURE_VARIANT_ADD(layout3_fs, cgroup2) {
|
||||||
|
.mnt = {
|
||||||
|
.type = "cgroup2",
|
||||||
|
},
|
||||||
|
.file_path = TMP_DIR "/test/cgroup.procs",
|
||||||
|
};
|
||||||
|
|
||||||
|
FIXTURE_VARIANT_ADD(layout3_fs, proc) {
|
||||||
|
.mnt = {
|
||||||
|
.type = "proc",
|
||||||
|
},
|
||||||
|
.file_path = TMP_DIR "/self/status",
|
||||||
|
};
|
||||||
|
|
||||||
|
FIXTURE_VARIANT_ADD(layout3_fs, sysfs) {
|
||||||
|
.mnt = {
|
||||||
|
.type = "sysfs",
|
||||||
|
},
|
||||||
|
.file_path = TMP_DIR "/kernel/notes",
|
||||||
|
};
|
||||||
|
|
||||||
|
FIXTURE_VARIANT_ADD(layout3_fs, hostfs) {
|
||||||
|
.mnt = {
|
||||||
|
.source = TMP_DIR,
|
||||||
|
.flags = MS_BIND,
|
||||||
|
},
|
||||||
|
.file_path = TMP_DIR "/dir/file",
|
||||||
|
.cwd_fs_magic = HOSTFS_SUPER_MAGIC,
|
||||||
|
};
|
||||||
|
|
||||||
|
FIXTURE_SETUP(layout3_fs)
|
||||||
|
{
|
||||||
|
struct stat statbuf;
|
||||||
|
const char *slash;
|
||||||
|
size_t dir_len;
|
||||||
|
|
||||||
|
if (!supports_filesystem(variant->mnt.type) ||
|
||||||
|
!cwd_matches_fs(variant->cwd_fs_magic)) {
|
||||||
|
self->skip_test = true;
|
||||||
|
SKIP(return, "this filesystem is not supported (setup)");
|
||||||
|
}
|
||||||
|
|
||||||
|
slash = strrchr(variant->file_path, '/');
|
||||||
|
ASSERT_NE(slash, NULL);
|
||||||
|
dir_len = (size_t)slash - (size_t)variant->file_path;
|
||||||
|
ASSERT_LT(0, dir_len);
|
||||||
|
self->dir_path = malloc(dir_len + 1);
|
||||||
|
self->dir_path[dir_len] = '\0';
|
||||||
|
strncpy(self->dir_path, variant->file_path, dir_len);
|
||||||
|
|
||||||
|
prepare_layout_opt(_metadata, &variant->mnt);
|
||||||
|
|
||||||
|
/* Creates directory when required. */
|
||||||
|
if (stat(self->dir_path, &statbuf)) {
|
||||||
|
set_cap(_metadata, CAP_DAC_OVERRIDE);
|
||||||
|
EXPECT_EQ(0, mkdir(self->dir_path, 0700))
|
||||||
|
{
|
||||||
|
TH_LOG("Failed to create directory \"%s\": %s",
|
||||||
|
self->dir_path, strerror(errno));
|
||||||
|
free(self->dir_path);
|
||||||
|
self->dir_path = NULL;
|
||||||
|
}
|
||||||
|
self->has_created_dir = true;
|
||||||
|
clear_cap(_metadata, CAP_DAC_OVERRIDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Creates file when required. */
|
||||||
|
if (stat(variant->file_path, &statbuf)) {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
set_cap(_metadata, CAP_DAC_OVERRIDE);
|
||||||
|
fd = creat(variant->file_path, 0600);
|
||||||
|
EXPECT_LE(0, fd)
|
||||||
|
{
|
||||||
|
TH_LOG("Failed to create file \"%s\": %s",
|
||||||
|
variant->file_path, strerror(errno));
|
||||||
|
}
|
||||||
|
EXPECT_EQ(0, close(fd));
|
||||||
|
self->has_created_file = true;
|
||||||
|
clear_cap(_metadata, CAP_DAC_OVERRIDE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FIXTURE_TEARDOWN(layout3_fs)
|
||||||
|
{
|
||||||
|
if (self->skip_test)
|
||||||
|
SKIP(return, "this filesystem is not supported (teardown)");
|
||||||
|
|
||||||
|
if (self->has_created_file) {
|
||||||
|
set_cap(_metadata, CAP_DAC_OVERRIDE);
|
||||||
|
/*
|
||||||
|
* Don't check for error because the file might already
|
||||||
|
* have been removed (cf. release_inode test).
|
||||||
|
*/
|
||||||
|
unlink(variant->file_path);
|
||||||
|
clear_cap(_metadata, CAP_DAC_OVERRIDE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self->has_created_dir) {
|
||||||
|
set_cap(_metadata, CAP_DAC_OVERRIDE);
|
||||||
|
/*
|
||||||
|
* Don't check for error because the directory might already
|
||||||
|
* have been removed (cf. release_inode test).
|
||||||
|
*/
|
||||||
|
rmdir(self->dir_path);
|
||||||
|
clear_cap(_metadata, CAP_DAC_OVERRIDE);
|
||||||
|
}
|
||||||
|
free(self->dir_path);
|
||||||
|
self->dir_path = NULL;
|
||||||
|
|
||||||
|
cleanup_layout(_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void layer3_fs_tag_inode(struct __test_metadata *const _metadata,
|
||||||
|
FIXTURE_DATA(layout3_fs) * self,
|
||||||
|
const FIXTURE_VARIANT(layout3_fs) * variant,
|
||||||
|
const char *const rule_path)
|
||||||
|
{
|
||||||
|
const struct rule layer1_allow_read_file[] = {
|
||||||
|
{
|
||||||
|
.path = rule_path,
|
||||||
|
.access = LANDLOCK_ACCESS_FS_READ_FILE,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
const struct landlock_ruleset_attr layer2_deny_everything_attr = {
|
||||||
|
.handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE,
|
||||||
|
};
|
||||||
|
const char *const dev_null_path = "/dev/null";
|
||||||
|
int ruleset_fd;
|
||||||
|
|
||||||
|
if (self->skip_test)
|
||||||
|
SKIP(return, "this filesystem is not supported (test)");
|
||||||
|
|
||||||
|
/* Checks without Landlock. */
|
||||||
|
EXPECT_EQ(0, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
|
||||||
|
EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
|
||||||
|
|
||||||
|
ruleset_fd = create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_FILE,
|
||||||
|
layer1_allow_read_file);
|
||||||
|
EXPECT_LE(0, ruleset_fd);
|
||||||
|
enforce_ruleset(_metadata, ruleset_fd);
|
||||||
|
EXPECT_EQ(0, close(ruleset_fd));
|
||||||
|
|
||||||
|
EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
|
||||||
|
EXPECT_EQ(0, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
|
||||||
|
|
||||||
|
/* Forbids directory reading. */
|
||||||
|
ruleset_fd =
|
||||||
|
landlock_create_ruleset(&layer2_deny_everything_attr,
|
||||||
|
sizeof(layer2_deny_everything_attr), 0);
|
||||||
|
EXPECT_LE(0, ruleset_fd);
|
||||||
|
enforce_ruleset(_metadata, ruleset_fd);
|
||||||
|
EXPECT_EQ(0, close(ruleset_fd));
|
||||||
|
|
||||||
|
/* Checks with Landlock and forbidden access. */
|
||||||
|
EXPECT_EQ(EACCES, test_open(dev_null_path, O_RDONLY | O_CLOEXEC));
|
||||||
|
EXPECT_EQ(EACCES, test_open(variant->file_path, O_RDONLY | O_CLOEXEC));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Matrix of tests to check file hierarchy evaluation. */
|
||||||
|
|
||||||
|
TEST_F_FORK(layout3_fs, tag_inode_dir_parent)
|
||||||
|
{
|
||||||
|
/* The current directory must not be the root for this test. */
|
||||||
|
layer3_fs_tag_inode(_metadata, self, variant, ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F_FORK(layout3_fs, tag_inode_dir_mnt)
|
||||||
|
{
|
||||||
|
layer3_fs_tag_inode(_metadata, self, variant, TMP_DIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F_FORK(layout3_fs, tag_inode_dir_child)
|
||||||
|
{
|
||||||
|
layer3_fs_tag_inode(_metadata, self, variant, self->dir_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F_FORK(layout3_fs, tag_inode_file)
|
||||||
|
{
|
||||||
|
layer3_fs_tag_inode(_metadata, self, variant, variant->file_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Light version of layout1.release_inodes */
|
||||||
|
TEST_F_FORK(layout3_fs, release_inodes)
|
||||||
|
{
|
||||||
|
const struct rule layer1[] = {
|
||||||
|
{
|
||||||
|
.path = TMP_DIR,
|
||||||
|
.access = LANDLOCK_ACCESS_FS_READ_DIR,
|
||||||
|
},
|
||||||
|
{},
|
||||||
|
};
|
||||||
|
int ruleset_fd;
|
||||||
|
|
||||||
|
if (self->skip_test)
|
||||||
|
SKIP(return, "this filesystem is not supported (test)");
|
||||||
|
|
||||||
|
/* Clean up for the teardown to not fail. */
|
||||||
|
if (self->has_created_file)
|
||||||
|
EXPECT_EQ(0, remove_path(variant->file_path));
|
||||||
|
|
||||||
|
if (self->has_created_dir)
|
||||||
|
/* Don't check for error because of cgroup specificities. */
|
||||||
|
remove_path(self->dir_path);
|
||||||
|
|
||||||
|
ruleset_fd =
|
||||||
|
create_ruleset(_metadata, LANDLOCK_ACCESS_FS_READ_DIR, layer1);
|
||||||
|
ASSERT_LE(0, ruleset_fd);
|
||||||
|
|
||||||
|
/* Unmount the filesystem while it is being used by a ruleset. */
|
||||||
|
set_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
|
ASSERT_EQ(0, umount(TMP_DIR));
|
||||||
|
clear_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
|
|
||||||
|
/* Replaces with a new mount point to simplify FIXTURE_TEARDOWN. */
|
||||||
|
set_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
|
ASSERT_EQ(0, mount_opt(&mnt_tmp, TMP_DIR));
|
||||||
|
clear_cap(_metadata, CAP_SYS_ADMIN);
|
||||||
|
|
||||||
|
enforce_ruleset(_metadata, ruleset_fd);
|
||||||
|
ASSERT_EQ(0, close(ruleset_fd));
|
||||||
|
|
||||||
|
/* Checks that access to the new mount point is denied. */
|
||||||
|
ASSERT_EQ(EACCES, test_open(TMP_DIR, O_RDONLY));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_HARNESS_MAIN
|
TEST_HARNESS_MAIN
|
||||||
|
Reference in New Issue
Block a user