Signed-off-by: Dmitry Monakhov <dmonakhov@xxxxxxxxxx> --- fs/ext4/Kconfig | 8 +++ fs/ext4/Makefile | 1 + fs/ext4/ext4.h | 8 ++- fs/ext4/ialloc.c | 5 +- fs/ext4/inode.c | 13 ++++- fs/ext4/super.c | 9 +++- fs/ext4/xattr.c | 7 ++ fs/ext4/xattr.h | 11 +++ fs/ext4/xattr_metagroup.c | 153 +++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 211 insertions(+), 4 deletions(-) create mode 100644 fs/ext4/xattr_metagroup.c diff --git a/fs/ext4/Kconfig b/fs/ext4/Kconfig index 9ed1bb1..e3365db 100644 --- a/fs/ext4/Kconfig +++ b/fs/ext4/Kconfig @@ -74,6 +74,14 @@ config EXT4_FS_SECURITY If you are not using a security module that requires using extended attributes for file security labels, say N. +config EXT4_METAGROUP + bool "Ext4 metagroup support" + depends on METAGROUP + depends on EXT4_FS_XATTR + help + Enables metagroup inode identifier support for ext4 filesystem. + This feature allow to assign some id to inodes similar to + uid/gid. config EXT4_DEBUG bool "EXT4 debugging support" diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile index 8867b2a..62f75b8 100644 --- a/fs/ext4/Makefile +++ b/fs/ext4/Makefile @@ -11,3 +11,4 @@ ext4-y := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \ ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o +ext4-$(CONFIG_EXT4_METAGROUP) += xattr_metagroup.o diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index b2c01a2..c3f95e7 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -719,6 +719,10 @@ struct ext4_inode_info { */ tid_t i_sync_tid; tid_t i_datasync_tid; +#ifdef CONFIG_EXT4_METAGROUP + /* metagroup id, additional owner identifier similar to uid/gid */ + unsigned int i_mid; +#endif }; /* @@ -766,7 +770,9 @@ struct ext4_inode_info { #define EXT4_MOUNT_DELALLOC 0x8000000LL /* Delalloc support */ #define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000LL /* Abort on file data write */ #define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000LL /* Block validity checking */ -#define EXT4_MOUNT_DISCARD 0x40000000LL /* Issue DISCARD requests */ +#define EXT4_MOUNT_DISCARD 0x40000000LL /* Issue DISCARD requests +*/ +#define EXT4_MOUNT_METAGROUP 0x80000000LL /* extended owner id */ #define clear_opt(o, opt) o &= ~EXT4_MOUNT_##opt #define set_opt(o, opt) o |= EXT4_MOUNT_##opt diff --git a/fs/ext4/ialloc.c b/fs/ext4/ialloc.c index f3624ea..535b905 100644 --- a/fs/ext4/ialloc.c +++ b/fs/ext4/ialloc.c @@ -1032,7 +1032,10 @@ got: ei->i_state = EXT4_STATE_NEW; ei->i_extra_isize = EXT4_SB(sb)->s_want_extra_isize; - +#ifdef CONFIG_EXT4_METAGROUP + // XXX: move this to generic inode init helper + EXT4_I(inode)->i_mid = EXT4_I(dir)->i_mid; +#endif ret = inode; if (vfs_dq_alloc_inode(inode)) { err = -EDQUOT; diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index e119524..b1b5fdc 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4936,7 +4936,18 @@ struct inode *ext4_iget(struct super_block *sb, unsigned long ino) } if (ret) goto bad_inode; - +#ifdef CONFIG_EXT4_METAGROUP + if(test_opt(inode->i_sb, METAGROUP)) { + ret = ext4_metagroup_read(inode, &ei->i_mid); + if (ret == -ENODATA) { + ei->i_mid = 0; + ret = 0; + } + if (ret) + goto bad_inode; + } else + ei->i_mid = 0; +#endif if (S_ISREG(inode->i_mode)) { inode->i_op = &ext4_file_inode_operations; inode->i_fop = &ext4_file_operations; diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 80d6c14..cb169f8 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -923,6 +923,9 @@ static int ext4_show_options(struct seq_file *seq, struct vfsmount *vfs) if (test_opt(sb, DISCARD)) seq_puts(seq, ",discard"); + if (test_opt(sb, METAGROUP)) + seq_puts(seq, ",metagroup"); + if (test_opt(sb, NOLOAD)) seq_puts(seq, ",norecovery"); @@ -1112,7 +1115,7 @@ enum { Opt_stripe, Opt_delalloc, Opt_nodelalloc, Opt_block_validity, Opt_noblock_validity, Opt_inode_readahead_blks, Opt_journal_ioprio, - Opt_discard, Opt_nodiscard, + Opt_discard, Opt_nodiscard, Opt_metagroup, }; static const match_table_t tokens = { @@ -1181,6 +1184,7 @@ static const match_table_t tokens = { {Opt_noauto_da_alloc, "noauto_da_alloc"}, {Opt_discard, "discard"}, {Opt_nodiscard, "nodiscard"}, + {Opt_metagroup, "metagroup"}, {Opt_err, NULL}, }; @@ -1612,6 +1616,9 @@ set_qf_format: case Opt_nodiscard: clear_opt(sbi->s_mount_opt, DISCARD); break; + case Opt_metagroup: + set_opt(sbi->s_mount_opt, METAGROUP); + break; default: ext4_msg(sb, KERN_ERR, "Unrecognized mount option \"%s\" " diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index f3a2f7e..a97294b 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -107,6 +107,10 @@ static struct xattr_handler *ext4_xattr_handler_map[] = { #ifdef CONFIG_EXT4_FS_SECURITY [EXT4_XATTR_INDEX_SECURITY] = &ext4_xattr_security_handler, #endif +#ifdef CONFIG_EXT4_METAGROUP + [EXT4_XATTR_INDEX_METAGROUP] = &ext4_xattr_metagroup_handler, +#endif + }; struct xattr_handler *ext4_xattr_handlers[] = { @@ -119,6 +123,9 @@ struct xattr_handler *ext4_xattr_handlers[] = { #ifdef CONFIG_EXT4_FS_SECURITY &ext4_xattr_security_handler, #endif +#ifdef CONFIG_EXT4_METAGROUP + &ext4_xattr_metagroup_handler, +#endif NULL }; diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 8ede88b..46b8369 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h @@ -21,6 +21,7 @@ #define EXT4_XATTR_INDEX_TRUSTED 4 #define EXT4_XATTR_INDEX_LUSTRE 5 #define EXT4_XATTR_INDEX_SECURITY 6 +#define EXT4_XATTR_INDEX_METAGROUP 7 struct ext4_xattr_header { __le32 h_magic; /* magic number for identification */ @@ -70,6 +71,7 @@ extern struct xattr_handler ext4_xattr_trusted_handler; extern struct xattr_handler ext4_xattr_acl_access_handler; extern struct xattr_handler ext4_xattr_acl_default_handler; extern struct xattr_handler ext4_xattr_security_handler; +extern struct xattr_handler ext4_xattr_metagroup_handler; extern ssize_t ext4_listxattr(struct dentry *, char *, size_t); @@ -153,3 +155,12 @@ static inline int ext4_init_security(handle_t *handle, struct inode *inode, return 0; } #endif + +#ifdef CONFIG_EXT4_METAGROUP +extern int ext4_metagroup_read(struct inode *inode, unsigned int *mid); +#else +inline int ext4_metagroup_read(struct inode *inode, unsigned int *mid) +{ + return -ENOTSUPP; +} +#endif diff --git a/fs/ext4/xattr_metagroup.c b/fs/ext4/xattr_metagroup.c new file mode 100644 index 0000000..5585d4d --- /dev/null +++ b/fs/ext4/xattr_metagroup.c @@ -0,0 +1,153 @@ +/* + * linux/fs/ext4/xattr_metagroup.c + * + * Copyright (C) 2010 Parallels Inc + * Dmitry Monakhov <dmonakhov@xxxxxxxxxx> + */ + +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/slab.h> +#include <linux/capability.h> +#include <linux/fs.h> +#include <linux/quotaops.h> +#include "ext4_jbd2.h" +#include "ext4.h" +#include "xattr.h" + +/* + * Read metagroup id from inode's xattr + * Locking: none + */ +int ext4_metagroup_read(struct inode *inode, unsigned int *mid) +{ + __le32 dsk_mid; + int retval; + retval = ext4_xattr_get(inode, EXT4_XATTR_INDEX_METAGROUP, "", + &dsk_mid, sizeof (dsk_mid)); + if (retval > 0 && retval != sizeof(dsk_mid)) + return -EIO; + *mid = le32_to_cpu(dsk_mid); + return retval; + +} + +/* + * Save metagroup id to inode's xattr + * Locking: none + */ +static int ext4_metagroup_write(handle_t *handle, struct inode *inode, + unsigned int mid, int xflags) +{ + __le32 dsk_mid; + int retval; + retval = ext4_xattr_set_handle(handle, inode, EXT4_XATTR_INDEX_METAGROUP, "", + &dsk_mid, sizeof (dsk_mid), xflags); + if (retval > 0 && retval != sizeof(dsk_mid)) + return -EIO; + return retval; +} + +/* + * Change metagroup id. + * Called under inode->i_mutex + */ +static int ext4_metagroup_change(struct inode *inode, unsigned int new_mid) +{ + /* + * One data_trans_blocks chunk for xattr update. + * One quota_trans_blocks chunk for quota transfer, and one + * quota_trans_block chunk for emergency quota rollback transfer, + * because quota rollback may result new quota blocks allocation. + */ + unsigned credits = EXT4_DATA_TRANS_BLOCKS(inode->i_sb) + + EXT4_QUOTA_TRANS_BLOCKS(inode->i_sb) * 2; + qid_t qid[MAXQUOTAS]; + int ret, ret2 = 0; + unsigned retries = 0; + handle_t *handle; + + vfs_dq_init(inode); +retry: + handle = ext4_journal_start(inode, credits); + if (IS_ERR(handle)) { + ret = PTR_ERR(handle); + ext4_std_error(inode->i_sb, ret); + goto out; + } + /* Inode may not have metagroup xattr yet. Create it explicitly */ + ret = ext4_metagroup_write(handle, inode, EXT4_I(inode)->i_mid, + XATTR_CREATE); + if (ret == -EEXIST) + ret = 0; + if (ret) { + ret2 = ext4_journal_stop(handle); + if (ret2) + ret = ret2; + if (ret == -ENOSPC && + ext4_should_retry_alloc(inode->i_sb, &retries)) + goto retry; + } +#ifdef CONFIG_QUOTA + qid[MGRQUOTA] = new_mid; + if (inode->i_sb->dq_op->transfer(inode, qid, 1 << MGRQUOTA)) + ret = -EDQUOT; +#endif + ret = ext4_metagroup_write(handle, inode, new_mid, XATTR_REPLACE); + if (ret) { + /* + * Function may fail only due to fatal error, Nor than less + * we have try to rollback quota changes. + */ +#ifdef CONFIG_QUOTA + qid[MGRQUOTA] = EXT4_I(inode)->i_mid; + if (inode->i_sb->dq_op->transfer(inode, qid, 1 << MGRQUOTA)) + ret = -EDQUOT; +#endif + ext4_std_error(inode->i_sb, ret); + + } + EXT4_I(inode)->i_mid = new_mid; + ret2 = ext4_journal_stop(handle); +out: + if (ret2) + ret = ret2; + return ret; +} +static size_t +ext4_xattr_metagroup_list(struct dentry *dentry, char *list, size_t list_size, + const char *name, size_t name_len, int type) +{ + if (list && XATTR_METAGROUP_LEN <= list_size) + memcpy(list, XATTR_METAGROUP_PREFIX, XATTR_METAGROUP_LEN); + return XATTR_METAGROUP_LEN; + +} + +static int +ext4_xattr_metagroup_get(struct dentry *dentry, const char *name, + void *buffer, size_t size, int type) +{ + if (strcmp(name, "") != 0) + return -EINVAL; + return ext4_xattr_get(dentry->d_inode, EXT4_XATTR_INDEX_METAGROUP, + name, buffer, size); +} + +static int +ext4_xattr_metagroup_set(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags, int type) +{ + unsigned int new_mid; + if (strcmp(name, "") != 0) + return -EINVAL; + new_mid = simple_strtoul(value, (char **)&value, 0); + return ext4_metagroup_change(dentry->d_inode, new_mid); +} + +struct xattr_handler ext4_xattr_metagroup_handler = { + .prefix = XATTR_METAGROUP, + .list = ext4_xattr_metagroup_list, + .get = ext4_xattr_metagroup_get, + .set = ext4_xattr_metagroup_set, +}; -- 1.6.6 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html