Subject: group quota and rquotad
To: None <tech-userlevel@netbsd.org>
From: Manuel Bouyer <bouyer@antioche.lip6.fr>
List: tech-userlevel
Date: 12/06/2002 18:14:42
--d6Gm4EdcadzBjdND
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi,
I'm about to switch to group quotas on one partition of my NFS server.
Because of this I need group quota support in rpc.rquotad.

Linux already have support for this; they defined version 2 rquota protocol,
with an extended argument struct including the quota type.
The attached diff implement this in NetBSD's rpc.rquotad(8) and quota(1).
I checked that the rpc.rquotad is compatible with linux's quota(1).

Now, I don't know if this version 2 protocol is just a linux thing, or
is standart (maybe part of NFSv4 ?). Solaris9 still only defines rquota
version 1; it seems it has no group quota support even for local filesystems.
FreeBSD has no group quota support in rquota.
Can someone with access to others operating systems check this ?

--
Manuel Bouyer, LIP6, Universite Paris VI.           Manuel.Bouyer@lip6.fr
     NetBSD: 23 ans d'experience feront toujours la difference
--

--d6Gm4EdcadzBjdND
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="diff.quota"

? usr.bin/quota/quota.cat1
Index: lib/librpcsvc/rquota.x
===================================================================
RCS file: /cvsroot/basesrc/lib/librpcsvc/rquota.x,v
retrieving revision 1.4
diff -u -r1.4 rquota.x
--- rquota.x	1999/07/02 15:44:13	1.4
+++ rquota.x	2002/12/06 16:55:43
@@ -19,6 +19,16 @@
 	int gqa_uid;	        	/* inquire about quota for uid */
 };
 
+const RQUOTA_MAXQUOTAS = 0x02;
+const RQUOTA_USRQUOTA = 0x00;
+const RQUOTA_GRPQUOTA = 0x01;
+
+struct ext_getquota_args {
+	string gqa_pathp<RQ_PATHLEN>;  	/* path to filesystem of interest */
+	int gqa_type;			/* type of quota */
+	int gqa_id;	        	/* inquire about quota for uid/gid */
+};
+
 /*
  * remote quota structure
  */
@@ -64,4 +74,17 @@
 		getquota_rslt
 		RQUOTAPROC_GETACTIVEQUOTA(getquota_args) = 2;
 	} = 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;
+	} = 2;
 } = 100011;
Index: libexec/rpc.rquotad/rquotad.c
===================================================================
RCS file: /cvsroot/basesrc/libexec/rpc.rquotad/rquotad.c,v
retrieving revision 1.17
diff -u -r1.17 rquotad.c
--- rquotad.c	2001/01/10 01:50:05	1.17
+++ rquotad.c	2002/12/06 16:55:43
@@ -37,11 +37,12 @@
 #include <arpa/inet.h>
 
 void rquota_service(struct svc_req *request, SVCXPRT *transp);
-void sendquota(struct svc_req *request, SVCXPRT *transp);
+void ext_rquota_service(struct svc_req *request, SVCXPRT *transp);
+void sendquota(struct svc_req *request, int vers, SVCXPRT *transp);
 void printerr_reply(SVCXPRT *transp);
 void initfs(void);
-int getfsquota(long id, char *path, struct dqblk *dqblk);
-int hasquota(struct fstab *fs, char **qfnamep);
+int getfsquota(int type, long id, char *path, struct dqblk *dqblk);
+int hasquota(struct fstab *fs, char **uqfnamep, char **gqfnamep);
 void cleanup(int);
 int main(int, char *[]);
 
@@ -52,7 +53,8 @@
 struct fs_stat {
 	struct fs_stat *fs_next;	/* next element */
 	char   *fs_file;		/* mount point of the filesystem */
-	char   *qfpathname;		/* pathname of the quota file */
+	char   *uqfpathname;		/* pathname of the user quota file */
+	char   *gqfpathname;		/* pathname of the group quota file */
 	dev_t   st_dev;			/* device of the filesystem */
 } fs_stat;
 struct fs_stat *fs_begin = NULL;
@@ -65,6 +67,7 @@
 {
 
 	(void)rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
+	(void)rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL);
 	exit(0);
 }
 
@@ -83,7 +86,7 @@
 		daemon(0, 0);
 
 		(void) rpcb_unset(RQUOTAPROG, RQUOTAVERS, NULL);
-
+		(void) rpcb_unset(RQUOTAPROG, EXT_RQUOTAVERS, NULL);
 		(void) signal(SIGINT, cleanup);
 		(void) signal(SIGTERM, cleanup);
 		(void) signal(SIGHUP, cleanup);
@@ -104,12 +107,24 @@
 			    "unable to register (RQUOTAPROG, RQUOTAVERS).");
 			exit(1);
 		}
