📄 2.6.5-quotafix.patch
字号:
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -