Skip to content

Commit 74ce793

Browse files
committed
hostfs: Fix ephemeral inodes
hostfs creates a new inode for each opened or created file, which created useless inode allocations and forbade identifying a host file with a kernel inode. Fix this uncommon filesystem behavior by tying kernel inodes to host file's inode and device IDs. Even if the host filesystem inodes may be recycled, this cannot happen while a file referencing it is opened, which is the case with hostfs. It should be noted that hostfs inode IDs may not be unique for the same hostfs superblock because multiple host's (backed) superblocks may be used. Delete inodes when dropping them to force backed host's file descriptors closing. This enables to entirely remove ARCH_EPHEMERAL_INODES, and then makes Landlock fully supported by UML. This is very useful for testing changes. These changes also factor out and simplify some helpers thanks to the new hostfs_inode_update() and the hostfs_iget() revamp: read_name(), hostfs_create(), hostfs_lookup(), hostfs_mknod(), and hostfs_fill_sb_common(). A following commit with new Landlock tests check this new hostfs inode consistency. Cc: Anton Ivanov <anton.ivanov@cambridgegreys.com> Cc: Johannes Berg <johannes@sipsolutions.net> Acked-by: Richard Weinberger <richard@nod.at> Link: https://lore.kernel.org/r/20230612191430.339153-2-mic@digikod.net Signed-off-by: Mickaël Salaün <mic@digikod.net>
1 parent 858fd16 commit 74ce793

6 files changed

Lines changed: 109 additions & 116 deletions

File tree

arch/Kconfig

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1188,13 +1188,6 @@ config COMPAT_32BIT_TIME
11881188
config ARCH_NO_PREEMPT
11891189
bool
11901190

1191-
config ARCH_EPHEMERAL_INODES
1192-
def_bool n
1193-
help
1194-
An arch should select this symbol if it doesn't keep track of inode
1195-
instances on its own, but instead relies on something else (e.g. the
1196-
host kernel for an UML kernel).
1197-
11981191
config ARCH_SUPPORTS_RT
11991192
bool
12001193

arch/um/Kconfig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ menu "UML-specific options"
55
config UML
66
bool
77
default y
8-
select ARCH_EPHEMERAL_INODES
98
select ARCH_HAS_FORTIFY_SOURCE
109
select ARCH_HAS_GCOV_PROFILE_ALL
1110
select ARCH_HAS_KCOV

fs/hostfs/hostfs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ struct hostfs_stat {
6565
unsigned long long blocks;
6666
unsigned int maj;
6767
unsigned int min;
68+
dev_t dev;
6869
};
6970

7071
extern int stat_file(const char *path, struct hostfs_stat *p, int fd);

fs/hostfs/hostfs_kern.c

Lines changed: 106 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ struct hostfs_inode_info {
2626
fmode_t mode;
2727
struct inode vfs_inode;
2828
struct mutex open_mutex;
29+
dev_t dev;
2930
};
3031

3132
static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode)
@@ -182,14 +183,6 @@ static char *follow_link(char *link)
182183
return ERR_PTR(n);
183184
}
184185

