mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
xfs: persist quota options with metadir [v5.5 07/10]
Store the quota files in the metadata directory tree instead of the superblock. Since we're introducing a new incompat feature flag, let's also make the mount process bring up quotas in whatever state they were when the filesystem was last unmounted, instead of requiring sysadmins to remember that themselves. With a bit of luck, this should all go splendidly. Signed-off-by: Darrick J. Wong <djwong@kernel.org> -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQQ2qTKExjcn+O1o2YRKO3ySh0YRpgUCZyqQdAAKCRBKO3ySh0YR pod/AP9+FSHh3UF9ccpBQXMAG0NmhXYJBQ7grnAp4q89ko6fCAD+JyUsqi55zDw6 KnLSWZgNuO+aaCmMKXX4hEttA0//gAY= =0YFz -----END PGP SIGNATURE----- Merge tag 'metadir-quotas-6.13_2024-11-05' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into staging-merge xfs: persist quota options with metadir [v5.5 07/10] Store the quota files in the metadata directory tree instead of the superblock. Since we're introducing a new incompat feature flag, let's also make the mount process bring up quotas in whatever state they were when the filesystem was last unmounted, instead of requiring sysadmins to remember that themselves. With a bit of luck, this should all go splendidly. Signed-off-by: Darrick J. Wong <djwong@kernel.org>
This commit is contained in:
commit
93c0f79edf
@ -16,6 +16,9 @@
|
||||
#include "xfs_trans.h"
|
||||
#include "xfs_qm.h"
|
||||
#include "xfs_error.h"
|
||||
#include "xfs_health.h"
|
||||
#include "xfs_metadir.h"
|
||||
#include "xfs_metafile.h"
|
||||
|
||||
int
|
||||
xfs_calc_dquots_per_chunk(
|
||||
@ -323,3 +326,190 @@ xfs_dquot_to_disk_ts(
|
||||
|
||||
return cpu_to_be32(t);
|
||||
}
|
||||
|
||||
inline unsigned int
|
||||
xfs_dqinode_sick_mask(xfs_dqtype_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case XFS_DQTYPE_USER:
|
||||
return XFS_SICK_FS_UQUOTA;
|
||||
case XFS_DQTYPE_GROUP:
|
||||
return XFS_SICK_FS_GQUOTA;
|
||||
case XFS_DQTYPE_PROJ:
|
||||
return XFS_SICK_FS_PQUOTA;
|
||||
}
|
||||
|
||||
ASSERT(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the inode for a given type of quota, assuming that the sb fields have
|
||||
* been sorted out. This is not true when switching quota types on a V4
|
||||
* filesystem, so do not use this function for that. If metadir is enabled,
|
||||
* @dp must be the /quota metadir.
|
||||
*
|
||||
* Returns -ENOENT if the quota inode field is NULLFSINO; 0 and an inode on
|
||||
* success; or a negative errno.
|
||||
*/
|
||||
int
|
||||
xfs_dqinode_load(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_inode *dp,
|
||||
xfs_dqtype_t type,
|
||||
struct xfs_inode **ipp)
|
||||
{
|
||||
struct xfs_mount *mp = tp->t_mountp;
|
||||
struct xfs_inode *ip;
|
||||
enum xfs_metafile_type metafile_type = xfs_dqinode_metafile_type(type);
|
||||
int error;
|
||||
|
||||
if (!xfs_has_metadir(mp)) {
|
||||
xfs_ino_t ino;
|
||||
|
||||
switch (type) {
|
||||
case XFS_DQTYPE_USER:
|
||||
ino = mp->m_sb.sb_uquotino;
|
||||
break;
|
||||
case XFS_DQTYPE_GROUP:
|
||||
ino = mp->m_sb.sb_gquotino;
|
||||
break;
|
||||
case XFS_DQTYPE_PROJ:
|
||||
ino = mp->m_sb.sb_pquotino;
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
/* Should have set 0 to NULLFSINO when loading superblock */
|
||||
if (ino == NULLFSINO)
|
||||
return -ENOENT;
|
||||
|
||||
error = xfs_trans_metafile_iget(tp, ino, metafile_type, &ip);
|
||||
} else {
|
||||
error = xfs_metadir_load(tp, dp, xfs_dqinode_path(type),
|
||||
metafile_type, &ip);
|
||||
if (error == -ENOENT)
|
||||
return error;
|
||||
}
|
||||
if (error) {
|
||||
if (xfs_metadata_is_sick(error))
|
||||
xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
|
||||
return error;
|
||||
}
|
||||
|
||||
if (XFS_IS_CORRUPT(mp, ip->i_df.if_format != XFS_DINODE_FMT_EXTENTS &&
|
||||
ip->i_df.if_format != XFS_DINODE_FMT_BTREE)) {
|
||||
xfs_irele(ip);
|
||||
xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
if (XFS_IS_CORRUPT(mp, ip->i_projid != 0)) {
|
||||
xfs_irele(ip);
|
||||
xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
*ipp = ip;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Create a metadata directory quota inode. */
|
||||
int
|
||||
xfs_dqinode_metadir_create(
|
||||
struct xfs_inode *dp,
|
||||
xfs_dqtype_t type,
|
||||
struct xfs_inode **ipp)
|
||||
{
|
||||
struct xfs_metadir_update upd = {
|
||||
.dp = dp,
|
||||
.metafile_type = xfs_dqinode_metafile_type(type),
|
||||
.path = xfs_dqinode_path(type),
|
||||
};
|
||||
int error;
|
||||
|
||||
error = xfs_metadir_start_create(&upd);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xfs_metadir_create(&upd, S_IFREG);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
|
||||
|
||||
error = xfs_metadir_commit(&upd);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_finish_inode_setup(upd.ip);
|
||||
*ipp = upd.ip;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef __KERNEL__
|
||||
/* Link a metadata directory quota inode. */
|
||||
int
|
||||
xfs_dqinode_metadir_link(
|
||||
struct xfs_inode *dp,
|
||||
xfs_dqtype_t type,
|
||||
struct xfs_inode *ip)
|
||||
{
|
||||
struct xfs_metadir_update upd = {
|
||||
.dp = dp,
|
||||
.metafile_type = xfs_dqinode_metafile_type(type),
|
||||
.path = xfs_dqinode_path(type),
|
||||
.ip = ip,
|
||||
};
|
||||
int error;
|
||||
|
||||
error = xfs_metadir_start_link(&upd);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xfs_metadir_link(&upd);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
|
||||
|
||||
return xfs_metadir_commit(&upd);
|
||||
}
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
/* Create the parent directory for all quota inodes and load it. */
|
||||
int
|
||||
xfs_dqinode_mkdir_parent(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_inode **dpp)
|
||||
{
|
||||
if (!mp->m_metadirip) {
|
||||
xfs_fs_mark_sick(mp, XFS_SICK_FS_METADIR);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
return xfs_metadir_mkdir(mp->m_metadirip, "quota", dpp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load the parent directory of all quota inodes. Pass the inode to the caller
|
||||
* because quota functions (e.g. QUOTARM) can be called on the quota files even
|
||||
* if quotas are not enabled.
|
||||
*/
|
||||
int
|
||||
xfs_dqinode_load_parent(
|
||||
struct xfs_trans *tp,
|
||||
struct xfs_inode **dpp)
|
||||
{
|
||||
struct xfs_mount *mp = tp->t_mountp;
|
||||
|
||||
if (!mp->m_metadirip) {
|
||||
xfs_fs_mark_sick(mp, XFS_SICK_FS_METADIR);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
return xfs_metadir_load(tp, mp->m_metadirip, "quota", XFS_METAFILE_DIR,
|
||||
dpp);
|
||||
}
|
||||
|
@ -825,9 +825,13 @@ struct xfs_scrub_vec_head {
|
||||
#define XFS_SCRUB_METAPATH_RTDIR (1) /* rtrgroups metadir */
|
||||
#define XFS_SCRUB_METAPATH_RTBITMAP (2) /* per-rtg bitmap */
|
||||
#define XFS_SCRUB_METAPATH_RTSUMMARY (3) /* per-rtg summary */
|
||||
#define XFS_SCRUB_METAPATH_QUOTADIR (4) /* quota metadir */
|
||||
#define XFS_SCRUB_METAPATH_USRQUOTA (5) /* user quota */
|
||||
#define XFS_SCRUB_METAPATH_GRPQUOTA (6) /* group quota */
|
||||
#define XFS_SCRUB_METAPATH_PRJQUOTA (7) /* project quota */
|
||||
|
||||
/* Number of metapath sm_ino values */
|
||||
#define XFS_SCRUB_METAPATH_NR (4)
|
||||
#define XFS_SCRUB_METAPATH_NR (8)
|
||||
|
||||
/*
|
||||
* ioctl limits
|
||||
|
@ -143,4 +143,47 @@ time64_t xfs_dquot_from_disk_ts(struct xfs_disk_dquot *ddq,
|
||||
__be32 dtimer);
|
||||
__be32 xfs_dquot_to_disk_ts(struct xfs_dquot *ddq, time64_t timer);
|
||||
|
||||
static inline const char *
|
||||
xfs_dqinode_path(xfs_dqtype_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case XFS_DQTYPE_USER:
|
||||
return "user";
|
||||
case XFS_DQTYPE_GROUP:
|
||||
return "group";
|
||||
case XFS_DQTYPE_PROJ:
|
||||
return "project";
|
||||
}
|
||||
|
||||
ASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline enum xfs_metafile_type
|
||||
xfs_dqinode_metafile_type(xfs_dqtype_t type)
|
||||
{
|
||||
switch (type) {
|
||||
case XFS_DQTYPE_USER:
|
||||
return XFS_METAFILE_USRQUOTA;
|
||||
case XFS_DQTYPE_GROUP:
|
||||
return XFS_METAFILE_GRPQUOTA;
|
||||
case XFS_DQTYPE_PROJ:
|
||||
return XFS_METAFILE_PRJQUOTA;
|
||||
}
|
||||
|
||||
ASSERT(0);
|
||||
return XFS_METAFILE_UNKNOWN;
|
||||
}
|
||||
|
||||
unsigned int xfs_dqinode_sick_mask(xfs_dqtype_t type);
|
||||
|
||||
int xfs_dqinode_load(struct xfs_trans *tp, struct xfs_inode *dp,
|
||||
xfs_dqtype_t type, struct xfs_inode **ipp);
|
||||
int xfs_dqinode_metadir_create(struct xfs_inode *dp, xfs_dqtype_t type,
|
||||
struct xfs_inode **ipp);
|
||||
int xfs_dqinode_metadir_link(struct xfs_inode *dp, xfs_dqtype_t type,
|
||||
struct xfs_inode *ip);
|
||||
int xfs_dqinode_mkdir_parent(struct xfs_mount *mp, struct xfs_inode **dpp);
|
||||
int xfs_dqinode_load_parent(struct xfs_trans *tp, struct xfs_inode **dpp);
|
||||
|
||||
#endif /* __XFS_QUOTA_H__ */
|
||||
|
@ -858,6 +858,7 @@ xfs_sb_quota_to_disk(
|
||||
|
||||
if (xfs_sb_is_v5(from) &&
|
||||
(from->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_METADIR)) {
|
||||
to->sb_qflags = cpu_to_be16(from->sb_qflags);
|
||||
to->sb_uquotino = cpu_to_be64(0);
|
||||
to->sb_gquotino = cpu_to_be64(0);
|
||||
to->sb_pquotino = cpu_to_be64(0);
|
||||
|
@ -165,6 +165,74 @@ out_put_rtg:
|
||||
# define xchk_setup_metapath_rtginode(...) (-ENOENT)
|
||||
#endif /* CONFIG_XFS_RT */
|
||||
|
||||
#ifdef CONFIG_XFS_QUOTA
|
||||
/* Scan the /quota directory itself. */
|
||||
static int
|
||||
xchk_setup_metapath_quotadir(
|
||||
struct xfs_scrub *sc)
|
||||
{
|
||||
struct xfs_trans *tp;
|
||||
struct xfs_inode *dp = NULL;
|
||||
int error;
|
||||
|
||||
error = xfs_trans_alloc_empty(sc->mp, &tp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xfs_dqinode_load_parent(tp, &dp);
|
||||
xfs_trans_cancel(tp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xchk_setup_metapath_scan(sc, sc->mp->m_metadirip,
|
||||
kasprintf(GFP_KERNEL, "quota"), dp);
|
||||
xfs_irele(dp);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Scan a quota inode under the /quota directory. */
|
||||
static int
|
||||
xchk_setup_metapath_dqinode(
|
||||
struct xfs_scrub *sc,
|
||||
xfs_dqtype_t type)
|
||||
{
|
||||
struct xfs_trans *tp = NULL;
|
||||
struct xfs_inode *dp = NULL;
|
||||
struct xfs_inode *ip = NULL;
|
||||
const char *path;
|
||||
int error;
|
||||
|
||||
error = xfs_trans_alloc_empty(sc->mp, &tp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xfs_dqinode_load_parent(tp, &dp);
|
||||
if (error)
|
||||
goto out_cancel;
|
||||
|
||||
error = xfs_dqinode_load(tp, dp, type, &ip);
|
||||
if (error)
|
||||
goto out_dp;
|
||||
|
||||
xfs_trans_cancel(tp);
|
||||
tp = NULL;
|
||||
|
||||
path = kasprintf(GFP_KERNEL, "%s", xfs_dqinode_path(type));
|
||||
error = xchk_setup_metapath_scan(sc, dp, path, ip);
|
||||
|
||||
xfs_irele(ip);
|
||||
out_dp:
|
||||
xfs_irele(dp);
|
||||
out_cancel:
|
||||
if (tp)
|
||||
xfs_trans_cancel(tp);
|
||||
return error;
|
||||
}
|
||||
#else
|
||||
# define xchk_setup_metapath_quotadir(...) (-ENOENT)
|
||||
# define xchk_setup_metapath_dqinode(...) (-ENOENT)
|
||||
#endif /* CONFIG_XFS_QUOTA */
|
||||
|
||||
int
|
||||
xchk_setup_metapath(
|
||||
struct xfs_scrub *sc)
|
||||
@ -186,6 +254,14 @@ xchk_setup_metapath(
|
||||
return xchk_setup_metapath_rtginode(sc, XFS_RTGI_BITMAP);
|
||||
case XFS_SCRUB_METAPATH_RTSUMMARY:
|
||||
return xchk_setup_metapath_rtginode(sc, XFS_RTGI_SUMMARY);
|
||||
case XFS_SCRUB_METAPATH_QUOTADIR:
|
||||
return xchk_setup_metapath_quotadir(sc);
|
||||
case XFS_SCRUB_METAPATH_USRQUOTA:
|
||||
return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_USER);
|
||||
case XFS_SCRUB_METAPATH_GRPQUOTA:
|
||||
return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_GROUP);
|
||||
case XFS_SCRUB_METAPATH_PRJQUOTA:
|
||||
return xchk_setup_metapath_dqinode(sc, XFS_DQTYPE_PROJ);
|
||||
default:
|
||||
return -ENOENT;
|
||||
}
|
||||
|
@ -852,6 +852,13 @@ xfs_mountfs(
|
||||
if (error)
|
||||
goto out_fail_wait;
|
||||
|
||||
/*
|
||||
* If we're resuming quota status, pick up the preliminary qflags from
|
||||
* the ondisk superblock so that we know if we should recover dquots.
|
||||
*/
|
||||
if (xfs_is_resuming_quotaon(mp))
|
||||
xfs_qm_resume_quotaon(mp);
|
||||
|
||||
/*
|
||||
* Log's mount-time initialization. The first part of recovery can place
|
||||
* some items on the AIL, to be handled when recovery is finished or
|
||||
@ -865,6 +872,14 @@ xfs_mountfs(
|
||||
goto out_inodegc_shrinker;
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're resuming quota status and recovered the log, re-sample the
|
||||
* qflags from the ondisk superblock now that we've recovered it, just
|
||||
* in case someone shut down enforcement just before a crash.
|
||||
*/
|
||||
if (xfs_clear_resuming_quotaon(mp) && xlog_recovery_needed(mp->m_log))
|
||||
xfs_qm_resume_quotaon(mp);
|
||||
|
||||
/*
|
||||
* If logged xattrs are still enabled after log recovery finishes, then
|
||||
* they'll be available until unmount. Otherwise, turn them off.
|
||||
|
@ -499,6 +499,8 @@ __XFS_HAS_FEAT(nouuid, NOUUID)
|
||||
#define XFS_OPSTATE_WARNED_PPTR 16
|
||||
/* Kernel has logged a warning about metadata dirs being used on this fs. */
|
||||
#define XFS_OPSTATE_WARNED_METADIR 17
|
||||
/* Filesystem should use qflags to determine quotaon status */
|
||||
#define XFS_OPSTATE_RESUMING_QUOTAON 18
|
||||
|
||||
#define __XFS_IS_OPSTATE(name, NAME) \
|
||||
static inline bool xfs_is_ ## name (struct xfs_mount *mp) \
|
||||
@ -523,9 +525,24 @@ __XFS_IS_OPSTATE(inodegc_enabled, INODEGC_ENABLED)
|
||||
__XFS_IS_OPSTATE(blockgc_enabled, BLOCKGC_ENABLED)
|
||||
#ifdef CONFIG_XFS_QUOTA
|
||||
__XFS_IS_OPSTATE(quotacheck_running, QUOTACHECK_RUNNING)
|
||||
__XFS_IS_OPSTATE(resuming_quotaon, RESUMING_QUOTAON)
|
||||
#else
|
||||
# define xfs_is_quotacheck_running(mp) (false)
|
||||
#endif
|
||||
static inline bool xfs_is_quotacheck_running(struct xfs_mount *mp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline bool xfs_is_resuming_quotaon(struct xfs_mount *mp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
static inline void xfs_set_resuming_quotaon(struct xfs_mount *m)
|
||||
{
|
||||
}
|
||||
static inline bool xfs_clear_resuming_quotaon(struct xfs_mount *mp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif /* CONFIG_XFS_QUOTA */
|
||||
__XFS_IS_OPSTATE(done_with_log_incompat, UNSET_LOG_INCOMPAT)
|
||||
__XFS_IS_OPSTATE(using_logged_xattrs, USE_LARP)
|
||||
|
||||
|
250
fs/xfs/xfs_qm.c
250
fs/xfs/xfs_qm.c
@ -40,7 +40,6 @@
|
||||
STATIC int xfs_qm_init_quotainos(struct xfs_mount *mp);
|
||||
STATIC int xfs_qm_init_quotainfo(struct xfs_mount *mp);
|
||||
|
||||
STATIC void xfs_qm_destroy_quotainos(struct xfs_quotainfo *qi);
|
||||
STATIC void xfs_qm_dqfree_one(struct xfs_dquot *dqp);
|
||||
/*
|
||||
* We use the batch lookup interface to iterate over the dquots as it
|
||||
@ -226,6 +225,24 @@ xfs_qm_unmount_rt(
|
||||
xfs_rtgroup_rele(rtg);
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_qm_destroy_quotainos(
|
||||
struct xfs_quotainfo *qi)
|
||||
{
|
||||
if (qi->qi_uquotaip) {
|
||||
xfs_irele(qi->qi_uquotaip);
|
||||
qi->qi_uquotaip = NULL; /* paranoia */
|
||||
}
|
||||
if (qi->qi_gquotaip) {
|
||||
xfs_irele(qi->qi_gquotaip);
|
||||
qi->qi_gquotaip = NULL;
|
||||
}
|
||||
if (qi->qi_pquotaip) {
|
||||
xfs_irele(qi->qi_pquotaip);
|
||||
qi->qi_pquotaip = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from the vfsops layer.
|
||||
*/
|
||||
@ -250,20 +267,8 @@ xfs_qm_unmount_quotas(
|
||||
/*
|
||||
* Release the quota inodes.
|
||||
*/
|
||||
if (mp->m_quotainfo) {
|
||||
if (mp->m_quotainfo->qi_uquotaip) {
|
||||
xfs_irele(mp->m_quotainfo->qi_uquotaip);
|
||||
mp->m_quotainfo->qi_uquotaip = NULL;
|
||||
}
|
||||
if (mp->m_quotainfo->qi_gquotaip) {
|
||||
xfs_irele(mp->m_quotainfo->qi_gquotaip);
|
||||
mp->m_quotainfo->qi_gquotaip = NULL;
|
||||
}
|
||||
if (mp->m_quotainfo->qi_pquotaip) {
|
||||
xfs_irele(mp->m_quotainfo->qi_pquotaip);
|
||||
mp->m_quotainfo->qi_pquotaip = NULL;
|
||||
}
|
||||
}
|
||||
if (mp->m_quotainfo)
|
||||
xfs_qm_destroy_quotainos(mp->m_quotainfo);
|
||||
}
|
||||
|
||||
STATIC int
|
||||
@ -640,6 +645,157 @@ xfs_qm_init_timelimits(
|
||||
xfs_qm_dqdestroy(dqp);
|
||||
}
|
||||
|
||||
static int
|
||||
xfs_qm_load_metadir_qinos(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_quotainfo *qi,
|
||||
struct xfs_inode **dpp)
|
||||
{
|
||||
struct xfs_trans *tp;
|
||||
int error;
|
||||
|
||||
error = xfs_trans_alloc_empty(mp, &tp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
error = xfs_dqinode_load_parent(tp, dpp);
|
||||
if (error == -ENOENT) {
|
||||
/* no quota dir directory, but we'll create one later */
|
||||
error = 0;
|
||||
goto out_trans;
|
||||
}
|
||||
if (error)
|
||||
goto out_trans;
|
||||
|
||||
if (XFS_IS_UQUOTA_ON(mp)) {
|
||||
error = xfs_dqinode_load(tp, *dpp, XFS_DQTYPE_USER,
|
||||
&qi->qi_uquotaip);
|
||||
if (error && error != -ENOENT)
|
||||
goto out_trans;
|
||||
}
|
||||
|
||||
if (XFS_IS_GQUOTA_ON(mp)) {
|
||||
error = xfs_dqinode_load(tp, *dpp, XFS_DQTYPE_GROUP,
|
||||
&qi->qi_gquotaip);
|
||||
if (error && error != -ENOENT)
|
||||
goto out_trans;
|
||||
}
|
||||
|
||||
if (XFS_IS_PQUOTA_ON(mp)) {
|
||||
error = xfs_dqinode_load(tp, *dpp, XFS_DQTYPE_PROJ,
|
||||
&qi->qi_pquotaip);
|
||||
if (error && error != -ENOENT)
|
||||
goto out_trans;
|
||||
}
|
||||
|
||||
error = 0;
|
||||
out_trans:
|
||||
xfs_trans_cancel(tp);
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Create quota inodes in the metadata directory tree. */
|
||||
STATIC int
|
||||
xfs_qm_create_metadir_qinos(
|
||||
struct xfs_mount *mp,
|
||||
struct xfs_quotainfo *qi,
|
||||
struct xfs_inode **dpp)
|
||||
{
|
||||
int error;
|
||||
|
||||
if (!*dpp) {
|
||||
error = xfs_dqinode_mkdir_parent(mp, dpp);
|
||||
if (error && error != -EEXIST)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (XFS_IS_UQUOTA_ON(mp) && !qi->qi_uquotaip) {
|
||||
error = xfs_dqinode_metadir_create(*dpp, XFS_DQTYPE_USER,
|
||||
&qi->qi_uquotaip);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (XFS_IS_GQUOTA_ON(mp) && !qi->qi_gquotaip) {
|
||||
error = xfs_dqinode_metadir_create(*dpp, XFS_DQTYPE_GROUP,
|
||||
&qi->qi_gquotaip);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
if (XFS_IS_PQUOTA_ON(mp) && !qi->qi_pquotaip) {
|
||||
error = xfs_dqinode_metadir_create(*dpp, XFS_DQTYPE_PROJ,
|
||||
&qi->qi_pquotaip);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add QUOTABIT to sb_versionnum and initialize qflags in preparation for
|
||||
* creating quota files on a metadir filesystem.
|
||||
*/
|
||||
STATIC int
|
||||
xfs_qm_prep_metadir_sb(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
struct xfs_trans *tp;
|
||||
int error;
|
||||
|
||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_sb, 0, 0, 0, &tp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
spin_lock(&mp->m_sb_lock);
|
||||
|
||||
xfs_add_quota(mp);
|
||||
|
||||
/* qflags will get updated fully _after_ quotacheck */
|
||||
mp->m_sb.sb_qflags = mp->m_qflags & XFS_ALL_QUOTA_ACCT;
|
||||
|
||||
spin_unlock(&mp->m_sb_lock);
|
||||
xfs_log_sb(tp);
|
||||
|
||||
return xfs_trans_commit(tp);
|
||||
}
|
||||
|
||||
/*
|
||||
* Load existing quota inodes or create them. Since this is a V5 filesystem,
|
||||
* we don't have to deal with the grp/prjquota switcheroo thing from V4.
|
||||
*/
|
||||
STATIC int
|
||||
xfs_qm_init_metadir_qinos(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
struct xfs_quotainfo *qi = mp->m_quotainfo;
|
||||
struct xfs_inode *dp = NULL;
|
||||
int error;
|
||||
|
||||
if (!xfs_has_quota(mp)) {
|
||||
error = xfs_qm_prep_metadir_sb(mp);
|
||||
if (error)
|
||||
return error;
|
||||
}
|
||||
|
||||
error = xfs_qm_load_metadir_qinos(mp, qi, &dp);
|
||||
if (error)
|
||||
goto out_err;
|
||||
|
||||
error = xfs_qm_create_metadir_qinos(mp, qi, &dp);
|
||||
if (error)
|
||||
goto out_err;
|
||||
|
||||
xfs_irele(dp);
|
||||
return 0;
|
||||
out_err:
|
||||
xfs_qm_destroy_quotainos(mp->m_quotainfo);
|
||||
if (dp)
|
||||
xfs_irele(dp);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* This initializes all the quota information that's kept in the
|
||||
* mount structure
|
||||
@ -664,7 +820,10 @@ xfs_qm_init_quotainfo(
|
||||
* See if quotainodes are setup, and if not, allocate them,
|
||||
* and change the superblock accordingly.
|
||||
*/
|
||||
error = xfs_qm_init_quotainos(mp);
|
||||
if (xfs_has_metadir(mp))
|
||||
error = xfs_qm_init_metadir_qinos(mp);
|
||||
else
|
||||
error = xfs_qm_init_quotainos(mp);
|
||||
if (error)
|
||||
goto out_free_lru;
|
||||
|
||||
@ -1576,7 +1735,7 @@ xfs_qm_mount_quotas(
|
||||
}
|
||||
|
||||
if (error) {
|
||||
xfs_warn(mp, "Failed to initialize disk quotas.");
|
||||
xfs_warn(mp, "Failed to initialize disk quotas, err %d.", error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -1595,31 +1754,26 @@ xfs_qm_qino_load(
|
||||
xfs_dqtype_t type,
|
||||
struct xfs_inode **ipp)
|
||||
{
|
||||
xfs_ino_t ino = NULLFSINO;
|
||||
enum xfs_metafile_type metafile_type = XFS_METAFILE_UNKNOWN;
|
||||
struct xfs_trans *tp;
|
||||
struct xfs_inode *dp = NULL;
|
||||
int error;
|
||||
|
||||
switch (type) {
|
||||
case XFS_DQTYPE_USER:
|
||||
ino = mp->m_sb.sb_uquotino;
|
||||
metafile_type = XFS_METAFILE_USRQUOTA;
|
||||
break;
|
||||
case XFS_DQTYPE_GROUP:
|
||||
ino = mp->m_sb.sb_gquotino;
|
||||
metafile_type = XFS_METAFILE_GRPQUOTA;
|
||||
break;
|
||||
case XFS_DQTYPE_PROJ:
|
||||
ino = mp->m_sb.sb_pquotino;
|
||||
metafile_type = XFS_METAFILE_PRJQUOTA;
|
||||
break;
|
||||
default:
|
||||
ASSERT(0);
|
||||
return -EFSCORRUPTED;
|
||||
error = xfs_trans_alloc_empty(mp, &tp);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
if (xfs_has_metadir(mp)) {
|
||||
error = xfs_dqinode_load_parent(tp, &dp);
|
||||
if (error)
|
||||
goto out_cancel;
|
||||
}
|
||||
|
||||
if (ino == NULLFSINO)
|
||||
return -ENOENT;
|
||||
|
||||
return xfs_metafile_iget(mp, ino, metafile_type, ipp);
|
||||
error = xfs_dqinode_load(tp, dp, type, ipp);
|
||||
if (dp)
|
||||
xfs_irele(dp);
|
||||
out_cancel:
|
||||
xfs_trans_cancel(tp);
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1712,24 +1866,6 @@ error_rele:
|
||||
return error;
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_qm_destroy_quotainos(
|
||||
struct xfs_quotainfo *qi)
|
||||
{
|
||||
if (qi->qi_uquotaip) {
|
||||
xfs_irele(qi->qi_uquotaip);
|
||||
qi->qi_uquotaip = NULL; /* paranoia */
|
||||
}
|
||||
if (qi->qi_gquotaip) {
|
||||
xfs_irele(qi->qi_gquotaip);
|
||||
qi->qi_gquotaip = NULL;
|
||||
}
|
||||
if (qi->qi_pquotaip) {
|
||||
xfs_irele(qi->qi_pquotaip);
|
||||
qi->qi_pquotaip = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
STATIC void
|
||||
xfs_qm_dqfree_one(
|
||||
struct xfs_dquot *dqp)
|
||||
|
@ -135,3 +135,21 @@ xfs_qm_newmount(
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the sysadmin didn't provide any quota mount options, restore the quota
|
||||
* accounting and enforcement state from the ondisk superblock. Only do this
|
||||
* for metadir filesystems because this is a behavior change.
|
||||
*/
|
||||
void
|
||||
xfs_qm_resume_quotaon(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
if (!xfs_has_metadir(mp))
|
||||
return;
|
||||
if (xfs_has_norecovery(mp))
|
||||
return;
|
||||
|
||||
mp->m_qflags = mp->m_sb.sb_qflags & (XFS_ALL_QUOTA_ACCT |
|
||||
XFS_ALL_QUOTA_ENFD);
|
||||
}
|
||||
|
@ -125,6 +125,7 @@ extern void xfs_qm_dqdetach(struct xfs_inode *);
|
||||
extern void xfs_qm_dqrele(struct xfs_dquot *);
|
||||
extern void xfs_qm_statvfs(struct xfs_inode *, struct kstatfs *);
|
||||
extern int xfs_qm_newmount(struct xfs_mount *, uint *, uint *);
|
||||
void xfs_qm_resume_quotaon(struct xfs_mount *mp);
|
||||
extern void xfs_qm_mount_quotas(struct xfs_mount *);
|
||||
extern void xfs_qm_unmount(struct xfs_mount *);
|
||||
extern void xfs_qm_unmount_quotas(struct xfs_mount *);
|
||||
@ -202,6 +203,7 @@ xfs_trans_reserve_quota_icreate(struct xfs_trans *tp, struct xfs_dquot *udqp,
|
||||
#define xfs_qm_dqrele(d) do { (d) = (d); } while(0)
|
||||
#define xfs_qm_statvfs(ip, s) do { } while(0)
|
||||
#define xfs_qm_newmount(mp, a, b) (0)
|
||||
#define xfs_qm_resume_quotaon(mp) ((void)0)
|
||||
#define xfs_qm_mount_quotas(mp)
|
||||
#define xfs_qm_unmount(mp)
|
||||
#define xfs_qm_unmount_quotas(mp)
|
||||
|
@ -67,6 +67,9 @@ enum xfs_dax_mode {
|
||||
XFS_DAX_NEVER = 2,
|
||||
};
|
||||
|
||||
/* Were quota mount options provided? Must use the upper 16 bits of qflags. */
|
||||
#define XFS_QFLAGS_MNTOPTS (1U << 31)
|
||||
|
||||
static void
|
||||
xfs_mount_set_dax_mode(
|
||||
struct xfs_mount *mp,
|
||||
@ -1264,6 +1267,8 @@ xfs_fs_parse_param(
|
||||
int size = 0;
|
||||
int opt;
|
||||
|
||||
BUILD_BUG_ON(XFS_QFLAGS_MNTOPTS & XFS_MOUNT_QUOTA_ALL);
|
||||
|
||||
opt = fs_parse(fc, xfs_fs_parameters, param, &result);
|
||||
if (opt < 0)
|
||||
return opt;
|
||||
@ -1341,32 +1346,39 @@ xfs_fs_parse_param(
|
||||
case Opt_noquota:
|
||||
parsing_mp->m_qflags &= ~XFS_ALL_QUOTA_ACCT;
|
||||
parsing_mp->m_qflags &= ~XFS_ALL_QUOTA_ENFD;
|
||||
parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
|
||||
return 0;
|
||||
case Opt_quota:
|
||||
case Opt_uquota:
|
||||
case Opt_usrquota:
|
||||
parsing_mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ENFD);
|
||||
parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
|
||||
return 0;
|
||||
case Opt_qnoenforce:
|
||||
case Opt_uqnoenforce:
|
||||
parsing_mp->m_qflags |= XFS_UQUOTA_ACCT;
|
||||
parsing_mp->m_qflags &= ~XFS_UQUOTA_ENFD;
|
||||
parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
|
||||
return 0;
|
||||
case Opt_pquota:
|
||||
case Opt_prjquota:
|
||||
parsing_mp->m_qflags |= (XFS_PQUOTA_ACCT | XFS_PQUOTA_ENFD);
|
||||
parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
|
||||
return 0;
|
||||
case Opt_pqnoenforce:
|
||||
parsing_mp->m_qflags |= XFS_PQUOTA_ACCT;
|
||||
parsing_mp->m_qflags &= ~XFS_PQUOTA_ENFD;
|
||||
parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
|
||||
return 0;
|
||||
case Opt_gquota:
|
||||
case Opt_grpquota:
|
||||
parsing_mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ENFD);
|
||||
parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
|
||||
return 0;
|
||||
case Opt_gqnoenforce:
|
||||
parsing_mp->m_qflags |= XFS_GQUOTA_ACCT;
|
||||
parsing_mp->m_qflags &= ~XFS_GQUOTA_ENFD;
|
||||
parsing_mp->m_qflags |= XFS_QFLAGS_MNTOPTS;
|
||||
return 0;
|
||||
case Opt_discard:
|
||||
parsing_mp->m_features |= XFS_FEAT_DISCARD;
|
||||
@ -1433,7 +1445,8 @@ xfs_fs_validate_params(
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!IS_ENABLED(CONFIG_XFS_QUOTA) && mp->m_qflags != 0) {
|
||||
if (!IS_ENABLED(CONFIG_XFS_QUOTA) &&
|
||||
(mp->m_qflags & ~XFS_QFLAGS_MNTOPTS)) {
|
||||
xfs_warn(mp, "quota support not available in this kernel.");
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -1768,6 +1781,14 @@ xfs_fs_fill_super(
|
||||
if (xfs_has_parent(mp))
|
||||
xfs_warn_experimental(mp, XFS_EXPERIMENTAL_PPTR);
|
||||
|
||||
/*
|
||||
* If no quota mount options were provided, maybe we'll try to pick
|
||||
* up the quota accounting and enforcement flags from the ondisk sb.
|
||||
*/
|
||||
if (!(mp->m_qflags & XFS_QFLAGS_MNTOPTS))
|
||||
xfs_set_resuming_quotaon(mp);
|
||||
mp->m_qflags &= ~XFS_QFLAGS_MNTOPTS;
|
||||
|
||||
error = xfs_mountfs(mp);
|
||||
if (error)
|
||||
goto out_filestream_unmount;
|
||||
@ -1954,6 +1975,8 @@ xfs_fs_reconfigure(
|
||||
int flags = fc->sb_flags;
|
||||
int error;
|
||||
|
||||
new_mp->m_qflags &= ~XFS_QFLAGS_MNTOPTS;
|
||||
|
||||
/* version 5 superblocks always support version counters. */
|
||||
if (xfs_has_crc(mp))
|
||||
fc->sb_flags |= SB_I_VERSION;
|
||||
|
Loading…
Reference in New Issue
Block a user