diff options
Diffstat (limited to 'kernel/bpf/inode.c')
| -rw-r--r-- | kernel/bpf/inode.c | 99 | 
1 files changed, 92 insertions, 7 deletions
diff --git a/kernel/bpf/inode.c b/kernel/bpf/inode.c index 1ed8473ec537..0b030c9126d3 100644 --- a/kernel/bpf/inode.c +++ b/kernel/bpf/inode.c @@ -18,6 +18,7 @@  #include <linux/namei.h>  #include <linux/fs.h>  #include <linux/kdev_t.h> +#include <linux/parser.h>  #include <linux/filter.h>  #include <linux/bpf.h> @@ -87,6 +88,7 @@ static struct inode *bpf_get_inode(struct super_block *sb,  	switch (mode & S_IFMT) {  	case S_IFDIR:  	case S_IFREG: +	case S_IFLNK:  		break;  	default:  		return ERR_PTR(-EINVAL); @@ -119,6 +121,16 @@ static int bpf_inode_type(const struct inode *inode, enum bpf_type *type)  	return 0;  } +static void bpf_dentry_finalize(struct dentry *dentry, struct inode *inode, +				struct inode *dir) +{ +	d_instantiate(dentry, inode); +	dget(dentry); + +	dir->i_mtime = current_time(dir); +	dir->i_ctime = dir->i_mtime; +} +  static int bpf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)  {  	struct inode *inode; @@ -133,9 +145,7 @@ static int bpf_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)  	inc_nlink(inode);  	inc_nlink(dir); -	d_instantiate(dentry, inode); -	dget(dentry); - +	bpf_dentry_finalize(dentry, inode, dir);  	return 0;  } @@ -151,9 +161,7 @@ static int bpf_mkobj_ops(struct inode *dir, struct dentry *dentry,  	inode->i_op = iops;  	inode->i_private = dentry->d_fsdata; -	d_instantiate(dentry, inode); -	dget(dentry); - +	bpf_dentry_finalize(dentry, inode, dir);  	return 0;  } @@ -181,13 +189,37 @@ bpf_lookup(struct inode *dir, struct dentry *dentry, unsigned flags)  {  	if (strchr(dentry->d_name.name, '.'))  		return ERR_PTR(-EPERM); +  	return simple_lookup(dir, dentry, flags);  } +static int bpf_symlink(struct inode *dir, struct dentry *dentry, +		       const char *target) +{ +	char *link = kstrdup(target, GFP_USER | __GFP_NOWARN); +	struct inode *inode; + +	if (!link) +		return -ENOMEM; + +	inode = bpf_get_inode(dir->i_sb, dir, S_IRWXUGO | S_IFLNK); +	if (IS_ERR(inode)) { +		kfree(link); +		return PTR_ERR(inode); +	} + +	inode->i_op = &simple_symlink_inode_operations; +	inode->i_link = link; + +	bpf_dentry_finalize(dentry, inode, dir); +	return 0; +} +  static const struct inode_operations bpf_dir_iops = {  	.lookup		= bpf_lookup,  	.mknod		= bpf_mkobj,  	.mkdir		= bpf_mkdir, +	.symlink	= bpf_symlink,  	.rmdir		= simple_rmdir,  	.rename		= simple_rename,  	.link		= simple_link, @@ -324,6 +356,8 @@ static void bpf_evict_inode(struct inode *inode)  	truncate_inode_pages_final(&inode->i_data);  	clear_inode(inode); +	if (S_ISLNK(inode->i_mode)) +		kfree(inode->i_link);  	if (!bpf_inode_type(inode, &type))  		bpf_any_put(inode->i_private, type);  } @@ -331,15 +365,66 @@ static void bpf_evict_inode(struct inode *inode)  static const struct super_operations bpf_super_ops = {  	.statfs		= simple_statfs,  	.drop_inode	= generic_delete_inode, +	.show_options	= generic_show_options,  	.evict_inode	= bpf_evict_inode,  }; +enum { +	OPT_MODE, +	OPT_ERR, +}; + +static const match_table_t bpf_mount_tokens = { +	{ OPT_MODE, "mode=%o" }, +	{ OPT_ERR, NULL }, +}; + +struct bpf_mount_opts { +	umode_t mode; +}; + +static int bpf_parse_options(char *data, struct bpf_mount_opts *opts) +{ +	substring_t args[MAX_OPT_ARGS]; +	int option, token; +	char *ptr; + +	opts->mode = S_IRWXUGO; + +	while ((ptr = strsep(&data, ",")) != NULL) { +		if (!*ptr) +			continue; + +		token = match_token(ptr, bpf_mount_tokens, args); +		switch (token) { +		case OPT_MODE: +			if (match_octal(&args[0], &option)) +				return -EINVAL; +			opts->mode = option & S_IALLUGO; +			break; +		/* We might like to report bad mount options here, but +		 * traditionally we've ignored all mount options, so we'd +		 * better continue to ignore non-existing options for bpf. +		 */ +		} +	} + +	return 0; +} +  static int bpf_fill_super(struct super_block *sb, void *data, int silent)  {  	static struct tree_descr bpf_rfiles[] = { { "" } }; +	struct bpf_mount_opts opts;  	struct inode *inode;  	int ret; +	save_mount_options(sb, data); + +	ret = bpf_parse_options(data, &opts); +	if (ret) +		return ret; +  	ret = simple_fill_super(sb, BPF_FS_MAGIC, bpf_rfiles);  	if (ret)  		return ret; @@ -349,7 +434,7 @@ static int bpf_fill_super(struct super_block *sb, void *data, int silent)  	inode = sb->s_root->d_inode;  	inode->i_op = &bpf_dir_iops;  	inode->i_mode &= ~S_IALLUGO; -	inode->i_mode |= S_ISVTX | S_IRWXUGO; +	inode->i_mode |= S_ISVTX | opts.mode;  	return 0;  }  |