Linux XFS filesystem development
 help / color / mirror / Atom feed
* [PATCHSET 2/2] xfsprogs: use quotactl_fd when possible
@ 2026-09-14  5:48 Darrick J. Wong
  2026-09-14  5:49 ` [PATCH 1/7] libfrog: hoist xfsquotactl out of xfs_quota Darrick J. Wong
                   ` (6 more replies)
  0 siblings, 7 replies; 18+ messages in thread
From: Darrick J. Wong @ 2026-09-14  5:48 UTC (permalink / raw)
  To: aalbersh, djwong; +Cc: linux-xfs

Hi all,

xfs_quota (really, quotactl()) has an interesting interface quirk in
that one passes a path to a block device in order to administer quotas
on a mounted filesystem.  Linux 5.17 added a new quotactl_fd syscall
so that one only has to have an open fd to the target filesystem.

Let's port xfsprogs to this new syscall because repeated path lookups
in xfs_quota mean that it's prone to failure if the device gets renamed
while it's running (e.g. lvm).  Weirder still, you can unmount the
filesystem while xfs_quota is running.

If you're going to start using this code, I strongly recommend pulling
from my git trees, which are linked below.

With a bit of luck, this should all go splendidly.
Comments and questions are, as always, welcome.

--D

xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=quotactl-fd
---
Commits in this patchset:
 * libfrog: hoist xfsquotactl out of xfs_quota
 * libfrog: clean up xfsquotactl a little bit
 * libfrog: enhance struct fs_path to store optional mount fd
 * libfrog: try to pass struct fs_path objects to quotactl wrapper
 * libfrog: add quotactl_fd support to xfsquotactl
 * xfs_quota: open the filesystem mountpoint for quota operations
 * xfs_spaceman: port makecfg to use xfsquotactl
---
 libfrog/paths.h       |    1 +
 libfrog/quotactl.h    |   30 ++++++++++++++++++++++++
 quota/init.h          |    2 ++
 quota/quota.h         |   18 +-------------
 configure.ac          |    1 +
 include/builddefs.in  |    1 +
 libfrog/Makefile      |    6 +++++
 libfrog/paths.c       |    3 ++
 libfrog/quotactl.c    |   45 +++++++++++++++++++++++++++---------
 m4/package_libcdev.m4 |   19 +++++++++++++++
 quota/Makefile        |    2 +-
 quota/edit.c          |   36 +++++++++++++++++------------
 quota/free.c          |   11 +++++----
 quota/init.c          |   15 ++++++++++++
 quota/path.c          |    4 ++-
 quota/project.c       |    7 ++++++
 quota/quota.c         |    6 +++--
 quota/report.c        |   61 ++++++++++++++++++++++++-------------------------
 quota/state.c         |   38 ++++++++++++++++++-------------
 spaceman/info.c       |    7 ++----
 20 files changed, 208 insertions(+), 105 deletions(-)
 create mode 100644 libfrog/quotactl.h
 rename quota/linux.c => libfrog/quotactl.c (53%)


^ permalink raw reply	[flat|nested] 18+ messages in thread

* [PATCH 1/7] libfrog: hoist xfsquotactl out of xfs_quota
  2026-09-14  5:48 [PATCHSET 2/2] xfsprogs: use quotactl_fd when possible Darrick J. Wong
@ 2026-09-14  5:49 ` Darrick J. Wong
  2026-09-15 12:24   ` Christoph Hellwig
  2026-09-14  5:50 ` [PATCH 2/7] libfrog: clean up xfsquotactl a little bit Darrick J. Wong
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Darrick J. Wong @ 2026-09-14  5:49 UTC (permalink / raw)
  To: aalbersh, djwong; +Cc: linux-xfs

From: Darrick J. Wong <djwong@kernel.org>

Hoist this generic function to libfrog so that we can share it with
xfs_spaceman later on.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
 libfrog/quotactl.h |   28 ++++++++++++++++++++++++++++
 quota/quota.h      |   18 +-----------------
 libfrog/Makefile   |    2 ++
 libfrog/quotactl.c |    2 +-
 quota/Makefile     |    2 +-
 5 files changed, 33 insertions(+), 19 deletions(-)
 create mode 100644 libfrog/quotactl.h
 rename quota/linux.c => libfrog/quotactl.c (97%)


diff --git a/libfrog/quotactl.h b/libfrog/quotactl.h
new file mode 100644
index 00000000000000..f76d02567408a0
--- /dev/null
+++ b/libfrog/quotactl.h
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2005 Silicon Graphics, Inc.
+ * All Rights Reserved.
+ */
+#ifndef LIBFROG_QUOTACTL_H_
+#define LIBFROG_QUOTACTL_H_
+
+#include "xqm.h"
+
+/*
+ * System call definitions mapping to platform-specific quotactl
+ */
+extern int xfsquotactl(int __cmd, const char *__device,
+			uint __type, uint __id, void * __addr);
+enum {
+	XFS_QUOTAON,	/* enable accounting/enforcement */
+	XFS_QUOTAOFF,	/* disable accounting/enforcement */
+	XFS_GETQUOTA,	/* get disk limits and usage */
+	XFS_SETQLIM,	/* set disk limits */
+	XFS_GETQSTAT,	/* get quota subsystem status */
+	XFS_QUOTARM,	/* free disk space used by dquots */
+	XFS_QSYNC,	/* flush delayed allocate space */
+	XFS_GETQSTATV,	/* newer version of quota stats */
+	XFS_GETNEXTQUOTA, /* get disk limits and usage */
+};
+
+#endif /* LIBFROG_QUOTACTL_H_ */
diff --git a/quota/quota.h b/quota/quota.h
index ece920ef6c1b46..82132e88c671c7 100644
--- a/quota/quota.h
+++ b/quota/quota.h
@@ -9,6 +9,7 @@
 #include "xqm.h"
 #include "libfrog/paths.h"
 #include "libfrog/projects.h"
+#include "libfrog/quotactl.h"
 #include <stdbool.h>
 
 /*
@@ -20,23 +21,6 @@ enum {
 	XFS_RTBLOCK_QUOTA =	0x4,
 };
 
-/*
- * System call definitions mapping to platform-specific quotactl
- */
-extern int xfsquotactl(int __cmd, const char *__device,
-			uint __type, uint __id, void * __addr);
-enum {
-	XFS_QUOTAON,	/* enable accounting/enforcement */
-	XFS_QUOTAOFF,	/* disable accounting/enforcement */
-	XFS_GETQUOTA,	/* get disk limits and usage */
-	XFS_SETQLIM,	/* set disk limits */
-	XFS_GETQSTAT,	/* get quota subsystem status */
-	XFS_QUOTARM,	/* free disk space used by dquots */
-	XFS_QSYNC,	/* flush delayed allocate space */
-	XFS_GETQSTATV,	/* newer version of quota stats */
-	XFS_GETNEXTQUOTA, /* get disk limits and usage */
-};
-
 /*
  * Utility routines
  */
diff --git a/libfrog/Makefile b/libfrog/Makefile
index 6ada83407e48c5..b0e8d1a10cd0c7 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -33,6 +33,7 @@ logging.c \
 paths.c \
 projects.c \
 ptvar.c \
+quotactl.c \
 radix-tree.c \
 randbytes.c \
 scrub.c \
@@ -67,6 +68,7 @@ logging.h \
 paths.h \
 projects.h \
 ptvar.h \
+quotactl.h \
 radix-tree.h \
 randbytes.h \
 scrub.h \
diff --git a/quota/linux.c b/libfrog/quotactl.c
similarity index 97%
rename from quota/linux.c
rename to libfrog/quotactl.c
index 63e0d34ce28c4e..ffef72b44eeadf 100644
--- a/quota/linux.c
+++ b/libfrog/quotactl.c
@@ -4,7 +4,7 @@
  * All Rights Reserved.
  */
 
-#include "quota.h"
+#include "libfrog/quotactl.h"
 #include <sys/quota.h>
 
 #ifndef PRJQUOTA
diff --git a/quota/Makefile b/quota/Makefile
index da5a1489e468b2..296cea44129e7f 100644
--- a/quota/Makefile
+++ b/quota/Makefile
@@ -8,7 +8,7 @@ include $(TOPDIR)/include/builddefs
 LTCOMMAND = xfs_quota
 HFILES = init.h quota.h
 CFILES = init.c util.c \
-	edit.c free.c linux.c path.c project.c quot.c quota.c report.c state.c
+	edit.c free.c path.c project.c quot.c quota.c report.c state.c
 
 LLDLIBS = $(LIBXCMD) $(LIBFROG)
 LTDEPENDENCIES = $(LIBXCMD) $(LIBFROG)


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 2/7] libfrog: clean up xfsquotactl a little bit
  2026-09-14  5:48 [PATCHSET 2/2] xfsprogs: use quotactl_fd when possible Darrick J. Wong
  2026-09-14  5:49 ` [PATCH 1/7] libfrog: hoist xfsquotactl out of xfs_quota Darrick J. Wong
