mirror of
https://github.com/torvalds/linux.git
synced 2026-10-23 15:32:04 +02:00
nullfs: add an empty immutable regular file
Add nullfs_new_file() to allocate an empty immutable regular file on a nullfs instance as a dentry of its own. It is never hashed under the root and so can't be found by lookup. Reads return nothing, changes are refused, file locks, leases and delegations are refused as. Link: https://patch.msgid.link/20261002-work-mount-cover-v1-1-232a8f52b43c@kernel.org Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
parent
1514faf41f
commit
54867884fe
|
|
@ -6,6 +6,7 @@
|
|||
#include <linux/fs_pin.h>
|
||||
|
||||
extern struct file_system_type nullfs_fs_type;
|
||||
extern struct dentry *nullfs_new_file(struct super_block *sb);
|
||||
extern struct vfsmount *knullfs;
|
||||
extern struct list_head notify_list;
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ static struct hlist_head *mount_hashtable __ro_after_init;
|
|||
static struct hlist_head *mountpoint_hashtable __ro_after_init;
|
||||
static struct kmem_cache *mnt_cache __ro_after_init;
|
||||
struct vfsmount *knullfs __ro_after_init; /* private nullfs instance */
|
||||
static struct vfsmount *knullfs_file __ro_after_init; /* its regular file */
|
||||
static DECLARE_RWSEM(namespace_sem);
|
||||
static HLIST_HEAD(unmounted); /* protected by namespace_sem */
|
||||
static LIST_HEAD(ex_mountpoints); /* protected by namespace_sem */
|
||||
|
|
@ -6330,6 +6331,25 @@ static void __init mount_rootfs_on_nullfs(struct vfsmount *mnt,
|
|||
attach_mnt(real_mount(mnt), mp.parent, mp.mp);
|
||||
}
|
||||
|
||||
static struct vfsmount *__init knullfs_file_mount(void)
|
||||
{
|
||||
struct dentry *file;
|
||||
struct mount *mnt;
|
||||
|
||||
file = nullfs_new_file(knullfs->mnt_sb);
|
||||
if (IS_ERR(file))
|
||||
return ERR_CAST(file);
|
||||
mnt = clone_mnt(real_mount(knullfs), file, CL_PRIVATE);
|
||||
dput(file);
|
||||
if (IS_ERR(mnt))
|
||||
return ERR_CAST(mnt);
|
||||
mnt->mnt_ns = MNT_NS_INTERNAL;
|
||||
mnt->mnt.mnt_flags |= MNT_INTERNAL;
|
||||
mnt->mnt.mnt_flags &= ~MNT_READONLY;
|
||||
dont_mount(mnt->mnt.mnt_root);
|
||||
return &mnt->mnt;
|
||||
}
|
||||
|
||||
static void __init init_mount_tree(void)
|
||||
{
|
||||
struct vfsmount *mnt, *nullfs_mnt;
|
||||
|
|
@ -6342,6 +6362,8 @@ static void __init init_mount_tree(void)
|
|||
* (1) nullfs with mount id 1
|
||||
* (2) mutable rootfs with mount id 2
|
||||
* (3) private nullfs for kthreads (SB_KERNMOUNT), kept in knullfs
|
||||
* (4) a second mount of (3) rooted on a regular file, kept in
|
||||
* knullfs_file
|
||||
*
|
||||
* with (2) mounted on top of (1). The init_task's root and pwd
|
||||
* are pointed at (3) so all kthreads start isolated in nullfs.
|
||||
|
|
@ -6383,6 +6405,9 @@ static void __init init_mount_tree(void)
|
|||
dont_mount(knullfs->mnt_root);
|
||||
/* and nothing is ever written through it */
|
||||
knullfs->mnt_flags |= MNT_READONLY;
|
||||
knullfs_file = knullfs_file_mount();
|
||||
if (IS_ERR(knullfs_file))
|
||||
panic("VFS: Failed to create the nullfs file stand-in");
|
||||
root.mnt = knullfs;
|
||||
root.dentry = knullfs->mnt_root;
|
||||
|
||||
|
|
|
|||
88
fs/nullfs.c
88
fs/nullfs.c
|
|
@ -3,6 +3,8 @@
|
|||
#include <linux/fs/super_types.h>
|
||||
#include <linux/fs_context.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/splice.h>
|
||||
#include <linux/uio.h>
|
||||
|
||||
#include "mount.h"
|
||||
|
||||
|
|
@ -39,6 +41,92 @@ static const struct file_operations nullfs_dir_operations = {
|
|||
.fop_flags = FOP_IMMUTABLE,
|
||||
};
|
||||
|
||||
static ssize_t nullfs_file_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t nullfs_file_splice_read(struct file *in, loff_t *ppos,
|
||||
struct pipe_inode_info *pipe,
|
||||
size_t len, unsigned int flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t nullfs_file_write_iter(struct kiocb *iocb, struct iov_iter *from)
|
||||
{
|
||||
size_t count = iov_iter_count(from);
|
||||
|
||||
iov_iter_advance(from, count);
|
||||
return count;
|
||||
}
|
||||
|
||||
static int nullfs_pipe_to_null(struct pipe_inode_info *pipe,
|
||||
struct pipe_buffer *buf, struct splice_desc *sd)
|
||||
{
|
||||
return sd->len;
|
||||
}
|
||||
|
||||
static ssize_t nullfs_file_splice_write(struct pipe_inode_info *pipe,
|
||||
struct file *out, loff_t *ppos,
|
||||
size_t len, unsigned int flags)
|
||||
{
|
||||
return splice_from_pipe(pipe, out, ppos, len, flags, nullfs_pipe_to_null);
|
||||
}
|
||||
|
||||
static int nullfs_file_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
struct iattr *attr)
|
||||
{
|
||||
if (attr->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID))
|
||||
return -EPERM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct inode_operations nullfs_file_inode_operations = {
|
||||
.setattr = nullfs_file_setattr,
|
||||
};
|
||||
|
||||
static const struct file_operations nullfs_file_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = nullfs_file_read_iter,
|
||||
.write_iter = nullfs_file_write_iter,
|
||||
.splice_read = nullfs_file_splice_read,
|
||||
.splice_write = nullfs_file_splice_write,
|
||||
.fsync = noop_fsync,
|
||||
.lock = nullfs_nolock,
|
||||
.flock = nullfs_nolock,
|
||||
};
|
||||
|
||||
/*
|
||||
* An empty regular file on @sb as a dentry of its own, never hashed under
|
||||
* the root so no lookup finds it.
|
||||
*/
|
||||
struct dentry *nullfs_new_file(struct super_block *sb)
|
||||
{
|
||||
struct dentry *dentry;
|
||||
struct inode *inode;
|
||||
|
||||
inode = new_inode(sb);
|
||||
if (!inode)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* the root directory is 1 */
|
||||
inode->i_ino = 2;
|
||||
inode->i_mode = S_IFREG | 0666;
|
||||
inode->i_op = &nullfs_file_inode_operations;
|
||||
inode->i_fop = &nullfs_file_operations;
|
||||
simple_inode_init_ts(inode);
|
||||
inode->i_flags |= S_NOATIME;
|
||||
|
||||
dentry = d_alloc_anon(sb);
|
||||
if (!dentry) {
|
||||
iput(inode);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
d_instantiate(dentry, inode);
|
||||
return dentry;
|
||||
}
|
||||
|
||||
static int nullfs_fs_fill_super(struct super_block *s, struct fs_context *fc)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user