f2fs: support project quota
authorChao Yu <yuchao0@huawei.com>
Tue, 25 Jul 2017 16:01:41 +0000 (00:01 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Mon, 31 Jul 2017 23:48:32 +0000 (16:48 -0700)
This patch adds to support plain project quota.

Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Documentation/filesystems/f2fs.txt
fs/f2fs/f2fs.h
fs/f2fs/file.c
fs/f2fs/inode.c
fs/f2fs/namei.c
fs/f2fs/node.c
fs/f2fs/super.c
include/linux/f2fs_fs.h

index 273ccb26885e9c6ff6d1da17c655d6316cbefef8..b8f495a8b67dd7c7fd27ab5fdba030a113cbb88a 100644 (file)
@@ -164,6 +164,7 @@ io_bits=%u             Set the bit size of write IO requests. It should be set
                        with "mode=lfs".
 usrquota               Enable plain user disk quota accounting.
 grpquota               Enable plain group disk quota accounting.
+prjquota               Enable plain project quota accounting.
 
 ================================================================================
 DEBUGFS ENTRIES
index d0682fd4c690aeb172beee6c22f0aaffd99276f6..d486539086b69277ec9c7858cdfe4744af8184c2 100644 (file)
@@ -91,6 +91,7 @@ extern char *fault_name[FAULT_MAX];
 #define F2FS_MOUNT_LFS                 0x00040000
 #define F2FS_MOUNT_USRQUOTA            0x00080000
 #define F2FS_MOUNT_GRPQUOTA            0x00100000
+#define F2FS_MOUNT_PRJQUOTA            0x00200000
 
 #define clear_opt(sbi, option) ((sbi)->mount_opt.opt &= ~F2FS_MOUNT_##option)
 #define set_opt(sbi, option)   ((sbi)->mount_opt.opt |= F2FS_MOUNT_##option)
@@ -114,6 +115,7 @@ struct f2fs_mount_info {
 #define F2FS_FEATURE_BLKZONED          0x0002
 #define F2FS_FEATURE_ATOMIC_WRITE      0x0004
 #define F2FS_FEATURE_EXTRA_ATTR                0x0008
+#define F2FS_FEATURE_PRJQUOTA          0x0010
 
 #define F2FS_HAS_FEATURE(sb, mask)                                     \
        ((F2FS_SB(sb)->raw_super->feature & cpu_to_le32(mask)) != 0)
@@ -570,6 +572,7 @@ struct f2fs_inode_info {
        struct rw_semaphore i_mmap_sem;
 
        int i_extra_isize;              /* size of extra space located in i_addr */
+       kprojid_t i_projid;             /* id for project quota */
 };
 
 static inline void get_extent_info(struct extent_info *ext,
@@ -1887,6 +1890,20 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
        *addr ^= mask;
 }
 
+#define F2FS_REG_FLMASK                (~(FS_DIRSYNC_FL | FS_TOPDIR_FL))
+#define F2FS_OTHER_FLMASK      (FS_NODUMP_FL | FS_NOATIME_FL)
+#define F2FS_FL_INHERITED      (FS_PROJINHERIT_FL)
+
+static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags)
+{
+       if (S_ISDIR(mode))
+               return flags;
+       else if (S_ISREG(mode))
+               return flags & F2FS_REG_FLMASK;
+       else
+               return flags & F2FS_OTHER_FLMASK;
+}
+
 /* used for f2fs_inode_info->flags */
 enum {
        FI_NEW_INODE,           /* indicate newly allocated inode */
@@ -1916,6 +1933,7 @@ enum {
        FI_NO_PREALLOC,         /* indicate skipped preallocated blocks */
        FI_HOT_DATA,            /* indicate file is hot */
        FI_EXTRA_ATTR,          /* indicate file has extra attribute */
+       FI_PROJ_INHERIT,        /* indicate file inherits projectid */
 };
 
 static inline void __mark_inode_dirty_flag(struct inode *inode,
@@ -2239,6 +2257,12 @@ static inline int get_extra_isize(struct inode *inode)
        (offsetof(struct f2fs_inode, i_extra_end) -     \
        offsetof(struct f2fs_inode, i_extra_isize))     \
 
+#define F2FS_OLD_ATTRIBUTE_SIZE        (offsetof(struct f2fs_inode, i_addr))
+#define F2FS_FITS_IN_INODE(f2fs_inode, extra_isize, field)             \
+               ((offsetof(typeof(*f2fs_inode), field) +        \
+               sizeof((f2fs_inode)->field))                    \
+               <= (F2FS_OLD_ATTRIBUTE_SIZE + extra_isize))     \
+
 /*
  * file.c
  */
@@ -2836,6 +2860,11 @@ static inline int f2fs_sb_has_extra_attr(struct super_block *sb)
        return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_EXTRA_ATTR);
 }
 
+static inline int f2fs_sb_has_project_quota(struct super_block *sb)
+{
+       return F2FS_HAS_FEATURE(sb, F2FS_FEATURE_PRJQUOTA);
+}
+
 #ifdef CONFIG_BLK_DEV_ZONED
 static inline int get_blkz_type(struct f2fs_sb_info *sbi,
                        struct block_device *bdev, block_t blkaddr)
index d2ca4b71cb53549371a4e00245e4ea83b217530c..a0413c9514588f5b2dd308d2bd0c570af11b7b9e 100644 (file)
@@ -1518,19 +1518,6 @@ static int f2fs_file_flush(struct file *file, fl_owner_t id)
        return 0;
 }
 
-#define F2FS_REG_FLMASK                (~(FS_DIRSYNC_FL | FS_TOPDIR_FL))
-#define F2FS_OTHER_FLMASK      (FS_NODUMP_FL | FS_NOATIME_FL)
-
-static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags)
-{
-       if (S_ISDIR(mode))
-               return flags;
-       else if (S_ISREG(mode))
-               return flags & F2FS_REG_FLMASK;
-       else
-               return flags & F2FS_OTHER_FLMASK;
-}
-
 static int f2fs_ioc_getflags(struct file *filp, unsigned long arg)
 {
        struct inode *inode = file_inode(filp);
index 0a6699a23dfb5de1e738a823741ffad7938c950a..f15e663a1a15e506d51301dc40b496d951abfec8 100644 (file)
@@ -114,6 +114,7 @@ static int do_read_inode(struct inode *inode)
        struct f2fs_inode_info *fi = F2FS_I(inode);
        struct page *node_page;
        struct f2fs_inode *ri;
+       projid_t i_projid;
 
        /* Check if ino is within scope */
        if (check_nid_range(sbi, inode->i_ino)) {
@@ -173,6 +174,16 @@ static int do_read_inode(struct inode *inode)
        if (!need_inode_block_update(sbi, inode->i_ino))
                fi->last_disk_size = inode->i_size;
 
+       if (fi->i_flags & FS_PROJINHERIT_FL)
+               set_inode_flag(inode, FI_PROJ_INHERIT);
+
+       if (f2fs_has_extra_attr(inode) && f2fs_sb_has_project_quota(sbi->sb) &&
+                       F2FS_FITS_IN_INODE(ri, fi->i_extra_isize, i_projid))
+               i_projid = (projid_t)le32_to_cpu(ri->i_projid);
+       else
+               i_projid = F2FS_DEF_PROJID;
+       fi->i_projid = make_kprojid(&init_user_ns, i_projid);
+
        f2fs_put_page(node_page, 1);
 
        stat_inc_inline_xattr(inode);
@@ -299,9 +310,20 @@ int update_inode(struct inode *inode, struct page *node_page)
        ri->i_generation = cpu_to_le32(inode->i_generation);
        ri->i_dir_level = F2FS_I(inode)->i_dir_level;
 
-       if (f2fs_has_extra_attr(inode))
+       if (f2fs_has_extra_attr(inode)) {
                ri->i_extra_isize = cpu_to_le16(F2FS_I(inode)->i_extra_isize);
 
+               if (f2fs_sb_has_project_quota(F2FS_I_SB(inode)->sb) &&
+                       F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize,
+                                                               i_projid)) {
+                       projid_t i_projid;
+
+                       i_projid = from_kprojid(&init_user_ns,
+                                               F2FS_I(inode)->i_projid);
+                       ri->i_projid = cpu_to_le32(i_projid);
+               }
+       }
+
        __set_inode_rdev(inode, ri);
        set_cold_node(inode, node_page);
 
index 98e13f123d8737c8724b007ca10ee8abd1461c84..e241c1b2c636dd9f1a4f38da65c34ced1b22cccd 100644 (file)
@@ -58,6 +58,13 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
                goto fail;
        }
 
