Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 5 Jun 2018 12:11:50 -0700 (PDT)
From:      Sean Eric Fagan <sef@Kithrup.COM>
To:        james.rose@framestore.com
Cc:        freebsd-fs@freebsd.org
Subject:   Re: support for ZFS user quotas in rquotad
Message-ID:  <201806051911.w55JBogT028097@kithrup.com>
In-Reply-To: <CAOArqT2vrXbzftTb=OpGOtEb8x387m_Rt_XfOJZWzZrkjcizKQ@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help
>I will try your patches.

They're about a year old, I think, there hasn't seemed to be much call for the
feature.  But enjoy!


diff --git a/include/rpcsvc/rquota.x b/include/rpcsvc/rquota.x
index 0f3ecfc54d2..83e3533d0cc 100644
--- a/include/rpcsvc/rquota.x
+++ b/include/rpcsvc/rquota.x
@@ -1,3 +1,6 @@
+/* @(#)rquota.x	2.1 88/08/01 4.0 RPCSRC */
+/* @(#)rquota.x 1.2 87/09/20 Copyr 1987 Sun Micro */
+
 /*
  * Remote quota protocol
  * Requires unix authentication
@@ -14,9 +17,41 @@
 
 const RQ_PATHLEN = 1024;
 
+struct sq_dqblk {
+	unsigned int rq_bhardlimit;	/* absolute limit on disk blks alloc */
+	unsigned int rq_bsoftlimit;	/* preferred limit on disk blks */
+	unsigned int rq_curblocks;	/* current block count */
+	unsigned int rq_fhardlimit;	/* absolute limit on allocated files */
+	unsigned int rq_fsoftlimit;	/* preferred file limit */
+	unsigned int rq_curfiles;	/* current # allocated files */
+	unsigned int rq_btimeleft;	/* time left for excessive disk use */
+	unsigned int rq_ftimeleft;	/* time left for excessive files */
+};
+
 struct getquota_args {
 	string gqa_pathp<RQ_PATHLEN>;  	/* path to filesystem of interest */
-	int gqa_uid;	        	/* inquire about quota for uid */
+	int gqa_uid;			/* Inquire about quota for uid */
+};
+
+struct setquota_args {
+	int sqa_qcmd;
+	string sqa_pathp<RQ_PATHLEN>;  	/* path to filesystem of interest */
+	int sqa_id;			/* Set quota for uid */
+	sq_dqblk sqa_dqblk;
+};
+
+struct ext_getquota_args {
+	string gqa_pathp<RQ_PATHLEN>;  	/* path to filesystem of interest */
+	int gqa_type;			/* Type of quota info is needed about */
+	int gqa_id;			/* Inquire about quota for id */
+};
+
+struct ext_setquota_args {
+	int sqa_qcmd;
+	string sqa_pathp<RQ_PATHLEN>;  	/* path to filesystem of interest */
+	int sqa_id;			/* Set quota for id */
+	int sqa_type;			/* Type of quota to set */
+	sq_dqblk sqa_dqblk;
 };
 
 /*
@@ -37,7 +72,7 @@ struct rquota {
 
 enum gqr_status {
 	Q_OK = 1,		/* quota returned */
-	Q_NOQUOTA = 2,  	/* noquota for uid */
+	Q_NOQUOTA = 2,		/* noquota for uid */
 	Q_EPERM = 3		/* no permission to access quota */
 };
 
@@ -50,6 +85,15 @@ case Q_EPERM:
 	void;
 };
 