@ 2026-09-14  5:50 ` Darrick J. Wong
  2026-09-15 12:25   ` Christoph Hellwig
  2026-09-14  5:50 ` [PATCH 3/7] libfrog: enhance struct fs_path to store optional mount fd Darrick J. Wong
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Darrick J. Wong @ 2026-09-14  5:50 UTC (permalink / raw)
  To: aalbersh, djwong; +Cc: linux-xfs

From: Darrick J. Wong <djwong@kernel.org>

Clean up this function so that we actually take advantage of enums for
the quota command, and get ready for adding quotactl_fd in the next
patch.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
 libfrog/quotactl.h |   10 ++++------
 libfrog/quotactl.c |   22 ++++++++++------------
 2 files changed, 14 insertions(+), 18 deletions(-)


diff --git a/libfrog/quotactl.h b/libfrog/quotactl.h
index f76d02567408a0..5e351d53bb4b1e 100644
--- a/libfrog/quotactl.h
+++ b/libfrog/quotactl.h
@@ -8,12 +8,7 @@
 
 #include "xqm.h"
 
-/*
- * System call definitions mapping to platform-specific quotactl
- */
-extern int xfsquotactl(int __cmd, const char *__device,
-			uint __type, uint __id, void * __addr);
-enum {
+enum xfs_quota_cmd {
 	XFS_QUOTAON,	/* enable accounting/enforcement */
 	XFS_QUOTAOFF,	/* disable accounting/enforcement */
 	XFS_GETQUOTA,	/* get disk limits and usage */
@@ -25,4 +20,7 @@ enum {
 	XFS_GETNEXTQUOTA, /* get disk limits and usage */
 };
 
+int xfsquotactl(enum xfs_quota_cmd xcommand, const char *device,
+		unsigned int xtype, unsigned int id, void *addr);
+
 #endif /* LIBFROG_QUOTACTL_H_ */
diff --git a/libfrog/quotactl.c b/libfrog/quotactl.c
index ffef72b44eeadf..81df2baf4a76b4 100644
--- a/libfrog/quotactl.c
+++ b/libfrog/quotactl.c
@@ -28,9 +28,9 @@ xtype_to_qtype(
 
 static int
 xcommand_to_qcommand(
-	uint		command)
+	enum xfs_quota_cmd	xcommand)
 {
-	switch (command) {
+	switch (xcommand) {
 	case XFS_QUOTAON:
 		return Q_XQUOTAON;
 	case XFS_QUOTAOFF:
@@ -55,16 +55,14 @@ xcommand_to_qcommand(
 
 int
 xfsquotactl(
-	int		command,
-	const char	*device,
-	uint		type,
-	uint		id,
-	void		*addr)
+	enum xfs_quota_cmd	xcommand,
+	const char		*device,
+	uint			xtype,
+	uint			id,
+	void			*addr)
 {
-	int		qcommand, qtype;
+	const int		op = QCMD(xcommand_to_qcommand(xcommand),
+					  xtype_to_qtype(xtype));
 
-	qtype = xtype_to_qtype(type);
-	qcommand = xcommand_to_qcommand(command);
-
-	return quotactl(QCMD(qcommand, qtype), device, id, addr);
+	return quotactl(op, device, id, addr);
 }


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 3/7] libfrog: enhance struct fs_path to store optional mount fd
  2026-09-14  5:48 [PATCHSET 2/2] xfsprogs: use quotactl_fd when possible Darrick J. Wong
  2026-09-14  5:49 ` [PATCH 1/7] libfrog: hoist xfsquotactl out of xfs_quota Darrick J. Wong
  2026-09-14  5:50 ` [PATCH 2/7] libfrog: clean up xfsquotactl a little bit Darrick J. Wong
@ 2026-09-14  5:50 ` Darrick J. Wong
  2026-09-15 12:25   ` Christoph Hellwig
  2026-09-14  5:50 ` [PATCH 4/7] libfrog: try to pass struct fs_path objects to quotactl wrapper Darrick J. Wong
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Darrick J. Wong @ 2026-09-14  5:50 UTC (permalink / raw)
  To: aalbersh, djwong; +Cc: linux-xfs

From: Darrick J. Wong <djwong@kernel.org>

The fs_path object stores information about various mounted XFS(?)
filesystems.  We'd like to be able to store an fd to an open file on
each mounted filesystem, so add a mnt_fd field to fs_path for that
purpose.  Users can store a fd in here if they want, and it'll get
cleaned up when the fs_table is destroyed.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
 libfrog/paths.h |    1 +
 libfrog/paths.c |    3 +++
 2 files changed, 4 insertions(+)


diff --git a/libfrog/paths.h b/libfrog/paths.h
index 306fd3cb8fde57..90689e7ab69fe8 100644
--- a/libfrog/paths.h
+++ b/libfrog/paths.h
@@ -29,6 +29,7 @@ typedef struct fs_path {
 	char		*fs_dir;	/* Directory / mount point	*/
 	uint		fs_flags;	/* FS_{MOUNT_POINT,PROJECT_PATH}*/
 	uint		fs_prid;	/* Project ID for tree root	*/
+	int		mnt_fd;		/* optional fd for mountpoint	*/
 } fs_path_t;
 
 extern int fs_count;		/* number of entries in fs table */
