Module Name:    src
Committed By:   dholland
Date:           Mon Feb 13 01:35:09 UTC 2012

Modified Files:
        src/distrib/sets/lists/base: mi
        src/distrib/sets/lists/comp: mi
        src/distrib/sets/lists/man: mi
        src/usr.sbin: Makefile
        src/usr.sbin/repquota: Makefile repquota.8 repquota.c
Added Files:
        src/usr.sbin/quotarestore: Makefile quotarestore.8 quotarestore.c

Log Message:
Add quotadump(8), which is a link to repquota, and quotarestore(8),
which is new. Includes man page updates. Approved by releng for
freeze.


To generate a diff of this commit:
cvs rdiff -u -r1.981 -r1.982 src/distrib/sets/lists/base/mi
cvs rdiff -u -r1.1734 -r1.1735 src/distrib/sets/lists/comp/mi
cvs rdiff -u -r1.1376 -r1.1377 src/distrib/sets/lists/man/mi
cvs rdiff -u -r1.261 -r1.262 src/usr.sbin/Makefile
cvs rdiff -u -r0 -r1.1 src/usr.sbin/quotarestore/Makefile \
    src/usr.sbin/quotarestore/quotarestore.8 \
    src/usr.sbin/quotarestore/quotarestore.c
cvs rdiff -u -r1.9 -r1.10 src/usr.sbin/repquota/Makefile
cvs rdiff -u -r1.13 -r1.14 src/usr.sbin/repquota/repquota.8
cvs rdiff -u -r1.42 -r1.43 src/usr.sbin/repquota/repquota.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/distrib/sets/lists/base/mi
diff -u src/distrib/sets/lists/base/mi:1.981 src/distrib/sets/lists/base/mi:1.982
--- src/distrib/sets/lists/base/mi:1.981	Tue Feb  7 19:13:25 2012
+++ src/distrib/sets/lists/base/mi	Mon Feb 13 01:35:06 2012
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.981 2012/02/07 19:13:25 joerg Exp $
+# $NetBSD: mi,v 1.982 2012/02/13 01:35:06 dholland Exp $
 #
 # Note:	Don't delete entries from here - mark them as "obsolete" instead,
 #	unless otherwise stated below.
@@ -1390,8 +1390,10 @@
 ./usr/sbin/quot					base-sysutil-bin
 ./usr/sbin/quotacheck				base-sysutil-bin
 ./usr/sbin/quotactl				base-sysutil-bin
+./usr/sbin/quotadump				base-sysutil-bin
 ./usr/sbin/quotaoff				base-sysutil-bin
 ./usr/sbin/quotaon				base-sysutil-bin
+./usr/sbin/quotarestore				base-sysutil-bin
 ./usr/sbin/racoon				base-netutil-bin	crypto
 ./usr/sbin/racoonctl				base-netutil-bin	crypto
 ./usr/sbin/rarpd				base-bootserver-bin

Index: src/distrib/sets/lists/comp/mi
diff -u src/distrib/sets/lists/comp/mi:1.1734 src/distrib/sets/lists/comp/mi:1.1735
--- src/distrib/sets/lists/comp/mi:1.1734	Mon Feb 13 01:24:01 2012
+++ src/distrib/sets/lists/comp/mi	Mon Feb 13 01:35:06 2012
@@ -1,4 +1,4 @@
-#	$NetBSD: mi,v 1.1734 2012/02/13 01:24:01 dholland Exp $
+#	$NetBSD: mi,v 1.1735 2012/02/13 01:35:06 dholland Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -4230,7 +4230,9 @@
 ./usr/libdata/debug/usr/sbin/quot.debug		comp-sysutil-debug	debug
 ./usr/libdata/debug/usr/sbin/quotacheck.debug	comp-sysutil-debug	debug
 ./usr/libdata/debug/usr/sbin/quotactl.debug	comp-sysutil-debug	debug
+./usr/libdata/debug/usr/sbin/quotadump.debug	comp-sysutil-debug	debug
 ./usr/libdata/debug/usr/sbin/quotaon.debug	comp-sysutil-debug	debug
