Blob Blame History Raw
From f3eb31d9c005558ce975e2806f8dc73b0ecbd7f7 Mon Sep 17 00:00:00 2001
From: "Darrick J. Wong" <darrick.wong@oracle.com>
Date: Fri, 20 Nov 2020 17:03:28 -0500
Subject: [PATCH] xfs_quota: support editing and reporting quotas with bigtime

Enhance xfs_quota to detect and report grace period expirations past
2038.

Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
---

diff --git a/include/xqm.h b/include/xqm.h
index 8ab1907..573441d 100644
--- a/include/xqm.h
+++ b/include/xqm.h
@@ -47,7 +47,10 @@ typedef struct fs_disk_quota {
 	__s32		d_btimer;	/* similar to above; for disk blocks */
 	__u16		d_iwarns;	/* # warnings issued wrt num inodes */
 	__u16		d_bwarns;	/* # warnings issued wrt disk blocks */
-	__s32		d_padding2;	/* padding2 - for future use */
+	__s8		d_itimer_hi;	/* upper 8 bits of timer values */
+	__s8		d_btimer_hi;
+	__s8		d_rtbtimer_hi;
+	__s8		d_padding2;	/* padding2 - for future use */
 	__u64		d_rtb_hardlimit;/* absolute limit on realtime blks */
 	__u64		d_rtb_softlimit;/* preferred limit on RT disk blks */
 	__u64		d_rtbcount;	/* # realtime blocks owned */
@@ -93,6 +96,21 @@ typedef struct fs_disk_quota {
 #define FS_DQ_RTBWARNS	(1<<11)
 #define FS_DQ_WARNS_MASK	(FS_DQ_BWARNS | FS_DQ_IWARNS | FS_DQ_RTBWARNS)
 
+/*
+ * Accounting values.  These can only be set for filesystem with
+ * non-transactional quotas that require quotacheck(8) in userspace.
+ */
+#define FS_DQ_BCOUNT		(1<<12)
+#define FS_DQ_ICOUNT		(1<<13)
+#define FS_DQ_RTBCOUNT		(1<<14)
+#define FS_DQ_ACCT_MASK		(FS_DQ_BCOUNT | FS_DQ_ICOUNT | FS_DQ_RTBCOUNT)
+
+/*
+ * Quota expiration timestamps are 40-bit signed integers, with the upper 8
+ * bits encoded in the _hi fields.
+ */
+#define FS_DQ_BIGTIME		(1<<15)
+
 /*
  * Various flags related to quotactl(2).  Only relevant to XFS filesystems.
  */
diff --git a/quota/edit.c b/quota/edit.c
index b3cad02..1a3b2d9 100644
--- a/quota/edit.c
+++ b/quota/edit.c
@@ -417,6 +417,53 @@ restore_f(
 	return 0;
 }
 
+time64_t
+decode_timer(
+	const struct fs_disk_quota *d,
+	__s32			timer_lo,
+	__s8			timer_hi)
+{
+	if (d->d_fieldmask & FS_DQ_BIGTIME)
+		return (uint32_t)timer_lo | (int64_t)timer_hi << 32;
+	return timer_lo;
+}
+
+static inline void
+encode_timer(
+	const struct fs_disk_quota *d,
+	__s32			*timer_lo,
+	__s8			*timer_hi,
+	time64_t		timer)
+{
+	*timer_lo = timer;
+	if (d->d_fieldmask & FS_DQ_BIGTIME)
+		*timer_hi = timer >> 32;
+	else
+		*timer_hi = 0;
+}
+
+static inline bool want_bigtime(time64_t timer)
+{
+	return timer > INT32_MAX || timer < INT32_MIN;
+}
+
+static void
+encode_timers(
+	struct fs_disk_quota	*d,
+	time64_t		btimer,
+	time64_t		itimer,
+	time64_t		rtbtimer)
+{
+	d->d_fieldmask &= ~FS_DQ_BIGTIME;
+	if (want_bigtime(btimer) || want_bigtime(itimer) ||
+	    want_bigtime(rtbtimer))
+		d->d_fieldmask |= FS_DQ_BIGTIME;
+
+	encode_timer(d, &d->d_btimer, &d->d_btimer_hi, btimer);
+	encode_timer(d, &d->d_itimer, &d->d_itimer_hi, itimer);
+	encode_timer(d, &d->d_rtbtimer, &d->d_rtbtimer_hi, rtbtimer);
+}
+
 static void
 set_timer(
 	uint32_t		id,
@@ -426,6 +473,7 @@ set_timer(
 	time64_t		value)
 {
 	struct fs_disk_quota	d;
+	time64_t		btimer, itimer, rtbtimer;
 
 	memset(&d, 0, sizeof(d));
 
@@ -446,23 +494,28 @@ set_timer(
 
 		time(&now);
 
+		btimer = decode_timer(&d, d.d_btimer, d.d_btimer_hi);
+		itimer = decode_timer(&d, d.d_itimer, d.d_itimer_hi);
+		rtbtimer = decode_timer(&d, d.d_rtbtimer, d.d_rtbtimer_hi);
+
 		/* Only set grace time if user is already past soft limit */
 		if (d.d_blk_softlimit && d.d_bcount > d.d_blk_softlimit)
-			d.d_btimer = now + value;
+			btimer = now + value;
 		if (d.d_ino_softlimit && d.d_icount > d.d_ino_softlimit)
-			d.d_itimer = now + value;
+			itimer = now + value;
 		if (d.d_rtb_softlimit && d.d_rtbcount > d.d_rtb_softlimit)
-			d.d_rtbtimer = now + value;
+			rtbtimer = now + value;
 	} else {
-		d.d_btimer = value;
-		d.d_itimer = value;
-		d.d_rtbtimer = value;
+		btimer = value;
+		itimer = value;
+		rtbtimer = value;
 	}
 
 	d.d_version = FS_DQUOT_VERSION;
 	d.d_flags = type;
 	d.d_fieldmask = mask;
 	d.d_id = id;
+	encode_timers(&d, btimer, itimer, rtbtimer);
 
 	if (xfsquotactl(XFS_SETQLIM, dev, type, id, (void *)&d) < 0) {
 		exitcode = 1;
diff --git a/quota/quota.c b/quota/quota.c
index 8ba0995..0747ced 100644
--- a/quota/quota.c
+++ b/quota/quota.c
@@ -101,7 +101,7 @@ quota_mount(
 	}
 
 	if (form & XFS_BLOCK_QUOTA) {
-		timer = d.d_btimer;
+		timer = decode_timer(&d, d.d_btimer, d.d_btimer_hi);
 		qflags = (flags & HUMAN_FLAG);
 		if (d.d_blk_hardlimit && d.d_bcount > d.d_blk_hardlimit)
 			qflags |= LIMIT_FLAG;
@@ -123,7 +123,7 @@ quota_mount(
 				time_to_string(timer, qflags));
 	}
 	if (form & XFS_INODE_QUOTA) {
-		timer = d.d_itimer;
+		timer = decode_timer(&d, d.d_itimer, d.d_itimer_hi);
 		qflags = (flags & HUMAN_FLAG);
 		if (d.d_ino_hardlimit && d.d_icount > d.d_ino_hardlimit)
 			qflags |= LIMIT_FLAG;
@@ -145,7 +145,7 @@ quota_mount(
 				time_to_string(timer, qflags));
 	}
 	if (form & XFS_RTBLOCK_QUOTA) {
-		timer = d.d_rtbtimer;
+		timer = decode_timer(&d, d.d_rtbtimer, d.d_rtbtimer_hi);
 		qflags = (flags & HUMAN_FLAG);
 		if (d.d_rtb_hardlimit && d.d_rtbcount > d.d_rtb_hardlimit)
 			qflags |= LIMIT_FLAG;
diff --git a/quota/quota.h b/quota/quota.h
index 13ae450..74eb146 100644
--- a/quota/quota.h
+++ b/quota/quota.h
@@ -3,6 +3,8 @@
  * Copyright (c) 2005 Silicon Graphics, Inc.
  * All Rights Reserved.
  */
+#ifndef XFS_QUOTA_QUOTA_H_
+#define XFS_QUOTA_QUOTA_H_
 
 #include "xqm.h"
 #include "path.h"
@@ -73,3 +75,8 @@ extern char *uid_to_name(uint32_t __uid);
 extern char *gid_to_name(uint32_t __gid);
 extern char *prid_to_name(uint32_t __prid);
 extern bool isdigits_only(const char *);
+
+time64_t decode_timer(const struct fs_disk_quota *d, __s32 timer_lo,
+		__s8 timer_hi);
+
+#endif /* XFS_QUOTA_QUOTA_H_ */
diff --git a/quota/report.c b/quota/report.c
index 2d5024e..6ac5549 100644
--- a/quota/report.c
+++ b/quota/report.c
@@ -398,7 +398,7 @@ report_mount(
 	}
 
 	if (form & XFS_BLOCK_QUOTA) {
-		timer = d.d_btimer;
+		timer = decode_timer(&d, d.d_btimer, d.d_btimer_hi);
 		qflags = (flags & HUMAN_FLAG);
 		if (d.d_blk_hardlimit && d.d_bcount > d.d_blk_hardlimit)
 			qflags |= LIMIT_FLAG;
@@ -420,7 +420,7 @@ report_mount(
 				time_to_string(timer, qflags));
 	}
 	if (form & XFS_INODE_QUOTA) {
-		timer = d.d_itimer;
+		timer = decode_timer(&d, d.d_itimer, d.d_itimer_hi);
 		qflags = (flags & HUMAN_FLAG);
 		if (d.d_ino_hardlimit && d.d_icount > d.d_ino_hardlimit)
 			qflags |= LIMIT_FLAG;
@@ -442,7 +442,7 @@ report_mount(
 				time_to_string(timer, qflags));
 	}
 	if (form & XFS_RTBLOCK_QUOTA) {
-		timer = d.d_rtbtimer;
+		timer = decode_timer(&d, d.d_rtbtimer, d.d_rtbtimer_hi);
 		qflags = (flags & HUMAN_FLAG);
 		if (d.d_rtb_hardlimit && d.d_rtbcount > d.d_rtb_hardlimit)
 			qflags |= LIMIT_FLAG;