/* Add a dquot to the tail of the free list */ staticinlinevoid put_dquot_last(struct dquot *dquot)
{
list_add_tail(&dquot->dq_free, &free_dquots);
dqstats_inc(DQST_FREE_DQUOTS);
}
staticinlinevoid remove_free_dquot(struct dquot *dquot)
{ if (list_empty(&dquot->dq_free)) return;
list_del_init(&dquot->dq_free); if (!test_bit(DQ_RELEASING_B, &dquot->dq_flags))
dqstats_dec(DQST_FREE_DQUOTS); else
clear_bit(DQ_RELEASING_B, &dquot->dq_flags);
}
staticinlinevoid put_inuse(struct dquot *dquot)
{ /* We add to the back of inuse list so we don't have to restart
* when traversing this list and we block */
list_add_tail(&dquot->dq_inuse, &inuse_list);
dqstats_inc(DQST_ALLOC_DQUOTS);
}
/* Mark dquot dirty in atomic manner, and return it's old dirty flag state */ int dquot_mark_dquot_dirty(struct dquot *dquot)
{ int ret = 1;
if (!dquot_active(dquot)) return0;
if (sb_dqopt(dquot->dq_sb)->flags & DQUOT_NOLIST_DIRTY) return test_and_set_bit(DQ_MOD_B, &dquot->dq_flags);
/* If quota is dirty already, we don't have to acquire dq_list_lock */ if (dquot_dirty(dquot)) return1;
spin_lock(&dq_list_lock); if (!test_and_set_bit(DQ_MOD_B, &dquot->dq_flags)) {
list_add(&dquot->dq_dirty, &sb_dqopt(dquot->dq_sb)->
info[dquot->dq_id.type].dqi_dirty_list);
ret = 0;
}
spin_unlock(&dq_list_lock); return ret;
}
EXPORT_SYMBOL(dquot_mark_dquot_dirty);
/* Dirtify all the dquots - this can block when journalling */ staticinlineint mark_all_dquot_dirty(struct dquot __rcu * const *dquots)
{ int ret, err, cnt; struct dquot *dquot;
ret = err = 0; for (cnt = 0; cnt < MAXQUOTAS; cnt++) {
dquot = srcu_dereference(dquots[cnt], &dquot_srcu); if (dquot) /* Even in case of error we have to continue */
ret = mark_dquot_dirty(dquot); if (!err && ret < 0)
err = ret;
} return err;
}
int dquot_acquire(struct dquot *dquot)
{ int ret = 0, ret2 = 0; unsignedint memalloc; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
mutex_lock(&dquot->dq_lock);
memalloc = memalloc_nofs_save(); if (!test_bit(DQ_READ_B, &dquot->dq_flags)) {
ret = dqopt->ops[dquot->dq_id.type]->read_dqblk(dquot); if (ret < 0) goto out_iolock;
} /* Make sure flags update is visible after dquot has been filled */
smp_mb__before_atomic();
set_bit(DQ_READ_B, &dquot->dq_flags); /* Instantiate dquot if needed */ if (!dquot_active(dquot) && !dquot->dq_off) {
ret = dqopt->ops[dquot->dq_id.type]->commit_dqblk(dquot); /* Write the info if needed */ if (info_dirty(&dqopt->info[dquot->dq_id.type])) {
ret2 = dqopt->ops[dquot->dq_id.type]->write_file_info(
dquot->dq_sb, dquot->dq_id.type);
} if (ret < 0) goto out_iolock; if (ret2 < 0) {
ret = ret2; goto out_iolock;
}
} /* *Makesureflagsupdateisvisibleafteron-diskstructhasbeen *allocated.Pairedwithsmp_rmb()indqget().
*/
smp_mb__before_atomic();
set_bit(DQ_ACTIVE_B, &dquot->dq_flags);
out_iolock:
memalloc_nofs_restore(memalloc);
mutex_unlock(&dquot->dq_lock); return ret;
}
EXPORT_SYMBOL(dquot_acquire);
/* *Writedquottodisk
*/ int dquot_commit(struct dquot *dquot)
{ int ret = 0; unsignedint memalloc; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
mutex_lock(&dquot->dq_lock);
memalloc = memalloc_nofs_save(); if (!clear_dquot_dirty(dquot)) goto out_lock; /* Inactive dquot can be only if there was error during read/init
* => we have better not writing it */ if (dquot_active(dquot))
ret = dqopt->ops[dquot->dq_id.type]->commit_dqblk(dquot); else
ret = -EIO;
out_lock:
memalloc_nofs_restore(memalloc);
mutex_unlock(&dquot->dq_lock); return ret;
}
EXPORT_SYMBOL(dquot_commit);
/* *Releasedquot
*/ int dquot_release(struct dquot *dquot)
{ int ret = 0, ret2 = 0; unsignedint memalloc; struct quota_info *dqopt = sb_dqopt(dquot->dq_sb);
mutex_lock(&dquot->dq_lock);
memalloc = memalloc_nofs_save(); /* Check whether we are not racing with some other dqget() */ if (dquot_is_busy(dquot)) goto out_dqlock; if (dqopt->ops[dquot->dq_id.type]->release_dqblk) {
ret = dqopt->ops[dquot->dq_id.type]->release_dqblk(dquot); /* Write the info */ if (info_dirty(&dqopt->info[dquot->dq_id.type])) {
ret2 = dqopt->ops[dquot->dq_id.type]->write_file_info(
dquot->dq_sb, dquot->dq_id.type);
} if (ret >= 0)
ret = ret2;
}
clear_bit(DQ_ACTIVE_B, &dquot->dq_flags);
out_dqlock:
memalloc_nofs_restore(memalloc);
mutex_unlock(&dquot->dq_lock); return ret;
}
EXPORT_SYMBOL(dquot_release);
/* Invalidate all dquots on the list. Note that this function is called after *quotaisdisabledandpointersfrominodesremovedsotherecannotbenew *quotausers.Therecanstillbesomeusersofquotasduetoinodesbeing *justdeletedorprunedbyprune_icache()(thosearenotattachedtoany *list)orparallelquotactlcall.Wehavetowaitforsuchusers.
*/ staticvoid invalidate_dquots(struct super_block *sb, int type)
{ struct dquot *dquot, *tmp;
restart:
flush_delayed_work("a_release_work);
spin_lock(&dq_list_lock);
list_for_each_entry_safe(dquot, tmp, &inuse_list, dq_inuse) { if (dquot->dq_sb != sb) continue; if (dquot->dq_id.type != type) continue; /* Wait for dquot users */ if (atomic_read(&dquot->dq_count)) {
atomic_inc(&dquot->dq_count);
spin_unlock(&dq_list_lock); /* *Oncedqput()wakesusup,weknowit'stimetofree *thedquot. *IMPORTANT:werelyonthefactthatthereisalways *atmostoneprocesswaitingfordquottofree. *Otherwisedq_countwouldbe>1andwewouldnever *wakeup.
*/
wait_event(dquot_ref_wq,
atomic_read(&dquot->dq_count) == 1);
dqput(dquot); /* At this moment dquot() need not exist (it could be *reclaimedbyprune_dqcache().Hencewemust
* restart. */ goto restart;
} /* *Thelastuseralreadydroppeditsreferencebutdquotdidn't *getfullycleanedupyet.Restartthescanwhichflushesthe *workcleaningupreleaseddquots.
*/ if (test_bit(DQ_RELEASING_B, &dquot->dq_flags)) {
spin_unlock(&dq_list_lock); goto restart;
} /* *Quotanowhasnousersandithasbeenwrittenonlast *dqput()
*/
remove_dquot_hash(dquot);
remove_free_dquot(dquot);
remove_inuse(dquot);
do_destroy_dquot(dquot);
}
spin_unlock(&dq_list_lock);
}
/* Call callback for every active dquot on given filesystem */ int dquot_scan_active(struct super_block *sb, int (*fn)(struct dquot *dquot, unsignedlong priv), unsignedlong priv)
{ struct dquot *dquot, *old_dquot = NULL; int ret = 0;
WARN_ON_ONCE(!rwsem_is_locked(&sb->s_umount));
spin_lock(&dq_list_lock);
list_for_each_entry(dquot, &inuse_list, dq_inuse) { if (!dquot_active(dquot)) continue; if (dquot->dq_sb != sb) continue; /* Now we have active dquot so we can just increase use count */
atomic_inc(&dquot->dq_count);
spin_unlock(&dq_list_lock);
dqput(old_dquot);
old_dquot = dquot; /* *->release_dquot()canberacingwithus.Ourreference *protectsusfromnewcallstoitsojustwaitforany *outstandingcallandrechecktheDQ_ACTIVE_Bafterthat.
*/
wait_on_dquot(dquot); if (dquot_active(dquot)) {
ret = fn(dquot, priv); if (ret < 0) goto out;
}
spin_lock(&dq_list_lock); /* We are safe to continue now because our dquot could not
* be moved out of the inuse list while we hold the reference */
}
spin_unlock(&dq_list_lock);
out:
dqput(old_dquot); return ret;
}
EXPORT_SYMBOL(dquot_scan_active);
staticinlineint dquot_write_dquot(struct dquot *dquot)
{ int ret = dquot->dq_sb->dq_op->write_dquot(dquot); if (ret < 0) {
quota_error(dquot->dq_sb, "Can't write quota structure " "(error %d). Quota may get out of sync!", ret); /* Clear dirty bit anyway to avoid infinite loop. */
clear_dquot_dirty(dquot);
} return ret;
}
/* Write all dquot structures to quota files */ int dquot_writeback_dquots(struct super_block *sb, int type)
{ struct list_head dirty; struct dquot *dquot; struct quota_info *dqopt = sb_dqopt(sb); int cnt; int err, ret = 0;
WARN_ON_ONCE(!rwsem_is_locked(&sb->s_umount));
flush_delayed_work("a_release_work);
for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; if (!sb_has_quota_active(sb, cnt)) continue;
spin_lock(&dq_list_lock); /* Move list away to avoid livelock. */
list_replace_init(&dqopt->info[cnt].dqi_dirty_list, &dirty); while (!list_empty(&dirty)) {
dquot = list_first_entry(&dirty, struct dquot,
dq_dirty);
WARN_ON(!dquot_active(dquot)); /* If the dquot is releasing we should not touch it */ if (test_bit(DQ_RELEASING_B, &dquot->dq_flags)) {
spin_unlock(&dq_list_lock);
flush_delayed_work("a_release_work);
spin_lock(&dq_list_lock); continue;
}
/* Now we have active dquot from which someone is *holdingreferencesowecansafelyjustincrease
* use count */
dqgrab(dquot);
spin_unlock(&dq_list_lock);
err = dquot_write_dquot(dquot); if (err && !ret)
ret = err;
dqput(dquot);
spin_lock(&dq_list_lock);
}
spin_unlock(&dq_list_lock);
}
for (cnt = 0; cnt < MAXQUOTAS; cnt++) if ((cnt == type || type == -1) && sb_has_quota_active(sb, cnt)
&& info_dirty(&dqopt->info[cnt]))
sb->dq_op->write_info(sb, cnt);
dqstats_inc(DQST_SYNCS);
/* Write all dquot structures to disk and make them visible from userspace */ int dquot_quota_sync(struct super_block *sb, int type)
{ struct quota_info *dqopt = sb_dqopt(sb); int cnt; int ret;
ret = dquot_writeback_dquots(sb, type); if (ret) return ret; if (dqopt->flags & DQUOT_QUOTA_SYS_FILE) return0;
/* This is not very clever (and fast) but currently I don't know about *anyothersimplewayofgettingquotadatatodiskandwemustget
* them there for userspace to be visible... */ if (sb->s_op->sync_fs) {
ret = sb->s_op->sync_fs(sb, 1); if (ret) return ret;
}
ret = sync_blockdev(sb->s_bdev); if (ret) return ret;
spin_lock(&dq_list_lock); /* Exchange the list head to avoid livelock. */
list_replace_init(&releasing_dquots, &rls_head);
spin_unlock(&dq_list_lock);
synchronize_srcu(&dquot_srcu);
restart:
spin_lock(&dq_list_lock); while (!list_empty(&rls_head)) {
dquot = list_first_entry(&rls_head, struct dquot, dq_free);
WARN_ON_ONCE(atomic_read(&dquot->dq_count)); /* *NotethatDQ_RELEASING_Bprotectsusfromracingwith *invalidate_dquots()callssowearesafetoworkwiththe *dquotevenafterwedropdq_list_lock.
*/ if (dquot_dirty(dquot)) {
spin_unlock(&dq_list_lock); /* Commit dquot before releasing */
dquot_write_dquot(dquot); goto restart;
} if (dquot_active(dquot)) {
spin_unlock(&dq_list_lock);
dquot->dq_sb->dq_op->release_dquot(dquot); goto restart;
} /* Dquot is inactive and clean, now move it to free list */
remove_free_dquot(dquot);
put_dquot_last(dquot);
}
spin_unlock(&dq_list_lock);
}
/* *Putreferencetodquot
*/ void dqput(struct dquot *dquot)
{ if (!dquot) return; #ifdef CONFIG_QUOTA_DEBUG if (!atomic_read(&dquot->dq_count)) {
quota_error(dquot->dq_sb, "trying to free free dquot of %s %d",
quotatypes[dquot->dq_id.type],
from_kqid(&init_user_ns, dquot->dq_id));
BUG();
} #endif
dqstats_inc(DQST_DROPS);
spin_lock(&dq_list_lock); if (atomic_read(&dquot->dq_count) > 1) { /* We have more than one user... nothing to do */
atomic_dec(&dquot->dq_count); /* Releasing dquot during quotaoff phase? */ if (!sb_has_quota_active(dquot->dq_sb, dquot->dq_id.type) &&
atomic_read(&dquot->dq_count) == 1)
wake_up(&dquot_ref_wq);
spin_unlock(&dq_list_lock); return;
}
/* Need to release dquot? */
WARN_ON_ONCE(!list_empty(&dquot->dq_free));
put_releasing_dquots(dquot);
atomic_dec(&dquot->dq_count);
spin_unlock(&dq_list_lock);
queue_delayed_work(quota_unbound_wq, "a_release_work, 1);
}
EXPORT_SYMBOL(dqput);
dquot = find_dquot(hashent, sb, qid); if (!dquot) { if (!empty) {
spin_unlock(&dq_list_lock);
empty = get_empty_dquot(sb, qid.type); if (!empty)
schedule(); /* Try to wait for a moment... */ goto we_slept;
}
dquot = empty;
empty = NULL;
dquot->dq_id = qid; /* all dquots go on the inuse_list */
put_inuse(dquot); /* hash it first so it can be found */
insert_dquot_hash(dquot);
spin_unlock(&dq_list_lock);
dqstats_inc(DQST_LOOKUPS);
} else { if (!atomic_read(&dquot->dq_count))
remove_free_dquot(dquot);
atomic_inc(&dquot->dq_count);
spin_unlock(&dq_list_lock);
dqstats_inc(DQST_CACHE_HITS);
dqstats_inc(DQST_LOOKUPS);
} /* Wait for dq_lock - after this we know that either dquot_release() is
* already finished or it will be canceled due to dq_count > 0 test */
wait_on_dquot(dquot); /* Read the dquot / allocate space in quota file */ if (!dquot_active(dquot)) { int err;
err = sb->dq_op->acquire_dquot(dquot); if (err < 0) {
dqput(dquot);
dquot = ERR_PTR(err); goto out;
}
} /* *Makesurefollowingreadsseefilledstructure-pairedwith *smp_mb__before_atomic()indquot_acquire().
*/
smp_rmb(); /* Has somebody invalidated entry under us? */
WARN_ON_ONCE(hlist_unhashed(&dquot->dq_hash));
out: if (empty)
do_destroy_dquot(empty);
#ifdef CONFIG_QUOTA_DEBUG if (unlikely(inode_get_rsv_space(inode) > 0))
reserved = 1; #endif
rcu_assign_pointer(dquots[type], NULL); if (dquot)
dqput(dquot);
}
spin_unlock(&dq_data_lock);
}
spin_unlock(&sb->s_inode_list_lock); #ifdef CONFIG_QUOTA_DEBUG if (reserved) {
printk(KERN_WARNING "VFS (%s): Writes happened after quota" " was disabled thus quota information is probably " "inconsistent. Please run quotacheck(8).\n", sb->s_id);
} #endif
}
/* Gather all references from inodes and drop them */ staticvoid drop_dquot_ref(struct super_block *sb, int type)
{ if (sb->dq_op)
remove_dquot_ref(sb, type);
}
if (IS_NOQUOTA(inode)) return0; return sb_any_quota_loaded(sb) & ~sb_any_quota_suspended(sb);
}
/* *Initializequotapointersininode * *Itisbettertocallthisfunctionoutsideofanytransactionasit *mightneedalotofspaceinjournalfordquotstructureallocation.
*/ staticint __dquot_initialize(struct inode *inode, int type)
{ int cnt, init_needed = 0; struct dquot __rcu **dquots; struct dquot *got[MAXQUOTAS] = {}; struct super_block *sb = inode->i_sb;
qsize_t rsv; int ret = 0;
if (!inode_quota_active(inode)) return0;
dquots = i_dquot(inode);
/* First get references to structures we might need. */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { struct kqid qid;
kprojid_t projid; int rc; struct dquot *dquot;
if (type != -1 && cnt != type) continue; /* *Thei_dquotshouldhavebeeninitializedinmostcases, *wecheckitwithoutlockingheretoavoidunnecessary *dqget()/dqput()calls.
*/ if (dquots[cnt]) continue;
if (!sb_has_quota_active(sb, cnt)) continue;
init_needed = 1;
switch (cnt) { case USRQUOTA:
qid = make_kqid_uid(inode->i_uid); break; case GRPQUOTA:
qid = make_kqid_gid(inode->i_gid); break; case PRJQUOTA:
rc = inode->i_sb->dq_op->get_projid(inode, &projid); if (rc) continue;
qid = make_kqid_projid(projid); break;
}
dquot = dqget(sb, qid); if (IS_ERR(dquot)) { /* We raced with somebody turning quotas off... */ if (PTR_ERR(dquot) != -ESRCH) {
ret = PTR_ERR(dquot); goto out_put;
}
dquot = NULL;
}
got[cnt] = dquot;
}
/* All required i_dquot has been initialized */ if (!init_needed) return0;
spin_lock(&dq_data_lock); if (IS_NOQUOTA(inode)) goto out_lock; for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (type != -1 && cnt != type) continue; /* Avoid races with quotaoff() */ if (!sb_has_quota_active(sb, cnt)) continue; /* We could race with quotaon or dqget() could have failed */ if (!got[cnt]) continue; if (!dquots[cnt]) {
rcu_assign_pointer(dquots[cnt], got[cnt]);
got[cnt] = NULL; /* *Makequotareservationsystemhappyifsomeone *didawritebeforequotawasturnedon
*/
rsv = inode_get_rsv_space(inode); if (unlikely(rsv)) { struct dquot *dquot = srcu_dereference_check(
dquots[cnt], &dquot_srcu,
lockdep_is_held(&dq_data_lock));
spin_lock(&inode->i_lock); /* Get reservation again under proper lock */
rsv = __inode_get_rsv_space(inode);
spin_lock(&dquot->dq_dqb_lock);
dquot->dq_dqb.dqb_rsvspace += rsv;
spin_unlock(&dquot->dq_dqb_lock);
spin_unlock(&inode->i_lock);
}
}
}
out_lock:
spin_unlock(&dq_data_lock);
out_put: /* Drop unused references */
dqput_all(got);
return ret;
}
int dquot_initialize(struct inode *inode)
{ return __dquot_initialize(inode, -1);
}
EXPORT_SYMBOL(dquot_initialize);
dquots = i_dquot(inode); for (i = 0; i < MAXQUOTAS; i++) if (!dquots[i] && sb_has_quota_active(inode->i_sb, i)) returntrue; returnfalse;
}
EXPORT_SYMBOL(dquot_initialize_needed);
spin_lock(&dq_data_lock);
spin_lock(&inode->i_lock); if (IS_NOQUOTA(inode)) { /* File without quota accounting? */
spin_unlock(&inode->i_lock);
spin_unlock(&dq_data_lock); return0;
}
cur_space = __inode_get_bytes(inode);
rsv_space = __inode_get_rsv_space(inode);
dquots = i_dquot(inode); /* *Buildthetransfer_fromlist,checklimits,andupdateusagein *thetargetstructures.
*/ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { /* *Skipchangesforsameuidorgidorforturnedoffquota-type.
*/ if (!transfer_to[cnt]) continue; /* Avoid races with quotaoff() */ if (!sb_has_quota_active(inode->i_sb, cnt)) continue;
is_valid[cnt] = 1;
transfer_from[cnt] = srcu_dereference_check(dquots[cnt],
&dquot_srcu, lockdep_is_held(&dq_data_lock));
ret = dquot_add_inodes(transfer_to[cnt], inode_usage,
&warn_to[cnt]); if (ret) goto over_quota;
ret = dquot_add_space(transfer_to[cnt], cur_space, rsv_space,
DQUOT_SPACE_WARN, &warn_to[cnt]); if (ret) {
spin_lock(&transfer_to[cnt]->dq_dqb_lock);
dquot_decr_inodes(transfer_to[cnt], inode_usage);
spin_unlock(&transfer_to[cnt]->dq_dqb_lock); goto over_quota;
}
}
/* Decrease usage for source structures and update quota pointers */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) { if (!is_valid[cnt]) continue; /* Due to IO error we might not have transfer_from[] structure */ if (transfer_from[cnt]) { int wtype;
/* We still have to keep quota loaded? */ if (sb_has_quota_loaded(sb, cnt) && !(flags & DQUOT_SUSPENDED)) continue;
/* Note: these are blocking operations */
drop_dquot_ref(sb, cnt);
invalidate_dquots(sb, cnt); /* *Nowalldquotsshouldbeinvalidated,allwritesdonesowe *shouldbeonlyusersoftheinfo.Nolocksneeded.
*/ 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);
dqopt->info[cnt].dqi_flags = 0;
dqopt->info[cnt].dqi_igrace = 0;
dqopt->info[cnt].dqi_bgrace = 0;
dqopt->ops[cnt] = NULL;
}
/* Skip syncing and setting flags if quota files are hidden */ if (dqopt->flags & DQUOT_QUOTA_SYS_FILE) goto put_inodes;
/* Sync the superblock so that buffers with quota data are written to
* disk (and so userspace sees correct data afterwards). */ 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 *inodeflagsback.Moreoverwediscardthepagecachesothat *userspaceseesthewriteswedidbypassingthepagecache.We *mustalsodiscardtheblockdevbufferssothatweseethe
* changes done by userspace on the next quotaon() */ for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (!sb_has_quota_loaded(sb, cnt) && dqopt->files[cnt]) {
inode_lock(dqopt->files[cnt]);
truncate_inode_pages(&dqopt->files[cnt]->i_data, 0);
inode_unlock(dqopt->files[cnt]);
} if (sb->s_bdev)
invalidate_bdev(sb->s_bdev);
put_inodes: /* We are done when suspending quotas */ if (flags & DQUOT_SUSPENDED) return0;
for (cnt = 0; cnt < MAXQUOTAS; cnt++) if (!sb_has_quota_loaded(sb, cnt))
vfs_cleanup_quota_inode(sb, cnt); return0;
}
EXPORT_SYMBOL(dquot_disable);
int dquot_quota_off(struct super_block *sb, int type)
{ return dquot_disable(sb, type,
DQUOT_USAGE_ENABLED | DQUOT_LIMITS_ENABLED);
}
EXPORT_SYMBOL(dquot_quota_off);
if (is_bad_inode(inode)) return -EUCLEAN; if (!S_ISREG(inode->i_mode)) return -EACCES; if (IS_RDONLY(inode)) return -EROFS; if (sb_has_quota_loaded(sb, type)) return -EBUSY;
dqopt->files[type] = igrab(inode); if (!dqopt->files[type]) return -EIO; if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) { /* We don't want quota and atime on quota files (deadlocks *possible)Alsonobodyshouldwritetothefile-weuse
* special IO operations which ignore the immutable bit. */
inode_lock(inode);
inode->i_flags |= S_NOQUOTA;
inode_unlock(inode); /* *WhenS_NOQUOTAisset,removedquotreferencesasnomore *referencescanbeadded
*/
__dquot_drop(inode);
} return0;
}
int dquot_load_quota_sb(struct super_block *sb, int type, int format_id, unsignedint flags)
{ struct quota_format_type *fmt; struct quota_info *dqopt = sb_dqopt(sb); int error;
lockdep_assert_held_write(&sb->s_umount);
/* Just unsuspend quotas? */ if (WARN_ON_ONCE(flags & DQUOT_SUSPENDED)) return -EINVAL;
fmt = find_quota_format(format_id); if (!fmt) return -ESRCH; if (!sb->dq_op || !sb->s_qcop ||
(type == PRJQUOTA && sb->dq_op->get_projid == NULL)) {
error = -EINVAL; goto out_fmt;
} /* Filesystems outside of init_user_ns not yet supported */ if (sb->s_user_ns != &init_user_ns) {
error = -EINVAL; goto out_fmt;
} /* Usage always has to be set... */ if (!(flags & DQUOT_USAGE_ENABLED)) {
error = -EINVAL; goto out_fmt;
} if (sb_has_quota_loaded(sb, type)) {
error = -EBUSY; goto out_fmt;
}
if (!(dqopt->flags & DQUOT_QUOTA_SYS_FILE)) { /* As we bypass the pagecache we must now flush all the *dirtydataandinvalidatecachessothatkernelsees *changesfromuserspace.Itisnotenoughtojustflush *thequotafilesinceifblocksize<pagesize,invalidation *ofthecachecouldfailbecauseofotherunrelateddirty
* data */
sync_filesystem(sb);
invalidate_bdev(sb->s_bdev);
}
error = -EINVAL; if (!fmt->qf_ops->check_quota_file(sb, type)) goto out_fmt;
/* *Morepowerfulfunctionforturningonquotasongivenquotainodeallowing *settingofindividualquotaflags
*/ int dquot_load_quota_inode(struct inode *inode, int type, int format_id, unsignedint flags)
{ int err;
flags = dquot_generic_flag(flags, cnt);
ret = dquot_load_quota_sb(sb, cnt, dqopt->info[cnt].dqi_fmt_id,
flags); if (ret < 0)
vfs_cleanup_quota_inode(sb, cnt);
}
return ret;
}
EXPORT_SYMBOL(dquot_resume);
int dquot_quota_on(struct super_block *sb, int type, int format_id, conststruct path *path)
{ int error = security_quota_on(path->dentry); if (error) return error; /* Quota file not on the same filesystem? */ if (path->dentry->d_sb != sb)
error = -EXDEV; else
error = dquot_load_quota_inode(d_inode(path->dentry), type,
format_id, DQUOT_USAGE_ENABLED |
DQUOT_LIMITS_ENABLED); return error;
}
EXPORT_SYMBOL(dquot_quota_on);
/* *Thisfunctionisusedwhenfilesystemneedstoinitializequotas *duringmounttime.
*/ int dquot_quota_on_mount(struct super_block *sb, char *qf_name, int format_id, int type)
{ struct dentry *dentry; int error;
dentry = lookup_noperm_positive_unlocked(&QSTR(qf_name), sb->s_root); if (IS_ERR(dentry)) return PTR_ERR(dentry);
/* Generic routine for getting common part of quota file information */ int dquot_get_state(struct super_block *sb, struct qc_state *state)
{ struct mem_dqinfo *mi; struct qc_type_state *tstate; struct quota_info *dqopt = sb_dqopt(sb); int type;
memset(state, 0, sizeof(*state)); for (type = 0; type < MAXQUOTAS; type++) { if (!sb_has_quota_active(sb, type)) continue;
tstate = state->s_state + type;
mi = sb_dqopt(sb)->info + type;
tstate->flags = QCI_ACCT_ENABLED;
spin_lock(&dq_data_lock); if (mi->dqi_flags & DQF_SYS_FILE)
tstate->flags |= QCI_SYSFILE; if (mi->dqi_flags & DQF_ROOT_SQUASH)
tstate->flags |= QCI_ROOT_SQUASH; if (sb_has_quota_limits_enabled(sb, type))
tstate->flags |= QCI_LIMITS_ENFORCED;
tstate->spc_timelimit = mi->dqi_bgrace;
tstate->ino_timelimit = mi->dqi_igrace; if (dqopt->files[type]) {
tstate->ino = dqopt->files[type]->i_ino;
tstate->blocks = dqopt->files[type]->i_blocks;
}
tstate->nextents = 1; /* We don't know... */
spin_unlock(&dq_data_lock);
} return0;
}
EXPORT_SYMBOL(dquot_get_state);
/* Generic routine for setting common part of quota file information */ int dquot_set_dqinfo(struct super_block *sb, int type, struct qc_info *ii)
{ struct mem_dqinfo *mi;
if ((ii->i_fieldmask & QC_WARNS_MASK) ||
(ii->i_fieldmask & QC_RT_SPC_TIMER)) return -EINVAL; if (!sb_has_quota_active(sb, type)) return -ESRCH;
mi = sb_dqopt(sb)->info + type; if (ii->i_fieldmask & QC_FLAGS) { if ((ii->i_flags & QCI_ROOT_SQUASH &&
mi->dqi_format->qf_fmt_id != QFMT_VFS_OLD)) return -EINVAL;
}
spin_lock(&dq_data_lock); if (ii->i_fieldmask & QC_SPC_TIMER)
mi->dqi_bgrace = ii->i_spc_timelimit; if (ii->i_fieldmask & QC_INO_TIMER)
mi->dqi_igrace = ii->i_ino_timelimit; if (ii->i_fieldmask & QC_FLAGS) { if (ii->i_flags & QCI_ROOT_SQUASH)
mi->dqi_flags |= DQF_ROOT_SQUASH; else
mi->dqi_flags &= ~DQF_ROOT_SQUASH;
}
spin_unlock(&dq_data_lock);
mark_info_dirty(sb, type); /* Force write to disk */ return sb->dq_op->write_info(sb, type);
}
EXPORT_SYMBOL(dquot_set_dqinfo);
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.111Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-09-29)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.