diff --git a/fs/namespace.c b/fs/namespace.c index 6e76f2a72cfca0a691f125e2ffafaa569a585fd4..3334f6916b3b36919ee10bad3ebc521b4fce63d9 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2900,6 +2900,8 @@ static int do_new_mount(struct path *path, const char *fstype, int sb_flags, err = vfs_get_tree(fc); if (!err) err = do_new_mount_fc(fc, path, mnt_flags); + if (!err && type->mount_end) + err = type->mount_end(fc, path); put_fs_context(fc); return err; diff --git a/fs/overlayfs/Makefile b/fs/overlayfs/Makefile index 9164c585eb2f262a2d23801822379ae6a0918977..328656b819a82834e39eb30b463a47df2e8cfd69 100644 --- a/fs/overlayfs/Makefile +++ b/fs/overlayfs/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_OVERLAY_FS) += overlay.o overlay-objs := super.o namei.o util.o inode.o file.o dir.o readdir.o \ - copy_up.o export.o + copy_up.o export.o sysfs.o diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 898de3bf884e468fedc9408e697d1eebfb8ffa49..d447d8ecea89b9fc8e56ddd253405959cc4e415d 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -546,3 +546,10 @@ int ovl_set_origin(struct dentry *dentry, struct dentry *lower, /* export.c */ extern const struct export_operations ovl_export_operations; + +/* sysfs.c */ +int __init ovl_init_sysfs(void); +void ovl_exit_sysfs(void); +int ovl_register_sysfs(struct super_block *sb); +void ovl_unregister_sysfs(struct super_block *sb); +int ovl_mergedir_backup(struct super_block *sb, const char *str); diff --git a/fs/overlayfs/ovl_entry.h b/fs/overlayfs/ovl_entry.h index b208eba5d0b64d388def17af4445c34711b91808..9b4662336e84d3d19ee2d5995eb3333b9c00742c 100644 --- a/fs/overlayfs/ovl_entry.h +++ b/fs/overlayfs/ovl_entry.h @@ -5,10 +5,13 @@ * Copyright (C) 2016 Red Hat, Inc. */ +#include + struct ovl_config { char *lowerdir; char *upperdir; char *workdir; + char *mergedir; bool default_permissions; bool redirect_dir; bool redirect_follow; @@ -81,6 +84,8 @@ struct ovl_fs { struct dentry *whiteout; /* r/o snapshot of upperdir sb's only taken on volatile mounts */ errseq_t errseq; + struct kobject kobj; + struct completion kobj_unregister; }; static inline struct vfsmount *ovl_upper_mnt(struct ovl_fs *ofs) diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index 45c596dfe3a360b5ac3abc8ee3ae3ecf29a70343..477147faa67d74195b32081a891e10a308379753 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "overlayfs.h" MODULE_AUTHOR("Miklos Szeredi "); @@ -244,9 +245,14 @@ static void ovl_free_fs(struct ovl_fs *ofs) kfree(ofs->config.lowerdir); kfree(ofs->config.upperdir); kfree(ofs->config.workdir); + kfree(ofs->config.mergedir); kfree(ofs->config.redirect_mode); if (ofs->creator_cred) put_cred(ofs->creator_cred); + + kobject_put(&ofs->kobj); + wait_for_completion(&ofs->kobj_unregister); + kfree(ofs); } @@ -254,6 +260,8 @@ static void ovl_put_super(struct super_block *sb) { struct ovl_fs *ofs = sb->s_fs_info; + ovl_unregister_sysfs(sb); + ovl_free_fs(ofs); } @@ -2054,11 +2062,32 @@ static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags, return mount_nodev(fs_type, flags, raw_data, ovl_fill_super); } +static int ovl_mount_end(struct fs_context *fc, struct path *path) +{ + char buffer[PATH_MAX]; + const char *tmp; + int err; + + err = ovl_register_sysfs(fc->root->d_sb); + if (err) + return err; + + tmp = dentry_path_raw(path->dentry, buffer, PATH_MAX); + if (IS_ERR_OR_NULL(tmp)) + tmp = path->dentry->d_name.name; + err = ovl_mergedir_backup(fc->root->d_sb, tmp); + if (err) + return err; + + return err; +} + static struct file_system_type ovl_fs_type = { .owner = THIS_MODULE, .name = "overlay", .mount = ovl_mount, .kill_sb = kill_anon_super, + .mount_end = ovl_mount_end, }; MODULE_ALIAS_FS("overlay"); @@ -2081,6 +2110,12 @@ static int __init ovl_init(void) if (ovl_inode_cachep == NULL) return -ENOMEM; + err = ovl_init_sysfs(); + if (err) { + kmem_cache_destroy(ovl_inode_cachep); + return err; + } + err = ovl_aio_request_cache_init(); if (!err) { err = register_filesystem(&ovl_fs_type); @@ -2089,6 +2124,7 @@ static int __init ovl_init(void) ovl_aio_request_cache_destroy(); } + ovl_exit_sysfs(); kmem_cache_destroy(ovl_inode_cachep); return err; @@ -2105,6 +2141,8 @@ static void __exit ovl_exit(void) rcu_barrier(); kmem_cache_destroy(ovl_inode_cachep); ovl_aio_request_cache_destroy(); + + ovl_exit_sysfs(); } module_init(ovl_init); diff --git a/fs/overlayfs/sysfs.c b/fs/overlayfs/sysfs.c new file mode 100644 index 0000000000000000000000000000000000000000..2faf4edf22b31d5f171e27271715fa724e658d79 --- /dev/null +++ b/fs/overlayfs/sysfs.c @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * fs/overlayfs/sysfs.c + * + * This file is used for creating a sysfs file for the + * overlay file system + * + * Based significantly on the fs/ext4/sysfs.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ovl_entry.h" + +static struct kobject *ovl_root; + +enum attr_id_t { + attr_upper, + attr_lower, + attr_work, + attr_merge, +}; + +struct ovl_attr { + struct attribute attr; + short attr_id; +}; + +#define OVL_ATTR(_name, _mode) \ + static struct ovl_attr ovl_attr_##_name = { \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ + .attr_id = attr_##_name, \ + } + +#define ATTR_LIST(name) &ovl_attr_##name.attr + +OVL_ATTR(upper, 0440); +OVL_ATTR(lower, 0440); +OVL_ATTR(work, 0440); +OVL_ATTR(merge, 0440); +static struct attribute *default_attrs[] = { + ATTR_LIST(upper), + ATTR_LIST(lower), + ATTR_LIST(work), + ATTR_LIST(merge), + NULL +}; + +int ovl_mergedir_backup(struct super_block *sb, const char *str) +{ + struct ovl_fs *ofs = sb->s_fs_info; + + ofs->config.mergedir = kstrdup(str, GFP_KERNEL); + if (!ofs->config.mergedir) + return -ENOMEM; + return 0; +} + +static ssize_t ovl_sysfs_show(struct kobject *kobj, struct attribute *attr, char *buf) +{ + struct ovl_fs *ofs = container_of(kobj, struct ovl_fs, kobj); + struct ovl_attr *ovl_attribute = + container_of(attr, struct ovl_attr, attr); + + switch (ovl_attribute->attr_id) { + case attr_upper: + return snprintf(buf, PAGE_SIZE, "%s\n", + ofs->config.upperdir); + case attr_lower: + return snprintf(buf, PAGE_SIZE, "%s\n", + ofs->config.lowerdir); + case attr_work: + return snprintf(buf, PAGE_SIZE, "%s\n", + ofs->config.workdir); + case attr_merge: + return snprintf(buf, PAGE_SIZE, "%s\n", + ofs->config.mergedir); + default: + return -EPERM; + } +} + +static void ovl_kobj_release(struct kobject *kobj) +{ + struct ovl_fs *ofs = container_of(kobj, struct ovl_fs, kobj); + + complete(&ofs->kobj_unregister); +} + +static struct sysfs_ops ovl_sysfs_ops = { + .show = ovl_sysfs_show +}; + +static struct kobj_type ovl_sb_ktype = { + .sysfs_ops = &ovl_sysfs_ops, + .default_attrs = default_attrs, + .release = ovl_kobj_release +}; + +int ovl_register_sysfs(struct super_block *sb) +{ + struct ovl_fs *ofs = OVL_FS(sb); + int err; + + init_completion(&ofs->kobj_unregister); + + err = kobject_init_and_add(&ofs->kobj, &ovl_sb_ktype, + ovl_root, "merge_%d_%d", + MAJOR(sb->s_dev), MINOR(sb->s_dev)); + if (err) { + kobject_put(&ofs->kobj); + wait_for_completion(&ofs->kobj_unregister); + } + return err; +} + +void ovl_unregister_sysfs(struct super_block *sb) +{ + kobject_del(&OVL_FS(sb)->kobj); +} + +int __init ovl_init_sysfs(void) +{ + ovl_root = kobject_create_and_add("overlayfs", fs_kobj); + if (!ovl_root) + return -ENOMEM; + return 0; +} + +void ovl_exit_sysfs(void) +{ + kobject_put(ovl_root); + ovl_root = NULL; +} diff --git a/include/linux/fs.h b/include/linux/fs.h index fa9d89379da199efc3e818d977c4b12f98d113a0..32d29c007a894729782d0ea1febe637e16f9a3f9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2316,7 +2316,11 @@ struct file_system_type { struct lock_class_key i_mutex_key; struct lock_class_key i_mutex_dir_key; +#ifndef __GENKSYMS__ + int (*mount_end) (struct fs_context *, struct path *); +#else KABI_RESERVE(1) +#endif KABI_RESERVE(2) KABI_RESERVE(3) KABI_RESERVE(4)