+./usr/libdata/debug/usr/sbin/quotarestore.debug	comp-sysutil-debug	debug
 ./usr/libdata/debug/usr/sbin/racoon.debug	comp-netutil-debug	crypto,debug
 ./usr/libdata/debug/usr/sbin/racoonctl.debug	comp-netutil-debug	crypto,debug
 ./usr/libdata/debug/usr/sbin/rarpd.debug	comp-bootserver-debug	debug

Index: src/distrib/sets/lists/man/mi
diff -u src/distrib/sets/lists/man/mi:1.1376 src/distrib/sets/lists/man/mi:1.1377
--- src/distrib/sets/lists/man/mi:1.1376	Tue Feb  7 19:13:29 2012
+++ src/distrib/sets/lists/man/mi	Mon Feb 13 01:35:08 2012
@@ -1,4 +1,4 @@
-# $NetBSD: mi,v 1.1376 2012/02/07 19:13:29 joerg Exp $
+# $NetBSD: mi,v 1.1377 2012/02/13 01:35:08 dholland Exp $
 #
 # Note: don't delete entries from here - mark them as "obsolete" instead.
 #
@@ -2699,8 +2699,10 @@
 ./usr/share/man/cat8/quot.0			man-sysutil-catman	.cat
 ./usr/share/man/cat8/quotacheck.0		man-sysutil-catman	.cat
 ./usr/share/man/cat8/quotactl.0			man-sysutil-catman	.cat
+./usr/share/man/cat8/quotadump.0		man-sysutil-catman	.cat
 ./usr/share/man/cat8/quotaoff.0			man-sysutil-catman	.cat
 ./usr/share/man/cat8/quotaon.0			man-sysutil-catman	.cat
+./usr/share/man/cat8/quotarestore.0		man-sysutil-catman	.cat
 ./usr/share/man/cat8/racoon.0			man-netutil-catman	crypto,.cat
 ./usr/share/man/cat8/racoonctl.0		man-netutil-catman	crypto,.cat
 ./usr/share/man/cat8/raidctl.0			man-sysutil-catman	.cat
@@ -5311,8 +5313,10 @@
 ./usr/share/man/html8/quot.html			man-sysutil-htmlman	html
 ./usr/share/man/html8/quotacheck.html		man-sysutil-htmlman	html
 ./usr/share/man/html8/quotactl.html		man-sysutil-htmlman	html
+./usr/share/man/html8/quotadump.html		man-sysutil-htmlman	html
 ./usr/share/man/html8/quotaoff.html		man-sysutil-htmlman	html
 ./usr/share/man/html8/quotaon.html		man-sysutil-htmlman	html
+./usr/share/man/html8/quotarestore.html		man-sysutil-htmlman	html
 ./usr/share/man/html8/racoon.html		man-netutil-htmlman	crypto,html
 ./usr/share/man/html8/racoonctl.html		man-netutil-htmlman	crypto,html
 ./usr/share/man/html8/raidctl.html		man-sysutil-htmlman	html
@@ -8200,8 +8204,10 @@
 ./usr/share/man/man8/quot.8			man-sysutil-man		.man
 ./usr/share/man/man8/quotacheck.8		man-sysutil-man		.man
 ./usr/share/man/man8/quotactl.8			man-sysutil-man		.man
+./usr/share/man/man8/quotadump.8		man-sysutil-man		.man
 ./usr/share/man/man8/quotaoff.8			man-sysutil-man		.man
 ./usr/share/man/man8/quotaon.8			man-sysutil-man		.man
+./usr/share/man/man8/quotarestore.8		man-sysutil-man		.man
 ./usr/share/man/man8/racoon.8			man-netutil-man		crypto,.man
 ./usr/share/man/man8/racoonctl.8		man-netutil-man		crypto,.man
 ./usr/share/man/man8/raidctl.8			man-sysutil-man		.man

Index: src/usr.sbin/Makefile
diff -u src/usr.sbin/Makefile:1.261 src/usr.sbin/Makefile:1.262
--- src/usr.sbin/Makefile:1.261	Tue Feb  7 19:13:32 2012
+++ src/usr.sbin/Makefile	Mon Feb 13 01:35:08 2012
@@ -1,4 +1,4 @@
-#	$NetBSD: Makefile,v 1.261 2012/02/07 19:13:32 joerg Exp $
+#	$NetBSD: Makefile,v 1.262 2012/02/13 01:35:08 dholland Exp $
 #	from: @(#)Makefile	5.20 (Berkeley) 6/12/93
 
 .include <bsd.own.mk>