diff --git a/libfrog/paths.c b/libfrog/paths.c
index a5dfab48ec1e2a..d4cf65810c0029 100644
--- a/libfrog/paths.c
+++ b/libfrog/paths.c
@@ -206,6 +206,7 @@ fs_table_insert(
 	fs_path->fs_datadev = datadev;
 	fs_path->fs_logdev = logdev;
 	fs_path->fs_rtdev = rtdev;
+	fs_path->mnt_fd = -1;
 	fs_count++;
 	if (!(flags & FS_FOREIGN))
 		xfs_fs_count++;
@@ -232,6 +233,8 @@ fs_table_destroy(void)
 	struct fs_path	*fsp;
 
 	for (i = 0, fsp = fs_table; i < fs_count; i++, fsp++) {
+		if (fsp->mnt_fd >= 0)
+			close(fsp->mnt_fd);
 		free(fsp->fs_name);
 		free(fsp->fs_dir);
 		free(fsp->fs_log);


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 4/7] libfrog: try to pass struct fs_path objects to quotactl wrapper
  2026-09-14  5:48 [PATCHSET 2/2] xfsprogs: use quotactl_fd when possible Darrick J. Wong
                   ` (2 preceding siblings ...)
  2026-09-14  5:50 ` [PATCH 3/7] libfrog: enhance struct fs_path to store optional mount fd Darrick J. Wong
@ 2026-09-14  5:50 ` Darrick J. Wong
  2026-09-15 10:34   ` Andrey Albershteyn
  2026-09-14  5:50 ` [PATCH 5/7] libfrog: add quotactl_fd support to xfsquotactl Darrick J. Wong
                   ` (2 subsequent siblings)
  6 siblings, 1 reply; 18+ messages in thread
From: Darrick J. Wong @ 2026-09-14  5:50 UTC (permalink / raw)
  To: aalbersh, djwong; +Cc: linux-xfs

From: Darrick J. Wong <djwong@kernel.org>

We're going to adapt xfs_quota to use quotactl_fd(), so to prepare for
that, we need to pass fs_path pointers as deep into the stack as we can
so that xfs_quota can open the associated mountpoint(s) and stash the
fds in the fs_path objects; and then pass that to our quotactl wrapper.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
 libfrog/quotactl.h |    4 +++
 libfrog/quotactl.c |   12 ++++++++++
 quota/edit.c       |   29 ++++++++++++++-----------
 quota/free.c       |   11 +++++----
 quota/path.c       |    4 ++-
 quota/quota.c      |    6 +++--
 quota/report.c     |   61 ++++++++++++++++++++++++++--------------------------
 quota/state.c      |   38 +++++++++++++++++++-------------
 8 files changed, 97 insertions(+), 68 deletions(-)


diff --git a/libfrog/quotactl.h b/libfrog/quotactl.h
index 5e351d53bb4b1e..dae03b787b7663 100644
--- a/libfrog/quotactl.h
+++ b/libfrog/quotactl.h
@@ -23,4 +23,8 @@ enum xfs_quota_cmd {
 int xfsquotactl(enum xfs_quota_cmd xcommand, const char *device,
 		unsigned int xtype, unsigned int id, void *addr);
 
+struct fs_path;
+int xfrog_quotactl(const struct fs_path *mount, enum xfs_quota_cmd xcommand,
+		uint xtype, uint id, void *addr);
+
 #endif /* LIBFROG_QUOTACTL_H_ */
diff --git a/libfrog/quotactl.c b/libfrog/quotactl.c
index 81df2baf4a76b4..6129fa155ce75e 100644
--- a/libfrog/quotactl.c
+++ b/libfrog/quotactl.c
@@ -4,6 +4,7 @@
  * All Rights Reserved.
  */
 
+#include "libfrog/paths.h"
 #include "libfrog/quotactl.h"
 #include <sys/quota.h>
 
@@ -66,3 +67,14 @@ xfsquotactl(
 
 	return quotactl(op, device, id, addr);
 }
+
+int
+xfrog_quotactl(
+	const struct fs_path	*mount,
+	enum xfs_quota_cmd	xcommand,
+	uint			xtype,
+	uint			id,
+	void			*addr)
+{
+	return xfsquotactl(xcommand, mount->fs_name, xtype, id, addr);
+}
diff --git a/quota/edit.c b/quota/edit.c
index 1a3b2d9f959be8..d28a4319024038 100644
--- a/quota/edit.c
+++ b/quota/edit.c
@@ -469,11 +469,12 @@ set_timer(
 	uint32_t		id,
 	uint			type,
 	uint			mask,
-	char			*dev,
+	const struct fs_path	*mount,
 	time64_t		value)
 {
 	struct fs_disk_quota	d;
 	time64_t		btimer, itimer, rtbtimer;
+	int			ret;
 
 	memset(&d, 0, sizeof(d));
 
@@ -485,7 +486,8 @@ set_timer(
 		time_t	now;
 
 		/* Get quota to find out whether user is past soft limits */
-		if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0) {
+		ret = xfrog_quotactl(mount, XFS_GETQUOTA, type, id, &d);
+		if (ret < 0) {
 			exitcode = 1;
 			fprintf(stderr, _("%s: cannot get quota: %s\n"),
 					progname, strerror(errno));
@@ -517,7 +519,8 @@ set_timer(
 	d.d_id = id;
 	encode_timers(&d, btimer, itimer, rtbtimer);
 
-	if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
+	ret = xfrog_quotactl(mount, XFS_SETQLIM, type, id, &d);
+	if (ret < 0) {
 		exitcode = 1;
 		fprintf(stderr, _("%s: cannot set timer: %s\n"),
 				progname, strerror(errno));
@@ -601,19 +604,20 @@ timer_f(
 	if (id == -1)
 		return 0;
 
-	set_timer(id, type, mask, fs_path->fs_name, value);
+	set_timer(id, type, mask, fs_path, value);
 	return 0;
 }
 
 static void
 set_warnings(
-	uint32_t	id,
-	uint		type,
-	uint		mask,
-	char		*dev,
-	uint		value)
+	uint32_t		id,
+	uint			type,
+	uint			mask,
+	const struct fs_path	*mount,
+	uint			value)
 {
-	fs_disk_quota_t	d;
+	struct fs_disk_quota	d;
+	int			ret;
 
 	memset(&d, 0, sizeof(d));
 	d.d_version = FS_DQUOT_VERSION;
@@ -624,7 +628,8 @@ set_warnings(
 	d.d_bwarns = value;
 	d.d_rtbwarns = value;
 
-	if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
+	ret = xfrog_quotactl(mount, XFS_SETQLIM, type, id, &d);
+	if (ret < 0) {
 		exitcode = 1;
 		fprintf(stderr, _("%s: cannot set warnings: %s\n"),
 				progname, strerror(errno));
@@ -699,7 +704,7 @@ warn_f(
 	if (id == -1)
 		return 0;
 
-	set_warnings(id, type, mask, fs_path->fs_name, value);
+	set_warnings(id, type, mask, fs_path, value);
 	return 0;
 }
 
diff --git a/quota/free.c b/quota/free.c
index ea9c112f314a12..5793885c44fd1e 100644
--- a/quota/free.c
+++ b/quota/free.c
@@ -136,11 +136,11 @@ projects_free_space_data(
 	fs_disk_quota_t		d;
 	struct fsxattr		fsx;
 	uint			type = XFS_PROJ_QUOTA;
-	char			*dev = path->fs_name;
 	int			fd;
+	int			ret;
 
-	if (xfsquotactl(XFS_GETQSTAT, dev, type, 0, &qfs) < 0 ||
-			!(qfs.qs_flags & XFS_QUOTA_PDQ_ACCT))
+	ret = xfrog_quotactl(path, XFS_GETQSTAT, type, 0, &qfs);
+	if (ret < 0 || !(qfs.qs_flags & XFS_QUOTA_PDQ_ACCT))
 		return 0;
 
 	if ((fd = open(path->fs_dir, O_RDONLY)) < 0) {
@@ -174,8 +174,9 @@ projects_free_space_data(
 		return 0;
 	}
 
-	xfsquotactl(XFS_QSYNC, dev, type, fsx.fsx_projid, NULL);
-	if (xfsquotactl(XFS_GETQUOTA, dev, type, fsx.fsx_projid, &d) < 0) {
+	xfrog_quotactl(path, XFS_QSYNC, type, fsx.fsx_projid, NULL);
+	ret = xfrog_quotactl(path, XFS_GETQUOTA, type, fsx.fsx_projid, &d);
+	if (ret < 0) {
 		perror("XFS_GETQUOTA");
 		close(fd);
 		return 0;
diff --git a/quota/path.c b/quota/path.c
index 16b8bec4d883c0..4654f1553c65e0 100644
--- a/quota/path.c
+++ b/quota/path.c
@@ -39,8 +39,8 @@ printpath(
 		if (prj)
 			printf(_(", %s"), prj->pr_name);
 		printf(")");
-	} else if (xfsquotactl(XFS_GETQSTAT, path->fs_name, 0, 0,
-			       (void *)&qstat) == 0 && qstat.qs_flags) {
+	} else if (xfrog_quotactl(path, XFS_GETQSTAT, 0, 0, &qstat) == 0 &&
+			qstat.qs_flags) {
 		c = 0;
 		printf(" (");
 		if (qstat.qs_flags & XFS_QUOTA_UDQ_ENFD)
diff --git a/quota/quota.c b/quota/quota.c
index 08a07b2088817b..e1dfad62b9d762 100644
--- a/quota/quota.c
+++ b/quota/quota.c
@@ -53,9 +53,11 @@ quota_mount(
 	char		c[8], h[8], s[8];
 	uint		qflags;
 	int		count;
+	int		ret;
 
-	xfsquotactl(XFS_QSYNC, dev, type, 0, NULL);
-	if (xfsquotactl(XFS_GETQUOTA, dev, type, id, (void *)&d) < 0)
+	xfrog_quotactl(mount, XFS_QSYNC, type, id, NULL);
+	ret = xfrog_quotactl(mount, XFS_GETQUOTA, type, id, &d);
+	if (ret < 0)
 		return 0;
 
 	dquot_fudge_numbers(&d);
diff --git a/quota/report.c b/quota/report.c
index 2acf81b81ba612..86eaa50f84c20b 100644
--- a/quota/report.c
+++ b/quota/report.c
@@ -61,13 +61,14 @@ report_help(void)
 
 static int
 get_dquot(
-	struct fs_disk_quota *d,
-	uint		id,
-	uint		type,
-	char		*dev,
-	int		flags)
+	struct fs_disk_quota	*d,
+	uint			id,
+	uint			type,
+	const struct fs_path	*mount,
+	int			flags)
 {
-	int		cmd;
+	int			cmd;
+	int			ret;
 
 	if (flags & GETNEXTQUOTA_FLAG)
 		cmd = XFS_GETNEXTQUOTA;
@@ -75,7 +76,8 @@ get_dquot(
 		cmd = XFS_GETQUOTA;
 
 	/* Fall back silently if XFS_GETNEXTQUOTA fails, warn on XFS_GETQUOTA */
-	if (xfsquotactl(cmd, dev, type, id, (void *)d) < 0) {
+	ret = xfrog_quotactl(mount, cmd, type, id, d);
+	if (ret < 0) {
 		if (errno != ENOENT && errno != ENOSYS && errno != ESRCH &&
 		    cmd == XFS_GETQUOTA)
 			perror("XFS_GETQUOTA");
@@ -140,8 +142,7 @@ dump_limits_any_type(
 		return;
 	}
 
-	while (get_dquot(&d, id, type, mount->fs_name,
-				flags | GETNEXTQUOTA_FLAG) &&
+	while (get_dquot(&d, id, type, mount, flags | GETNEXTQUOTA_FLAG) &&
 			!(upper && (d.d_id > upper))) {
 		dump_file(fp, &d, mount->fs_name);
 		id = d.d_id + 1;
@@ -157,8 +158,7 @@ dump_limits_any_type(
 			struct group *g;
 			setgrent();
 			while ((g = getgrent()) != NULL) {
-				if (get_dquot(&d, g->gr_gid, type,
-							mount->fs_name, 0) &&
+				if (get_dquot(&d, g->gr_gid, type, mount, 0) &&
 						!(lower && (d.d_id < lower)) &&
 						!(upper && (d.d_id > upper)))
 					dump_file(fp, &d, mount->fs_name);
@@ -170,8 +170,7 @@ dump_limits_any_type(
 			struct fs_project *p;
 			setprent();
 			while ((p = getprent()) != NULL) {
-				if (get_dquot(&d, p->pr_prid, type,
-							mount->fs_name, 0) &&
+				if (get_dquot(&d, p->pr_prid, type, mount, 0) &&
 						!(lower && (d.d_id < lower)) &&
 						!(upper && (d.d_id > upper)))
 					dump_file(fp, &d, mount->fs_name);
@@ -183,8 +182,7 @@ dump_limits_any_type(
 			struct passwd *u;
 			setpwent();
 			while ((u = getpwent()) != NULL) {
-				if (get_dquot(&d, u->pw_uid, type,
-							mount->fs_name, 0) &&
+				if (get_dquot(&d, u->pw_uid, type, mount, 0) &&
 						!(lower && (d.d_id < lower)) &&
 						!(upper && (d.d_id > upper)))
 					dump_file(fp, &d, mount->fs_name);
@@ -466,7 +464,7 @@ report_user_mount(
 	struct fs_disk_quota	d;
 	uint		id = lower;
 
-	while (get_dquot(&d, id, XFS_USER_QUOTA, mount->fs_name,
+	while (get_dquot(&d, id, XFS_USER_QUOTA, mount,
 				flags | GETNEXTQUOTA_FLAG) &&
 			!(upper && (d.d_id > upper))) {
 		report_mount(fp, &d, NULL, form, XFS_USER_QUOTA, mount, flags);
@@ -480,7 +478,7 @@ report_user_mount(
 		setpwent();
 		while ((u = getpwent()) != NULL) {
 			if (get_dquot(&d, u->pw_uid, XFS_USER_QUOTA,
-						mount->fs_name, flags) &&
+						mount, flags) &&
 					!(lower && (d.d_id < lower)) &&
 					!(upper && (d.d_id > upper))) {
 				report_mount(fp, &d, u->pw_name, form,
@@ -508,7 +506,7 @@ report_group_mount(
 	struct fs_disk_quota	d;
 	uint		id = lower;
 
-	while (get_dquot(&d, id, XFS_GROUP_QUOTA, mount->fs_name,
+	while (get_dquot(&d, id, XFS_GROUP_QUOTA, mount,
 				flags | GETNEXTQUOTA_FLAG) &&
 			!(upper && (d.d_id > upper))) {
 		report_mount(fp, &d, NULL, form, XFS_GROUP_QUOTA, mount, flags);
@@ -522,7 +520,7 @@ report_group_mount(
 		setgrent();
 		while ((g = getgrent()) != NULL) {
 			if (get_dquot(&d, g->gr_gid, XFS_GROUP_QUOTA,
-						mount->fs_name, flags) &&
+						mount, flags) &&
 					!(lower && (d.d_id < lower)) &&
 					!(upper && (d.d_id > upper))) {
 				report_mount(fp, &d, g->gr_name, form,
@@ -549,7 +547,7 @@ report_project_mount(
 	struct fs_disk_quota	d;
 	uint		id = lower;
 
-	while (get_dquot(&d, id, XFS_PROJ_QUOTA, mount->fs_name,
+	while (get_dquot(&d, id, XFS_PROJ_QUOTA, mount,
 				flags | GETNEXTQUOTA_FLAG) &&
 			!(upper && (d.d_id > upper))) {
 		report_mount(fp, &d, NULL, form, XFS_PROJ_QUOTA, mount, flags);
@@ -565,7 +563,7 @@ report_project_mount(
 			 * Print default project quota, even if projid 0
 			 * isn't defined
 			 */
-			if (get_dquot(&d, 0, XFS_PROJ_QUOTA, mount->fs_name,
+			if (get_dquot(&d, 0, XFS_PROJ_QUOTA, mount,
 						flags) &&
 					!(lower && (d.d_id < lower)) &&
 					!(upper && (d.d_id > upper))) {
@@ -578,7 +576,7 @@ report_project_mount(
 		setprent();
 		while ((p = getprent()) != NULL) {
 			if (get_dquot(&d, p->pr_prid, XFS_PROJ_QUOTA,
-						mount->fs_name, flags) &&
+						mount, flags) &&
 					!(lower && (d.d_id < lower)) &&
 					!(upper && (d.d_id > upper))) {
 				report_mount(fp, &d, p->pr_name, form,
@@ -605,15 +603,16 @@ report_any_type(
 {
 	fs_cursor_t	cursor;
 	fs_path_t	*mount;
+	int		ret;
 
 	if (type & XFS_USER_QUOTA) {
 		fs_cursor_initialise(dir, FS_MOUNT_POINT, &cursor);
 		while ((mount = fs_cursor_next_entry(&cursor))) {
 			if (!foreign_allowed && (mount->fs_flags & FS_FOREIGN))
 				continue;
-			if (xfsquotactl(XFS_QSYNC, mount->fs_name,
-						XFS_USER_QUOTA, 0, NULL) < 0
-					&& errno != ENOENT && errno != ENOSYS)
+			ret = xfrog_quotactl(mount, XFS_QSYNC, XFS_USER_QUOTA,
+					0, NULL);
+			if (ret < 0 && errno != ENOENT && errno != ENOSYS)
 				perror("XFS_QSYNC user quota");
 			report_user_mount(fp, form, mount,
 						lower, upper, flags);
@@ -624,9 +623,9 @@ report_any_type(
 		while ((mount = fs_cursor_next_entry(&cursor))) {
 			if (!foreign_allowed && (mount->fs_flags & FS_FOREIGN))
 				continue;
-			if (xfsquotactl(XFS_QSYNC, mount->fs_name,
-						XFS_GROUP_QUOTA, 0, NULL) < 0
-					&& errno != ENOENT && errno != ENOSYS)
+			ret = xfrog_quotactl(mount, XFS_QSYNC, XFS_GROUP_QUOTA,
+					0, NULL);
+			if (ret < 0 && errno != ENOENT && errno != ENOSYS)
 				perror("XFS_QSYNC group quota");
 			report_group_mount(fp, form, mount,
 						lower, upper, flags);
@@ -637,9 +636,9 @@ report_any_type(
 		while ((mount = fs_cursor_next_entry(&cursor))) {
 			if (!foreign_allowed && (mount->fs_flags & FS_FOREIGN))
 				continue;
-			if (xfsquotactl(XFS_QSYNC, mount->fs_name,
-						XFS_PROJ_QUOTA, 0, NULL) < 0
-					&& errno != ENOENT && errno != ENOSYS)
+			ret = xfrog_quotactl(mount, XFS_QSYNC, XFS_PROJ_QUOTA,
+					0, NULL);
+			if (ret < 0 && errno != ENOENT && errno != ENOSYS)
 				perror("XFS_QSYNC proj quota");
 			report_project_mount(fp, form, mount,
 						lower, upper, flags);
diff --git a/quota/state.c b/quota/state.c
index 43fb700f9a7317..3679c247ea6204 100644
--- a/quota/state.c
+++ b/quota/state.c
@@ -202,9 +202,12 @@ state_quotafile_stat(
 	bool			accounting, enforcing;
 	struct fs_qfilestatv	*qsv;
 	char			*dev = mount->fs_name;
+	int			ret;
 
-	if (xfsquotactl(XFS_GETQSTATV, dev, type, 0, (void *)sv) < 0) {
-		if (xfsquotactl(XFS_GETQSTAT, dev, type, 0, (void *)s) < 0) {
+	ret = xfrog_quotactl(mount, XFS_GETQSTATV, type, 0, sv);
+	if (ret < 0) {
+		ret = xfrog_quotactl(mount, XFS_GETQSTAT, type, 0, sv);
+		if (ret < 0) {
 			if (flags & VERBOSE_FLAG)
 				fprintf(fp,
 					_("%s quota are not enabled on %s\n"),
@@ -351,6 +354,7 @@ enable_enforcement(
 	uint		flags)
 {
 	fs_path_t	*mount;
+	int		ret;
 
 	mount = fs_table_lookup(dir, FS_MOUNT_POINT);
 	if (!mount) {
@@ -358,8 +362,8 @@ enable_enforcement(
 		fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
 		return;
 	}
-	dir = mount->fs_name;
-	if (xfsquotactl(XFS_QUOTAON, dir, type, 0, (void *)&qflags) < 0) {
+	ret = xfrog_quotactl(mount, XFS_QUOTAON, type, 0, &qflags);
+	if (ret < 0) {
 		if (errno == EEXIST)
 			fprintf(stderr,
 				_("Quota enforcement already enabled.\n"));
@@ -381,6 +385,7 @@ disable_enforcement(
 	uint		flags)
 {
 	fs_path_t	*mount;
+	int		ret;
 
 	mount = fs_table_lookup(dir, FS_MOUNT_POINT);
 	if (!mount) {
@@ -388,8 +393,8 @@ disable_enforcement(
 		fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
 		return;
 	}
-	dir = mount->fs_name;
-	if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qflags) < 0) {
+	ret = xfrog_quotactl(mount, XFS_QUOTAOFF, type, 0, &qflags);
+	if (ret < 0) {
 		if (errno == EEXIST)
 			fprintf(stderr,
 				_("Quota enforcement already disabled.\n"));
@@ -411,6 +416,7 @@ quotaoff(
 	uint		flags)
 {
 	fs_path_t	*mount;
+	int		ret;
 
 	mount = fs_table_lookup(dir, FS_MOUNT_POINT);
 	if (!mount) {
@@ -418,8 +424,8 @@ quotaoff(
 		fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
 		return;
 	}
-	dir = mount->fs_name;
-	if (xfsquotactl(XFS_QUOTAOFF, dir, type, 0, (void *)&qflags) < 0) {
+	ret = xfrog_quotactl(mount, XFS_QUOTAOFF, type, 0, &qflags);
+	if (ret) {
 		if (errno == EEXIST || errno == ENOSYS)
 			fprintf(stderr, _("Quota already off.\n"));
 		else
@@ -431,12 +437,13 @@ quotaoff(
 
 static int
 remove_qtype_extents(
-	char		*dir,
-	uint		type)
+	const struct fs_path	*mount,
+	uint			type)
 {
-	int	error = 0;
+	int			error;
 
-	if ((error = xfsquotactl(XFS_QUOTARM, dir, type, 0, (void *)&type)) < 0)
+	error = xfrog_quotactl(mount, XFS_QUOTARM, type, 0, &type);
+	if (error < 0)
 		perror("XFS_QUOTARM");
 	return error;
 }
@@ -455,17 +462,16 @@ remove_extents(
 		fprintf(stderr, "%s: unknown mount point %s\n", progname, dir);
 		return;
 	}
-	dir = mount->fs_name;
 	if (type & XFS_USER_QUOTA) {
-		if (remove_qtype_extents(dir, XFS_USER_QUOTA) < 0)
+		if (remove_qtype_extents(mount, XFS_USER_QUOTA) < 0)
 			return;
 	}
 	if (type & XFS_GROUP_QUOTA) {
-		if (remove_qtype_extents(dir, XFS_GROUP_QUOTA) < 0)
+		if (remove_qtype_extents(mount, XFS_GROUP_QUOTA) < 0)
 			return;
 	}
 	if (type & XFS_PROJ_QUOTA) {
-		if (remove_qtype_extents(dir, XFS_PROJ_QUOTA) < 0)
+		if (remove_qtype_extents(mount, XFS_PROJ_QUOTA) < 0)
 			return;
 	}
 	if (flags & VERBOSE_FLAG)


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 5/7] libfrog: add quotactl_fd support to xfsquotactl
  2026-09-14  5:48 [PATCHSET 2/2] xfsprogs: use quotactl_fd when possible Darrick J. Wong
                   ` (3 preceding siblings ...)
  2026-09-14  5:50 ` [PATCH 4/7] libfrog: try to pass struct fs_path objects to quotactl wrapper Darrick J. Wong
@ 2026-09-14  5:50 ` Darrick J. Wong
  2026-09-15 12:27   ` Christoph Hellwig
  2026-09-14  5:51 ` [PATCH 6/7] xfs_quota: open the filesystem mountpoint for quota operations Darrick J. Wong
  2026-09-14  5:51 ` [PATCH 7/7] xfs_spaceman: port makecfg to use xfsquotactl Darrick J. Wong
  6 siblings, 1 reply; 18+ messages in thread
From: Darrick J. Wong @ 2026-09-14  5:50 UTC (permalink / raw)
  To: aalbersh, djwong; +Cc: linux-xfs

From: Darrick J. Wong <djwong@kernel.org>

Add support for the quotactl_fd syscall to the xfsquotactl call in
libfrog.  I rearranged the function arguments so that the fs connection
point (either an open fd or the fs source) come first.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
 libfrog/quotactl.h    |    2 +-
 configure.ac          |    1 +
 include/builddefs.in  |    1 +
 libfrog/Makefile      |    4 ++++
 libfrog/quotactl.c    |   15 +++++++++++++--
 m4/package_libcdev.m4 |   19 +++++++++++++++++++
 quota/edit.c          |    7 ++++---
 7 files changed, 43 insertions(+), 6 deletions(-)


diff --git a/libfrog/quotactl.h b/libfrog/quotactl.h
index dae03b787b7663..9df9327402726d 100644
--- a/libfrog/quotactl.h
+++ b/libfrog/quotactl.h
@@ -20,7 +20,7 @@ enum xfs_quota_cmd {
 	XFS_GETNEXTQUOTA, /* get disk limits and usage */
 };
 
-int xfsquotactl(enum xfs_quota_cmd xcommand, const char *device,
+int xfsquotactl(int mnt_fd, const char *device, enum xfs_quota_cmd xcommand,
 		unsigned int xtype, unsigned int id, void *addr);
 
 struct fs_path;
diff --git a/configure.ac b/configure.ac
index 40ccd88aa2a950..8a2505e37c70ab 100644
--- a/configure.ac
+++ b/configure.ac
@@ -195,6 +195,7 @@ if test "$have_listmount" = "yes"; then
 fi
 AC_HAVE_STATMOUNT_SUPPORTED_MASK
 AC_HAVE_FANOTIFY_MOUNTINFO
+AC_HAVE_QUOTACTL_FD
 
 if test "$enable_ubsan" = "yes" || test "$enable_ubsan" = "probe"; then
         AC_PACKAGE_CHECK_UBSAN
diff --git a/include/builddefs.in b/include/builddefs.in
index 3b52d1afd7031c..5d2e6c1b90ddbe 100644
--- a/include/builddefs.in
+++ b/include/builddefs.in
@@ -126,6 +126,7 @@ HAVE_LISTMOUNT_NS_FD = @have_listmount_ns_fd@
 HAVE_STATMOUNT_SUPPORTED_MASK = @have_statmount_supported_mask@
 NEED_INTERNAL_STATMOUNT = @need_internal_statmount@
 HAVE_FANOTIFY_MOUNTINFO = @have_fanotify_mountinfo@
+HAVE_QUOTACTL_FD = @have_quotactl_fd@
 
 GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall
 #	   -Wbitwise -Wno-transparent-union -Wno-old-initializer -Wno-decl
diff --git a/libfrog/Makefile b/libfrog/Makefile
index b0e8d1a10cd0c7..c7bcd6a778d702 100644
--- a/libfrog/Makefile
+++ b/libfrog/Makefile
@@ -108,6 +108,10 @@ ifeq ($(HAVE_LISTMOUNT_NS_FD),yes)
 CFLAGS+=-DHAVE_LISTMOUNT_NS_FD
 endif
 
+ifeq ($(HAVE_QUOTACTL_FD),yes)
+CFLAGS += -DHAVE_QUOTACTL_FD
+endif
+
 default: ltdepend $(LTLIBRARY) $(GETTEXT_PY)
 
 crc32table.h: gen_crc32table.c crc32defs.h
diff --git a/libfrog/quotactl.c b/libfrog/quotactl.c
index 6129fa155ce75e..19b5405be47946 100644
--- a/libfrog/quotactl.c
+++ b/libfrog/quotactl.c
@@ -56,14 +56,24 @@ xcommand_to_qcommand(
 
 int
 xfsquotactl(
-	enum xfs_quota_cmd	xcommand,
+	int			mnt_fd,
 	const char		*device,
+	enum xfs_quota_cmd	xcommand,
 	uint			xtype,
 	uint			id,
 	void			*addr)
 {
 	const int		op = QCMD(xcommand_to_qcommand(xcommand),
 					  xtype_to_qtype(xtype));
+	int			ret = -1;
+
+	errno = ENOSYS;
+#ifdef HAVE_QUOTACTL_FD
+	if (mnt_fd >= 0)
+		ret = syscall(SYS_quotactl_fd, mnt_fd, op, id, addr);
+#endif
+	if (ret != -1 || errno != ENOSYS)
+		return ret;
 
 	return quotactl(op, device, id, addr);
 }
@@ -76,5 +86,6 @@ xfrog_quotactl(
 	uint			id,
 	void			*addr)
 {
-	return xfsquotactl(xcommand, mount->fs_name, xtype, id, addr);
+	return xfsquotactl(mount->mnt_fd, mount->fs_name, xcommand, xtype, id,
+			addr);
 }
diff --git a/m4/package_libcdev.m4 b/m4/package_libcdev.m4
index 9586bc01fe0f25..5c1b6e4667d0ee 100644
--- a/m4/package_libcdev.m4
+++ b/m4/package_libcdev.m4
@@ -476,3 +476,22 @@ AC_DEFUN([AC_HAVE_FANOTIFY_MOUNTINFO],
        AC_MSG_RESULT(no))
     AC_SUBST(have_fanotify_mountinfo)
   ])
+
+#
+# Check if we have a quotactl_fd system call (5.14).
+#
+AC_DEFUN([AC_HAVE_QUOTACTL_FD],
+  [AC_MSG_CHECKING([for quotactl_fd])
+    AC_LINK_IFELSE(
+    [AC_LANG_PROGRAM([[
+#define _GNU_SOURCE
+#include <sys/syscall.h>
+#include <unistd.h>
+  ]], [[
+	return syscall(SYS_quotactl_fd);
+  ]])
+    ], have_quotactl_fd=yes
+       AC_MSG_RESULT(yes),
+       AC_MSG_RESULT(no))
+    AC_SUBST(have_quotactl_fd)
+  ])
diff --git a/quota/edit.c b/quota/edit.c
index d28a4319024038..cf4f8cdaeead63 100644
--- a/quota/edit.c
+++ b/quota/edit.c
@@ -140,6 +140,7 @@ set_limits(
 	uint32_t	id,
 	uint		type,
 	uint		mask,
+	int		mnt_fd,
 	char		*dev,
 	uint64_t	*bsoft,
 	uint64_t	*bhard,
@@ -162,7 +163,7 @@ set_limits(
 	d.d_rtb_hardlimit = *rtbhard;
 	d.d_rtb_softlimit = *rtbsoft;
 
-	if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
+	if (xfsquotactl(mnt_fd, dev, XFS_SETQLIM, type, id, (void *)&d) < 0) {
 		exitcode = 1;
 		fprintf(stderr, _("%s: cannot set limits: %s\n"),
 				progname, strerror(errno));
@@ -310,7 +311,7 @@ limit_f(
 	if (id == -1)
 		return 0;
 
-	set_limits(id, type, mask, fs_path->fs_name,
+	set_limits(id, type, mask, fs_path->mnt_fd, fs_path->fs_name,
 		   &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
 	return 0;
 }
@@ -356,7 +357,7 @@ restore_file(
 			mask = FS_DQ_ISOFT|FS_DQ_IHARD|FS_DQ_BSOFT|FS_DQ_BHARD;
 			if (cnt == 7)
 				mask |= FS_DQ_RTBSOFT|FS_DQ_RTBHARD;
-			set_limits(id, type, mask, dev, &bsoft, &bhard,
+			set_limits(id, type, mask, -1, dev, &bsoft, &bhard,
 					&isoft, &ihard, &rtbsoft, &rtbhard);
 		}
 	}


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 6/7] xfs_quota: open the filesystem mountpoint for quota operations
  2026-09-14  5:48 [PATCHSET 2/2] xfsprogs: use quotactl_fd when possible Darrick J. Wong
                   ` (4 preceding siblings ...)
  2026-09-14  5:50 ` [PATCH 5/7] libfrog: add quotactl_fd support to xfsquotactl Darrick J. Wong
@ 2026-09-14  5:51 ` Darrick J. Wong
  2026-09-15 12:27   ` Christoph Hellwig
  2026-09-14  5:51 ` [PATCH 7/7] xfs_spaceman: port makecfg to use xfsquotactl Darrick J. Wong
  6 siblings, 1 reply; 18+ messages in thread
From: Darrick J. Wong @ 2026-09-14  5:51 UTC (permalink / raw)
  To: aalbersh, djwong; +Cc: linux-xfs

From: Darrick J. Wong <djwong@kernel.org>

Change xfs_quota's behavior so that it tries to open the filesystem
mountpoint when it's starting up.  This enables it to use the
quotactl_fd system call instead of the kernel having to do a path lookup
of the data device.  Doing so adds robustness against the data device
getting renamed (e.g. lvm rename) while xfs_quota is running.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
 quota/init.h    |    2 ++
 quota/init.c    |   15 +++++++++++++++
 quota/project.c |    7 +++++++
 3 files changed, 24 insertions(+)


diff --git a/quota/init.h b/quota/init.h
index 9191d822e55819..e85cfa45580cb2 100644
--- a/quota/init.h
+++ b/quota/init.h
@@ -19,3 +19,5 @@ extern void	report_init(void);
 extern void	state_init(void);
 
 extern void init_cvtnum(unsigned int *, unsigned int *);
+
+void open_mnt_fds(void);
diff --git a/quota/init.c b/quota/init.c
index 9425827591c798..e7fae917d641b7 100644
--- a/quota/init.c
+++ b/quota/init.c
@@ -128,6 +128,19 @@ init_check_command(
 	return 0;
 }
 
+void
+open_mnt_fds(void)
+{
+	int		c;
+
+	for (c = 0; c < fs_count; c++) {
+		if (fs_table[c].mnt_fd < 0) {
+			/* if this fails, we fall back to path-based quotactl */
+			fs_table[c].mnt_fd = open(fs_table[c].fs_dir, O_RDONLY);
+		}
+	}
+}
+
 static void
 init(
 	int		argc,
@@ -189,6 +202,8 @@ init(
 		fs_path = fs_table_lookup(argv[optind], FS_MOUNT_POINT);
 	else
 		fs_path = &fs_table[0];
+
+	open_mnt_fds();
 }
 
 int
diff --git a/quota/project.c b/quota/project.c
index 33449e01ef4dbb..9b057df75b6aed 100644
--- a/quota/project.c
+++ b/quota/project.c
@@ -296,6 +296,13 @@ _("%s: cannot setup path for project dir %s: %s\n"),
 					progname, optarg, strerror(error));
 				return 0;
 			}
+
+			/*
+			 * fs_table could have been arbitrarily rearranged by
+			 * fs_table_insert, so we need to try to open any new
+			 * mounts.
+			 */
+			open_mnt_fds();
 			break;
 		case 's':
 			type = SETUP_PROJECT;


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* [PATCH 7/7] xfs_spaceman: port makecfg to use xfsquotactl
  2026-09-14  5:48 [PATCHSET 2/2] xfsprogs: use quotactl_fd when possible Darrick J. Wong
                   ` (5 preceding siblings ...)
  2026-09-14  5:51 ` [PATCH 6/7] xfs_quota: open the filesystem mountpoint for quota operations Darrick J. Wong
@ 2026-09-14  5:51 ` Darrick J. Wong
  2026-09-15 12:28   ` Christoph Hellwig
  6 siblings, 1 reply; 18+ messages in thread
From: Darrick J. Wong @ 2026-09-14  5:51 UTC (permalink / raw)
  To: aalbersh, djwong; +Cc: linux-xfs

From: Darrick J. Wong <djwong@kernel.org>

Port this code to use our xfsquotactl function, which means that we can
call quotactl_fd/quotactl in the same manner as xfs_quota does.

Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
 spaceman/info.c |    7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)


diff --git a/spaceman/info.c b/spaceman/info.c
index bacc48e8aa796f..56c7a05c3f81ec 100644
--- a/spaceman/info.c
+++ b/spaceman/info.c
@@ -3,7 +3,6 @@
  * Copyright (C) 2018 Oracle.  All Rights Reserved.
  * Author: Darrick J. Wong <darrick.wong@oracle.com>
  */
-#include <sys/quota.h>
 #include "libxfs.h"
 #include "command.h"
 #include "init.h"
@@ -11,8 +10,8 @@
 #include "libfrog/fsgeom.h"
 #include "libfrog/fsproperties.h"
 #include "libfrog/fsprops.h"
+#include "libfrog/quotactl.h"
 #include "space.h"
-#include "include/xqm.h"
 
 static void
 info_help(void)
@@ -126,8 +125,8 @@ get_qflags(
 	*qflags = 0;
 
 	/* GETQSTAT returns qflags for all quota types, not just user */
-	ret = quotactl(QCMD(Q_XGETQSTAT, USRQUOTA), f->fs_path.fs_name, 0,
-			(void *)&qstat);
+	ret = xfsquotactl(f->xfd.fd, f->fs_path.fs_name, XFS_GETQSTAT,
+			XFS_USER_QUOTA, 0, (void *)&qstat);
 	if (ret) {
 		/*
 		 * ENOSYS means quota is not enabled or compiled in; ENODEV


^ permalink raw reply related	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/7] libfrog: try to pass struct fs_path objects to quotactl wrapper
  2026-09-14  5:50 ` [PATCH 4/7] libfrog: try to pass struct fs_path objects to quotactl wrapper Darrick J. Wong
@ 2026-09-15 10:34   ` Andrey Albershteyn
  2026-09-15 12:26     ` Christoph Hellwig
  0 siblings, 1 reply; 18+ messages in thread
From: Andrey Albershteyn @ 2026-09-15 10:34 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: linux-xfs

On 2026-09-13 22:50:37, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
> 
> We're going to adapt xfs_quota to use quotactl_fd(), so to prepare for
> that, we need to pass fs_path pointers as deep into the stack as we can
> so that xfs_quota can open the associated mountpoint(s) and stash the
> fds in the fs_path objects; and then pass that to our quotactl wrapper.
> 
> Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
> ---
>  libfrog/quotactl.h |    4 +++
>  libfrog/quotactl.c |   12 ++++++++++
>  quota/edit.c       |   29 ++++++++++++++-----------
>  quota/free.c       |   11 +++++----
>  quota/path.c       |    4 ++-
>  quota/quota.c      |    6 +++--
>  quota/report.c     |   61 ++++++++++++++++++++++++++--------------------------
>  quota/state.c      |   38 +++++++++++++++++++-------------
>  8 files changed, 97 insertions(+), 68 deletions(-)
> 
> 
> diff --git a/libfrog/quotactl.h b/libfrog/quotactl.h
> index 5e351d53bb4b1e..dae03b787b7663 100644
> --- a/libfrog/quotactl.h
> +++ b/libfrog/quotactl.h
> @@ -23,4 +23,8 @@ enum xfs_quota_cmd {
>  int xfsquotactl(enum xfs_quota_cmd xcommand, const char *device,
>  		unsigned int xtype, unsigned int id, void *addr);
>  
> +struct fs_path;
> +int xfrog_quotactl(const struct fs_path *mount, enum xfs_quota_cmd xcommand,
> +		uint xtype, uint id, void *addr);
> +
>  #endif /* LIBFROG_QUOTACTL_H_ */
> diff --git a/libfrog/quotactl.c b/libfrog/quotactl.c
> index 81df2baf4a76b4..6129fa155ce75e 100644
> --- a/libfrog/quotactl.c
> +++ b/libfrog/quotactl.c
> @@ -4,6 +4,7 @@
>   * All Rights Reserved.
>   */
>  
> +#include "libfrog/paths.h"
>  #include "libfrog/quotactl.h"
>  #include <sys/quota.h>
>  
> @@ -66,3 +67,14 @@ xfsquotactl(
>  
>  	return quotactl(op, device, id, addr);
>  }
> +
> +int
> +xfrog_quotactl(
> +	const struct fs_path	*mount,
> +	enum xfs_quota_cmd	xcommand,
> +	uint			xtype,
> +	uint			id,
> +	void			*addr)
> +{
> +	return xfsquotactl(xcommand, mount->fs_name, xtype, id, addr);

The xfsquotactl() can be unfolded here and calls may be replaced
with xfrog_quotactl(), no?

-- 
- Andrey

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 1/7] libfrog: hoist xfsquotactl out of xfs_quota
  2026-09-14  5:49 ` [PATCH 1/7] libfrog: hoist xfsquotactl out of xfs_quota Darrick J. Wong
@ 2026-09-15 12:24   ` Christoph Hellwig
  0 siblings, 0 replies; 18+ messages in thread
From: Christoph Hellwig @ 2026-09-15 12:24 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: aalbersh, linux-xfs

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 2/7] libfrog: clean up xfsquotactl a little bit
  2026-09-14  5:50 ` [PATCH 2/7] libfrog: clean up xfsquotactl a little bit Darrick J. Wong
@ 2026-09-15 12:25   ` Christoph Hellwig
  0 siblings, 0 replies; 18+ messages in thread
From: Christoph Hellwig @ 2026-09-15 12:25 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: aalbersh, linux-xfs

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 3/7] libfrog: enhance struct fs_path to store optional mount fd
  2026-09-14  5:50 ` [PATCH 3/7] libfrog: enhance struct fs_path to store optional mount fd Darrick J. Wong
@ 2026-09-15 12:25   ` Christoph Hellwig
  0 siblings, 0 replies; 18+ messages in thread
From: Christoph Hellwig @ 2026-09-15 12:25 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: aalbersh, linux-xfs

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/7] libfrog: try to pass struct fs_path objects to quotactl wrapper
  2026-09-15 10:34   ` Andrey Albershteyn
@ 2026-09-15 12:26     ` Christoph Hellwig
  2026-09-15 16:15       ` Darrick J. Wong
  0 siblings, 1 reply; 18+ messages in thread
From: Christoph Hellwig @ 2026-09-15 12:26 UTC (permalink / raw)
  To: Andrey Albershteyn; +Cc: Darrick J. Wong, linux-xfs

On Tue, Sep 15, 2026 at 12:34:33PM +0200, Andrey Albershteyn wrote:
> > +	const struct fs_path	*mount,
> > +	enum xfs_quota_cmd	xcommand,
> > +	uint			xtype,
> > +	uint			id,
> > +	void			*addr)
> > +{
> > +	return xfsquotactl(xcommand, mount->fs_name, xtype, id, addr);
> 
> The xfsquotactl() can be unfolded here and calls may be replaced
> with xfrog_quotactl(), no?

Yeah, looks like we should be able to kill it.  But maybe do that
as a follow on cleanup?


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 5/7] libfrog: add quotactl_fd support to xfsquotactl
  2026-09-14  5:50 ` [PATCH 5/7] libfrog: add quotactl_fd support to xfsquotactl Darrick J. Wong
@ 2026-09-15 12:27   ` Christoph Hellwig
  0 siblings, 0 replies; 18+ messages in thread
From: Christoph Hellwig @ 2026-09-15 12:27 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: aalbersh, linux-xfs

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 6/7] xfs_quota: open the filesystem mountpoint for quota operations
  2026-09-14  5:51 ` [PATCH 6/7] xfs_quota: open the filesystem mountpoint for quota operations Darrick J. Wong
@ 2026-09-15 12:27   ` Christoph Hellwig
  0 siblings, 0 replies; 18+ messages in thread
From: Christoph Hellwig @ 2026-09-15 12:27 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: aalbersh, linux-xfs

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>


^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 7/7] xfs_spaceman: port makecfg to use xfsquotactl
  2026-09-14  5:51 ` [PATCH 7/7] xfs_spaceman: port makecfg to use xfsquotactl Darrick J. Wong
@ 2026-09-15 12:28   ` Christoph Hellwig
  0 siblings, 0 replies; 18+ messages in thread
From: Christoph Hellwig @ 2026-09-15 12:28 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: aalbersh, linux-xfs

Looks good:

Reviewed-by: Christoph Hellwig <hch@lst.de>

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/7] libfrog: try to pass struct fs_path objects to quotactl wrapper
  2026-09-15 12:26     ` Christoph Hellwig
@ 2026-09-15 16:15       ` Darrick J. Wong
  2026-09-16  8:33         ` Andrey Albershteyn
  0 siblings, 1 reply; 18+ messages in thread
From: Darrick J. Wong @ 2026-09-15 16:15 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: Andrey Albershteyn, linux-xfs

On Tue, Sep 15, 2026 at 05:26:40AM -0700, Christoph Hellwig wrote:
> On Tue, Sep 15, 2026 at 12:34:33PM +0200, Andrey Albershteyn wrote:
> > > +	const struct fs_path	*mount,
> > > +	enum xfs_quota_cmd	xcommand,
> > > +	uint			xtype,
> > > +	uint			id,
> > > +	void			*addr)
> > > +{
> > > +	return xfsquotactl(xcommand, mount->fs_name, xtype, id, addr);
> > 
> > The xfsquotactl() can be unfolded here and calls may be replaced
> > with xfrog_quotactl(), no?
> 
> Yeah, looks like we should be able to kill it.  But maybe do that
> as a follow on cleanup?

Hrm.  There are two xfsquotactl() callers remaining after this patch.
One of them is set_limits() in quota/edit.c.  One of the callers of
set_limits is limit_f, which could very well pass through the fs_path
like everywhere else:

	set_limits(id, type, mask, fs_path->mnt_fd, fs_path->fs_name,
		   &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);

Obviously a good candidate for passing the fs_path instead of the raw
pieces.

The other set_limits caller is restore_file:

static void
restore_file(
	FILE		*fp,
	uint		type)
{
	char		buffer[512];
	char		dev[512];
	uint		mask;
	int		cnt;
	uint32_t	id;
	uint64_t	bsoft, bhard, isoft, ihard, rtbsoft, rtbhard;

	while (fgets(buffer, sizeof(buffer), fp) != NULL) {
		if (strncmp("fs = ", buffer, 5) == 0) {
			/*
			 * Copy the device name to dev, strip off the trailing
			 * newline, and move on to the next line.
			 */
			strncpy(dev, buffer + 5, sizeof(dev) - 1);
			dev[strlen(dev) - 1] = '\0';
			continue;
		}
		rtbsoft = rtbhard = 0;
		cnt = sscanf(buffer, "%u %llu %llu %llu %llu %llu %llu\n",
				&id,
				(unsigned long long *)&bsoft,
				(unsigned long long *)&bhard,
				(unsigned long long *)&isoft,
				(unsigned long long *)&ihard,
				(unsigned long long *)&rtbsoft,
				(unsigned long long *)&rtbhard);
		if (cnt == 5 || cnt == 7) {
			mask = FS_DQ_ISOFT|FS_DQ_IHARD|FS_DQ_BSOFT|FS_DQ_BHARD;
			if (cnt == 7)
				mask |= FS_DQ_RTBSOFT|FS_DQ_RTBHARD;
			set_limits(id, type, mask, -1, dev, &bsoft, &bhard,
					&isoft, &ihard, &rtbsoft, &rtbhard);
		}
	}
}

Here, we have a device string, but no open fd to it.  I could construct
a fake fs_path object to use the xfrog_quotactl() interface, but that's
kinda nasty so I'd rather just leave it as an xfsquotactl() site.

The second caller is makecfg_f -> get_qflags, which is added in a couple
of patches.  That one I could just pass it through

	mount = fs_table_lookup_mount(file->name);
	if (!mount) {
		fprintf(stderr, _("%s: Not a XFS mount point.\n"), file->name);
		return 1;
	}

	mount->mnt_fd = file->xfd.fd;
	ret = get_qflags(mount, &qflags);
	mount->mnt_fd = -1;

Regrettably, struct fileio in spaceman/ is private to xfs_spaceman so
there's no general way to pass that to a libfrog function, which is why
we have to play switcheroo games with mount->mnt_fd here.

--D

^ permalink raw reply	[flat|nested] 18+ messages in thread

* Re: [PATCH 4/7] libfrog: try to pass struct fs_path objects to quotactl wrapper
  2026-09-15 16:15       ` Darrick J. Wong
@ 2026-09-16  8:33         ` Andrey Albershteyn
  0 siblings, 0 replies; 18+ messages in thread
From: Andrey Albershteyn @ 2026-09-16  8:33 UTC (permalink / raw)
  To: Darrick J. Wong; +Cc: Christoph Hellwig, linux-xfs

On 2026-09-15 09:15:33, Darrick J. Wong wrote:
> On Tue, Sep 15, 2026 at 05:26:40AM -0700, Christoph Hellwig wrote:
> > On Tue, Sep 15, 2026 at 12:34:33PM +0200, Andrey Albershteyn wrote:
> > > > +	const struct fs_path	*mount,
> > > > +	enum xfs_quota_cmd	xcommand,
> > > > +	uint			xtype,
> > > > +	uint			id,
> > > > +	void			*addr)
> > > > +{
> > > > +	return xfsquotactl(xcommand, mount->fs_name, xtype, id, addr);
> > > 
> > > The xfsquotactl() can be unfolded here and calls may be replaced
> > > with xfrog_quotactl(), no?
> > 
> > Yeah, looks like we should be able to kill it.  But maybe do that
> > as a follow on cleanup?
> 
> Hrm.  There are two xfsquotactl() callers remaining after this patch.
> One of them is set_limits() in quota/edit.c.  One of the callers of
> set_limits is limit_f, which could very well pass through the fs_path
> like everywhere else:
> 
> 	set_limits(id, type, mask, fs_path->mnt_fd, fs_path->fs_name,
> 		   &bsoft, &bhard, &isoft, &ihard, &rtbsoft, &rtbhard);
> 
> Obviously a good candidate for passing the fs_path instead of the raw
> pieces.
> 
> The other set_limits caller is restore_file:
> 
> static void
> restore_file(
> 	FILE		*fp,
> 	uint		type)
> {
> 	char		buffer[512];
> 	char		dev[512];
> 	uint		mask;
> 	int		cnt;
> 	uint32_t	id;
> 	uint64_t	bsoft, bhard, isoft, ihard, rtbsoft, rtbhard;
> 
> 	while (fgets(buffer, sizeof(buffer), fp) != NULL) {
> 		if (strncmp("fs = ", buffer, 5) == 0) {
> 			/*
> 			 * Copy the device name to dev, strip off the trailing
> 			 * newline, and move on to the next line.
> 			 */
> 			strncpy(dev, buffer + 5, sizeof(dev) - 1);
> 			dev[strlen(dev) - 1] = '\0';
> 			continue;
> 		}
> 		rtbsoft = rtbhard = 0;
> 		cnt = sscanf(buffer, "%u %llu %llu %llu %llu %llu %llu\n",
> 				&id,
> 				(unsigned long long *)&bsoft,
> 				(unsigned long long *)&bhard,
> 				(unsigned long long *)&isoft,
> 				(unsigned long long *)&ihard,
> 				(unsigned long long *)&rtbsoft,
> 				(unsigned long long *)&rtbhard);
> 		if (cnt == 5 || cnt == 7) {
> 			mask = FS_DQ_ISOFT|FS_DQ_IHARD|FS_DQ_BSOFT|FS_DQ_BHARD;
> 			if (cnt == 7)
> 				mask |= FS_DQ_RTBSOFT|FS_DQ_RTBHARD;
> 			set_limits(id, type, mask, -1, dev, &bsoft, &bhard,
> 					&isoft, &ihard, &rtbsoft, &rtbhard);
> 		}
> 	}
> }
> 
> Here, we have a device string, but no open fd to it.  I could construct
> a fake fs_path object to use the xfrog_quotactl() interface, but that's
> kinda nasty so I'd rather just leave it as an xfsquotactl() site.
> 
> The second caller is makecfg_f -> get_qflags, which is added in a couple
> of patches.  That one I could just pass it through
> 
> 	mount = fs_table_lookup_mount(file->name);
> 	if (!mount) {
> 		fprintf(stderr, _("%s: Not a XFS mount point.\n"), file->name);
> 		return 1;
> 	}
> 
> 	mount->mnt_fd = file->xfd.fd;
> 	ret = get_qflags(mount, &qflags);
> 	mount->mnt_fd = -1;
> 
> Regrettably, struct fileio in spaceman/ is private to xfs_spaceman so
> there's no general way to pass that to a libfrog function, which is why
> we have to play switcheroo games with mount->mnt_fd here.

aha

Looks good to me then
Reviewed-by: Andrey Albershteyn <aalbersh@kernel.org>

-- 
- Andrey

^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2026-09-16  8:33 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-14  5:48 [PATCHSET 2/2] xfsprogs: use quotactl_fd when possible Darrick J. Wong
2026-09-14  5:49 ` [PATCH 1/7] libfrog: hoist xfsquotactl out of xfs_quota Darrick J. Wong
2026-09-15 12:24   ` Christoph Hellwig
2026-09-14  5:50 ` [PATCH 2/7] libfrog: clean up xfsquotactl a little bit Darrick J. Wong
2026-09-15 12:25   ` Christoph Hellwig
2026-09-14  5:50 ` [PATCH 3/7] libfrog: enhance struct fs_path to store optional mount fd Darrick J. Wong
2026-09-15 12:25   ` Christoph Hellwig
2026-09-14  5:50 ` [PATCH 4/7] libfrog: try to pass struct fs_path objects to quotactl wrapper Darrick J. Wong
2026-09-15 10:34   ` Andrey Albershteyn
2026-09-15 12:26     ` Christoph Hellwig
2026-09-15 16:15       ` Darrick J. Wong
2026-09-16  8:33         ` Andrey Albershteyn
2026-09-14  5:50 ` [PATCH 5/7] libfrog: add quotactl_fd support to xfsquotactl Darrick J. Wong
2026-09-15 12:27   ` Christoph Hellwig
2026-09-14  5:51 ` [PATCH 6/7] xfs_quota: open the filesystem mountpoint for quota operations Darrick J. Wong
2026-09-15 12:27   ` Christoph Hellwig
2026-09-14  5:51 ` [PATCH 7/7] xfs_spaceman: port makecfg to use xfsquotactl Darrick J. Wong
2026-09-15 12:28   ` Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox