1*876002daSjoerg /*	$NetBSD: quotarestore.c,v 1.3 2012/09/13 21:44:50 joerg Exp $	*/
2c6434ac7Sdholland /*-
3c6434ac7Sdholland  * Copyright (c) 2012 The NetBSD Foundation, Inc.
4c6434ac7Sdholland  * All rights reserved.
5c6434ac7Sdholland  *
6c6434ac7Sdholland  * This code is derived from software contributed to The NetBSD Foundation
7c6434ac7Sdholland  * by David A. Holland.
8c6434ac7Sdholland  *
9c6434ac7Sdholland  * Redistribution and use in source and binary forms, with or without
10c6434ac7Sdholland  * modification, are permitted provided that the following conditions
11c6434ac7Sdholland  * are met:
12c6434ac7Sdholland  * 1. Redistributions of source code must retain the above copyright
13c6434ac7Sdholland  *    notice, this list of conditions and the following disclaimer.
14c6434ac7Sdholland  * 2. Redistributions in binary form must reproduce the above copyright
15c6434ac7Sdholland  *    notice, this list of conditions and the following disclaimer in the
16c6434ac7Sdholland  *    documentation and/or other materials provided with the distribution.
17c6434ac7Sdholland  *
18c6434ac7Sdholland  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
19c6434ac7Sdholland  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
20c6434ac7Sdholland  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21c6434ac7Sdholland  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
22c6434ac7Sdholland  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23c6434ac7Sdholland  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24c6434ac7Sdholland  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25c6434ac7Sdholland  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26c6434ac7Sdholland  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27c6434ac7Sdholland  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28c6434ac7Sdholland  * POSSIBILITY OF SUCH DAMAGE.
29c6434ac7Sdholland  */
30c6434ac7Sdholland 
31c6434ac7Sdholland #include <sys/cdefs.h>
32*876002daSjoerg __RCSID("$NetBSD: quotarestore.c,v 1.3 2012/09/13 21:44:50 joerg Exp $");
33c6434ac7Sdholland 
34c6434ac7Sdholland #include <stdio.h>
35c6434ac7Sdholland #include <stdlib.h>
36c6434ac7Sdholland #include <string.h>
37c6434ac7Sdholland #include <assert.h>
38c6434ac7Sdholland #include <getopt.h>
39c6434ac7Sdholland #include <limits.h>
40c6434ac7Sdholland #include <errno.h>
41c6434ac7Sdholland #include <err.h>
42c6434ac7Sdholland 
43c6434ac7Sdholland #include <quota.h>
44c6434ac7Sdholland 
45*876002daSjoerg static const char ws[] = " \t\r\n";
46c6434ac7Sdholland 
47c6434ac7Sdholland static char **idtypenames;
48c6434ac7Sdholland static unsigned numidtypes;
49c6434ac7Sdholland 
50c6434ac7Sdholland static char **objtypenames;
51c6434ac7Sdholland static unsigned numobjtypes;
52c6434ac7Sdholland 
53c6434ac7Sdholland ////////////////////////////////////////////////////////////
54c6434ac7Sdholland // table of quota keys
55c6434ac7Sdholland 
56c6434ac7Sdholland struct qklist {
57c6434ac7Sdholland 	struct quotakey *keys;
58c6434ac7Sdholland 	unsigned num, max;
59c6434ac7Sdholland };
60c6434ac7Sdholland 
61c6434ac7Sdholland static struct qklist *
qklist_create(void)62c6434ac7Sdholland qklist_create(void)
63c6434ac7Sdholland {
64c6434ac7Sdholland 	struct qklist *l;
65c6434ac7Sdholland 
66c6434ac7Sdholland 	l = malloc(sizeof(*l));
67c6434ac7Sdholland 	if (l == NULL) {
68c6434ac7Sdholland 		err(EXIT_FAILURE, "malloc");
69c6434ac7Sdholland 	}
70c6434ac7Sdholland 	l->keys = 0;
71c6434ac7Sdholland 	l->num = 0;
72c6434ac7Sdholland 	l->max = 0;
73c6434ac7Sdholland 	return l;
74c6434ac7Sdholland }
75c6434ac7Sdholland 
76c6434ac7Sdholland static void
qklist_destroy(struct qklist * l)77c6434ac7Sdholland qklist_destroy(struct qklist *l)
78c6434ac7Sdholland {
79c6434ac7Sdholland 	free(l->keys);
80c6434ac7Sdholland 	free(l);
81c6434ac7Sdholland }
82c6434ac7Sdholland 
83c6434ac7Sdholland static void
qklist_truncate(struct qklist * l)84c6434ac7Sdholland qklist_truncate(struct qklist *l)
85c6434ac7Sdholland {
86c6434ac7Sdholland 	l->num = 0;
87c6434ac7Sdholland }
88c6434ac7Sdholland 
89c6434ac7Sdholland static void
qklist_add(struct qklist * l,const struct quotakey * qk)90c6434ac7Sdholland qklist_add(struct qklist *l, const struct quotakey *qk)
91c6434ac7Sdholland {
92c6434ac7Sdholland 	assert(l->num <= l->max);
93c6434ac7Sdholland 	if (l->num == l->max) {
94c6434ac7Sdholland 		l->max = l->max ? l->max * 2 : 4;
95c6434ac7Sdholland 		l->keys = realloc(l->keys, l->max * sizeof(l->keys[0]));
96c6434ac7Sdholland 		if (l->keys == NULL) {
97c6434ac7Sdholland 			err(EXIT_FAILURE, "realloc");
98c6434ac7Sdholland 		}
99c6434ac7Sdholland 	}
100c6434ac7Sdholland 	l->keys[l->num++] = *qk;
101c6434ac7Sdholland }
102c6434ac7Sdholland 
103c6434ac7Sdholland static int
qk_compare(const void * av,const void * bv)104c6434ac7Sdholland qk_compare(const void *av, const void *bv)
105c6434ac7Sdholland {
106c6434ac7Sdholland 	const struct quotakey *a = av;
107c6434ac7Sdholland 	const struct quotakey *b = bv;
108c6434ac7Sdholland 
109c6434ac7Sdholland 	if (a->qk_idtype < b->qk_idtype) {
110c6434ac7Sdholland 		return -1;
111c6434ac7Sdholland 	}
112c6434ac7Sdholland 	if (a->qk_idtype > b->qk_idtype) {
113c6434ac7Sdholland 		return 1;
114c6434ac7Sdholland 	}
115c6434ac7Sdholland 
116c6434ac7Sdholland 	if (a->qk_id < b->qk_id) {
117c6434ac7Sdholland 		return -1;
118c6434ac7Sdholland 	}
119c6434ac7Sdholland 	if (a->qk_id > b->qk_id) {
120c6434ac7Sdholland 		return 1;
121c6434ac7Sdholland 	}
122c6434ac7Sdholland 
123c6434ac7Sdholland 	if (a->qk_objtype < b->qk_objtype) {
124c6434ac7Sdholland 		return -1;
125c6434ac7Sdholland 	}
126c6434ac7Sdholland 	if (a->qk_objtype > b->qk_objtype) {
127c6434ac7Sdholland 		return 1;
128c6434ac7Sdholland 	}
129c6434ac7Sdholland 
130c6434ac7Sdholland 	return 0;
131c6434ac7Sdholland }
132c6434ac7Sdholland 
133c6434ac7Sdholland static void
qklist_sort(struct qklist * l)134c6434ac7Sdholland qklist_sort(struct qklist *l)
135c6434ac7Sdholland {
136c6434ac7Sdholland 	qsort(l->keys, l->num, sizeof(l->keys[0]), qk_compare);
137c6434ac7Sdholland }
138c6434ac7Sdholland 
139c6434ac7Sdholland static int
qklist_present(struct qklist * l,const struct quotakey * key)140c6434ac7Sdholland qklist_present(struct qklist *l, const struct quotakey *key)
141c6434ac7Sdholland {
142c6434ac7Sdholland 	void *p;
143c6434ac7Sdholland 
144c6434ac7Sdholland 	p = bsearch(key, l->keys, l->num, sizeof(l->keys[0]), qk_compare);
145c6434ac7Sdholland 	return p != NULL;
146c6434ac7Sdholland }
147c6434ac7Sdholland 
148c6434ac7Sdholland ////////////////////////////////////////////////////////////
149c6434ac7Sdholland // name tables and string conversion
150c6434ac7Sdholland 
151c6434ac7Sdholland static void
maketables(struct quotahandle * qh)152c6434ac7Sdholland maketables(struct quotahandle *qh)
153c6434ac7Sdholland {
154c6434ac7Sdholland 	unsigned i;
155c6434ac7Sdholland 
156c6434ac7Sdholland 	numidtypes = quota_getnumidtypes(qh);
157c6434ac7Sdholland 	idtypenames = malloc(numidtypes * sizeof(idtypenames[0]));
158c6434ac7Sdholland 	if (idtypenames == NULL) {
159c6434ac7Sdholland 		err(EXIT_FAILURE, "malloc");
160c6434ac7Sdholland 	}
161c6434ac7Sdholland 
162c6434ac7Sdholland 	for (i=0; i<numidtypes; i++) {
163c6434ac7Sdholland 		idtypenames[i] = strdup(quota_idtype_getname(qh, i));
164c6434ac7Sdholland 		if (idtypenames[i] == NULL) {
165c6434ac7Sdholland 			err(EXIT_FAILURE, "strdup");
166c6434ac7Sdholland 		}
167c6434ac7Sdholland 	}
168c6434ac7Sdholland 
169c6434ac7Sdholland 	numobjtypes = quota_getnumobjtypes(qh);
170c6434ac7Sdholland 	objtypenames = malloc(numobjtypes * sizeof(objtypenames[0]));
171c6434ac7Sdholland 	if (objtypenames == NULL) {
172c6434ac7Sdholland 		err(EXIT_FAILURE, "malloc");
173c6434ac7Sdholland 	}
174c6434ac7Sdholland 
175c6434ac7Sdholland 	for (i=0; i<numobjtypes; i++) {
176c6434ac7Sdholland 		objtypenames[i] = strdup(quota_objtype_getname(qh, i));
177c6434ac7Sdholland 		if (objtypenames[i] == NULL) {
178c6434ac7Sdholland 			err(EXIT_FAILURE, "strdup");
179c6434ac7Sdholland 		}
180c6434ac7Sdholland 	}
181c6434ac7Sdholland }
182c6434ac7Sdholland 
183c6434ac7Sdholland static int
getidtype(const char * name,int * ret)184c6434ac7Sdholland getidtype(const char *name, int *ret)
185c6434ac7Sdholland {
186c6434ac7Sdholland 	unsigned i;
187c6434ac7Sdholland 
188c6434ac7Sdholland 	for (i=0; i<numidtypes; i++) {
189c6434ac7Sdholland 		if (!strcmp(name, idtypenames[i])) {
190c6434ac7Sdholland 			*ret = i;
191c6434ac7Sdholland 			return 0;
192c6434ac7Sdholland 		}
193c6434ac7Sdholland 	}
194c6434ac7Sdholland 	return -1;
195c6434ac7Sdholland }
196c6434ac7Sdholland 
197c6434ac7Sdholland static int
getid(const char * name,int idtype,id_t * ret)198c6434ac7Sdholland getid(const char *name, int idtype, id_t *ret)
199c6434ac7Sdholland {
200c6434ac7Sdholland 	unsigned long val;
201c6434ac7Sdholland 	char *s;
202c6434ac7Sdholland 
203c6434ac7Sdholland 	if (!strcmp(name, "default")) {
204c6434ac7Sdholland 		*ret = QUOTA_DEFAULTID;
205c6434ac7Sdholland 		return 0;
206c6434ac7Sdholland 	}
207c6434ac7Sdholland 	errno = 0;
208c6434ac7Sdholland 	val = strtoul(name, &s, 10);
209c6434ac7Sdholland 	if (errno || *s != 0) {
210c6434ac7Sdholland 		return -1;
211c6434ac7Sdholland 	}
212c6434ac7Sdholland 	if (idtype == QUOTA_IDTYPE_USER && val > UID_MAX) {
213c6434ac7Sdholland 		return -1;
214c6434ac7Sdholland 	}
215c6434ac7Sdholland 	if (idtype == QUOTA_IDTYPE_GROUP && val > GID_MAX) {
216c6434ac7Sdholland 		return -1;
217c6434ac7Sdholland 	}
218c6434ac7Sdholland 	*ret = val;
219c6434ac7Sdholland 	return 0;
220c6434ac7Sdholland }
221c6434ac7Sdholland 
222c6434ac7Sdholland static int
getobjtype(const char * name,int * ret)223c6434ac7Sdholland getobjtype(const char *name, int *ret)
224c6434ac7Sdholland {
225c6434ac7Sdholland 	unsigned i;
226c6434ac7Sdholland 	size_t len;
227c6434ac7Sdholland 
228c6434ac7Sdholland 	for (i=0; i<numobjtypes; i++) {
229c6434ac7Sdholland 		if (!strcmp(name, objtypenames[i])) {
230c6434ac7Sdholland 			*ret = i;
231c6434ac7Sdholland 			return 0;
232c6434ac7Sdholland 		}
233c6434ac7Sdholland 	}
234c6434ac7Sdholland 
235c6434ac7Sdholland 	/*
236c6434ac7Sdholland 	 * Sigh. Some early committed versions of quotadump used
237c6434ac7Sdholland 	 * "blocks" and "files" instead of "block" and "file".
238c6434ac7Sdholland 	 */
239c6434ac7Sdholland 	len = strlen(name);
240c6434ac7Sdholland 	if (len == 0) {
241c6434ac7Sdholland 		return -1;
242c6434ac7Sdholland 	}
243c6434ac7Sdholland 	for (i=0; i<numobjtypes; i++) {
244c6434ac7Sdholland 		if (name[len-1] == 's' &&
245c6434ac7Sdholland 		    !strncmp(name, objtypenames[i], len-1)) {
246c6434ac7Sdholland 			*ret = i;
247c6434ac7Sdholland 			return 0;
248c6434ac7Sdholland 		}
249c6434ac7Sdholland 	}
250c6434ac7Sdholland 	return -1;
251c6434ac7Sdholland }
252c6434ac7Sdholland 
253c6434ac7Sdholland static int
getlimit(const char * name,uint64_t * ret)254c6434ac7Sdholland getlimit(const char *name, uint64_t *ret)
255c6434ac7Sdholland {
256c6434ac7Sdholland 	unsigned long long val;
257c6434ac7Sdholland 	char *s;
258c6434ac7Sdholland 
259c6434ac7Sdholland 	if (!strcmp(name, "-")) {
260c6434ac7Sdholland 		*ret = QUOTA_NOLIMIT;
261c6434ac7Sdholland 		return 0;
262c6434ac7Sdholland 	}
263c6434ac7Sdholland 	errno = 0;
264c6434ac7Sdholland 	val = strtoull(name, &s, 10);
265c6434ac7Sdholland 	if (errno || *s != 0) {
266c6434ac7Sdholland 		return -1;
267c6434ac7Sdholland 	}
268c6434ac7Sdholland 	*ret = val;
269c6434ac7Sdholland 	return 0;
270c6434ac7Sdholland }
271c6434ac7Sdholland 
272c6434ac7Sdholland static int
gettime(const char * name,int64_t * ret)273c6434ac7Sdholland gettime(const char *name, int64_t *ret)
274c6434ac7Sdholland {
275c6434ac7Sdholland 	long long val;
276c6434ac7Sdholland 	char *s;
277c6434ac7Sdholland 
278c6434ac7Sdholland 	if (!strcmp(name, "-")) {
279c6434ac7Sdholland 		*ret = QUOTA_NOTIME;
280c6434ac7Sdholland 		return 0;
281c6434ac7Sdholland 	}
282c6434ac7Sdholland 	errno = 0;
283c6434ac7Sdholland 	val = strtoll(name, &s, 10);
284c6434ac7Sdholland 	if (errno || *s != 0 || val < 0) {
285c6434ac7Sdholland 		return -1;
286c6434ac7Sdholland 	}
287c6434ac7Sdholland 	*ret = val;
288c6434ac7Sdholland 	return 0;
289c6434ac7Sdholland }
290c6434ac7Sdholland 
291c6434ac7Sdholland ////////////////////////////////////////////////////////////
292c6434ac7Sdholland // parsing tools
293c6434ac7Sdholland 
294c6434ac7Sdholland static int
isws(int ch)295c6434ac7Sdholland isws(int ch)
296c6434ac7Sdholland {
297c6434ac7Sdholland 	return ch != '\0' && strchr(ws, ch) != NULL;
298c6434ac7Sdholland }
299c6434ac7Sdholland 
300c6434ac7Sdholland static char *
skipws(char * s)301c6434ac7Sdholland skipws(char *s)
302c6434ac7Sdholland {
303c6434ac7Sdholland 	while (isws(*s)) {
304c6434ac7Sdholland 		s++;
305c6434ac7Sdholland 	}
306c6434ac7Sdholland 	return s;
307c6434ac7Sdholland }
308c6434ac7Sdholland 
309c6434ac7Sdholland ////////////////////////////////////////////////////////////
310c6434ac7Sdholland // deletion of extra records
311c6434ac7Sdholland 
312c6434ac7Sdholland static void
scankeys(struct quotahandle * qh,struct qklist * seenkeys,struct qklist * dropkeys)313c6434ac7Sdholland scankeys(struct quotahandle *qh, struct qklist *seenkeys,
314c6434ac7Sdholland 	struct qklist *dropkeys)
315c6434ac7Sdholland {
316c6434ac7Sdholland 	struct quotacursor *qc;
317c6434ac7Sdholland #define MAX 8
318c6434ac7Sdholland 	struct quotakey keys[MAX];
319c6434ac7Sdholland 	struct quotaval vals[MAX];
320c6434ac7Sdholland 	int num, i;
321c6434ac7Sdholland 
322c6434ac7Sdholland 	qc = quota_opencursor(qh);
323c6434ac7Sdholland 	if (qc == NULL) {
324c6434ac7Sdholland 		err(EXIT_FAILURE, "quota_opencursor");
325c6434ac7Sdholland 	}
326c6434ac7Sdholland 
327c6434ac7Sdholland 	while (quotacursor_atend(qc) == 0) {
328c6434ac7Sdholland 		num = quotacursor_getn(qc, keys, vals, MAX);
329c6434ac7Sdholland 		if (num < 0) {
330c6434ac7Sdholland 			if (errno == EDEADLK) {
331c6434ac7Sdholland 				quotacursor_rewind(qc);
332c6434ac7Sdholland 				qklist_truncate(dropkeys);
333c6434ac7Sdholland 				continue;
334c6434ac7Sdholland 			}
335c6434ac7Sdholland 			err(EXIT_FAILURE, "quotacursor_getn");
336c6434ac7Sdholland 		}
337c6434ac7Sdholland 		for (i=0; i<num; i++) {
338c6434ac7Sdholland 			if (qklist_present(seenkeys, &keys[i]) == 0) {
339c6434ac7Sdholland 				qklist_add(dropkeys, &keys[i]);
340c6434ac7Sdholland 			}
341c6434ac7Sdholland 		}
342c6434ac7Sdholland 	}
343c6434ac7Sdholland 
344c6434ac7Sdholland 	quotacursor_close(qc);
345c6434ac7Sdholland }
346c6434ac7Sdholland 
347c6434ac7Sdholland static void
purge(struct quotahandle * qh,struct qklist * dropkeys)348c6434ac7Sdholland purge(struct quotahandle *qh, struct qklist *dropkeys)
349c6434ac7Sdholland {
350c6434ac7Sdholland 	unsigned i;
351c6434ac7Sdholland 
352c6434ac7Sdholland 	for (i=0; i<dropkeys->num; i++) {
353c6434ac7Sdholland 		if (quota_delete(qh, &dropkeys->keys[i])) {
354c6434ac7Sdholland 			err(EXIT_FAILURE, "quota_delete");
355c6434ac7Sdholland 		}
356c6434ac7Sdholland 	}
357c6434ac7Sdholland }
358c6434ac7Sdholland 
359c6434ac7Sdholland ////////////////////////////////////////////////////////////
360c6434ac7Sdholland // dumpfile reader
361c6434ac7Sdholland 
362c6434ac7Sdholland static void
readdumpfile(struct quotahandle * qh,FILE * f,const char * path,struct qklist * seenkeys)363c6434ac7Sdholland readdumpfile(struct quotahandle *qh, FILE *f, const char *path,
364c6434ac7Sdholland 	     struct qklist *seenkeys)
365c6434ac7Sdholland {
366c6434ac7Sdholland 	char buf[128];
367c6434ac7Sdholland 	unsigned lineno;
368c6434ac7Sdholland 	unsigned long version;
369c6434ac7Sdholland 	char *s;
370c6434ac7Sdholland 	char *fields[8];
371c6434ac7Sdholland 	unsigned num;
372c6434ac7Sdholland 	char *x;
373c6434ac7Sdholland 	struct quotakey key;
374c6434ac7Sdholland 	struct quotaval val;
375c6434ac7Sdholland 	int ch;
376c6434ac7Sdholland 
377c6434ac7Sdholland 	lineno = 0;
378c6434ac7Sdholland 	if (fgets(buf, sizeof(buf), f) == NULL) {
379c6434ac7Sdholland 		errx(EXIT_FAILURE, "%s: EOF before quotadump header", path);
380c6434ac7Sdholland 	}
381c6434ac7Sdholland 	lineno++;
382c6434ac7Sdholland 	if (strncmp(buf, "@format netbsd-quota-dump v", 27) != 0) {
383c6434ac7Sdholland 		errx(EXIT_FAILURE, "%s: Missing quotadump header", path);
384c6434ac7Sdholland 	}
385c6434ac7Sdholland 	s = buf+27;
386c6434ac7Sdholland 	errno = 0;
387c6434ac7Sdholland 	version = strtoul(s, &s, 10);
388c6434ac7Sdholland 	if (errno) {
389c6434ac7Sdholland 		errx(EXIT_FAILURE, "%s: Corrupted quotadump header", path);
390c6434ac7Sdholland 	}
391c6434ac7Sdholland 	s = skipws(s);
392c6434ac7Sdholland 	if (*s != '\0') {
393c6434ac7Sdholland 		errx(EXIT_FAILURE, "%s: Trash after quotadump header", path);
394c6434ac7Sdholland 	}
395c6434ac7Sdholland 
396c6434ac7Sdholland 	switch (version) {
397c6434ac7Sdholland 	    case 1: break;
398c6434ac7Sdholland 	    default:
399c6434ac7Sdholland 		errx(EXIT_FAILURE, "%s: Unsupported quotadump version %lu",
400c6434ac7Sdholland 		     path, version);
401c6434ac7Sdholland 	}
402c6434ac7Sdholland 
403c6434ac7Sdholland 	while (fgets(buf, sizeof(buf), f)) {
404c6434ac7Sdholland 		lineno++;
405c6434ac7Sdholland 		if (buf[0] == '#') {
406c6434ac7Sdholland 			continue;
407c6434ac7Sdholland 		}
408c6434ac7Sdholland 		if (!strncmp(buf, "@end", 4)) {
409c6434ac7Sdholland 			s = skipws(buf+4);
410c6434ac7Sdholland 			if (*s != '\0') {
411c6434ac7Sdholland 				errx(EXIT_FAILURE, "%s:%u: Invalid @end tag",
412c6434ac7Sdholland 				     path, lineno);
413c6434ac7Sdholland 			}
414c6434ac7Sdholland 			break;
415c6434ac7Sdholland 		}
416c6434ac7Sdholland 
417c6434ac7Sdholland 		num = 0;
418c6434ac7Sdholland 		for (s = strtok_r(buf, ws, &x);
419c6434ac7Sdholland 		     s != NULL;
420c6434ac7Sdholland 		     s = strtok_r(NULL, ws, &x)) {
421c6434ac7Sdholland 			if (num < 8) {
422c6434ac7Sdholland 				fields[num++] = s;
423c6434ac7Sdholland 			} else {
424c6434ac7Sdholland 				errx(EXIT_FAILURE, "%s:%u: Too many fields",
425c6434ac7Sdholland 				     path, lineno);
426c6434ac7Sdholland 			}
427c6434ac7Sdholland 		}
428c6434ac7Sdholland 		if (num < 8) {
429c6434ac7Sdholland 			errx(EXIT_FAILURE, "%s:%u: Not enough fields",
430c6434ac7Sdholland 			     path, lineno);
431c6434ac7Sdholland 		}
432c6434ac7Sdholland 
433c6434ac7Sdholland 		if (getidtype(fields[0], &key.qk_idtype)) {
434c6434ac7Sdholland 			errx(EXIT_FAILURE, "%s:%u: Invalid/unknown ID type %s",
435c6434ac7Sdholland 			     path, lineno, fields[0]);
436c6434ac7Sdholland 		}
437c6434ac7Sdholland 		if (getid(fields[1], key.qk_idtype, &key.qk_id)) {
438c6434ac7Sdholland 			errx(EXIT_FAILURE, "%s:%u: Invalid ID number %s",
439c6434ac7Sdholland 			     path, lineno, fields[1]);
440c6434ac7Sdholland 		}
441c6434ac7Sdholland 		if (getobjtype(fields[2], &key.qk_objtype)) {
442c6434ac7Sdholland 			errx(EXIT_FAILURE, "%s:%u: Invalid/unknown object "
443c6434ac7Sdholland 			     "type %s",
444c6434ac7Sdholland 			     path, lineno, fields[2]);
445c6434ac7Sdholland 		}
446c6434ac7Sdholland 
447c6434ac7Sdholland 		if (getlimit(fields[3], &val.qv_hardlimit)) {
448c6434ac7Sdholland 			errx(EXIT_FAILURE, "%s:%u: Invalid hard limit %s",
449c6434ac7Sdholland 			     path, lineno, fields[3]);
450c6434ac7Sdholland 		}
451c6434ac7Sdholland 		if (getlimit(fields[4], &val.qv_softlimit)) {
452c6434ac7Sdholland 			errx(EXIT_FAILURE, "%s:%u: Invalid soft limit %s",
453c6434ac7Sdholland 			     path, lineno, fields[4]);
454c6434ac7Sdholland 		}
455c6434ac7Sdholland 		if (getlimit(fields[5], &val.qv_usage)) {
456c6434ac7Sdholland 			/*
457c6434ac7Sdholland 			 * Make this nonfatal as it'll be ignored by
458c6434ac7Sdholland 			 * quota_put() anyway.
459c6434ac7Sdholland 			 */
460c6434ac7Sdholland 			warnx("%s:%u: Invalid current usage %s",
461c6434ac7Sdholland 			     path, lineno, fields[5]);
462c6434ac7Sdholland 			val.qv_usage = 0;
463c6434ac7Sdholland 		}
464c6434ac7Sdholland 		if (gettime(fields[6], &val.qv_expiretime)) {
465c6434ac7Sdholland 			errx(EXIT_FAILURE, "%s:%u: Invalid expire time %s",
466c6434ac7Sdholland 			     path, lineno, fields[6]);
467c6434ac7Sdholland 		}
468c6434ac7Sdholland 		if (gettime(fields[7], &val.qv_grace)) {
469c6434ac7Sdholland 			errx(EXIT_FAILURE, "%s:%u: Invalid grace period %s",
470c6434ac7Sdholland 			     path, lineno, fields[7]);
471c6434ac7Sdholland 		}
472c6434ac7Sdholland 
473c6434ac7Sdholland 		if (quota_put(qh, &key, &val)) {
474c6434ac7Sdholland 			err(EXIT_FAILURE, "%s:%u: quota_put", path, lineno);
475c6434ac7Sdholland 		}
476c6434ac7Sdholland 
477c6434ac7Sdholland 		if (seenkeys != NULL) {
478c6434ac7Sdholland 			qklist_add(seenkeys, &key);
479c6434ac7Sdholland 		}
480c6434ac7Sdholland 	}
481c6434ac7Sdholland 	if (feof(f)) {
482c6434ac7Sdholland 		return;
483c6434ac7Sdholland 	}
484c6434ac7Sdholland 	if (ferror(f)) {
485c6434ac7Sdholland 		errx(EXIT_FAILURE, "%s: Read error", path);
486c6434ac7Sdholland 	}
487c6434ac7Sdholland 	/* not at EOF, not an error... what's left? */
488c6434ac7Sdholland 	while (1) {
489c6434ac7Sdholland 		ch = fgetc(f);
490c6434ac7Sdholland 		if (ch == EOF)
491c6434ac7Sdholland 			break;
492c6434ac7Sdholland 		if (isws(ch)) {
493c6434ac7Sdholland 			continue;
494c6434ac7Sdholland 		}
495c6434ac7Sdholland 		warnx("%s:%u: Trash after @end tag", path, lineno);
496c6434ac7Sdholland 	}
497c6434ac7Sdholland }
498c6434ac7Sdholland 
499c6434ac7Sdholland ////////////////////////////////////////////////////////////
500c6434ac7Sdholland // top level control logic
501c6434ac7Sdholland 
502c12c7665Sjoerg __dead static void
usage(void)503c6434ac7Sdholland usage(void)
504c6434ac7Sdholland {
505c6434ac7Sdholland 	fprintf(stderr, "usage: %s [-d] volume [dump-file]\n",
506c6434ac7Sdholland 		getprogname());
507c6434ac7Sdholland 	exit(EXIT_FAILURE);
508c6434ac7Sdholland }
509c6434ac7Sdholland 
510c6434ac7Sdholland int
main(int argc,char * argv[])511c6434ac7Sdholland main(int argc, char *argv[])
512c6434ac7Sdholland {
513c6434ac7Sdholland 	int ch;
514c6434ac7Sdholland 	FILE *f;
515c6434ac7Sdholland 	struct quotahandle *qh;
516c6434ac7Sdholland 
517c6434ac7Sdholland 	int dflag = 0;
518c6434ac7Sdholland 	const char *volume = NULL;
519c6434ac7Sdholland 	const char *dumpfile = NULL;
520c6434ac7Sdholland 
521c6434ac7Sdholland 	while ((ch = getopt(argc, argv, "d")) != -1) {
522c6434ac7Sdholland 		switch (ch) {
523c6434ac7Sdholland 		    case 'd': dflag = 1; break;
524c6434ac7Sdholland 		    default: usage(); break;
525c6434ac7Sdholland 		}
526c6434ac7Sdholland 	}
527c6434ac7Sdholland 
528c6434ac7Sdholland 	if (optind >= argc) {
529c6434ac7Sdholland 		usage();
530c6434ac7Sdholland 	}
531c6434ac7Sdholland 	volume = argv[optind++];
532c6434ac7Sdholland 	if (optind < argc) {
533c6434ac7Sdholland 		dumpfile = argv[optind++];
534c6434ac7Sdholland 	}
535c6434ac7Sdholland 	if (optind < argc) {
536c6434ac7Sdholland 		usage();
537c6434ac7Sdholland 	}
538c6434ac7Sdholland 
539c6434ac7Sdholland 	qh = quota_open(volume);
540c6434ac7Sdholland 	if (qh == NULL) {
541c6434ac7Sdholland 		err(EXIT_FAILURE, "quota_open: %s", volume);
542c6434ac7Sdholland 	}
543c6434ac7Sdholland 	if (dumpfile != NULL) {
544c6434ac7Sdholland 		f = fopen(dumpfile, "r");
545c6434ac7Sdholland 		if (f == NULL) {
546c6434ac7Sdholland 			err(EXIT_FAILURE, "%s", dumpfile);
547c6434ac7Sdholland 		}
548c6434ac7Sdholland 	} else {
549c6434ac7Sdholland 		f = stdin;
550c6434ac7Sdholland 		dumpfile = "<stdin>";
551c6434ac7Sdholland 	}
552c6434ac7Sdholland 
553c6434ac7Sdholland 	maketables(qh);
554c6434ac7Sdholland 
555c6434ac7Sdholland 	if (dflag) {
556c6434ac7Sdholland 		struct qklist *seenkeys, *dropkeys;
557c6434ac7Sdholland 
558c6434ac7Sdholland 		seenkeys = qklist_create();
559c6434ac7Sdholland 		dropkeys = qklist_create();
560c6434ac7Sdholland 
561c6434ac7Sdholland 		readdumpfile(qh, f, dumpfile, seenkeys);
562c6434ac7Sdholland 		qklist_sort(seenkeys);
563c6434ac7Sdholland 		scankeys(qh, seenkeys, dropkeys);
564c6434ac7Sdholland 		purge(qh, dropkeys);
565c6434ac7Sdholland 
566c6434ac7Sdholland 		qklist_destroy(dropkeys);
567c6434ac7Sdholland 		qklist_destroy(seenkeys);
568c6434ac7Sdholland 	} else {
569c6434ac7Sdholland 		readdumpfile(qh, f, dumpfile, NULL);
570c6434ac7Sdholland 	}
571c6434ac7Sdholland 
572c6434ac7Sdholland 	if (f != stdin) {
573c6434ac7Sdholland 		fclose(f);
574c6434ac7Sdholland 	}
575c6434ac7Sdholland 	quota_close(qh);
576c6434ac7Sdholland 	return EXIT_SUCCESS;
577c6434ac7Sdholland }
578