185-
static struct inode *hostfs_iget(struct super_block *sb)
186-
{
187-
struct inode *inode = new_inode(sb);
188-
if (!inode)
189-
return ERR_PTR(-ENOMEM);
190-
return inode;
191-
}
192-
193186
static int hostfs_statfs(struct dentry *dentry, struct kstatfs *sf)
194187
{
195188
/*
@@ -228,6 +221,7 @@ static struct inode *hostfs_alloc_inode(struct super_block *sb)
228221
return NULL;
229222
hi->fd = -1;
230223
hi->mode = 0;
224+
hi->dev = 0;
231225
inode_init_once(&hi->vfs_inode);
232226
mutex_init(&hi->open_mutex);
233227
return &hi->vfs_inode;
@@ -240,6 +234,7 @@ static void hostfs_evict_inode(struct inode *inode)
240234
if (HOSTFS_I(inode)->fd != -1) {
241235
close_file(&HOSTFS_I(inode)->fd);
242236
HOSTFS_I(inode)->fd = -1;
237+
HOSTFS_I(inode)->dev = 0;
243238
}
244239
}
245240

@@ -265,6 +260,7 @@ static int hostfs_show_options(struct seq_file *seq, struct dentry *root)
265260
static const struct super_operations hostfs_sbops = {
266261
.alloc_inode = hostfs_alloc_inode,
267262
.free_inode = hostfs_free_inode,
263+
.drop_inode = generic_delete_inode,
268264
.evict_inode = hostfs_evict_inode,
269265
.statfs = hostfs_statfs,
270266
.show_options = hostfs_show_options,
@@ -512,18 +508,31 @@ static const struct address_space_operations hostfs_aops = {
512508
.write_end = hostfs_write_end,
513509
};
514510

515-
static int read_name(struct inode *ino, char *name)
511+
static int hostfs_inode_update(struct inode *ino, const struct hostfs_stat *st)
512+
{
513+
set_nlink(ino, st->nlink);
514+
i_uid_write(ino, st->uid);
515+
i_gid_write(ino, st->gid);
516+
ino->i_atime =
517+
(struct timespec64){ st->atime.tv_sec, st->atime.tv_nsec };
518+
ino->i_mtime =
519+
(struct timespec64){ st->mtime.tv_sec, st->mtime.tv_nsec };
520+
ino->i_ctime =
521+
(struct timespec64){ st->ctime.tv_sec, st->ctime.tv_nsec };
522+
ino->i_size = st->size;
523+
ino->i_blocks = st->blocks;
524+
return 0;
525+
}
526+
527+
static int hostfs_inode_set(struct inode *ino, void *data)
516528
{
529+
struct hostfs_stat *st = data;
517530
dev_t rdev;
518-
struct hostfs_stat st;
519-
int err = stat_file(name, &st, -1);
520-
if (err)
521-
return err;
522531

523532
/* Reencode maj and min with the kernel encoding.*/
524-
rdev = MKDEV(st.maj, st.min);
533+
rdev = MKDEV(st->maj, st->min);
525534

526-
switch (st.mode & S_IFMT) {
535+
switch (st->mode & S_IFMT) {
527536
case S_IFLNK:
528537
ino->i_op = &hostfs_link_iops;
529538
break;
@@ -535,7 +544,7 @@ static int read_name(struct inode *ino, char *name)
535544
case S_IFBLK:
536545
case S_IFIFO:
537546
case S_IFSOCK:
538-
init_special_inode(ino, st.mode & S_IFMT, rdev);
547+
init_special_inode(ino, st->mode & S_IFMT, rdev);
539548
ino->i_op = &hostfs_iops;
540549
break;
541550
case S_IFREG:
@@ -547,80 +556,91 @@ static int read_name(struct inode *ino, char *name)
547556
return -EIO;
548557
}
549558

550-
ino->i_ino = st.ino;
551-
ino->i_mode = st.mode;
552-
set_nlink(ino, st.nlink);
553-
i_uid_write(ino, st.uid);
554-
i_gid_write(ino, st.gid);
555-
ino->i_atime = (struct timespec64){ st.atime.tv_sec, st.atime.tv_nsec };
556-
ino->i_mtime = (struct timespec64){ st.mtime.tv_sec, st.mtime.tv_nsec };
557-
ino->i_ctime = (struct timespec64){ st.ctime.tv_sec, st.ctime.tv_nsec };
558-
ino->i_size = st.size;
559-
ino->i_blocks = st.blocks;
560-
return 0;
559+
HOSTFS_I(ino)->dev = st->dev;
560+
ino->i_ino = st->ino;
561+
ino->i_mode = st->mode;
562+
return hostfs_inode_update(ino, st);
563+
}
564+
565+
static int hostfs_inode_test(struct inode *inode, void *data)
566+
{
567+
const struct hostfs_stat *st = data;
568+
569+
return inode->i_ino == st->ino && HOSTFS_I(inode)->dev == st->dev;
570+
}
571+
572+
static struct inode *hostfs_iget(struct super_block *sb, char *name)
573+
{
574+
struct inode *inode;
575+
struct hostfs_stat st;
576+
int err = stat_file(name, &st, -1);
577+
578+
if (err)
579+
return ERR_PTR(err);
580+
581+
inode = iget5_locked(sb, st.ino, hostfs_inode_test, hostfs_inode_set,
582+
&st);
583+
if (!inode)
584+
return ERR_PTR(-ENOMEM);
585+
586+
if (inode->i_state & I_NEW) {
587+
unlock_new_inode(inode);
588+
} else {
589+
spin_lock(&inode->i_lock);
590+
hostfs_inode_update(inode, &st);
591+
spin_unlock(&inode->i_lock);
592+
}
593+
594+
return inode;
561595
}
562596

563597
static int hostfs_create(struct mnt_idmap *idmap, struct inode *dir,
564598
struct dentry *dentry, umode_t mode, bool excl)
565599
{
566600
struct inode *inode;
567601
char *name;
568-
int error, fd;
569-
570-
inode = hostfs_iget(dir->i_sb);
571-
if (IS_ERR(inode)) {
572-
error = PTR_ERR(inode);
573-
goto out;
574-
}
602+
int fd;
575603

576-
error = -ENOMEM;
577604
name = dentry_name(dentry);
578605
if (name == NULL)
579-
goto out_put;
606+
return -ENOMEM;
580607

581608
fd = file_create(name, mode & 0777);
582-
if (fd < 0)
583-
error = fd;
584-
else
585-
error = read_name(inode, name);
609+
if (fd < 0) {
610+
__putname(name);
611+
return fd;
612+
}
586613

614+
inode = hostfs_iget(dir->i_sb, name);
587615
__putname(name);
588-
if (error)
589-
goto out_put;
616+
if (IS_ERR(inode))
617+
return PTR_ERR(inode);
590618

591619
HOSTFS_I(inode)->fd = fd;
592620
HOSTFS_I(inode)->mode = FMODE_READ | FMODE_WRITE;
593621
d_instantiate(dentry, inode);
594622
return 0;
595-
596-
out_put:
597-
iput(inode);
598-
out:
599-
return error;
600623
}
601624

602625
static struct dentry *hostfs_lookup(struct inode *ino, struct dentry *dentry,
603626
unsigned int flags)
604627
{
605-
struct inode *inode;
628+
struct inode *inode = NULL;
606629
char *name;
607-
int err;
608-
609-
inode = hostfs_iget(ino->i_sb);
610-
if (IS_ERR(inode))
611-
goto out;
612630

613-
err = -ENOMEM;
614631
name = dentry_name(dentry);
615-
if (name) {
616-
err = read_name(inode, name);
617-
__putname(name);
618-
}
619-
if (err) {
620-
iput(inode);
621-
inode = (err == -ENOENT) ? NULL : ERR_PTR(err);
632+
if (name == NULL)
633+
return ERR_PTR(-ENOMEM);
634+
635+
inode = hostfs_iget(ino->i_sb, name);
636+
__putname(name);
637+
if (IS_ERR(inode)) {
638+
if (PTR_ERR(inode) == -ENOENT)
639+
inode = NULL;
640+
else
641+
return ERR_CAST(inode);
622642
}
623-
out:
643+
624644
return d_splice_alias(inode, dentry);
625645
}
626646

@@ -704,35 +724,23 @@ static int hostfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
704724
char *name;
705725
int err;
706726

707-
inode = hostfs_iget(dir->i_sb);
708-
if (IS_ERR(inode)) {
709-
err = PTR_ERR(inode);
710-
goto out;
711-
}
712-
713-
err = -ENOMEM;
714727
name = dentry_name(dentry);
715728
if (name == NULL)
716-
goto out_put;
729+
return -ENOMEM;
717730

718731
err = do_mknod(name, mode, MAJOR(dev), MINOR(dev));
719-
if (err)
720-
goto out_free;
732+
if (err) {
733+
__putname(name);
734+
return err;
735+
}
721736

722-
err = read_name(inode, name);
737+
inode = hostfs_iget(dir->i_sb, name);
723738
__putname(name);
724-
if (err)
725-
goto out_put;
739+
if (IS_ERR(inode))
740+
return PTR_ERR(inode);
726741

727742
d_instantiate(dentry, inode);
728743
return 0;
729-
730-
out_free:
731-
__putname(name);
732-
out_put:
733-
iput(inode);
734-
out:
735-
return err;
736744
}
737745

738746
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)
929937
sb->s_maxbytes = MAX_LFS_FILESIZE;
930938
err = super_setup_bdi(sb);
931939
if (err)
932-
goto out;
940+
return err;
933941

