Whamcloud - gitweb
Branch b1_6
authoranserper <anserper>
Tue, 25 Dec 2007 19:15:30 +0000 (19:15 +0000)
committeranserper <anserper>
Tue, 25 Dec 2007 19:15:30 +0000 (19:15 +0000)
b=13904
i=tianzy
i=johann

Implementation of 64 bit quota support

20 files changed:
lustre/doc/lfs.1
lustre/include/linux/lustre_fsfilt.h
lustre/include/lustre/lustre_idl.h
lustre/include/lustre/lustre_user.h
lustre/include/lustre_quota.h
lustre/llite/dir.c
lustre/lvfs/Makefile.in
lustre/lvfs/autoMakefile.am
lustre/lvfs/fsfilt_ext3.c
lustre/lvfs/lustre_quota_fmt.c
lustre/lvfs/lustre_quota_fmt.h
lustre/lvfs/lustre_quota_fmt_convert.c [new file with mode: 0644]
lustre/lvfs/quotafmt_test.c
lustre/quota/quota_ctl.c
lustre/quota/quota_interface.c
lustre/quota/quota_internal.h
lustre/quota/quota_master.c
lustre/tests/sanity-quota.sh
lustre/tests/test-framework.sh
lustre/utils/lfs.c

index 662bf06..5192239 100644 (file)
@@ -26,6 +26,8 @@ lfs \- Lustre utility to create a file with specific striping pattern, find the
 .br
 .B lfs quotaoff [-ug] <filesystem>
 .br
+.B lfs quotainv [-ug] <filesystem>
+.br
 .B lfs setquota [-u|-g] <name> <block-softlimit> <block-hardlimit> 
              \fB<inode-softlimit> <inode-hardlimit> <filesystem>\fR
 .br
@@ -68,6 +70,9 @@ To turn filesystem quotas on. Options specify quota for users (-u) groups (-g) a
 .B quotaoff [-ugf] <filesystem>
 To turn filesystem quotas off.  Options specify quota for users (-u) groups (-g) and force (-f)
 .TP
+.B quotainv [-ug] <filesystem>
+Clear quota files, all of their quota entries, for (-u) users or (-g) groups; after quotainv one must use quotacheck before using quotas. USE THIS COMMAND WITH EXTREME CARE, ITS RESULTS CANNOT BE UNDONE.
+.TP
 .B setquota  [-u|-g] <name> <block-softlimit> <block-hardlimit> <inode-softlimit> <inode-hardlimit> <filesystem>
 To set filesystem quotas for users or groups. Limits are specific as blocks and inodes, see EXAMPLES
 .TP
index cee0c21..e6c767c 100644 (file)
@@ -404,7 +404,7 @@ static inline int fsfilt_quotainfo(struct obd_device *obd,
 }
 
 static inline int fsfilt_qids(struct obd_device *obd, struct file *file,
-                              struct inode *inode, int type, 
+                              struct inode *inode, int type,
                               struct list_head *list)
 {
         if (obd->obd_fsops->fs_qids)
index 3f94144..a3ba388 100644 (file)
@@ -1493,4 +1493,5 @@ typedef enum {
 #define QUOTA_RET_NOQUOTA      1 /* not support quota */
 #define QUOTA_RET_NOLIMIT      2 /* quota limit isn't set */
 #define QUOTA_RET_ACQUOTA      3 /* need to acquire extra quota */
+
 #endif
index e046c94..57e7c64 100644 (file)
@@ -163,12 +163,15 @@ static inline char *obd_uuid2str(struct obd_uuid *uuid)
         return (char *)(uuid->uuid);
 }
 
-#define LUSTRE_Q_QUOTAON  0x800002     /* turn quotas on */
-#define LUSTRE_Q_QUOTAOFF 0x800003     /* turn quotas off */
-#define LUSTRE_Q_GETINFO  0x800005     /* get information about quota files */
-#define LUSTRE_Q_SETINFO  0x800006     /* set information about quota files */
-#define LUSTRE_Q_GETQUOTA 0x800007     /* get user quota structure */
-#define LUSTRE_Q_SETQUOTA 0x800008     /* set user quota structure */
+/* these must be explicitly translated into linux Q_* in ll_dir_ioctl */
+#define LUSTRE_Q_QUOTAON    0x800002     /* turn quotas on */
+#define LUSTRE_Q_QUOTAOFF   0x800003     /* turn quotas off */
+#define LUSTRE_Q_GETINFO    0x800005     /* get information about quota files */
+#define LUSTRE_Q_SETINFO    0x800006     /* set information about quota files */
+#define LUSTRE_Q_GETQUOTA   0x800007     /* get user quota structure */
+#define LUSTRE_Q_SETQUOTA   0x800008     /* set user quota structure */
+/* lustre-specific control commands */
+#define LUSTRE_Q_INVALIDATE 0x80000b     /* invalidate quota data */
 
 #define UGQUOTA 2       /* set both USRQUOTA and GRPQUOTA */
 
@@ -218,6 +221,11 @@ struct mds_grp_downcall_data {
 
 #endif /* !__KERNEL__ */
 
+typedef enum lustre_quota_version {
+        LUSTRE_QUOTA_V1 = 0,
+        LUSTRE_QUOTA_V2 = 1
+} lustre_quota_version_t;
+
 /* XXX: same as if_dqinfo struct in kernel */
 struct obd_dqinfo {
         __u64 dqi_bgrace;
index c7e97c1..2e0acb8 100644 (file)
@@ -42,12 +42,24 @@ struct lustre_mem_dqinfo {
 struct lustre_quota_info {
         struct file *qi_files[MAXQUOTAS];
         struct lustre_mem_dqinfo qi_info[MAXQUOTAS];
+        lustre_quota_version_t qi_version;
 };
 
 #define DQ_STATUS_AVAIL         0x0     /* Available dquot */
 #define DQ_STATUS_SET           0x01    /* Sombody is setting dquot */
 #define DQ_STATUS_RECOVERY      0x02    /* dquot is in recovery */
 
+struct lustre_mem_dqblk {
+        __u64 dqb_bhardlimit;  /* absolute limit on disk blks alloc */
+        __u64 dqb_bsoftlimit;  /* preferred limit on disk blks */
+        __u64 dqb_curspace;    /* current used space */
+        __u64 dqb_ihardlimit;  /* absolute limit on allocated inodes */
+        __u64 dqb_isoftlimit;  /* preferred inode limit */
+        __u64 dqb_curinodes;   /* current # allocated inodes */
+        time_t dqb_btime;      /* time limit for excessive disk use */
+        time_t dqb_itime;      /* time limit for excessive inode use */
+};
+
 struct lustre_dquot {
         /* Hash list in memory, protect by dquot_hash_lock */
         struct list_head dq_hash;
@@ -63,7 +75,7 @@ struct lustre_dquot {
         int dq_type;                    /* Type fo quota (USRQUOTA, GRPQUOUTA) */
         unsigned short dq_status;       /* See DQ_STATUS_ */
         unsigned long dq_flags;         /* See DQ_ in quota.h */
-        struct mem_dqblk dq_dqb;        /* Diskquota usage */
+        struct lustre_mem_dqblk dq_dqb; /* Diskquota usage */
 };
 
 struct dquot_id {
@@ -77,6 +89,7 @@ struct dquot_id {
 #define QFILE_INIT_INFO         4
 #define QFILE_RD_DQUOT          5
 #define QFILE_WR_DQUOT          6
+#define QFILE_CONVERT           7
 
 /* admin quotafile operations */
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
@@ -88,6 +101,7 @@ int lustre_commit_dquot(struct lustre_dquot *dquot);
 int lustre_init_quota_info(struct lustre_quota_info *lqi, int type);
 int lustre_get_qids(struct file *file, struct inode *inode, int type, 
                     struct list_head *list);
+int lustre_quota_convert(struct lustre_quota_info *lqi, int type);
 #else
 
 #ifndef DQ_FAKE_B
@@ -122,6 +136,11 @@ static inline int lustre_init_quota_info(struct lustre_quota_info *lqi,
 {
         return 0;
 }
+static inline int lustre_quota_convert(struct lustre_quota_info *lqi,
+                                       int type)
+{
+        return 0;
+}
 #endif  /* KERNEL_VERSION(2,5,0) */
 
 #define LL_DQUOT_OFF(sb)    DQUOT_OFF(sb)
@@ -461,4 +480,14 @@ extern quota_interface_t mdc_quota_interface;
 extern quota_interface_t lov_quota_interface;
 #endif
 
+#define LUSTRE_ADMIN_QUOTAFILES_V1 {\
+        "admin_quotafile.usr", /* user admin quotafile */\
+        "admin_quotafile.grp"  /* group admin quotafile */\
+}
+
+#define LUSTRE_ADMIN_QUOTAFILES_V2 {\
+        "admin_quotafile_v2.usr",       /* user admin quotafile */\
+        "admin_quotafile_v2.grp"        /* group admin quotafile */\
+}
+
 #endif /* _LUSTRE_QUOTA_H */
index 6e95fa3..813351d 100644 (file)
@@ -967,6 +967,7 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file,
                 type = qctl->qc_type;
                 id = qctl->qc_id;
                 switch (cmd) {
+                case LUSTRE_Q_INVALIDATE:
                 case Q_QUOTAON:
                 case Q_QUOTAOFF:
                 case Q_SETQUOTA:
index f103b62..de72dbd 100644 (file)
@@ -3,7 +3,7 @@ MODULES := lvfs
 @QUOTA_TRUE@MODULES += quotafmt_test
 
 lvfs-objs := lvfs_common.o lvfs_linux.o fsfilt.o upcall_cache.o prng.o lvfs_lib.o
-@QUOTA_TRUE@lvfs-objs += lustre_quota_fmt.o
+@QUOTA_TRUE@lvfs-objs += lustre_quota_fmt.o lustre_quota_fmt_convert.o
 
 @QUOTA_TRUE@quotafmt-objs := quotafmt_test.o
 
index f5cb165..b7b3a48 100644 (file)
@@ -76,6 +76,7 @@ DIST_SOURCES = fsfilt.c fsfilt_ext3.c fsfilt_reiserfs.c lvfs_common.c \
        lvfs_internal.h lvfs_linux.c lvfs_userfs.c \
        upcall_cache.c prng.c lvfs_lib.c\
        lustre_quota_fmt.c lustre_quota_fmt.h quotafmt_test.c \
+       lustre_quota_fmt_convert.c \
         # quotacheck_test.c quotactl_test.c fsfilt_ext3_quota.h
 
 MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ 
index e2f5e10..693de9e 100644 (file)
@@ -2063,6 +2063,9 @@ static int fsfilt_ext3_quotainfo(struct lustre_quota_info *lqi, int type,
         case QFILE_INIT_INFO:
                 rc = lustre_init_quota_info(lqi, type);
                 break;
+        case QFILE_CONVERT:
+                rc = lustre_quota_convert(lqi, type);
+                break;
         default:
                 CERROR("Unsupported admin quota file cmd %d\n", cmd);
                 LBUG();
index 96d9833..e910a0c 100644 (file)
 #include <asm/uaccess.h>
 
 #include <lustre_quota.h>
+#include <obd_support.h>
 #include "lustre_quota_fmt.h"
 
-typedef char *dqbuf_t;
+static const uint lustre_initqversions[][MAXQUOTAS] = {
+        [LUSTRE_QUOTA_V1] = LUSTRE_INITQVERSIONS,
+        [LUSTRE_QUOTA_V2] = LUSTRE_INITQVERSIONS_V2
+};
 
-#define GETIDINDEX(id, depth) (((id) >> ((LUSTRE_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
-#define GETENTRIES(buf) ((struct lustre_disk_dqblk *)(((char *)buf)+sizeof(struct lustre_disk_dqdbheader)))
+static const int lustre_dqstrinblk[] = {
+        [LUSTRE_QUOTA_V1] = LUSTRE_DQSTRINBLK,
+        [LUSTRE_QUOTA_V2] = LUSTRE_DQSTRINBLK_V2
+};
 
-static int check_quota_file(struct file *f, struct inode *inode, int type)
+static const int lustre_disk_dqblk_sz[] = {
+        [LUSTRE_QUOTA_V1] = sizeof(struct lustre_disk_dqblk),
+        [LUSTRE_QUOTA_V2] = sizeof(struct lustre_disk_dqblk_v2)
+};
+
+int check_quota_file(struct file *f, struct inode *inode, int type, 
+                     lustre_quota_version_t version)
 {
         struct lustre_disk_dqheader dqhead;
         mm_segment_t fs;
         ssize_t size;
         loff_t offset = 0;
         static const uint quota_magics[] = LUSTRE_INITQMAGICS;
-        static const uint quota_versions[] = LUSTRE_INITQVERSIONS;
+        const uint *quota_versions = lustre_initqversions[version];
 
         if (f) {
                 fs = get_fs();
@@ -57,27 +69,24 @@ static int check_quota_file(struct file *f, struct inode *inode, int type)
 #endif
         }
         if (size != sizeof(struct lustre_disk_dqheader))
-                return 0;
+                return -EINVAL;
         if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
             le32_to_cpu(dqhead.dqh_version) != quota_versions[type])
-                return 0;
-        return 1;
+                return -EINVAL;
+        return 0;
 }
 
 /* Check whether given file is really lustre admin quotafile */
 int lustre_check_quota_file(struct lustre_quota_info *lqi, int type)
 {
         struct file *f = lqi->qi_files[type];
-        return check_quota_file(f, NULL, type);
+        return check_quota_file(f, NULL, type, lqi->qi_version);
 }
 
-/* Read information header from quota file */
-int lustre_read_quota_info(struct lustre_quota_info *lqi, int type)
+int lustre_read_quota_file_info(struct file* f, struct lustre_mem_dqinfo* info)
 {
         mm_segment_t fs;
         struct lustre_disk_dqinfo dinfo;
-        struct lustre_mem_dqinfo *info = &lqi->qi_info[type];
-        struct file *f = lqi->qi_files[type];
         ssize_t size;
         loff_t offset = LUSTRE_DQINFOOFF;
 
@@ -87,9 +96,9 @@ int lustre_read_quota_info(struct lustre_quota_info *lqi, int type)
                              sizeof(struct lustre_disk_dqinfo), &offset);
         set_fs(fs);
         if (size != sizeof(struct lustre_disk_dqinfo)) {
-                printk(KERN_WARNING "Can't read info structure on device %s.\n",
+                CERROR("Can't read info structure on device %s.\n",
                        f->f_vfsmnt->mnt_sb->s_id);
-                return -1;
+                return -EINVAL;
         }
         info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
         info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
@@ -100,6 +109,12 @@ int lustre_read_quota_info(struct lustre_quota_info *lqi, int type)
         return 0;
 }
 
+/* Read information header from quota file */
+int lustre_read_quota_info(struct lustre_quota_info *lqi, int type)
+{
+        return lustre_read_quota_file_info(lqi->qi_files[type], &lqi->qi_info[type]);
+}
+
 /* Write information header to quota file */
 int lustre_write_quota_info(struct lustre_quota_info *lqi, int type)
 {
@@ -131,33 +146,65 @@ int lustre_write_quota_info(struct lustre_quota_info *lqi, int type)
         return 0;
 }
 
-static void disk2memdqb(struct mem_dqblk *m, struct lustre_disk_dqblk *d)
+#define DQ2MQ(v) ((sizeof(v) == sizeof(__u64)) ? \
+                le64_to_cpu(v) : le32_to_cpu(v))
+
+#define MQ2DQ(v,newv) ((sizeof(v) == sizeof(__u64)) ? \
+                (v = cpu_to_le64((__u64)newv)) : (v = cpu_to_le32((__u32)newv)))
+
+#define DQF_GET(var,ver,field) ((ver == LUSTRE_QUOTA_V1)?\
+                DQ2MQ(((struct lustre_disk_dqblk*)(var))->field):\
+                DQ2MQ(((struct lustre_disk_dqblk_v2*)(var))->field))
+
+#define DQF_PUT(var,ver,field,val) ((ver == LUSTRE_QUOTA_V1)?\
+                MQ2DQ(((struct lustre_disk_dqblk*)(var))->field, val):\
+                MQ2DQ(((struct lustre_disk_dqblk_v2*)(var))->field, val))
+
+void disk2memdqb(struct lustre_mem_dqblk *m, void *d,
+                 lustre_quota_version_t version)
 {
-        m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);
-        m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit);
-        m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes);
-        m->dqb_itime = le64_to_cpu(d->dqb_itime);
-        m->dqb_bhardlimit = le32_to_cpu(d->dqb_bhardlimit);
-        m->dqb_bsoftlimit = le32_to_cpu(d->dqb_bsoftlimit);
-        m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
-        m->dqb_btime = le64_to_cpu(d->dqb_btime);
+        m->dqb_ihardlimit = DQF_GET(d, version, dqb_ihardlimit);
+        m->dqb_isoftlimit = DQF_GET(d, version, dqb_isoftlimit);
+        m->dqb_curinodes = DQF_GET(d, version, dqb_curinodes);
+        m->dqb_itime = DQF_GET(d, version, dqb_itime);
+        m->dqb_bhardlimit = DQF_GET(d, version, dqb_bhardlimit);
+        m->dqb_bsoftlimit = DQF_GET(d, version, dqb_bsoftlimit);
+        m->dqb_curspace = DQF_GET(d, version, dqb_curspace);
+        m->dqb_btime = DQF_GET(d, version, dqb_btime);
 }
 