+		if (!svc_reg(transp, RQUOTAPROG, EXT_RQUOTAVERS,
+		    ext_rquota_service, NULL)) {
+			syslog(LOG_ERR,
+			    "unable to register (RQUOTAPROG, EXT_RQUOTAVERS).");
+			exit(1);
+		}
 	} else {
 		if (!svc_create(rquota_service, RQUOTAPROG, RQUOTAVERS, "udp")){
 			syslog(LOG_ERR,
 			    "unable to create (RQUOTAPROG, RQUOTAVERS).");
 			exit(1);
 		}
+		if (!svc_create(ext_rquota_service, RQUOTAPROG,
+		    EXT_RQUOTAVERS, "udp")){
+			syslog(LOG_ERR,
+			    "unable to create (RQUOTAPROG, EXT_RQUOTAVERS).");
+			exit(1);
+		}
 	}
 
 	initfs();		/* init the fs_stat list */
@@ -127,8 +142,29 @@
 		break;
 
 	case RQUOTAPROC_GETQUOTA:
+	case RQUOTAPROC_GETACTIVEQUOTA:
+		sendquota(request, RQUOTAVERS, transp);
+		break;
+
+	default:
+		svcerr_noproc(transp);
+		break;
+	}
+	if (from_inetd)
+		exit(0);
+}
+
+void 
+ext_rquota_service(struct svc_req *request, SVCXPRT *transp)
+{
+	switch (request->rq_proc) {
+	case NULLPROC:
+		(void)svc_sendreply(transp, xdr_void, (char *)NULL);
+		break;
+
+	case RQUOTAPROC_GETQUOTA:
 	case RQUOTAPROC_GETACTIVEQUOTA:
-		sendquota(request, transp);
+		sendquota(request, EXT_RQUOTAVERS, transp);
 		break;
 
 	default:
@@ -141,23 +177,39 @@
 
 /* read quota for the specified id, and send it */
 void 
-sendquota(struct svc_req *request, SVCXPRT *transp)
+sendquota(struct svc_req *request, int vers, SVCXPRT *transp)
 {
 	struct getquota_args getq_args;
+	struct ext_getquota_args ext_getq_args;
 	struct getquota_rslt getq_rslt;
 	struct dqblk dqblk;
 	struct timeval timev;
 
 	memset((char *)&getq_args, 0, sizeof(getq_args));
-	if (!svc_getargs(transp, xdr_getquota_args, (caddr_t)&getq_args)) {
-		svcerr_decode(transp);
-		return;
+	switch (vers) {
+	case RQUOTAVERS:
+		if (!svc_getargs(transp, xdr_getquota_args,
+		    (caddr_t)&getq_args)) {
+			svcerr_decode(transp);
+			return;
+		}
+		ext_getq_args.gqa_pathp = getq_args.gqa_pathp;
+		ext_getq_args.gqa_id = getq_args.gqa_uid;
+		ext_getq_args.gqa_type = RQUOTA_USRQUOTA;
+		break;
+	case EXT_RQUOTAVERS:
+		if (!svc_getargs(transp, xdr_ext_getquota_args,
+		    (caddr_t)&ext_getq_args)) {
+			svcerr_decode(transp);
+			return;
+		}
+		break;
 	}
 	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(ext_getq_args.gqa_type, ext_getq_args.gqa_id,
+	    ext_getq_args.gqa_pathp, &dqblk)) {
 		/* failed, return noquota */
 		getq_rslt.status = Q_NOQUOTA;
 	} else {
@@ -214,7 +266,7 @@
 {
 	struct fs_stat *fs_current = NULL;
 	struct fs_stat *fs_next = NULL;
-	char *qfpathname;
+	char *uqfpathname, *gqfpathname;
 	struct fstab *fs;
 	struct stat st;
 
@@ -222,7 +274,7 @@
 	while ((fs = getfsent())) {
 		if (strcmp(fs->fs_vfstype, MOUNT_FFS))
 			continue;
-		if (!hasquota(fs, &qfpathname))
+		if (!hasquota(fs, &uqfpathname, &gqfpathname))
 			continue;
 
 		fs_current = (struct fs_stat *) malloc(sizeof(struct fs_stat));
@@ -237,13 +289,23 @@
 			syslog(LOG_ERR, "can't strdup: %m");
 			exit(1);
 		}
-
-		fs_current->qfpathname = strdup(qfpathname);
-		if (fs_current->qfpathname == NULL) {
-			syslog(LOG_ERR, "can't strdup: %m");
-			exit(1);
-		}
 
+		if (uqfpathname) {
+			fs_current->uqfpathname = strdup(uqfpathname);
+			if (fs_current->uqfpathname == NULL) {
+				syslog(LOG_ERR, "can't strdup: %m");
+				exit(1);
+			}
+		} else
+			fs_current->uqfpathname = NULL;
+		if (gqfpathname) {
+			fs_current->gqfpathname = strdup(gqfpathname);
+			if (fs_current->gqfpathname == NULL) {
+				syslog(LOG_ERR, "can't strdup: %m");
+				exit(1);
+			}
+		} else
+			fs_current->gqfpathname = NULL;
 		stat(fs->fs_file, &st);
 		fs_current->st_dev = st.st_dev;
 
@@ -258,16 +320,17 @@
  * Return 0 if fail, 1 otherwise
  */
 int
-getfsquota(long id, char *path, struct dqblk *dqblk)
+getfsquota(int type, long id, char *path, struct dqblk *dqblk)
 {
 	struct stat st_path;
 	struct fs_stat *fs;
 	int	qcmd, fd, ret = 0;
+	char *filename;
 
 	if (stat(path, &st_path) < 0)
 		return (0);
 
-	qcmd = QCMD(Q_GETQUOTA, USRQUOTA);
+	qcmd = QCMD(Q_GETQUOTA, type == RQUOTA_USRQUOTA ? USRQUOTA : GRPQUOTA);
 
 	for (fs = fs_begin; fs != NULL; fs = fs->fs_next) {
 		/* where the device is the same as path */
@@ -277,16 +340,18 @@
 		/* find the specified filesystem. get and return quota */
 		if (quotactl(fs->fs_file, qcmd, id, dqblk) == 0)
 			return (1);
-
-		if ((fd = open(fs->qfpathname, O_RDONLY)) < 0) {
-			syslog(LOG_WARNING, "open error: %s: %m",
-			    fs->qfpathname);
+		filename = (type == RQUOTA_USRQUOTA) ?
+		    fs->uqfpathname : fs->gqfpathname;
+		if (filename == NULL)
+			return 0;
+		if ((fd = open(filename, O_RDONLY)) < 0) {
+			syslog(LOG_WARNING, "open error: %s: %m", filename);
 			return (0);
 		}
 		if (lseek(fd, (off_t)(id * sizeof(struct dqblk)), SEEK_SET)
 		    == (off_t)-1) {
 			close(fd);
-			return (1);
+			return (0);
 		}
 		switch (read(fd, dqblk, sizeof(struct dqblk))) {
 		case 0:
@@ -301,8 +366,7 @@
 			ret = 1;
 			break;
 		default:	/* ERROR */
-			syslog(LOG_WARNING, "read error: %s: %m",
-			    fs->qfpathname);
+			syslog(LOG_WARNING, "read error: %s: %m", filename);
 			close(fd);
 			return (0);
 		}
@@ -316,33 +380,48 @@
  * Comes from quota.c, NetBSD 0.9
  */
 int
-hasquota(struct fstab *fs, char **qfnamep)
+hasquota(struct fstab *fs, char **uqfnamep, char **gqfnamep)
 {
-	static char initname, usrname[100];
-	static char buf[MAXPATHLEN];
+	static char initname=0, usrname[100], grpname[100];
+	static char buf[MAXPATHLEN], ubuf[MAXPATHLEN], gbuf[MAXPATHLEN];
 	char	*opt, *cp = NULL;
+	int ret = 0;
 
 	if (!initname) {
 		(void)snprintf(usrname, sizeof usrname, "%s%s",
 		    qfextension[USRQUOTA], QUOTAFILENAME);
-		initname = 1;
+		(void)snprintf(grpname, sizeof grpname, "%s%s",
+		    qfextension[GRPQUOTA], QUOTAFILENAME);
 	}
+
+	*uqfnamep = NULL;
+	*gqfnamep = NULL;
 	strncpy(buf, fs->fs_mntops, sizeof(buf) - 1);
 	buf[sizeof(buf) - 1] = '\0';
 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
 		if ((cp = strchr(opt, '=')))
 			*cp++ = '\0';
-		if (strcmp(opt, usrname) == 0)
-			break;
+		if (strcmp(opt, usrname) == 0) {
+			ret = 1;
+			if (cp)
+				*uqfnamep = cp;
+			else {
+				(void)snprintf(ubuf, sizeof ubuf, "%s/%s.%s",
+				    fs->fs_file, QUOTAFILENAME,
+				    qfextension[USRQUOTA]);
+				*uqfnamep = ubuf;
+			}
+		} else if (strcmp(opt, grpname) == 0) {
+			ret = 1;
+			if (cp)
+				*gqfnamep = cp;
+			else {
+				(void)snprintf(gbuf, sizeof gbuf, "%s/%s.%s",
+				    fs->fs_file, QUOTAFILENAME,
+				    qfextension[GRPQUOTA]);
+				*gqfnamep = gbuf;
+			}
+		}
 	}
-	if (!opt)
-		return (0);
-	if (cp) {
-		*qfnamep = cp;
-		return (1);
-	}
-	(void)snprintf(buf, sizeof buf, "%s/%s.%s", fs->fs_file, QUOTAFILENAME,
-	    qfextension[USRQUOTA]);
-	*qfnamep = buf;
-	return (1);
+	return (ret);
 }

--d6Gm4EdcadzBjdND--