Skip to content

Commit e80fbe1

Browse files
author
Darrick J. Wong
committed
xfs: use metadir for quota inodes
Store the quota inodes in the /quota metadata directory if metadir is enabled. This enables us to stop using the sb_[ugp]uotino fields in the superblock. From this point on, all metadata files will be children of the metadata directory tree root. Signed-off-by: Darrick J. Wong <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
1 parent fc23a42 commit e80fbe1

File tree

4 files changed

+410
-27
lines changed

4 files changed

+410
-27
lines changed

fs/xfs/libxfs/xfs_dquot_buf.c

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
#include "xfs_trans.h"
1717
#include "xfs_qm.h"
1818
#include "xfs_error.h"
19+
#include "xfs_health.h"
20+
#include "xfs_metadir.h"
21+
#include "xfs_metafile.h"
1922

2023
int
2124
xfs_calc_dquots_per_chunk(
@@ -323,3 +326,190 @@ xfs_dquot_to_disk_ts(
323326

324327
return cpu_to_be32(t);
325328
}
329+
330+
inline unsigned int
331+
xfs_dqinode_sick_mask(xfs_dqtype_t type)
332+
{
333+
switch (type) {
334+
case XFS_DQTYPE_USER:
335+
return XFS_SICK_FS_UQUOTA;
336+
case XFS_DQTYPE_GROUP:
337+
return XFS_SICK_FS_GQUOTA;
338+
case XFS_DQTYPE_PROJ:
339+
return XFS_SICK_FS_PQUOTA;
340+
}
341+
342+
ASSERT(0);
343+
return 0;
344+
}
345+
346+
/*
347+
* Load the inode for a given type of quota, assuming that the sb fields have
348+
* been sorted out. This is not true when switching quota types on a V4
349+
* filesystem, so do not use this function for that. If metadir is enabled,
350+
* @dp must be the /quota metadir.
351+
*
352+
* Returns -ENOENT if the quota inode field is NULLFSINO; 0 and an inode on
353+
* success; or a negative errno.
354+
*/
355+
int
356+
xfs_dqinode_load(
357+
struct xfs_trans *tp,
358+
struct xfs_inode *dp,
359+
xfs_dqtype_t type,
360+
struct xfs_inode **ipp)
361+
{
362+
struct xfs_mount *mp = tp->t_mountp;
363+
struct xfs_inode *ip;
364+
enum xfs_metafile_type metafile_type = xfs_dqinode_metafile_type(type);
365+
int error;
366+
367+
if (!xfs_has_metadir(mp)) {
368+
xfs_ino_t ino;
369+
370+
switch (type) {
371+
case XFS_DQTYPE_USER:
372+
ino = mp->m_sb.sb_uquotino;
373+
break;
374+
case XFS_DQTYPE_GROUP:
375+
ino = mp->m_sb.sb_gquotino;
376+
break;
377+
case XFS_DQTYPE_PROJ:
378+
ino = mp->m_sb.sb_pquotino;
379+
break;
380+
default:
381+
ASSERT(0);
382+
return -EFSCORRUPTED;
383+
}
384+
385+
/* Should have set 0 to NULLFSINO when loading superblock */
386+
if (ino == NULLFSINO)
387+
return -ENOENT;
388+
389+
error = xfs_trans_metafile_iget(tp, ino, metafile_type, &ip);
390+
} else {
391+
error = xfs_metadir_load(tp, dp, xfs_dqinode_path(type),
392+
metafile_type, &ip);
393+
if (error == -ENOENT)
394+
return error;
395+
}
396+
if (error) {
397+
if (xfs_metadata_is_sick(error))
398+
xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
399+
return error;
400+
}
401+
402+
if (XFS_IS_CORRUPT(mp, ip->i_df.if_format != XFS_DINODE_FMT_EXTENTS &&
403+
ip->i_df.if_format != XFS_DINODE_FMT_BTREE)) {
404+
xfs_irele(ip);
405+
xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
406+
return -EFSCORRUPTED;
407+
}
408+
409+
if (XFS_IS_CORRUPT(mp, ip->i_projid != 0)) {
410+
xfs_irele(ip);
411+
xfs_fs_mark_sick(mp, xfs_dqinode_sick_mask(type));
412+
return -EFSCORRUPTED;
413+
}
414+
415+
*ipp = ip;
416+
return 0;
417+
}
418+
419+
/* Create a metadata directory quota inode. */
420+
int
421+
xfs_dqinode_metadir_create(
422+
struct xfs_inode *dp,
423+
xfs_dqtype_t type,
424+
struct xfs_inode **ipp)
425+
{
426+
struct xfs_metadir_update upd = {
427+
.dp = dp,
428+
.metafile_type = xfs_dqinode_metafile_type(type),
429+
.path = xfs_dqinode_path(type),
430+
};
431+
int error;
432+
433+
error = xfs_metadir_start_create(&upd);
434+
if (error)
435+
return error;
436+
437+
error = xfs_metadir_create(&upd, S_IFREG);
438+
if (error)
439+
return error;
440+
441+
xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
442+
443+
error = xfs_metadir_commit(&upd);
444+
if (error)
445+
return error;
446+
447+
xfs_finish_inode_setup(upd.ip);
448+
*ipp = upd.ip;
449+
return 0;
450+
}
451+
452+
#ifndef __KERNEL__
453+
/* Link a metadata directory quota inode. */
454+
int
455+
xfs_dqinode_metadir_link(
456+
struct xfs_inode *dp,
457+
xfs_dqtype_t type,
458+
struct xfs_inode *ip)
459+
{
460+
struct xfs_metadir_update upd = {
461+
.dp = dp,
462+
.metafile_type = xfs_dqinode_metafile_type(type),
463+
.path = xfs_dqinode_path(type),
464+
.ip = ip,
465+
};
466+
int error;
467+
468+
error = xfs_metadir_start_link(&upd);
469+
if (error)
470+
return error;
471+
472+
error = xfs_metadir_link(&upd);
473+
if (error)
474+
return error;
475+
476+
xfs_trans_log_inode(upd.tp, upd.ip, XFS_ILOG_CORE);
477+
478+
return xfs_metadir_commit(&upd);
479+
}
480+
#endif /* __KERNEL__ */
481+
482+
/* Create the parent directory for all quota inodes and load it. */
483+
int
484+
xfs_dqinode_mkdir_parent(
485+
struct xfs_mount *mp,
486+
struct xfs_inode **dpp)
487+
{
488+
if (!mp->m_metadirip) {
489+
xfs_fs_mark_sick(mp, XFS_SICK_FS_METADIR);
490+
return -EFSCORRUPTED;
491+
}
492+
493+
return xfs_metadir_mkdir(mp->m_metadirip, "quota", dpp);
494+
}
495+
496+
/*
497+
* Load the parent directory of all quota inodes. Pass the inode to the caller
498+
* because quota functions (e.g. QUOTARM) can be called on the quota files even
499+
* if quotas are not enabled.
500+
*/
501+
int
502+
xfs_dqinode_load_parent(
503+
struct xfs_trans *tp,
504+
struct xfs_inode **dpp)
505+
{
506+
struct xfs_mount *mp = tp->t_mountp;
507+
508+
if (!mp->m_metadirip) {
509+
xfs_fs_mark_sick(mp, XFS_SICK_FS_METADIR);
510+
return -EFSCORRUPTED;
511+
}
512+
513+
return xfs_metadir_load(tp, mp->m_metadirip, "quota", XFS_METAFILE_DIR,
514+
dpp);
515+
}

fs/xfs/libxfs/xfs_quota_defs.h

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,4 +143,47 @@ time64_t xfs_dquot_from_disk_ts(struct xfs_disk_dquot *ddq,
143143
__be32 dtimer);
144144
__be32 xfs_dquot_to_disk_ts(struct xfs_dquot *ddq, time64_t timer);
145145

146+
static inline const char *
147+
xfs_dqinode_path(xfs_dqtype_t type)
148+
{
149+
switch (type) {
150+
case XFS_DQTYPE_USER:
151+
return "user";
152+
case XFS_DQTYPE_GROUP:
153+
return "group";
154+
case XFS_DQTYPE_PROJ:
155+
return "project";
156+
}
157+
158+
ASSERT(0);
159+
return NULL;
160+
}
161+
162+
static inline enum xfs_metafile_type
163+
xfs_dqinode_metafile_type(xfs_dqtype_t type)
164+
{
165+
switch (type) {
166+
case XFS_DQTYPE_USER:
167+
return XFS_METAFILE_USRQUOTA;
168+
case XFS_DQTYPE_GROUP:
169+
return XFS_METAFILE_GRPQUOTA;
170+
case XFS_DQTYPE_PROJ:
171+
return XFS_METAFILE_PRJQUOTA;
172+
}
173+
174+
ASSERT(0);
175+
return XFS_METAFILE_UNKNOWN;
176+
}
177+
178+
unsigned int xfs_dqinode_sick_mask(xfs_dqtype_t type);
179+
180+
int xfs_dqinode_load(struct xfs_trans *tp, struct xfs_inode *dp,
181+
xfs_dqtype_t type, struct xfs_inode **ipp);
182+
int xfs_dqinode_metadir_create(struct xfs_inode *dp, xfs_dqtype_t type,
183+
struct xfs_inode **ipp);
184+
int xfs_dqinode_metadir_link(struct xfs_inode *dp, xfs_dqtype_t type,
185+
struct xfs_inode *ip);
186+
int xfs_dqinode_mkdir_parent(struct xfs_mount *mp, struct xfs_inode **dpp);
187+
int xfs_dqinode_load_parent(struct xfs_trans *tp, struct xfs_inode **dpp);
188+
146189
#endif /* __XFS_QUOTA_H__ */

fs/xfs/libxfs/xfs_sb.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,7 @@ xfs_sb_quota_to_disk(
844844

845845
if (xfs_sb_is_v5(from) &&
846846
(from->sb_features_incompat & XFS_SB_FEAT_INCOMPAT_METADIR)) {
847+
to->sb_qflags = cpu_to_be16(from->sb_qflags);
847848
to->sb_uquotino = cpu_to_be64(0);
848849
to->sb_gquotino = cpu_to_be64(0);
849850
to->sb_pquotino = cpu_to_be64(0);

0 commit comments

Comments
 (0)