+union setquota_rslt switch (gqr_status status) {
+case Q_OK:
+	rquota sqr_rquota;	/* valid if status == Q_OK */
+case Q_NOQUOTA:
+	void;
+case Q_EPERM:
+	void;
+};
+
 program RQUOTAPROG {
 	version RQUOTAVERS {
 		/*
@@ -63,5 +107,42 @@ program RQUOTAPROG {
 		 */
 		getquota_rslt
 		RQUOTAPROC_GETACTIVEQUOTA(getquota_args) = 2;
+
+		/*
+		 * Set all quotas
+		 */
+		setquota_rslt
+		RQUOTAPROC_SETQUOTA(setquota_args) = 3;
+
+		/*
+	 	 * Get active quotas only
+		 */
+		setquota_rslt
+		RQUOTAPROC_SETACTIVEQUOTA(setquota_args) = 4;
 	} = 1;
+	version EXT_RQUOTAVERS {
+		/*
+		 * Get all quotas
+		 */
+		getquota_rslt
+		RQUOTAPROC_GETQUOTA(ext_getquota_args) = 1;
+
+		/*
+	 	 * Get active quotas only
+		 */
+		getquota_rslt
+		RQUOTAPROC_GETACTIVEQUOTA(ext_getquota_args) = 2;
+
+		/*
+		 * Set all quotas
+		 */
+		setquota_rslt
+		RQUOTAPROC_SETQUOTA(ext_setquota_args) = 3;
+
+		/*
+	 	 * Set active quotas only
+		 */
+		setquota_rslt
+		RQUOTAPROC_SETACTIVEQUOTA(ext_setquota_args) = 4;
+	} = 2;
 } = 100011;
diff --git a/lib/libutil/quotafile.c b/lib/libutil/quotafile.c
index 27444c2e31a..1ea16c58a2f 100644
--- a/lib/libutil/quotafile.c
+++ b/lib/libutil/quotafile.c
@@ -118,8 +118,6 @@ quota_open(struct fstab *fs, int quotatype, int openflags)
 	struct stat st;
 	int qcmd, serrno;
 
-	if (strcmp(fs->fs_vfstype, "ufs"))
-		return (NULL);
 	if ((qf = calloc(1, sizeof(*qf))) == NULL)
 		return (NULL);
 	qf->fd = -1;
@@ -128,10 +126,13 @@ quota_open(struct fstab *fs, int quotatype, int openflags)
 	if (stat(qf->fsname, &st) != 0)
 		goto error;
 	qf->dev = st.st_dev;
-	serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname));
 	qcmd = QCMD(Q_GETQUOTASIZE, quotatype);
 	if (quotactl(qf->fsname, qcmd, 0, &qf->wordsize) == 0)
 		return (qf);
+	/* We only check the quota file for ufs */
+	if (strcmp(fs->fs_vfstype, "ufs"))
+		return (NULL);
+	serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname));
 	if (serrno == 0) {
 		errno = EOPNOTSUPP;
 		goto error;
diff --git a/libexec/rpc.rquotad/rquotad.c b/libexec/rpc.rquotad/rquotad.c
index d9ce06aac82..61bc7466687 100644
--- a/libexec/rpc.rquotad/rquotad.c
+++ b/libexec/rpc.rquotad/rquotad.c
@@ -28,18 +28,19 @@ __FBSDID("$FreeBSD$");
 #include <signal.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <err.h>
 #include <string.h>
 #include <syslog.h>
 #include <unistd.h>
 
-static void rquota_service(struct svc_req *request, SVCXPRT *transp);
+static void rquota_service_1(struct svc_req *request, SVCXPRT *transp);
+static void rquota_service_2(struct svc_req *request, SVCXPRT *transp);
 static void sendquota(struct svc_req *request, SVCXPRT *transp);
-static void initfs(void);
-static int getfsquota(long id, char *path, struct dqblk *dqblk);
+static void sendquota_extended(struct svc_req *request, SVCXPRT *transp);
+static int getfsquota(int type, long id, char *path, struct dqblk *dqblk);
 
-static struct quotafile **qfa;	/* array of qfs */
-static int nqf, szqf;		/* number of qfs and size of array */
 static int from_inetd = 1;
+static int debug = 0;
 
 static void
 cleanup(int sig)
@@ -51,19 +52,32 @@ cleanup(int sig)
 }
 
 int