-static void mem2diskdqb(struct lustre_disk_dqblk *d, struct mem_dqblk *m,
-                        qid_t id)
+static int check_quota_bounds(struct lustre_mem_dqblk *m, 
+                              lustre_quota_version_t version)
 {
-        d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
-        d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
-        d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);
-        d->dqb_itime = cpu_to_le64(m->dqb_itime);
-        d->dqb_bhardlimit = cpu_to_le32(m->dqb_bhardlimit);
-        d->dqb_bsoftlimit = cpu_to_le32(m->dqb_bsoftlimit);
-        d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
-        d->dqb_btime = cpu_to_le64(m->dqb_btime);
-        d->dqb_id = cpu_to_le32(id);
+        return (version == LUSTRE_QUOTA_V1  &&
+                m->dqb_ihardlimit <= MAX_UL &&
+                m->dqb_isoftlimit <= MAX_UL &&
+                m->dqb_curinodes <= MAX_UL  &&
+                m->dqb_bhardlimit <= MAX_UL &&
+                m->dqb_bsoftlimit <= MAX_UL) ||
+                version != LUSTRE_QUOTA_V1;
 }
 
-static dqbuf_t getdqbuf(void)
+static int mem2diskdqb(void *d, struct lustre_mem_dqblk *m,
+                       qid_t id, lustre_quota_version_t version)
+{
+        if (!check_quota_bounds(m, version))
+                return -EINVAL;
+
+        DQF_PUT(d, version, dqb_ihardlimit, m->dqb_ihardlimit);
+        DQF_PUT(d, version, dqb_isoftlimit, m->dqb_isoftlimit);
+        DQF_PUT(d, version, dqb_curinodes, m->dqb_curinodes);
+        DQF_PUT(d, version, dqb_itime, m->dqb_itime);
+        DQF_PUT(d, version, dqb_bhardlimit, m->dqb_bhardlimit);
+        DQF_PUT(d, version, dqb_bsoftlimit, m->dqb_bsoftlimit);
+        DQF_PUT(d, version, dqb_curspace, m->dqb_curspace);
+        DQF_PUT(d, version, dqb_btime, m->dqb_btime);
+        DQF_PUT(d, version, dqb_id, id);
+
+        return 0;
+}
+
+dqbuf_t getdqbuf(void)
 {
         dqbuf_t buf = kmalloc(LUSTRE_DQBLKSIZE, GFP_NOFS);
         if (!buf)
@@ -166,12 +213,12 @@ static dqbuf_t getdqbuf(void)
         return buf;
 }
 
-static inline void freedqbuf(dqbuf_t buf)
+void freedqbuf(dqbuf_t buf)
 {
         kfree(buf);
 }
 
-static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf)
+ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf)
 {
         mm_segment_t fs;
         ssize_t ret;
@@ -185,7 +232,7 @@ static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf)
         return ret;
 }
 
-static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf)
+ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf)
 {
         mm_segment_t fs;
         ssize_t ret;
@@ -196,18 +243,15 @@ static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf)
         ret = filp->f_op->write(filp, (char *)buf, LUSTRE_DQBLKSIZE, &offset);
         set_fs(fs);
         return ret;
-
 }
 
-static void lustre_mark_info_dirty(struct lustre_mem_dqinfo *info)
+void lustre_mark_info_dirty(struct lustre_mem_dqinfo *info)
 {
         set_bit(DQF_INFO_DIRTY_B, &info->dqi_flags);
 }
 
-#define lustre_info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags)
-
 /* Remove empty block from list and return it */
-static int get_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info)
+int get_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info)
 {
         dqbuf_t buf = getdqbuf();
         struct lustre_disk_dqdbheader *dh =
@@ -223,7 +267,8 @@ static int get_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info)
                 info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
         } else {
                 memset(buf, 0, LUSTRE_DQBLKSIZE);
-                if ((ret = write_blk(filp, info->dqi_blocks, buf)) < 0) /* Assure block allocation... */
+                /* Assure block allocation... */
+                if ((ret = write_blk(filp, info->dqi_blocks, buf)) < 0)
                         goto out_buf;
                 blk = info->dqi_blocks++;
         }
@@ -235,8 +280,8 @@ out_buf:
 }
 
 /* Insert empty block to the list */
-static int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info,
-                          dqbuf_t buf, uint blk)
+int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info,
+                   dqbuf_t buf, uint blk)
 {
         struct lustre_disk_dqdbheader *dh =
             (struct lustre_disk_dqdbheader *)buf;
@@ -254,9 +299,9 @@ static int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info,
 }
 
 /* Remove given block from the list of blocks with free entries */
-static int remove_free_dqentry(struct file *filp,
-                               struct lustre_mem_dqinfo *info, dqbuf_t buf,
-                               uint blk)
+int remove_free_dqentry(struct file *filp,
+                        struct lustre_mem_dqinfo *info, dqbuf_t buf,
+                        uint blk)
 {
         dqbuf_t tmpbuf = getdqbuf();
         struct lustre_disk_dqdbheader *dh =
@@ -288,9 +333,10 @@ static int remove_free_dqentry(struct file *filp,
         }
         freedqbuf(tmpbuf);
         dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
-        if (write_blk(filp, blk, buf) < 0)      /* No matter whether write succeeds block is out of list */
-                printk(KERN_ERR
-                       "VFS: Can't write block (%u) with free entries.\n", blk);
+        err = write_blk(filp, blk, buf);
+        if (err < 0)      /* No matter whether write succeeds block is out of list */
+                CERROR("VFS: Can't write block (%u) with "
+                       "free entries (rc=%d).\n", blk, err);
         return 0;
 out_buf:
         freedqbuf(tmpbuf);
@@ -298,9 +344,9 @@ out_buf:
 }
 
 /* Insert given block to the beginning of list with free entries */
-static int insert_free_dqentry(struct file *filp,
-                               struct lustre_mem_dqinfo *info, dqbuf_t buf,
-                               uint blk)
+int insert_free_dqentry(struct file *filp,
+                        struct lustre_mem_dqinfo *info, dqbuf_t buf,
+                        uint blk)
 {
         dqbuf_t tmpbuf = getdqbuf();
         struct lustre_disk_dqdbheader *dh =
@@ -330,16 +376,21 @@ out_buf:
         return err;
 }
 
+
+
 /* Find space for dquot */
-static uint find_free_dqentry(struct lustre_dquot *dquot, int *err)
+static uint find_free_dqentry(struct lustre_dquot *dquot, int *err, 
+                              lustre_quota_version_t version)
 {
         struct lustre_quota_info *lqi = dquot->dq_info;
         struct file *filp = lqi->qi_files[dquot->dq_type];
         struct lustre_mem_dqinfo *info = &lqi->qi_info[dquot->dq_type];
         uint blk, i;
         struct lustre_disk_dqdbheader *dh;
-        struct lustre_disk_dqblk *ddquot;
-        struct lustre_disk_dqblk fakedquot;
+        void *ddquot;
+        int dqblk_sz = lustre_disk_dqblk_sz[version];
+        int dqstrinblk = lustre_dqstrinblk[version];
+        char fakedquot[dqblk_sz];
         dqbuf_t buf;
 
         *err = 0;
@@ -348,7 +399,7 @@ static uint find_free_dqentry(struct lustre_dquot *dquot, int *err)
                 return 0;
         }
         dh = (struct lustre_disk_dqdbheader *)buf;
