Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6cbff00f authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Chris Mason
Browse files

Btrfs: implement FS_IOC_GETFLAGS/SETFLAGS/GETVERSION



Add support for the standard attributes set via chattr and read via
lsattr.  Currently we store the attributes in the flags value in
the btrfs inode, but I wonder whether we should split it into two so
that we don't have to keep converting between the two formats.

Remove the btrfs_clear_flag/btrfs_set_flag/btrfs_test_flag macros
as they were confusing the existing code and got in the way of the
new additions.

Also add the FS_IOC_GETVERSION ioctl for getting i_generation as it's
trivial.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent c289811c
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -157,5 +157,4 @@ static inline void btrfs_i_size_write(struct inode *inode, u64 size)
	BTRFS_I(inode)->disk_i_size = size;
}


#endif
+3 −3
Original line number Diff line number Diff line
@@ -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);
+10 −6
Original line number Diff line number Diff line
@@ -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);
+16 −11
Original line number Diff line number Diff line
@@ -368,7 +368,7 @@ static noinline int compress_file_range(struct inode *inode,
	 * 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 @@ static noinline int compress_file_range(struct inode *inode,
		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);
}
+171 −0
Original line number Diff line number Diff line
@@ -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: