| From fbabfd0f4ee2e8847bf56edf481249ad1bb8c44d Mon Sep 17 00:00:00 2001 |
| From: "Eric W. Biederman" <ebiederm@xmission.com> |
| Date: Sat, 9 May 2015 15:54:49 -0500 |
| Subject: fs: Add helper functions for permanently empty directories. |
| |
| From: "Eric W. Biederman" <ebiederm@xmission.com> |
| |
| commit fbabfd0f4ee2e8847bf56edf481249ad1bb8c44d upstream. |
| |
| To ensure it is safe to mount proc and sysfs I need to check if |
| filesystems that are mounted on top of them are mounted on truly empty |
| directories. Given that some directories can gain entries over time, |
| knowing that a directory is empty right now is insufficient. |
| |
| Therefore add supporting infrastructure for permantently empty |
| directories that proc and sysfs can use when they create mount points |
| for filesystems and fs_fully_visible can use to test for permanently |
| empty directories to ensure that nothing will be gained by mounting a |
| fresh copy of proc or sysfs. |
| |
| Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/libfs.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| include/linux/fs.h | 2 + |
| 2 files changed, 98 insertions(+) |
| |
| --- a/fs/libfs.c |
| +++ b/fs/libfs.c |
| @@ -1093,3 +1093,99 @@ simple_nosetlease(struct file *filp, lon |
| return -EINVAL; |
| } |
| EXPORT_SYMBOL(simple_nosetlease); |
| + |
| + |
| +/* |
| + * Operations for a permanently empty directory. |
| + */ |
| +static struct dentry *empty_dir_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) |
| +{ |
| + return ERR_PTR(-ENOENT); |
| +} |
| + |
| +static int empty_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, |
| + struct kstat *stat) |
| +{ |
| + struct inode *inode = d_inode(dentry); |
| + generic_fillattr(inode, stat); |
| + return 0; |
| +} |
| + |
| +static int empty_dir_setattr(struct dentry *dentry, struct iattr *attr) |
| +{ |
| + return -EPERM; |
| +} |
| + |
| +static int empty_dir_setxattr(struct dentry *dentry, const char *name, |
| + const void *value, size_t size, int flags) |
| +{ |
| + return -EOPNOTSUPP; |
| +} |
| + |
| +static ssize_t empty_dir_getxattr(struct dentry *dentry, const char *name, |
| + void *value, size_t size) |
| +{ |
| + return -EOPNOTSUPP; |
| +} |
| + |
| +static int empty_dir_removexattr(struct dentry *dentry, const char *name) |
| +{ |
| + return -EOPNOTSUPP; |
| +} |
| + |
| +static ssize_t empty_dir_listxattr(struct dentry *dentry, char *list, size_t size) |
| +{ |
| + return -EOPNOTSUPP; |
| +} |
| + |
| +static const struct inode_operations empty_dir_inode_operations = { |
| + .lookup = empty_dir_lookup, |
| + .permission = generic_permission, |
| + .setattr = empty_dir_setattr, |
| + .getattr = empty_dir_getattr, |
| + .setxattr = empty_dir_setxattr, |
| + .getxattr = empty_dir_getxattr, |
| + .removexattr = empty_dir_removexattr, |
| + .listxattr = empty_dir_listxattr, |
| +}; |
| + |
| +static loff_t empty_dir_llseek(struct file *file, loff_t offset, int whence) |
| +{ |
| + /* An empty directory has two entries . and .. at offsets 0 and 1 */ |
| + return generic_file_llseek_size(file, offset, whence, 2, 2); |
| +} |
| + |
| +static int empty_dir_readdir(struct file *file, struct dir_context *ctx) |
| +{ |
| + dir_emit_dots(file, ctx); |
| + return 0; |
| +} |
| + |
| +static const struct file_operations empty_dir_operations = { |
| + .llseek = empty_dir_llseek, |
| + .read = generic_read_dir, |
| + .iterate = empty_dir_readdir, |
| + .fsync = noop_fsync, |
| +}; |
| + |
| + |
| +void make_empty_dir_inode(struct inode *inode) |
| +{ |
| + set_nlink(inode, 2); |
| + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; |
| + inode->i_uid = GLOBAL_ROOT_UID; |
| + inode->i_gid = GLOBAL_ROOT_GID; |
| + inode->i_rdev = 0; |
| + inode->i_size = 2; |
| + inode->i_blkbits = PAGE_SHIFT; |
| + inode->i_blocks = 0; |
| + |
| + inode->i_op = &empty_dir_inode_operations; |
| + inode->i_fop = &empty_dir_operations; |
| +} |
| + |
| +bool is_empty_dir_inode(struct inode *inode) |
| +{ |
| + return (inode->i_fop == &empty_dir_operations) && |
| + (inode->i_op == &empty_dir_inode_operations); |
| +} |
| --- a/include/linux/fs.h |
| +++ b/include/linux/fs.h |
| @@ -2721,6 +2721,8 @@ extern struct dentry *simple_lookup(stru |
| extern ssize_t generic_read_dir(struct file *, char __user *, size_t, loff_t *); |
| extern const struct file_operations simple_dir_operations; |
| extern const struct inode_operations simple_dir_inode_operations; |
| +extern void make_empty_dir_inode(struct inode *inode); |
| +extern bool is_empty_dir_inode(struct inode *inode); |
| struct tree_descr { char *name; const struct file_operations *ops; int mode; }; |
| struct dentry *d_alloc_name(struct dentry *, const char *); |
| extern int simple_fill_super(struct super_block *, unsigned long, struct tree_descr *); |