/* * Starting at @id and progressing upwards, look for an initialized incore * dquot, lock it, and return it. */ int xfs_qm_dqget_next( struct xfs_mount *mp, xfs_dqid_t id, uint type, struct xfs_dquot **dqpp) { struct xfs_dquot *dqp; int error = 0; *dqpp = NULL; for (; !error; error = xfs_dq_get_next_id(mp, type, &id)) { error = xfs_qm_dqget(mp, id, type, false, &dqp); if (error == -ENOENT) continue; else if (error != 0) break; if (!XFS_IS_DQUOT_UNINITIALIZED(dqp)) { *dqpp = dqp; return 0; } xfs_qm_dqput(dqp); } return error; }
int xfs_qm_scall_getquota( xfs_mount_t *mp, xfs_dqid_t id, uint type, fs_disk_quota_t *out) { xfs_dquot_t *dqp; int error; /* * Try to get the dquot. We don't want it allocated on disk, so * we aren't passing the XFS_QMOPT_DOALLOC flag. If it doesn't * exist, we'll get ENOENT back. */ if ((error = xfs_qm_dqget(mp, NULL, id, type, 0, &dqp))) { return (error); } /* * If everything's NULL, this dquot doesn't quite exist as far as * our utility programs are concerned. */ if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) { xfs_qm_dqput(dqp); return XFS_ERROR(ENOENT); } /* xfs_qm_dqprint(dqp); */ /* * Convert the disk dquot to the exportable format */ xfs_qm_export_dquot(mp, &dqp->q_core, out); xfs_qm_dqput(dqp); return (error ? XFS_ERROR(EFAULT) : 0); }
int xfs_qm_scall_getquota( struct xfs_mount *mp, xfs_dqid_t id, uint type, struct qc_dqblk *dst) { struct xfs_dquot *dqp; int error; /* * Try to get the dquot. We don't want it allocated on disk, so * we aren't passing the XFS_QMOPT_DOALLOC flag. If it doesn't * exist, we'll get ENOENT back. */ error = xfs_qm_dqget(mp, NULL, id, type, 0, &dqp); if (error) return error; /* * If everything's NULL, this dquot doesn't quite exist as far as * our utility programs are concerned. */ if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) { error = XFS_ERROR(ENOENT); goto out_put; } memset(dst, 0, sizeof(*dst)); dst->d_spc_hardlimit = XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_blk_hardlimit)); dst->d_spc_softlimit = XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_blk_softlimit)); dst->d_ino_hardlimit = be64_to_cpu(dqp->q_core.d_ino_hardlimit); dst->d_ino_softlimit = be64_to_cpu(dqp->q_core.d_ino_softlimit); dst->d_space = XFS_FSB_TO_B(mp, dqp->q_res_bcount); dst->d_ino_count = dqp->q_res_icount; dst->d_spc_timer = be32_to_cpu(dqp->q_core.d_btimer); dst->d_ino_timer = be32_to_cpu(dqp->q_core.d_itimer); dst->d_ino_warns = be16_to_cpu(dqp->q_core.d_iwarns); dst->d_spc_warns = be16_to_cpu(dqp->q_core.d_bwarns); dst->d_rt_spc_hardlimit = XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_rtb_hardlimit)); dst->d_rt_spc_softlimit = XFS_FSB_TO_B(mp, be64_to_cpu(dqp->q_core.d_rtb_softlimit)); dst->d_rt_space = XFS_FSB_TO_B(mp, dqp->q_res_rtbcount); dst->d_rt_spc_timer = be32_to_cpu(dqp->q_core.d_rtbtimer); dst->d_rt_spc_warns = be16_to_cpu(dqp->q_core.d_rtbwarns); /* * Internally, we don't reset all the timers when quota enforcement * gets turned off. No need to confuse the user level code, * so return zeroes in that case. */ if ((!XFS_IS_UQUOTA_ENFORCED(mp) && dqp->q_core.d_flags == XFS_DQ_USER) || (!XFS_IS_GQUOTA_ENFORCED(mp) && dqp->q_core.d_flags == XFS_DQ_GROUP) || (!XFS_IS_PQUOTA_ENFORCED(mp) && dqp->q_core.d_flags == XFS_DQ_PROJ)) { dst->d_spc_timer = 0; dst->d_ino_timer = 0; dst->d_rt_spc_timer = 0; } #ifdef DEBUG if (((XFS_IS_UQUOTA_ENFORCED(mp) && type == XFS_DQ_USER) || (XFS_IS_GQUOTA_ENFORCED(mp) && type == XFS_DQ_GROUP) || (XFS_IS_PQUOTA_ENFORCED(mp) && type == XFS_DQ_PROJ)) && id != 0) { if ((dst->d_space > dst->d_spc_softlimit) && (dst->d_spc_softlimit > 0)) { ASSERT(dst->d_spc_timer != 0); } if ((dst->d_ino_count > dst->d_ino_softlimit) && (dst->d_ino_softlimit > 0)) { ASSERT(dst->d_ino_timer != 0); } } #endif out_put: xfs_qm_dqput(dqp); return error; }
/* * Given the file system, inode OR id, and type (UDQUOT/GDQUOT), return a * a locked dquot, doing an allocation (if requested) as needed. * When both an inode and an id are given, the inode's id takes precedence. * That is, if the id changes while we don't hold the ilock inside this * function, the new dquot is returned, not necessarily the one requested * in the id argument. */ int xfs_qm_dqget( xfs_mount_t *mp, xfs_inode_t *ip, /* locked inode (optional) */ xfs_dqid_t id, /* uid/projid/gid depending on type */ uint type, /* XFS_DQ_USER/XFS_DQ_PROJ/XFS_DQ_GROUP */ uint flags, /* DQALLOC, DQSUSER, DQREPAIR, DOWARN */ xfs_dquot_t **O_dqpp) /* OUT : locked incore dquot */ { struct xfs_quotainfo *qi = mp->m_quotainfo; struct radix_tree_root *tree = xfs_dquot_tree(qi, type); struct xfs_dquot *dqp; int error; ASSERT(XFS_IS_QUOTA_RUNNING(mp)); if ((! XFS_IS_UQUOTA_ON(mp) && type == XFS_DQ_USER) || (! XFS_IS_PQUOTA_ON(mp) && type == XFS_DQ_PROJ) || (! XFS_IS_GQUOTA_ON(mp) && type == XFS_DQ_GROUP)) { return -ESRCH; } ASSERT(type == XFS_DQ_USER || type == XFS_DQ_PROJ || type == XFS_DQ_GROUP); if (ip) { ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); ASSERT(xfs_inode_dquot(ip, type) == NULL); } restart: mutex_lock(&qi->qi_tree_lock); dqp = radix_tree_lookup(tree, id); if (dqp) { xfs_dqlock(dqp); if (dqp->dq_flags & XFS_DQ_FREEING) { xfs_dqunlock(dqp); mutex_unlock(&qi->qi_tree_lock); trace_xfs_dqget_freeing(dqp); delay(1); goto restart; } /* uninit / unused quota found in radix tree, keep looking */ if (flags & XFS_QMOPT_DQNEXT) { if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) { xfs_dqunlock(dqp); mutex_unlock(&qi->qi_tree_lock); error = xfs_dq_get_next_id(mp, type, &id); if (error) return error; goto restart; } } dqp->q_nrefs++; mutex_unlock(&qi->qi_tree_lock); trace_xfs_dqget_hit(dqp); XFS_STATS_INC(mp, xs_qm_dqcachehits); *O_dqpp = dqp; return 0; } mutex_unlock(&qi->qi_tree_lock); XFS_STATS_INC(mp, xs_qm_dqcachemisses); /* * Dquot cache miss. We don't want to keep the inode lock across * a (potential) disk read. Also we don't want to deal with the lock * ordering between quotainode and this inode. OTOH, dropping the inode * lock here means dealing with a chown that can happen before * we re-acquire the lock. */ if (ip) xfs_iunlock(ip, XFS_ILOCK_EXCL); error = xfs_qm_dqread(mp, id, type, flags, &dqp); if (ip) xfs_ilock(ip, XFS_ILOCK_EXCL); /* If we are asked to find next active id, keep looking */ if (error == -ENOENT && (flags & XFS_QMOPT_DQNEXT)) { error = xfs_dq_get_next_id(mp, type, &id); if (!error) goto restart; } if (error) return error; if (ip) { /* * A dquot could be attached to this inode by now, since * we had dropped the ilock. */ if (xfs_this_quota_on(mp, type)) { struct xfs_dquot *dqp1; dqp1 = xfs_inode_dquot(ip, type); if (dqp1) { xfs_qm_dqdestroy(dqp); dqp = dqp1; xfs_dqlock(dqp); goto dqret; } } else { /* inode stays locked on return */ xfs_qm_dqdestroy(dqp); return -ESRCH; } } mutex_lock(&qi->qi_tree_lock); error = radix_tree_insert(tree, id, dqp); if (unlikely(error)) { WARN_ON(error != -EEXIST); /* * Duplicate found. Just throw away the new dquot and start * over. */ mutex_unlock(&qi->qi_tree_lock); trace_xfs_dqget_dup(dqp); xfs_qm_dqdestroy(dqp); XFS_STATS_INC(mp, xs_qm_dquot_dups); goto restart; } /* * We return a locked dquot to the caller, with a reference taken */ xfs_dqlock(dqp); dqp->q_nrefs = 1; qi->qi_dquots++; mutex_unlock(&qi->qi_tree_lock); /* If we are asked to find next active id, keep looking */ if (flags & XFS_QMOPT_DQNEXT) { if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) { xfs_qm_dqput(dqp); error = xfs_dq_get_next_id(mp, type, &id); if (error) return error; goto restart; } } dqret: ASSERT((ip == NULL) || xfs_isilocked(ip, XFS_ILOCK_EXCL)); trace_xfs_dqget_miss(dqp); *O_dqpp = dqp; return 0; }
int xfs_qm_scall_getquota( struct xfs_mount *mp, xfs_dqid_t id, uint type, struct fs_disk_quota *dst) { struct xfs_dquot *dqp; int error; error = xfs_qm_dqget(mp, NULL, id, type, 0, &dqp); if (error) return error; if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) { error = XFS_ERROR(ENOENT); goto out_put; } memset(dst, 0, sizeof(*dst)); dst->d_version = FS_DQUOT_VERSION; dst->d_flags = xfs_qm_export_qtype_flags(dqp->q_core.d_flags); dst->d_id = be32_to_cpu(dqp->q_core.d_id); dst->d_blk_hardlimit = XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_blk_hardlimit)); dst->d_blk_softlimit = XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_blk_softlimit)); dst->d_ino_hardlimit = be64_to_cpu(dqp->q_core.d_ino_hardlimit); dst->d_ino_softlimit = be64_to_cpu(dqp->q_core.d_ino_softlimit); dst->d_bcount = XFS_FSB_TO_BB(mp, dqp->q_res_bcount); dst->d_icount = dqp->q_res_icount; dst->d_btimer = be32_to_cpu(dqp->q_core.d_btimer); dst->d_itimer = be32_to_cpu(dqp->q_core.d_itimer); dst->d_iwarns = be16_to_cpu(dqp->q_core.d_iwarns); dst->d_bwarns = be16_to_cpu(dqp->q_core.d_bwarns); dst->d_rtb_hardlimit = XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_rtb_hardlimit)); dst->d_rtb_softlimit = XFS_FSB_TO_BB(mp, be64_to_cpu(dqp->q_core.d_rtb_softlimit)); dst->d_rtbcount = XFS_FSB_TO_BB(mp, dqp->q_res_rtbcount); dst->d_rtbtimer = be32_to_cpu(dqp->q_core.d_rtbtimer); dst->d_rtbwarns = be16_to_cpu(dqp->q_core.d_rtbwarns); if ((!XFS_IS_UQUOTA_ENFORCED(mp) && dqp->q_core.d_flags == XFS_DQ_USER) || (!XFS_IS_OQUOTA_ENFORCED(mp) && (dqp->q_core.d_flags & (XFS_DQ_PROJ | XFS_DQ_GROUP)))) { dst->d_btimer = 0; dst->d_itimer = 0; dst->d_rtbtimer = 0; } #ifdef DEBUG if (((XFS_IS_UQUOTA_ENFORCED(mp) && dst->d_flags == FS_USER_QUOTA) || (XFS_IS_OQUOTA_ENFORCED(mp) && (dst->d_flags & (FS_PROJ_QUOTA | FS_GROUP_QUOTA)))) && dst->d_id != 0) { if (((int) dst->d_bcount > (int) dst->d_blk_softlimit) && (dst->d_blk_softlimit > 0)) { ASSERT(dst->d_btimer != 0); } if (((int) dst->d_icount > (int) dst->d_ino_softlimit) && (dst->d_ino_softlimit > 0)) { ASSERT(dst->d_itimer != 0); } } #endif out_put: xfs_qm_dqput(dqp); return error; }