Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 5 Jul 2018 22:56:14 +0000 (UTC)
From:      Sean Eric Fagan <sef@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r336017 - in head: include/rpcsvc lib/libutil libexec/rpc.rquotad sys/cddl/contrib/opensolaris/uts/common/fs/zfs usr.bin/quota
Message-ID:  <201807052256.w65MuETx038724@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: sef
Date: Thu Jul  5 22:56:13 2018
New Revision: 336017
URL: https://svnweb.freebsd.org/changeset/base/336017

Log:
  This exposes ZFS user and group quotas via the normal
  quatactl(2) mechanism.  (Read-only at this point, however.)
  In particular, this is to allow rpc.rquotad query quotas
  for NFS mounts, allowing users to see their quotas on the
  hosts using the datasets.
  
  The changes specifically:
  
  * Add new RPC entry points for querying quotas.
  * Changes the library routines to allow non-UFS quotas.
  * Changes rquotad to check for quotas on mounted filesystems,
  rather than being limited to entries in /etc/fstab
  * Lastly, adds a VFS entry-point for ZFS to query quotas.
  
  Note that this makes one unavoidable behavioural change: if quotas
  are enabled, then they can be queried, as opposed to the current
  method of checking for quotas being specified in fstab.  (With
  ZFS, if there are user or group quotas, they're used, always.)
  
  Reviewed by:	delphij, mav
  Approved by:	mav
  Sponsored by:	iXsystems Inc
  Differential Revision:	https://reviews.freebsd.org/D15886

Modified:
  head/include/rpcsvc/rquota.x
  head/lib/libutil/quotafile.c
  head/libexec/rpc.rquotad/rquotad.c
  head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
  head/usr.bin/quota/quota.c

Modified: head/include/rpcsvc/rquota.x
==============================================================================
--- head/include/rpcsvc/rquota.x	Thu Jul  5 21:38:54 2018	(r336016)
+++ head/include/rpcsvc/rquota.x	Thu Jul  5 22:56:13 2018	(r336017)
@@ -1,24 +1,55 @@
+/* @(#)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
  */
 
 #ifndef RPC_HDR
-%#ifndef lint
-%/*static char sccsid[] = "from: @(#)rquota.x 1.2 87/09/20 Copyr 1987 Sun Micro";*/
-%/*static char sccsid[] = "from: @(#)rquota.x	2.1 88/08/01 4.0 RPCSRC";*/
-%#endif /* not lint */
 %#include <sys/cdefs.h>
 %__FBSDID("$FreeBSD$");
 #endif
 
 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;
+};
+
 /*
  * remote quota structure
  */
@@ -37,7 +68,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 +81,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 +103,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;

Modified: head/lib/libutil/quotafile.c
==============================================================================
--- head/lib/libutil/quotafile.c	Thu Jul  5 21:38:54 2018	(r336016)
+++ head/lib/libutil/quotafile.c	Thu Jul  5 22:56:13 2018	(r336017)
@@ -120,8 +120,6 @@ quota_open(struct fstab *fs, int quotatype, int openfl
 	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;
@@ -130,10 +128,15 @@ quota_open(struct fstab *fs, int quotatype, int openfl
 	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")) {
+		errno = 0;
+		goto error;
+	}
+	serrno = hasquota(fs, quotatype, qf->qfname, sizeof(qf->qfname));
 	if (serrno == 0) {
 		errno = EOPNOTSUPP;
 		goto error;

Modified: head/libexec/rpc.rquotad/rquotad.c
==============================================================================
--- head/libexec/rpc.rquotad/rquotad.c	Thu Jul  5 21:38:54 2018	(r336016)
+++ head/libexec/rpc.rquotad/rquotad.c	Thu Jul  5 22:56:13 2018	(r336017)
@@ -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,40 @@ 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) {
@@ -108,6 +135,26 @@ rquota_service(struct svc_req *request, SVCXPRT *trans
 		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) {
+	case NULLPROC:
+		(void)svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
+		break;
+	case RQUOTAPROC_GETQUOTA:
+	case RQUOTAPROC_GETACTIVEQUOTA:
 		sendquota(request, transp);
 		break;
 	default:
@@ -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;
+	struct ext_getquota_args getq_args;
+	struct getquota_rslt getq_rslt;
+	struct dqblk dqblk;
+	struct timeval timev;
+	int scale;
 
-	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 */
+	bzero(&getq_args, sizeof(getq_args));
+	if (!svc_getargs(transp, (xdrproc_t)xdr_ext_getquota_args, &getq_args)) {
+		svcerr_decode(transp);
+		return;
 	}
-	endfsent();
-	return;
-enomem:
-	syslog(LOG_ERR, "out of memory");
-	exit(1);
-fserr:
-	syslog(LOG_ERR, "%s: %s", fs->fs_file, strerror(errno));
-	exit(1);
+	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);
+	}
 }
 
 /*
@@ -211,12 +275,43 @@ fserr:
  * 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;
 
-	for (i = 0; i < nqf; ++i)
-		if (quota_check_path(qfa[i], path) == 1)
-			return (quota_read(qfa[i], dqblk, id) == 0);
-	return (0);
+	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_open(<%s, %s>, %d) returned %p",
+		      fst.fs_file, fst.fs_mntops, type,
+		      qf);
+	if (qf == NULL)
+		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);
 }

Modified: head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c
==============================================================================
--- head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c	Thu Jul  5 21:38:54 2018	(r336016)
+++ head/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_vfsops.c	Thu Jul  5 22:56:13 2018	(r336017)
@@ -64,6 +64,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;
@@ -90,6 +92,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);
@@ -111,6 +114,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);
  * from being unloaded after a umount -f
  */
 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

Modified: head/usr.bin/quota/quota.c
==============================================================================
--- head/usr.bin/quota/quota.c	Thu Jul  5 21:38:54 2018	(r336016)
+++ head/usr.bin/quota/quota.c	Thu Jul  5 22:56:13 2018	(r336017)
@@ -98,7 +98,7 @@ static int getufsquota(struct fstab *fs, struct quotau
 	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);
 
@@ -568,22 +568,18 @@ getufsquota(struct fstab *fs, struct quotause *qup, lo
 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"
 	 */
 	cp = fst->f_mntfromname;
@@ -604,12 +600,27 @@ getnfsquota(struct statfs *fst, struct quotause *qup, 
 		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:
 		break;
@@ -650,7 +661,7 @@ getnfsquota(struct statfs *fst, struct quotause *qup, 
 	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)
 {
@@ -671,8 +682,7 @@ callaurpc(char *host, int prognum, int versnum, int pr
 	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?201807052256.w65MuETx038724>