-        ddquot = GETENTRIES(buf);
+        ddquot = GETENTRIES(buf, version);
         if (info->dqi_free_entry) {
                 blk = info->dqi_free_entry;
                 if ((*err = read_blk(filp, blk, buf)) < 0)
@@ -361,39 +412,40 @@ static uint find_free_dqentry(struct lustre_dquot *dquot, int *err)
                         return 0;
                 }
                 memset(buf, 0, LUSTRE_DQBLKSIZE);
-                info->dqi_free_entry = blk;     /* This is enough as block is already zeroed and entry list is empty... */
+                info->dqi_free_entry = blk; /* This is enough as block is 
+                                               already zeroed and entry list
+                                               is empty... */
                 lustre_mark_info_dirty(info);
         }
-        if (le16_to_cpu(dh->dqdh_entries) + 1 >= LUSTRE_DQSTRINBLK)     /* Block will be full? */
+
+        /* Will block be full */
+        if (le16_to_cpu(dh->dqdh_entries) + 1 >= dqstrinblk)
                 if ((*err = remove_free_dqentry(filp, info, buf, blk)) < 0) {
-                        printk(KERN_ERR
-                               "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n",
-                               blk);
+                        CERROR("VFS: Can't remove block %u"
+                               " from entry free list.\n", blk);
                         goto out_buf;
                 }
         dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries) + 1);
-        memset(&fakedquot, 0, sizeof(struct lustre_disk_dqblk));
+        memset(fakedquot, 0, dqblk_sz);
         /* Find free structure in block */
-        for (i = 0; i < LUSTRE_DQSTRINBLK && 
-             memcmp(&fakedquot, ddquot + i, sizeof(fakedquot)); i++) ;
+        for (i = 0; i < dqstrinblk &&
+             memcmp(fakedquot, (char*)ddquot + i * dqblk_sz, 
+                    sizeof(fakedquot)); i++);
 
-        if (i == LUSTRE_DQSTRINBLK) {
-                printk(KERN_ERR
-                       "VFS: find_free_dqentry(): Data block full but it shouldn't.\n");
+        if (i == dqstrinblk) {
+                CERROR("VFS: Data block full but it shouldn't.\n");
                 *err = -EIO;
                 goto out_buf;
         }
 
         if ((*err = write_blk(filp, blk, buf)) < 0) {
-                printk(KERN_ERR
-                       "VFS: find_free_dqentry(): Can't write quota data block %u.\n",
-                       blk);
+                CERROR("VFS: Can't write quota data block %u.\n", blk);
                 goto out_buf;
         }
         dquot->dq_off =
             (blk << LUSTRE_DQBLKSIZE_BITS) +
             sizeof(struct lustre_disk_dqdbheader) +
-            i * sizeof(struct lustre_disk_dqblk);
+            i * dqblk_sz;
         freedqbuf(buf);
         return blk;
 out_buf:
@@ -402,7 +454,8 @@ out_buf:
 }
 
 /* Insert reference to structure into the trie */