-main(void)
+main(int argc, char **argv)
 {
 	SVCXPRT *transp;
 	int ok;
 	struct sockaddr_storage from;
 	socklen_t fromlen;
+	int vers;
+	int ch;
+
+	while ((ch = getopt(argc, argv, "d")) != -1) {
+		switch (ch) {
+		case 'd':
+			debug++;
+			break;
+		default:
+			break;
+		}
+	}
 
 	fromlen = sizeof(from);
 	if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0)
 		from_inetd = 0;
 
 	if (!from_inetd) {
-		daemon(0, 0);
+		if (!debug)
+			daemon(0, 0);
 		(void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
 		(void)signal(SIGINT, cleanup);
 		(void)signal(SIGTERM, cleanup);
@@ -79,27 +93,60 @@ main(void)
 			syslog(LOG_ERR, "couldn't create udp service.");
 			exit(1);
 		}
+		vers = RQUOTAVERS;
 		ok = svc_reg(transp, RQUOTAPROG, RQUOTAVERS,
-		    rquota_service, NULL);
+		    rquota_service_1, NULL);
+		if (ok) {
+			vers = EXT_RQUOTAVERS;
+			ok = svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS,
+				     rquota_service_2, NULL);
+		}
 	} else {
-		ok = svc_create(rquota_service,
+		vers = RQUOTAVERS;
+		ok = svc_create(rquota_service_1,
 		    RQUOTAPROG, RQUOTAVERS, "udp");
+		if (ok) {
+			vers = EXT_RQUOTAVERS;
+			ok = svc_create(rquota_service_2,
+					RQUOTAPROG, EXT_RQUOTAVERS, "udp");
+
+		}
 	}
 	if (!ok) {
 		syslog(LOG_ERR,
-		    "unable to register (RQUOTAPROG, RQUOTAVERS, %s)",
-		    from_inetd ? "(inetd)" : "udp");
+		    "unable to register (RQUOTAPROG, %s, %s)",
+		       vers == RQUOTAVERS ? "RQUOTAVERS" : "EXT_RQUOTAVERS",
+		       from_inetd ? "(inetd)" : "udp");
 		exit(1);
 	}
 
-	initfs();
 	svc_run();
 	syslog(LOG_ERR, "svc_run returned");
 	exit(1);
 }
 
 static void
