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