@@ -21,7 +21,7 @@ SUBDIR=	ac accton acpitools altq apm apm
 	ofctl \
 	paxctl pcictl perfused pppd psrset pstat pwd_mkdb postinstall \
 	powerd puffs \
-	quot quotacheck quotactl quotaon \
+	quot quotacheck quotactl quotaon quotarestore \
 	rarpd rbootd rdate repquota rmt rpc.bootparamd rpc.lockd \
 	rpc.pcnfsd rpc.statd rpcbind rwhod \
 	sa screenblank sdpd services_mkdb sesd schedctl sliplogin spray \

Index: src/usr.sbin/repquota/Makefile
diff -u src/usr.sbin/repquota/Makefile:1.9 src/usr.sbin/repquota/Makefile:1.10
--- src/usr.sbin/repquota/Makefile:1.9	Wed Feb  1 17:53:01 2012
+++ src/usr.sbin/repquota/Makefile	Mon Feb 13 01:35:09 2012
@@ -1,5 +1,5 @@
 #	from: @(#)Makefile	8.1 (Berkeley) 6/6/93
-#	$NetBSD: Makefile,v 1.9 2012/02/01 17:53:01 dholland Exp $
+#	$NetBSD: Makefile,v 1.10 2012/02/13 01:35:09 dholland Exp $
 
 WARNS ?= 4
 .include <bsd.own.mk>
@@ -7,6 +7,9 @@ PROG=	repquota
 SRCS=	repquota.c 
 MAN=	repquota.8
 
+LINKS+=	${BINDIR}/repquota ${BINDIR}/quotadump
+MLINKS+=repquota.8 quotadump.8
+
 CPPFLAGS+=-I${NETBSDSRCDIR}/sys -I${NETBSDSRCDIR}/usr.bin/quota
 DPADD=	${LIBQUOTA} ${LIBRPCSVC}
 LDADD=	-lquota -lrpcsvc

Index: src/usr.sbin/repquota/repquota.8
diff -u src/usr.sbin/repquota/repquota.8:1.13 src/usr.sbin/repquota/repquota.8:1.14
--- src/usr.sbin/repquota/repquota.8:1.13	Wed Feb  1 09:30:01 2012
+++ src/usr.sbin/repquota/repquota.8	Mon Feb 13 01:35:09 2012
@@ -29,7 +29,7 @@
 .\" SUCH DAMAGE.
 .\"
 .\"     from: @(#)repquota.8	8.1 (Berkeley) 6/6/93
-.\"	$NetBSD: repquota.8,v 1.13 2012/02/01 09:30:01 wiz Exp $
+.\"	$NetBSD: repquota.8,v 1.14 2012/02/13 01:35:09 dholland Exp $
 .\"
 .Dd February 2, 2012
 .Dt REPQUOTA 8
@@ -48,6 +48,9 @@
 .Fl x
 .Op Fl Dgu
 .Ar file-system
+.Nm quotadump
+.Op Fl Dgu
+.Ar file-system
 .Sh DESCRIPTION
 .Nm
 prints a summary of the disk usage and quotas for the
@@ -71,11 +74,16 @@ group and user quotas if they exist).
 Print a header line before printing each file system quotas.
 Print all exiting quotas, including those whose current usage is 0.
 .It Fl x
-export file system quota in a tabular dump format suitable for
+Export file system quota data in a tabular dump format suitable for
 .Xr quotarestore 8 .
 A single file system should be specified.
 .El
 .Pp
