diff options
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/btrfs/btrfs_inode.h | 1 | ||||
| -rw-r--r-- | fs/btrfs/compression.c | 6 | ||||
| -rw-r--r-- | fs/btrfs/ctree.h | 16 | ||||
| -rw-r--r-- | fs/btrfs/inode.c | 27 | ||||
| -rw-r--r-- | fs/btrfs/ioctl.c | 171 | 
5 files changed, 200 insertions, 21 deletions
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index ecf5f7d8166..acb4f351758 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -157,5 +157,4 @@ static inline void btrfs_i_size_write(struct inode *inode, u64 size)  	BTRFS_I(inode)->disk_i_size = size;  } -  #endif diff --git a/fs/btrfs/compression.c b/fs/btrfs/compression.c index ab07627084f..de1e2fd3208 100644 --- a/fs/btrfs/compression.c +++ b/fs/btrfs/compression.c @@ -123,7 +123,7 @@ static int check_compressed_csum(struct inode *inode,  	u32 csum;  	u32 *cb_sum = &cb->sums; -	if (btrfs_test_flag(inode, NODATASUM)) +	if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)  		return 0;  	for (i = 0; i < cb->nr_pages; i++) { @@ -670,7 +670,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,  			 */  			atomic_inc(&cb->pending_bios); -			if (!btrfs_test_flag(inode, NODATASUM)) { +			if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)) {  				btrfs_lookup_bio_sums(root, inode, comp_bio,  						      sums);  			} @@ -697,7 +697,7 @@ int btrfs_submit_compressed_read(struct inode *inode, struct bio *bio,  	ret = btrfs_bio_wq_end_io(root->fs_info, comp_bio, 0);  	BUG_ON(ret); -	if (!btrfs_test_flag(inode, NODATASUM)) +	if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM))  		btrfs_lookup_bio_sums(root, inode, comp_bio, sums);  	ret = btrfs_map_bio(root, READ, comp_bio, mirror_num, 0); diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h index 5fa7d7d287a..4d6e0b6f21e 100644 --- a/fs/btrfs/ctree.h +++ b/fs/btrfs/ctree.h @@ -1115,12 +1115,14 @@ struct btrfs_root {  #define BTRFS_INODE_READONLY		(1 << 2)  #define BTRFS_INODE_NOCOMPRESS		(1 << 3)  #define BTRFS_INODE_PREALLOC		(1 << 4) -#define btrfs_clear_flag(inode, flag)	(BTRFS_I(inode)->flags &= \ -					 ~BTRFS_INODE_##flag) -#define btrfs_set_flag(inode, flag)	(BTRFS_I(inode)->flags |= \ -					 BTRFS_INODE_##flag) -#define btrfs_test_flag(inode, flag)	(BTRFS_I(inode)->flags & \ -					 BTRFS_INODE_##flag) +#define BTRFS_INODE_SYNC		(1 << 5) +#define BTRFS_INODE_IMMUTABLE		(1 << 6) +#define BTRFS_INODE_APPEND		(1 << 7) +#define BTRFS_INODE_NODUMP		(1 << 8) +#define BTRFS_INODE_NOATIME		(1 << 9) +#define BTRFS_INODE_DIRSYNC		(1 << 10) + +  /* some macros to generate set/get funcs for the struct fields.  This   * assumes there is a lefoo_to_cpu for every type, so lets make a simple   * one for u8: @@ -2260,6 +2262,8 @@ int btrfs_cont_expand(struct inode *inode, loff_t size);  /* ioctl.c */  long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); +void btrfs_update_iflags(struct inode *inode); +void btrfs_inherit_iflags(struct inode *inode, struct inode *dir);  /* file.c */  int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync); diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index 917bf10597c..5b68330f858 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -368,7 +368,7 @@ again:  	 * inode has not been flagged as nocompress.  This flag can  	 * change at any time if we discover bad compression ratios.  	 */ -	if (!btrfs_test_flag(inode, NOCOMPRESS) && +	if (!(BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS) &&  	    btrfs_test_opt(root, COMPRESS)) {  		WARN_ON(pages);  		pages = kzalloc(sizeof(struct page *) * nr_pages, GFP_NOFS); @@ -469,7 +469,7 @@ again:  		nr_pages_ret = 0;  		/* flag the file so we don't compress in the future */ -		btrfs_set_flag(inode, NOCOMPRESS); +		BTRFS_I(inode)->flags |= BTRFS_INODE_NOCOMPRESS;  	}  	if (will_compress) {  		*num_added += 1; @@ -862,7 +862,7 @@ static int cow_file_range_async(struct inode *inode, struct page *locked_page,  		async_cow->locked_page = locked_page;  		async_cow->start = start; -		if (btrfs_test_flag(inode, NOCOMPRESS)) +		if (BTRFS_I(inode)->flags & BTRFS_INODE_NOCOMPRESS)  			cur_end = end;  		else  			cur_end = min(end, start + 512 * 1024 - 1); @@ -1133,10 +1133,10 @@ static int run_delalloc_range(struct inode *inode, struct page *locked_page,  	int ret;  	struct btrfs_root *root = BTRFS_I(inode)->root; -	if (btrfs_test_flag(inode, NODATACOW)) +	if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATACOW)  		ret = run_delalloc_nocow(inode, locked_page, start, end,  					 page_started, 1, nr_written); -	else if (btrfs_test_flag(inode, PREALLOC)) +	else if (BTRFS_I(inode)->flags & BTRFS_INODE_PREALLOC)  		ret = run_delalloc_nocow(inode, locked_page, start, end,  					 page_started, 0, nr_written);  	else if (!btrfs_test_opt(root, COMPRESS)) @@ -1290,7 +1290,7 @@ static int btrfs_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,  	int ret = 0;  	int skip_sum; -	skip_sum = btrfs_test_flag(inode, NODATASUM); +	skip_sum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM;  	ret = btrfs_bio_wq_end_io(root->fs_info, bio, 0);  	BUG_ON(ret); @@ -1790,7 +1790,8 @@ static int btrfs_readpage_end_io_hook(struct page *page, u64 start, u64 end,  		ClearPageChecked(page);  		goto good;  	} -	if (btrfs_test_flag(inode, NODATASUM)) + +	if (BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM)  		return 0;  	if (root->root_key.objectid == BTRFS_DATA_RELOC_TREE_OBJECTID && @@ -2156,6 +2157,8 @@ static void btrfs_read_locked_inode(struct inode *inode)  		init_special_inode(inode, inode->i_mode, rdev);  		break;  	} + +	btrfs_update_iflags(inode);  	return;  make_bad: @@ -3586,9 +3589,9 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,  			btrfs_find_block_group(root, 0, alloc_hint, owner);  	if ((mode & S_IFREG)) {  		if (btrfs_test_opt(root, NODATASUM)) -			btrfs_set_flag(inode, NODATASUM); +			BTRFS_I(inode)->flags |= BTRFS_INODE_NODATASUM;  		if (btrfs_test_opt(root, NODATACOW)) -			btrfs_set_flag(inode, NODATACOW); +			BTRFS_I(inode)->flags |= BTRFS_INODE_NODATACOW;  	}  	key[0].objectid = objectid; @@ -3642,6 +3645,8 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,  	location->offset = 0;  	btrfs_set_key_type(location, BTRFS_INODE_ITEM_KEY); +	btrfs_inherit_iflags(inode, dir); +  	insert_inode_hash(inode);  	inode_tree_add(inode);  	return inode; @@ -5075,7 +5080,7 @@ static int prealloc_file_range(struct btrfs_trans_handle *trans,  out:  	if (cur_offset > start) {  		inode->i_ctime = CURRENT_TIME; -		btrfs_set_flag(inode, PREALLOC); +		BTRFS_I(inode)->flags |= BTRFS_INODE_PREALLOC;  		if (!(mode & FALLOC_FL_KEEP_SIZE) &&  		    cur_offset > i_size_read(inode))  			btrfs_i_size_write(inode, cur_offset); @@ -5196,7 +5201,7 @@ static int btrfs_set_page_dirty(struct page *page)  static int btrfs_permission(struct inode *inode, int mask)  { -	if (btrfs_test_flag(inode, READONLY) && (mask & MAY_WRITE)) +	if ((BTRFS_I(inode)->flags & BTRFS_INODE_READONLY) && (mask & MAY_WRITE))  		return -EACCES;  	return generic_permission(inode, mask, btrfs_check_acl);  } diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 54dfd45cc59..926332a73cd 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -50,7 +50,172 @@  #include "volumes.h"  #include "locking.h" +/* Mask out flags that are inappropriate for the given type of inode. */ +static inline __u32 btrfs_mask_flags(umode_t mode, __u32 flags) +{ +	if (S_ISDIR(mode)) +		return flags; +	else if (S_ISREG(mode)) +		return flags & ~FS_DIRSYNC_FL; +	else +		return flags & (FS_NODUMP_FL | FS_NOATIME_FL); +} + +/* + * Export inode flags to the format expected by the FS_IOC_GETFLAGS ioctl. + */ +static unsigned int btrfs_flags_to_ioctl(unsigned int flags) +{ +	unsigned int iflags = 0; + +	if (flags & BTRFS_INODE_SYNC) +		iflags |= FS_SYNC_FL; +	if (flags & BTRFS_INODE_IMMUTABLE) +		iflags |= FS_IMMUTABLE_FL; +	if (flags & BTRFS_INODE_APPEND) +		iflags |= FS_APPEND_FL; +	if (flags & BTRFS_INODE_NODUMP) +		iflags |= FS_NODUMP_FL; +	if (flags & BTRFS_INODE_NOATIME) +		iflags |= FS_NOATIME_FL; +	if (flags & BTRFS_INODE_DIRSYNC) +		iflags |= FS_DIRSYNC_FL; + +	return iflags; +} + +/* + * Update inode->i_flags based on the btrfs internal flags. + */ +void btrfs_update_iflags(struct inode *inode) +{ +	struct btrfs_inode *ip = BTRFS_I(inode); + +	inode->i_flags &= ~(S_SYNC|S_APPEND|S_IMMUTABLE|S_NOATIME|S_DIRSYNC); + +	if (ip->flags & BTRFS_INODE_SYNC) +		inode->i_flags |= S_SYNC; +	if (ip->flags & BTRFS_INODE_IMMUTABLE) +		inode->i_flags |= S_IMMUTABLE; +	if (ip->flags & BTRFS_INODE_APPEND) +		inode->i_flags |= S_APPEND; +	if (ip->flags & BTRFS_INODE_NOATIME) +		inode->i_flags |= S_NOATIME; +	if (ip->flags & BTRFS_INODE_DIRSYNC) +		inode->i_flags |= S_DIRSYNC; +} + +/* + * Inherit flags from the parent inode. + * + * Unlike extN we don't have any flags we don't want to inherit currently. + */ +void btrfs_inherit_iflags(struct inode *inode, struct inode *dir) +{ +	unsigned int flags = BTRFS_I(dir)->flags; + +	if (S_ISREG(inode->i_mode)) +		flags &= ~BTRFS_INODE_DIRSYNC; +	else if (!S_ISDIR(inode->i_mode)) +		flags &= (BTRFS_INODE_NODUMP | BTRFS_INODE_NOATIME); + +	BTRFS_I(inode)->flags = flags; +	btrfs_update_iflags(inode); +} + +static int btrfs_ioctl_getflags(struct file *file, void __user *arg) +{ +	struct btrfs_inode *ip = BTRFS_I(file->f_path.dentry->d_inode); +	unsigned int flags = btrfs_flags_to_ioctl(ip->flags); + +	if (copy_to_user(arg, &flags, sizeof(flags))) +		return -EFAULT; +	return 0; +} + +static int btrfs_ioctl_setflags(struct file *file, void __user *arg) +{ +	struct inode *inode = file->f_path.dentry->d_inode; +	struct btrfs_inode *ip = BTRFS_I(inode); +	struct btrfs_root *root = ip->root; +	struct btrfs_trans_handle *trans; +	unsigned int flags, oldflags; +	int ret; + +	if (copy_from_user(&flags, arg, sizeof(flags))) +		return -EFAULT; + +	if (flags & ~(FS_IMMUTABLE_FL | FS_APPEND_FL | \ +		      FS_NOATIME_FL | FS_NODUMP_FL | \ +		      FS_SYNC_FL | FS_DIRSYNC_FL)) +		return -EOPNOTSUPP; + +	if (!is_owner_or_cap(inode)) +		return -EACCES; + +	mutex_lock(&inode->i_mutex); + +	flags = btrfs_mask_flags(inode->i_mode, flags); +	oldflags = btrfs_flags_to_ioctl(ip->flags); +	if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { +		if (!capable(CAP_LINUX_IMMUTABLE)) { +			ret = -EPERM; +			goto out_unlock; +		} +	} + +	ret = mnt_want_write(file->f_path.mnt); +	if (ret) +		goto out_unlock; + +	if (flags & FS_SYNC_FL) +		ip->flags |= BTRFS_INODE_SYNC; +	else +		ip->flags &= ~BTRFS_INODE_SYNC; +	if (flags & FS_IMMUTABLE_FL) +		ip->flags |= BTRFS_INODE_IMMUTABLE; +	else +		ip->flags &= ~BTRFS_INODE_IMMUTABLE; +	if (flags & FS_APPEND_FL) +		ip->flags |= BTRFS_INODE_APPEND; +	else +		ip->flags &= ~BTRFS_INODE_APPEND; +	if (flags & FS_NODUMP_FL) +		ip->flags |= BTRFS_INODE_NODUMP; +	else +		ip->flags &= ~BTRFS_INODE_NODUMP; +	if (flags & FS_NOATIME_FL) +		ip->flags |= BTRFS_INODE_NOATIME; +	else +		ip->flags &= ~BTRFS_INODE_NOATIME; +	if (flags & FS_DIRSYNC_FL) +		ip->flags |= BTRFS_INODE_DIRSYNC; +	else +		ip->flags &= ~BTRFS_INODE_DIRSYNC; + + +	trans = btrfs_join_transaction(root, 1); +	BUG_ON(!trans); + +	ret = btrfs_update_inode(trans, root, inode); +	BUG_ON(ret); + +	btrfs_update_iflags(inode); +	inode->i_ctime = CURRENT_TIME; +	btrfs_end_transaction(trans, root); +	mnt_drop_write(file->f_path.mnt); + out_unlock: +	mutex_unlock(&inode->i_mutex); +	return 0; +} + +static int btrfs_ioctl_getversion(struct file *file, int __user *arg) +{ +	struct inode *inode = file->f_path.dentry->d_inode; + +	return put_user(inode->i_generation, arg); +}  static noinline int create_subvol(struct btrfs_root *root,  				  struct dentry *dentry, @@ -1077,6 +1242,12 @@ long btrfs_ioctl(struct file *file, unsigned int  	void __user *argp = (void __user *)arg;  	switch (cmd) { +	case FS_IOC_GETFLAGS: +		return btrfs_ioctl_getflags(file, argp); +	case FS_IOC_SETFLAGS: +		return btrfs_ioctl_setflags(file, argp); +	case FS_IOC_GETVERSION: +		return btrfs_ioctl_getversion(file, argp);  	case BTRFS_IOC_SNAP_CREATE:  		return btrfs_ioctl_snap_create(file, argp, 0);  	case BTRFS_IOC_SUBVOL_CREATE:  |