+       if (f2fs_sb_has_project_quota(sbi->sb) &&
+               (F2FS_I(dir)->i_flags & FS_PROJINHERIT_FL))
+               F2FS_I(inode)->i_projid = F2FS_I(dir)->i_projid;
+       else
+               F2FS_I(inode)->i_projid = make_kprojid(&init_user_ns,
+                                                       F2FS_DEF_PROJID);
+
        err = dquot_initialize(inode);
        if (err)
                goto fail_drop;
@@ -90,6 +97,12 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
        stat_inc_inline_inode(inode);
        stat_inc_inline_dir(inode);
 
+       F2FS_I(inode)->i_flags =
+               f2fs_mask_flags(mode, F2FS_I(dir)->i_flags & F2FS_FL_INHERITED);
+
+       if (F2FS_I(inode)->i_flags & FS_PROJINHERIT_FL)
+               set_inode_flag(inode, FI_PROJ_INHERIT);
+
        trace_f2fs_new_inode(inode, 0);
        return inode;
 
@@ -209,6 +222,11 @@ static int f2fs_link(struct dentry *old_dentry, struct inode *dir,
                        !fscrypt_has_permitted_context(dir, inode))
                return -EPERM;
 
+       if (is_inode_flag_set(dir, FI_PROJ_INHERIT) &&
+                       (!projid_eq(F2FS_I(dir)->i_projid,
+                       F2FS_I(old_dentry->d_inode)->i_projid)))
+               return -EXDEV;
+
        err = dquot_initialize(dir);
        if (err)
                return err;
@@ -733,6 +751,11 @@ static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry,
                goto out;
        }
 
+       if (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
+                       (!projid_eq(F2FS_I(new_dir)->i_projid,
+                       F2FS_I(old_dentry->d_inode)->i_projid)))
+               return -EXDEV;
+
        err = dquot_initialize(old_dir);
        if (err)
                goto out;
@@ -921,6 +944,14 @@ static int f2fs_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
                         !fscrypt_has_permitted_context(old_dir, new_inode)))
                return -EPERM;
 
+       if ((is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
+                       !projid_eq(F2FS_I(new_dir)->i_projid,
+                       F2FS_I(old_dentry->d_inode)->i_projid)) ||
+           (is_inode_flag_set(new_dir, FI_PROJ_INHERIT) &&
+                       !projid_eq(F2FS_I(old_dir)->i_projid,
+                       F2FS_I(new_dentry->d_inode)->i_projid)))
+               return -EXDEV;
+
        err = dquot_initialize(old_dir);
        if (err)
                goto out;
index 547b84fc10f64317a34f2e8316815992ecb0a7c0..11e2d49203b312a08bb9ab549f808163cbd9d37b 100644 (file)
@@ -2265,8 +2265,13 @@ retry:
        dst->i_links = cpu_to_le32(1);
        dst->i_xattr_nid = 0;
        dst->i_inline = src->i_inline & (F2FS_INLINE_XATTR | F2FS_EXTRA_ATTR);