934942
/* NULL is printed as '(null)' by printf(): avoid that. */
935943
if (req_root == NULL)
936944
req_root = "";
937945

938-
err = -ENOMEM;
939946
sb->s_fs_info = host_root_path =
940947
kasprintf(GFP_KERNEL, "%s/%s", root_ino, req_root);
941948
if (host_root_path == NULL)
942-
goto out;
943-
944-
root_inode = new_inode(sb);
945-
if (!root_inode)
946-
goto out;
949+
return -ENOMEM;
947950

948-
err = read_name(root_inode, host_root_path);
949-
if (err)
950-
goto out_put;
951+
root_inode = hostfs_iget(sb, host_root_path);
952+
if (IS_ERR(root_inode))
953+
return PTR_ERR(root_inode);
951954

952955
if (S_ISLNK(root_inode->i_mode)) {
953-
char *name = follow_link(host_root_path);
954-
if (IS_ERR(name)) {
955-
err = PTR_ERR(name);
956-
goto out_put;
957-
}
958-
err = read_name(root_inode, name);
956+
char *name;
957+
958+
iput(root_inode);
959+
name = follow_link(host_root_path);
960+
if (IS_ERR(name))
961+
return PTR_ERR(name);
962+
963+
root_inode = hostfs_iget(sb, name);
959964
kfree(name);
960-
if (err)
961-
goto out_put;
965+
if (IS_ERR(root_inode))
966+
return PTR_ERR(root_inode);
962967
}
963968

964-
err = -ENOMEM;
965969
sb->s_root = d_make_root(root_inode);
966970
if (sb->s_root == NULL)
967-
goto out;
971+
return -ENOMEM;
968972

969973
return 0;
970-
971-
out_put:
972-
iput(root_inode);
973-
out:
974-
return err;
975974
}
976975

977976
static struct dentry *hostfs_read_sb(struct file_system_type *type,

fs/hostfs/hostfs_user.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ static void stat64_to_hostfs(const struct stat64 *buf, struct hostfs_stat *p)
3636
p->blocks = buf->st_blocks;
3737
p->maj = os_major(buf->st_rdev);
3838
p->min = os_minor(buf->st_rdev);
39+
p->dev = buf->st_dev;
3940
}
4041

4142
int stat_file(const char *path, struct hostfs_stat *p, int fd)

security/landlock/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
config SECURITY_LANDLOCK
44
bool "Landlock support"
5-
depends on SECURITY && !ARCH_EPHEMERAL_INODES
5+
depends on SECURITY
66
select SECURITY_PATH
77
help
88
Landlock is a sandboxing mechanism that enables processes to restrict

0 commit comments

Comments
 (0)