Index: linux-2.6.5-7.283/include/linux/ext3_jbd.h =================================================================== --- linux-2.6.5-7.283.orig/include/linux/ext3_jbd.h +++ linux-2.6.5-7.283/include/linux/ext3_jbd.h @@ -72,6 +72,19 @@ extern int ext3_writepage_trans_blocks(s #define EXT3_INDEX_EXTRA_TRANS_BLOCKS 8 +#ifdef CONFIG_QUOTA +/* Amount of blocks needed for quota update - we know that the structure was + * allocated so we need to update only inode+data */ +#define EXT3_QUOTA_TRANS_BLOCKS 2 +/* Amount of blocks needed for quota insert/delete - we do some block writes + * but inode, sb and group updates are done only once */ +#define EXT3_QUOTA_INIT_BLOCKS (DQUOT_MAX_WRITES*\ + (EXT3_SINGLEDATA_TRANS_BLOCKS-3)+3) +#else +#define EXT3_QUOTA_TRANS_BLOCKS 0 +#define EXT3_QUOTA_INIT_BLOCKS 0 +#endif + int ext3_mark_iloc_dirty(handle_t *handle, struct inode *inode, @@ -97,6 +110,8 @@ int ext3_mark_inode_dirty(handle_t *hand void ext3_journal_abort_handle(const char *caller, const char *err_fn, struct buffer_head *bh, handle_t *handle, int err); +int ext3_journal_dirty_data(handle_t *handle, struct buffer_head *bh); + static inline int __ext3_journal_get_undo_access(const char *where, handle_t *handle, struct buffer_head *bh, int *credits) Index: linux-2.6.5-7.283/include/linux/fs.h =================================================================== --- linux-2.6.5-7.283.orig/include/linux/fs.h +++ linux-2.6.5-7.283/include/linux/fs.h @@ -967,6 +967,9 @@ struct super_operations { void (*umount_begin) (struct super_block *); int (*show_options)(struct seq_file *, struct vfsmount *); + + ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t); + ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t); }; /* Inode state bits. Protected by inode_lock. */ Index: linux-2.6.5-7.283/include/linux/quota.h =================================================================== --- linux-2.6.5-7.283.orig/include/linux/quota.h +++ linux-2.6.5-7.283/include/linux/quota.h @@ -138,6 +138,10 @@ struct if_dqinfo { #include #include +/* Maximal numbers of writes for quota operation (insert/delete/update) + * (over all formats) - info block, 4 pointer blocks, data block */ +#define DQUOT_MAX_WRITES 6 + /* * Data for one user/group kept in memory */ @@ -168,22 +172,21 @@ struct mem_dqinfo { } u; }; +struct super_block; + #define DQF_MASK 0xffff /* Mask for format specific flags */ #define DQF_INFO_DIRTY_B 16 #define DQF_ANY_DQUOT_DIRTY_B 17 #define DQF_INFO_DIRTY (1 << DQF_INFO_DIRTY_B) /* Is info dirty? */ #define DQF_ANY_DQUOT_DIRTY (1 << DQF_ANY_DQUOT_DIRTY_B) /* Is any dquot dirty? */ -extern inline void mark_info_dirty(struct mem_dqinfo *info) -{ - set_bit(DQF_INFO_DIRTY_B, &info->dqi_flags); -} - +extern void mark_info_dirty(struct super_block *sb, int type); #define info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags) #define info_any_dquot_dirty(info) test_bit(DQF_ANY_DQUOT_DIRTY_B, &(info)->dqi_flags) #define info_any_dirty(info) (info_dirty(info) || info_any_dquot_dirty(info)) #define sb_dqopt(sb) (&(sb)->s_dquot) +#define sb_dqinfo(sb, type) (sb_dqopt(sb)->info+(type)) struct dqstats { int lookups; @@ -204,6 +207,9 @@ extern struct dqstats dqstats; #define DQ_BLKS_B 1 #define DQ_INODES_B 2 #define DQ_FAKE_B 3 +#define DQ_READ_B 4 /* dquot was read into memory */ +#define DQ_ACTIVE_B 5 /* dquot is active (dquot_release not called) */ +#define DQ_WAITFREE_B 6 /* dquot being waited (by invalidate_dquots) */ #define DQ_MOD (1 << DQ_MOD_B) /* dquot modified since read */ #define DQ_BLKS (1 << DQ_BLKS_B) /* uid/gid has been warned about blk limit */ @@ -239,18 +245,22 @@ struct quota_format_ops { int (*free_file_info)(struct super_block *sb, int type); /* Called on quotaoff() */ int (*read_dqblk)(struct dquot *dquot); /* Read structure for one user */ int (*commit_dqblk)(struct dquot *dquot); /* Write (or delete) structure for one user */ + int (*release_dqblk)(struct dquot *dquot); /* Called when last reference to dquot is being dropped */ }; /* Operations working with dquots */ struct dquot_operations { - void (*initialize) (struct inode *, int); - void (*drop) (struct inode *); + int (*initialize) (struct inode *, int); + int (*drop) (struct inode *); int (*alloc_space) (struct inode *, qsize_t, int); int (*alloc_inode) (const struct inode *, unsigned long); - void (*free_space) (struct inode *, qsize_t); - void (*free_inode) (const struct inode *, unsigned long); + int (*free_space) (struct inode *, qsize_t); + int (*free_inode) (const struct inode *, unsigned long); int (*transfer) (struct inode *, struct iattr *); int (*write_dquot) (struct dquot *); + int (*acquire_dquot) (struct dquot *); /* Quota is going to be created on disk */ + int (*release_dquot) (struct dquot *); /* Quota is going to be deleted from disk */ + int (*write_info) (struct super_block *, int); /* Write of quota "superblock" */ }; /* Operations handling requests from userspace */ @@ -283,7 +293,8 @@ struct quota_info { struct semaphore dqio_sem; /* lock device while I/O in progress */ struct semaphore dqonoff_sem; /* Serialize quotaon & quotaoff */ struct rw_semaphore dqptr_sem; /* serialize ops using quota_info struct, pointers from inode to dquots */ - struct file *files[MAXQUOTAS]; /* fp's to quotafiles */ + struct inode *files[MAXQUOTAS]; /* inodes of quotafiles */ + struct vfsmount *mnt[MAXQUOTAS]; /* mountpoint entries of filesystems with quota files */ struct mem_dqinfo info[MAXQUOTAS]; /* Information for each quota type */ struct quota_format_ops *ops[MAXQUOTAS]; /* Operations for each type */ }; Index: linux-2.6.5-7.283/include/linux/quotaops.h =================================================================== --- linux-2.6.5-7.283.orig/include/linux/quotaops.h +++ linux-2.6.5-7.283/include/linux/quotaops.h @@ -22,17 +22,22 @@ */ extern void sync_dquots(struct super_block *sb, int type); -extern void dquot_initialize(struct inode *inode, int type); -extern void dquot_drop(struct inode *inode); +extern int dquot_initialize(struct inode *inode, int type); +extern int dquot_drop(struct inode *inode); extern int dquot_alloc_space(struct inode *inode, qsize_t number, int prealloc); extern int dquot_alloc_inode(const 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_free_space(struct inode *inode, qsize_t number); +extern int dquot_free_inode(const struct inode *inode, unsigned long number); extern int dquot_transfer(struct inode *inode, struct iattr *iattr); +extern int dquot_commit(struct dquot *dquot); +extern int dquot_commit_info(struct super_block *sb, int type); +extern int dquot_acquire(struct dquot *dquot); +extern int dquot_release(struct dquot *dquot); + /* * Operations supported for diskquotas. */ @@ -143,7 +148,7 @@ static __inline__ int DQUOT_OFF(struct s { int ret = -ENOSYS; - if (sb->s_qcop && sb->s_qcop->quota_off) + if (sb_any_quota_enabled(sb) && sb->s_qcop && sb->s_qcop->quota_off) ret = sb->s_qcop->quota_off(sb, -1); return ret; } Index: linux-2.6.5-7.283/include/linux/security.h =================================================================== --- linux-2.6.5-7.283.orig/include/linux/security.h +++ linux-2.6.5-7.283/include/linux/security.h @@ -1020,7 +1020,7 @@ struct security_operations { int (*sysctl) (ctl_table * table, int op); int (*capable) (struct task_struct * tsk, int cap); int (*quotactl) (int cmds, int type, int id, struct super_block * sb); - int (*quota_on) (struct file * f); + int (*quota_on) (struct dentry * dentry); int (*syslog) (int type); int (*vm_enough_memory) (long pages); @@ -1292,9 +1292,9 @@ static inline int security_quotactl (int 0); } -static inline int security_quota_on (struct file * file) +static inline int security_quota_on (struct dentry * dentry) { - return COND_SECURITY(quota_on (file), + return COND_SECURITY(quota_on (dentry), 0); } Index: linux-2.6.5-7.283/security/dummy.c =================================================================== --- linux-2.6.5-7.283.orig/security/dummy.c +++ linux-2.6.5-7.283/security/dummy.c @@ -90,7 +90,7 @@ static int dummy_quotactl (int cmds, int return 0; } -static int dummy_quota_on (struct file *f) +static int dummy_quota_on (struct dentry *dentry) { return 0; } Index: linux-2.6.5-7.283/security/selinux/hooks.c =================================================================== --- linux-2.6.5-7.283.orig/security/selinux/hooks.c +++ linux-2.6.5-7.283/security/selinux/hooks.c @@ -1454,9 +1454,9 @@ static int selinux_quotactl(int cmds, in return rc; } -static int selinux_quota_on(struct file *f) +static int selinux_quota_on(struct dentry *dentry) { - return file_has_perm(current, f, FILE__QUOTAON); + return dentry_has_perm(current, NULL, dentry, FILE__QUOTAON); } static int selinux_syslog(int type) Index: linux-2.6.5-7.283/fs/dquot.c =================================================================== --- linux-2.6.5-7.283.orig/fs/dquot.c +++ linux-2.6.5-7.283/fs/dquot.c @@ -52,6 +52,9 @@ * New SMP locking. * Jan Kara, , 10/2002 * + * Fix lock inversion problems + * Jan Kara, , 2003,2004 + * * (C) Copyright 1994 - 1997 Marco van Wieringen */ @@ -75,7 +78,8 @@ #include #include #include -#include +#include +#include #include @@ -109,7 +113,7 @@ * dqget(). Write operations on dquots don't hold dq_lock as they copy data * under dq_data_lock spinlock to internal buffers before writing. * - * Lock ordering (including journal_lock) is following: + * Lock ordering (including journal_lock) is the following: * dqonoff_sem > journal_lock > dqptr_sem > dquot->dq_lock > dqio_sem */ spinlock_t dq_list_lock = SPIN_LOCK_UNLOCKED; @@ -175,8 +179,7 @@ static void put_quota_format(struct quot * on all three lists, depending on its current state. * * All dquots are placed to the end of inuse_list when first created, and this - * list is used for the sync and invalidate operations, which must look - * at every dquot. + * list is used for invalidate operation, which must look at every dquot. * * Unused dquots (dq_count == 0) are added to the free_dquots list when freed, * and this list is searched whenever we need an available dquot. Dquots are @@ -264,30 +267,105 @@ static void wait_on_dquot(struct dquot * up(&dquot->dq_lock); } -static int read_dqblk(struct dquot *dquot) +void mark_info_dirty(struct super_block *sb, int type) { - int ret; + set_bit(DQF_INFO_DIRTY_B, &sb_dqopt(sb)->info[type].dqi_flags); +} +EXPORT_SYMBOL(mark_info_dirty); + +/* + * Read dquot from disk and alloc space for it + */ + +int dquot_acquire(struct dquot *dquot) +{ + int ret = 0, ret2 = 0; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); down(&dquot->dq_lock); down(&dqopt->dqio_sem); - ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot); + if (!test_bit(DQ_READ_B, &dquot->dq_flags)) + ret = dqopt->ops[dquot->dq_type]->read_dqblk(dquot); + if (ret < 0) + goto out_iolock; + set_bit(DQ_READ_B, &dquot->dq_flags); + /* Instantiate dquot if needed */ + if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && !dquot->dq_off) { + ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot); + /* Write the info if needed */ + if (info_dirty(&dqopt->info[dquot->dq_type])) + ret2 = dqopt->ops[dquot->dq_type]->write_file_info(dquot->dq_sb, dquot->dq_type); + if (ret < 0) + goto out_iolock; + if (ret2 < 0) { + ret = ret2; + goto out_iolock; + } + } + set_bit(DQ_ACTIVE_B, &dquot->dq_flags); +out_iolock: up(&dqopt->dqio_sem); up(&dquot->dq_lock); return ret; } -static int commit_dqblk(struct dquot *dquot) +/* + * Write dquot to disk + */ +int dquot_commit(struct dquot *dquot) { - int ret; + int ret = 0, ret2 = 0; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); down(&dqopt->dqio_sem); - ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot); + spin_lock(&dq_list_lock); + if (!test_and_clear_bit(DQ_MOD_B, &dquot->dq_flags)) { + spin_unlock(&dq_list_lock); + goto out_sem; + } + spin_unlock(&dq_list_lock); + /* Inactive dquot can be only if there was error during read/init + * => we have better not writing it */ + if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { + ret = dqopt->ops[dquot->dq_type]->commit_dqblk(dquot); + if (info_dirty(&dqopt->info[dquot->dq_type])) + ret2 = dqopt->ops[dquot->dq_type]->write_file_info(dquot->dq_sb, dquot->dq_type); + if (ret >= 0) + ret = ret2; + } +out_sem: up(&dqopt->dqio_sem); return ret; } +/* + * Release dquot + */ +int dquot_release(struct dquot *dquot) +{ + int ret = 0, ret2 = 0; + struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); + + down(&dquot->dq_lock); + /* Check whether we are not racing with some other dqget() */ + if (atomic_read(&dquot->dq_count) > 1) + goto out_dqlock; + down(&dqopt->dqio_sem); + if (dqopt->ops[dquot->dq_type]->release_dqblk) { + ret = dqopt->ops[dquot->dq_type]->release_dqblk(dquot); + /* Write the info */ + if (info_dirty(&dqopt->info[dquot->dq_type])) + ret2 = dqopt->ops[dquot->dq_type]->write_file_info(dquot->dq_sb, dquot->dq_type); + if (ret >= 0) + ret = ret2; + } + clear_bit(DQ_ACTIVE_B, &dquot->dq_flags); + up(&dqopt->dqio_sem); +out_dqlock: + up(&dquot->dq_lock); + return ret; +} + /* Invalidate all dquots on the list. Note that this function is called after * quota is disabled so no new quota might be created. Because we hold * dqonoff_sem and pointers were already removed from inodes we actually know @@ -343,6 +421,11 @@ restart: continue; if (!dquot_dirty(dquot)) continue; + /* Dirty and inactive can be only bad dquot... */ + if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { + test_and_clear_bit(DQ_MOD_B, &dquot->dq_flags); + continue; + } atomic_inc(&dquot->dq_count); dqstats.lookups++; spin_unlock(&dq_list_lock); @@ -353,11 +436,9 @@ restart: spin_unlock(&dq_list_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) - if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt) && info_dirty(&dqopt->info[cnt])) { - down(&dqopt->dqio_sem); - dqopt->ops[cnt]->write_file_info(sb, cnt); - up(&dqopt->dqio_sem); - } + if ((cnt == type || type == -1) && sb_has_quota_enabled(sb, cnt) + && info_dirty(&dqopt->info[cnt])) + sb->dq_op->write_info(sb, cnt); spin_lock(&dq_list_lock); dqstats.syncs++; spin_unlock(&dq_list_lock); @@ -432,11 +513,19 @@ we_slept: spin_unlock(&dq_list_lock); return; } - if (dquot_dirty(dquot)) { + if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && dquot_dirty(dquot)) { spin_unlock(&dq_list_lock); dquot->dq_sb->dq_op->write_dquot(dquot); goto we_slept; } + /* Clear flag in case dquot was inactive (something bad happened) */ + test_and_clear_bit(DQ_MOD_B, &dquot->dq_flags); + if (test_bit(DQ_ACTIVE_B, &dquot->dq_flags)) { + spin_unlock(&dq_list_lock); + dquot->dq_sb->dq_op->release_dquot(dquot); + goto we_slept; + } + atomic_dec(&dquot->dq_count); #ifdef __DQUOT_PARANOIA /* sanity check */ @@ -495,7 +584,6 @@ we_slept: insert_dquot_hash(dquot); dqstats.lookups++; spin_unlock(&dq_list_lock); - read_dqblk(dquot); } else { if (!atomic_read(&dquot->dq_count)) remove_free_dquot(dquot); @@ -503,10 +591,15 @@ we_slept: dqstats.cache_hits++; dqstats.lookups++; spin_unlock(&dq_list_lock); - wait_on_dquot(dquot); if (empty) kmem_cache_free(dquot_cachep, empty); } + wait_on_dquot(dquot); + /* Read the dquot and instantiate it (everything done only if needed) */ + if (!test_bit(DQ_ACTIVE_B, &dquot->dq_flags) && sb->dq_op->acquire_dquot(dquot) < 0) { + dqput(dquot); + return NODQUOT; + } #ifdef __DQUOT_PARANOIA if (!dquot->dq_sb) /* Has somebody invalidated entry under us? */ @@ -819,19 +912,19 @@ static int check_bdq(struct dquot *dquot * * Note: this is a blocking operation. */ -void dquot_initialize(struct inode *inode, int type) +int dquot_initialize(struct inode *inode, int type) { unsigned int id = 0; int cnt; /* Solve deadlock when we recurse when holding dqptr_sem... */ if (IS_NOQUOTA(inode)) - return; + return 0; down_write(&sb_dqopt(inode->i_sb)->dqptr_sem); /* Having dqptr_sem we know NOQUOTA flags can't be altered... */ if (IS_NOQUOTA(inode)) { up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); - return; + return 0; } /* Build list of quotas to initialize... */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { @@ -852,13 +945,14 @@ void dquot_initialize(struct inode *inod } } up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); + return 0; } /* * Release all quotas referenced by inode * Needs dqonoff_sem to guard dqput() */ -void dquot_drop(struct inode *inode) +int dquot_drop(struct inode *inode) { int cnt; @@ -871,6 +965,7 @@ void dquot_drop(struct inode *inode) } } up_write(&sb_dqopt(inode->i_sb)->dqptr_sem); + return 0; } /* @@ -958,14 +1053,14 @@ warn_put_all: /* * This is a non-blocking operation. */ -void dquot_free_space(struct inode *inode, qsize_t number) +int dquot_free_space(struct inode *inode, qsize_t number) { unsigned int cnt; /* Solve deadlock when we recurse when holding dqptr_sem... */ if (IS_NOQUOTA(inode)) { inode_add_bytes(inode, number); - return; + return QUOTA_OK; } down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); spin_lock(&dq_data_lock); @@ -981,23 +1076,24 @@ sub_bytes: inode_sub_bytes(inode, number); spin_unlock(&dq_data_lock); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + return QUOTA_OK; } /* * This is a non-blocking operation. */ -void dquot_free_inode(const struct inode *inode, unsigned long number) +int dquot_free_inode(const struct inode *inode, unsigned long number) { unsigned int cnt; /* Solve deadlock when we recurse when holding dqptr_sem... */ if (IS_NOQUOTA(inode)) - return; + return QUOTA_OK; down_read(&sb_dqopt(inode->i_sb)->dqptr_sem); /* Now recheck reliably when holding dqptr_sem */ if (IS_NOQUOTA(inode)) { up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); - return; + return QUOTA_OK; } spin_lock(&dq_data_lock); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { @@ -1007,6 +1103,7 @@ void dquot_free_inode(const struct inode } spin_unlock(&dq_data_lock); up_read(&sb_dqopt(inode->i_sb)->dqptr_sem); + return QUOTA_OK; } /* @@ -1104,6 +1201,20 @@ warn_put_all: } /* + * Write info of quota file to disk + */ +int dquot_commit_info(struct super_block *sb, int type) +{ + int ret; + struct quota_info *dqopt = sb_dqopt(sb); + + down(&dqopt->dqio_sem); + ret = dqopt->ops[type]->write_file_info(sb, type); + up(&dqopt->dqio_sem); + return ret; +} + +/* * Definitions of diskquota operations. */ struct dquot_operations dquot_operations = { @@ -1114,7 +1225,10 @@ struct dquot_operations dquot_operations .free_space = dquot_free_space, .free_inode = dquot_free_inode, .transfer = dquot_transfer, - .write_dquot = commit_dqblk + .write_dquot = dquot_commit, + .acquire_dquot = dquot_acquire, + .release_dquot = dquot_release, + .write_info = dquot_commit_info }; /* Function used by filesystems for initializing the dquot_operations structure */ @@ -1154,13 +1268,14 @@ int vfs_quota_off(struct super_block *sb { int cnt; struct quota_info *dqopt = sb_dqopt(sb); - - if (!sb) - goto out; + struct inode *toputinode[MAXQUOTAS]; + struct vfsmount *toputmnt[MAXQUOTAS]; /* We need to serialize quota_off() for device */ down(&dqopt->dqonoff_sem); for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + toputinode[cnt] = NULL; + toputmnt[cnt] = NULL; if (type != -1 && cnt != type) continue; if (!sb_has_quota_enabled(sb, cnt)) @@ -1172,94 +1287,115 @@ int vfs_quota_off(struct super_block *sb invalidate_dquots(sb, cnt); /* * Now all dquots should be invalidated, all writes done so we should be only - * users of the info. No locks needed. + * users of the info. */ - if (info_dirty(&dqopt->info[cnt])) { - down(&dqopt->dqio_sem); - dqopt->ops[cnt]->write_file_info(sb, cnt); - up(&dqopt->dqio_sem); - } + if (info_dirty(&dqopt->info[cnt])) + sb->dq_op->write_info(sb, cnt); if (dqopt->ops[cnt]->free_file_info) dqopt->ops[cnt]->free_file_info(sb, cnt); put_quota_format(dqopt->info[cnt].dqi_format); - fput(dqopt->files[cnt]); - dqopt->files[cnt] = (struct file *)NULL; + toputinode[cnt] = dqopt->files[cnt]; + toputmnt[cnt] = dqopt->mnt[cnt]; + dqopt->files[cnt] = NULL; + dqopt->mnt[cnt] = NULL; dqopt->info[cnt].dqi_flags = 0; dqopt->info[cnt].dqi_igrace = 0; dqopt->info[cnt].dqi_bgrace = 0; dqopt->ops[cnt] = NULL; } up(&dqopt->dqonoff_sem); -out: + /* Sync the superblock so that buffers with quota data are written to + * disk (and so userspace sees correct data afterwards). + * The reference to vfsmnt we are still holding protects us from + * umount (we don't have it only when quotas are turned on/off for + * journal replay but in that case we are guarded by the fs anyway). */ + if (sb->s_op->sync_fs) + sb->s_op->sync_fs(sb, 1); + sync_blockdev(sb->s_bdev); + /* Now the quota files are just ordinary files and we can set the + * inode flags back. Moreover we discard the pagecache so that + * userspace sees the writes we did bypassing the pagecache. We + * must also discard the blockdev buffers so that we see the + * changes done by userspace on the next quotaon() */ + for (cnt = 0; cnt < MAXQUOTAS; cnt++) + if (toputinode[cnt]) { + down(&dqopt->dqonoff_sem); + /* If quota was reenabled in the meantime, we have + * nothing to do */ + if (!sb_has_quota_enabled(sb, cnt)) { + down(&toputinode[cnt]->i_sem); + toputinode[cnt]->i_flags &= ~(S_IMMUTABLE | + S_NOATIME | S_NOQUOTA); + truncate_inode_pages(&toputinode[cnt]->i_data, 0); + up(&toputinode[cnt]->i_sem); + mark_inode_dirty(toputinode[cnt]); + iput(toputinode[cnt]); + } + up(&dqopt->dqonoff_sem); + /* We don't hold the reference when we turned on quotas + * just for the journal replay... */ + if (toputmnt[cnt]) + mntput(toputmnt[cnt]); + } + invalidate_bdev(sb->s_bdev, 0); return 0; } -int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path) +/* + * Turn quotas on on a device + */ + +/* Helper function when we already have the inode */ +static int vfs_quota_on_inode(struct inode *inode, int type, int format_id) { - struct file *f; - struct inode *inode; - struct quota_info *dqopt = sb_dqopt(sb); struct quota_format_type *fmt = find_quota_format(format_id); - int error, cnt; - struct dquot *to_drop[MAXQUOTAS]; - unsigned int oldflags; + struct super_block *sb = inode->i_sb; + struct quota_info *dqopt = sb_dqopt(sb); + int error; + int oldflags = -1; if (!fmt) return -ESRCH; - f = filp_open(path, O_RDWR, 0600); - if (IS_ERR(f)) { - error = PTR_ERR(f); + if (!S_ISREG(inode->i_mode)) { + error = -EACCES; + goto out_fmt; + } + if (IS_RDONLY(inode)) { + error = -EROFS; + goto out_fmt; + } + if (!sb->s_op->quota_write || !sb->s_op->quota_read) { + error = -EINVAL; goto out_fmt; } - error = -EIO; - if (!f->f_op || !f->f_op->read || !f->f_op->write) - goto out_f; - error = security_quota_on(f); - if (error) - goto out_f; - inode = f->f_dentry->d_inode; - error = -EACCES; - if (!S_ISREG(inode->i_mode)) - goto out_f; + /* As we bypass the pagecache we must now flush the inode so that + * we see all the changes from userspace... */ + write_inode_now(inode, 1); + /* And now flush the block cache so that kernel sees the changes */ + invalidate_bdev(sb->s_bdev, 0); + down(&inode->i_sem); down(&dqopt->dqonoff_sem); if (sb_has_quota_enabled(sb, type)) { error = -EBUSY; goto out_lock; } - oldflags = inode->i_flags; - dqopt->files[type] = f; - error = -EINVAL; - if (!fmt->qf_ops->check_quota_file(sb, type)) - goto out_file_init; /* We don't want quota and atime on quota files (deadlocks possible) - * We also need to set GFP mask differently because we cannot recurse - * into filesystem when allocating page for quota inode */ + * Also nobody should write to the file - we use special IO operations + * which ignore the immutable bit. */ down_write(&dqopt->dqptr_sem); - inode->i_flags |= S_NOQUOTA | S_NOATIME; - - /* - * We write to quota files deep within filesystem code. We don't want - * the VFS to reenter filesystem code when it tries to allocate a - * pagecache page for the quota file write. So clear __GFP_FS in - * the quota file's allocation flags. - */ - mapping_set_gfp_mask(inode->i_mapping, - mapping_gfp_mask(inode->i_mapping) & ~__GFP_FS); - - for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - to_drop[cnt] = inode->i_dquot[cnt]; - inode->i_dquot[cnt] = NODQUOT; - } - inode->i_flags &= ~S_QUOTA; + oldflags = inode->i_flags & (S_NOATIME | S_IMMUTABLE | S_NOQUOTA); + inode->i_flags |= S_NOQUOTA | S_NOATIME | S_IMMUTABLE; up_write(&dqopt->dqptr_sem); - /* We must put dquots outside of dqptr_sem because we may need to - * start transaction for write */ - for (cnt = 0; cnt < MAXQUOTAS; cnt++) { - if (to_drop[cnt]) - dqput(to_drop[cnt]); - } + + error = -EIO; + dqopt->files[type] = igrab(inode); + if (!dqopt->files[type]) + goto out_lock; + error = -EINVAL; + if (!fmt->qf_ops->check_quota_file(sb, type)) + goto out_file_init; dqopt->ops[type] = fmt->qf_ops; dqopt->info[type].dqi_format = fmt; @@ -1269,6 +1405,7 @@ int vfs_quota_on(struct super_block *sb, goto out_file_init; } up(&dqopt->dqio_sem); + up(&inode->i_sem); set_enable_flags(dqopt, type); add_dquot_ref(sb, type); @@ -1277,18 +1414,51 @@ int vfs_quota_on(struct super_block *sb, return 0; out_file_init: - inode->i_flags = oldflags; dqopt->files[type] = NULL; + iput(inode); out_lock: up(&dqopt->dqonoff_sem); -out_f: - filp_close(f, NULL); + if (oldflags != -1) { + down_write(&dqopt->dqptr_sem); + /* Set the flags back (in the case of accidental quotaon() + * on a wrong file we don't want to mess up the flags) */ + inode->i_flags &= ~(S_NOATIME | S_NOQUOTA | S_IMMUTABLE); + inode->i_flags |= oldflags; + up_write(&dqopt->dqptr_sem); + } + up(&inode->i_sem); out_fmt: put_quota_format(fmt); return error; } +/* Actual function called from quotactl() */ +int vfs_quota_on(struct super_block *sb, int type, int format_id, char *path) +{ + struct nameidata nd; + int error; + + error = path_lookup(path, LOOKUP_FOLLOW, &nd); + if (error < 0) + return error; + error = security_quota_on(nd.dentry); + if (error) + goto out_path; + /* Quota file not on the same filesystem? */ + if (nd.mnt->mnt_sb != sb) + error = -EXDEV; + else { + error = vfs_quota_on_inode(nd.dentry->d_inode, type, format_id); + if (!error) + sb_dqopt(sb)->mnt[type] = mntget(nd.mnt); + } + +out_path: + path_release(&nd); + return error; +} + /* Generic routine for getting common part of quota structure */ static void do_get_dqblk(struct dquot *dquot, struct if_dqblk *di) { @@ -1430,8 +1600,10 @@ int vfs_set_dqinfo(struct super_block *s mi->dqi_igrace = ii->dqi_igrace; if (ii->dqi_valid & IIF_FLAGS) mi->dqi_flags = (mi->dqi_flags & ~DQF_MASK) | (ii->dqi_flags & DQF_MASK); - mark_info_dirty(mi); spin_unlock(&dq_data_lock); + mark_info_dirty(sb, type); + /* Force write to disk */ + sb->dq_op->write_info(sb, type); up(&sb_dqopt(sb)->dqonoff_sem); return 0; } @@ -1564,3 +1736,15 @@ EXPORT_SYMBOL(dqstats); EXPORT_SYMBOL(dq_list_lock); EXPORT_SYMBOL(dq_data_lock); EXPORT_SYMBOL(init_dquot_operations); +EXPORT_SYMBOL(dquot_commit); +EXPORT_SYMBOL(dquot_commit_info); +EXPORT_SYMBOL(dquot_acquire); +EXPORT_SYMBOL(dquot_release); +EXPORT_SYMBOL(dquot_initialize); +EXPORT_SYMBOL(dquot_drop); +EXPORT_SYMBOL(dquot_alloc_space); +EXPORT_SYMBOL(dquot_alloc_inode); +EXPORT_SYMBOL(dquot_free_space); +EXPORT_SYMBOL(dquot_free_inode); +EXPORT_SYMBOL(dquot_transfer); + Index: linux-2.6.5-7.283/fs/quota_v2.c =================================================================== --- linux-2.6.5-7.283.orig/fs/quota_v2.c +++ linux-2.6.5-7.283/fs/quota_v2.c @@ -13,7 +13,6 @@ #include #include -#include MODULE_AUTHOR("Jan Kara"); MODULE_DESCRIPTION("Quota format v2 support"); @@ -30,19 +29,15 @@ typedef char *dqbuf_t; static int v2_check_quota_file(struct super_block *sb, int type) { struct v2_disk_dqheader dqhead; - struct file *f = sb_dqopt(sb)->files[type]; - mm_segment_t fs; ssize_t size; - loff_t offset = 0; static const uint quota_magics[] = V2_INITQMAGICS; static const uint quota_versions[] = V2_INITQVERSIONS; - fs = get_fs(); - set_fs(KERNEL_DS); - size = f->f_op->read(f, (char *)&dqhead, sizeof(struct v2_disk_dqheader), &offset); - set_fs(fs); - if (size != sizeof(struct v2_disk_dqheader)) + size = sb->s_op->quota_read(sb, type, (char *)&dqhead, sizeof(struct v2_disk_dqheader), 0); + if (size != sizeof(struct v2_disk_dqheader)) { + printk("failed read\n"); return 0; + } if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] || le32_to_cpu(dqhead.dqh_version) != quota_versions[type]) return 0; @@ -52,20 +47,15 @@ static int v2_check_quota_file(struct su /* Read information header from quota file */ static int v2_read_file_info(struct super_block *sb, int type) { - mm_segment_t fs; struct v2_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 = V2_DQINFOOFF; - fs = get_fs(); - set_fs(KERNEL_DS); - size = f->f_op->read(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset); - set_fs(fs); + size = sb->s_op->quota_read(sb, type, (char *)&dinfo, + sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); if (size != sizeof(struct v2_disk_dqinfo)) { printk(KERN_WARNING "Can't read info structure on device %s.\n", - f->f_vfsmnt->mnt_sb->s_id); + sb->s_id); return -1; } info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); @@ -80,12 +70,9 @@ static int v2_read_file_info(struct supe /* Write information header to quota file */ static int v2_write_file_info(struct super_block *sb, int type) { - mm_segment_t fs; struct v2_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 = V2_DQINFOOFF; info->dqi_flags &= ~DQF_INFO_DIRTY; dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace); @@ -94,13 +81,13 @@ static int v2_write_file_info(struct sup dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.dqi_blocks); dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.dqi_free_blk); dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.dqi_free_entry); - fs = get_fs(); - set_fs(KERNEL_DS); - size = f->f_op->write(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset); - set_fs(fs); + + size = sb->s_op->quota_write(sb, type, (char *)&dinfo, + sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF); + if (size != sizeof(struct v2_disk_dqinfo)) { printk(KERN_WARNING "Can't write info structure on device %s.\n", - f->f_vfsmnt->mnt_sb->s_id); + sb->s_id); return -1; } return 0; @@ -144,38 +131,24 @@ static inline void freedqbuf(dqbuf_t buf kfree(buf); } -static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf) +static inline ssize_t read_blk(struct super_block *sb, int type, uint blk, dqbuf_t buf) { - mm_segment_t fs; - ssize_t ret; - loff_t offset = blk<f_op->read(filp, (char *)buf, V2_DQBLKSIZE, &offset); - set_fs(fs); - return ret; + return sb->s_op->quota_read(sb, type, (char *)buf, + V2_DQBLKSIZE, blk << V2_DQBLKSIZE_BITS); } -static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf) +static inline ssize_t write_blk(struct super_block *sb, int type, uint blk, dqbuf_t buf) { - mm_segment_t fs; - ssize_t ret; - loff_t offset = blk<f_op->write(filp, (char *)buf, V2_DQBLKSIZE, &offset); - set_fs(fs); - return ret; - + return sb->s_op->quota_write(sb, type, (char *)buf, + V2_DQBLKSIZE, blk << V2_DQBLKSIZE_BITS); } /* Remove empty block from list and return it */ -static int get_free_dqblk(struct file *filp, struct mem_dqinfo *info) +static int get_free_dqblk(struct super_block *sb, int type) { dqbuf_t buf = getdqbuf(); + struct mem_dqinfo *info = sb_dqinfo(sb, type); struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; int ret, blk; @@ -183,17 +156,18 @@ static int get_free_dqblk(struct file *f return -ENOMEM; if (info->u.v2_i.dqi_free_blk) { blk = info->u.v2_i.dqi_free_blk; - if ((ret = read_blk(filp, blk, buf)) < 0) + if ((ret = read_blk(sb, type, blk, buf)) < 0) goto out_buf; info->u.v2_i.dqi_free_blk = le32_to_cpu(dh->dqdh_next_free); } else { memset(buf, 0, V2_DQBLKSIZE); - if ((ret = write_blk(filp, info->u.v2_i.dqi_blocks, buf)) < 0) /* Assure block allocation... */ + /* Assure block allocation... */ + if ((ret = write_blk(sb, type, info->u.v2_i.dqi_blocks, buf)) < 0) goto out_buf; blk = info->u.v2_i.dqi_blocks++; } - mark_info_dirty(info); + mark_info_dirty(sb, type); ret = blk; out_buf: freedqbuf(buf); @@ -201,8 +175,9 @@ out_buf: } /* Insert empty block to the list */ -static int put_free_dqblk(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk) +static int put_free_dqblk(struct super_block *sb, int type, dqbuf_t buf, uint blk) { + struct mem_dqinfo *info = sb_dqinfo(sb, type); struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; int err; @@ -210,16 +185,18 @@ static int put_free_dqblk(struct file *f dh->dqdh_prev_free = cpu_to_le32(0); dh->dqdh_entries = cpu_to_le16(0); info->u.v2_i.dqi_free_blk = blk; - mark_info_dirty(info); - if ((err = write_blk(filp, blk, buf)) < 0) /* Some strange block. We had better leave it... */ + mark_info_dirty(sb, type); + /* Some strange block. We had better leave it... */ + if ((err = write_blk(sb, type, blk, buf)) < 0) 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) +static int remove_free_dqentry(struct super_block *sb, int type, dqbuf_t buf, uint blk) { dqbuf_t tmpbuf = getdqbuf(); + struct mem_dqinfo *info = sb_dqinfo(sb, type); struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free); int err; @@ -227,26 +204,27 @@ static int remove_free_dqentry(struct fi if (!tmpbuf) return -ENOMEM; if (nextblk) { - if ((err = read_blk(filp, nextblk, tmpbuf)) < 0) + if ((err = read_blk(sb, type, nextblk, tmpbuf)) < 0) goto out_buf; ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free; - if ((err = write_blk(filp, nextblk, tmpbuf)) < 0) + if ((err = write_blk(sb, type, nextblk, tmpbuf)) < 0) goto out_buf; } if (prevblk) { - if ((err = read_blk(filp, prevblk, tmpbuf)) < 0) + if ((err = read_blk(sb, type, prevblk, tmpbuf)) < 0) goto out_buf; ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free; - if ((err = write_blk(filp, prevblk, tmpbuf)) < 0) + if ((err = write_blk(sb, type, prevblk, tmpbuf)) < 0) goto out_buf; } else { info->u.v2_i.dqi_free_entry = nextblk; - mark_info_dirty(info); + mark_info_dirty(sb, type); } 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 */ + /* No matter whether write succeeds block is out of list */ + if (write_blk(sb, type, blk, buf) < 0) printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk); return 0; out_buf: @@ -255,9 +233,10 @@ out_buf: } /* 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) +static int insert_free_dqentry(struct super_block *sb, int type, dqbuf_t buf, uint blk) { dqbuf_t tmpbuf = getdqbuf(); + struct mem_dqinfo *info = sb_dqinfo(sb, type); struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf; int err; @@ -265,18 +244,18 @@ static int insert_free_dqentry(struct fi return -ENOMEM; dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_entry); dh->dqdh_prev_free = cpu_to_le32(0); - if ((err = write_blk(filp, blk, buf)) < 0) + if ((err = write_blk(sb, type, blk, buf)) < 0) goto out_buf; if (info->u.v2_i.dqi_free_entry) { - if ((err = read_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0) + if ((err = read_blk(sb, type, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0) goto out_buf; ((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk); - if ((err = write_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0) + if ((err = write_blk(sb, type, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0) goto out_buf; } freedqbuf(tmpbuf); info->u.v2_i.dqi_free_entry = blk; - mark_info_dirty(info); + mark_info_dirty(sb, type); return 0; out_buf: freedqbuf(tmpbuf); @@ -286,8 +265,8 @@ out_buf: /* 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; + struct super_block *sb = dquot->dq_sb; + struct mem_dqinfo *info = sb_dqopt(sb)->info+dquot->dq_type; uint blk, i; struct v2_disk_dqdbheader *dh; struct v2_disk_dqblk *ddquot; @@ -303,22 +282,23 @@ static uint find_free_dqentry(struct dqu ddquot = GETENTRIES(buf); if (info->u.v2_i.dqi_free_entry) { blk = info->u.v2_i.dqi_free_entry; - if ((*err = read_blk(filp, blk, buf)) < 0) + if ((*err = read_blk(sb, dquot->dq_type, blk, buf)) < 0) goto out_buf; } else { - blk = get_free_dqblk(filp, info); + blk = get_free_dqblk(sb, dquot->dq_type); if ((int)blk < 0) { *err = blk; freedqbuf(buf); return 0; } memset(buf, 0, V2_DQBLKSIZE); - info->u.v2_i.dqi_free_entry = blk; /* This is enough as block is already zeroed and entry list is empty... */ - mark_info_dirty(info); + /* This is enough as block is already zeroed and entry list is empty... */ + info->u.v2_i.dqi_free_entry = blk; + mark_info_dirty(sb, dquot->dq_type); } if (le16_to_cpu(dh->dqdh_entries)+1 >= V2_DQSTRINBLK) /* Block will be full? */ - if ((*err = remove_free_dqentry(filp, info, buf, blk)) < 0) { + if ((*err = remove_free_dqentry(sb, dquot->dq_type, buf, blk)) < 0) { printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk); goto out_buf; } @@ -333,7 +313,7 @@ static uint find_free_dqentry(struct dqu goto out_buf; } #endif - if ((*err = write_blk(filp, blk, buf)) < 0) { + if ((*err = write_blk(sb, dquot->dq_type, blk, buf)) < 0) { printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota data block %u.\n", blk); goto out_buf; } @@ -348,8 +328,7 @@ out_buf: /* Insert reference to structure into the trie */ static int do_insert_tree(struct dquot *dquot, uint *treeblk, 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; + struct super_block *sb = dquot->dq_sb; dqbuf_t buf; int ret = 0, newson = 0, newact = 0; u32 *ref; @@ -358,7 +337,7 @@ static int do_insert_tree(struct dquot * if (!(buf = getdqbuf())) return -ENOMEM; if (!*treeblk) { - ret = get_free_dqblk(filp, info); + ret = get_free_dqblk(sb, dquot->dq_type); if (ret < 0) goto out_buf; *treeblk = ret; @@ -366,7 +345,7 @@ static int do_insert_tree(struct dquot * newact = 1; } else { - if ((ret = read_blk(filp, *treeblk, buf)) < 0) { + if ((ret = read_blk(sb, dquot->dq_type, *treeblk, buf)) < 0) { printk(KERN_ERR "VFS: Can't read tree quota block %u.\n", *treeblk); goto out_buf; } @@ -389,10 +368,10 @@ static int do_insert_tree(struct dquot * 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); + ret = write_blk(sb, dquot->dq_type, *treeblk, buf); } else if (newact && ret < 0) - put_free_dqblk(filp, info, buf, *treeblk); + put_free_dqblk(sb, dquot->dq_type, buf, *treeblk); out_buf: freedqbuf(buf); return ret; @@ -411,19 +390,14 @@ static inline int dq_insert_tree(struct static int v2_write_dquot(struct dquot *dquot) { int type = dquot->dq_type; - struct file *filp; - mm_segment_t fs; - loff_t offset; ssize_t ret; struct v2_disk_dqblk ddquot, empty; if (!dquot->dq_off) if ((ret = dq_insert_tree(dquot)) < 0) { - printk(KERN_ERR "VFS: Error %Zd occurred while creating quota.\n", ret); + printk(KERN_ERR "VFS: Error %d occurred while creating quota.\n", ret); return ret; } - filp = sb_dqopt(dquot->dq_sb)->files[type]; - offset = dquot->dq_off; mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id); /* Argh... We may need to write structure full of zeroes but that would be * treated as an empty place by the rest of the code. Format change would @@ -431,10 +405,10 @@ static int v2_write_dquot(struct dquot * memset(&empty, 0, sizeof(struct v2_disk_dqblk)); if (!memcmp(&empty, &ddquot, sizeof(struct v2_disk_dqblk))) ddquot.dqb_itime = cpu_to_le64(1); - fs = get_fs(); - set_fs(KERNEL_DS); - ret = filp->f_op->write(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset); - set_fs(fs); + + ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type, + (char *)&ddquot, sizeof(struct v2_disk_dqblk), dquot->dq_off); + if (ret != sizeof(struct v2_disk_dqblk)) { printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", dquot->dq_sb->s_id); if (ret >= 0) @@ -450,8 +424,8 @@ static int v2_write_dquot(struct dquot * /* 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 super_block *sb = dquot->dq_sb; + int type = dquot->dq_type; struct v2_disk_dqdbheader *dh; dqbuf_t buf = getdqbuf(); int ret = 0; @@ -459,34 +433,39 @@ static int free_dqentry(struct dquot *dq if (!buf) return -ENOMEM; if (dquot->dq_off >> V2_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 >> V2_DQBLKSIZE_BITS)); + printk(KERN_ERR "VFS: Quota structure has offset to other " + "block (%u) than it should (%u).\n", blk, + (uint)(dquot->dq_off >> V2_DQBLKSIZE_BITS)); goto out_buf; } - if ((ret = read_blk(filp, blk, buf)) < 0) { + if ((ret = read_blk(sb, type, blk, buf)) < 0) { printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk); goto out_buf; } dh = (struct v2_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); + if ((ret = remove_free_dqentry(sb, type, buf, blk)) < 0 || + (ret = put_free_dqblk(sb, type, 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 << V2_DQBLKSIZE_BITS)-1)), 0, sizeof(struct v2_disk_dqblk)); + memset(buf+(dquot->dq_off & ((1 << V2_DQBLKSIZE_BITS)-1)), 0, + sizeof(struct v2_disk_dqblk)); if (le16_to_cpu(dh->dqdh_entries) == V2_DQSTRINBLK-1) { /* Insert will write block itself */ - if ((ret = insert_free_dqentry(filp, info, buf, blk)) < 0) { + if ((ret = insert_free_dqentry(sb, type, 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); + if ((ret = write_blk(sb, type, blk, buf)) < 0) { + printk(KERN_ERR "VFS: Can't write quota data " + "block %u\n", blk); goto out_buf; } } @@ -499,8 +478,8 @@ out_buf: /* 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; + struct super_block *sb = dquot->dq_sb; + int type = dquot->dq_type; dqbuf_t buf = getdqbuf(); int ret = 0; uint newblk; @@ -508,7 +487,7 @@ static int remove_tree(struct dquot *dqu if (!buf) return -ENOMEM; - if ((ret = read_blk(filp, *blk, buf)) < 0) { + if ((ret = read_blk(sb, type, *blk, buf)) < 0) { printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk); goto out_buf; } @@ -524,12 +503,13 @@ static int remove_tree(struct dquot *dqu ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0); for (i = 0; i < V2_DQBLKSIZE && !buf[i]; i++); /* Block got empty? */ if (i == V2_DQBLKSIZE) { - put_free_dqblk(filp, info, buf, *blk); + put_free_dqblk(sb, type, 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); + if ((ret = write_blk(sb, type, *blk, buf)) < 0) + printk(KERN_ERR "VFS: Can't write quota tree " + "block %u.\n", *blk); } out_buf: freedqbuf(buf); @@ -549,7 +529,6 @@ static int v2_delete_dquot(struct dquot /* 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; @@ -557,27 +536,31 @@ static loff_t find_block_dqentry(struct if (!buf) return -ENOMEM; - if ((ret = read_blk(filp, blk, buf)) < 0) { + if ((ret = read_blk(dquot->dq_sb, dquot->dq_type, 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 < V2_DQSTRINBLK && le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++); + for (i = 0; i < V2_DQSTRINBLK && + le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++); else { /* ID 0 as a bit more complicated searching... */ struct v2_disk_dqblk fakedquot; memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk)); for (i = 0; i < V2_DQSTRINBLK; i++) - if (!le32_to_cpu(ddquot[i].dqb_id) && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk))) + if (!le32_to_cpu(ddquot[i].dqb_id) && + memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk))) break; } if (i == V2_DQSTRINBLK) { - printk(KERN_ERR "VFS: Quota for id %u referenced but not present.\n", dquot->dq_id); + printk(KERN_ERR "VFS: Quota for id %u referenced " + "but not present.\n", dquot->dq_id); ret = -EIO; goto out_buf; } else - ret = (blk << V2_DQBLKSIZE_BITS) + sizeof(struct v2_disk_dqdbheader) + i * sizeof(struct v2_disk_dqblk); + ret = (blk << V2_DQBLKSIZE_BITS) + sizeof(struct + v2_disk_dqdbheader) + i * sizeof(struct v2_disk_dqblk); out_buf: freedqbuf(buf); return ret; @@ -586,14 +569,13 @@ out_buf: /* 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) { + if ((ret = read_blk(dquot->dq_sb, dquot->dq_type, blk, buf)) < 0) { printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); goto out_buf; } @@ -619,16 +601,13 @@ static inline loff_t find_dqentry(struct static int v2_read_dquot(struct dquot *dquot) { int type = dquot->dq_type; - struct file *filp; - mm_segment_t fs; loff_t offset; struct v2_disk_dqblk ddquot, empty; int ret = 0; - filp = sb_dqopt(dquot->dq_sb)->files[type]; - #ifdef __QUOTA_V2_PARANOIA - if (!filp || !dquot->dq_sb) { /* Invalidated quota? */ + /* Invalidated quota? */ + if (!dquot->dq_sb || !dquot->dq_sb->s_op->quota_read) { printk(KERN_ERR "VFS: Quota invalidated while reading!\n"); return -EIO; } @@ -636,7 +615,8 @@ static int v2_read_dquot(struct dquot *d 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); + 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)); @@ -644,12 +624,13 @@ static int v2_read_dquot(struct dquot *d } else { dquot->dq_off = offset; - fs = get_fs(); - set_fs(KERNEL_DS); - if ((ret = filp->f_op->read(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset)) != sizeof(struct v2_disk_dqblk)) { + if ((ret = dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, + (char *)&ddquot, sizeof(struct v2_disk_dqblk), offset)) + != sizeof(struct v2_disk_dqblk)) { if (ret >= 0) ret = -EIO; - printk(KERN_ERR "VFS: Error while reading quota structure for id %u.\n", dquot->dq_id); + printk(KERN_ERR "VFS: Error while reading quota " + "structure for id %u.\n", dquot->dq_id); memset(&ddquot, 0, sizeof(struct v2_disk_dqblk)); } else { @@ -660,7 +641,6 @@ static int v2_read_dquot(struct dquot *d if (!memcmp(&empty, &ddquot, sizeof(struct v2_disk_dqblk))) ddquot.dqb_itime = 0; } - set_fs(fs); disk2memdqb(&dquot->dq_dqb, &ddquot); } dqstats.reads++; @@ -668,15 +648,13 @@ static int v2_read_dquot(struct dquot *d return ret; } -/* Commit changes of dquot to disk - it might also mean deleting it when quota became fake one and user has no blocks... */ -static int v2_commit_dquot(struct dquot *dquot) +/* Check whether dquot should not be deleted. We know we are + * * the only one operating on dquot (thanks to dq_lock) */ +static int v2_release_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)) + if (test_bit(DQ_FAKE_B, &dquot->dq_flags) && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace)) return v2_delete_dquot(dquot); - else - return v2_write_dquot(dquot); + return 0; } static struct quota_format_ops v2_format_ops = { @@ -685,7 +663,8 @@ static struct quota_format_ops v2_format .write_file_info = v2_write_file_info, .free_file_info = NULL, .read_dqblk = v2_read_dquot, - .commit_dqblk = v2_commit_dquot, + .commit_dqblk = v2_write_dquot, + .release_dqblk = v2_release_dquot, }; static struct quota_format_type v2_quota_format = { Index: linux-2.6.5-7.283/fs/quota.c =================================================================== --- linux-2.6.5-7.283.orig/fs/quota.c +++ linux-2.6.5-7.283/fs/quota.c @@ -14,6 +14,9 @@ #include #include #include +#include +#include + /* Check validity of quotactl */ static int check_quotactl_valid(struct super_block *sb, int type, int cmd, qid_t id) @@ -134,16 +137,54 @@ restart: return NULL; } +void quota_sync_sb(struct super_block *sb, int type) +{ + int cnt; + struct inode *discard[MAXQUOTAS]; + + sb->s_qcop->quota_sync(sb, type); + /* This is not very clever (and fast) but currently I don't know about + * any other simple way of getting quota data to disk and we must get + * them there for userspace to be visible... */ + if (sb->s_op->sync_fs) + sb->s_op->sync_fs(sb, 1); + sync_blockdev(sb->s_bdev); + + /* Now when everything is written we can discard the pagecache so + * that userspace sees the changes. We need i_sem and so we could + * not do it inside dqonoff_sem. Moreover we need to be carefull + * about races with quotaoff() (that is the reason why we have own + * reference to inode). */ + down(&sb_dqopt(sb)->dqonoff_sem); + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + discard[cnt] = NULL; + if (type != -1 && cnt != type) + continue; + if (!sb_has_quota_enabled(sb, cnt)) + continue; + discard[cnt] = igrab(sb_dqopt(sb)->files[cnt]); + } + up(&sb_dqopt(sb)->dqonoff_sem); + for (cnt = 0; cnt < MAXQUOTAS; cnt++) { + if (discard[cnt]) { + down(&discard[cnt]->i_sem); + truncate_inode_pages(&discard[cnt]->i_data, 0); + up(&discard[cnt]->i_sem); + iput(discard[cnt]); + } + } +} + void sync_dquots(struct super_block *sb, int type) { if (sb) { if (sb->s_qcop->quota_sync) - sb->s_qcop->quota_sync(sb, type); + quota_sync_sb(sb, type); } else { - while ((sb = get_super_to_sync(type))) { + while ((sb = get_super_to_sync(type)) != NULL) { if (sb->s_qcop->quota_sync) - sb->s_qcop->quota_sync(sb, type); + quota_sync_sb(sb, type); drop_super(sb); } } Index: linux-2.6.5-7.283/fs/ext3/inode.c =================================================================== --- linux-2.6.5-7.283.orig/fs/ext3/inode.c +++ linux-2.6.5-7.283/fs/ext3/inode.c @@ -1015,7 +1015,7 @@ out: return ret; } -static int +int ext3_journal_dirty_data(handle_t *handle, struct buffer_head *bh) { int err = journal_dirty_data(handle, bh); Index: linux-2.6.5-7.283/fs/ext3/super.c =================================================================== --- linux-2.6.5-7.283.orig/fs/ext3/super.c +++ linux-2.6.5-7.283/fs/ext3/super.c @@ -33,6 +33,7 @@ #include #include #include +#include #include "xattr.h" #include "acl.h" @@ -505,7 +506,33 @@ static void ext3_clear_inode(struct inod ext3_discard_reservation(inode); } -static struct dquot_operations ext3_qops; +#ifdef CONFIG_QUOTA + +static int ext3_dquot_initialize(struct inode *inode, int type); +static int ext3_dquot_drop(struct inode *inode); +static int ext3_write_dquot(struct dquot *dquot); +static int ext3_acquire_dquot(struct dquot *dquot); +static int ext3_release_dquot(struct dquot *dquot); +static int ext3_write_info(struct super_block *sb, int type); +static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data, + size_t len, loff_t off); +static ssize_t ext3_quota_write(struct super_block *sb, int type, + const char *data, size_t len, loff_t off); + +static struct dquot_operations ext3_quota_operations = { + .initialize = ext3_dquot_initialize, + .drop = ext3_dquot_drop, + .alloc_space = dquot_alloc_space, + .alloc_inode = dquot_alloc_inode, + .free_space = dquot_free_space, + .free_inode = dquot_free_inode, + .transfer = dquot_transfer, + .write_dquot = ext3_write_dquot, + .acquire_dquot = ext3_acquire_dquot, + .release_dquot = ext3_release_dquot, + .write_info = ext3_write_info +}; +#endif static struct super_operations ext3_sops = { .alloc_inode = ext3_alloc_inode, @@ -522,6 +549,10 @@ static struct super_operations ext3_sops .statfs = ext3_statfs, .remount_fs = ext3_remount, .clear_inode = ext3_clear_inode, +#ifdef CONFIG_QUOTA + .quota_read = ext3_quota_read, + .quota_write = ext3_quota_write, +#endif }; static struct dentry *ext3_get_dentry(struct super_block *sb, void *vobjp) @@ -1377,7 +1408,9 @@ static int ext3_fill_super (struct super */ sb->s_op = &ext3_sops; sb->s_export_op = &ext3_export_ops; - sb->dq_op = &ext3_qops; +#ifdef CONFIG_QUOTA + sb->dq_op = &ext3_quota_operations; +#endif INIT_LIST_HEAD(&sbi->s_orphan); /* unlinked but open files */ sb->s_root = 0; @@ -2040,70 +2073,200 @@ int ext3_statfs (struct super_block * sb #ifdef CONFIG_QUOTA -/* Blocks: (2 data blocks) * (3 indirect + 1 descriptor + 1 bitmap) + superblock */ -#define EXT3_OLD_QFMT_BLOCKS 11 -/* Blocks: quota info + (4 pointer blocks + 1 entry block) * (3 indirect + 1 descriptor + 1 bitmap) + superblock */ -#define EXT3_V0_QFMT_BLOCKS 27 - -static int (*old_write_dquot)(struct dquot *dquot); -static void (*old_drop_dquot)(struct inode *inode); - -static int fmt_to_blocks(int fmt) -{ - switch (fmt) { - case QFMT_VFS_OLD: - return EXT3_OLD_QFMT_BLOCKS; - case QFMT_VFS_V0: - return EXT3_V0_QFMT_BLOCKS; - } - return EXT3_MAX_TRANS_DATA; +static inline struct inode *dquot_to_inode(struct dquot *dquot) +{ + return sb_dqopt(dquot->dq_sb)->files[dquot->dq_type]; +} + +static int ext3_dquot_initialize(struct inode *inode, int type) +{ + handle_t *handle; + int ret, err; + + /* We may create quota structure so we need to reserve enough blocks */ + handle = ext3_journal_start(inode, 2*EXT3_QUOTA_INIT_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + ret = dquot_initialize(inode, type); + err = ext3_journal_stop(handle); + if (!ret) + ret = err; + return ret; +} + +static int ext3_dquot_drop(struct inode *inode) +{ + handle_t *handle; + int ret, err; + + /* We may delete quota structure so we need to reserve enough blocks */ + handle = ext3_journal_start(inode, 2*EXT3_QUOTA_INIT_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + ret = dquot_drop(inode); + err = ext3_journal_stop(handle); + if (!ret) + ret = err; + return ret; } static int ext3_write_dquot(struct dquot *dquot) { - int nblocks; - int ret; - int err; + int ret, err; handle_t *handle; - struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); - struct inode *qinode; + struct inode *inode; - nblocks = fmt_to_blocks(dqopt->info[dquot->dq_type].dqi_format->qf_fmt_id); - qinode = dqopt->files[dquot->dq_type]->f_dentry->d_inode; - handle = ext3_journal_start(qinode, nblocks); - if (IS_ERR(handle)) { - ret = PTR_ERR(handle); - goto out; - } - ret = old_write_dquot(dquot); + inode = dquot_to_inode(dquot); + handle = ext3_journal_start(inode, + EXT3_QUOTA_TRANS_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + ret = dquot_commit(dquot); err = ext3_journal_stop(handle); - if (ret == 0) + if (!ret) ret = err; -out: return ret; } -static void ext3_drop_dquot(struct inode *inode) +static int ext3_acquire_dquot(struct dquot *dquot) { - int nblocks, type; - struct quota_info *dqopt = sb_dqopt(inode->i_sb); + int ret, err; handle_t *handle; - for (type = 0; type < MAXQUOTAS; type++) { - if (sb_has_quota_enabled(inode->i_sb, type)) - break; - } - if (type < MAXQUOTAS) - nblocks = fmt_to_blocks(dqopt->info[type].dqi_format->qf_fmt_id); - else - nblocks = 0; /* No quota => no drop */ - handle = ext3_journal_start(inode, 2*nblocks); + handle = ext3_journal_start(dquot_to_inode(dquot), + EXT3_QUOTA_INIT_BLOCKS); if (IS_ERR(handle)) - return; - old_drop_dquot(inode); - ext3_journal_stop(handle); - return; + return PTR_ERR(handle); + ret = dquot_acquire(dquot); + err = ext3_journal_stop(handle); + if (!ret) + ret = err; + return ret; } + +static int ext3_release_dquot(struct dquot *dquot) +{ + int ret, err; + handle_t *handle; + + handle = ext3_journal_start(dquot_to_inode(dquot), + EXT3_QUOTA_INIT_BLOCKS); + if (IS_ERR(handle)) + return PTR_ERR(handle); + ret = dquot_release(dquot); + err = ext3_journal_stop(handle); + if (!ret) + ret = err; + return ret; +} + +static int ext3_write_info(struct super_block *sb, int type) +{ + int ret, err; + handle_t *handle; + + /* Data block + inode block */ + handle = ext3_journal_start(sb->s_root->d_inode, 2); + if (IS_ERR(handle)) + return PTR_ERR(handle); + ret = dquot_commit_info(sb, type); + err = ext3_journal_stop(handle); + if (!ret) + ret = err; + return ret; +} + +/* Read data from quotafile - avoid pagecache and such because we cannot afford + * acquiring the locks... As quota files are never truncated and quota code + * itself serializes the operations (and noone else should touch the files) + * we don't have to be afraid of races */ +static ssize_t ext3_quota_read(struct super_block *sb, int type, char *data, + size_t len, loff_t off) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb); + int err = 0; + int offset = off & (sb->s_blocksize - 1); + int tocopy; + size_t toread; + struct buffer_head *bh; + loff_t i_size = i_size_read(inode); + + if (off > i_size) + return 0; + if (off+len > i_size) + len = i_size-off; + toread = len; + while (toread > 0) { + tocopy = sb->s_blocksize - offset < toread ? + sb->s_blocksize - offset : toread; + bh = ext3_bread(NULL, inode, blk, 0, &err); + if (err) + return err; + if (!bh) /* A hole? */ + memset(data, 0, tocopy); + else + memcpy(data, bh->b_data+offset, tocopy); + brelse(bh); + offset = 0; + toread -= tocopy; + data += tocopy; + blk++; + } + return len; +} + +/* Write to quotafile (we know the transaction is already started and has + * enough credits) */ +static ssize_t ext3_quota_write(struct super_block *sb, int type, + const char *data, size_t len, loff_t off) +{ + struct inode *inode = sb_dqopt(sb)->files[type]; + sector_t blk = off >> EXT3_BLOCK_SIZE_BITS(sb); + int err = 0; + int offset = off & (sb->s_blocksize - 1); + int tocopy; + size_t towrite = len; + struct buffer_head *bh; + handle_t *handle = journal_current_handle(); + + down(&inode->i_sem); + while (towrite > 0) { + tocopy = sb->s_blocksize - offset < towrite ? + sb->s_blocksize - offset : towrite; + bh = ext3_bread(handle, inode, blk, 1, &err); + if (!bh) + goto out; + + lock_buffer(bh); + memcpy(bh->b_data+offset, data, tocopy); + flush_dcache_page(bh->b_page); + unlock_buffer(bh); + /* Always do at least ordered writes for quotas */ + err = ext3_journal_dirty_data(handle, bh); + mark_buffer_dirty(bh); + brelse(bh); + if (err) + goto out; + offset = 0; + towrite -= tocopy; + data += tocopy; + blk++; + } +out: + if (len == towrite) + return err; + if (inode->i_size < off+len-towrite) { + i_size_write(inode, off+len-towrite); + EXT3_I(inode)->i_disksize = inode->i_size; + } + inode->i_version++; + inode->i_mtime = inode->i_ctime = CURRENT_TIME; + ext3_mark_inode_dirty(handle, inode); + up(&inode->i_sem); + return len - towrite; +} + #endif static struct super_block *ext3_get_sb(struct file_system_type *fs_type, @@ -2128,13 +2291,7 @@ static int __init init_ext3_fs(void) err = init_inodecache(); if (err) goto out1; -#ifdef CONFIG_QUOTA - init_dquot_operations(&ext3_qops); - old_write_dquot = ext3_qops.write_dquot; - old_drop_dquot = ext3_qops.drop; - ext3_qops.write_dquot = ext3_write_dquot; - ext3_qops.drop = ext3_drop_dquot; -#endif + err = register_filesystem_lifo(&ext3_fs_type); if (err) goto out; Index: linux-2.6.5-7.283/fs/quota_v1.c =================================================================== --- linux-2.6.5-7.283.orig/fs/quota_v1.c +++ linux-2.6.5-7.283/fs/quota_v1.c @@ -7,7 +7,6 @@ #include #include -#include #include MODULE_AUTHOR("Jan Kara"); @@ -41,23 +40,14 @@ static void v1_mem2disk_dqblk(struct v1_ static int v1_read_dqblk(struct dquot *dquot) { int type = dquot->dq_type; - struct file *filp; - mm_segment_t fs; - loff_t offset; struct v1_disk_dqblk dqblk; - filp = sb_dqopt(dquot->dq_sb)->files[type]; - if (filp == (struct file *)NULL) + if (!sb_dqopt(dquot->dq_sb)->files[type]) return -EINVAL; - /* Now we are sure filp is valid */ - offset = v1_dqoff(dquot->dq_id); /* Set structure to 0s in case read fails/is after end of file */ memset(&dqblk, 0, sizeof(struct v1_disk_dqblk)); - fs = get_fs(); - set_fs(KERNEL_DS); - filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset); - set_fs(fs); + dquot->dq_sb->s_op->quota_read(dquot->dq_sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id)); v1_disk2mem_dqblk(&dquot->dq_dqb, &dqblk); if (dquot->dq_dqb.dqb_bhardlimit == 0 && dquot->dq_dqb.dqb_bsoftlimit == 0 && @@ -71,17 +61,9 @@ static int v1_read_dqblk(struct dquot *d static int v1_commit_dqblk(struct dquot *dquot) { short type = dquot->dq_type; - struct file *filp; - mm_segment_t fs; - loff_t offset; ssize_t ret; struct v1_disk_dqblk dqblk; - filp = sb_dqopt(dquot->dq_sb)->files[type]; - offset = v1_dqoff(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. @@ -93,9 +75,10 @@ static int v1_commit_dqblk(struct dquot dqblk.dqb_itime = sb_dqopt(dquot->dq_sb)->info[type].dqi_igrace; } ret = 0; - if (filp) - ret = filp->f_op->write(filp, (char *)&dqblk, - sizeof(struct v1_disk_dqblk), &offset); + if (sb_dqopt(dquot->dq_sb)->files[type]) + ret = dquot->dq_sb->s_op->quota_write(dquot->dq_sb, type, (char *)&dqblk, + sizeof(struct v1_disk_dqblk), v1_dqoff(dquot->dq_id)); + if (ret != sizeof(struct v1_disk_dqblk)) { printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", dquot->dq_sb->s_id); @@ -106,7 +89,6 @@ static int v1_commit_dqblk(struct dquot ret = 0; out: - set_fs(fs); dqstats.writes++; return ret; @@ -126,14 +108,11 @@ struct v2_disk_dqheader { static int v1_check_quota_file(struct super_block *sb, int type) { - struct file *f = sb_dqopt(sb)->files[type]; - struct inode *inode = f->f_dentry->d_inode; + struct inode *inode = sb_dqopt(sb)->files[type]; ulong blocks; size_t off; struct v2_disk_dqheader dqhead; - mm_segment_t fs; ssize_t size; - loff_t offset = 0; loff_t isize; static const uint quota_magics[] = V2_INITQMAGICS; @@ -145,10 +124,7 @@ static int v1_check_quota_file(struct su if ((blocks % sizeof(struct v1_disk_dqblk) * BLOCK_SIZE + off) % sizeof(struct v1_disk_dqblk)) return 0; /* Doublecheck whether we didn't get file with new format - with old quotactl() this could happen */ - fs = get_fs(); - set_fs(KERNEL_DS); - size = f->f_op->read(f, (char *)&dqhead, sizeof(struct v2_disk_dqheader), &offset); - set_fs(fs); + size = sb->s_op->quota_read(sb, type, (char *)&dqhead, sizeof(struct v2_disk_dqheader), 0); if (size != sizeof(struct v2_disk_dqheader)) return 1; /* Probably not new format */ if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type]) @@ -160,16 +136,10 @@ static int v1_check_quota_file(struct su static int v1_read_file_info(struct super_block *sb, int type) { struct quota_info *dqopt = sb_dqopt(sb); - mm_segment_t fs; - loff_t offset; - struct file *filp = dqopt->files[type]; struct v1_disk_dqblk dqblk; int ret; - offset = v1_dqoff(0); - fs = get_fs(); - set_fs(KERNEL_DS); - if ((ret = filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset)) != sizeof(struct v1_disk_dqblk)) { + if ((ret = sb->s_op->quota_read(sb, type, (char *)&dqblk, sizeof(struct v1_disk_dqblk), v1_dqoff(0))) != sizeof(struct v1_disk_dqblk)) { if (ret >= 0) ret = -EIO; goto out; @@ -178,38 +148,31 @@ static int v1_read_file_info(struct supe dqopt->info[type].dqi_igrace = dqblk.dqb_itime ? dqblk.dqb_itime : MAX_IQ_TIME; dqopt->info[type].dqi_bgrace = dqblk.dqb_btime ? dqblk.dqb_btime : MAX_DQ_TIME; out: - set_fs(fs); return ret; } static int v1_write_file_info(struct super_block *sb, int type) { struct quota_info *dqopt = sb_dqopt(sb); - mm_segment_t fs; - struct file *filp = dqopt->files[type]; struct v1_disk_dqblk dqblk; - loff_t offset; int ret; dqopt->info[type].dqi_flags &= ~DQF_INFO_DIRTY; - offset = v1_dqoff(0); - fs = get_fs(); - set_fs(KERNEL_DS); - if ((ret = filp->f_op->read(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset)) != sizeof(struct v1_disk_dqblk)) { + if ((ret = sb->s_op->quota_read(sb, type, (char *)&dqblk, + sizeof(struct v1_disk_dqblk), v1_dqoff(0))) != sizeof(struct v1_disk_dqblk)) { if (ret >= 0) ret = -EIO; goto out; } dqblk.dqb_itime = dqopt->info[type].dqi_igrace; dqblk.dqb_btime = dqopt->info[type].dqi_bgrace; - offset = v1_dqoff(0); - ret = filp->f_op->write(filp, (char *)&dqblk, sizeof(struct v1_disk_dqblk), &offset); + ret = sb->s_op->quota_write(sb, type, (char *)&dqblk, + sizeof(struct v1_disk_dqblk), v1_dqoff(0)); if (ret == sizeof(struct v1_disk_dqblk)) ret = 0; else if (ret > 0) ret = -EIO; out: - set_fs(fs); return ret; } Index: linux-2.6.5-7.283/fs/reiserfs/super.c =================================================================== --- linux-2.6.5-7.283.orig/fs/reiserfs/super.c +++ linux-2.6.5-7.283/fs/reiserfs/super.c @@ -1677,7 +1677,7 @@ inversion between quota and journal lock #define REISERFS_V0_QFMT_BLOCKS (4*(JOURNAL_PER_BALANCE_CNT+2)+2+1) static int (*old_write_dquot)(struct dquot *dquot); -static void (*old_drop_dquot)(struct inode *inode); +static int (*old_drop_dquot)(struct inode *inode); static int fmt_to_blocks(int fmt) { @@ -1697,10 +1697,8 @@ static int reiserfs_write_dquot(struct d int err; struct reiserfs_transaction_handle handle; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb); - struct inode *qinode; nblocks = fmt_to_blocks(dqopt->info[dquot->dq_type].dqi_format->qf_fmt_id); - qinode = dqopt->files[dquot->dq_type]->f_dentry->d_inode; reiserfs_write_lock(dquot->dq_sb); ret = journal_begin(&handle, dquot->dq_sb, nblocks); if (ret) @@ -1713,11 +1711,12 @@ static int reiserfs_write_dquot(struct d return ret; } -static void reiserfs_drop_dquot(struct inode *inode) +static int reiserfs_drop_dquot(struct inode *inode) { int nblocks, type; struct quota_info *dqopt = sb_dqopt(inode->i_sb); struct reiserfs_transaction_handle handle; + int err, ret; for (type = 0; type < MAXQUOTAS; type++) { if (sb_has_quota_enabled(inode->i_sb, type)) @@ -1728,12 +1727,15 @@ static void reiserfs_drop_dquot(struct i else nblocks = 0; /* No quota => no drop */ reiserfs_write_lock(inode->i_sb); - if (journal_begin(&handle, inode->i_sb, 2*nblocks)) - return; - old_drop_dquot(inode); - journal_end(&handle, inode->i_sb, 2*nblocks); + err = journal_begin(&handle, inode->i_sb, 2*nblocks); + if (err); + return err; + ret = old_drop_dquot(inode); + err = journal_end(&handle, inode->i_sb, 2*nblocks); + if (!ret) + ret = err; reiserfs_write_unlock(inode->i_sb); - return; + return ret; } #endif