/* * This gets called when the inode's version needs to be changed from 1 to 2. * Currently this happens when the nlink field overflows the old 16-bit value * or when chproj is called to change the project for the first time. * As a side effect the superblock version will also get rev'd * to contain the NLINK bit. */ void xfs_bump_ino_vers2( xfs_trans_t *tp, xfs_inode_t *ip) { xfs_mount_t *mp; unsigned long s; ASSERT(ismrlocked (&ip->i_lock, MR_UPDATE)); ASSERT(ip->i_d.di_version == XFS_DINODE_VERSION_1); ip->i_d.di_version = XFS_DINODE_VERSION_2; ip->i_d.di_onlink = 0; memset(&(ip->i_d.di_pad[0]), 0, sizeof(ip->i_d.di_pad)); mp = tp->t_mountp; if (!XFS_SB_VERSION_HASNLINK(&mp->m_sb)) { s = XFS_SB_LOCK(mp); if (!XFS_SB_VERSION_HASNLINK(&mp->m_sb)) { XFS_SB_VERSION_ADDNLINK(&mp->m_sb); XFS_SB_UNLOCK(mp, s); xfs_mod_sb(tp, XFS_SB_VERSIONNUM); } else { XFS_SB_UNLOCK(mp, s); } } /* Caller must log the inode */ }
STATIC int xfs_qm_log_quotaoff( xfs_mount_t *mp, xfs_qoff_logitem_t **qoffstartp, uint flags) { xfs_trans_t *tp; int error; unsigned long s; xfs_qoff_logitem_t *qoffi=NULL; uint oldsbqflag=0; tp = xfs_trans_alloc(mp, XFS_TRANS_QM_QUOTAOFF); if ((error = xfs_trans_reserve(tp, 0, sizeof(xfs_qoff_logitem_t) * 2 + mp->m_sb.sb_sectsize + 128, 0, 0, XFS_DEFAULT_LOG_COUNT))) { goto error0; } qoffi = xfs_trans_get_qoff_item(tp, NULL, flags & XFS_ALL_QUOTA_ACCT); xfs_trans_log_quotaoff_item(tp, qoffi); s = XFS_SB_LOCK(mp); oldsbqflag = mp->m_sb.sb_qflags; mp->m_sb.sb_qflags = (mp->m_qflags & ~(flags)) & XFS_MOUNT_QUOTA_ALL; XFS_SB_UNLOCK(mp, s); xfs_mod_sb(tp, XFS_SB_QFLAGS); /* * We have to make sure that the transaction is secure on disk before we * return and actually stop quota accounting. So, make it synchronous. * We don't care about quotoff's performance. */ xfs_trans_set_sync(tp); error = xfs_trans_commit(tp, 0, NULL); error0: if (error) { xfs_trans_cancel(tp, 0); /* * No one else is modifying sb_qflags, so this is OK. * We still hold the quotaofflock. */ s = XFS_SB_LOCK(mp); mp->m_sb.sb_qflags = oldsbqflag; XFS_SB_UNLOCK(mp, s); } *qoffstartp = qoffi; return (error); }
/* * xfs_statvfs * * Fill in the statvfs structure for the given file system. We use * the superblock lock in the mount structure to ensure a consistent * snapshot of the counters returned. */ STATIC int xfs_statvfs( bhv_desc_t *bdp, xfs_statfs_t *statp, vnode_t *vp) { __uint64_t fakeinos; xfs_extlen_t lsize; xfs_mount_t *mp; xfs_sb_t *sbp; unsigned long s; u64 id; mp = XFS_BHVTOM(bdp); sbp = &(mp->m_sb); statp->f_type = XFS_SB_MAGIC; s = XFS_SB_LOCK(mp); statp->f_bsize = sbp->sb_blocksize; lsize = sbp->sb_logstart ? sbp->sb_logblocks : 0; statp->f_blocks = sbp->sb_dblocks - lsize; statp->f_bfree = statp->f_bavail = sbp->sb_fdblocks; fakeinos = statp->f_bfree << sbp->sb_inopblog; #if XFS_BIG_INUMS fakeinos += mp->m_inoadd; #endif statp->f_files = MIN(sbp->sb_icount + fakeinos, (__uint64_t)XFS_MAXINUMBER); if (mp->m_maxicount) #if XFS_BIG_INUMS if (!mp->m_inoadd) #endif statp->f_files = min_t(typeof(statp->f_files), statp->f_files, mp->m_maxicount); statp->f_ffree = statp->f_files - (sbp->sb_icount - sbp->sb_ifree); XFS_SB_UNLOCK(mp, s); id = huge_encode_dev(mp->m_dev); statp->f_fsid.val[0] = (u32)id; statp->f_fsid.val[1] = (u32)(id >> 32); statp->f_namelen = MAXNAMELEN - 1; return 0; }
/* * Clear the quotaflags in memory and in the superblock. */ int xfs_mount_reset_sbqflags(xfs_mount_t *mp) { int error; xfs_trans_t *tp; unsigned long s; mp->m_qflags = 0; /* * It is OK to look at sb_qflags here in mount path, * without SB_LOCK. */ if (mp->m_sb.sb_qflags == 0) return 0; s = XFS_SB_LOCK(mp); mp->m_sb.sb_qflags = 0; XFS_SB_UNLOCK(mp, s); /* * if the fs is readonly, let the incore superblock run * with quotas off but don't flush the update out to disk */ if (XFS_MTOVFS(mp)->vfs_flag & VFS_RDONLY) return 0; #ifdef QUOTADEBUG xfs_fs_cmn_err(CE_NOTE, mp, "Writing superblock quota changes"); #endif tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE); if ((error = xfs_trans_reserve(tp, 0, mp->m_sb.sb_sectsize + 128, 0, 0, XFS_DEFAULT_LOG_COUNT))) { xfs_trans_cancel(tp, 0); xfs_fs_cmn_err(CE_ALERT, mp, "xfs_mount_reset_sbqflags: Superblock update failed!"); return error; } xfs_mod_sb(tp, XFS_SB_QFLAGS); error = xfs_trans_commit(tp, 0, NULL); return error; }
/* * Switch on (a given) quota enforcement for a filesystem. This takes * effect immediately. * (Switching on quota accounting must be done at mount time.) */ STATIC int xfs_qm_scall_quotaon( xfs_mount_t *mp, uint flags) { int error; unsigned long s; uint qf; uint accflags; __int64_t sbflags; if (!capable(CAP_SYS_ADMIN)) return XFS_ERROR(EPERM); flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD); /* * Switching on quota accounting must be done at mount time. */ accflags = flags & XFS_ALL_QUOTA_ACCT; flags &= ~(XFS_ALL_QUOTA_ACCT); sbflags = 0; if (flags == 0) { qdprintk("quotaon: zero flags, m_qflags=%x\n", mp->m_qflags); return XFS_ERROR(EINVAL); } /* No fs can turn on quotas with a delayed effect */ ASSERT((flags & XFS_ALL_QUOTA_ACCT) == 0); /* * Can't enforce without accounting. We check the superblock * qflags here instead of m_qflags because rootfs can have * quota acct on ondisk without m_qflags' knowing. */ if (((flags & XFS_UQUOTA_ACCT) == 0 && (mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) == 0 && (flags & XFS_UQUOTA_ENFD)) || ((flags & XFS_PQUOTA_ACCT) == 0 && (mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) == 0 && (flags & XFS_OQUOTA_ENFD)) || ((flags & XFS_GQUOTA_ACCT) == 0 && (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) == 0 && (flags & XFS_OQUOTA_ENFD))) { qdprintk("Can't enforce without acct, flags=%x sbflags=%x\n", flags, mp->m_sb.sb_qflags); return XFS_ERROR(EINVAL); } /* * If everything's upto-date incore, then don't waste time. */ if ((mp->m_qflags & flags) == flags) return XFS_ERROR(EEXIST); /* * Change sb_qflags on disk but not incore mp->qflags * if this is the root filesystem. */ s = XFS_SB_LOCK(mp); qf = mp->m_sb.sb_qflags; mp->m_sb.sb_qflags = qf | flags; XFS_SB_UNLOCK(mp, s); /* * There's nothing to change if it's the same. */ if ((qf & flags) == flags && sbflags == 0) return XFS_ERROR(EEXIST); sbflags |= XFS_SB_QFLAGS; if ((error = xfs_qm_write_sb_changes(mp, sbflags))) return (error); /* * If we aren't trying to switch on quota enforcement, we are done. */ if (((mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) != (mp->m_qflags & XFS_UQUOTA_ACCT)) || ((mp->m_sb.sb_qflags & XFS_PQUOTA_ACCT) != (mp->m_qflags & XFS_PQUOTA_ACCT)) || ((mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) != (mp->m_qflags & XFS_GQUOTA_ACCT)) || (flags & XFS_ALL_QUOTA_ENFD) == 0) return (0); if (! XFS_IS_QUOTA_RUNNING(mp)) return XFS_ERROR(ESRCH); /* * Switch on quota enforcement in core. */ mutex_lock(&(XFS_QI_QOFFLOCK(mp))); mp->m_qflags |= (flags & XFS_ALL_QUOTA_ENFD); mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); return (0); }
/* * Turn off quota accounting and/or enforcement for all udquots and/or * gdquots. Called only at unmount time. * * This assumes that there are no dquots of this file system cached * incore, and modifies the ondisk dquot directly. Therefore, for example, * it is an error to call this twice, without purging the cache. */ STATIC int xfs_qm_scall_quotaoff( xfs_mount_t *mp, uint flags, boolean_t force) { uint dqtype; unsigned long s; int error; uint inactivate_flags; xfs_qoff_logitem_t *qoffstart; int nculprits; if (!force && !capable(CAP_SYS_ADMIN)) return XFS_ERROR(EPERM); /* * No file system can have quotas enabled on disk but not in core. * Note that quota utilities (like quotaoff) _expect_ * errno == EEXIST here. */ if ((mp->m_qflags & flags) == 0) return XFS_ERROR(EEXIST); error = 0; flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD); /* * We don't want to deal with two quotaoffs messing up each other, * so we're going to serialize it. quotaoff isn't exactly a performance * critical thing. * If quotaoff, then we must be dealing with the root filesystem. */ ASSERT(mp->m_quotainfo); if (mp->m_quotainfo) mutex_lock(&(XFS_QI_QOFFLOCK(mp))); ASSERT(mp->m_quotainfo); /* * If we're just turning off quota enforcement, change mp and go. */ if ((flags & XFS_ALL_QUOTA_ACCT) == 0) { mp->m_qflags &= ~(flags); s = XFS_SB_LOCK(mp); mp->m_sb.sb_qflags = mp->m_qflags; XFS_SB_UNLOCK(mp, s); mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); /* XXX what to do if error ? Revert back to old vals incore ? */ error = xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS); return (error); } dqtype = 0; inactivate_flags = 0; /* * If accounting is off, we must turn enforcement off, clear the * quota 'CHKD' certificate to make it known that we have to * do a quotacheck the next time this quota is turned on. */ if (flags & XFS_UQUOTA_ACCT) { dqtype |= XFS_QMOPT_UQUOTA; flags |= (XFS_UQUOTA_CHKD | XFS_UQUOTA_ENFD); inactivate_flags |= XFS_UQUOTA_ACTIVE; } if (flags & XFS_GQUOTA_ACCT) { dqtype |= XFS_QMOPT_GQUOTA; flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD); inactivate_flags |= XFS_GQUOTA_ACTIVE; } else if (flags & XFS_PQUOTA_ACCT) { dqtype |= XFS_QMOPT_PQUOTA; flags |= (XFS_OQUOTA_CHKD | XFS_OQUOTA_ENFD); inactivate_flags |= XFS_PQUOTA_ACTIVE; } /* * Nothing to do? Don't complain. This happens when we're just * turning off quota enforcement. */ if ((mp->m_qflags & flags) == 0) { mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); return (0); } /* * Write the LI_QUOTAOFF log record, and do SB changes atomically, * and synchronously. */ xfs_qm_log_quotaoff(mp, &qoffstart, flags); /* * Next we clear the XFS_MOUNT_*DQ_ACTIVE bit(s) in the mount struct * to take care of the race between dqget and quotaoff. We don't take * any special locks to reset these bits. All processes need to check * these bits *after* taking inode lock(s) to see if the particular * quota type is in the process of being turned off. If *ACTIVE, it is * guaranteed that all dquot structures and all quotainode ptrs will all * stay valid as long as that inode is kept locked. * * There is no turning back after this. */ mp->m_qflags &= ~inactivate_flags; /* * Give back all the dquot reference(s) held by inodes. * Here we go thru every single incore inode in this file system, and * do a dqrele on the i_udquot/i_gdquot that it may have. * Essentially, as long as somebody has an inode locked, this guarantees * that quotas will not be turned off. This is handy because in a * transaction once we lock the inode(s) and check for quotaon, we can * depend on the quota inodes (and other things) being valid as long as * we keep the lock(s). */ xfs_qm_dqrele_all_inodes(mp, flags); /* * Next we make the changes in the quota flag in the mount struct. * This isn't protected by a particular lock directly, because we * don't want to take a mrlock everytime we depend on quotas being on. */ mp->m_qflags &= ~(flags); /* * Go through all the dquots of this file system and purge them, * according to what was turned off. We may not be able to get rid * of all dquots, because dquots can have temporary references that * are not attached to inodes. eg. xfs_setattr, xfs_create. * So, if we couldn't purge all the dquots from the filesystem, * we can't get rid of the incore data structures. */ while ((nculprits = xfs_qm_dqpurge_all(mp, dqtype|XFS_QMOPT_QUOTAOFF))) delay(10 * nculprits); /* * Transactions that had started before ACTIVE state bit was cleared * could have logged many dquots, so they'd have higher LSNs than * the first QUOTAOFF log record does. If we happen to crash when * the tail of the log has gone past the QUOTAOFF record, but * before the last dquot modification, those dquots __will__ * recover, and that's not good. * * So, we have QUOTAOFF start and end logitems; the start * logitem won't get overwritten until the end logitem appears... */ xfs_qm_log_quotaoff_end(mp, qoffstart, flags); /* * If quotas is completely disabled, close shop. */ if (((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET1) || ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_SET2)) { mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); xfs_qm_destroy_quotainfo(mp); return (0); } /* * Release our quotainode references, and vn_purge them, * if we don't need them anymore. */ if ((dqtype & XFS_QMOPT_UQUOTA) && XFS_QI_UQIP(mp)) { XFS_PURGE_INODE(XFS_QI_UQIP(mp)); XFS_QI_UQIP(mp) = NULL; } if ((dqtype & (XFS_QMOPT_GQUOTA|XFS_QMOPT_PQUOTA)) && XFS_QI_GQIP(mp)) { XFS_PURGE_INODE(XFS_QI_GQIP(mp)); XFS_QI_GQIP(mp) = NULL; } mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); return (error); }