James Bourne This patch includes: o the addition of the rpm-changelog file o 32-bit uid quote patch diff -ruN linux-2.4.20.orig/arch/ia64/ia32/sys_ia32.c linux-2.4.20/arch/ia64/ia32/sys_ia32.c --- linux-2.4.20.orig/arch/ia64/ia32/sys_ia32.c Fri Aug 2 18:39:42 2002 +++ linux-2.4.20/arch/ia64/ia32/sys_ia32.c Wed Dec 4 22:42:49 2002 @@ -3693,24 +3693,25 @@ return result; } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; +struct mem_dqblk32 { __u32 dqb_ihardlimit; __u32 dqb_isoftlimit; __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + /* Not __u64 because of alignment */ + __u32 dqb_curspace[2]; __kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_itime; }; asmlinkage long -sys32_quotactl (int cmd, unsigned int special, int id, struct dqblk32 *addr) +sys32_quotactl (int cmd, unsigned int special, int id, struct mem_dqblk32 *addr) { - extern asmlinkage long sys_quotactl (int, const char *, int, caddr_t); + extern asmlinkage long sys_quotactl (int, const char *, int, __kernel_caddr_t); int cmds = cmd >> SUBCMDSHIFT; mm_segment_t old_fs; - struct dqblk d; + struct mem_dqblk d; char *spec; long err; @@ -3720,13 +3721,15 @@ case Q_SETQUOTA: case Q_SETUSE: case Q_SETQLIM: - if (copy_from_user (&d, addr, sizeof(struct dqblk32))) + if (copy_from_user (&d, (struct mem_dqblk32 *)addr, + sizeof(struct mem_dqblk32))) return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime; + memcpy(&d.dqb_curspace, ((struct mem_dqblk32 *)&d)->dqb_curspace, sizeof(__u64)); break; default: - return sys_quotactl(cmd, (void *) A(special), id, (caddr_t) addr); + return sys_quotactl(cmd, (void *) A(special), id, (__kernel_caddr_t) addr); } spec = getname32((void *) A(special)); err = PTR_ERR(spec); @@ -3734,17 +3737,22 @@ return err; old_fs = get_fs (); set_fs(KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs(old_fs); putname(spec); + if (err) + return err; if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user(addr, &d, sizeof(struct dqblk32))) + qsize_t s = d.dqb_curspace; + ((struct mem_dqblk32 *)&d)->dqb_itime = i; + ((struct mem_dqblk32 *)&d)->dqb_btime = b; + memcpy(((struct mem_dqblk32 *)&d)->dqb_curspace, &s, sizeof(__u64)); + if (copy_to_user((struct mem_dqblk32 *)addr, &d, + sizeof(struct mem_dqblk32))) return -EFAULT; } - return err; + return 0; } asmlinkage long diff -ruN linux-2.4.20.orig/arch/sparc64/kernel/sys_sparc32.c linux-2.4.20/arch/sparc64/kernel/sys_sparc32.c --- linux-2.4.20.orig/arch/sparc64/kernel/sys_sparc32.c Thu Nov 28 16:53:12 2002 +++ linux-2.4.20/arch/sparc64/kernel/sys_sparc32.c Wed Dec 4 22:42:49 2002 @@ -888,24 +888,24 @@ return sys32_fcntl(fd, cmd, arg); } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; +struct mem_dqblk32 { __u32 dqb_ihardlimit; __u32 dqb_isoftlimit; __u32 dqb_curinodes; + __u32 dqb_bhardlimit; + __u32 dqb_bsoftlimit; + __u64 dqb_curspace; __kernel_time_t32 dqb_btime; __kernel_time_t32 dqb_itime; }; -extern asmlinkage int sys_quotactl(int cmd, const char *special, int id, caddr_t addr); +extern asmlinkage long sys_quotactl(int cmd, const char *special, int id, __kernel_caddr_t addr); asmlinkage int sys32_quotactl(int cmd, const char *special, int id, unsigned long addr) { int cmds = cmd >> SUBCMDSHIFT; int err; - struct dqblk d; + struct mem_dqblk d; mm_segment_t old_fs; char *spec; @@ -915,33 +915,35 @@ case Q_SETQUOTA: case Q_SETUSE: case Q_SETQLIM: - if (copy_from_user (&d, (struct dqblk32 *)addr, - sizeof (struct dqblk32))) + if (copy_from_user (&d, (struct mem_dqblk32 *)addr, + sizeof (struct mem_dqblk32))) return -EFAULT; - d.dqb_itime = ((struct dqblk32 *)&d)->dqb_itime; - d.dqb_btime = ((struct dqblk32 *)&d)->dqb_btime; + d.dqb_itime = ((struct mem_dqblk32 *)&d)->dqb_itime; + d.dqb_btime = ((struct mem_dqblk32 *)&d)->dqb_btime; break; default: return sys_quotactl(cmd, special, - id, (caddr_t)addr); + id, (__kernel_caddr_t)addr); } spec = getname (special); err = PTR_ERR(spec); if (IS_ERR(spec)) return err; old_fs = get_fs (); set_fs (KERNEL_DS); - err = sys_quotactl(cmd, (const char *)spec, id, (caddr_t)&d); + err = sys_quotactl(cmd, (const char *)spec, id, (__kernel_caddr_t)&d); set_fs (old_fs); putname (spec); + if (err) + return err; if (cmds == Q_GETQUOTA) { __kernel_time_t b = d.dqb_btime, i = d.dqb_itime; - ((struct dqblk32 *)&d)->dqb_itime = i; - ((struct dqblk32 *)&d)->dqb_btime = b; - if (copy_to_user ((struct dqblk32 *)addr, &d, - sizeof (struct dqblk32))) + ((struct mem_dqblk32 *)&d)->dqb_itime = i; + ((struct mem_dqblk32 *)&d)->dqb_btime = b; + if (copy_to_user ((struct mem_dqblk32 *)addr, &d, + sizeof (struct mem_dqblk32))) return -EFAULT; } - return err; + return 0; } static inline int put_statfs (struct statfs32 *ubuf, struct statfs *kbuf) diff -ruN linux-2.4.20.orig/fs/dquot.c linux-2.4.20/fs/dquot.c --- linux-2.4.20.orig/fs/dquot.c Fri Aug 2 18:39:45 2002 +++ linux-2.4.20/fs/dquot.c Wed Dec 4 22:42:49 2002 @@ -35,7 +35,7 @@ * Jan Kara, , sponsored by SuSE CR, 10-11/99 * * Used struct list_head instead of own list struct - * Invalidation of dquots with dq_count > 0 no longer possible + * Invalidation of referenced dquots is no longer possible * Improved free_dquots list management * Quota and i_blocks are now updated in one place to avoid races * Warnings are now delayed so we won't block in critical section @@ -45,6 +45,10 @@ * Added dynamic quota structure allocation * Jan Kara 12/2000 * + * New quotafile format + * Alocation units changed to bytes + * Jan Kara, , 2000 + * * (C) Copyright 1994 - 1997 Marco van Wieringen */ @@ -63,14 +67,19 @@ #include #include +#include -#define __DQUOT_VERSION__ "dquot_6.4.0" +#define __DQUOT_VERSION__ "dquot_6.5.0" +#define __DQUOT_NUM_VERSION__ (6*10000+5*100+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; } @@ -120,7 +129,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: @@ -136,6 +145,26 @@ return is_enabled(sb_dqopt(sb), type); } +static inline void get_dquot_ref(struct dquot *dquot) +{ + dquot->dq_count++; +} + +static inline void put_dquot_ref(struct dquot *dquot) +{ + dquot->dq_count--; +} + +static inline void get_dquot_dup_ref(struct dquot *dquot) +{ + dquot->dq_dup_ref++; +} + +static inline void put_dquot_dup_ref(struct dquot *dquot) +{ + dquot->dq_dup_ref--; +} + static inline int const hashfn(kdev_t dev, unsigned int id, short type) { return((HASHDEV(dev) ^ id) * (MAXQUOTAS - type)) % NR_DQHASH; @@ -243,6 +272,7 @@ wake_up(&dquot->dq_wait_lock); } +/* Wait for dquot to be unused */ static void __wait_dquot_unused(struct dquot *dquot) { DECLARE_WAITQUEUE(wait, current); @@ -258,6 +288,394 @@ current->state = TASK_RUNNING; } +/* Wait for all duplicated dquot references to be dropped */ +static void __wait_dup_drop(struct dquot *dquot) +{ + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&dquot->dq_wait_free, &wait); +repeat: + set_current_state(TASK_UNINTERRUPTIBLE); + if (dquot->dq_dup_ref) { + schedule(); + goto repeat; + } + remove_wait_queue(&dquot->dq_wait_free, &wait); + current->state = TASK_RUNNING; +} + +/* + * 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 "VFS: %s: Can't read info structure.\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 "VFS: %s: Can't write info structure.\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: %s: Can't write block (%u) with free entries.\n", kdevname(filp->f_dentry->d_inode->i_dev), 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: %s: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", kdevname(dquot->dq_dev), 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: %s: find_free_dqentry(): Data block full but it shouldn't.\n", kdevname(dquot->dq_dev)); + *err = -EIO; + goto out_buf; + } +#endif + if ((*err = write_blk(filp, blk, buf)) < 0) { + printk(KERN_ERR "VFS: %s: find_free_dqentry(): Can't write quota data block %u.\n", kdevname(dquot->dq_dev), 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: %s: Can't read tree quota block %u.\n", kdevname(dquot->dq_dev), *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: %s: Inserting already present quota entry (block %u).\n", kdevname(dquot->dq_dev), 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... */ @@ -268,32 +686,201 @@ 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: %s: Error %d occured while creating quota.\n", kdevname(dquot->dq_dev), 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); + 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: %s: quota write failed.\n", + kdevname(dquot->dq_dev)); + dqstats.writes++; +out_sem: + up(sem); +} - /* - * 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; +/* 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: %s: Quota structure has offset to other block (%u) than it should (%u).\n", kdevname(dquot->dq_dev), blk, (uint)(dquot->dq_off >> DQBLKSIZE_BITS)); + goto out_buf; + } + if ((ret = read_blk(filp, blk, buf)) < 0) { + printk(KERN_ERR "VFS: %s: Can't read quota data block %u\n", kdevname(dquot->dq_dev), 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: %s: Can't move quota data block (%u) to free list.\n", kdevname(dquot->dq_dev), 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: %s: Can't insert quota data block (%u) to free entry list.\n", kdevname(dquot->dq_dev), blk); + goto out_buf; + } + } + else + if ((ret = write_blk(filp, blk, buf)) < 0) { + printk(KERN_ERR "VFS: %s: Can't write quota data block %u\n", kdevname(dquot->dq_dev), 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: %s: Can't read quota data block %u\n", kdevname(dquot->dq_dev), *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: %s: Can't write quota tree block %u.\n", kdevname(dquot->dq_dev), *blk); + } +out_buf: + freedqbuf(buf); + return ret; +} + +/* Delete dquot from tree */ +static void delete_dquot(struct dquot *dquot) +{ + uint tmp = DQTREEOFF; + + down(&sb_dqopt(dquot->dq_sb)->dqio_sem); + if (!dquot->dq_off) { /* Even not allocated? */ + up(&sb_dqopt(dquot->dq_sb)->dqio_sem); + return; + } + 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: %s: Can't read quota tree block %u.\n", kdevname(dquot->dq_dev), 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: %s: Quota for id %u referenced but not present.\n", kdevname(dquot->dq_dev), 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: %s: Can't read quota tree block %u.\n", kdevname(dquot->dq_dev), blk); + goto out_buf; + } ret = 0; - if (filp) - ret = filp->f_op->write(filp, (char *)&dqbuf, - sizeof(struct dqblk), &offset); - if (ret != sizeof(struct dqblk)) - printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", - kdevname(dquot->dq_dev)); + 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; +} - set_fs(fs); - up(sem); - dqstats.writes++; +/* 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) @@ -302,31 +889,58 @@ 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: %s: Quota invalidated while reading!\n", kdevname(dquot->dq_dev)); 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: %s: Can't read quota structure for id %u.\n", kdevname(dquot->dq_dev), 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) +{ + /* 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); +} + +/* + * End of IO functions + */ + /* Invalidate all dquots on the list, wait for all users. Note that this function is called * after quota is disabled so no new quota might be created. As we only insert to the end of * inuse list, we don't have to restart searching... */ @@ -376,12 +990,14 @@ continue; if (!(dquot->dq_flags & (DQ_MOD | DQ_LOCKED))) continue; - /* Raise use count so quota won't be invalidated. We can't use dqduplicate() as it does too many tests */ - dquot->dq_count++; + /* Get reference to quota so it won't be invalidated. get_dquot_ref() + * is enough since if dquot is locked/modified it can't be + * on the free list */ + get_dquot_ref(dquot); 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; } @@ -416,11 +1032,15 @@ return kmem_cache_shrink(dquot_cachep); } -/* NOTE: If you change this function please check whether dqput_blocks() works right... */ +/* + * Put reference to dquot + * NOTE: If you change this function please check whether dqput_blocks() works right... + */ static void dqput(struct dquot *dquot) { if (!dquot) return; +#ifdef __DQUOT_PARANOIA if (!dquot->dq_count) { printk("VFS: dqput: trying to free free dquot\n"); printk("VFS: device %s, dquot of %s %d\n", @@ -428,29 +1048,36 @@ dquot->dq_id); return; } +#endif dqstats.drops++; we_slept: + if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1) { /* Last unduplicated reference? */ + __wait_dup_drop(dquot); + goto we_slept; + } if (dquot->dq_count > 1) { /* We have more than one user... We can simply decrement use count */ - dquot->dq_count--; + put_dquot_ref(dquot); 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... */ + put_dquot_ref(dquot); return; } - dquot->dq_count--; - /* If dquot is going to be invalidated invalidate_dquots() is going to free it so */ +#endif + put_dquot_ref(dquot); + /* Don't put dquot on free list if it will be invalidated (to avoid races between invalidate_dquots() and prune_dqcache()) */ if (!(dquot->dq_flags & DQ_INVAL)) - put_dquot_last(dquot); /* Place at end of LRU free queue */ + /* Place at end of LRU free queue */ + put_dquot_last(dquot); wake_up(&dquot->dq_wait_free); } @@ -479,7 +1106,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)) { @@ -503,42 +1130,65 @@ insert_dquot_hash(dquot); read_dquot(dquot); } else { - if (!dquot->dq_count++) + if (!dquot->dq_count) remove_free_dquot(dquot); + get_dquot_ref(dquot); dqstats.cache_hits++; wait_on_dquot(dquot); if (empty) 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++; return dquot; } +/* Duplicate reference to dquot got from inode */ static struct dquot *dqduplicate(struct dquot *dquot) { if (dquot == NODQUOT) return NODQUOT; - dquot->dq_count++; + get_dquot_ref(dquot); +#ifdef __DQUOT_PARANOIA if (!dquot->dq_sb) { printk(KERN_ERR "VFS: dqduplicate(): Invalidated quota to be duplicated!\n"); - dquot->dq_count--; + put_dquot_ref(dquot); return NODQUOT; } if (dquot->dq_flags & DQ_LOCKED) printk(KERN_ERR "VFS: dqduplicate(): Locked quota to be duplicated!\n"); +#endif + get_dquot_dup_ref(dquot); dquot->dq_referenced++; dqstats.lookups++; return dquot; } +/* Put duplicated reference */ +static void dqputduplicate(struct dquot *dquot) +{ +#ifdef __DQUOT_PARANOIA + if (!dquot->dq_dup_ref) { + printk(KERN_ERR "VFS: dqputduplicate(): Duplicated dquot put without duplicate reference.\n"); + return; + } +#endif + put_dquot_dup_ref(dquot); + if (!dquot->dq_dup_ref) + wake_up(&dquot->dq_wait_free); + put_dquot_ref(dquot); + dqstats.drops++; +} + static int dqinit_needed(struct inode *inode, short type) { int cnt; @@ -582,7 +1232,9 @@ /* Return 0 if dqput() won't block (note that 1 doesn't necessarily mean blocking) */ static inline int dqput_blocks(struct dquot *dquot) { - if (dquot->dq_count == 1) + if (dquot->dq_dup_ref && dquot->dq_count - dquot->dq_dup_ref <= 1) + return 1; + if (dquot->dq_count <= 1 && dquot->dq_flags & DQ_MOD) return 1; return 0; } @@ -639,9 +1291,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; } @@ -657,13 +1309,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; @@ -704,7 +1356,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"; @@ -739,7 +1391,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) @@ -767,20 +1419,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; @@ -788,7 +1440,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) @@ -797,11 +1449,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 /* @@ -818,18 +1470,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; @@ -840,22 +1492,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 && @@ -870,10 +1522,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)) @@ -882,10 +1534,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; @@ -906,46 +1558,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. @@ -955,7 +1609,7 @@ void dquot_initialize(struct inode *inode, short type) { struct dquot *dquot[MAXQUOTAS]; - unsigned int id = 0; + qid_t id = 0; short cnt; if (IS_NOQUOTA(inode)) @@ -1004,6 +1658,7 @@ struct dquot *dquot; short cnt; + /* Here we needn't be afraid of races as inode is not used any more */ inode->i_flags &= ~S_QUOTA; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (inode->i_dquot[cnt] == NODQUOT) @@ -1017,7 +1672,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]; @@ -1032,22 +1687,22 @@ 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); - /* NOBLOCK End */ + inode_add_bytes(inode, number); + /* NOBLOCK End */ ret = QUOTA_OK; warn_put_all: flush_warnings(dquot, warntype); for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (dquot[cnt] != NODQUOT) - dqput(dquot[cnt]); + dqputduplicate(dquot[cnt]); return ret; } @@ -1084,14 +1739,14 @@ flush_warnings(dquot, warntype); for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (dquot[cnt] != NODQUOT) - dqput(dquot[cnt]); + dqputduplicate(dquot[cnt]); return ret; } /* * 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; @@ -1101,10 +1756,10 @@ dquot = dqduplicate(inode->i_dquot[cnt]); if (dquot == NODQUOT) continue; - dquot_decr_blocks(dquot, number); - dqput(dquot); + dquot_decr_space(dquot, number); + dqputduplicate(dquot); } - inode->i_blocks -= number << (BLOCK_SIZE_BITS - 9); + inode_sub_bytes(inode, number); /* NOBLOCK End */ } @@ -1122,7 +1777,7 @@ if (dquot == NODQUOT) continue; dquot_decr_inodes(dquot, number); - dqput(dquot); + dqputduplicate(dquot); } /* NOBLOCK End */ } @@ -1134,7 +1789,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, @@ -1164,7 +1819,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... */ @@ -1174,7 +1829,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; } @@ -1189,10 +1844,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(); @@ -1209,7 +1864,7 @@ flush_warnings(transfer_to, warntype); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (transfer_to[cnt] != NODQUOT) - dqput(transfer_to[cnt]); + dqputduplicate(transfer_to[cnt]); if (transfer_from[cnt] != NODQUOT) dqput(transfer_from[cnt]); } @@ -1222,6 +1877,7 @@ for (i = 0; i < NR_DQHASH; i++) INIT_LIST_HEAD(dquot_hash + i); + dqstats.version = __DQUOT_NUM_VERSION__; printk(KERN_NOTICE "VFS: Diskquotas version %s initialized\n", __DQUOT_VERSION__); return 0; } @@ -1233,14 +1889,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: @@ -1252,7 +1908,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: @@ -1274,7 +1930,7 @@ { struct file *filp; short cnt; - struct quota_mount_options *dqopt = sb_dqopt(sb); + struct quota_info *dqopt = sb_dqopt(sb); lock_kernel(); if (!sb) @@ -1292,11 +1948,12 @@ /* Note: these are blocking operations */ remove_dquot_ref(sb, cnt); invalidate_dquots(sb, 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); @@ -1305,20 +1962,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; @@ -1345,26 +2013,23 @@ 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; /* We don't want quota on quota files */ dquot_drop(inode); inode->i_flags |= S_NOQUOTA; dqopt->files[type] = f; + 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); - - 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); - 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: @@ -1379,7 +2044,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; @@ -1392,13 +2057,15 @@ if ((u_int) type >= MAXQUOTAS) goto out; - if (id & ~0xFFFF) + if ((uint) type >= MAXQUOTAS || cmds > 0x1100 || cmds < 0x100 || cmds == 0x0300 || + cmds == 0x0400 || cmds == 0x0500 || cmds == 0x1000) goto out; ret = -EPERM; switch (cmds) { case Q_SYNC: case Q_GETSTATS: + case Q_GETINFO: break; case Q_GETQUOTA: if (((type == USRQUOTA && current->euid != id) || @@ -1411,7 +2078,6 @@ goto out; } - ret = -EINVAL; dev = NODEV; if (special != NULL || (cmds != Q_SYNC && cmds != Q_GETSTATS)) { mode_t mode; @@ -1443,7 +2109,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; @@ -1460,16 +2126,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: if (sb) drop_super(sb); diff -ruN linux-2.4.20.orig/fs/inode.c linux-2.4.20/fs/inode.c --- linux-2.4.20.orig/fs/inode.c Fri Aug 2 18:39:45 2002 +++ linux-2.4.20/fs/inode.c Wed Dec 4 22:42:49 2002 @@ -777,6 +777,7 @@ 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 -ruN linux-2.4.20.orig/fs/ioctl.c linux-2.4.20/fs/ioctl.c --- linux-2.4.20.orig/fs/ioctl.c Fri Feb 9 12:29:44 2001 +++ linux-2.4.20/fs/ioctl.c Wed Dec 4 22:42:49 2002 @@ -101,6 +101,16 @@ 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 res = inode_get_bytes(filp->f_dentry->d_inode); + error = copy_to_user((loff_t *)arg, &res, sizeof(res)) ? -EFAULT : 0; + } + else + error = -ENOTTY; + break; default: error = -ENOTTY; if (S_ISREG(filp->f_dentry->d_inode->i_mode)) diff -ruN linux-2.4.20.orig/include/asm-alpha/ioctls.h linux-2.4.20/include/asm-alpha/ioctls.h --- linux-2.4.20.orig/include/asm-alpha/ioctls.h Thu Nov 28 16:53:15 2002 +++ linux-2.4.20/include/asm-alpha/ioctls.h Wed Dec 4 22:42:49 2002 @@ -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 -ruN linux-2.4.20.orig/include/asm-cris/ioctls.h linux-2.4.20/include/asm-cris/ioctls.h --- linux-2.4.20.orig/include/asm-cris/ioctls.h Fri Aug 2 18:39:45 2002 +++ linux-2.4.20/include/asm-cris/ioctls.h Wed Dec 4 22:42:49 2002 @@ -69,6 +69,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 #define TIOCSERSETRS485 0x5460 /* enable rs-485 */ #define TIOCSERWRRS485 0x5461 /* write rs-485 */ diff -ruN linux-2.4.20.orig/include/asm-i386/ioctls.h linux-2.4.20/include/asm-i386/ioctls.h --- linux-2.4.20.orig/include/asm-i386/ioctls.h Fri Aug 2 18:39:45 2002 +++ linux-2.4.20/include/asm-i386/ioctls.h Wed Dec 4 22:42:49 2002 @@ -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 -ruN linux-2.4.20.orig/include/asm-ia64/ioctls.h linux-2.4.20/include/asm-ia64/ioctls.h --- linux-2.4.20.orig/include/asm-ia64/ioctls.h Thu Nov 28 16:53:15 2002 +++ linux-2.4.20/include/asm-ia64/ioctls.h Wed Dec 4 22:42:49 2002 @@ -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 -ruN linux-2.4.20.orig/include/asm-m68k/ioctls.h linux-2.4.20/include/asm-m68k/ioctls.h --- linux-2.4.20.orig/include/asm-m68k/ioctls.h Thu Feb 12 17:25:04 1998 +++ linux-2.4.20/include/asm-m68k/ioctls.h Wed Dec 4 22:42:50 2002 @@ -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 -ruN linux-2.4.20.orig/include/asm-sh/ioctls.h linux-2.4.20/include/asm-sh/ioctls.h --- linux-2.4.20.orig/include/asm-sh/ioctls.h Sat Nov 6 11:40:31 1999 +++ linux-2.4.20/include/asm-sh/ioctls.h Wed Dec 4 22:42:57 2002 @@ -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 -ruN linux-2.4.20.orig/include/asm-sparc/ioctls.h linux-2.4.20/include/asm-sparc/ioctls.h --- linux-2.4.20.orig/include/asm-sparc/ioctls.h Fri Aug 2 18:39:45 2002 +++ linux-2.4.20/include/asm-sparc/ioctls.h Wed Dec 4 22:42:57 2002 @@ -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 -ruN linux-2.4.20.orig/include/asm-sparc64/ioctls.h linux-2.4.20/include/asm-sparc64/ioctls.h --- linux-2.4.20.orig/include/asm-sparc64/ioctls.h Fri Aug 2 18:39:45 2002 +++ linux-2.4.20/include/asm-sparc64/ioctls.h Wed Dec 4 22:42:57 2002 @@ -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 -ruN linux-2.4.20.orig/include/linux/fs.h linux-2.4.20/include/linux/fs.h --- linux-2.4.20.orig/include/linux/fs.h Wed Dec 4 22:44:25 2002 +++ linux-2.4.20/include/linux/fs.h Wed Dec 4 22:42:57 2002 @@ -454,6 +454,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; @@ -514,6 +515,39 @@ } u; }; +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 */ @@ -662,15 +696,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 */ }; /* @@ -734,7 +766,7 @@ struct block_device *s_bdev; struct list_head s_instances; - struct quota_mount_options s_dquot; /* Diskquota specific options */ + struct quota_info s_dquot; /* Diskquota specific options */ union { struct minix_sb_info minix_sb; @@ -957,9 +989,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 -ruN linux-2.4.20.orig/include/linux/quota.h linux-2.4.20/include/linux/quota.h --- linux-2.4.20.orig/include/linux/quota.h Thu Nov 22 11:38:31 2001 +++ linux-2.4.20/include/linux/quota.h Wed Dec 4 22:42:57 2002 @@ -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,107 @@ #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 */ +/* GETSTATS at 0x0800 is now longer... */ +#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 */ +#define Q_GETSTATS 0x1100 /* get collected stats */ /* * 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; @@ -140,12 +206,12 @@ __u32 allocated_dquots; __u32 free_dquots; __u32 syncs; + __u32 version; }; #ifdef __KERNEL__ extern int nr_dquots, nr_free_dquots; -extern int dquot_root_squash; #define NR_DQHASH 43 /* Just an arbitrary number */ @@ -162,37 +228,57 @@ struct list_head dq_free; /* Free list element */ wait_queue_head_t dq_wait_lock; /* Pointer to waitqueue on dquot lock */ wait_queue_head_t dq_wait_free; /* Pointer to waitqueue for quota to be unused */ - int dq_count; /* Reference count */ + int dq_count; /* Use count */ + int dq_dup_ref; /* Number of duplicated refences */ /* 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 -ruN linux-2.4.20.orig/include/linux/quotaops.h linux-2.4.20/include/linux/quotaops.h --- linux-2.4.20.orig/include/linux/quotaops.h Fri Aug 2 18:39:45 2002 +++ linux-2.4.20/include/linux/quotaops.h Wed Dec 4 22:42:57 2002 @@ -25,10 +25,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,50 +59,50 @@ unlock_kernel(); } -static __inline__ int DQUOT_PREALLOC_BLOCK_NODIRTY(struct inode *inode, int nr) +static __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(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); unlock_kernel(); return 0; } -static __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr) +static __inline__ int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr) { int ret; - if (!(ret = DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr))) + if (!(ret = DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr))) mark_inode_dirty(inode); return ret; } -static __inline__ int DQUOT_ALLOC_BLOCK_NODIRTY(struct inode *inode, int nr) +static __inline__ int DQUOT_ALLOC_SPACE_NODIRTY(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); unlock_kernel(); return 0; } -static __inline__ int DQUOT_ALLOC_BLOCK(struct inode *inode, int nr) +static __inline__ int DQUOT_ALLOC_SPACE(struct inode *inode, qsize_t nr) { int ret; - if (!(ret = DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr))) + if (!(ret = DQUOT_ALLOC_SPACE_NODIRTY(inode, nr))) mark_inode_dirty(inode); return ret; } @@ -121,22 +121,22 @@ return 0; } -static __inline__ void DQUOT_FREE_BLOCK_NODIRTY(struct inode *inode, int nr) +static __inline__ void DQUOT_FREE_SPACE_NODIRTY(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); unlock_kernel(); } -static __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr) +static __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr) { - DQUOT_FREE_BLOCK_NODIRTY(inode, nr); + DQUOT_FREE_SPACE_NODIRTY(inode, nr); mark_inode_dirty(inode); } - + static __inline__ void DQUOT_FREE_INODE(struct inode *inode) { lock_kernel(); @@ -174,48 +174,56 @@ #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_NODIRTY(struct inode *inode, int nr) +extern __inline__ int DQUOT_PREALLOC_SPACE_NODIRTY(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(); return 0; } -extern __inline__ int DQUOT_PREALLOC_BLOCK(struct inode *inode, int nr) +extern __inline__ int DQUOT_PREALLOC_SPACE(struct inode *inode, qsize_t nr) { - DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr); + DQUOT_PREALLOC_SPACE_NODIRTY(inode, nr); mark_inode_dirty(inode); return 0; } -extern __inline__ int DQUOT_ALLOC_BLOCK_NODIRTY(struct inode *inode, int nr) +extern __inline__ int DQUOT_ALLOC_SPACE_NODIRTY(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(); 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) { - DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr); + DQUOT_ALLOC_SPACE_NODIRTY(inode, nr); mark_inode_dirty(inode); return 0; } -extern __inline__ void DQUOT_FREE_BLOCK_NODIRTY(struct inode *inode, int nr) +extern __inline__ void DQUOT_FREE_SPACE_NODIRTY(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(); } -extern __inline__ void DQUOT_FREE_BLOCK(struct inode *inode, int nr) +extern __inline__ void DQUOT_FREE_SPACE(struct inode *inode, qsize_t nr) { - DQUOT_FREE_BLOCK_NODIRTY(inode, nr); + DQUOT_FREE_SPACE_NODIRTY(inode, nr); mark_inode_dirty(inode); -} +} #endif /* CONFIG_QUOTA */ + +#define DQUOT_ALLOC_BLOCK_NODIRTY(inode, nr) DQUOT_ALLOC_SPACE_NODIRTY((inode), ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits) +#define DQUOT_ALLOC_BLOCK(inode, nr) DQUOT_ALLOC_SPACE((inode), ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits) +#define DQUOT_PREALLOC_BLOCK_NODIRTY(inode, nr) DQUOT_PREALLOC_SPACE_NODIRTY((inode), ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits) +#define DQUOT_PREALLOC_BLOCK(inode, nr) DQUOT_PREALLOC_SPACE((inode), ((qsize_t)(nr)) << (inode)->i_sb->s_blocksize_bits) +#define DQUOT_FREE_BLOCK_NODIRTY(inode, nr) DQUOT_FREE_SPACE_NODIRTY((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_ */ diff -ruN linux-2.4.20.orig/scripts/mkspec linux-2.4.20/scripts/mkspec --- linux-2.4.20.orig/scripts/mkspec Fri Aug 2 18:39:46 2002 +++ linux-2.4.20/scripts/mkspec Wed Dec 4 22:42:57 2002 @@ -75,3 +75,6 @@ echo "/lib/modules/$VERSION.$PATCHLEVEL.$SUBLEVEL$EXTRAVERSION" echo "/boot/*" echo "" +if [ -f rpm-changelog ] ; then + cat rpm-changelog +fi