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;
+}