-rquota_service(struct svc_req *request, SVCXPRT *transp)
+rquota_service_2(struct svc_req *request, SVCXPRT *transp)
+{
+
+	switch (request->rq_proc) {
+	case NULLPROC:
+		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
+		break;
+	case RQUOTAPROC_GETQUOTA:
+	case RQUOTAPROC_GETACTIVEQUOTA:
+		sendquota_extended(request, transp);
+		break;
+	default:
+		svcerr_noproc(transp);
+		break;
+	}
+	if (from_inetd)
+		exit(0);
+}
+
+static void
+rquota_service_1(struct svc_req *request, SVCXPRT *transp)
 {
 
 	switch (request->rq_proc) {
@@ -136,7 +183,7 @@ sendquota(struct svc_req *request, SVCXPRT *transp)
 	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
 		/* bad auth */
 		getq_rslt.status = Q_EPERM;
-	} else if (!getfsquota(getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
+	} else if (!getfsquota(USRQUOTA, getq_args.gqa_uid, getq_args.gqa_pathp, &dqblk)) {
 		/* failed, return noquota */
 		getq_rslt.status = Q_NOQUOTA;
 	} else {
@@ -172,38 +219,55 @@ sendquota(struct svc_req *request, SVCXPRT *transp)
 }
 
 static void
-initfs(void)
+sendquota_extended(struct svc_req *request, SVCXPRT *transp)
 {
-	struct fstab *fs;
-
-	setfsent();
-	szqf = 8;
-	if ((qfa = malloc(szqf * sizeof *qfa)) == NULL)
-		goto enomem;
-	while ((fs = getfsent())) {
-		if (strcmp(fs->fs_vfstype, "ufs"))
-			continue;
-		if (nqf >= szqf) {
-			szqf *= 2;
-			if ((qfa = reallocf(qfa, szqf * sizeof *qfa)) == NULL)
-				goto enomem;
-		}
-		if ((qfa[nqf] = quota_open(fs, USRQUOTA, O_RDONLY)) == NULL) {
-			if (errno != EOPNOTSUPP)
-				goto fserr;
-			continue;
-		}
-		++nqf;
-		/* XXX */
+	struct ext_getquota_args getq_args;
+	struct getquota_rslt getq_rslt;
+	struct dqblk dqblk;
+	struct timeval timev;
+	int scale;
+
+	bzero(&getq_args, sizeof(getq_args));
+	if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args, &getq_args)) {
+		svcerr_decode(transp);
+		return;
+	}
+	if (request->rq_cred.oa_flavor != AUTH_UNIX) {
+		/* bad auth */
+		getq_rslt.status = Q_EPERM;
+	} else if (!getfsquota(getq_args.gqa_type, getq_args.gqa_id, getq_args.gqa_pathp, &dqblk)) {
+		/* failed, return noquota */
+		getq_rslt.status = Q_NOQUOTA;
+	} else {
+		gettimeofday(&timev, NULL);
+		getq_rslt.status = Q_OK;
+		getq_rslt.getquota_rslt_u.gqr_rquota.rq_active = TRUE;
+		scale = 1 << flsll(dqblk.dqb_bhardlimit >> 32);
+		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsize =
+		    DEV_BSIZE * scale;
+		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bhardlimit =
+		    dqblk.dqb_bhardlimit / scale;
+		getq_rslt.getquota_rslt_u.gqr_rquota.rq_bsoftlimit =
+		    dqblk.dqb_bsoftlimit / scale;
+		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curblocks =
+		    dqblk.dqb_curblocks / scale;
+		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fhardlimit =
+		    dqblk.dqb_ihardlimit;
+		getq_rslt.getquota_rslt_u.gqr_rquota.rq_fsoftlimit =
+		    dqblk.dqb_isoftlimit;
+		getq_rslt.getquota_rslt_u.gqr_rquota.rq_curfiles =
+		    dqblk.dqb_curinodes;
+		getq_rslt.getquota_rslt_u.gqr_rquota.rq_btimeleft =
+		    dqblk.dqb_btime - timev.tv_sec;
+		getq_rslt.getquota_rslt_u.gqr_rquota.rq_ftimeleft =
+		    dqblk.dqb_itime - timev.tv_sec;
+	}
+	if (!svc_sendreply(transp, (xdrproc_t)xdr_getquota_rslt, &getq_rslt))
+		svcerr_systemerr(transp);
+	if (!svc_freeargs(transp, (xdrproc_t)xdr_getquota_args, &getq_args)) {
+		syslog(LOG_ERR, "unable to free arguments");
+		exit(1);
 	}
-	endfsent();
-	return;
-enomem:
-	syslog(LOG_ERR, "out of memory");
-	exit(1);
-fserr:
-	syslog(LOG_ERR, "%s: %s", fs->fs_file, strerror(errno));
-	exit(1);
 }
 
 /*
@@ -211,12 +275,43 @@ initfs(void)
  * Return 0 if fail, 1 otherwise
  */
 static int
-getfsquota(long id, char *path, struct dqblk *dqblk)
+getfsquota(int type, long id, char *path, struct dqblk *dqblk)
 {
-	int i;
+	struct quotafile *qf;
+	/*
+	 * Remote quota checking is limited to mounted filesystems.
+	 * Since UFS and ZFS support the quota system calls, we
+	 * only need to make an fstab object that has the path, and
+	 * a blank name for the filesystem type.
+	 * This allows the quota_open() call to work the way we
+	 * expect it to.
+	 *
+	 * The static char declaration is because compiler warnings
+	 * don't allow passing a const char * to a char *.
+	 */
+	int rv;
+	static char blank[] = "";
+	struct fstab fst;
+
+	fst.fs_file = path;
+	fst.fs_mntops = blank;
+	fst.fs_vfstype = blank;
+	
+	if (type != USRQUOTA && type != GRPQUOTA)
+		return (0);
+	
+	qf = quota_open(&fst, type, O_RDONLY);
+	if (debug)
+		warnx("quota_opoen(<%s, %s>, %d) returned %p",
+		      fst.fs_file, fst.fs_mntops, type,
+		      qf);
+	if (qf == NULL)
+		return (0);
 
-	for (i = 0; i < nqf; ++i)
-		if (quota_check_path(qfa[i], path) == 1)
-			return (quota_read(qfa[i], dqblk, id) == 0);
-	return (0);
+	rv = quota_read(qf, dqblk, id) == 0;
+	quota_close(qf);
+	if (debug)
+		warnx("getfsquota(%d, %ld, %s, %p) -> %d",
+		      type, id, path, dqblk, rv);
+	return (rv);
 }
diff --git a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
index 430ee528cdd..aabc04827c1 100644
--- a/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
+++ b/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
@@ -63,6 +63,8 @@
 #include <sys/dmu_objset.h>
 #include <sys/spa_boot.h>
 #include <sys/jail.h>
+#include <ufs/ufs/quota.h>
+
 #include "zfs_comutil.h"
 
 struct mtx zfs_debug_mtx;
@@ -89,6 +91,7 @@ static int zfs_version_zpl = ZPL_VERSION;
 SYSCTL_INT(_vfs_zfs_version, OID_AUTO, zpl, CTLFLAG_RD, &zfs_version_zpl, 0,
     "ZPL_VERSION");
 
+static int zfs_quotactl(vfs_t *vfsp, int cmds, uid_t id, void *arg);
 static int zfs_mount(vfs_t *vfsp);
 static int zfs_umount(vfs_t *vfsp, int fflag);
 static int zfs_root(vfs_t *vfsp, int flags, vnode_t **vpp);
@@ -110,6 +113,7 @@ struct vfsops zfs_vfsops = {
 	.vfs_sync =		zfs_sync,
 	.vfs_checkexp =		zfs_checkexp,
 	.vfs_fhtovp =		zfs_fhtovp,
+	.vfs_quotactl =		zfs_quotactl,
 };
 
 VFS_SET(zfs_vfsops, zfs, VFCF_JAIL | VFCF_DELEGADMIN);
@@ -121,6 +125,159 @@ VFS_SET(zfs_vfsops, zfs, VFCF_JAIL | VFCF_DELEGADMIN);
  */
 static uint32_t	zfs_active_fs_count = 0;
 
+static int
+zfs_getquota(zfsvfs_t *zfsvfs, uid_t id, int isgroup, struct dqblk64 *dqp)
+{
+	int error = 0;
+	char buf[32];
+	int err;
+	uint64_t usedobj, quotaobj;
+	uint64_t quota, used = 0;
+	timespec_t now;
+	
+	usedobj = isgroup ? DMU_GROUPUSED_OBJECT : DMU_USERUSED_OBJECT;
+	quotaobj = isgroup ? zfsvfs->z_groupquota_obj : zfsvfs->z_userquota_obj;
+
+	if (quotaobj == 0 || zfsvfs->z_replay) {
+		error = ENOENT;
+		goto done;
+	}
+	(void)sprintf(buf, "%llx", (longlong_t)id);
+	if ((error = zap_lookup(zfsvfs->z_os, quotaobj,
+				buf, sizeof(quota), 1, &quota)) != 0) {
+		dprintf("%s(%d): quotaobj lookup failed\n", __FUNCTION__, __LINE__);
+		goto done;
+	}
+	/*
+	 * quota(8) uses bsoftlimit as "quoota", and hardlimit as "limit".
+	 * So we set them to be the same.
+	 */
+	dqp->dqb_bsoftlimit = dqp->dqb_bhardlimit = btodb(quota);
+	error = zap_lookup(zfsvfs->z_os, usedobj, buf, sizeof(used), 1, &used);
+	if (error && error != ENOENT) {
+		dprintf("%s(%d):  usedobj failed; %d\n", __FUNCTION__, __LINE__, error);
+		goto done;
+	}
+	dqp->dqb_curblocks = btodb(used);
+	dqp->dqb_ihardlimit = dqp->dqb_isoftlimit = 0;
+	vfs_timestamp(&now);
+	/*
+	 * Setting this to 0 causes FreeBSD quota(8) to print
+	 * the number of days since the epoch, which isn't
+	 * particularly useful.
+	 */
+	dqp->dqb_btime = dqp->dqb_itime = now.tv_sec;
+done:
+	return (error);
+}
+
+static int
+zfs_quotactl(vfs_t *vfsp, int cmds, uid_t id, void *arg)
+{
+	zfsvfs_t *zfsvfs = vfsp->vfs_data;
+	struct thread *td;
+	int cmd, type, error = 0;
+	int bitsize;
+	uint64_t fuid;
+	zfs_userquota_prop_t quota_type;
+	struct dqblk64 dqblk = { 0 };
+	
+	td = curthread;
+	cmd = cmds >> SUBCMDSHIFT;
+	type = cmds & SUBCMDMASK;
+
+	ZFS_ENTER(zfsvfs);
+	if (id == -1) {
+		switch (type) {
+		case USRQUOTA:
+			id = td->td_ucred->cr_ruid;
+			break;
+		case GRPQUOTA:
+			id = td->td_ucred->cr_rgid;
+			break;
+		default:
+			error = EINVAL;
+			goto done;
+		}
+	}
+	/*
+	 * Map BSD type to:
+	 * ZFS_PROP_USERUSED,
+	 * ZFS_PROP_USERQUOTA,
+	 * ZFS_PROP_GROUPUSED,
+	 * ZFS_PROP_GROUPQUOTA
+	 */
+	switch (cmd) {
+	case Q_SETQUOTA:
+	case Q_SETQUOTA32:
+		if (type == USRQUOTA)
+			quota_type = ZFS_PROP_USERQUOTA;
+		else if (type == GRPQUOTA)
+			quota_type = ZFS_PROP_GROUPQUOTA;
+		else
+			error = EINVAL;
+		break;
+	case Q_GETQUOTA:
+	case Q_GETQUOTA32:
+		if (type == USRQUOTA)
+			quota_type = ZFS_PROP_USERUSED;
+		else if (type == GRPQUOTA)
+			quota_type = ZFS_PROP_GROUPUSED;
+		else
+			error = EINVAL;
+		break;
+	}
+
+	/*
+	 * Depending on the cmd, we may need to get
+	 * the ruid and domain (see fuidstr_to_sid?),
+	 * the fuid (how?), or other information.
+	 * Create fuid using zfs_fuid_create(zfsvfs, id,
+	 * ZFS_OWNER or ZFS_GROUP, cr, &fuidp)?
+	 * I think I can use just the id?
+	 *
+	 * Look at zfs_fuid_overquota() to look up a quota.
+	 * zap_lookup(something, quotaobj, fuidstring, sizeof(long long), 1, &quota)
+	 *
+	 * See zfs_set_userquota() to set a quota.
+	 */
+	if ((u_int)type >= MAXQUOTAS) {
+		error = EINVAL;
+		goto done;
+	}
+
+	switch (cmd) {
+	case Q_GETQUOTASIZE:
+		bitsize = 64;
+		error = copyout(&bitsize, arg, sizeof(int));
+		break;
+	case Q_QUOTAON:
+		// As far as I can tell, you can't turn quotas on or off on zfs
+		error = 0;
+		break;
+	case Q_QUOTAOFF:
+		error = ENOTSUP;
+		break;
+	case Q_SETQUOTA:
+		error = copyin(&dqblk, arg, sizeof(dqblk));
+		if (error == 0)
+			error = zfs_set_userquota(zfsvfs, quota_type,
+						  "", id, dbtob(dqblk.dqb_bhardlimit));
+		break;
+	case Q_GETQUOTA:
+		error = zfs_getquota(zfsvfs, id, type == GRPQUOTA, &dqblk);
+		if (error == 0)
+			error = copyout(&dqblk, arg, sizeof(dqblk));
+		break;
+	default:
+		error = EINVAL;
+		break;
+	}
+done:
+	ZFS_EXIT(zfsvfs);
+	return (error);
+}
+
 /*ARGSUSED*/
 static int
 zfs_sync(vfs_t *vfsp, int waitfor)
diff --git a/usr.bin/quota/quota.c b/usr.bin/quota/quota.c
index c88f2aaefd8..32bdeabdadf 100644
--- a/usr.bin/quota/quota.c
+++ b/usr.bin/quota/quota.c
@@ -96,7 +96,7 @@ static int getufsquota(struct fstab *fs, struct quotause *qup, long id,
 	int quotatype);
 static int getnfsquota(struct statfs *fst, struct quotause *qup, long id,
 	int quotatype);
-static int callaurpc(char *host, int prognum, int versnum, int procnum, 
+static enum clnt_stat callaurpc(char *host, int prognum, int versnum, int procnum, 
 	xdrproc_t inproc, char *in, xdrproc_t outproc, char *out);
 static int alldigits(char *s);
 
@@ -566,21 +566,17 @@ getufsquota(struct fstab *fs, struct quotause *qup, long id, int quotatype)
 static int
 getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype)
 {
-	struct getquota_args gq_args;
+	struct ext_getquota_args gq_args;
+	struct getquota_args old_gq_args;
 	struct getquota_rslt gq_rslt;
 	struct dqblk *dqp = &qup->dqblk;
 	struct timeval tv;
 	char *cp, host[NI_MAXHOST];
+	enum clnt_stat call_stat;
 
 	if (fst->f_flags & MNT_LOCAL)
 		return (0);
 
-	/*
-	 * rpc.rquotad does not support group quotas
-	 */
-	if (quotatype != USRQUOTA)
-		return (0);
-
 	/*
 	 * must be some form of "hostname:/path"
 	 */
@@ -602,11 +598,26 @@ getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype)
 		return (0);
 
 	gq_args.gqa_pathp = cp + 1;
-	gq_args.gqa_uid = id;
-	if (callaurpc(host, RQUOTAPROG, RQUOTAVERS,
-	    RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, (char *)&gq_args,
-	    (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt) != 0)
-		return (0);
+	gq_args.gqa_id = id;
+	gq_args.gqa_type = quotatype;
+
+	call_stat = callaurpc(host, RQUOTAPROG, EXT_RQUOTAVERS,
+			      RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_ext_getquota_args, (char *)&gq_args,
+			      (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt);
+	if (call_stat == RPC_PROGVERSMISMATCH) {
+		if (quotatype == USRQUOTA) {
+			old_gq_args.gqa_pathp = cp + 1;
+			old_gq_args.gqa_uid = id;
+			call_stat = callaurpc(host, RQUOTAPROG, RQUOTAVERS,
+					      RQUOTAPROC_GETQUOTA, (xdrproc_t)xdr_getquota_args, (char *)&old_gq_args,
+					      (xdrproc_t)xdr_getquota_rslt, (char *)&gq_rslt);
+		} else {
+			/* Old rpc quota does not support group type */
+			return (0);
+		}
+	}
+	if (call_stat != 0)
+		return (call_stat);
 
 	switch (gq_rslt.status) {
 	case Q_NOQUOTA:
@@ -648,7 +659,7 @@ getnfsquota(struct statfs *fst, struct quotause *qup, long id, int quotatype)
 	return (0);
 }
  
-static int
+static enum clnt_stat
 callaurpc(char *host, int prognum, int versnum, int procnum,
     xdrproc_t inproc, char *in, xdrproc_t outproc, char *out)
 {
@@ -669,8 +680,7 @@ callaurpc(char *host, int prognum, int versnum, int procnum,
 	tottimeout.tv_usec = 0;
 	clnt_stat = clnt_call(client, procnum, inproc, in,
 	    outproc, out, tottimeout);
- 
-	return ((int) clnt_stat);
+	return (clnt_stat);
 }
 
 static int



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201806051911.w55JBogT028097>