-       if (dst->i_inline & F2FS_EXTRA_ATTR)
+       if (dst->i_inline & F2FS_EXTRA_ATTR) {
                dst->i_extra_isize = src->i_extra_isize;
+               if (f2fs_sb_has_project_quota(sbi->sb) &&
+                       F2FS_FITS_IN_INODE(src, le16_to_cpu(src->i_extra_isize),
+                                                               i_projid))
+                       dst->i_projid = src->i_projid;
+       }
 
        new_ni = old_ni;
        new_ni.ino = ino;
index b30097190605661dbae4ca71de1fcef17f322c64..1241739f4d547b8af5c3314d4c40c7340be4e157 100644 (file)
@@ -109,6 +109,7 @@ enum {
        Opt_nolazytime,
        Opt_usrquota,
        Opt_grpquota,
+       Opt_prjquota,
        Opt_err,
 };
 
@@ -146,6 +147,7 @@ static match_table_t f2fs_tokens = {
        {Opt_nolazytime, "nolazytime"},
        {Opt_usrquota, "usrquota"},
        {Opt_grpquota, "grpquota"},
+       {Opt_prjquota, "prjquota"},
        {Opt_err, NULL},
 };
 
@@ -392,9 +394,13 @@ static int parse_options(struct super_block *sb, char *options)
                case Opt_grpquota:
                        set_opt(sbi, GRPQUOTA);
                        break;
+               case Opt_prjquota:
+                       set_opt(sbi, PRJQUOTA);
+                       break;
 #else
                case Opt_usrquota:
                case Opt_grpquota:
+               case Opt_prjquota:
                        f2fs_msg(sb, KERN_INFO,
                                        "quota operations not supported");
                        break;
@@ -814,6 +820,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
                seq_puts(seq, ",usrquota");
        if (test_opt(sbi, GRPQUOTA))
                seq_puts(seq, ",grpquota");
+       if (test_opt(sbi, PRJQUOTA))
+               seq_puts(seq, ",prjquota");
 #endif
 
        return 0;
@@ -1172,6 +1180,12 @@ static void f2fs_quota_off_umount(struct super_block *sb)
                f2fs_quota_off(sb, type);
 }
 
+int f2fs_get_projid(struct inode *inode, kprojid_t *projid)
+{
+       *projid = F2FS_I(inode)->i_projid;
+       return 0;
+}
+
 static const struct dquot_operations f2fs_quota_operations = {
        .get_reserved_space = f2fs_get_reserved_space,
        .write_dquot    = dquot_commit,
@@ -1181,6 +1195,7 @@ static const struct dquot_operations f2fs_quota_operations = {
        .write_info     = dquot_commit_info,
        .alloc_dquot    = dquot_alloc,
        .destroy_dquot  = dquot_destroy,
+       .get_projid     = f2fs_get_projid,
        .get_next_id    = dquot_get_next_id,
 };
 
@@ -1964,7 +1979,7 @@ try_onemore:
 #ifdef CONFIG_QUOTA
        sb->dq_op = &f2fs_quota_operations;
        sb->s_qcop = &f2fs_quotactl_ops;
-       sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;
+       sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ;
 #endif
 
        sb->s_op = &f2fs_sops;
index 0c79ddd40b7015db11fcd0c119b9c249f537f603..50f1766836764659b8dd70bc1e26ccc7a0fd8677 100644 (file)
@@ -239,6 +239,7 @@ struct f2fs_inode {
                struct {
                        __le16 i_extra_isize;   /* extra inode attribute size */
                        __le16 i_padding;       /* padding */
+                       __le32 i_projid;        /* project id */
                        __le32 i_extra_end[0];  /* for attribute size calculation */
                };
                __le32 i_addr[DEF_ADDRS_PER_INODE];     /* Pointers to data blocks */
@@ -522,4 +523,6 @@ enum {
 
 #define S_SHIFT 12
 
+#define        F2FS_DEF_PROJID         0       /* default project ID */
+
 #endif  /* _LINUX_F2FS_FS_H */