Home | History | Annotate | Line # | Download | only in repquota
repquota.c revision 1.26
      1 /*
      2  * Copyright (c) 1980, 1990, 1993
      3  *	The Regents of the University of California.  All rights reserved.
      4  *
      5  * This code is derived from software contributed to Berkeley by
      6  * Robert Elz at The University of Melbourne.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  * 3. Neither the name of the University nor the names of its contributors
     17  *    may be used to endorse or promote products derived from this software
     18  *    without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     30  * SUCH DAMAGE.
     31  */
     32 
     33 #include <sys/cdefs.h>
     34 #ifndef lint
     35 __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\
     36  The Regents of the University of California.  All rights reserved.");
     37 #endif /* not lint */
     38 
     39 #ifndef lint
     40 #if 0
     41 static char sccsid[] = "@(#)repquota.c	8.2 (Berkeley) 11/22/94";
     42 #else
     43 __RCSID("$NetBSD: repquota.c,v 1.26 2011/03/06 17:08:43 bouyer Exp $");
     44 #endif
     45 #endif /* not lint */
     46 
     47 /*
     48  * Quota report
     49  */
     50 #include <sys/param.h>
     51 #include <sys/stat.h>
     52 #include <sys/types.h>
     53 #include <sys/statvfs.h>
     54 #include <prop/proplib.h>
     55 #include <sys/quota.h>
     56 
     57 #include <errno.h>
     58 #include <err.h>
     59 #include <fstab.h>
     60 #include <grp.h>
     61 #include <pwd.h>
     62 #include <stdio.h>
     63 #include <stdlib.h>
     64 #include <string.h>
     65 #include <unistd.h>
     66 
     67 #include <ufs/ufs/quota2_prop.h>
     68 #include <ufs/ufs/quota1.h>
     69 
     70 #include <printquota.h>
     71 
     72 const char *qfextension[] = INITQFNAMES;
     73 const char *qfname = QUOTAFILENAME;
     74 
     75 struct fileusage {
     76 	struct	fileusage *fu_next;
     77 	struct	quota2_entry fu_q2e;
     78 	u_long	fu_id;
     79 	char	fu_name[1];
     80 	/* actually bigger */
     81 };
     82 #define FUHASH 1024	/* must be power of two */
     83 struct fileusage *fuhead[MAXQUOTAS][FUHASH];
     84 u_long highid[MAXQUOTAS];	/* highest addid()'ed identifier per type */
     85 int valid[MAXQUOTAS];
     86 struct quota2_entry defaultq2e[MAXQUOTAS];
     87 
     88 int	vflag = 0;		/* verbose */
     89 int	aflag = 0;		/* all file systems */
     90 int	Dflag = 0;		/* debug */
     91 int	hflag = 0;		/* humanize */
     92 int	xflag = 0;		/* export */
     93 
     94 
     95 struct fileusage *addid(u_long, int, const char *);
     96 int	hasquota(struct fstab *, int, char **);
     97 struct fileusage *lookup(u_long, int);
     98 struct fileusage *qremove(u_long, int);
     99 int	main(int, char **);
    100 int	oneof(const char *, char **, int);
    101 int	repquota(const struct statvfs *, int);
    102 int	repquota2(const struct statvfs *, int);
    103 int	repquota1(const struct statvfs *, int);
    104 void	usage(void);
    105 void	printquotas(int, const struct statvfs *, int);
    106 void	exportquotas(void);
    107 void	dqblk2q2e(const struct dqblk *, struct quota2_entry *);
    108 
    109 int
    110 main(argc, argv)
    111 	int argc;
    112 	char **argv;
    113 {
    114 	int gflag = 0, uflag = 0, errs = 0;
    115 	long i, argnum, done = 0;
    116 	int ch;
    117 	struct statvfs *fst;
    118 	int nfst;
    119 
    120 	while ((ch = getopt(argc, argv, "Daguhvx")) != -1) {
    121 		switch(ch) {
    122 		case 'a':
    123 			aflag++;
    124 			break;
    125 		case 'g':
    126 			gflag++;
    127 			break;
    128 		case 'u':
    129 			uflag++;
    130 			break;
    131 		case 'h':
    132 			hflag++;
    133 			break;
    134 		case 'v':
    135 			vflag++;
    136 			break;
    137 		case 'D':
    138 			Dflag++;
    139 			break;
    140 		case 'x':
    141 			xflag++;
    142 			break;
    143 		default:
    144 			usage();
    145 		}
    146 	}
    147 	argc -= optind;
    148 	argv += optind;
    149 	if (xflag && (argc != 1 || aflag))
    150 		usage();
    151 	if (argc == 0 && !aflag)
    152 		usage();
    153 	if (!gflag && !uflag) {
    154 		if (aflag)
    155 			gflag++;
    156 		uflag++;
    157 	}
    158 
    159 	nfst = getmntinfo(&fst, MNT_WAIT);
    160 	if (nfst == 0)
    161 		errx(2, "no filesystems mounted!");
    162 	for (i = 0; i < nfst; i++) {
    163 		if ((fst[i].f_flag & ST_QUOTA) == 0)
    164 			continue;
    165 		if (aflag) {
    166 			if (gflag)
    167 				errs += repquota(&fst[i], GRPQUOTA);
    168 			if (uflag)
    169 				errs += repquota(&fst[i], USRQUOTA);
    170 			continue;
    171 		}
    172 		if ((argnum = oneof(fst[i].f_mntonname, argv, argc)) >= 0 ||
    173 		    (argnum = oneof(fst[i].f_mntfromname, argv, argc)) >= 0) {
    174 			done |= 1 << argnum;
    175 			if (gflag)
    176 				errs += repquota(&fst[i], GRPQUOTA);
    177 			if (uflag)
    178 				errs += repquota(&fst[i], USRQUOTA);
    179 		}
    180 	}
    181 	if (xflag)
    182 		exportquotas();
    183 	for (i = 0; i < argc; i++)
    184 		if ((done & (1 << i)) == 0)
    185 			fprintf(stderr, "%s not mounted\n", argv[i]);
    186 	exit(errs);
    187 }
    188 
    189 void
    190 usage()
    191 {
    192 	fprintf(stderr, "usage:\n"
    193 		"\trepquota [-D] [-v] [-g] [-u] -a\n"
    194 		"\trepquota [-D] [-v] [-g] [-u] filesys ...\n"
    195 		"\trepquota -x [-D] [-g] [-u] filesys\n");
    196 	exit(1);
    197 }
    198 
    199 int
    200 repquota(const struct statvfs *vfs, int type)
    201 {
    202 	if (repquota2(vfs, type) != 0)
    203 		return (repquota1(vfs, type));
    204 	return 0;
    205 }
    206 
    207 int
    208 repquota2(const struct statvfs *vfs, int type)
    209 {
    210 	prop_dictionary_t dict, data, cmd;
    211 	prop_array_t cmds, datas;
    212 	struct plistref pref;
    213 	int error;
    214 	int8_t error8, version = 0;
    215 	prop_object_iterator_t cmditer, dataiter;
    216 	struct quota2_entry *q2ep;
    217 	struct fileusage *fup;
    218 	const char *strid;
    219 	uint32_t id;
    220 
    221 	dict = quota2_prop_create();
    222 	cmds = prop_array_create();
    223 	datas = prop_array_create();
    224 
    225 	if (dict == NULL || cmds == NULL || datas == NULL)
    226 		errx(1, "can't allocate proplist");
    227 	if (!quota2_prop_add_command(cmds, "getall", qfextension[type], datas))
    228 		err(1, "prop_add_command");
    229 	if (!quota2_prop_add_command(cmds, "get version", qfextension[type],
    230 	     prop_array_create()))
    231 		err(1, "prop_add_command");
    232 	if (!prop_dictionary_set(dict, "commands", cmds))
    233 		err(1, "prop_dictionary_set(command)");
    234 	if (Dflag)
    235 		printf("message to kernel:\n%s\n",
    236 		    prop_dictionary_externalize(dict));
    237 	if (!prop_dictionary_send_syscall(dict, &pref))
    238 		err(1, "prop_dictionary_send_syscall");
    239 	prop_object_release(dict);
    240 
    241 	if (quotactl(vfs->f_mntonname, &pref) != 0)
    242 		err(1, "quotactl");
    243 
    244 	if ((error = prop_dictionary_recv_syscall(&pref, &dict)) != 0) {
    245 		errx(1, "prop_dictionary_recv_syscall: %s\n",
    246 		    strerror(error));
    247 	}
    248 	if (Dflag)
    249 		printf("reply from kernel:\n%s\n",
    250 		    prop_dictionary_externalize(dict));
    251 	if ((error = quota2_get_cmds(dict, &cmds)) != 0) {
    252 		errx(1, "quota2_get_cmds: %s\n",
    253 		    strerror(error));
    254 	}
    255 	cmditer = prop_array_iterator(cmds);
    256 	if (cmditer == NULL)
    257 		err(1, "prop_array_iterator(cmds)");
    258 
    259 	while ((cmd = prop_object_iterator_next(cmditer)) != NULL) {
    260 		const char *cmdstr;
    261 		if (!prop_dictionary_get_cstring_nocopy(cmd, "command",
    262 		    &cmdstr))
    263 			err(1, "prop_get(command)");
    264 
    265 		if (!prop_dictionary_get_int8(cmd, "return", &error8))
    266 			err(1, "prop_get(return)");
    267 
    268 		if (error8) {
    269 			prop_object_release(dict);
    270 			if (error8 != EOPNOTSUPP) {
    271 				fprintf(stderr, "get %s quotas: %s\n",
    272 				    qfextension[type], strerror(error8));
    273 			}
    274 			return (error8);
    275 		}
    276 		datas = prop_dictionary_get(cmd, "data");
    277 		if (datas == NULL)
    278 			err(1, "prop_dict_get(datas)");
    279 
    280 		if (strcmp("get version", cmdstr) == 0) {
    281 			data = prop_array_get(datas, 0);
    282 			if (data == NULL)
    283 				err(1, "prop_array_get(version)");
    284 			if (!prop_dictionary_get_int8(data, "version",
    285 			    &version))
    286 				err(1, "prop_get_int8(version)");
    287 			continue;
    288 		}
    289 		dataiter = prop_array_iterator(datas);
    290 		if (dataiter == NULL)
    291 			err(1, "prop_array_iterator");
    292 
    293 		valid[type] = 1;
    294 		while ((data = prop_object_iterator_next(dataiter)) != NULL) {
    295 			strid = NULL;
    296 			if (!prop_dictionary_get_uint32(data, "id", &id)) {
    297 				if (!prop_dictionary_get_cstring_nocopy(data,
    298 				    "id", &strid))
    299 					errx(1, "can't find id in quota entry");
    300 				if (strcmp(strid, "default") != 0) {
    301 					errx(1,
    302 					    "wrong id string %s in quota entry",
    303 					    strid);
    304 				}
    305 				q2ep = &defaultq2e[type];
    306 			} else {
    307 				if ((fup = lookup(id, type)) == 0)
    308 					fup = addid(id, type, (char *)0);
    309 				q2ep = &fup->fu_q2e;
    310 				q2ep->q2e_uid = id;
    311 			}
    312 
    313 			error = quota2_dict_get_q2e_usage(data, q2ep);
    314 			if (error) {
    315 				errx(1, "quota2_dict_get_q2e_usage: %s\n",
    316 				    strerror(error));
    317 			}
    318 		}
    319 		prop_object_iterator_release(dataiter);
    320 	}
    321 	prop_object_iterator_release(cmditer);
    322 	prop_object_release(dict);
    323 	if (xflag == 0)
    324 		printquotas(type, vfs, version);
    325 	return (0);
    326 }
    327 
    328 int repquota1(const struct statvfs *vfs, int type)
    329 {
    330 	char *qfpathname;
    331 	struct fstab *fs;
    332 	struct fileusage *fup;
    333 	FILE *qf;
    334 	u_long id;
    335 	struct dqblk dqbuf;
    336 	time_t bgrace = MAX_DQ_TIME, igrace = MAX_DQ_TIME;
    337 
    338 	setfsent();
    339 	while ((fs = getfsent()) != NULL) {
    340 		if (strcmp(fs->fs_vfstype, "ffs") == 0 &&
    341 		   strcmp(fs->fs_file, vfs->f_mntonname) == 0)
    342 			break;
    343 	}
    344 	endfsent();
    345 	if (fs == NULL) {
    346 		fprintf(stderr, "%s not found in fstab\n", vfs->f_mntonname);
    347 		return 1;
    348 	}
    349 	if (!hasquota(fs, type, &qfpathname))
    350 		return 0;
    351 
    352 	if ((qf = fopen(qfpathname, "r")) == NULL) {
    353 		perror(qfpathname);
    354 		return (1);
    355 	}
    356 	for (id = 0; ; id++) {
    357 		fread(&dqbuf, sizeof(struct dqblk), 1, qf);
    358 		if (feof(qf))
    359 			break;
    360 		if (id == 0) {
    361 			if (dqbuf.dqb_btime > 0)
    362 				bgrace = dqbuf.dqb_btime;
    363 			if (dqbuf.dqb_itime > 0)
    364 				igrace = dqbuf.dqb_itime;
    365 		}
    366 		if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0 &&
    367 		    dqbuf.dqb_bsoftlimit == 0 && dqbuf.dqb_bhardlimit == 0 &&
    368 		    dqbuf.dqb_isoftlimit == 0 && dqbuf.dqb_ihardlimit == 0)
    369 			continue;
    370 		if ((fup = lookup(id, type)) == 0)
    371 			fup = addid(id, type, (char *)0);
    372 		dqblk2q2e(&dqbuf, &fup->fu_q2e);
    373 		fup->fu_q2e.q2e_val[QL_BLOCK].q2v_grace = bgrace;
    374 		fup->fu_q2e.q2e_val[QL_FILE].q2v_grace = igrace;
    375 	}
    376 	defaultq2e[type].q2e_val[QL_BLOCK].q2v_grace = bgrace;
    377 	defaultq2e[type].q2e_val[QL_FILE].q2v_grace = igrace;
    378 	defaultq2e[type].q2e_val[QL_BLOCK].q2v_softlimit =
    379 	    defaultq2e[type].q2e_val[QL_BLOCK].q2v_hardlimit =
    380 	    defaultq2e[type].q2e_val[QL_FILE].q2v_softlimit =
    381 	    defaultq2e[type].q2e_val[QL_FILE].q2v_hardlimit = UQUAD_MAX;
    382 	fclose(qf);
    383 	valid[type] = 1;
    384 	if (xflag == 0)
    385 		printquotas(type, vfs, 1);
    386 	return (0);
    387 }
    388 
    389 void
    390 printquotas(int type, const struct statvfs *vfs, int version)
    391 {
    392 	static int multiple = 0;
    393 	u_long id;
    394 	int i;
    395 	struct fileusage *fup;
    396 	const char *timemsg[N_QL];
    397 	char overchar[N_QL];
    398 	static time_t now;
    399 
    400 	switch(type) {
    401 	case  GRPQUOTA:
    402 		{
    403 		struct group *gr;
    404 		setgrent();
    405 		while ((gr = getgrent()) != 0)
    406 			(void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
    407 		endgrent();
    408 		break;
    409 		}
    410 	case USRQUOTA:
    411 		{
    412 		struct passwd *pw;
    413 		setpwent();
    414 		while ((pw = getpwent()) != 0)
    415 			(void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
    416 		endpwent();
    417 		break;
    418 		}
    419 	default:
    420 		errx(1, "unknown quota type %d\n", type);
    421 	}
    422 
    423 	if (now == 0)
    424 		time(&now);
    425 
    426 	if (multiple++)
    427 		printf("\n");
    428 	if (vflag)
    429 		fprintf(stdout,
    430 		    "*** Report for %s quotas on %s (%s, version %d)\n",
    431 		    qfextension[type], vfs->f_mntonname, vfs->f_mntfromname,
    432 		    version);
    433 	printf("                        Block limits               File limits\n");
    434 	printf(type == USRQUOTA ? "User " : "Group");
    435 	printf("            used     soft     hard  grace      used    soft    hard  grace\n");
    436 	for (id = 0; id <= highid[type]; id++) {
    437 		fup = qremove(id, type);
    438 		if (fup == 0)
    439 			continue;
    440 		for (i = 0; i < N_QL; i++) {
    441 			switch (QL_STATUS(quota2_check_limit(
    442 			     &fup->fu_q2e.q2e_val[i], 1, now))) {
    443 			case QL_S_DENY_HARD:
    444 			case QL_S_DENY_GRACE:
    445 			case QL_S_ALLOW_SOFT:
    446 				timemsg[i] = timeprt(now,
    447 				    fup->fu_q2e.q2e_val[i].q2v_time, 7);
    448 				overchar[i] = '+';
    449 				break;
    450 			default:
    451 				timemsg[i] =  (vflag && version == 2) ?
    452 				    timeprt(0,
    453 					fup->fu_q2e.q2e_val[i].q2v_grace, 7):
    454 				    "";
    455 				overchar[i] = '-';
    456 				break;
    457 			}
    458 		}
    459 
    460 		if (fup->fu_q2e.q2e_val[QL_BLOCK].q2v_cur == 0 &&
    461 		    fup->fu_q2e.q2e_val[QL_FILE].q2v_cur == 0 && vflag == 0 &&
    462 		    overchar[QL_BLOCK] == '-' && overchar[QL_FILE] == '-')
    463 			continue;
    464 		if (strlen(fup->fu_name) > 9)
    465 			printf("%s ", fup->fu_name);
    466 		else
    467 			printf("%-10s", fup->fu_name);
    468 		printf("%c%c%9s%9s%9s%7s",
    469 			overchar[QL_BLOCK], overchar[QL_FILE],
    470 			intprt(fup->fu_q2e.q2e_val[QL_BLOCK].q2v_cur,
    471 				HN_B, hflag, 9),
    472 			intprt(fup->fu_q2e.q2e_val[QL_BLOCK].q2v_softlimit,
    473 				HN_B, hflag, 9),
    474 			intprt(fup->fu_q2e.q2e_val[QL_BLOCK].q2v_hardlimit,
    475 				HN_B, hflag, 9),
    476 			timemsg[QL_BLOCK]);
    477 		printf("  %8s%8s%8s%7s\n",
    478 			intprt(fup->fu_q2e.q2e_val[QL_FILE].q2v_cur,
    479 				0, hflag, 8),
    480 			intprt(fup->fu_q2e.q2e_val[QL_FILE].q2v_softlimit,
    481 				0, hflag, 8),
    482 			intprt(fup->fu_q2e.q2e_val[QL_FILE].q2v_hardlimit,
    483 				0, hflag, 8),
    484 			timemsg[QL_FILE]);
    485 		free(fup);
    486 	}
    487 }
    488 
    489 void
    490 exportquotas()
    491 {
    492 	u_long id;
    493 	struct fileusage *fup;
    494 	prop_dictionary_t dict, data;
    495 	prop_array_t cmds, datas;
    496 	int type;
    497 
    498 	dict = quota2_prop_create();
    499 	cmds = prop_array_create();
    500 
    501 	if (dict == NULL || cmds == NULL) {
    502 		errx(1, "can't allocate proplist");
    503 	}
    504 
    505 
    506 	for (type = 0; type < MAXQUOTAS; type++) {
    507 		if (valid[type] == 0)
    508 			continue;
    509 		datas = prop_array_create();
    510 		if (datas == NULL)
    511 			errx(1, "can't allocate proplist");
    512 		data = q2etoprop(&defaultq2e[type], 1);
    513 		if (data == NULL)
    514 			err(1, "q2etoprop(default)");
    515 		if (!prop_array_add_and_rel(datas, data))
    516 			err(1, "prop_array_add(data)");
    517 
    518 		for (id = 0; id <= highid[type]; id++) {
    519 			fup = qremove(id, type);
    520 			if (fup == 0)
    521 				continue;
    522 			fup->fu_q2e.q2e_uid = id;
    523 			data = q2etoprop(&fup->fu_q2e, 0);
    524 			if (data == NULL)
    525 				err(1, "q2etoprop(default)");
    526 			if (!prop_array_add_and_rel(datas, data))
    527 				err(1, "prop_array_add(data)");
    528 			free(fup);
    529 		}
    530 
    531 		if (!quota2_prop_add_command(cmds, "set",
    532 		    qfextension[type], datas))
    533 			err(1, "prop_add_command");
    534 	}
    535 
    536 	if (!prop_dictionary_set(dict, "commands", cmds))
    537 		err(1, "prop_dictionary_set(command)");
    538 
    539 	printf("%s\n", prop_dictionary_externalize(dict));
    540 	return;
    541 }
    542 
    543 /*
    544  * Check to see if target appears in list of size cnt.
    545  */
    546 int
    547 oneof(target, list, cnt)
    548 	const char *target;
    549 	char *list[];
    550 	int cnt;
    551 {
    552 	int i;
    553 
    554 	for (i = 0; i < cnt; i++)
    555 		if (strcmp(target, list[i]) == 0)
    556 			return (i);
    557 	return (-1);
    558 }
    559 
    560 /*
    561  * Check to see if a particular quota is to be enabled.
    562  */
    563 int
    564 hasquota(fs, type, qfnamep)
    565 	struct fstab *fs;
    566 	int type;
    567 	char **qfnamep;
    568 {
    569 	char *opt;
    570 	char *cp = NULL;
    571 	static char initname, usrname[100], grpname[100];
    572 	static char buf[BUFSIZ];
    573 
    574 	if (!initname) {
    575 		sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
    576 		sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
    577 		initname = 1;
    578 	}
    579 	strcpy(buf, fs->fs_mntops);
    580 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
    581 		if ((cp = strchr(opt, '=')) != NULL)
    582 			*cp++ = '\0';
    583 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
    584 			break;
    585 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
    586 			break;
    587 	}
    588 	if (!opt)
    589 		return (0);
    590 	if (cp) {
    591 		*qfnamep = cp;
    592 		return (1);
    593 	}
    594 	(void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
    595 	*qfnamep = buf;
    596 	return (1);
    597 }
    598 
    599 /*
    600  * Routines to manage the file usage table.
    601  *
    602  * Lookup an id of a specific type.
    603  */
    604 struct fileusage *
    605 lookup(id, type)
    606 	u_long id;
    607 	int type;
    608 {
    609 	struct fileusage *fup;
    610 
    611 	for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
    612 		if (fup->fu_id == id)
    613 			return (fup);
    614 	return ((struct fileusage *)0);
    615 }
    616 /*
    617  * Lookup and remove an id of a specific type.
    618  */
    619 struct fileusage *
    620 qremove(id, type)
    621 	u_long id;
    622 	int type;
    623 {
    624 	struct fileusage *fup, **fupp;
    625 
    626 	for (fupp = &fuhead[type][id & (FUHASH-1)]; *fupp != 0;) {
    627 		fup = *fupp;
    628 		if (fup->fu_id == id) {
    629 			*fupp = fup->fu_next;
    630 			return (fup);
    631 		}
    632 		fupp = &fup->fu_next;
    633 	}
    634 	return ((struct fileusage *)0);
    635 }
    636 
    637 /*
    638  * Add a new file usage id if it does not already exist.
    639  */
    640 struct fileusage *
    641 addid(id, type, name)
    642 	u_long id;
    643 	int type;
    644 	const char *name;
    645 {
    646 	struct fileusage *fup, **fhp;
    647 	struct group *gr = NULL;
    648 	struct passwd *pw = NULL;
    649 	int len;
    650 
    651 	if ((fup = lookup(id, type)) != NULL) {
    652 		return (fup);
    653 	}
    654 	if (name == NULL) {
    655 		switch(type) {
    656 		case  GRPQUOTA:
    657 			gr = getgrgid(id);
    658 
    659 			if (gr != NULL)
    660 				name = gr->gr_name;
    661 			break;
    662 		case USRQUOTA:
    663 			pw = getpwuid(id);
    664 			if (pw)
    665 				name = pw->pw_name;
    666 			break;
    667 		default:
    668 			errx(1, "unknown quota type %d\n", type);
    669 		}
    670 	}
    671 
    672 	if (name)
    673 		len = strlen(name);
    674 	else
    675 		len = 10;
    676 	if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL) {
    677 		fprintf(stderr, "out of memory for fileusage structures\n");
    678 		exit(1);
    679 	}
    680 	fhp = &fuhead[type][id & (FUHASH - 1)];
    681 	fup->fu_next = *fhp;
    682 	*fhp = fup;
    683 	fup->fu_id = id;
    684 	if (id > highid[type])
    685 		highid[type] = id;
    686 	if (name) {
    687 		memmove(fup->fu_name, name, len + 1);
    688 	} else {
    689 		sprintf(fup->fu_name, "%lu", (u_long)id);
    690 	}
    691 	fup->fu_q2e = defaultq2e[type];
    692 	return (fup);
    693 }
    694