+If invoked as
+.Nm quotadump
+the behavior is the same as
+.Nm repquota Fl x .
+.Pp
 For each user or group, the current
 number files and amount of space (in kilobytes, unless the
 .Fl h

Index: src/usr.sbin/repquota/repquota.c
diff -u src/usr.sbin/repquota/repquota.c:1.42 src/usr.sbin/repquota/repquota.c:1.43
--- src/usr.sbin/repquota/repquota.c:1.42	Sun Feb  5 14:14:44 2012
+++ src/usr.sbin/repquota/repquota.c	Mon Feb 13 01:35:09 2012
@@ -1,4 +1,4 @@
-/*	$NetBSD: repquota.c,v 1.42 2012/02/05 14:14:44 dholland Exp $	*/
+/*	$NetBSD: repquota.c,v 1.43 2012/02/13 01:35:09 dholland Exp $	*/
 
 /*
  * Copyright (c) 1980, 1990, 1993
@@ -42,7 +42,7 @@ __COPYRIGHT("@(#) Copyright (c) 1980, 19
 #if 0
 static char sccsid[] = "@(#)repquota.c	8.2 (Berkeley) 11/22/94";
 #else
-__RCSID("$NetBSD: repquota.c,v 1.42 2012/02/05 14:14:44 dholland Exp $");
+__RCSID("$NetBSD: repquota.c,v 1.43 2012/02/13 01:35:09 dholland Exp $");
 #endif
 #endif /* not lint */
 
@@ -129,6 +129,10 @@ main(int argc, char **argv)
 	int nfst;
 	struct quotahandle *qh;
 
+	if (!strcmp(getprogname(), "quotadump")) {
+		xflag = 1;
+	}
+
 	while ((ch = getopt(argc, argv, "Daguhvx")) != -1) {
 		switch(ch) {
 		case 'a':
@@ -431,11 +435,11 @@ exportquotas(void)
 		if (valid[idtype] == 0)
 			continue;
 
-		printf("%s default blocks  ", repquota_idtype_names[idtype]);
+		printf("%s default block  ", repquota_idtype_names[idtype]);
 		exportquotaval(&defaultqv[idtype][QUOTA_OBJTYPE_BLOCKS]);
 		printf("\n");
 			
-		printf("%s default files  ", repquota_idtype_names[idtype]);
+		printf("%s default file  ", repquota_idtype_names[idtype]);
 		exportquotaval(&defaultqv[idtype][QUOTA_OBJTYPE_FILES]);
 		printf("\n");
 
@@ -444,12 +448,12 @@ exportquotas(void)
 			if (fup == 0)
 				continue;
 
-			printf("%s %u blocks  ", repquota_idtype_names[idtype],
+			printf("%s %u block  ", repquota_idtype_names[idtype],
 			       id);
 			exportquotaval(&fup->fu_qv[QUOTA_OBJTYPE_BLOCKS]);
 			printf("\n");
 
-			printf("%s %u files  ", repquota_idtype_names[idtype],
+			printf("%s %u file  ", repquota_idtype_names[idtype],
 			       id);
 			exportquotaval(&fup->fu_qv[QUOTA_OBJTYPE_FILES]);
 			printf("\n");

Added files:

Index: src/usr.sbin/quotarestore/Makefile
diff -u /dev/null src/usr.sbin/quotarestore/Makefile:1.1
--- /dev/null	Mon Feb 13 01:35:09 2012
+++ src/usr.sbin/quotarestore/Makefile	Mon Feb 13 01:35:09 2012
@@ -0,0 +1,10 @@
+#	$NetBSD: Makefile,v 1.1 2012/02/13 01:35:09 dholland Exp $
+
+PROG=	quotarestore
+SRCS=	quotarestore.c 
+MAN=	quotarestore.8
+
+DPADD=	${LIBQUOTA} ${LIBRPCSVC}
+LDADD=	-lquota -lrpcsvc
+
+.include <bsd.prog.mk>
Index: src/usr.sbin/quotarestore/quotarestore.8
diff -u /dev/null src/usr.sbin/quotarestore/quotarestore.8:1.1
--- /dev/null	Mon Feb 13 01:35:09 2012
+++ src/usr.sbin/quotarestore/quotarestore.8	Mon Feb 13 01:35:09 2012
@@ -0,0 +1,76 @@
+.\"	$NetBSD: quotarestore.8,v 1.1 2012/02/13 01:35:09 dholland Exp $
+.\"
+.\" Copyright (c) 2012 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by David A. Holland.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd February 11, 2012
+.Dt QUOTARESTORE 8
+.Os
+.Sh NAME
+.Nm quotarestore
+.Nd restore dumped quota information to a file system volume
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Ar file-system
+.Op Ar dump-file
+.Sh DESCRIPTION
+The
+.Nm
+program restores dumped quota information to a file system.
+The file
+.Ar dump-file
+should be in the format produced with
+.Xr quotadump 8 .
+The quotas, expiration times, and configured grace times listed in the
+dump file are loaded into the named file system.
+The
+.Ar file-system
+argument should be a file or directory on the
+.Pq mounted
+file system, not a device special file.
+.Pp
+If the
+.Fl d
+option is given, quota entries on the file system that are not
+mentioned in the dump file will be deleted.
+Otherwise, they are left alone.
+.Pp
+If the
+.Ar dump-file
+is not specified, standard input is used.
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr libquota 3 ,
+.Xr fstab 5 ,
+.Xr edquota 8 ,
+.Xr quotadump 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Nx 6.0 .
Index: src/usr.sbin/quotarestore/quotarestore.c
diff -u /dev/null src/usr.sbin/quotarestore/quotarestore.c:1.1
--- /dev/null	Mon Feb 13 01:35:09 2012
+++ src/usr.sbin/quotarestore/quotarestore.c	Mon Feb 13 01:35:09 2012
@@ -0,0 +1,577 @@
+/*	$NetBSD: quotarestore.c,v 1.1 2012/02/13 01:35:09 dholland Exp $	*/
+/*-
+ * Copyright (c) 2012 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by David A. Holland.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__RCSID("$NetBSD: quotarestore.c,v 1.1 2012/02/13 01:35:09 dholland Exp $");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <getopt.h>
+#include <limits.h>
+#include <errno.h>
+#include <err.h>
+
+#include <quota.h>
+
+static const char const ws[] = " \t\r\n";
+
+static char **idtypenames;
+static unsigned numidtypes;
+
+static char **objtypenames;
+static unsigned numobjtypes;
+
+////////////////////////////////////////////////////////////
+// table of quota keys
+
+struct qklist {
+	struct quotakey *keys;
+	unsigned num, max;
+};
+
+static struct qklist *
+qklist_create(void)
+{
+	struct qklist *l;
+
+	l = malloc(sizeof(*l));
+	if (l == NULL) {
+		err(EXIT_FAILURE, "malloc");
+	}
+	l->keys = 0;
+	l->num = 0;
+	l->max = 0;
+	return l;
+}
+
+static void
+qklist_destroy(struct qklist *l)
+{
+	free(l->keys);
+	free(l);
+}
+
+static void
+qklist_truncate(struct qklist *l)
+{
+	l->num = 0;
+}
+
+static void
+qklist_add(struct qklist *l, const struct quotakey *qk)
+{
+	assert(l->num <= l->max);
+	if (l->num == l->max) {
+		l->max = l->max ? l->max * 2 : 4;
+		l->keys = realloc(l->keys, l->max * sizeof(l->keys[0]));
+		if (l->keys == NULL) {
+			err(EXIT_FAILURE, "realloc");
+		}
+	}
+	l->keys[l->num++] = *qk;
+}
+
+static int
+qk_compare(const void *av, const void *bv)
+{
+	const struct quotakey *a = av;
+	const struct quotakey *b = bv;
+
+	if (a->qk_idtype < b->qk_idtype) {
+		return -1;
+	}
+	if (a->qk_idtype > b->qk_idtype) {
+		return 1;
+	}
+
+	if (a->qk_id < b->qk_id) {
+		return -1;
+	}
+	if (a->qk_id > b->qk_id) {
+		return 1;
+	}
+
+	if (a->qk_objtype < b->qk_objtype) {
+		return -1;
+	}
+	if (a->qk_objtype > b->qk_objtype) {
+		return 1;
+	}
+
+	return 0;
+}
+
+static void
+qklist_sort(struct qklist *l)
+{
+	qsort(l->keys, l->num, sizeof(l->keys[0]), qk_compare);
+}
+
+static int
+qklist_present(struct qklist *l, const struct quotakey *key)
+{
+	void *p;
+
+	p = bsearch(key, l->keys, l->num, sizeof(l->keys[0]), qk_compare);
+	return p != NULL;
+}
+
+////////////////////////////////////////////////////////////
+// name tables and string conversion
+
+static void
+maketables(struct quotahandle *qh)
+{
+	unsigned i;
+
+	numidtypes = quota_getnumidtypes(qh);
+	idtypenames = malloc(numidtypes * sizeof(idtypenames[0]));
+	if (idtypenames == NULL) {
+		err(EXIT_FAILURE, "malloc");
+	}
+
+	for (i=0; i<numidtypes; i++) {
+		idtypenames[i] = strdup(quota_idtype_getname(qh, i));
+		if (idtypenames[i] == NULL) {
+			err(EXIT_FAILURE, "strdup");
+		}
+	}
+
+	numobjtypes = quota_getnumobjtypes(qh);
+	objtypenames = malloc(numobjtypes * sizeof(objtypenames[0]));
+	if (objtypenames == NULL) {
+		err(EXIT_FAILURE, "malloc");
+	}
+
+	for (i=0; i<numobjtypes; i++) {
+		objtypenames[i] = strdup(quota_objtype_getname(qh, i));
+		if (objtypenames[i] == NULL) {
+			err(EXIT_FAILURE, "strdup");
+		}
+	}
+}
+
+static int
+getidtype(const char *name, int *ret)
+{
+	unsigned i;
+
+	for (i=0; i<numidtypes; i++) {
+		if (!strcmp(name, idtypenames[i])) {
+			*ret = i;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static int
+getid(const char *name, int idtype, id_t *ret)
+{
+	unsigned long val;
+	char *s;
+
+	if (!strcmp(name, "default")) {
+		*ret = QUOTA_DEFAULTID;
+		return 0;
+	}
+	errno = 0;
+	val = strtoul(name, &s, 10);
+	if (errno || *s != 0) {
+		return -1;
+	}
+	if (idtype == QUOTA_IDTYPE_USER && val > UID_MAX) {
+		return -1;
+	}
+	if (idtype == QUOTA_IDTYPE_GROUP && val > GID_MAX) {
+		return -1;
+	}
+	*ret = val;
+	return 0;
+}
+
+static int
+getobjtype(const char *name, int *ret)
+{
+	unsigned i;
+	size_t len;
+
+	for (i=0; i<numobjtypes; i++) {
+		if (!strcmp(name, objtypenames[i])) {
+			*ret = i;
+			return 0;
+		}
+	}
+
+	/*
+	 * Sigh. Some early committed versions of quotadump used
+	 * "blocks" and "files" instead of "block" and "file".
+	 */
+	len = strlen(name);
+	if (len == 0) {
+		return -1;
+	}
+	for (i=0; i<numobjtypes; i++) {
+		if (name[len-1] == 's' &&
+		    !strncmp(name, objtypenames[i], len-1)) {
+			*ret = i;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+static int
+getlimit(const char *name, uint64_t *ret)
+{
+	unsigned long long val;
+	char *s;
+
+	if (!strcmp(name, "-")) {
+		*ret = QUOTA_NOLIMIT;
+		return 0;
+	}
+	errno = 0;
+	val = strtoull(name, &s, 10);
+	if (errno || *s != 0) {
+		return -1;
+	}
+	*ret = val;
+	return 0;
+}
+
+static int
+gettime(const char *name, int64_t *ret)
+{
+	long long val;
+	char *s;
+
+	if (!strcmp(name, "-")) {
+		*ret = QUOTA_NOTIME;
+		return 0;
+	}
+	errno = 0;
+	val = strtoll(name, &s, 10);
+	if (errno || *s != 0 || val < 0) {
+		return -1;
+	}
+	*ret = val;
+	return 0;
+}
+
+////////////////////////////////////////////////////////////
+// parsing tools
+
+static int
+isws(int ch)
+{
+	return ch != '\0' && strchr(ws, ch) != NULL;
+}
+
+static char *
+skipws(char *s)
+{
+	while (isws(*s)) {
+		s++;
+	}
+	return s;
+}
+
+////////////////////////////////////////////////////////////
+// deletion of extra records
+
+static void
+scankeys(struct quotahandle *qh, struct qklist *seenkeys,
+	struct qklist *dropkeys)
+{
+	struct quotacursor *qc;
+#define MAX 8
+	struct quotakey keys[MAX];
+	struct quotaval vals[MAX];
+	int num, i;
+
+	qc = quota_opencursor(qh);
+	if (qc == NULL) {
+		err(EXIT_FAILURE, "quota_opencursor");
+	}
+
+	while (quotacursor_atend(qc) == 0) {
+		num = quotacursor_getn(qc, keys, vals, MAX);
+		if (num < 0) {
+			if (errno == EDEADLK) {
+				quotacursor_rewind(qc);
+				qklist_truncate(dropkeys);
+				continue;
+			}
+			err(EXIT_FAILURE, "quotacursor_getn");
+		}
+		for (i=0; i<num; i++) {
+			if (qklist_present(seenkeys, &keys[i]) == 0) {
+				qklist_add(dropkeys, &keys[i]);
+			}
+		}
+	}
+
+	quotacursor_close(qc);
+}
+
+static void
+purge(struct quotahandle *qh, struct qklist *dropkeys)
+{
+	unsigned i;
+
+	for (i=0; i<dropkeys->num; i++) {
+		if (quota_delete(qh, &dropkeys->keys[i])) {
+			err(EXIT_FAILURE, "quota_delete");
+		}
+	}
+}
+
+////////////////////////////////////////////////////////////
+// dumpfile reader
+
+static void
+readdumpfile(struct quotahandle *qh, FILE *f, const char *path,
+	     struct qklist *seenkeys)
+{
+	char buf[128];
+	unsigned lineno;
+	unsigned long version;
+	char *s;
+	char *fields[8];
+	unsigned num;
+	char *x;
+	struct quotakey key;
+	struct quotaval val;
+	int ch;
+
+	lineno = 0;
+	if (fgets(buf, sizeof(buf), f) == NULL) {
+		errx(EXIT_FAILURE, "%s: EOF before quotadump header", path);
+	}
+	lineno++;
+	if (strncmp(buf, "@format netbsd-quota-dump v", 27) != 0) {
+		errx(EXIT_FAILURE, "%s: Missing quotadump header", path);
+	}
+	s = buf+27;
+	errno = 0;
+	version = strtoul(s, &s, 10);
+	if (errno) {
+		errx(EXIT_FAILURE, "%s: Corrupted quotadump header", path);
+	}
+	s = skipws(s);
+	if (*s != '\0') {
+		errx(EXIT_FAILURE, "%s: Trash after quotadump header", path);
+	}
+
+	switch (version) {
+	    case 1: break;
+	    default:
+		errx(EXIT_FAILURE, "%s: Unsupported quotadump version %lu",
+		     path, version);
+	}
+
+	while (fgets(buf, sizeof(buf), f)) {
+		lineno++;
+		if (buf[0] == '#') {
+			continue;
+		}
+		if (!strncmp(buf, "@end", 4)) {
+			s = skipws(buf+4);
+			if (*s != '\0') {
+				errx(EXIT_FAILURE, "%s:%u: Invalid @end tag",
+				     path, lineno);
+			}
+			break;
+		}
+
+		num = 0;
+		for (s = strtok_r(buf, ws, &x);
+		     s != NULL;
+		     s = strtok_r(NULL, ws, &x)) {
+			if (num < 8) {
+				fields[num++] = s;
+			} else {
+				errx(EXIT_FAILURE, "%s:%u: Too many fields",
+				     path, lineno);
+			}
+		}
+		if (num < 8) {
+			errx(EXIT_FAILURE, "%s:%u: Not enough fields",
+			     path, lineno);
+		}
+
+		if (getidtype(fields[0], &key.qk_idtype)) {
+			errx(EXIT_FAILURE, "%s:%u: Invalid/unknown ID type %s",
+			     path, lineno, fields[0]);
+		}
+		if (getid(fields[1], key.qk_idtype, &key.qk_id)) {
+			errx(EXIT_FAILURE, "%s:%u: Invalid ID number %s",
+			     path, lineno, fields[1]);
+		}
+		if (getobjtype(fields[2], &key.qk_objtype)) {
+			errx(EXIT_FAILURE, "%s:%u: Invalid/unknown object "
+			     "type %s",
+			     path, lineno, fields[2]);
+		}
+
+		if (getlimit(fields[3], &val.qv_hardlimit)) {
+			errx(EXIT_FAILURE, "%s:%u: Invalid hard limit %s",
+			     path, lineno, fields[3]);
+		}
+		if (getlimit(fields[4], &val.qv_softlimit)) {
+			errx(EXIT_FAILURE, "%s:%u: Invalid soft limit %s",
+			     path, lineno, fields[4]);
+		}
+		if (getlimit(fields[5], &val.qv_usage)) {
+			/*
+			 * Make this nonfatal as it'll be ignored by
+			 * quota_put() anyway.
+			 */
+			warnx("%s:%u: Invalid current usage %s",
+			     path, lineno, fields[5]);
+			val.qv_usage = 0;
+		}
+		if (gettime(fields[6], &val.qv_expiretime)) {
+			errx(EXIT_FAILURE, "%s:%u: Invalid expire time %s",
+			     path, lineno, fields[6]);
+		}
+		if (gettime(fields[7], &val.qv_grace)) {
+			errx(EXIT_FAILURE, "%s:%u: Invalid grace period %s",
+			     path, lineno, fields[7]);
+		}
+
+		if (quota_put(qh, &key, &val)) {
+			err(EXIT_FAILURE, "%s:%u: quota_put", path, lineno);
+		}
+
+		if (seenkeys != NULL) {
+			qklist_add(seenkeys, &key);
+		}
+	}
+	if (feof(f)) {
+		return;
+	}
+	if (ferror(f)) {
+		errx(EXIT_FAILURE, "%s: Read error", path);
+	}
+	/* not at EOF, not an error... what's left? */
+	while (1) {
+		ch = fgetc(f);
+		if (ch == EOF)
+			break;
+		if (isws(ch)) {
+			continue;
+		}
+		warnx("%s:%u: Trash after @end tag", path, lineno);
+	}
+}
+
+////////////////////////////////////////////////////////////
+// top level control logic
+
+static void
+usage(void)
+{
+	fprintf(stderr, "usage: %s [-d] volume [dump-file]\n",
+		getprogname());
+	exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int ch;
+	FILE *f;
+	struct quotahandle *qh;
+
+	int dflag = 0;
+	const char *volume = NULL;
+	const char *dumpfile = NULL;
+
+	while ((ch = getopt(argc, argv, "d")) != -1) {
+		switch (ch) {
+		    case 'd': dflag = 1; break;
+		    default: usage(); break;
+		}
+	}
+
+	if (optind >= argc) {
+		usage();
+	}
+	volume = argv[optind++];
+	if (optind < argc) {
+		dumpfile = argv[optind++];
+	}
+	if (optind < argc) {
+		usage();
+	}
+
+	qh = quota_open(volume);
+	if (qh == NULL) {
+		err(EXIT_FAILURE, "quota_open: %s", volume);
+	}
+	if (dumpfile != NULL) {
+		f = fopen(dumpfile, "r");
+		if (f == NULL) {
+			err(EXIT_FAILURE, "%s", dumpfile);
+		}
+	} else {
+		f = stdin;
+		dumpfile = "<stdin>";
+	}
+
+	maketables(qh);
+
+	if (dflag) {
+		struct qklist *seenkeys, *dropkeys;
+
+		seenkeys = qklist_create();
+		dropkeys = qklist_create();
+
+		readdumpfile(qh, f, dumpfile, seenkeys);
+		qklist_sort(seenkeys);
+		scankeys(qh, seenkeys, dropkeys);
+		purge(qh, dropkeys);
+
+		qklist_destroy(dropkeys);
+		qklist_destroy(seenkeys);
+	} else {
+		readdumpfile(qh, f, dumpfile, NULL);
+	}
+
+	if (f != stdin) {
+		fclose(f);
+	}
+	quota_close(qh);
+	return EXIT_SUCCESS;
+}

Reply via email to