diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/fs/dquot.c linux-2.4.0-test11-newquota/fs/dquot.c --- linux-2.4.0-test11-quotafix/fs/dquot.c Mon Nov 27 09:43:43 2000 +++ linux-2.4.0-test11-newquota/fs/dquot.c Mon Nov 27 10:08:00 2000 @@ -26,7 +26,7 @@ * dquot_incr_...() to calling functions. * invalidate_dquots() now writes modified dquots. * Serialized quota_off() and quota_on() for mount point. - * Fixed a few bugs in grow_dquots. + * Fixed a few bugs in grow_dquots(). * Fixed deadlock in write_dquot() - we no longer account quotas on * quota files * remove_dquot_ref() moved to inode.c - it now traverses through inodes @@ -42,6 +42,10 @@ * Write updated not to require dquot lock * Jan Kara, , 9/2000 * + * New quotafile format + * Alocation units changed to bytes + * Jan Kara, , 2000 + * * (C) Copyright 1994 - 1997 Marco van Wieringen */ @@ -60,14 +64,18 @@ #include #include +#include -#define __DQUOT_VERSION__ "dquot_6.4.0" +#define __DQUOT_VERSION__ "dquot_6.5.0" +#define __DQUOT_PARANOIA int nr_dquots, nr_free_dquots; -static char *quotatypes[] = INITQFNAMES; +static const char *quotatypes[] = INITQFNAMES; +static const uint quota_magics[] = INITQMAGICS; +static const uint quota_versions[] = INITQVERSIONS; -static inline struct quota_mount_options *sb_dqopt(struct super_block *sb) +static inline struct quota_info *sb_dqopt(struct super_block *sb) { return &sb->s_dquot; } @@ -107,7 +115,7 @@ static void dqput(struct dquot *); static struct dquot *dqduplicate(struct dquot *); -static inline char is_enabled(struct quota_mount_options *dqopt, short type) +static inline char is_enabled(struct quota_info *dqopt, short type) { switch (type) { case USRQUOTA: @@ -176,11 +184,12 @@ static inline void remove_free_dquot(struct dquot *dquot) { - /* sanity check */ +#ifdef __DQUOT_PARANOIA if (list_empty(&dquot->dq_free)) { printk("remove_free_dquot: dquot not on the free list??\n"); return; /* J.K. Just don't do anything */ } +#endif list_del(&dquot->dq_free); INIT_LIST_HEAD(&dquot->dq_free); nr_free_dquots--; @@ -249,6 +258,378 @@ } /* + * IO operations on file + */ + +#define GETIDINDEX(id, depth) (((id) >> ((DQTREEDEPTH-(depth)-1)*8)) & 0xff) +#define GETENTRIES(buf) ((struct disk_dqblk *)(((char *)buf)+sizeof(struct disk_dqdbheader))) + +static inline void mark_quotafile_info_dirty(struct mem_dqinfo *info) +{ + info->dqi_flags |= DQF_DIRTY; +} + +static inline int quotafile_info_dirty(struct mem_dqinfo *info) +{ + return info->dqi_flags & DQF_DIRTY; +} + +/* Read information header from quota file */ +static int read_quotafile_info(struct super_block *sb, short type) +{ + mm_segment_t fs; + struct disk_dqinfo dinfo; + struct mem_dqinfo *info = sb_dqopt(sb)->info+type; + struct file *f = sb_dqopt(sb)->files[type]; + ssize_t size; + loff_t offset = DQINFOOFF; + + fs = get_fs(); + set_fs(KERNEL_DS); + size = f->f_op->read(f, (char *)&dinfo, sizeof(struct disk_dqinfo), &offset); + set_fs(fs); + if (size != sizeof(struct disk_dqinfo)) { + printk(KERN_WARNING "Can't read info structure on device %s.\n", + kdevname(f->f_dentry->d_sb->s_dev)); + return -1; + } + info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); + info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace); + info->dqi_flags = le32_to_cpu(dinfo.dqi_flags); + info->dqi_blocks = le32_to_cpu(dinfo.dqi_blocks); + info->dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk); + info->dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry); + return 0; +} + +/* Write information header to quota file */ +static int write_quotafile_info(struct super_block *sb, short type) +{ + mm_segment_t fs; + struct disk_dqinfo dinfo; + struct mem_dqinfo *info = sb_dqopt(sb)->info+type; + struct file *f = sb_dqopt(sb)->files[type]; + ssize_t size; + loff_t offset = DQINFOOFF; + + info[type].dqi_flags &= ~DQF_DIRTY; + dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace); + dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace); + dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK); + dinfo.dqi_blocks = cpu_to_le32(info->dqi_blocks); + dinfo.dqi_free_blk = cpu_to_le32(info->dqi_free_blk); + dinfo.dqi_free_entry = cpu_to_le32(info->dqi_free_entry); + fs = get_fs(); + set_fs(KERNEL_DS); + size = f->f_op->write(f, (char *)&dinfo, sizeof(struct disk_dqinfo), &offset); + set_fs(fs); + if (size != sizeof(struct disk_dqinfo)) { + printk(KERN_WARNING "Can't write info structure on device %s.\n", + kdevname(f->f_dentry->d_sb->s_dev)); + return -1; + } + return 0; +} + +static void disk2memdqb(struct mem_dqblk *m, struct disk_dqblk *d) +{ + 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); +} + +static void mem2diskdqb(struct disk_dqblk *d, struct mem_dqblk *m, qid_t id) +{ + 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); +} + +static dqbuf_t getdqbuf(void) +{ + dqbuf_t buf = kmalloc(DQBLKSIZE, GFP_KERNEL); + if (!buf) + printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n"); + return buf; +} + +static inline void freedqbuf(dqbuf_t buf) +{ + kfree(buf); +} + +static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf) +{ + mm_segment_t fs; + ssize_t ret; + loff_t offset = blk<f_op->read(filp, (char *)buf, DQBLKSIZE, &offset); + set_fs(fs); + return ret; +} + +static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf) +{ + mm_segment_t fs; + ssize_t ret; + loff_t offset = blk<f_op->write(filp, (char *)buf, DQBLKSIZE, &offset); + set_fs(fs); + return ret; + +} + +/* Remove empty block from list and return it */ +static int get_free_dqblk(struct file *filp, struct mem_dqinfo *info) +{ + dqbuf_t buf = getdqbuf(); + struct disk_dqdbheader *dh = (struct disk_dqdbheader *)buf; + int ret, blk; + + if (!buf) + return -ENOMEM; + if (info->dqi_free_blk) { + blk = info->dqi_free_blk; + if ((ret = read_blk(filp, blk, buf)) < 0) + goto out_buf; + info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free); + } + else { + memset(buf, 0, DQBLKSIZE); + if ((ret = write_blk(filp, info->dqi_blocks, buf)) < 0) /* Assure block allocation... */ + goto out_buf; + blk = info->dqi_blocks++; + } + mark_quotafile_info_dirty(info); + ret = blk; +out_buf: + freedqbuf(buf); + return ret; +} + +/* Insert empty block to the list */ +static int put_free_dqblk(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk) +{ + struct disk_dqdbheader *dh = (struct disk_dqdbheader *)buf; + int err; + + dh->dqdh_next_free = cpu_to_le32(info->dqi_free_blk); + dh->dqdh_prev_free = cpu_to_le32(0); + dh->dqdh_entries = cpu_to_le16(0); + info->dqi_free_blk = blk; + mark_quotafile_info_dirty(info); + if ((err = write_blk(filp, blk, buf)) < 0) /* Some strange block. We had better leave it... */ + return err; + return 0; +} + +/* Remove given block from the list of blocks with free entries */ +static int remove_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk) +{ + dqbuf_t tmpbuf = getdqbuf(); + struct disk_dqdbheader *dh = (struct disk_dqdbheader *)buf; + uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free); + int err; + + if (!tmpbuf) + return -ENOMEM; + if (nextblk) { + if ((err = read_blk(filp, nextblk, tmpbuf)) < 0) + goto out_buf; + ((struct disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free; + if ((err = write_blk(filp, nextblk, tmpbuf)) < 0) + goto out_buf; + } + if (prevblk) { + if ((err = read_blk(filp, prevblk, tmpbuf)) < 0) + goto out_buf; + ((struct disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free; + if ((err = write_blk(filp, prevblk, tmpbuf)) < 0) + goto out_buf; + } + else { + info->dqi_free_entry = nextblk; + mark_quotafile_info_dirty(info); + } + 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); + return 0; +out_buf: + freedqbuf(tmpbuf); + return err; +} + +/* Insert given block to the beginning of list with free entries */ +static int insert_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk) +{ + dqbuf_t tmpbuf = getdqbuf(); + struct disk_dqdbheader *dh = (struct disk_dqdbheader *)buf; + int err; + + if (!tmpbuf) + return -ENOMEM; + dh->dqdh_next_free = cpu_to_le32(info->dqi_free_entry); + dh->dqdh_prev_free = cpu_to_le32(0); + if ((err = write_blk(filp, blk, buf)) < 0) + goto out_buf; + if (info->dqi_free_entry) { + if ((err = read_blk(filp, info->dqi_free_entry, tmpbuf)) < 0) + goto out_buf; + ((struct disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk); + if ((err = write_blk(filp, info->dqi_free_entry, tmpbuf)) < 0) + goto out_buf; + } + freedqbuf(tmpbuf); + info->dqi_free_entry = blk; + mark_quotafile_info_dirty(info); + return 0; +out_buf: + freedqbuf(tmpbuf); + return err; +} + +/* Find space for dquot */ +static uint find_free_dqentry(struct dquot *dquot, int *err) +{ + struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; + struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info+dquot->dq_type; + uint blk, i; + struct disk_dqdbheader *dh; + struct disk_dqblk *ddquot; + struct disk_dqblk fakedquot; + dqbuf_t buf; + + *err = 0; + if (!(buf = getdqbuf())) { + *err = -ENOMEM; + return 0; + } + dh = (struct disk_dqdbheader *)buf; + ddquot = GETENTRIES(buf); + if (info->dqi_free_entry) { + blk = info->dqi_free_entry; + if ((*err = read_blk(filp, blk, buf)) < 0) + goto out_buf; + } + else { + blk = get_free_dqblk(filp, info); + if ((int)blk < 0) { + *err = blk; + return 0; + } + memset(buf, 0, DQBLKSIZE); + info->dqi_free_entry = blk; /* This is enough as block is already zeroed and entry list is empty... */ + mark_quotafile_info_dirty(info); + } + if (le16_to_cpu(dh->dqdh_entries)+1 >= DQSTRINBLK) /* Block will be full? */ + 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); + goto out_buf; + } + dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)+1); + memset(&fakedquot, 0, sizeof(struct disk_dqblk)); + /* Find free structure in block */ + for (i = 0; i < DQSTRINBLK && memcmp(&fakedquot, ddquot+i, sizeof(struct disk_dqblk)); i++); +#ifdef __DQUOT_PARANOIA + if (i == DQSTRINBLK) { + printk(KERN_ERR "VFS: find_free_dqentry(): Data block full but it shouldn't.\n"); + *err = -EIO; + goto out_buf; + } +#endif + if ((*err = write_blk(filp, blk, buf)) < 0) { + printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota data block %u.\n", blk); + goto out_buf; + } + dquot->dq_off = (blk<dq_sb)->files[dquot->dq_type]; + struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type; + dqbuf_t buf; + int ret = 0, newson = 0, newact = 0; + u32 *ref; + uint newblk; + + if (!(buf = getdqbuf())) + return -ENOMEM; + if (!*treeblk) { + ret = get_free_dqblk(filp, info); + if (ret < 0) + goto out_buf; + *treeblk = ret; + memset(buf, 0, DQBLKSIZE); + newact = 1; + } + else { + if ((ret = read_blk(filp, *treeblk, buf)) < 0) { + printk(KERN_ERR "VFS: Can't read tree quota block %u.\n", *treeblk); + goto out_buf; + } + } + ref = (u32 *)buf; + newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]); + if (!newblk) + newson = 1; + if (depth == DQTREEDEPTH-1) { +#ifdef __DQUOT_PARANOIA + if (newblk) { + printk(KERN_ERR "VFS: Inserting already present quota entry (block %u).\n", ref[GETIDINDEX(dquot->dq_id, depth)]); + ret = -EIO; + goto out_buf; + } +#endif + newblk = find_free_dqentry(dquot, &ret); + } + else + ret = do_insert_tree(dquot, &newblk, depth+1); + if (newson && ret >= 0) { + ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk); + ret = write_blk(filp, *treeblk, buf); + } + else if (newact && ret < 0) + put_free_dqblk(filp, info, buf, *treeblk); +out_buf: + freedqbuf(buf); + return ret; +} + +/* Wrapper for inserting quota structure into tree */ +static inline int dq_insert_tree(struct dquot *dquot) +{ + int tmp = DQTREEOFF; + return do_insert_tree(dquot, &tmp, 0); +} + +/* * We don't have to be afraid of deadlocks as we never have quotas on quota files... */ static void write_dquot(struct dquot *dquot) @@ -258,32 +639,199 @@ mm_segment_t fs; loff_t offset; ssize_t ret; - struct semaphore *sem = &dquot->dq_sb->s_dquot.dqio_sem; - struct dqblk dqbuf; + struct semaphore *sem = &sb_dqopt(dquot->dq_sb)->dqio_sem; + struct disk_dqblk ddquot; down(sem); - filp = dquot->dq_sb->s_dquot.files[type]; - offset = dqoff(dquot->dq_id); + if (!dquot->dq_off) + if ((ret = dq_insert_tree(dquot)) < 0) { + printk(KERN_ERR "VFS: Error %d occured while creating quota.\n", ret); + goto out_sem; + } + filp = sb_dqopt(dquot->dq_sb)->files[type]; + offset = dquot->dq_off; + mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id); fs = get_fs(); set_fs(KERNEL_DS); - - /* - * Note: clear the DQ_MOD flag unconditionally, - * so we don't loop forever on failure. - */ - memcpy(&dqbuf, &dquot->dq_dqb, sizeof(struct dqblk)); - dquot->dq_flags &= ~DQ_MOD; - ret = 0; - if (filp) - ret = filp->f_op->write(filp, (char *)&dqbuf, - sizeof(struct dqblk), &offset); - if (ret != sizeof(struct dqblk)) + ret = filp->f_op->write(filp, (char *)&ddquot, sizeof(struct disk_dqblk), &offset); + set_fs(fs); + if (ret != sizeof(struct disk_dqblk)) printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", kdevname(dquot->dq_dev)); - - set_fs(fs); - up(sem); dqstats.writes++; +out_sem: + up(sem); +} + +/* Free dquot entry in data block */ +static int free_dqentry(struct dquot *dquot, uint blk) +{ + struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; + struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type; + struct disk_dqdbheader *dh; + dqbuf_t buf = getdqbuf(); + int ret = 0; + + if (!buf) + return -ENOMEM; + if (dquot->dq_off >> 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 >> 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); + goto out_buf; + } + dh = (struct disk_dqdbheader *)buf; + dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)-1); + 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); + goto out_buf; + } + } + else { + memset(buf+(dquot->dq_off & ((1 << DQBLKSIZE_BITS)-1)), 0, sizeof(struct disk_dqblk)); + 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); + 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); + goto out_buf; + } + } + dquot->dq_off = 0; /* Quota is now unattached */ +out_buf: + freedqbuf(buf); + return ret; +} + +/* Remove reference to dquot from tree */ +static int remove_tree(struct dquot *dquot, uint *blk, int depth) +{ + struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; + struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type; + dqbuf_t buf = getdqbuf(); + int ret = 0; + uint newblk; + u32 *ref = (u32 *)buf; + + 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); + goto out_buf; + } + newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]); + if (depth == DQTREEDEPTH-1) { + ret = free_dqentry(dquot, newblk); + newblk = 0; + } + else + ret = remove_tree(dquot, &newblk, depth+1); + if (ret >= 0 && !newblk) { + int i; + ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0); + for (i = 0; i < DQBLKSIZE && !buf[i]; i++); /* Block got empty? */ + if (i == DQBLKSIZE) { + 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); + } +out_buf: + freedqbuf(buf); + return ret; +} + +/* Delete dquot from tree */ +static void delete_dquot(struct dquot *dquot) +{ + uint tmp = DQTREEOFF; + + if (!dquot->dq_off) /* Even not allocated? */ + return; + down(&sb_dqopt(dquot->dq_sb)->dqio_sem); + remove_tree(dquot, &tmp, 0); + up(&sb_dqopt(dquot->dq_sb)->dqio_sem); +} + +/* Find entry in block */ +static loff_t find_block_dqentry(struct dquot *dquot, uint blk) +{ + struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; + dqbuf_t buf = getdqbuf(); + loff_t ret = 0; + int i; + struct disk_dqblk *ddquot = GETENTRIES(buf); + + 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); + goto out_buf; + } + if (dquot->dq_id) + for (i = 0; i < DQSTRINBLK && le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++); + else { /* ID 0 as a bit more complicated searching... */ + struct disk_dqblk fakedquot; + + memset(&fakedquot, 0, sizeof(struct disk_dqblk)); + for (i = 0; i < DQSTRINBLK; i++) + if (!le32_to_cpu(ddquot[i].dqb_id) && memcmp(&fakedquot, ddquot+i, sizeof(struct disk_dqblk))) + break; + } + if (i == DQSTRINBLK) { + printk(KERN_ERR "VFS: Quota for id %u referenced but not present.\n", dquot->dq_id); + ret = -EIO; + goto out_buf; + } + else + ret = (blk << DQBLKSIZE_BITS) + sizeof(struct disk_dqdbheader) + i * sizeof(struct disk_dqblk); +out_buf: + freedqbuf(buf); + return ret; +} + +/* Find entry for given id in the tree */ +static loff_t find_tree_dqentry(struct dquot *dquot, uint blk, int depth) +{ + struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; + dqbuf_t buf = getdqbuf(); + loff_t ret = 0; + u32 *ref = (u32 *)buf; + + 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); + goto out_buf; + } + ret = 0; + blk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]); + if (!blk) /* No reference? */ + goto out_buf; + if (depth < DQTREEDEPTH-1) + ret = find_tree_dqentry(dquot, blk, depth+1); + else + ret = find_block_dqentry(dquot, blk); +out_buf: + freedqbuf(buf); + return ret; +} + +/* Find entry for given id in the tree - wrapper function */ +static inline loff_t find_dqentry(struct dquot *dquot) +{ + return find_tree_dqentry(dquot, DQTREEOFF, 0); } static void read_dquot(struct dquot *dquot) @@ -292,31 +840,61 @@ struct file *filp; mm_segment_t fs; loff_t offset; + struct disk_dqblk ddquot; - filp = dquot->dq_sb->s_dquot.files[type]; + filp = sb_dqopt(dquot->dq_sb)->files[type]; if (filp == (struct file *)NULL) return; lock_dquot(dquot); - if (!dquot->dq_sb) /* Invalidated quota? */ +#ifdef __DQUOT_PARANOIA + if (!dquot->dq_sb) { /* Invalidated quota? */ + printk(KERN_ERR "VFS: Quota invalidated while reading!\n"); goto out_lock; + } +#endif /* Now we are sure filp is valid - the dquot isn't invalidated */ - down(&dquot->dq_sb->s_dquot.dqio_sem); - offset = dqoff(dquot->dq_id); - fs = get_fs(); - set_fs(KERNEL_DS); - filp->f_op->read(filp, (char *)&dquot->dq_dqb, sizeof(struct dqblk), &offset); - up(&dquot->dq_sb->s_dquot.dqio_sem); - set_fs(fs); - - if (dquot->dq_bhardlimit == 0 && dquot->dq_bsoftlimit == 0 && - dquot->dq_ihardlimit == 0 && dquot->dq_isoftlimit == 0) + down(&sb_dqopt(dquot->dq_sb)->dqio_sem); + offset = find_dqentry(dquot); + if (offset <= 0) { /* Entry not present? */ + if (offset < 0) + printk(KERN_ERR "VFS: Can't read quota structure for id %u.\n", dquot->dq_id); + dquot->dq_off = 0; dquot->dq_flags |= DQ_FAKE; + memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); + } + else { + dquot->dq_off = offset; + fs = get_fs(); + set_fs(KERNEL_DS); + filp->f_op->read(filp, (char *)&ddquot, sizeof(struct disk_dqblk), &offset); + set_fs(fs); + disk2memdqb(&dquot->dq_dqb, &ddquot); + } + up(&sb_dqopt(dquot->dq_sb)->dqio_sem); dqstats.reads++; out_lock: unlock_dquot(dquot); } +/* Commit changes of dquot to disk - it might also mean deleting it when quota became fake one and user has no blocks... */ +static void commit_dquot(struct dquot *dquot) +{ + lock_dquot(dquot); + /* We clear the flag everytime so we don't loop when there was an IO error... */ + dquot->dq_flags &= ~DQ_MOD; + if (dquot->dq_flags & DQ_FAKE && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) + delete_dquot(dquot); + else + write_dquot(dquot); + unlock_dquot(dquot); +} + +/* + * End of IO functions + */ + + /* * Unhash and selectively clear the dquot structure, * but preserve the use count, list pointers, and @@ -331,8 +909,9 @@ dquot->dq_dev = NODEV; dquot->dq_type = -1; dquot->dq_flags = 0; + dquot->dq_off = 0; dquot->dq_referenced = 0; - memset(&dquot->dq_dqb, 0, sizeof(struct dqblk)); + memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); } /* Invalidate all dquots on the list, wait for all users. Note that this function is called @@ -389,7 +968,7 @@ if (dquot->dq_flags & DQ_LOCKED) wait_on_dquot(dquot); if (dquot->dq_flags & DQ_MOD) - write_dquot(dquot); + commit_dquot(dquot); dqput(dquot); goto restart; } @@ -442,16 +1021,17 @@ return; } if (dquot->dq_flags & DQ_MOD) { - write_dquot(dquot); + commit_dquot(dquot); goto we_slept; } - /* sanity check */ +#ifdef __DQUOT_PARANOIA if (!list_empty(&dquot->dq_free)) { printk(KERN_ERR "dqput: dquot already on free list??\n"); dquot->dq_count--; /* J.K. Just decrementing use count seems safer... */ return; } +#endif dquot->dq_count--; /* Place at end of LRU free queue */ put_dquot_last(dquot); @@ -483,7 +1063,7 @@ { unsigned int hashent = hashfn(sb->s_dev, id, type); struct dquot *dquot, *empty = NODQUOT; - struct quota_mount_options *dqopt = sb_dqopt(sb); + struct quota_info *dqopt = sb_dqopt(sb); we_slept: if (!is_enabled(dqopt, type)) { @@ -515,11 +1095,13 @@ dqput(empty); } +#ifdef __DQUOT_PARANOIA if (!dquot->dq_sb) { /* Has somebody invalidated entry under us? */ printk(KERN_ERR "VFS: dqget(): Quota invalidated in dqget()!\n"); dqput(dquot); return NODQUOT; } +#endif dquot->dq_referenced++; dqstats.lookups++; @@ -531,6 +1113,7 @@ if (dquot == NODQUOT) return NODQUOT; dquot->dq_count++; +#ifdef __DQUOT_PARANOIA if (!dquot->dq_sb) { printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n"); dquot->dq_count--; @@ -538,6 +1121,7 @@ } if (dquot->dq_flags & DQ_LOCKED) printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n"); +#endif dquot->dq_referenced++; dqstats.lookups++; return dquot; @@ -547,7 +1131,7 @@ static inline int is_quotafile(struct inode *inode) { int cnt; - struct quota_mount_options *dqopt = sb_dqopt(inode->i_sb); + struct quota_info *dqopt = sb_dqopt(inode->i_sb); struct file **files; if (!dqopt) @@ -658,9 +1242,9 @@ dquot->dq_flags |= DQ_MOD; } -static inline void dquot_incr_blocks(struct dquot *dquot, unsigned long number) +static inline void dquot_incr_space(struct dquot *dquot, qsize_t number) { - dquot->dq_curblocks += number; + dquot->dq_curspace += number; dquot->dq_flags |= DQ_MOD; } @@ -676,13 +1260,13 @@ dquot->dq_flags |= DQ_MOD; } -static inline void dquot_decr_blocks(struct dquot *dquot, unsigned long number) +static inline void dquot_decr_space(struct dquot *dquot, qsize_t number) { - if (dquot->dq_curblocks > number) - dquot->dq_curblocks -= number; + if (dquot->dq_curspace > number) + dquot->dq_curspace -= number; else - dquot->dq_curblocks = 0; - if (dquot->dq_curblocks < dquot->dq_bsoftlimit) + dquot->dq_curspace = 0; + if (toqb(dquot->dq_curspace) < dquot->dq_bsoftlimit) dquot->dq_btime = (time_t) 0; dquot->dq_flags &= ~DQ_BLKS; dquot->dq_flags |= DQ_MOD; @@ -723,7 +1307,7 @@ tty_write_message(current->tty, ": warning, "); else tty_write_message(current->tty, ": write failed, "); - tty_write_message(current->tty, quotatypes[dquot->dq_type]); + tty_write_message(current->tty, (char *)quotatypes[dquot->dq_type]); switch (warntype) { case IHARDWARN: msg = " file limit reached.\n"; @@ -758,7 +1342,7 @@ static inline char ignore_hardlimit(struct dquot *dquot) { - return capable(CAP_SYS_RESOURCE) && !dquot->dq_sb->s_dquot.rsquash[dquot->dq_type]; + return capable(CAP_SYS_RESOURCE); } static int check_idq(struct dquot *dquot, ulong inodes, char *warntype) @@ -786,20 +1370,20 @@ (dquot->dq_curinodes + inodes) > dquot->dq_isoftlimit && dquot->dq_itime == 0) { *warntype = ISOFTWARN; - dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[dquot->dq_type]; + dquot->dq_itime = CURRENT_TIME + (__kernel_time_t)(sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_igrace); } return QUOTA_OK; } -static int check_bdq(struct dquot *dquot, ulong blocks, char prealloc, char *warntype) +static int check_bdq(struct dquot *dquot, qsize_t space, char prealloc, char *warntype) { *warntype = 0; - if (blocks <= 0 || dquot->dq_flags & DQ_FAKE) + if (space <= 0 || dquot->dq_flags & DQ_FAKE) return QUOTA_OK; if (dquot->dq_bhardlimit && - (dquot->dq_curblocks + blocks) > dquot->dq_bhardlimit && + toqb(dquot->dq_curspace + space) > dquot->dq_bhardlimit && !ignore_hardlimit(dquot)) { if (!prealloc) *warntype = BHARDWARN; @@ -807,7 +1391,7 @@ } if (dquot->dq_bsoftlimit && - (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit && + toqb(dquot->dq_curspace + space) > dquot->dq_bsoftlimit && dquot->dq_btime && CURRENT_TIME >= dquot->dq_btime && !ignore_hardlimit(dquot)) { if (!prealloc) @@ -816,11 +1400,11 @@ } if (dquot->dq_bsoftlimit && - (dquot->dq_curblocks + blocks) > dquot->dq_bsoftlimit && + toqb(dquot->dq_curspace + space) > dquot->dq_bsoftlimit && dquot->dq_btime == 0) { if (!prealloc) { *warntype = BSOFTWARN; - dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[dquot->dq_type]; + dquot->dq_btime = CURRENT_TIME + (__kernel_time_t)(sb_dqopt(dquot->dq_sb)->info[dquot->dq_type].dqi_bgrace); } else /* @@ -837,18 +1421,18 @@ * Initialize a dquot-struct with new quota info. This is used by the * system call interface functions. */ -static int set_dqblk(struct super_block *sb, int id, short type, int flags, struct dqblk *dqblk) +static int set_dqblk(struct super_block *sb, qid_t id, short type, int flags, struct mem_dqblk *dqblk) { struct dquot *dquot; int error = -EFAULT; - struct dqblk dq_dqblk; + struct mem_dqblk dq_dqblk; - if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct dqblk))) + if (copy_from_user(&dq_dqblk, dqblk, sizeof(struct mem_dqblk))) return error; if (sb && (dquot = dqget(sb, id, type)) != NODQUOT) { /* We can't block while changing quota structure... */ - if (id > 0 && ((flags & SET_QUOTA) || (flags & SET_QLIMIT))) { + if ((flags & SET_QUOTA) || (flags & SET_QLIMIT)) { dquot->dq_bhardlimit = dq_dqblk.dqb_bhardlimit; dquot->dq_bsoftlimit = dq_dqblk.dqb_bsoftlimit; dquot->dq_ihardlimit = dq_dqblk.dqb_ihardlimit; @@ -859,22 +1443,22 @@ if (dquot->dq_isoftlimit && dquot->dq_curinodes < dquot->dq_isoftlimit && dq_dqblk.dqb_curinodes >= dquot->dq_isoftlimit) - dquot->dq_itime = CURRENT_TIME + dquot->dq_sb->s_dquot.inode_expire[type]; + dquot->dq_itime = CURRENT_TIME + (__kernel_time_t)(sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace); dquot->dq_curinodes = dq_dqblk.dqb_curinodes; - if (dquot->dq_curinodes < dquot->dq_isoftlimit) - dquot->dq_flags &= ~DQ_INODES; if (dquot->dq_bsoftlimit && - dquot->dq_curblocks < dquot->dq_bsoftlimit && - dq_dqblk.dqb_curblocks >= dquot->dq_bsoftlimit) - dquot->dq_btime = CURRENT_TIME + dquot->dq_sb->s_dquot.block_expire[type]; - dquot->dq_curblocks = dq_dqblk.dqb_curblocks; - if (dquot->dq_curblocks < dquot->dq_bsoftlimit) - dquot->dq_flags &= ~DQ_BLKS; + toqb(dquot->dq_curspace) < dquot->dq_bsoftlimit && + toqb(dq_dqblk.dqb_curspace) >= dquot->dq_bsoftlimit) + dquot->dq_btime = CURRENT_TIME + (__kernel_time_t)(sb_dqopt(dquot->dq_sb)->info[type].dqi_bgrace); + dquot->dq_curspace = dq_dqblk.dqb_curspace; } - if (id == 0) { - dquot->dq_sb->s_dquot.block_expire[type] = dquot->dq_btime = dq_dqblk.dqb_btime; - dquot->dq_sb->s_dquot.inode_expire[type] = dquot->dq_itime = dq_dqblk.dqb_itime; + if (dquot->dq_curinodes < dquot->dq_isoftlimit || !dquot->dq_isoftlimit) { + dquot->dq_itime = 0; + dquot->dq_flags &= ~DQ_INODES; + } + if (toqb(dquot->dq_curspace) < dquot->dq_bsoftlimit || !dquot->dq_bsoftlimit) { + dquot->dq_btime = 0; + dquot->dq_flags &= ~DQ_BLKS; } if (dq_dqblk.dqb_bhardlimit == 0 && dq_dqblk.dqb_bsoftlimit == 0 && @@ -889,10 +1473,10 @@ return 0; } -static int get_quota(struct super_block *sb, int id, short type, struct dqblk *dqblk) +static int get_quota(struct super_block *sb, qid_t id, short type, struct mem_dqblk *dqblk) { struct dquot *dquot; - struct dqblk data; + struct mem_dqblk data; int error = -ESRCH; if (!sb || !sb_has_quota_enabled(sb, type)) @@ -901,10 +1485,10 @@ if (dquot == NODQUOT) goto out; - memcpy(&data, &dquot->dq_dqb, sizeof(struct dqblk)); /* We copy data to preserve them from changing */ + memcpy(&data, &dquot->dq_dqb, sizeof(struct mem_dqblk)); /* We copy data to preserve them from changing */ dqput(dquot); error = -EFAULT; - if (dqblk && !copy_to_user(dqblk, &data, sizeof(struct dqblk))) + if (dqblk && !copy_to_user(dqblk, &data, sizeof(struct mem_dqblk))) error = 0; out: return error; @@ -925,46 +1509,48 @@ return error; } -static int quota_root_squash(struct super_block *sb, short type, int *addr) +static int get_info(struct super_block *sb, short type, struct mem_dqinfo *pinfo) { - int new_value, error; - - if (!sb) - return(-ENODEV); - - error = -EFAULT; - if (!copy_from_user(&new_value, addr, sizeof(int))) { - sb_dqopt(sb)->rsquash[type] = new_value; - error = 0; - } - return error; + struct mem_dqinfo kinfo; + + if (!sb || !sb_has_quota_enabled(sb, type)) + return -ESRCH; + /* Make our own copy so we can guarantee consistent structure */ + memcpy(&kinfo, sb_dqopt(sb)->info+type, sizeof(struct mem_dqinfo)); + kinfo.dqi_flags &= DQF_MASK; + if (copy_to_user(pinfo, &kinfo, sizeof(struct mem_dqinfo))) + return -EFAULT; + return 0; } -#if 0 /* We are not going to support filesystems without i_blocks... */ -/* - * This is a simple algorithm that calculates the size of a file in blocks. - * This is only used on filesystems that do not have an i_blocks count. - */ -static u_long isize_to_blocks(loff_t isize, size_t blksize_bits) +static int set_info(int op, struct super_block *sb, short type, struct mem_dqinfo *pinfo) { - u_long blocks; - u_long indirect; + struct mem_dqinfo info; + struct quota_info *dqopt = sb_dqopt(sb); - if (!blksize_bits) - blksize_bits = BLOCK_SIZE_BITS; - blocks = (isize >> blksize_bits) + ((isize & ~((1 << blksize_bits)-1)) ? 1 : 0); - if (blocks > 10) { - indirect = ((blocks - 11) >> 8) + 1; /* single indirect blocks */ - if (blocks > (10 + 256)) { - indirect += ((blocks - 267) >> 16) + 1; /* double indirect blocks */ - if (blocks > (10 + 256 + (256 << 8))) - indirect++; /* triple indirect blocks */ - } - blocks += indirect; - } - return blocks; + if (!sb || !sb_has_quota_enabled(sb, type)) + return -ESRCH; + + if (copy_from_user(&info, pinfo, sizeof(struct mem_dqinfo))) + return -EFAULT; + + switch (op) { + case ISET_FLAGS: + dqopt->info[type].dqi_flags = (dqopt->info[type].dqi_flags & ~DQF_MASK) + | (info.dqi_flags & DQF_MASK); + break; + case ISET_GRACE: + dqopt->info[type].dqi_bgrace = info.dqi_bgrace; + dqopt->info[type].dqi_igrace = info.dqi_igrace; + break; + case ISET_ALL: + info.dqi_flags &= ~DQF_MASK; + memcpy(dqopt->info + type, &info, sizeof(struct mem_dqinfo)); + break; + } + mark_quotafile_info_dirty(dqopt->info + type); + return 0; } -#endif /* * Externally referenced functions through dquot_operations in inode. @@ -974,7 +1560,7 @@ void dquot_initialize(struct inode *inode, short type) { struct dquot *dquot[MAXQUOTAS]; - unsigned int id = 0; + qid_t id = 0; short cnt; if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode) && @@ -1042,7 +1628,7 @@ /* * This operation can block, but only after everything is updated */ -int dquot_alloc_block(struct inode *inode, unsigned long number, char warn) +int dquot_alloc_space(struct inode *inode, qsize_t number, char prealloc) { int cnt, ret = NO_QUOTA; struct dquot *dquot[MAXQUOTAS]; @@ -1056,15 +1642,15 @@ dquot[cnt] = dqduplicate(inode->i_dquot[cnt]); if (dquot[cnt] == NODQUOT) continue; - if (check_bdq(dquot[cnt], number, warn, warntype+cnt) == NO_QUOTA) + if (check_bdq(dquot[cnt], number, prealloc, warntype+cnt) == NO_QUOTA) goto warn_put_all; } for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (dquot[cnt] == NODQUOT) continue; - dquot_incr_blocks(dquot[cnt], number); + dquot_incr_space(dquot[cnt], number); } - inode->i_blocks += number << (BLOCK_SIZE_BITS - 9); + inode_add_bytes(inode, number); mark_inode_dirty(inode); ret = QUOTA_OK; warn_put_all: @@ -1113,7 +1699,7 @@ /* * This is a non-blocking operation. */ -void dquot_free_block(struct inode *inode, unsigned long number) +void dquot_free_space(struct inode *inode, qsize_t number) { unsigned short cnt; struct dquot *dquot; @@ -1122,10 +1708,10 @@ dquot = dqduplicate(inode->i_dquot[cnt]); if (dquot == NODQUOT) continue; - dquot_decr_blocks(dquot, number); + dquot_decr_space(dquot, number); dqput(dquot); } - inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9); + inode_sub_bytes(inode, number); mark_inode_dirty(inode); } @@ -1153,7 +1739,7 @@ */ int dquot_transfer(struct inode *inode, struct iattr *iattr) { - unsigned long blocks; + qsize_t space; struct dquot *transfer_from[MAXQUOTAS]; struct dquot *transfer_to[MAXQUOTAS]; int cnt, ret = NO_QUOTA, chuid = (iattr->ia_valid & ATTR_UID) && inode->i_uid != iattr->ia_uid, @@ -1183,7 +1769,7 @@ } } /* NOBLOCK START: From now on we shouldn't block */ - blocks = (inode->i_blocks >> 1); + space = inode_get_bytes(inode); /* Build the transfer_from list and check the limits */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { /* The second test can fail when quotaoff is in progress... */ @@ -1193,7 +1779,7 @@ if (transfer_from[cnt] == NODQUOT) /* Can happen on quotafiles (quota isn't initialized on them)... */ continue; if (check_idq(transfer_to[cnt], 1, warntype+cnt) == NO_QUOTA || - check_bdq(transfer_to[cnt], blocks, 0, warntype+cnt) == NO_QUOTA) + check_bdq(transfer_to[cnt], space, 0, warntype+cnt) == NO_QUOTA) goto warn_put_all; } @@ -1208,10 +1794,10 @@ continue; dquot_decr_inodes(transfer_from[cnt], 1); - dquot_decr_blocks(transfer_from[cnt], blocks); + dquot_decr_space(transfer_from[cnt], space); dquot_incr_inodes(transfer_to[cnt], 1); - dquot_incr_blocks(transfer_to[cnt], blocks); + dquot_incr_space(transfer_to[cnt], space); if (inode->i_dquot[cnt] == NODQUOT) BUG(); @@ -1251,14 +1837,14 @@ struct dquot_operations dquot_operations = { dquot_initialize, /* mandatory */ dquot_drop, /* mandatory */ - dquot_alloc_block, + dquot_alloc_space, dquot_alloc_inode, - dquot_free_block, + dquot_free_space, dquot_free_inode, dquot_transfer }; -static inline void set_enable_flags(struct quota_mount_options *dqopt, short type) +static inline void set_enable_flags(struct quota_info *dqopt, short type) { switch (type) { case USRQUOTA: @@ -1270,7 +1856,7 @@ } } -static inline void reset_enable_flags(struct quota_mount_options *dqopt, short type) +static inline void reset_enable_flags(struct quota_info *dqopt, short type) { switch (type) { case USRQUOTA: @@ -1292,7 +1878,7 @@ { struct file *filp; short cnt; - struct quota_mount_options *dqopt = sb_dqopt(sb); + struct quota_info *dqopt = sb_dqopt(sb); if (!sb) goto out; @@ -1309,11 +1895,12 @@ /* Note: these are blocking operations */ remove_dquot_ref(sb->s_dev, cnt); invalidate_dquots(sb->s_dev, cnt); - + /* When invalidate is finished there are no users of any dquot of our interest... */ + if (quotafile_info_dirty(sb_dqopt(sb)->info+cnt)) + write_quotafile_info(sb, cnt); filp = dqopt->files[cnt]; dqopt->files[cnt] = (struct file *)NULL; - dqopt->inode_expire[cnt] = 0; - dqopt->block_expire[cnt] = 0; + memset(dqopt->info+cnt, 0, sizeof(struct mem_dqinfo)); fput(filp); } up(&dqopt->dqoff_sem); @@ -1321,20 +1908,31 @@ return 0; } -static inline int check_quotafile_size(loff_t size) +static int check_quotafile(struct file *f, short type) { - ulong blocks = size >> BLOCK_SIZE_BITS; - size_t off = size & (BLOCK_SIZE - 1); - - return !(((blocks % sizeof(struct dqblk)) * BLOCK_SIZE + off % sizeof(struct dqblk)) % sizeof(struct dqblk)); + struct disk_dqheader dqhead; + mm_segment_t fs; + ssize_t size; + loff_t offset = 0; + + fs = get_fs(); + set_fs(KERNEL_DS); + size = f->f_op->read(f, (char *)&dqhead, sizeof(struct disk_dqheader), &offset); + set_fs(fs); + if (size != sizeof(struct disk_dqheader)) + return 0; + if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] || + le32_to_cpu(dqhead.dqh_version) != quota_versions[type]) + return 0; + return 1; } +/* Turn quotas of given type on */ static int quota_on(struct super_block *sb, short type, char *path) { struct file *f; struct inode *inode; - struct dquot *dquot; - struct quota_mount_options *dqopt = sb_dqopt(sb); + struct quota_info *dqopt = sb_dqopt(sb); char *tmp; int error; @@ -1361,24 +1959,21 @@ if (!S_ISREG(inode->i_mode)) goto out_f; error = -EINVAL; - if (inode->i_size == 0 || !check_quotafile_size(inode->i_size)) + if (inode->i_size == 0 || !check_quotafile(f, type)) goto out_f; dquot_drop(inode); /* We don't want quota on quota files - open might initialize the other quota type... */ dqopt->files[type] = f; - set_enable_flags(dqopt, type); - - dquot = dqget(sb, 0, type); - dqopt->inode_expire[type] = (dquot != NODQUOT) ? dquot->dq_itime : MAX_IQ_TIME; - dqopt->block_expire[type] = (dquot != NODQUOT) ? dquot->dq_btime : MAX_DQ_TIME; - dqput(dquot); - + if (read_quotafile_info(sb, type) < 0) /* Read header from file - OK? */ + goto out_f_tab; sb->dq_op = &dquot_operations; + set_enable_flags(dqopt, type); add_dquot_ref(sb, type); up(&dqopt->dqoff_sem); return 0; - +out_f_tab: + dqopt->files[type] = NULL; out_f: filp_close(f, NULL); out_lock: @@ -1393,7 +1988,7 @@ * calls. Maybe we need to add the process quotas etc. in the future, * but we probably should use rlimits for that. */ -asmlinkage long sys_quotactl(int cmd, const char *special, int id, caddr_t addr) +asmlinkage long sys_quotactl(int cmd, const char *special, qid_t id, __kernel_caddr_t addr) { int cmds = 0, type = 0, flags = 0; kdev_t dev; @@ -1404,9 +1999,9 @@ cmds = cmd >> SUBCMDSHIFT; type = cmd & SUBCMDMASK; - if ((u_int) type >= MAXQUOTAS) - goto out; - if (id & ~0xFFFF) + + if ((uint) type >= MAXQUOTAS || cmds > 0x0F00 || cmds < 0x100 || cmds == 0x0300 || + cmds == 0x0400 || cmds == 0x0500) goto out; ret = -EPERM; @@ -1425,7 +2020,6 @@ goto out; } - ret = -EINVAL; dev = NODEV; if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) { mode_t mode; @@ -1457,7 +2051,7 @@ ret = quota_off(sb, type); goto out; case Q_GETQUOTA: - ret = get_quota(sb, id, type, (struct dqblk *) addr); + ret = get_quota(sb, id, type, (struct mem_dqblk *) addr); goto out; case Q_SETQUOTA: flags |= SET_QUOTA; @@ -1474,16 +2068,25 @@ case Q_GETSTATS: ret = get_stats(addr); goto out; - case Q_RSQUASH: - ret = quota_root_squash(sb, type, (int *) addr); + case Q_GETINFO: + ret = get_info(sb, type, (struct mem_dqinfo *) addr); + goto out; + case Q_SETFLAGS: + ret = set_info(ISET_FLAGS, sb, type, (struct mem_dqinfo *)addr); + goto out; + case Q_SETGRACE: + ret = set_info(ISET_GRACE, sb, type, (struct mem_dqinfo *)addr); goto out; + case Q_SETINFO: + ret = set_info(ISET_ALL, sb, type, (struct mem_dqinfo *)addr); + goto out; default: goto out; } ret = -NODEV; if (sb && sb_has_quota_enabled(sb, type)) - ret = set_dqblk(sb, id, type, flags, (struct dqblk *) addr); + ret = set_dqblk(sb, id, type, flags, (struct mem_dqblk *) addr); out: unlock_kernel(); return ret; diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/fs/inode.c linux-2.4.0-test11-newquota/fs/inode.c --- linux-2.4.0-test11-quotafix/fs/inode.c Fri Nov 3 22:07:22 2000 +++ linux-2.4.0-test11-newquota/fs/inode.c Mon Nov 27 10:08:00 2000 @@ -523,6 +523,8 @@ inode->i_nlink = 1; atomic_set(&inode->i_writecount, 0); inode->i_size = 0; + inode->i_blocks = 0; + inode->i_bytes = 0; inode->i_generation = 0; memset(&inode->i_dquot, 0, sizeof(inode->i_dquot)); inode->i_pipe = NULL; diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/fs/ioctl.c linux-2.4.0-test11-newquota/fs/ioctl.c --- linux-2.4.0-test11-quotafix/fs/ioctl.c Fri Oct 6 00:10:57 2000 +++ linux-2.4.0-test11-newquota/fs/ioctl.c Sun Dec 3 21:11:41 2000 @@ -100,6 +100,15 @@ filp->f_flags &= ~FASYNC; break; + case FIOQSIZE: + if (S_ISDIR(filp->f_dentry->d_inode->i_mode) || + S_ISREG(filp->f_dentry->d_inode->i_mode) || + S_ISLNK(filp->f_dentry->d_inode->i_mode)) + *(loff_t *)arg = inode_get_bytes(filp->f_dentry->d_inode); + else + error = -ENOTTY; + break; + default: error = -ENOTTY; if (S_ISREG(filp->f_dentry->d_inode->i_mode)) diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-alpha/ioctls.h linux-2.4.0-test11-newquota/include/asm-alpha/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-alpha/ioctls.h Fri Oct 6 00:11:06 2000 +++ linux-2.4.0-test11-newquota/include/asm-alpha/ioctls.h Sun Dec 3 21:12:21 2000 @@ -9,6 +9,7 @@ #define FIONBIO _IOW('f', 126, int) #define FIONREAD _IOR('f', 127, int) #define TIOCINQ FIONREAD +#define FIOQSIZE _IOR('f', 128, loff_t) #define TIOCGETP _IOR('t', 8, struct sgttyb) #define TIOCSETP _IOW('t', 9, struct sgttyb) diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-arm/ioctls.h linux-2.4.0-test11-newquota/include/asm-arm/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-arm/ioctls.h Fri Oct 6 00:11:09 2000 +++ linux-2.4.0-test11-newquota/include/asm-arm/ioctls.h Sun Dec 3 21:12:21 2000 @@ -65,6 +65,7 @@ #define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define FIOQSIZE 0x545E /* Used for packet mode */ #define TIOCPKT_DATA 0 diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-i386/ioctls.h linux-2.4.0-test11-newquota/include/asm-i386/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-i386/ioctls.h Fri Oct 6 00:11:04 2000 +++ linux-2.4.0-test11-newquota/include/asm-i386/ioctls.h Sun Dec 3 21:12:21 2000 @@ -67,6 +67,7 @@ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ #define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ #define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ +#define FIOQSIZE 0x5460 /* Used for packet mode */ #define TIOCPKT_DATA 0 diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-ia64/ioctls.h linux-2.4.0-test11-newquota/include/asm-ia64/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-ia64/ioctls.h Fri Oct 6 00:11:14 2000 +++ linux-2.4.0-test11-newquota/include/asm-ia64/ioctls.h Sun Dec 3 21:12:21 2000 @@ -72,6 +72,7 @@ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ #define TIOCGHAYESESP 0x545E /* Get Hayes ESP configuration */ #define TIOCSHAYESESP 0x545F /* Set Hayes ESP configuration */ +#define FIOQSIZE 0x5460 /* Used for packet mode */ #define TIOCPKT_DATA 0 diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-m68k/ioctls.h linux-2.4.0-test11-newquota/include/asm-m68k/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-m68k/ioctls.h Fri Oct 6 00:11:08 2000 +++ linux-2.4.0-test11-newquota/include/asm-m68k/ioctls.h Sun Dec 3 21:12:21 2000 @@ -65,6 +65,7 @@ #define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define FIOQSIZE 0x545E /* Used for packet mode */ #define TIOCPKT_DATA 0 diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-mips/ioctls.h linux-2.4.0-test11-newquota/include/asm-mips/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-mips/ioctls.h Fri Oct 6 00:11:05 2000 +++ linux-2.4.0-test11-newquota/include/asm-mips/ioctls.h Sun Dec 3 21:12:21 2000 @@ -58,6 +58,7 @@ #define FIONCLEX 0x6602 /* these numbers need to be adjusted. */ #define FIOASYNC 0x667d #define FIONBIO 0x667e +#define FIOQSIZE 0x667f #if defined(__USE_MISC) || defined (__KERNEL__) #define TIOCGLTC (tIOC | 116) /* get special local chars */ diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-mips64/ioctls.h linux-2.4.0-test11-newquota/include/asm-mips64/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-mips64/ioctls.h Fri Oct 6 00:11:15 2000 +++ linux-2.4.0-test11-newquota/include/asm-mips64/ioctls.h Sun Dec 3 21:12:21 2000 @@ -58,6 +58,7 @@ #define FIONCLEX 0x6602 /* these numbers need to be adjusted. */ #define FIOASYNC 0x667d #define FIONBIO 0x667e +#define FIOQSIZE 0x667f #if defined(__USE_MISC) || defined (__KERNEL__) #define TIOCGLTC (tIOC | 116) /* get special local chars */ diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-ppc/ioctls.h linux-2.4.0-test11-newquota/include/asm-ppc/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-ppc/ioctls.h Fri Oct 6 00:11:09 2000 +++ linux-2.4.0-test11-newquota/include/asm-ppc/ioctls.h Sun Dec 3 21:12:21 2000 @@ -9,6 +9,7 @@ #define FIONBIO _IOW('f', 126, int) #define FIONREAD _IOR('f', 127, int) #define TIOCINQ FIONREAD +#define FIOQSIZE _IOR('f', 128, loff_t) #define TIOCGETP _IOR('t', 8, struct sgttyb) #define TIOCSETP _IOW('t', 9, struct sgttyb) diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-ppc/termios.h linux-2.4.0-test11-newquota/include/asm-ppc/termios.h --- linux-2.4.0-test11-quotafix/include/asm-ppc/termios.h Fri Oct 6 00:11:09 2000 +++ linux-2.4.0-test11-newquota/include/asm-ppc/termios.h Sun Dec 3 21:12:21 2000 @@ -43,6 +43,7 @@ #define FIONBIO _IOW('f', 126, int) #define FIONREAD _IOR('f', 127, int) #define TIOCINQ FIONREAD +#define FIOQSIZE _IOR('f', 128, loff_t) #define TIOCGETP _IOR('t', 8, struct sgttyb) #define TIOCSETP _IOW('t', 9, struct sgttyb) diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-s390/ioctls.h linux-2.4.0-test11-newquota/include/asm-s390/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-s390/ioctls.h Fri Oct 6 00:11:16 2000 +++ linux-2.4.0-test11-newquota/include/asm-s390/ioctls.h Sun Dec 3 21:12:21 2000 @@ -73,6 +73,7 @@ #define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */ #define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */ +#define FIOQSIZE 0x545E /* Used for packet mode */ #define TIOCPKT_DATA 0 diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-sh/ioctls.h linux-2.4.0-test11-newquota/include/asm-sh/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-sh/ioctls.h Fri Oct 6 00:11:11 2000 +++ linux-2.4.0-test11-newquota/include/asm-sh/ioctls.h Sun Dec 3 21:12:21 2000 @@ -9,6 +9,7 @@ #define FIONBIO _IOW('f', 126, int) #define FIONREAD _IOR('f', 127, int) #define TIOCINQ FIONREAD +#define FIOQSIZE _IOR('f', 128, loff_t) #define TCGETS 0x5401 #define TCSETS 0x5402 diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-sparc/ioctls.h linux-2.4.0-test11-newquota/include/asm-sparc/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-sparc/ioctls.h Fri Oct 6 00:11:08 2000 +++ linux-2.4.0-test11-newquota/include/asm-sparc/ioctls.h Sun Dec 3 21:12:21 2000 @@ -86,6 +86,7 @@ #define FIONBIO _IOW('f', 126, int) #define FIONREAD _IOR('f', 127, int) #define TIOCINQ FIONREAD +#define FIOQSIZE _IOR('f', 128, loff_t) /* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it * someday. This is completely bogus, I know... diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/asm-sparc64/ioctls.h linux-2.4.0-test11-newquota/include/asm-sparc64/ioctls.h --- linux-2.4.0-test11-quotafix/include/asm-sparc64/ioctls.h Fri Oct 6 00:11:09 2000 +++ linux-2.4.0-test11-newquota/include/asm-sparc64/ioctls.h Sun Dec 3 21:12:21 2000 @@ -87,6 +87,7 @@ #define FIONBIO _IOW('f', 126, int) #define FIONREAD _IOR('f', 127, int) #define TIOCINQ FIONREAD +#define FIOQSIZE _IOR('f', 128, loff_t) /* SCARY Rutgers local SunOS kernel hackery, perhaps I will support it * someday. This is completely bogus, I know... diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/linux/fs.h linux-2.4.0-test11-newquota/include/linux/fs.h --- linux-2.4.0-test11-quotafix/include/linux/fs.h Mon Nov 27 09:43:43 2000 +++ linux-2.4.0-test11-newquota/include/linux/fs.h Sun Dec 3 21:13:00 2000 @@ -398,6 +398,7 @@ unsigned long i_blksize; unsigned long i_blocks; unsigned long i_version; + unsigned short i_bytes; struct semaphore i_sem; struct semaphore i_zombie; struct inode_operations *i_op; @@ -464,6 +465,39 @@ __mark_inode_dirty(inode); } +static inline void inode_add_bytes(struct inode *inode, loff_t bytes) +{ + inode->i_blocks += bytes >> 9; + bytes &= 511; + inode->i_bytes += bytes; + if (inode->i_bytes >= 512) { + inode->i_blocks++; + inode->i_bytes -= 512; + } +} + +static inline void inode_sub_bytes(struct inode *inode, loff_t bytes) +{ + inode->i_blocks -= bytes >> 9; + bytes &= 511; + if (inode->i_bytes < bytes) { + inode->i_blocks--; + inode->i_bytes += 512; + } + inode->i_bytes -= bytes; +} + +static inline loff_t inode_get_bytes(struct inode *inode) +{ + return (((loff_t)inode->i_blocks) << 9) + inode->i_bytes; +} + +static inline void inode_set_bytes(struct inode *inode, loff_t bytes) +{ + inode->i_blocks = bytes >> 9; + inode->i_bytes = bytes & 511; +} + struct fown_struct { int pid; /* pid or -pgrp where SIGIO should be sent */ uid_t uid, euid; /* uid/euid of process setting the owner */ @@ -596,15 +630,13 @@ #define DQUOT_USR_ENABLED 0x01 /* User diskquotas enabled */ #define DQUOT_GRP_ENABLED 0x02 /* Group diskquotas enabled */ -struct quota_mount_options +struct quota_info { unsigned int flags; /* Flags for diskquotas on this device */ struct semaphore dqio_sem; /* lock device while I/O in progress */ struct semaphore dqoff_sem; /* serialize quota_off() and quota_on() on device */ struct file *files[MAXQUOTAS]; /* fp's to quotafiles */ - time_t inode_expire[MAXQUOTAS]; /* expiretime for inode-quota */ - time_t block_expire[MAXQUOTAS]; /* expiretime for block-quota */ - char rsquash[MAXQUOTAS]; /* for quotas threat root as any other user */ + struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */ }; /* @@ -657,7 +689,7 @@ struct block_device *s_bdev; struct list_head s_mounts; /* vfsmount(s) of this one */ - struct quota_mount_options s_dquot; /* Diskquota specific options */ + struct quota_info s_dquot; /* Diskquota specific options */ union { struct minix_sb_info minix_sb; @@ -803,9 +835,9 @@ struct dquot_operations { void (*initialize) (struct inode *, short); void (*drop) (struct inode *); - int (*alloc_block) (struct inode *, unsigned long, char); + int (*alloc_space) (struct inode *, qsize_t, char); int (*alloc_inode) (const struct inode *, unsigned long); - void (*free_block) (struct inode *, unsigned long); + void (*free_space) (struct inode *, qsize_t); void (*free_inode) (const struct inode *, unsigned long); int (*transfer) (struct inode *, struct iattr *); }; diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/linux/quota.h linux-2.4.0-test11-newquota/include/linux/quota.h --- linux-2.4.0-test11-quotafix/include/linux/quota.h Mon Nov 27 09:43:43 2000 +++ linux-2.4.0-test11-newquota/include/linux/quota.h Mon Nov 27 10:13:33 2000 @@ -40,30 +40,10 @@ #define _LINUX_QUOTA_ #include +#include -/* - * Convert diskblocks to blocks and the other way around. - */ -#define dbtob(num) (num << BLOCK_SIZE_BITS) -#define btodb(num) (num >> BLOCK_SIZE_BITS) - -/* - * Convert count of filesystem blocks to diskquota blocks, meant - * for filesystems where i_blksize != BLOCK_SIZE - */ -#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE) - -/* - * Definitions for disk quotas imposed on the average user - * (big brother finally hits Linux). - * - * The following constants define the amount of time given a user - * before the soft limits are treated as hard limits (usually resulting - * in an allocation failure). The timer is started when the user crosses - * their soft limit, it is reset when they go below their soft limit. - */ -#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */ -#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */ +typedef __kernel_uid32_t qid_t; /* Type in which we store ids in memory */ +typedef __u64 qsize_t; /* Type in which we store size limitations */ #define MAXQUOTAS 2 #define USRQUOTA 0 /* element used for user quotas */ @@ -76,11 +56,33 @@ "user", /* USRQUOTA */ \ "group", /* GRPQUOTA */ \ "undefined", \ -}; +} + +/* + * Definitions of magics and versions of current quota files + */ +#define INITQMAGICS {\ + 0xd9c01f11, /* USRQUOTA */\ + 0xd9c01927 /* GRPQUOTA */\ +} -#define QUOTAFILENAME "quota" +#define INITQVERSIONS {\ + 0, /* USRQUOTA */\ + 0 /* GRPQUOTA */\ +} + +#define QUOTAFILENAME "aquota" #define QUOTAGROUP "staff" +/* Size of blocks in which are counted size limits */ +#define QUOTABLOCK_BITS 10 +#define QUOTABLOCK_SIZE (1 << QUOTABLOCK_BITS) + +/* Conversion routines from and to quota blocks */ +#define qb2kb(x) ((x) << (QUOTABLOCK_BITS-10)) +#define kb2qb(x) ((x) >> (QUOTABLOCK_BITS-10)) +#define toqb(x) (((x) + QUOTABLOCK_SIZE - 1) >> QUOTABLOCK_BITS) + /* * Command definitions for the 'quotactl' system call. * The commands are broken into a main command defined below @@ -93,43 +95,106 @@ #define Q_QUOTAON 0x0100 /* enable quotas */ #define Q_QUOTAOFF 0x0200 /* disable quotas */ -#define Q_GETQUOTA 0x0300 /* get limits and usage */ -#define Q_SETQUOTA 0x0400 /* set limits and usage */ -#define Q_SETUSE 0x0500 /* set usage */ +/* GETQUOTA, SETQUOTA and SETUSE which were at 0x0300-0x0500 has now other parameteres */ #define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */ #define Q_SETQLIM 0x0700 /* set limits */ #define Q_GETSTATS 0x0800 /* get collected stats */ -#define Q_RSQUASH 0x1000 /* set root_squash option */ +#define Q_GETINFO 0x0900 /* get info about quotas - graces, flags... */ +#define Q_SETINFO 0x0A00 /* set info about quotas */ +#define Q_SETGRACE 0x0B00 /* set inode and block grace */ +#define Q_SETFLAGS 0x0C00 /* set flags for quota */ +#define Q_GETQUOTA 0x0D00 /* get limits and usage */ +#define Q_SETQUOTA 0x0E00 /* set limits and usage */ +#define Q_SETUSE 0x0F00 /* set usage */ +/* 0x1000 used by old RSQUASH */ /* * The following structure defines the format of the disk quota file - * (as it appears on disk) - the file is an array of these structures - * indexed by user or group number. + * (as it appears on disk) - the file is a hash table whose entries points + * to blocks of these structures. */ -struct dqblk { - __u32 dqb_bhardlimit; /* absolute limit on disk blks alloc */ - __u32 dqb_bsoftlimit; /* preferred limit on disk blks */ - __u32 dqb_curblocks; /* current block count */ +struct disk_dqblk { + __u32 dqb_id; /* id this quota applies to */ __u32 dqb_ihardlimit; /* absolute limit on allocated inodes */ __u32 dqb_isoftlimit; /* preferred inode limit */ __u32 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 */ + __u32 dqb_bhardlimit; /* absolute limit on disk space (in QUOTABLOCK_SIZE) */ + __u32 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 */ +}; + +/* This is in-memory copy of quota block. See meaning of entries above */ +struct mem_dqblk { + unsigned int dqb_ihardlimit; + unsigned int dqb_isoftlimit; + unsigned int dqb_curinodes; + unsigned int dqb_bhardlimit; + unsigned int dqb_bsoftlimit; + qsize_t dqb_curspace; + __kernel_time_t dqb_btime; + __kernel_time_t dqb_itime; }; /* - * Shorthand notation. + * Here are header structures as written on disk and their in-memory copies */ -#define dq_bhardlimit dq_dqb.dqb_bhardlimit -#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit -#define dq_curblocks dq_dqb.dqb_curblocks -#define dq_ihardlimit dq_dqb.dqb_ihardlimit -#define dq_isoftlimit dq_dqb.dqb_isoftlimit -#define dq_curinodes dq_dqb.dqb_curinodes -#define dq_btime dq_dqb.dqb_btime -#define dq_itime dq_dqb.dqb_itime +/* First generic header */ +struct disk_dqheader { + __u32 dqh_magic; /* Magic number identifying file */ + __u32 dqh_version; /* File version */ +}; + +/* Header with type and version specific information */ +struct disk_dqinfo { + __u32 dqi_bgrace; /* Time before block soft limit becomes hard limit */ + __u32 dqi_igrace; /* Time before inode soft limit becomes hard limit */ + __u32 dqi_flags; /* Flags for quotafile (DQF_*) */ + __u32 dqi_blocks; /* Number of blocks in file */ + __u32 dqi_free_blk; /* Number of first free block in the list */ + __u32 dqi_free_entry; /* Number of block with at least one free entry */ +}; -#define dqoff(UID) ((loff_t)((UID) * sizeof (struct dqblk))) +/* Inmemory copy of version specific information */ +struct mem_dqinfo { + unsigned int dqi_bgrace; + unsigned int dqi_igrace; + unsigned int dqi_flags; + unsigned int dqi_blocks; + unsigned int dqi_free_blk; + unsigned int dqi_free_entry; +}; + +/* Flags for version specific files */ +#define DQF_MASK 0x0000 /* Mask for all valid ondisk flags */ + +#ifdef __KERNEL__ +#define DQF_DIRTY 0x0010 /* Is info dirty? */ +extern inline void mark_info_dirty(struct mem_dqinfo *info) +{ + info->dqi_flags |= DQF_DIRTY; +} +#define info_dirty(info) ((info)->dqi_flags & DQF_DIRTY) +#endif +/* + * Structure of header of block with quota structures. It is padded to 16 bytes so + * there will be space for exactly 18 quota-entries in a block + */ +struct disk_dqdbheader { + __u32 dqdh_next_free; /* Number of next block with free entry */ + __u32 dqdh_prev_free; /* Number of previous block with free entry */ + __u16 dqdh_entries; /* Number of valid entries in block */ + __u16 dqdh_pad1; + __u32 dqdh_pad2; +}; + +#define DQINFOOFF sizeof(struct disk_dqheader) /* Offset of info header in file */ +#define DQBLKSIZE_BITS 10 +#define DQBLKSIZE (1 << DQBLKSIZE_BITS) /* Size of block with quota structures */ +#define DQTREEOFF 1 /* Offset of tree in file in blocks */ +#define DQTREEDEPTH 4 /* Depth of quota tree */ +#define DQSTRINBLK ((DQBLKSIZE - sizeof(struct disk_dqdbheader)) / sizeof(struct disk_dqblk)) /* Number of entries in one blocks */ struct dqstats { __u32 lookups; @@ -145,7 +210,6 @@ #ifdef __KERNEL__ extern int nr_dquots, nr_free_dquots; -extern int dquot_root_squash; #define NR_DQHASH 43 /* Just an arbitrary number */ @@ -165,33 +229,52 @@ /* fields after this point are cleared when invalidating */ struct super_block *dq_sb; /* superblock this applies to */ - unsigned int dq_id; /* ID this applies to (uid, gid) */ + qid_t dq_id; /* ID this applies to (uid, gid) */ kdev_t dq_dev; /* Device this applies to */ short dq_type; /* Type of quota */ short dq_flags; /* See DQ_* */ + loff_t dq_off; /* Offset of structure in file (0 for not allocated) */ unsigned long dq_referenced; /* Number of times this dquot was referenced during its lifetime */ - struct dqblk dq_dqb; /* Diskquota usage */ + struct mem_dqblk dq_dqb; /* Diskquota usage */ }; #define NODQUOT (struct dquot *)NULL +#define dq_curspace dq_dqb.dqb_curspace +#define dq_curinodes dq_dqb.dqb_curinodes +#define dq_isoftlimit dq_dqb.dqb_isoftlimit +#define dq_ihardlimit dq_dqb.dqb_ihardlimit +#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit +#define dq_bhardlimit dq_dqb.dqb_bhardlimit +#define dq_itime dq_dqb.dqb_itime +#define dq_btime dq_dqb.dqb_btime + /* * Flags used for set_dqblk. */ -#define SET_QUOTA 0x02 -#define SET_USE 0x04 -#define SET_QLIMIT 0x08 +#define SET_QUOTA 0x01 +#define SET_USE 0x02 +#define SET_QLIMIT 0x04 + +/* + * Flags used for set_info + */ +#define ISET_GRACE 0x01 +#define ISET_FLAGS 0x02 +#define ISET_ALL 0x03 #define QUOTA_OK 0 #define NO_QUOTA 1 +typedef char *dqbuf_t; + #else # /* nodep */ include __BEGIN_DECLS -long quotactl __P ((int, const char *, int, caddr_t)); +long quotactl __P ((int, const char *, qid_t, __kernel_caddr_t)); __END_DECLS #endif /* __KERNEL__ */ diff -r -u --exclude-from=/home/jack/.kerndiffexclude linux-2.4.0-test11-quotafix/include/linux/quotaops.h linux-2.4.0-test11-newquota/include/linux/quotaops.h --- linux-2.4.0-test11-quotafix/include/linux/quotaops.h Mon Nov 27 09:43:43 2000 +++ linux-2.4.0-test11-newquota/include/linux/quotaops.h Sun Dec 3 21:15:47 2000 @@ -26,10 +26,10 @@ extern int quota_off(struct super_block *sb, short type); extern int sync_dquots(kdev_t dev, short type); -extern int dquot_alloc_block(struct inode *inode, unsigned long number, char prealloc); +extern int dquot_alloc_space(struct inode *inode, qsize_t number, char prealloc); extern int dquot_alloc_inode(const struct inode *inode, unsigned long number); -extern void dquot_free_block(struct inode *inode, unsigned long number); +extern void dquot_free_space(struct inode *inode, qsize_t number); extern void dquot_free_inode(const struct inode *inode, unsigned long number); extern int dquot_transfer(struct inode *inode, struct iattr *iattr); @@ -59,36 +59,36 @@ unlock_kernel(); } -extern __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr) +extern __inline__ int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr) { lock_kernel(); if (sb_any_quota_enabled(inode->i_sb)) { /* Number of used blocks is updated in alloc_block() */ - if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 1) == NO_QUOTA) { + if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA) { unlock_kernel(); return 1; } } else { - inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); + inode_add_bytes(inode, nr); mark_inode_dirty(inode); } unlock_kernel(); return 0; } -extern __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr) +extern __inline__ int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr) { lock_kernel(); if (sb_any_quota_enabled(inode->i_sb)) { /* Number of used blocks is updated in alloc_block() */ - if (inode->i_sb->dq_op->alloc_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize), 0) == NO_QUOTA) { + if (inode->i_sb->dq_op->alloc_space(inode, nr, 0) == NO_QUOTA) { unlock_kernel(); return 1; } } else { - inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); + inode_add_bytes(inode, nr); mark_inode_dirty(inode); } unlock_kernel(); @@ -109,13 +109,13 @@ return 0; } -extern __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr) +extern __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr) { lock_kernel(); if (sb_any_quota_enabled(inode->i_sb)) - inode->i_sb->dq_op->free_block(inode, fs_to_dq_blocks(nr, inode->i_sb->s_blocksize)); + inode->i_sb->dq_op->free_space(inode, nr); else { - inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9); + inode_sub_bytes(inode, nr); mark_inode_dirty(inode); } unlock_kernel(); @@ -158,31 +158,36 @@ #define DQUOT_SYNC(dev) do { } while(0) #define DQUOT_OFF(sb) do { } while(0) #define DQUOT_TRANSFER(inode, iattr) (0) -extern __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr) +extern __inline__ int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr) { lock_kernel(); - inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); + inode_add_bytes(inode, nr); unlock_kernel(); mark_inode_dirty(inode); return 0; } -extern __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr) +extern __inline__ int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr) { lock_kernel(); - inode->i_blocks += nr << (inode->i_sb->s_blocksize_bits - 9); + inode_add_bytes(inode, nr); unlock_kernel(); mark_inode_dirty(inode); return 0; } -extern __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr) +extern __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr) { lock_kernel(); - inode->i_blocks -= nr << (inode->i_sb->s_blocksize_bits - 9); + inode_sub_bytes(inode, nr); unlock_kernel(); mark_inode_dirty(inode); } #endif /* CONFIG_QUOTA */ + +#define DQUOT_ALLOC_BLOCK(inode, nr) DQUOT_ALLOC_SPACE((inode), ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits) +#define DQUOT_PREALLOC_BLOCK(inode, nr) DQUOT_ALLOC_SPACE((inode), ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits) +#define DQUOT_FREE_BLOCK(inode, nr) DQUOT_FREE_SPACE((inode), ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits) + #endif /* _LINUX_QUOTAOPS_ */ .