-static int do_insert_tree(struct lustre_dquot *dquot, uint * treeblk, int depth)
+static int do_insert_tree(struct lustre_dquot *dquot, uint * treeblk, int depth, 
+                          lustre_quota_version_t version)
 {
         struct lustre_quota_info *lqi = dquot->dq_info;
         struct file *filp = lqi->qi_files[dquot->dq_type];
@@ -423,8 +476,7 @@ static int do_insert_tree(struct lustre_dquot *dquot, uint * treeblk, int depth)
                 newact = 1;
         } else {
                 if ((ret = read_blk(filp, *treeblk, buf)) < 0) {
-                        printk(KERN_ERR
-                               "VFS: Can't read tree quota block %u.\n",
+                        CERROR("VFS: Can't read tree quota block %u.\n",
                                *treeblk);
                         goto out_buf;
                 }
@@ -436,16 +488,16 @@ static int do_insert_tree(struct lustre_dquot *dquot, uint * treeblk, int depth)
         if (depth == LUSTRE_DQTREEDEPTH - 1) {
 
                 if (newblk) {
-                        printk(KERN_ERR
-                               "VFS: Inserting already present quota entry (block %u).\n",
+                        CERROR("VFS: Inserting already present quota entry "
+                               "(block %u).\n", 
                                ref[GETIDINDEX(dquot->dq_id, depth)]);
                         ret = -EIO;
                         goto out_buf;
                 }
 
-                newblk = find_free_dqentry(dquot, &ret);
+                newblk = find_free_dqentry(dquot, &ret, version);
         } else
-                ret = do_insert_tree(dquot, &newblk, depth + 1);
+                ret = do_insert_tree(dquot, &newblk, depth + 1, version);
         if (newson && ret >= 0) {
                 ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk);
                 ret = write_blk(filp, *treeblk, buf);
@@ -457,46 +509,51 @@ out_buf:
 }
 
 /* Wrapper for inserting quota structure into tree */
-static inline int dq_insert_tree(struct lustre_dquot *dquot)
+static inline int dq_insert_tree(struct lustre_dquot *dquot, 
+                                 lustre_quota_version_t version)
 {
         int tmp = LUSTRE_DQTREEOFF;
-        return do_insert_tree(dquot, &tmp, 0);
+        return do_insert_tree(dquot, &tmp, 0, version);
 }
 
 /*
- *     We don't have to be afraid of deadlocks as we never have quotas on quota files...
+ *  We don't have to be afraid of deadlocks as we never have quotas on quota files...
  */
-static int lustre_write_dquot(struct lustre_dquot *dquot)
+static int lustre_write_dquot(struct lustre_dquot *dquot, 
+                              lustre_quota_version_t version)
 {
         int type = dquot->dq_type;
         struct file *filp;
         mm_segment_t fs;
         loff_t offset;
         ssize_t ret;
-        struct lustre_disk_dqblk ddquot, empty;
+        int dqblk_sz = lustre_disk_dqblk_sz[version];
+        char ddquot[dqblk_sz], empty[dqblk_sz];
+
+        ret = mem2diskdqb(ddquot, &dquot->dq_dqb, dquot->dq_id, version);
+        if (ret < 0)
+                return ret;
 
         if (!dquot->dq_off)
-                if ((ret = dq_insert_tree(dquot)) < 0) {
-                        printk(KERN_ERR
-                               "VFS: Error %Zd occurred while creating quota.\n",
+                if ((ret = dq_insert_tree(dquot, version)) < 0) {
+                        CERROR("VFS: Error %Zd occurred while creating quota.\n",
                                ret);
                         return ret;
                 }
         filp = dquot->dq_info->qi_files[type];
         offset = dquot->dq_off;
-        mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
         /* Argh... We may need to write structure full of zeroes but that would be
          * treated as an empty place by the rest of the code. Format change would
          * be definitely cleaner but the problems probably are not worth it */
-        memset(&empty, 0, sizeof(struct lustre_disk_dqblk));
-        if (!memcmp(&empty, &ddquot, sizeof(struct lustre_disk_dqblk)))
-                ddquot.dqb_itime = cpu_to_le64(1);
+        memset(empty, 0, dqblk_sz);
+        if (!memcmp(empty, ddquot, dqblk_sz))
+                DQF_PUT(ddquot, version, dqb_itime, 1);
         fs = get_fs();
         set_fs(KERNEL_DS);
-        ret = filp->f_op->write(filp, (char *)&ddquot,
-                                sizeof(struct lustre_disk_dqblk), &offset);
+        ret = filp->f_op->write(filp, ddquot,
+                                dqblk_sz, &offset);
         set_fs(fs);
-        if (ret != sizeof(struct lustre_disk_dqblk)) {
+        if (ret != dqblk_sz) {
                 printk(KERN_WARNING "VFS: dquota write failed on dev %s\n",
                        filp->f_dentry->d_sb->s_id);
                 if (ret >= 0)
@@ -508,25 +565,27 @@ static int lustre_write_dquot(struct lustre_dquot *dquot)
 }
 
 /* Free dquot entry in data block */
-static int free_dqentry(struct lustre_dquot *dquot, uint blk)
+static int free_dqentry(struct lustre_dquot *dquot, uint blk, 
+                        lustre_quota_version_t version)
 {
         struct file *filp = dquot->dq_info->qi_files[dquot->dq_type];
         struct lustre_mem_dqinfo *info =
             &dquot->dq_info->qi_info[dquot->dq_type];
         struct lustre_disk_dqdbheader *dh;
         dqbuf_t buf = getdqbuf();
+        int dqstrinblk = lustre_dqstrinblk[version];
         int ret = 0;
 
         if (!buf)
                 return -ENOMEM;
         if (dquot->dq_off >> LUSTRE_DQBLKSIZE_BITS != blk) {
-                printk(KERN_ERR
-                       "VFS: Quota structure has offset to other block (%u) than it should (%u).\n",
-                       blk, (uint) (dquot->dq_off >> LUSTRE_DQBLKSIZE_BITS));
+                CERROR("VFS: Quota structure has offset to other block (%u) "
+                       "than it should (%u).\n", blk, 
+                       (uint)(dquot->dq_off >> LUSTRE_DQBLKSIZE_BITS));
                 goto out_buf;
         }
         if ((ret = read_blk(filp, blk, buf)) < 0) {
-                printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
+                CERROR("VFS: Can't read quota data block %u\n", blk);
                 goto out_buf;
         }
         dh = (struct lustre_disk_dqdbheader *)buf;
@@ -534,27 +593,23 @@ static int free_dqentry(struct lustre_dquot *dquot, uint blk)
         if (!le16_to_cpu(dh->dqdh_entries)) {   /* Block got free? */
                 if ((ret = remove_free_dqentry(filp, info, buf, blk)) < 0 ||
                     (ret = put_free_dqblk(filp, info, buf, blk)) < 0) {
-                        printk(KERN_ERR
-                               "VFS: Can't move quota data block (%u) to free list.\n",
-                               blk);
+                        CERROR("VFS: Can't move quota data block (%u) "
+                               "to free list.\n", blk);
                         goto out_buf;
                 }
         } else {
-                memset(buf +
-                       (dquot->dq_off & ((1 << LUSTRE_DQBLKSIZE_BITS) - 1)), 0,
-                       sizeof(struct lustre_disk_dqblk));
-                if (le16_to_cpu(dh->dqdh_entries) == LUSTRE_DQSTRINBLK - 1) {
+                memset(buf + (dquot->dq_off & ((1<<LUSTRE_DQBLKSIZE_BITS) - 1)),
+                       0, lustre_disk_dqblk_sz[version]);
+                if (le16_to_cpu(dh->dqdh_entries) == dqstrinblk - 1) {
                         /* Insert will write block itself */
                         if ((ret =
                              insert_free_dqentry(filp, info, buf, blk)) < 0) {
-                                printk(KERN_ERR
-                                       "VFS: Can't insert quota data block (%u) to free entry list.\n",
-                                       blk);
+                                CERROR("VFS: Can't insert quota data block (%u) "
+                                       "to free entry list.\n", blk);
                                 goto out_buf;
                         }
                 } else if ((ret = write_blk(filp, blk, buf)) < 0) {
-                        printk(KERN_ERR
-                               "VFS: Can't write quota data block %u\n", blk);
+                        CERROR("VFS: Can't write quota data block %u\n", blk);
                         goto out_buf;
                 }
         }
@@ -565,7 +620,8 @@ out_buf:
 }
 
 /* Remove reference to dquot from tree */
-static int remove_tree(struct lustre_dquot *dquot, uint * blk, int depth)
+static int remove_tree(struct lustre_dquot *dquot, uint * blk, int depth, 
+                       lustre_quota_version_t version)
 {
         struct file *filp = dquot->dq_info->qi_files[dquot->dq_type];
         struct lustre_mem_dqinfo *info =
@@ -578,26 +634,26 @@ static int remove_tree(struct lustre_dquot *dquot, uint * blk, int depth)
         if (!buf)
                 return -ENOMEM;
         if ((ret = read_blk(filp, *blk, buf)) < 0) {
-                printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
+                CERROR("VFS: Can't read quota data block %u\n", *blk);
                 goto out_buf;
         }
         newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
         if (depth == LUSTRE_DQTREEDEPTH - 1) {
-                ret = free_dqentry(dquot, newblk);
+                ret = free_dqentry(dquot, newblk, version);
                 newblk = 0;
         } else
-                ret = remove_tree(dquot, &newblk, depth + 1);
+                ret = remove_tree(dquot, &newblk, depth + 1, version);
         if (ret >= 0 && !newblk) {
                 int i;
                 ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0);
-                for (i = 0; i < LUSTRE_DQBLKSIZE && !buf[i]; i++) ;     /* Block got empty? */
+                for (i = 0; i < LUSTRE_DQBLKSIZE && !buf[i]; i++)
+                        /* Block got empty? */ ;
                 /* don't put the root block into free blk list! */
                 if (i == LUSTRE_DQBLKSIZE && *blk != LUSTRE_DQTREEOFF) {
                         put_free_dqblk(filp, info, buf, *blk);
                         *blk = 0;
                 } else if ((ret = write_blk(filp, *blk, buf)) < 0)
-                        printk(KERN_ERR
-                               "VFS: Can't write quota tree block %u.\n", *blk);
+                        CERROR("VFS: Can't write quota tree block %u.\n", *blk);
         }
 out_buf:
         freedqbuf(buf);
@@ -605,47 +661,50 @@ out_buf:
 }
 
 /* Delete dquot from tree */
-static int lustre_delete_dquot(struct lustre_dquot *dquot)
+static int lustre_delete_dquot(struct lustre_dquot *dquot, 
+                                lustre_quota_version_t version)
 {
         uint tmp = LUSTRE_DQTREEOFF;
 
         if (!dquot->dq_off)     /* Even not allocated? */
                 return 0;
-        return remove_tree(dquot, &tmp, 0);
+        return remove_tree(dquot, &tmp, 0, version);
 }
 
 /* Find entry in block */
-static loff_t find_block_dqentry(struct lustre_dquot *dquot, uint blk)
+static loff_t find_block_dqentry(struct lustre_dquot *dquot, uint blk, 
+                                 lustre_quota_version_t version)
 {
         struct file *filp = dquot->dq_info->qi_files[dquot->dq_type];
         dqbuf_t buf = getdqbuf();
         loff_t ret = 0;
         int i;
-        struct lustre_disk_dqblk *ddquot = GETENTRIES(buf);
+        char *ddquot = GETENTRIES(buf, version);
+        int dqblk_sz = lustre_disk_dqblk_sz[version];
+        int dqstrinblk = lustre_dqstrinblk[version];
 
         if (!buf)
                 return -ENOMEM;
         if ((ret = read_blk(filp, blk, buf)) < 0) {
-                printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+                CERROR("VFS: Can't read quota tree block %u.\n", blk);
                 goto out_buf;
         }
         if (dquot->dq_id)
-                for (i = 0;
-                     i < LUSTRE_DQSTRINBLK
-                     && le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++) ;
+                for (i = 0; i < dqstrinblk && 
+                     DQF_GET(ddquot+i*dqblk_sz, version, dqb_id) != dquot->dq_id;
+                     i++) ;
         else {                  /* ID 0 as a bit more complicated searching... */
-                struct lustre_disk_dqblk fakedquot;
+                char fakedquot[dqblk_sz];
 
-                memset(&fakedquot, 0, sizeof(struct lustre_disk_dqblk));
-                for (i = 0; i < LUSTRE_DQSTRINBLK; i++)
-                        if (!le32_to_cpu(ddquot[i].dqb_id)
-                            && memcmp(&fakedquot, ddquot + i,
-                                      sizeof(struct lustre_disk_dqblk)))
+                memset(fakedquot, 0, sizeof(fakedquot));
+                for (i = 0; i < dqstrinblk; i++)
+                        if (!DQF_GET(ddquot + i*dqblk_sz, version, dqb_id)
+                            && memcmp(fakedquot, ddquot + i*dqblk_sz,
+                                      dqblk_sz))
                                 break;
         }
-        if (i == LUSTRE_DQSTRINBLK) {
-                printk(KERN_ERR
-                       "VFS: Quota for id %u referenced but not present.\n",
+        if (i == dqstrinblk) {
+                CERROR("VFS: Quota for id %u referenced but not present.\n",
                        dquot->dq_id);
                 ret = -EIO;
                 goto out_buf;
@@ -653,14 +712,15 @@ static loff_t find_block_dqentry(struct lustre_dquot *dquot, uint blk)
                 ret =
                     (blk << LUSTRE_DQBLKSIZE_BITS) +
                     sizeof(struct lustre_disk_dqdbheader) +
-                    i * sizeof(struct lustre_disk_dqblk);
+                    i * dqblk_sz;
 out_buf:
         freedqbuf(buf);
         return ret;
 }
 
 /* Find entry for given id in the tree */
-static loff_t find_tree_dqentry(struct lustre_dquot *dquot, uint blk, int depth)
+static loff_t find_tree_dqentry(struct lustre_dquot *dquot, uint blk, int depth, 
+                                lustre_quota_version_t version)
 {
         struct file *filp = dquot->dq_info->qi_files[dquot->dq_type];
         dqbuf_t buf = getdqbuf();
@@ -670,7 +730,7 @@ static loff_t find_tree_dqentry(struct lustre_dquot *dquot, uint blk, int depth)
         if (!buf)
                 return -ENOMEM;
         if ((ret = read_blk(filp, blk, buf)) < 0) {
-                printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+                CERROR("VFS: Can't read quota tree block %u.\n", blk);
                 goto out_buf;
         }
         ret = 0;
@@ -678,18 +738,19 @@ static loff_t find_tree_dqentry(struct lustre_dquot *dquot, uint blk, int depth)
         if (!blk)               /* No reference? */
                 goto out_buf;
         if (depth < LUSTRE_DQTREEDEPTH - 1)
-                ret = find_tree_dqentry(dquot, blk, depth + 1);
+                ret = find_tree_dqentry(dquot, blk, depth + 1, version);
         else
-                ret = find_block_dqentry(dquot, blk);
+                ret = find_block_dqentry(dquot, blk, version);
 out_buf:
         freedqbuf(buf);
         return ret;
 }
 
 /* Find entry for given id in the tree - wrapper function */
-static inline loff_t find_dqentry(struct lustre_dquot *dquot)
+static inline loff_t find_dqentry(struct lustre_dquot *dquot, 
+                                  lustre_quota_version_t version)
 {
-        return find_tree_dqentry(dquot, LUSTRE_DQTREEOFF, 0);
+        return find_tree_dqentry(dquot, LUSTRE_DQTREEOFF, 0, version);
 }
 
 int lustre_read_dquot(struct lustre_dquot *dquot)
@@ -698,50 +759,50 @@ int lustre_read_dquot(struct lustre_dquot *dquot)
         struct file *filp;
         mm_segment_t fs;
         loff_t offset;
-        struct lustre_disk_dqblk ddquot, empty;
-        int ret = 0;
+        int ret = 0, dqblk_sz;
+        lustre_quota_version_t version;
 
         /* Invalidated quota? */
         if (!dquot->dq_info || !(filp = dquot->dq_info->qi_files[type])) {
-                printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
+                CERROR("VFS: Quota invalidated while reading!\n");
                 return -EIO;
         }
 
-        offset = find_dqentry(dquot);
+        version = dquot->dq_info->qi_version;
+        dqblk_sz = lustre_disk_dqblk_sz[version];
+
+        offset = find_dqentry(dquot, version);
         if (offset <= 0) {      /* Entry not present? */
                 if (offset < 0)
-                        printk(KERN_ERR
-                               "VFS: Can't read quota structure for id %u.\n",
+                        CERROR("VFS: Can't read quota structure for id %u.\n",
                                dquot->dq_id);
                 dquot->dq_off = 0;
                 set_bit(DQ_FAKE_B, &dquot->dq_flags);
-                memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
+                memset(&dquot->dq_dqb, 0, sizeof(struct lustre_mem_dqblk));
                 ret = offset;
         } else {
+                char ddquot[dqblk_sz], empty[dqblk_sz];
+
                 dquot->dq_off = offset;
                 fs = get_fs();
                 set_fs(KERNEL_DS);
-                if ((ret = filp->f_op->read(filp, (char *)&ddquot,
-                                            sizeof(struct lustre_disk_dqblk),
-                                            &offset)) !=
-                    sizeof(struct lustre_disk_dqblk)) {
+                if ((ret = filp->f_op->read(filp, ddquot, dqblk_sz, &offset)) !=
+                    dqblk_sz) {
                         if (ret >= 0)
                                 ret = -EIO;
-                        printk(KERN_ERR
-                               "VFS: Error while reading quota structure for id %u.\n",
-                               dquot->dq_id);
-                        memset(&ddquot, 0, sizeof(struct lustre_disk_dqblk));
+                        CERROR("VFS: Error while reading quota structure "
+                               "for id %u.\n", dquot->dq_id);
+                        memset(ddquot, 0, dqblk_sz);
                 } else {
                         ret = 0;
                         /* We need to escape back all-zero structure */
-                        memset(&empty, 0, sizeof(struct lustre_disk_dqblk));
-                        empty.dqb_itime = cpu_to_le64(1);
-                        if (!memcmp(&empty, &ddquot,
-                                    sizeof(struct lustre_disk_dqblk)))
-                                ddquot.dqb_itime = 0;
+                        memset(empty, 0, dqblk_sz);
+                        DQF_PUT(empty, version, dqb_itime, 1);
+                        if (!memcmp(empty, ddquot, dqblk_sz))
+                                DQF_PUT(ddquot, version, dqb_itime, 0);
                 }
                 set_fs(fs);
-                disk2memdqb(&dquot->dq_dqb, &ddquot);
+                disk2memdqb(&dquot->dq_dqb, ddquot, version);
         }
 
         return ret;
@@ -751,6 +812,8 @@ int lustre_read_dquot(struct lustre_dquot *dquot)
 int lustre_commit_dquot(struct lustre_dquot *dquot)
 {
         int rc = 0;
+        lustre_quota_version_t version = dquot->dq_info->qi_version;
+
         /* always clear the flag so we don't loop on an IO error... */
         clear_bit(DQ_MOD_B, &dquot->dq_flags);
 
@@ -758,9 +821,9 @@ int lustre_commit_dquot(struct lustre_dquot *dquot)
          * over all cluster, so keep the fake dquot entry on disk is
          * meaningless, just remove it */
         if (test_bit(DQ_FAKE_B, &dquot->dq_flags))
-                rc = lustre_delete_dquot(dquot);
+                rc = lustre_delete_dquot(dquot, version);
         else
-                rc = lustre_write_dquot(dquot);
+                rc = lustre_write_dquot(dquot, version);
 
         if (rc < 0)
                 return rc;
@@ -771,29 +834,41 @@ int lustre_commit_dquot(struct lustre_dquot *dquot)
         return rc;
 }
 
-/* We need to export this function to initialize quotafile, because we haven't
- * user level check utility */
-int lustre_init_quota_info(struct lustre_quota_info *lqi, int type)
+int lustre_init_quota_header(struct lustre_quota_info *lqi, int type, int fakemagics)
 {
-        struct lustre_mem_dqinfo *dqinfo = &lqi->qi_info[type];
+        static const uint quota_magics[] = LUSTRE_INITQMAGICS;
+        static const uint fake_magics[] = LUSTRE_BADQMAGICS;
+        const uint* quota_versions = lustre_initqversions[lqi->qi_version];
         struct lustre_disk_dqheader dqhead;
-        struct file *fp = lqi->qi_files[type];
         ssize_t size;
         loff_t offset = 0;
+        struct file *fp = lqi->qi_files[type];
         int rc = 0;
-        static const uint quota_magics[] = LUSTRE_INITQMAGICS;
-        static const uint quota_versions[] = LUSTRE_INITQVERSIONS;
 
         /* write quotafile header */
-        dqhead.dqh_magic = cpu_to_le32(quota_magics[type]);
+        dqhead.dqh_magic = cpu_to_le32(fakemagics ? 
+                                       fake_magics[type] : quota_magics[type]);
         dqhead.dqh_version = cpu_to_le32(quota_versions[type]);
         size = fp->f_op->write(fp, (char *)&dqhead,
                                sizeof(struct lustre_disk_dqheader), &offset);
 
         if (size != sizeof(struct lustre_disk_dqheader)) {
-                printk(KERN_ERR "error writing quoafile header (rc:%d)\n", rc);
+                CERROR("error writing quotafile header (rc:%d)\n", rc);
                 rc = size;
         }
+
+        return rc;
+}
+
+/* We need to export this function to initialize quotafile, because we haven't
+ * user level check utility */
+int lustre_init_quota_info_generic(struct lustre_quota_info *lqi, int type,
+                                   int fakemagics)
+{
+        struct lustre_mem_dqinfo *dqinfo = &lqi->qi_info[type];
+        int rc;
+
+        rc = lustre_init_quota_header(lqi, type, fakemagics);
         if (rc)
                 return rc;
 
@@ -806,13 +881,13 @@ int lustre_init_quota_info(struct lustre_quota_info *lqi, int type)
         return lustre_write_quota_info(lqi, type);
 }
 
-struct dqblk {
-        struct list_head link;
-        uint blk;
-};
+int lustre_init_quota_info(struct lustre_quota_info *lqi, int type)
+{
+        return lustre_init_quota_info_generic(lqi, type, 0);
+}
 
-static ssize_t quota_read(struct file *file, struct inode *inode, int type,
-                          uint blk, dqbuf_t buf)
+ssize_t quota_read(struct file *file, struct inode *inode, int type,
+                   uint blk, dqbuf_t buf)
 {
         if (file) {
                 return read_blk(file, blk, buf);
@@ -843,7 +918,7 @@ static int walk_block_dqentry(struct file *filp, struct inode *inode, int type,
         if (!buf)
                 return -ENOMEM;
         if ((ret = quota_read(filp, inode, type, blk, buf)) < 0) {
-                printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+                CERROR("VFS: Can't read quota tree block %u.\n", blk);
                 goto out_buf;
         }
         ret = 0;
@@ -880,8 +955,8 @@ out_buf:
         return ret;
 }
 
-static int walk_tree_dqentry(struct file *filp, struct inode *inode, int type, 
-                             uint blk, int depth, struct list_head *list)
+int walk_tree_dqentry(struct file *filp, struct inode *inode, int type, 
+                      uint blk, int depth, struct list_head *list)
 {
         dqbuf_t buf = getdqbuf();
         loff_t ret = 0;
@@ -891,7 +966,7 @@ static int walk_tree_dqentry(struct file *filp, struct inode *inode, int type,
         if (!buf)
                 return -ENOMEM;
         if ((ret = quota_read(filp, inode, type, blk, buf)) < 0) {
-                printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+                CERROR("VFS: Can't read quota tree block %u.\n", blk);
                 goto out_buf;
         }
         ret = 0;
@@ -902,7 +977,7 @@ static int walk_tree_dqentry(struct file *filp, struct inode *inode, int type,
                         continue;
 
                 if (depth < LUSTRE_DQTREEDEPTH - 1)
-                        ret = walk_tree_dqentry(filp, inode, type, blk, 
+                        ret = walk_tree_dqentry(filp, inode, type, blk,
                                                 depth + 1, list);
                 else
                         ret = walk_block_dqentry(filp, inode, type, blk, list);
@@ -919,61 +994,65 @@ int lustre_get_qids(struct file *fp, struct inode *inode, int type,
         struct list_head blk_list;
         struct dqblk *blk_item, *tmp;
         dqbuf_t buf = NULL;
-        struct lustre_disk_dqblk *ddquot;
+        char *ddquot;
         int rc;
+        lustre_quota_version_t version;
 
-        if (!check_quota_file(fp, inode, type)) {
-                printk(KERN_ERR "unknown quota file format!\n");
-                return -EINVAL;
+        ENTRY;
+
+        if (check_quota_file(fp, inode, type, LUSTRE_QUOTA_V1) == 0)
+                version = LUSTRE_QUOTA_V1;
+        else if (check_quota_file(fp, inode, type, LUSTRE_QUOTA_V2) == 0)
+                version = LUSTRE_QUOTA_V2;
+        else {
+                CERROR("unknown quota file format!\n");
+                RETURN(-EINVAL);
         }
+
         if (!list_empty(list)) {
-                printk(KERN_ERR "not empty list\n");
-                return -EINVAL;
+                CERROR("not empty list\n");
+                RETURN(-EINVAL);
         }
 
         INIT_LIST_HEAD(&blk_list);
         rc = walk_tree_dqentry(fp, inode, type, LUSTRE_DQTREEOFF, 0, &blk_list);
         if (rc) {
-                printk(KERN_ERR "walk through quota file failed!(%d)\n", rc);
-                goto out_free;
+                CERROR("walk through quota file failed!(%d)\n", rc);
+                GOTO(out_free, rc);
         }
         if (list_empty(&blk_list))
-                return 0;
+                RETURN(0);
 
         buf = getdqbuf();
         if (!buf)
-                return -ENOMEM;
-        ddquot = GETENTRIES(buf);
+                RETURN(-ENOMEM);
+        ddquot = GETENTRIES(buf, version);
 
         list_for_each_entry(blk_item, &blk_list, link) {
                 loff_t ret = 0;
-                int i;
-                struct lustre_disk_dqblk fakedquot;
+                int i, dqblk_sz = lustre_disk_dqblk_sz[version];
+                char fakedquot[dqblk_sz];
 
                 memset(buf, 0, LUSTRE_DQBLKSIZE);
                 if ((ret = quota_read(fp, inode, type, blk_item->blk, buf))<0) {
-                        printk(KERN_ERR
-                               "VFS: Can't read quota tree block %u.\n",
+                        CERROR("VFS: Can't read quota tree block %u.\n",
                                blk_item->blk);
-                        rc = ret;
-                        goto out_free;
+                        GOTO(out_free, rc = ret);
                 }
 
-                memset(&fakedquot, 0, sizeof(struct lustre_disk_dqblk));
-                for (i = 0; i < LUSTRE_DQSTRINBLK; i++) {
+                memset(fakedquot, 0, dqblk_sz);
+                for (i = 0; i < lustre_dqstrinblk[version]; i++) {
                         struct dquot_id *dqid;
                         /* skip empty entry */
-                        if (!memcmp
-                            (&fakedquot, ddquot + i,
-                             sizeof(struct lustre_disk_dqblk)))
+                        if (!memcmp(fakedquot, ddquot + i*dqblk_sz, dqblk_sz))
                                 continue;
 
                         dqid = kmalloc(sizeof(*dqid), GFP_NOFS);
-                        if (!dqid) {
-                                rc = -ENOMEM;
-                                goto out_free;
-                        }
-                        dqid->di_id = le32_to_cpu(ddquot[i].dqb_id);
+                        if (!dqid) 
+                                GOTO(out_free, rc = -ENOMEM);
+
+                        dqid->di_id = DQF_GET(ddquot + i * dqblk_sz, 
+                                              version, dqb_id);
                         INIT_LIST_HEAD(&dqid->di_link);
                         list_add(&dqid->di_link, list);
                 }
@@ -986,12 +1065,14 @@ out_free:
         }
         if (buf)
                 freedqbuf(buf);
-        return rc;
+
+        RETURN(rc);
 }
 
-EXPORT_SYMBOL(lustre_check_quota_file);
+
 EXPORT_SYMBOL(lustre_read_quota_info);
 EXPORT_SYMBOL(lustre_write_quota_info);
+EXPORT_SYMBOL(lustre_check_quota_file);
 EXPORT_SYMBOL(lustre_read_dquot);
 EXPORT_SYMBOL(lustre_commit_dquot);
 EXPORT_SYMBOL(lustre_init_quota_info);
index 28f5c15..78a22f7 100644 (file)
  * Same with quota v2's magic
  */
 #define LUSTRE_INITQMAGICS {\
-       0xd9c01f11,     /* USRQUOTA */\
-       0xd9c01927      /* GRPQUOTA */\
+        0xd9c01f11,     /* USRQUOTA */\
+        0xd9c01927      /* GRPQUOTA */\
 }
 
+/* Invalid magics that mark quota file as inconsistent */
+#define LUSTRE_BADQMAGICS {\
+        0xbadbadba,     /* USRQUOTA */\
+        0xbadbadba      /* GRPQUOTA */\
+}
+
+/* for the verson 1 of lustre_disk_dqblk*/
 #define LUSTRE_INITQVERSIONS {\
-       0,              /* USRQUOTA */\
-       0               /* GRPQUOTA */\
+        0,              /* USRQUOTA */\
+        0               /* GRPQUOTA */\
 }
 
 /*
  * The following structure defines the format of the disk quota file
  * (as it appears on disk) - the file is a radix tree whose leaves point
- * to blocks of these structures.
+ * to blocks of these structures. for the version 1.
  */
 struct lustre_disk_dqblk {
         __u32 dqb_id;           /* id this quota applies to */
@@ -43,6 +50,45 @@ struct lustre_disk_dqblk {
         __u64 dqb_itime;        /* time limit for excessive inode use */
 };
 
+/* Number of entries in one blocks(21 entries) */
+#define LUSTRE_DQSTRINBLK \
+                ((LUSTRE_DQBLKSIZE - sizeof(struct lustre_disk_dqdbheader)) \
+                / sizeof(struct lustre_disk_dqblk)) 
+#define GETENTRIES_V1(buf) (((char *)buf)+sizeof(struct lustre_disk_dqdbheader))
+
+/* for the verson 2 of lustre_disk_dqblk*/
+#define LUSTRE_INITQVERSIONS_V2 {\
+        1,             /* USRQUOTA */\
+        1              /* GRPQUOTA */\
+}
+
+/*
+ * The following structure defines the format of the disk quota file
+ * (as it appears on disk) - the file is a radix tree whose leaves point
+ * to blocks of these structures. for the version 2.
+ */
+struct lustre_disk_dqblk_v2 {
+        __u32 dqb_id;           /* id this quota applies to */
+        __u32 padding;
+        __u64 dqb_ihardlimit;   /* absolute limit on allocated inodes */
+        __u64 dqb_isoftlimit;   /* preferred inode limit */
+        __u64 dqb_curinodes;    /* current # allocated inodes */
+        __u64 dqb_bhardlimit;   /* absolute limit on disk space (in QUOTABLOCK_SIZE) */
+        __u64 dqb_bsoftlimit;   /* preferred limit on disk space (in QUOTABLOCK_SIZE) */
+        __u64 dqb_curspace;     /* current space occupied (in bytes) */
+        __u64 dqb_btime;        /* time limit for excessive disk use */
+        __u64 dqb_itime;        /* time limit for excessive inode use */
+};
+
+/* Number of entries in one blocks(14 entries) */
+#define LUSTRE_DQSTRINBLK_V2 \
+                ((LUSTRE_DQBLKSIZE - sizeof(struct lustre_disk_dqdbheader)) \
+               / sizeof(struct lustre_disk_dqblk_v2)) 
+#define GETENTRIES_V2(buf) (((char *)buf)+sizeof(struct lustre_disk_dqdbheader))
+
+#define GETENTRIES(buf,version) ((version == LUSTRE_QUOTA_V1) ? \
+                                GETENTRIES_V1(buf) : GETENTRIES_V2(buf))
+
 /*
  * Here are header structures as written on disk and their in-memory copies
  */
@@ -79,6 +125,59 @@ struct lustre_disk_dqdbheader {
 #define LUSTRE_DQBLKSIZE       (1 << LUSTRE_DQBLKSIZE_BITS)    /* Size of block with quota structures */
 #define LUSTRE_DQTREEOFF       1       /* Offset of tree in file in blocks */
 #define LUSTRE_DQTREEDEPTH     4       /* Depth of quota tree */
-#define LUSTRE_DQSTRINBLK      ((LUSTRE_DQBLKSIZE - sizeof(struct lustre_disk_dqdbheader)) / sizeof(struct lustre_disk_dqblk)) /* Number of entries in one blocks */
+
+typedef char *dqbuf_t;
+
+#define GETIDINDEX(id, depth) (((id) >> ((LUSTRE_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
+
+#define MAX_UL (0xffffffffUL)
+
+#define lustre_info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags)
+
+struct dqblk {
+        struct list_head link;
+        uint blk;
+};
+
+/* come from lustre_fmt_common.c */
+dqbuf_t getdqbuf(void);
+void freedqbuf(dqbuf_t buf);
+void disk2memdqb(struct lustre_mem_dqblk *m, void *d,
+                        enum lustre_quota_version version);
+void lustre_mark_info_dirty(struct lustre_mem_dqinfo *info);
+int lustre_init_quota_header(struct lustre_quota_info *lqi, int type, 
+                             int fakemagics);
+int lustre_init_quota_info_generic(struct lustre_quota_info *lqi, int type,
+                                   int fakemagics);
+int lustre_read_quota_info(struct lustre_quota_info *lqi, int type);
+int lustre_read_quota_file_info(struct file* f, struct lustre_mem_dqinfo* info);
+int lustre_write_quota_info(struct lustre_quota_info *lqi, int type);
+ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf);
+ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf);
+int get_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info);
+int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info,
+                          dqbuf_t buf, uint blk);
+int remove_free_dqentry(struct file *filp,
+                               struct lustre_mem_dqinfo *info, dqbuf_t buf,
+                               uint blk);
+int insert_free_dqentry(struct file *filp,
+                               struct lustre_mem_dqinfo *info, dqbuf_t buf,
+                               uint blk);
+ssize_t quota_read(struct file *file, struct inode *inode, int type,
+                   uint blk, dqbuf_t buf);
+int walk_tree_dqentry(struct file *filp, struct inode *inode, int type,
+                      uint blk, int depth, struct list_head *list);
+int check_quota_file(struct file *f, struct inode *inode, int type,
+                     lustre_quota_version_t version);
+int lustre_check_quota_file(struct lustre_quota_info *lqi, int type);
+int lustre_read_dquot(struct lustre_dquot *dquot);
+int lustre_commit_dquot(struct lustre_dquot *dquot);
+int lustre_init_quota_info(struct lustre_quota_info *lqi, int type);
+int lustre_get_qids(struct file *fp, struct inode *inode, int type,
+                    struct list_head *list);
+
+
+/* come from lustre_quota_fmt_conver.c */
+int lustre_quota_convert(struct lustre_quota_info *lqi, int type);
 
 #endif                          /* lustre_quota_fmt.h */
diff --git a/lustre/lvfs/lustre_quota_fmt_convert.c b/lustre/lvfs/lustre_quota_fmt_convert.c
new file mode 100644 (file)
index 0000000..70350ed
--- /dev/null
@@ -0,0 +1,185 @@
+/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*-
+ * vim:expandtab:shiftwidth=8:tabstop=8:
+ *
+ * convert quota format.
+ *
+ *  from
+ *  linux/fs/quota_v2.c
+ */
+
+#ifndef EXPORT_SYMTAB
+# define EXPORT_SYMTAB
+#endif
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/quotaio_v1.h>
+
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#include <lustre_quota.h>
+#include <obd_support.h>
+#include "lustre_quota_fmt.h"
+
+static int admin_convert_dqinfo(struct file *fp_v1, struct file *fp_v2,
+                                struct lustre_quota_info *lqi, int type)
+{
+        struct lustre_mem_dqinfo *info_old, *info_new = &lqi->qi_info[type];
+        int rc;
+
+        OBD_ALLOC_PTR(info_old);
+        if (info_old == NULL)
+                return -ENOMEM;
+
+        rc = lustre_read_quota_file_info(fp_v1, info_old);
+        if (!rc) {
+                /* save essential fields: bgrace, igrace, flags */
+                info_new->dqi_bgrace = info_old->dqi_bgrace;
+                info_new->dqi_igrace = info_old->dqi_igrace;
+                info_new->dqi_flags  = info_old->dqi_flags;
+                rc = lustre_write_quota_info(lqi, type);
+        }
+
+        OBD_FREE_PTR(info_old);
+
+        return rc;
+}
+
+static int admin_convert_v1_to_v2(struct file *fp_v1, struct file *fp_v2,
+                                  struct lustre_quota_info *lqi, int type)
+{
+        struct list_head blk_list;
+        struct dqblk *blk_item, *tmp;
+        dqbuf_t buf = NULL;
+        struct lustre_disk_dqblk *ddquot;
+        struct lustre_dquot *dquot = NULL;
+        int rc;
+
+        ENTRY;
+
+        INIT_LIST_HEAD(&blk_list);
+
+        rc = admin_convert_dqinfo(fp_v1, fp_v2, lqi, type);
+        if (rc) {
+                CERROR("could not copy dqinfo!(%d)\n", rc);
+                GOTO(out_free, rc);
+        }
+
+        rc = walk_tree_dqentry(fp_v1, NULL, type, LUSTRE_DQTREEOFF, 0, &blk_list);
+        if (rc) {
+                CERROR("walk through quota file failed!(%d)\n", rc);
+                GOTO(out_free, rc);
+        }
+        if (list_empty(&blk_list))
+                RETURN(0);
+
+        buf = getdqbuf();
+        if (!buf)
+                GOTO(out_free, rc = -ENOMEM);
+
+        ddquot = (struct lustre_disk_dqblk*)GETENTRIES(buf, LUSTRE_QUOTA_V1);
+
+        OBD_ALLOC_PTR(dquot);
+        if (dquot == NULL)
+                GOTO(out_free, rc = -ENOMEM);
+
+        list_for_each_entry(blk_item, &blk_list, link) {
+                loff_t ret = 0;
+                int i;
+                struct lustre_disk_dqblk fakedquot;
+
+                memset(buf, 0, LUSTRE_DQBLKSIZE);
+                if ((ret = quota_read(fp_v1, NULL, type, blk_item->blk, buf))<0) {
+                        CERROR("VFS: Can't read quota tree block %u.\n",
+                               blk_item->blk);
+                        GOTO(out_free, rc = ret);
+                }
+
+                memset(&fakedquot, 0, sizeof(struct lustre_disk_dqblk));
+                for (i = 0; i < LUSTRE_DQSTRINBLK; i++) {
+                        /* skip empty entry */
+                        if (!memcmp
+                            (&fakedquot, ddquot + i,
+                             sizeof(struct lustre_disk_dqblk)))
+                                continue;
+
+                        memset(dquot, 0, sizeof(*dquot));
+
+                        dquot->dq_id = le32_to_cpu(ddquot[i].dqb_id);
+                        dquot->dq_type = type;
+                        dquot->dq_info = lqi;
+
+                        disk2memdqb(&dquot->dq_dqb, &ddquot[i], LUSTRE_QUOTA_V1);
+                        rc = lustre_commit_dquot(dquot);
+                        if (rc < 0)
+                                GOTO(out_free, rc);
+                }
+        }
+
+        EXIT;
+
+out_free:
+        list_for_each_entry_safe(blk_item, tmp, &blk_list, link) {
+                list_del_init(&blk_item->link);
+                kfree(blk_item);
+        }
+        if (buf)
+                freedqbuf(buf);
+        if (dquot)
+                OBD_FREE_PTR(dquot);
+        return rc;
+}
+
+int lustre_quota_convert(struct lustre_quota_info *lqi, int type)
+{
+        struct file *f_v2 = lqi->qi_files[type];
+        const char *qf_v1[] = LUSTRE_ADMIN_QUOTAFILES_V1;
+        char name[64];
+        struct file *f_v1;
+        int rc = 0;
+        ENTRY;
+
+        LASSERT(f_v2);
+
+        rc = lustre_init_quota_info_generic(lqi, type, 1);
+        if (rc) {
+                CERROR("could not initialize new quota file(%d)\n", rc);
+                RETURN(rc);
+        }
+
+        /* Open old quota file and copy to the new one */
+        sprintf(name, "OBJECTS/%s", qf_v1[type]);
+        f_v1 = filp_open(name, O_RDONLY, 0);
+        if (!IS_ERR(f_v1)) {
+                if (!check_quota_file(f_v1, NULL, type, LUSTRE_QUOTA_V1)) {
+                        rc = admin_convert_v1_to_v2(f_v1, f_v2, lqi, type);
+                        if (rc)
+                                CERROR("failed to convert v1 quota file"
+                                       " to v2 quota file.\n");
+                        else
+                                CDEBUG(D_INFO, "Found v1 quota file, "
+                                               "successfully converted to v2.\n");
+                }
+                else
+                        CERROR("old quota file is broken, "
+                               "new quota file will be empty\n");
+
+                filp_close(f_v1, 0);
+        } else if (PTR_ERR(f_v1) != -ENOENT) /* No quota file is ok */
+                CERROR("old quota file can not be open, "
+                       "new quota file will be empty (%ld)\n", PTR_ERR(f_v1));
+
+        /* mark corresponding quota file as correct */
+        if (!rc)
+                lustre_init_quota_header(lqi, type, 0);
+
+        RETURN(rc);
+}
+
+EXPORT_SYMBOL(lustre_quota_convert);
index 09e10af..3af91f4 100644 (file)
@@ -70,7 +70,7 @@ static int quotfmt_initialize(struct lustre_quota_info *lqi,
                                        sizeof(struct lustre_disk_dqheader),
                                        &offset);
                 if (size != sizeof(struct lustre_disk_dqheader)) {
-                        CERROR("error writing quoafile header %s (rc = %d)\n",
+                        CERROR("error writing quotafile header %s (rc = %d)\n",
                                name, rc);
                         rc = size;
                         break;
@@ -129,7 +129,7 @@ static int quotfmt_test_1(struct lustre_quota_info *lqi)
         ENTRY;
 
         for (i = 0; i < MAXQUOTAS; i++) {
-                if (!lustre_check_quota_file(lqi, i))
+                if (lustre_check_quota_file(lqi, i))
                         RETURN(-EINVAL);
         }
         RETURN(0);
@@ -220,7 +220,7 @@ static void put_rand_dquot(struct lustre_dquot *dquot)
 static int write_check_dquot(struct lustre_quota_info *lqi)
 {
         struct lustre_dquot *dquot;
-        struct mem_dqblk dqblk;
+        struct lustre_mem_dqblk dqblk;
         int rc = 0;
         ENTRY;
 
index 8876603..40804f7 100644 (file)
@@ -74,6 +74,9 @@ int mds_quota_ctl(struct obd_export *exp, struct obd_quotactl *oqctl)
         case Q_GETOQUOTA:
                 rc = mds_get_obd_quota(obd, oqctl);
                 break;
+        case LUSTRE_Q_INVALIDATE:
+                rc = mds_quota_invalidate(obd, oqctl);
+                break;
         default:
                 CERROR("%s: unsupported mds_quotactl command: %d\n",
                        obd->obd_name, oqctl->qc_cmd);
index 8acd727..ab3b619 100644 (file)
@@ -175,7 +175,7 @@ EXPORT_SYMBOL(lprocfs_wr_itune);
 #define USER_QUOTA      1
 #define GROUP_QUOTA     2
 
-#define MAX_STYPE_SIZE  4
+#define MAX_STYPE_SIZE  5
 int lprocfs_rd_type(char *page, char **start, off_t off, int count, 
                     int *eof, void *data)
 {
@@ -192,7 +192,28 @@ int lprocfs_rd_type(char *page, char **start, off_t off, int count,
                 if (type & GROUP_QUOTA)
                         strcat(stype, "g");
         }
-        
+
+        /* append with quota version on MDS */
+        if (!strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME)) {
+                int rc;
+                lustre_quota_version_t version;
+
+                rc = mds_quota_get_version(obd, &version);
+                if (rc)
+                        return rc;
+
+                switch (version) {
+                        case LUSTRE_QUOTA_V1:
+                                strcat(stype, "1");
+                                break;
+                        case LUSTRE_QUOTA_V2:
+                                strcat(stype, "2");
+                                break;
+                        default:
+                                return -ENOSYS;
+                }
+        }
+
         return snprintf(page, count, "%s\n", stype);
 }
 EXPORT_SYMBOL(lprocfs_rd_type);
@@ -255,17 +276,52 @@ int lprocfs_wr_type(struct file *file, const char *buffer,
         struct obd_device *obd = (struct obd_device *)data;
         struct obd_device_target *obt = &obd->u.obt;
         int type = 0;
+        unsigned long i;
         char stype[MAX_STYPE_SIZE + 1] = "";
         LASSERT(obd != NULL);
 
-        if (copy_from_user(stype, buffer, MAX_STYPE_SIZE))
+        if (count > MAX_STYPE_SIZE)
+                return -EINVAL;
+
+        if (copy_from_user(stype, buffer, count))
                 return -EFAULT;
 
-        if (strchr(stype, 'u'))
-                type |= USER_QUOTA;
-        if (strchr(stype, 'g'))
-                type |= GROUP_QUOTA;
-        
+        for (i = 0 ; i < count ; i++) {
+                int rc;
+
+                switch (stype[i]) {
+                case 'u' : 
+                        type |= USER_QUOTA;
+                        break;
+                case 'g' : 
+                        type |= GROUP_QUOTA;
+                        break;
+                /* quota version specifiers */
+                case '1' : 
+                        if (strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME))
+                                break;
+
+                        rc = mds_quota_set_version(obd, LUSTRE_QUOTA_V1);
+                        if (rc) {
+                                CDEBUG(D_QUOTA, "failed to set quota v1! %d\n", rc);
+                                return rc;
+                        }
+                        break;
+                case '2' : 
+                        if (strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME))
+                                break;
+
+                        rc = mds_quota_set_version(obd, LUSTRE_QUOTA_V2);
+                        if (rc) {
+                                CDEBUG(D_QUOTA, "could not set quota v2! %d\n", rc);
+                                return rc;
+                        }
+                        break;
+                default  : /* just skip stray symbols like \n */
+                        break;
+                }
+        }
+
         obt->obt_qctxt.lqc_atype = type;
 
         if (type == 0)
@@ -496,6 +552,7 @@ static int mds_quota_setup(struct obd_device *obd)
         int rc;
         ENTRY;
 
+        mds->mds_quota_info.qi_version = LUSTRE_QUOTA_V2;
         atomic_set(&obt->obt_quotachecking, 1);
         /* initialize quota master and quota context */
         sema_init(&mds->mds_qonoff_sem, 1);
index fcfee55..7fa3629 100644 (file)
@@ -27,8 +27,8 @@
 
 #define DQUOT_DEBUG(dquot, fmt, arg...)                                       \
         CDEBUG(D_QUOTA, "refcnt(%u) id(%u) type(%u) off(%llu) flags(%lu) "    \
-               "bhardlimit(%u) curspace("LPX64") ihardlimit(%u) "             \
-               "curinodes(%u): " fmt, dquot->dq_refcnt,                       \
+               "bhardlimit("LPU64") curspace("LPU64") ihardlimit("LPU64")"    \
+               "curinodes("LPU64"): " fmt, dquot->dq_refcnt,                  \
                dquot->dq_id, dquot->dq_type, dquot->dq_off,  dquot->dq_flags, \
                dquot->dq_dqb.dqb_bhardlimit, dquot->dq_dqb.dqb_curspace,      \
                dquot->dq_dqb.dqb_ihardlimit, dquot->dq_dqb.dqb_curinodes,     \
@@ -74,6 +74,10 @@ int mds_quota_adjust(struct obd_device *obd, unsigned int qcids[],
 int filter_quota_adjust(struct obd_device *obd, unsigned int qcids[],
                         unsigned int qpids[], int rc, int opc);
 int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl);
+int mds_quota_get_version(struct obd_device *obd, lustre_quota_version_t *ver);
+int mds_quota_set_version(struct obd_device *obd, lustre_quota_version_t ver);
+int mds_quota_invalidate(struct obd_device *obd, struct obd_quotactl *oqctl);
+
 int mds_admin_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl);
 int mds_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl);
 int mds_quota_off(struct obd_device *obd, struct obd_quotactl *oqctl);
index 0a916a6..c112bdd 100644 (file)
@@ -361,31 +361,122 @@ int filter_quota_adjust(struct obd_device *obd, unsigned int qcids[],
         RETURN(0);
 }
 
-#define LUSTRE_ADMIN_QUOTAFILES {\
-       "admin_quotafile.usr",  /* user admin quotafile */\
-       "admin_quotafile.grp"   /* group admin quotafile */\
-}
 static const char prefix[] = "OBJECTS/";
 
+int mds_quota_get_version(struct obd_device *obd, 
+                          lustre_quota_version_t *version)
+{
+        struct mds_obd *mds = &obd->u.mds;
+        struct lustre_quota_info *qinfo = &mds->mds_quota_info;
+
+        *version = qinfo->qi_version;
+
+        return 0;
+}
+
+int mds_quota_set_version(struct obd_device *obd, lustre_quota_version_t version)
+{
+        struct mds_obd *mds = &obd->u.mds;
+        struct lustre_quota_info *qinfo = &mds->mds_quota_info;
+        int rc = 0, i;
+
+        if (version != LUSTRE_QUOTA_V1 &&
+            version != LUSTRE_QUOTA_V2)
+                return -EINVAL;
+
+        down(&mds->mds_qonoff_sem);
+
+        /* no need to change version? nothing to do then */
+        if (qinfo->qi_version == version)
+                goto out;
+
+        for (i = 0; i < MAXQUOTAS; i++) {
+                /* quota file has been opened ? */
+                if (qinfo->qi_files[i]) {
+                        rc = -EBUSY;
+                        goto out;
+                }
+        }
+
+        CDEBUG(D_INFO, "changing quota version %d -> %d\n", qinfo->qi_version,
+               version);
+
+        qinfo->qi_version = version;
+
+out:
+        up(&mds->mds_qonoff_sem);
+
+        return rc;
+}
+
+int mds_quota_invalidate(struct obd_device *obd, struct obd_quotactl *oqctl)
+{
+        struct mds_obd *mds = &obd->u.mds;
+        struct lustre_quota_info *qinfo = &mds->mds_quota_info;
+        int rc = 0, i;
+        char *quotafiles_v1[] = LUSTRE_ADMIN_QUOTAFILES_V1;
+        char *quotafiles_v2[] = LUSTRE_ADMIN_QUOTAFILES_V2;
+        char name[64];
+        struct lvfs_run_ctxt saved;
+
+        push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
+
+        down(&mds->mds_qonoff_sem);
+
+        for (i = 0; i < MAXQUOTAS; i++) {
+                struct file *fp;
+                char* quotafile = (qinfo->qi_version == LUSTRE_QUOTA_V1)?
+                                   quotafiles_v1[i]:quotafiles_v2[i];
+
+                if (!Q_TYPESET(oqctl, i))
+                        continue;
+
+                /* quota file has been opened ? */
+                if (qinfo->qi_files[i]) {
+                        rc = -EBUSY;
+                        goto out;
+                }
+
+                LASSERT(strlen(quotafile) + sizeof(prefix) <= sizeof(name));
+                sprintf(name, "%s%s", prefix, quotafile);
+
+                fp = filp_open(name, O_CREAT | O_TRUNC, 0644);
+                if (IS_ERR(fp)) {
+                        rc = PTR_ERR(fp);
+                        CERROR("error invalidating admin quotafile %s (rc:%d)\n",
+                               name, rc);
+                }
+                else
+                        filp_close(fp, 0);
+        }
+
+out:
+        up(&mds->mds_qonoff_sem);
+
+        pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
+
+        return rc;
+}
+
 int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl)
 {
         struct mds_obd *mds = &obd->u.mds;
         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
-        const char *quotafiles[] = LUSTRE_ADMIN_QUOTAFILES;
+        char *quotafiles_v1[] = LUSTRE_ADMIN_QUOTAFILES_V1;
+        char *quotafiles_v2[] = LUSTRE_ADMIN_QUOTAFILES_V2;
         struct lvfs_run_ctxt saved;
         char name[64];
         int i, rc = 0;
-        struct dentry *dparent = mds->mds_objects_dir;
-        struct inode *iparent = dparent->d_inode;
         ENTRY;
 
-        LASSERT(iparent);
         push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
 
         down(&mds->mds_qonoff_sem);
-        for (i = 0; i < MAXQUOTAS; i++) {
-                struct dentry *de;
+
+        for (i = 0; i < MAXQUOTAS && !rc; i++) {
                 struct file *fp;
+                char* quotafile = (qinfo->qi_version == LUSTRE_QUOTA_V1)?
+                                        quotafiles_v1[i]:quotafiles_v2[i];
 
                 if (!Q_TYPESET(oqctl, i))
                         continue;
@@ -397,33 +488,44 @@ int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl)
                         continue;
                 }
 
-                /* lookup quota file */
-                rc = 0;
-                LOCK_INODE_MUTEX(iparent);
-                de = lookup_one_len(quotafiles[i], dparent,
-                                    strlen(quotafiles[i]));
-                UNLOCK_INODE_MUTEX(iparent);
-                if (IS_ERR(de) || de->d_inode == NULL || 
-                    !S_ISREG(de->d_inode->i_mode))
-                        rc = IS_ERR(de) ? PTR_ERR(de) : -ENOENT;
-                if (!IS_ERR(de))
-                        dput(de);
-
-                if (rc && rc != -ENOENT) {
-                        CERROR("error lookup quotafile %s! (rc:%d)\n",
+                LASSERT(strlen(quotafile) + sizeof(prefix) <= sizeof(name));
+                sprintf(name, "%s%s", prefix, quotafile);
+
+                /* check if quota file exists and is correct */
+                fp = filp_open(name, O_RDONLY, 0);
+                if (!IS_ERR(fp)) {
+                        /* irregular file is not the right place for quota */
+                        if (!S_ISREG(fp->f_dentry->d_inode->i_mode)) {
+                                CERROR("admin quota file %s is not "
+                                       "regular!", quotafile);
+                                filp_close(fp, 0);
+                                rc = -EINVAL;
+                                break;
+                        }
+                        qinfo->qi_files[i] = fp;
+                        rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_CHK);
+                        qinfo->qi_files[i] = 0;
+                        filp_close(fp, 0);
+                }
+                else
+                        rc = PTR_ERR(fp);
+
+                if (!rc)
+                        continue;
+
+                /* -EINVAL may be returned by quotainfo for bad quota file */
+                if (rc != -ENOENT && rc != -EINVAL) {
+                        CERROR("error opening old quota file %s (%d)\n", 
                                name, rc);
                         break;
-                } else if (!rc) {
-                        continue;
                 }
 
-                LASSERT(strlen(quotafiles[i]) + sizeof(prefix) <= sizeof(name));
-                sprintf(name, "%s%s", prefix, quotafiles[i]);
+                CDEBUG(D_INFO, "%s new quota file %s\n", name, 
+                       rc == -ENOENT ? "creating" : "overwriting");
 
-                LASSERT(rc == -ENOENT);
-                /* create quota file */
-                fp = filp_open(name, O_CREAT | O_EXCL, 0644);
-                if (IS_ERR(fp) || !S_ISREG(fp->f_dentry->d_inode->i_mode)) {
+                /* create quota file overwriting old if needed */
+                fp = filp_open(name, O_CREAT | O_TRUNC, 0644);
+                if (IS_ERR(fp)) {
                         rc = PTR_ERR(fp);
                         CERROR("error creating admin quotafile %s (rc:%d)\n",
                                name, rc);
@@ -431,15 +533,26 @@ int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl)
                 }
 
                 qinfo->qi_files[i] = fp;
-                rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_INIT_INFO);
-                filp_close(fp, 0);
-                qinfo->qi_files[i] = NULL;
 
-                if (rc) {
-                        CERROR("error init %s admin quotafile! (rc:%d)\n",
-                               i == USRQUOTA ? "user" : "group", rc);
+                switch (qinfo->qi_version) {
+                case LUSTRE_QUOTA_V1:
+                        rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_INIT_INFO);
+                        if (rc)
+                                CERROR("error init %s admin quotafile! (rc:%d)\n",
+                                       i == USRQUOTA ? "user" : "group", rc);
+                        break;
+                case LUSTRE_QUOTA_V2:
+                        rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_CONVERT);
+                        if (rc)
+                                CERROR("error convert %s admin quotafile! (rc:%d)\n",
+                                       i == USRQUOTA ? "user" : "group", rc);
                         break;
+                default:
+                        LBUG();
                 }
+
+                filp_close(fp, 0);
+                qinfo->qi_files[i] = NULL;
         }
         up(&mds->mds_qonoff_sem);
 
@@ -470,38 +583,61 @@ int mds_admin_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl)
 {
         struct mds_obd *mds = &obd->u.mds;
         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
-        const char *quotafiles[] = LUSTRE_ADMIN_QUOTAFILES;
+        const char *quotafiles_v1[] = LUSTRE_ADMIN_QUOTAFILES_V1;
+        const char *quotafiles_v2[] = LUSTRE_ADMIN_QUOTAFILES_V2;
         char name[64];
         int i, rc = 0;
-        struct inode *iparent = mds->mds_objects_dir->d_inode;
         ENTRY;
 
-        LASSERT(iparent);
-
         /* open admin quota files and read quotafile info */
         for (i = 0; i < MAXQUOTAS; i++) {
                 struct file *fp;
+                const char* quotafile = qinfo->qi_version == LUSTRE_QUOTA_V1?
+                                        quotafiles_v1[i] : quotafiles_v2[i];
 
                 if (!Q_TYPESET(oqctl, i))
                         continue;
 
-                LASSERT(strlen(quotafiles[i]) + sizeof(prefix) <= sizeof(name));
-                sprintf(name, "%s%s", prefix, quotafiles[i]);
+                LASSERT(strlen(quotafile) 
+                        + sizeof(prefix) <= sizeof(name));
+                sprintf(name, "%s%s", prefix, quotafile);
 
                 if (qinfo->qi_files[i] != NULL) {
                         rc = -EBUSY;
                         break;
                 }
 
-                fp = filp_open(name, O_RDWR | O_EXCL, 0644);
+                fp = filp_open(name, O_RDWR, 0);
+                /* handle transparent migration to 64 bit quota file */
+                if (IS_ERR(fp) && PTR_ERR(fp) == -ENOENT && 
+                    qinfo->qi_version == LUSTRE_QUOTA_V2) {
+                        CDEBUG(D_INFO, "attempting to convert V1 quota file to V2 format.\n");
+                        fp = filp_open(name, O_CREAT | O_TRUNC, 0644);
+                        if (!IS_ERR(fp)) {
+                                qinfo->qi_files[i] = fp;
+                                rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_CONVERT);
+                                if (rc) {
+                                        CERROR("error convert %s admin quotafile! (rc:%d)\n",
+                                               i == USRQUOTA ? "user" : "group", rc);
+                                        break;
+                                }
+                        }
+                }
+
                 if (IS_ERR(fp) || !S_ISREG(fp->f_dentry->d_inode->i_mode)) {
-                        rc = PTR_ERR(fp);
-                        CDEBUG(rc == -ENOENT ? D_QUOTA : D_ERROR,
-                               "open %s failed! (rc:%d)\n", name, rc);
+                        rc = IS_ERR(fp) ? PTR_ERR(fp) : -EINVAL;
+                        CERROR("error open/create %s! (rc:%d)\n", name, rc);
                         break;
                 }
                 qinfo->qi_files[i] = fp;
 
+                rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_CHK);
+                if (rc) {
+                        CERROR("invalid quota file %s! (rc:%d)\n",
+                               name, rc);
+                        break;
+                }
+
                 rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_RD_INFO);
                 if (rc) {
                         CERROR("error read quotainfo of %s! (rc:%d)\n",
@@ -748,7 +884,7 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl)
 {
         struct mds_obd *mds = &obd->u.mds;
         struct lustre_quota_info *qinfo = &mds->mds_quota_info;
-        __u32 ihardlimit, isoftlimit, bhardlimit, bsoftlimit;
+        __u64 ihardlimit, isoftlimit, bhardlimit, bsoftlimit;
         time_t btime, itime;
         struct lustre_dquot *dquot;
         struct obd_dqblk *dqblk = &oqctl->qc_dqblk;
index 973c53d..bbe8463 100644 (file)
@@ -1029,6 +1029,79 @@ test_14(){ # b=12223 -- setting quota on root
 }
 run_test 14 "test setting quota on root ==="
 
+quota_set_version() {
+        do_facet mds "for i in /proc/fs/lustre/mds/${FSNAME}-MDT*/quota_type; do
+                echo $1 >> \\\$i;
+        done"
+}
+
+test_14a(){
+        # 1. check that required users exist
+        # 2. ensure that switch to new mode will start conversion
+        # 3. start quota in old mode and put some entries
+        # 4. restart quota in new mode forcing conversion and check the entries
+
+        MISSING_USERS=""
+        for i in `seq 1 30`; do
+                check_runas_id_ret quota15_$i
+                if [ "$?" != "0" ]; then
+                       MISSING_USERS="$MISSING_USERS quota15_$i"
+                fi
+        done
+
+        if [ -n "$MISSING_USERS" ]; then
+                echo "following users are missing: $MISSING_USERS, test skipped"
+                return
+        fi
+
+        $LFS quotaoff -ug $DIR
+        quota_set_version 1
+        $LFS quotacheck -ug $DIR
+
+        for i in `seq 1 30`; do 
+                $LFS setquota -u quota15_$i $i $i $i $i $DIR || error "lfs setquota failed"
+        done
+
+        $LFS quotaoff -ug $DIR
+        quota_set_version 2
+        $LFS quotainv -ug $DIR
+        $LFS quotacheck -ug $DIR
+
+        for i in `seq 1 30`; do 
+                # the format is "mntpnt   curspace[*]   bsoftlimit   bhardlimit   [time]   curinodes[*]    isoftlimit  ihardlimit"
+                ($LFS quota -u quota15_$i $DIR | grep -E '^ *'$DIR' *[0-9]+\** *'$i' *'$i' *[0-9]+\** *'$i' *'$i) \
+                 || error "lfs quota output is unexpected"
+                $LFS setquota -u quota15_$i 0 0 0 0 $DIR || error "ifs setquota clear failed"
+        done
+}
+run_test 14a "setting 30 quota entries in quota v1 file before conversion ==="
+
+test_15(){
+        LIMIT=$((24 * 1024 * 1024 * 1024 * 1024)) # 24 TB
+        PATTERN="`echo $DIR | sed 's/\//\\\\\//g'`"
+
+        # test for user
+        $LFS setquota -u $TSTUSR 0 $LIMIT 0 0 $DIR || error "failed setting user quota limit $LIMIT"
+        TOTAL_LIMIT="`$LFS quota -u $TSTUSR $DIR | awk '/^.*'$PATTERN'.*[[:digit:]+][[:space:]+]/ { print $4 }'`"
+        [ $TOTAL_LIMIT -eq $LIMIT ] || error "  (user)total limits = $TOTAL_LIMIT; limit = $LIMIT, failed!"
+        echo "  (user)total limits = $TOTAL_LIMIT; limit = $LIMIT, successful!"
+        $LFS setquota -u $TSTUSR 0 0 0 0 $DIR || error "failed removing user quota limit"
+
+        # test for group
+        $LFS setquota -g $TSTUSR 0 $LIMIT 0 0 $DIR || error "failed setting group quota limit $LIMIT"
+        TOTAL_LIMIT="`$LFS quota -g $TSTUSR $DIR | awk '/^.*'$PATTERN'.*[[:digit:]+][[:space:]+]/ { print $4 }'`"
+        [ $TOTAL_LIMIT -eq $LIMIT ] || error "  (group)total limits = $TOTAL_LIMIT; limit = $LIMIT, failed!"
+        echo "  (group)total limits = $TOTAL_LIMIT; limit = $LIMIT, successful!"
+        $LFS setquota -g $TSTUSR 0 0 0 0 $DIR || error "failed removing group quota limit"
+        $LFS quotaoff -ug $DIR
+        quota_set_version 1
+        $LFS quotacheck -ug $DIR || error "quotacheck failed"
+
+        echo "Testing that >4GB quota limits fail on volume with quota v1"
+        ! $LFS setquota -u $TSTUSR 0 $LIMIT 0 0 $DIR
+}
+run_test 15 "set block quota more than 4T ==="
+
 # turn off quota
 test_99()
 {
index 30578b6..3fe9d39 100644 (file)
@@ -1246,3 +1246,15 @@ check_runas_id() {
         add user $myRUNAS_ID:$myRUNAS_ID on these nodes."
     rm -rf $DIR/d0_runas_test
 }
+
+check_runas_id_ret() {
+    local myRUNAS_ID=$1
+    shift
+    local myRUNAS=$@
+    mkdir $DIR/d0_runas_test
+    chmod 0755 $DIR
+    chown $myRUNAS_ID:$myRUNAS_ID $DIR/d0_runas_test
+    $myRUNAS touch $DIR/d0_runas_test/f$$ || return 1
+    rm -rf $DIR/d0_runas_test
+    return 0
+}
index fae0aad..8bb138e 100644 (file)
@@ -66,6 +66,7 @@ static int lfs_quotaon(int argc, char **argv);
 static int lfs_quotaoff(int argc, char **argv);
 static int lfs_setquota(int argc, char **argv);
 static int lfs_quota(int argc, char **argv);
+static int lfs_quotainv(int argc, char **argv);
 #endif
 static int lfs_join(int argc, char **argv);
 
@@ -135,6 +136,8 @@ command_t cmdlist[] = {
          "       setquota -t [ -u | -g ] <block-grace> <inode-grace> <filesystem>"},
         {"quota", lfs_quota, 0, "Display disk usage and limits.\n"
          "usage: quota [ -o obd_uuid ] [{-u|-g  <name>}|-t] <filesystem>"},
+        {"quotainv", lfs_quotainv, 0, "Invalidate quota data.\n"
+         "usage: quotainv [-u|-g] <filesystem>"},
 #endif
         {"help", Parser_help, 0, "help"},
         {"exit", Parser_quit, 0, "quit"},
@@ -1257,6 +1260,52 @@ static int lfs_quotaoff(int argc, char **argv)
         return 0;
 }
 
+static int lfs_quotainv(int argc, char **argv)
+{
+        int c;
+        char *mnt;
+        struct if_quotactl qctl;
+        char *obd_type = (char *)qctl.obd_type;
+        int rc;
+
+        memset(&qctl, 0, sizeof(qctl));
+        qctl.qc_cmd = LUSTRE_Q_INVALIDATE;
+
+        optind = 0;
+        while ((c = getopt(argc, argv, "ug")) != -1) {
+                switch (c) {
+                case 'u':
+                        qctl.qc_type |= 0x01;
+                        break;
+                case 'g':
+                        qctl.qc_type |= 0x02;
+                        break;
+                default:
+                        fprintf(stderr, "error: %s: option '-%c' "
+                                        "unrecognized\n", argv[0], c);
+                        return CMD_HELP;
+                }
+        }
+
+        if (qctl.qc_type)
+                qctl.qc_type--;
+        else /* by default, invalidate quota for both user & group */
+                qctl.qc_type = 0x02;
+
+        if (argc == optind)
+                return CMD_HELP;
+
+        mnt = argv[optind];
+
+        rc = llapi_quotactl(mnt, &qctl);
+        if (rc) {
+                fprintf(stderr, "quotainv failed: %s\n", strerror(errno));
+                return rc;
+        }
+
+        return 0;
+}
+
 static int name2id(unsigned int *id, char *name, int type)
 {
         if (type == USRQUOTA) {
@@ -1380,6 +1429,17 @@ error:
         return ULONG_MAX;
 }
 
+#define ARG2ULL(nr, str, msg)                                           \
+do {                                                                    \
+        char *endp;                                                     \
+        nr = strtoull(str, &endp, 0);                                   \
+        if (*endp) {                                                    \
+                fprintf(stderr, "error: bad %s: %s\n", msg, str);       \
+                return CMD_HELP;                                        \
+        }                                                               \
+} while (0)
+
+
 int lfs_setquota(int argc, char **argv)
 {
         int c;
@@ -1432,10 +1492,10 @@ int lfs_setquota(int argc, char **argv)
                         return CMD_HELP;
                 }
 
-                ARG2INT(dqb->dqb_bsoftlimit, argv[optind++], "block-softlimit");
-                ARG2INT(dqb->dqb_bhardlimit, argv[optind++], "block-hardlimit");
-                ARG2INT(dqb->dqb_isoftlimit, argv[optind++], "inode-softlimit");
-                ARG2INT(dqb->dqb_ihardlimit, argv[optind++], "inode-hardlimit");
+                ARG2ULL(dqb->dqb_bsoftlimit, argv[optind++], "block-softlimit");
+                ARG2ULL(dqb->dqb_bhardlimit, argv[optind++], "block-hardlimit");
+                ARG2ULL(dqb->dqb_isoftlimit, argv[optind++], "inode-softlimit");
+                ARG2ULL(dqb->dqb_ihardlimit, argv[optind++], "inode-hardlimit");
 
                 dqb->dqb_valid = QIF_LIMITS;
         } else {