Home | History | Annotate | Line # | Download | only in edquota
edquota.c revision 1.27.18.1
      1        1.1       cgd /*
      2        1.5   mycroft  * Copyright (c) 1980, 1990, 1993
      3        1.5   mycroft  *	The Regents of the University of California.  All rights reserved.
      4        1.1       cgd  *
      5        1.1       cgd  * This code is derived from software contributed to Berkeley by
      6        1.1       cgd  * Robert Elz at The University of Melbourne.
      7        1.1       cgd  *
      8        1.1       cgd  * Redistribution and use in source and binary forms, with or without
      9        1.1       cgd  * modification, are permitted provided that the following conditions
     10        1.1       cgd  * are met:
     11        1.1       cgd  * 1. Redistributions of source code must retain the above copyright
     12        1.1       cgd  *    notice, this list of conditions and the following disclaimer.
     13        1.1       cgd  * 2. Redistributions in binary form must reproduce the above copyright
     14        1.1       cgd  *    notice, this list of conditions and the following disclaimer in the
     15        1.1       cgd  *    documentation and/or other materials provided with the distribution.
     16       1.24       agc  * 3. Neither the name of the University nor the names of its contributors
     17        1.1       cgd  *    may be used to endorse or promote products derived from this software
     18        1.1       cgd  *    without specific prior written permission.
     19        1.1       cgd  *
     20        1.1       cgd  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     21        1.1       cgd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     22        1.1       cgd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     23        1.1       cgd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     24        1.1       cgd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     25        1.1       cgd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     26        1.1       cgd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     27        1.1       cgd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     28        1.1       cgd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     29        1.1       cgd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     30        1.1       cgd  * SUCH DAMAGE.
     31        1.1       cgd  */
     32        1.1       cgd 
     33       1.15     lukem #include <sys/cdefs.h>
     34        1.1       cgd #ifndef lint
     35       1.15     lukem __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\n\
     36       1.15     lukem 	The Regents of the University of California.  All rights reserved.\n");
     37        1.1       cgd #endif /* not lint */
     38        1.1       cgd 
     39        1.1       cgd #ifndef lint
     40       1.15     lukem #if 0
     41       1.15     lukem static char sccsid[] = "from: @(#)edquota.c	8.3 (Berkeley) 4/27/95";
     42       1.15     lukem #else
     43  1.27.18.1      yamt __RCSID("$NetBSD: edquota.c,v 1.27.18.1 2008/05/18 12:36:15 yamt Exp $");
     44       1.15     lukem #endif
     45        1.1       cgd #endif /* not lint */
     46        1.1       cgd 
     47        1.1       cgd /*
     48        1.1       cgd  * Disk quota editor.
     49        1.1       cgd  */
     50        1.1       cgd #include <sys/param.h>
     51        1.1       cgd #include <sys/stat.h>
     52        1.1       cgd #include <sys/file.h>
     53        1.1       cgd #include <sys/wait.h>
     54       1.12     mikel #include <sys/queue.h>
     55        1.4       cgd #include <ufs/ufs/quota.h>
     56       1.15     lukem #include <err.h>
     57        1.1       cgd #include <errno.h>
     58        1.1       cgd #include <fstab.h>
     59        1.1       cgd #include <pwd.h>
     60        1.1       cgd #include <grp.h>
     61        1.1       cgd #include <ctype.h>
     62       1.15     lukem #include <signal.h>
     63        1.1       cgd #include <stdio.h>
     64        1.9       cgd #include <stdlib.h>
     65        1.1       cgd #include <string.h>
     66        1.5   mycroft #include <unistd.h>
     67        1.1       cgd #include "pathnames.h"
     68        1.1       cgd 
     69  1.27.18.1      yamt const char *qfname = QUOTAFILENAME;
     70  1.27.18.1      yamt const char *qfextension[] = INITQFNAMES;
     71  1.27.18.1      yamt const char *quotagroup = QUOTAGROUP;
     72        1.1       cgd char tmpfil[] = _PATH_TMP;
     73        1.1       cgd 
     74        1.1       cgd struct quotause {
     75        1.1       cgd 	struct	quotause *next;
     76        1.1       cgd 	long	flags;
     77        1.1       cgd 	struct	dqblk dqblk;
     78        1.1       cgd 	char	fsname[MAXPATHLEN + 1];
     79        1.1       cgd 	char	qfname[1];	/* actually longer */
     80       1.15     lukem };
     81        1.1       cgd #define	FOUND	0x01
     82       1.13     mikel 
     83       1.23      jonb #define MAX_TMPSTR	(100+MAXPATHLEN)
     84       1.23      jonb 
     85       1.15     lukem int	main __P((int, char **));
     86       1.13     mikel void	usage __P((void));
     87  1.27.18.1      yamt int	getentry __P((const char *, int));
     88       1.13     mikel struct quotause *
     89       1.22    bouyer 	getprivs __P((long, int, char *));
     90       1.13     mikel void	putprivs __P((long, int, struct quotause *));
     91       1.13     mikel int	editit __P((char *));
     92       1.13     mikel int	writeprivs __P((struct quotause *, int, char *, int));
     93       1.13     mikel int	readprivs __P((struct quotause *, int));
     94       1.13     mikel int	writetimes __P((struct quotause *, int, int));
     95       1.13     mikel int	readtimes __P((struct quotause *, int));
     96       1.13     mikel char *	cvtstoa __P((time_t));
     97       1.13     mikel int	cvtatos __P((time_t, char *, time_t *));
     98       1.13     mikel void	freeprivs __P((struct quotause *));
     99  1.27.18.1      yamt int	alldigits __P((const char *));
    100       1.13     mikel int	hasquota __P((struct fstab *, int, char **));
    101        1.1       cgd 
    102       1.12     mikel int
    103        1.1       cgd main(argc, argv)
    104        1.1       cgd 	int argc;
    105       1.15     lukem 	char **argv;
    106        1.1       cgd {
    107       1.15     lukem 	struct quotause *qup, *protoprivs, *curprivs;
    108       1.15     lukem 	long id, protoid;
    109       1.15     lukem 	int quotatype, tmpfd;
    110       1.11      mark 	char *protoname;
    111       1.22    bouyer 	char *soft = NULL, *hard = NULL;
    112       1.22    bouyer 	char *fs = NULL;
    113       1.11      mark 	int ch;
    114        1.1       cgd 	int tflag = 0, pflag = 0;
    115        1.1       cgd 
    116        1.1       cgd 	if (argc < 2)
    117        1.1       cgd 		usage();
    118       1.15     lukem 	if (getuid())
    119       1.15     lukem 		errx(1, "permission denied");
    120       1.15     lukem 	protoname = NULL;
    121        1.1       cgd 	quotatype = USRQUOTA;
    122       1.22    bouyer 	while ((ch = getopt(argc, argv, "ugtp:s:h:f:")) != -1) {
    123        1.1       cgd 		switch(ch) {
    124        1.1       cgd 		case 'p':
    125        1.1       cgd 			protoname = optarg;
    126        1.1       cgd 			pflag++;
    127        1.1       cgd 			break;
    128        1.1       cgd 		case 'g':
    129        1.1       cgd 			quotatype = GRPQUOTA;
    130        1.1       cgd 			break;
    131        1.1       cgd 		case 'u':
    132        1.1       cgd 			quotatype = USRQUOTA;
    133        1.1       cgd 			break;
    134        1.1       cgd 		case 't':
    135        1.1       cgd 			tflag++;
    136        1.1       cgd 			break;
    137       1.22    bouyer 		case 's':
    138       1.22    bouyer 			soft = optarg;
    139       1.22    bouyer 			break;
    140       1.22    bouyer 		case 'h':
    141       1.22    bouyer 			hard = optarg;
    142       1.22    bouyer 			break;
    143       1.22    bouyer 		case 'f':
    144       1.22    bouyer 			fs = optarg;
    145       1.22    bouyer 			break;
    146        1.1       cgd 		default:
    147        1.1       cgd 			usage();
    148        1.1       cgd 		}
    149        1.1       cgd 	}
    150        1.1       cgd 	argc -= optind;
    151        1.1       cgd 	argv += optind;
    152        1.1       cgd 	if (pflag) {
    153       1.22    bouyer 		if (soft || hard)
    154       1.22    bouyer 			usage();
    155        1.1       cgd 		if ((protoid = getentry(protoname, quotatype)) == -1)
    156        1.1       cgd 			exit(1);
    157       1.22    bouyer 		protoprivs = getprivs(protoid, quotatype, fs);
    158        1.1       cgd 		for (qup = protoprivs; qup; qup = qup->next) {
    159        1.1       cgd 			qup->dqblk.dqb_btime = 0;
    160        1.1       cgd 			qup->dqblk.dqb_itime = 0;
    161        1.1       cgd 		}
    162        1.1       cgd 		while (argc-- > 0) {
    163        1.1       cgd 			if ((id = getentry(*argv++, quotatype)) < 0)
    164        1.1       cgd 				continue;
    165        1.1       cgd 			putprivs(id, quotatype, protoprivs);
    166        1.1       cgd 		}
    167        1.1       cgd 		exit(0);
    168        1.1       cgd 	}
    169       1.22    bouyer 	if (soft || hard) {
    170  1.27.18.1      yamt 		struct quotause *lqup;
    171       1.22    bouyer 		u_int32_t softb, hardb, softi, hardi;
    172       1.22    bouyer 		if (tflag)
    173       1.22    bouyer 			usage();
    174       1.22    bouyer 		if (soft) {
    175       1.22    bouyer 			if (sscanf(soft, "%d/%d", &softb, &softi) != 2)
    176       1.22    bouyer 				usage();
    177       1.22    bouyer 			softb = btodb((u_quad_t)softb * 1024);
    178       1.22    bouyer 		}
    179       1.22    bouyer 		if (hard) {
    180       1.22    bouyer 			if (sscanf(hard, "%d/%d", &hardb, &hardi) != 2)
    181       1.22    bouyer 				usage();
    182       1.22    bouyer 			hardb = btodb((u_quad_t)hardb * 1024);
    183       1.22    bouyer 		}
    184       1.22    bouyer 		for ( ; argc > 0; argc--, argv++) {
    185       1.22    bouyer 			if ((id = getentry(*argv, quotatype)) == -1)
    186       1.22    bouyer 				continue;
    187       1.22    bouyer 			curprivs = getprivs(id, quotatype, fs);
    188  1.27.18.1      yamt 			for (lqup = curprivs; lqup; lqup = lqup->next) {
    189       1.22    bouyer 				if (soft) {
    190       1.22    bouyer 					if (softb &&
    191  1.27.18.1      yamt 					    lqup->dqblk.dqb_curblocks >= softb &&
    192  1.27.18.1      yamt 					    (lqup->dqblk.dqb_bsoftlimit == 0 ||
    193  1.27.18.1      yamt 					    lqup->dqblk.dqb_curblocks <
    194  1.27.18.1      yamt 					    lqup->dqblk.dqb_bsoftlimit))
    195  1.27.18.1      yamt 						lqup->dqblk.dqb_btime = 0;
    196       1.22    bouyer 					if (softi &&
    197  1.27.18.1      yamt 					    lqup->dqblk.dqb_curinodes >= softi &&
    198  1.27.18.1      yamt 					    (lqup->dqblk.dqb_isoftlimit == 0 ||
    199  1.27.18.1      yamt 					    lqup->dqblk.dqb_curinodes <
    200  1.27.18.1      yamt 					    lqup->dqblk.dqb_isoftlimit))
    201  1.27.18.1      yamt 						lqup->dqblk.dqb_itime = 0;
    202  1.27.18.1      yamt 					lqup->dqblk.dqb_bsoftlimit = softb;
    203  1.27.18.1      yamt 					lqup->dqblk.dqb_isoftlimit = softi;
    204       1.22    bouyer 				}
    205       1.22    bouyer 				if (hard) {
    206  1.27.18.1      yamt 					lqup->dqblk.dqb_bhardlimit = hardb;
    207  1.27.18.1      yamt 					lqup->dqblk.dqb_ihardlimit = hardi;
    208       1.22    bouyer 				}
    209       1.22    bouyer 			}
    210       1.22    bouyer 			putprivs(id, quotatype, curprivs);
    211       1.22    bouyer 			freeprivs(curprivs);
    212       1.22    bouyer 		}
    213       1.22    bouyer 		exit(0);
    214       1.22    bouyer 	}
    215        1.1       cgd 	tmpfd = mkstemp(tmpfil);
    216        1.1       cgd 	fchown(tmpfd, getuid(), getgid());
    217        1.1       cgd 	if (tflag) {
    218       1.22    bouyer 		if (soft || hard)
    219       1.22    bouyer 			usage();
    220       1.22    bouyer 		protoprivs = getprivs(0, quotatype, fs);
    221        1.1       cgd 		if (writetimes(protoprivs, tmpfd, quotatype) == 0)
    222        1.1       cgd 			exit(1);
    223        1.1       cgd 		if (editit(tmpfil) && readtimes(protoprivs, tmpfd))
    224        1.1       cgd 			putprivs(0, quotatype, protoprivs);
    225        1.1       cgd 		freeprivs(protoprivs);
    226        1.1       cgd 		exit(0);
    227        1.1       cgd 	}
    228        1.1       cgd 	for ( ; argc > 0; argc--, argv++) {
    229        1.1       cgd 		if ((id = getentry(*argv, quotatype)) == -1)
    230        1.1       cgd 			continue;
    231       1.22    bouyer 		curprivs = getprivs(id, quotatype, fs);
    232        1.1       cgd 		if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0)
    233        1.1       cgd 			continue;
    234        1.1       cgd 		if (editit(tmpfil) && readprivs(curprivs, tmpfd))
    235        1.1       cgd 			putprivs(id, quotatype, curprivs);
    236        1.1       cgd 		freeprivs(curprivs);
    237        1.1       cgd 	}
    238        1.1       cgd 	close(tmpfd);
    239        1.1       cgd 	unlink(tmpfil);
    240        1.1       cgd 	exit(0);
    241        1.1       cgd }
    242        1.1       cgd 
    243       1.12     mikel void
    244        1.1       cgd usage()
    245        1.1       cgd {
    246       1.22    bouyer 	fprintf(stderr,
    247       1.25      jmmv 	    "usage: edquota [-u] [-p username] [-f filesystem] username ...\n"
    248       1.22    bouyer 	    "\tedquota -g [-p groupname] [-f filesystem] groupname ...\n"
    249       1.22    bouyer 	    "\tedquota [-u] [-f filesystem] [-s b#/i#] [-h b#/i#] username ...\n"
    250       1.22    bouyer 	    "\tedquota -g [-f filesystem] [-s b#/i#] [-h b#/i#] groupname ...\n"
    251       1.22    bouyer 	    "\tedquota [-u] [-f filesystem] -t\n"
    252       1.22    bouyer 	    "\tedquota -g [-f filesystem] -t\n"
    253       1.22    bouyer 	    );
    254        1.1       cgd 	exit(1);
    255        1.1       cgd }
    256        1.1       cgd 
    257        1.1       cgd /*
    258        1.1       cgd  * This routine converts a name for a particular quota type to
    259        1.1       cgd  * an identifier. This routine must agree with the kernel routine
    260        1.1       cgd  * getinoquota as to the interpretation of quota types.
    261        1.1       cgd  */
    262       1.12     mikel int
    263        1.1       cgd getentry(name, quotatype)
    264  1.27.18.1      yamt 	const char *name;
    265        1.1       cgd 	int quotatype;
    266        1.1       cgd {
    267        1.1       cgd 	struct passwd *pw;
    268        1.1       cgd 	struct group *gr;
    269        1.1       cgd 
    270        1.1       cgd 	if (alldigits(name))
    271        1.1       cgd 		return (atoi(name));
    272        1.1       cgd 	switch(quotatype) {
    273        1.1       cgd 	case USRQUOTA:
    274       1.15     lukem 		if ((pw = getpwnam(name)) != NULL)
    275        1.1       cgd 			return (pw->pw_uid);
    276       1.15     lukem 		warnx("%s: no such user", name);
    277        1.1       cgd 		break;
    278        1.1       cgd 	case GRPQUOTA:
    279       1.15     lukem 		if ((gr = getgrnam(name)) != NULL)
    280        1.1       cgd 			return (gr->gr_gid);
    281       1.15     lukem 		warnx("%s: no such group", name);
    282        1.1       cgd 		break;
    283        1.1       cgd 	default:
    284       1.15     lukem 		warnx("%d: unknown quota type", quotatype);
    285        1.1       cgd 		break;
    286        1.1       cgd 	}
    287        1.1       cgd 	sleep(1);
    288        1.1       cgd 	return (-1);
    289        1.1       cgd }
    290        1.1       cgd 
    291        1.1       cgd /*
    292        1.1       cgd  * Collect the requested quota information.
    293        1.1       cgd  */
    294        1.1       cgd struct quotause *
    295       1.22    bouyer getprivs(id, quotatype, filesys)
    296       1.15     lukem 	long id;
    297        1.1       cgd 	int quotatype;
    298       1.22    bouyer 	char *filesys;
    299        1.1       cgd {
    300       1.15     lukem 	struct fstab *fs;
    301       1.15     lukem 	struct quotause *qup, *quptail;
    302        1.1       cgd 	struct quotause *quphead;
    303        1.1       cgd 	int qcmd, qupsize, fd;
    304        1.1       cgd 	char *qfpathname;
    305        1.1       cgd 	static int warned = 0;
    306        1.1       cgd 
    307        1.1       cgd 	setfsent();
    308       1.15     lukem 	quptail = NULL;
    309        1.1       cgd 	quphead = (struct quotause *)0;
    310        1.1       cgd 	qcmd = QCMD(Q_GETQUOTA, quotatype);
    311       1.15     lukem 	while ((fs = getfsent()) != NULL) {
    312       1.10       jtc 		if (strcmp(fs->fs_vfstype, "ffs"))
    313       1.22    bouyer 			continue;
    314       1.22    bouyer 		if (filesys && strcmp(fs->fs_spec, filesys) != 0 &&
    315       1.22    bouyer 		    strcmp(fs->fs_file, filesys) != 0)
    316        1.1       cgd 			continue;
    317        1.1       cgd 		if (!hasquota(fs, quotatype, &qfpathname))
    318        1.1       cgd 			continue;
    319        1.1       cgd 		qupsize = sizeof(*qup) + strlen(qfpathname);
    320       1.15     lukem 		if ((qup = (struct quotause *)malloc(qupsize)) == NULL)
    321       1.15     lukem 			errx(2, "out of memory");
    322        1.1       cgd 		if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
    323        1.1       cgd 	    		if (errno == EOPNOTSUPP && !warned) {
    324        1.1       cgd 				warned++;
    325       1.15     lukem 				warnx(
    326        1.1       cgd 				    "Quotas are not compiled into this kernel");
    327        1.1       cgd 				sleep(3);
    328        1.1       cgd 			}
    329        1.1       cgd 			if ((fd = open(qfpathname, O_RDONLY)) < 0) {
    330        1.1       cgd 				fd = open(qfpathname, O_RDWR|O_CREAT, 0640);
    331        1.1       cgd 				if (fd < 0 && errno != ENOENT) {
    332       1.15     lukem 					warnx("open `%s'", qfpathname);
    333        1.1       cgd 					free(qup);
    334        1.1       cgd 					continue;
    335        1.1       cgd 				}
    336       1.15     lukem 				warnx("Creating quota file %s", qfpathname);
    337        1.1       cgd 				sleep(3);
    338        1.1       cgd 				(void) fchown(fd, getuid(),
    339        1.1       cgd 				    getentry(quotagroup, GRPQUOTA));
    340        1.1       cgd 				(void) fchmod(fd, 0640);
    341        1.1       cgd 			}
    342       1.14    kleink 			(void)lseek(fd, (off_t)(id * sizeof(struct dqblk)),
    343       1.14    kleink 			    SEEK_SET);
    344        1.1       cgd 			switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
    345        1.1       cgd 			case 0:			/* EOF */
    346        1.1       cgd 				/*
    347        1.1       cgd 				 * Convert implicit 0 quota (EOF)
    348        1.1       cgd 				 * into an explicit one (zero'ed dqblk)
    349        1.1       cgd 				 */
    350       1.15     lukem 				memset((caddr_t)&qup->dqblk, 0,
    351        1.1       cgd 				    sizeof(struct dqblk));
    352        1.1       cgd 				break;
    353        1.1       cgd 
    354        1.1       cgd 			case sizeof(struct dqblk):	/* OK */
    355        1.1       cgd 				break;
    356        1.1       cgd 
    357        1.1       cgd 			default:		/* ERROR */
    358       1.15     lukem 				warn("read error in `%s'", qfpathname);
    359        1.1       cgd 				close(fd);
    360        1.1       cgd 				free(qup);
    361        1.1       cgd 				continue;
    362        1.1       cgd 			}
    363        1.1       cgd 			close(fd);
    364        1.1       cgd 		}
    365        1.1       cgd 		strcpy(qup->qfname, qfpathname);
    366        1.1       cgd 		strcpy(qup->fsname, fs->fs_file);
    367        1.1       cgd 		if (quphead == NULL)
    368        1.1       cgd 			quphead = qup;
    369        1.1       cgd 		else
    370        1.1       cgd 			quptail->next = qup;
    371        1.1       cgd 		quptail = qup;
    372        1.1       cgd 		qup->next = 0;
    373        1.1       cgd 	}
    374        1.1       cgd 	endfsent();
    375        1.1       cgd 	return (quphead);
    376        1.1       cgd }
    377        1.1       cgd 
    378        1.1       cgd /*
    379        1.1       cgd  * Store the requested quota information.
    380        1.1       cgd  */
    381       1.12     mikel void
    382        1.1       cgd putprivs(id, quotatype, quplist)
    383        1.1       cgd 	long id;
    384        1.1       cgd 	int quotatype;
    385        1.1       cgd 	struct quotause *quplist;
    386        1.1       cgd {
    387       1.15     lukem 	struct quotause *qup;
    388        1.1       cgd 	int qcmd, fd;
    389        1.1       cgd 
    390        1.1       cgd 	qcmd = QCMD(Q_SETQUOTA, quotatype);
    391        1.1       cgd 	for (qup = quplist; qup; qup = qup->next) {
    392        1.1       cgd 		if (quotactl(qup->fsname, qcmd, id, &qup->dqblk) == 0)
    393        1.1       cgd 			continue;
    394        1.1       cgd 		if ((fd = open(qup->qfname, O_WRONLY)) < 0) {
    395       1.15     lukem 			warnx("open `%s'", qup->qfname);
    396        1.1       cgd 		} else {
    397       1.14    kleink 			(void)lseek(fd,
    398       1.14    kleink 			    (off_t)(id * (long)sizeof (struct dqblk)),
    399       1.14    kleink 			    SEEK_SET);
    400        1.1       cgd 			if (write(fd, &qup->dqblk, sizeof (struct dqblk)) !=
    401       1.15     lukem 			    sizeof (struct dqblk))
    402       1.15     lukem 				warnx("writing `%s'", qup->qfname);
    403        1.1       cgd 			close(fd);
    404        1.1       cgd 		}
    405        1.1       cgd 	}
    406        1.1       cgd }
    407        1.1       cgd 
    408        1.1       cgd /*
    409       1.18    simonb  * Take a list of privileges and get it edited.
    410        1.1       cgd  */
    411       1.12     mikel int
    412  1.27.18.1      yamt editit(ltmpfile)
    413  1.27.18.1      yamt 	char *ltmpfile;
    414        1.1       cgd {
    415        1.1       cgd 	long omask;
    416  1.27.18.1      yamt 	int pid, lst;
    417       1.23      jonb 	char p[MAX_TMPSTR];
    418        1.1       cgd 
    419        1.1       cgd 	omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
    420        1.1       cgd  top:
    421        1.1       cgd 	if ((pid = fork()) < 0) {
    422        1.1       cgd 
    423        1.1       cgd 		if (errno == EPROCLIM) {
    424       1.15     lukem 			warnx("You have too many processes");
    425        1.1       cgd 			return(0);
    426        1.1       cgd 		}
    427        1.1       cgd 		if (errno == EAGAIN) {
    428        1.1       cgd 			sleep(1);
    429        1.1       cgd 			goto top;
    430        1.1       cgd 		}
    431       1.15     lukem 		warn("fork");
    432        1.1       cgd 		return (0);
    433        1.1       cgd 	}
    434        1.1       cgd 	if (pid == 0) {
    435  1.27.18.1      yamt 		const char *ed;
    436        1.1       cgd 
    437        1.1       cgd 		sigsetmask(omask);
    438        1.1       cgd 		setgid(getgid());
    439        1.1       cgd 		setuid(getuid());
    440        1.1       cgd 		if ((ed = getenv("EDITOR")) == (char *)0)
    441        1.1       cgd 			ed = _PATH_VI;
    442  1.27.18.1      yamt 		if (strlen(ed) + strlen(ltmpfile) + 2 >= MAX_TMPSTR) {
    443       1.23      jonb 			err (1, "%s", "editor or filename too long");
    444       1.23      jonb 		}
    445  1.27.18.1      yamt 		snprintf (p, MAX_TMPSTR, "%s %s", ed, ltmpfile);
    446       1.23      jonb 		execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", p, NULL);
    447       1.15     lukem 		err(1, "%s", ed);
    448        1.1       cgd 	}
    449  1.27.18.1      yamt 	waitpid(pid, &lst, 0);
    450        1.1       cgd 	sigsetmask(omask);
    451  1.27.18.1      yamt 	if (!WIFEXITED(lst) || WEXITSTATUS(lst) != 0)
    452        1.1       cgd 		return (0);
    453        1.1       cgd 	return (1);
    454        1.1       cgd }
    455        1.1       cgd 
    456        1.1       cgd /*
    457        1.1       cgd  * Convert a quotause list to an ASCII file.
    458        1.1       cgd  */
    459       1.12     mikel int
    460        1.1       cgd writeprivs(quplist, outfd, name, quotatype)
    461        1.1       cgd 	struct quotause *quplist;
    462        1.1       cgd 	int outfd;
    463        1.1       cgd 	char *name;
    464        1.1       cgd 	int quotatype;
    465        1.1       cgd {
    466       1.15     lukem 	struct quotause *qup;
    467        1.1       cgd 	FILE *fd;
    468        1.1       cgd 
    469        1.1       cgd 	ftruncate(outfd, 0);
    470       1.14    kleink 	(void)lseek(outfd, (off_t)0, SEEK_SET);
    471       1.15     lukem 	if ((fd = fdopen(dup(outfd), "w")) == NULL)
    472       1.15     lukem 		errx(1, "fdopen `%s'", tmpfil);
    473        1.1       cgd 	fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name);
    474        1.1       cgd 	for (qup = quplist; qup; qup = qup->next) {
    475        1.1       cgd 		fprintf(fd, "%s: %s %d, limits (soft = %d, hard = %d)\n",
    476        1.1       cgd 		    qup->fsname, "blocks in use:",
    477       1.19    bouyer 		    (int)(dbtob((u_quad_t)qup->dqblk.dqb_curblocks) / 1024),
    478       1.19    bouyer 		    (int)(dbtob((u_quad_t)qup->dqblk.dqb_bsoftlimit) / 1024),
    479       1.19    bouyer 		    (int)(dbtob((u_quad_t)qup->dqblk.dqb_bhardlimit) / 1024));
    480        1.1       cgd 		fprintf(fd, "%s %d, limits (soft = %d, hard = %d)\n",
    481        1.1       cgd 		    "\tinodes in use:", qup->dqblk.dqb_curinodes,
    482        1.1       cgd 		    qup->dqblk.dqb_isoftlimit, qup->dqblk.dqb_ihardlimit);
    483        1.1       cgd 	}
    484        1.1       cgd 	fclose(fd);
    485        1.1       cgd 	return (1);
    486        1.1       cgd }
    487        1.1       cgd 
    488        1.1       cgd /*
    489        1.1       cgd  * Merge changes to an ASCII file into a quotause list.
    490        1.1       cgd  */
    491       1.12     mikel int
    492        1.1       cgd readprivs(quplist, infd)
    493        1.1       cgd 	struct quotause *quplist;
    494        1.1       cgd 	int infd;
    495        1.1       cgd {
    496       1.15     lukem 	struct quotause *qup;
    497        1.1       cgd 	FILE *fd;
    498        1.1       cgd 	int cnt;
    499       1.15     lukem 	char *cp;
    500        1.1       cgd 	struct dqblk dqblk;
    501        1.1       cgd 	char *fsp, line1[BUFSIZ], line2[BUFSIZ];
    502        1.1       cgd 
    503       1.14    kleink 	(void)lseek(infd, (off_t)0, SEEK_SET);
    504        1.1       cgd 	fd = fdopen(dup(infd), "r");
    505        1.1       cgd 	if (fd == NULL) {
    506       1.15     lukem 		warn("Can't re-read temp file");
    507        1.1       cgd 		return (0);
    508        1.1       cgd 	}
    509        1.1       cgd 	/*
    510        1.1       cgd 	 * Discard title line, then read pairs of lines to process.
    511        1.1       cgd 	 */
    512        1.1       cgd 	(void) fgets(line1, sizeof (line1), fd);
    513        1.1       cgd 	while (fgets(line1, sizeof (line1), fd) != NULL &&
    514        1.1       cgd 	       fgets(line2, sizeof (line2), fd) != NULL) {
    515        1.1       cgd 		if ((fsp = strtok(line1, " \t:")) == NULL) {
    516       1.15     lukem 			warnx("%s: bad format", line1);
    517       1.26  christos 			goto out;
    518        1.1       cgd 		}
    519        1.1       cgd 		if ((cp = strtok((char *)0, "\n")) == NULL) {
    520       1.15     lukem 			warnx("%s: %s: bad format", fsp,
    521        1.1       cgd 			    &fsp[strlen(fsp) + 1]);
    522       1.26  christos 			goto out;
    523        1.1       cgd 		}
    524        1.1       cgd 		cnt = sscanf(cp,
    525        1.1       cgd 		    " blocks in use: %d, limits (soft = %d, hard = %d)",
    526        1.1       cgd 		    &dqblk.dqb_curblocks, &dqblk.dqb_bsoftlimit,
    527        1.1       cgd 		    &dqblk.dqb_bhardlimit);
    528        1.1       cgd 		if (cnt != 3) {
    529       1.15     lukem 			warnx("%s:%s: bad format", fsp, cp);
    530       1.26  christos 			goto out;
    531        1.1       cgd 		}
    532       1.19    bouyer 		dqblk.dqb_curblocks = btodb((u_quad_t)
    533       1.19    bouyer 		    dqblk.dqb_curblocks * 1024);
    534       1.19    bouyer 		dqblk.dqb_bsoftlimit = btodb((u_quad_t)
    535       1.19    bouyer 		    dqblk.dqb_bsoftlimit * 1024);
    536       1.19    bouyer 		dqblk.dqb_bhardlimit = btodb((u_quad_t)
    537       1.19    bouyer 		    dqblk.dqb_bhardlimit * 1024);
    538        1.1       cgd 		if ((cp = strtok(line2, "\n")) == NULL) {
    539       1.15     lukem 			warnx("%s: %s: bad format", fsp, line2);
    540       1.26  christos 			goto out;
    541        1.1       cgd 		}
    542        1.1       cgd 		cnt = sscanf(cp,
    543        1.1       cgd 		    "\tinodes in use: %d, limits (soft = %d, hard = %d)",
    544        1.1       cgd 		    &dqblk.dqb_curinodes, &dqblk.dqb_isoftlimit,
    545        1.1       cgd 		    &dqblk.dqb_ihardlimit);
    546        1.1       cgd 		if (cnt != 3) {
    547       1.15     lukem 			warnx("%s: %s: bad format", fsp, line2);
    548       1.27   jnemeth 			goto out;
    549        1.1       cgd 		}
    550        1.1       cgd 		for (qup = quplist; qup; qup = qup->next) {
    551        1.1       cgd 			if (strcmp(fsp, qup->fsname))
    552        1.1       cgd 				continue;
    553        1.1       cgd 			/*
    554        1.1       cgd 			 * Cause time limit to be reset when the quota
    555        1.1       cgd 			 * is next used if previously had no soft limit
    556        1.1       cgd 			 * or were under it, but now have a soft limit
    557        1.1       cgd 			 * and are over it.
    558        1.1       cgd 			 */
    559        1.1       cgd 			if (dqblk.dqb_bsoftlimit &&
    560        1.1       cgd 			    qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit &&
    561        1.1       cgd 			    (qup->dqblk.dqb_bsoftlimit == 0 ||
    562        1.1       cgd 			     qup->dqblk.dqb_curblocks <
    563        1.1       cgd 			     qup->dqblk.dqb_bsoftlimit))
    564        1.1       cgd 				qup->dqblk.dqb_btime = 0;
    565        1.1       cgd 			if (dqblk.dqb_isoftlimit &&
    566        1.1       cgd 			    qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit &&
    567        1.1       cgd 			    (qup->dqblk.dqb_isoftlimit == 0 ||
    568        1.1       cgd 			     qup->dqblk.dqb_curinodes <
    569        1.1       cgd 			     qup->dqblk.dqb_isoftlimit))
    570        1.1       cgd 				qup->dqblk.dqb_itime = 0;
    571        1.1       cgd 			qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit;
    572        1.1       cgd 			qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit;
    573        1.1       cgd 			qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit;
    574        1.1       cgd 			qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit;
    575        1.1       cgd 			qup->flags |= FOUND;
    576        1.1       cgd 			if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks &&
    577        1.1       cgd 			    dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
    578        1.1       cgd 				break;
    579       1.15     lukem 			warnx("%s: cannot change current allocation", fsp);
    580        1.1       cgd 			break;
    581        1.1       cgd 		}
    582        1.1       cgd 	}
    583       1.27   jnemeth out:
    584        1.1       cgd 	fclose(fd);
    585        1.1       cgd 	/*
    586        1.1       cgd 	 * Disable quotas for any filesystems that have not been found.
    587        1.1       cgd 	 */
    588        1.1       cgd 	for (qup = quplist; qup; qup = qup->next) {
    589        1.1       cgd 		if (qup->flags & FOUND) {
    590        1.1       cgd 			qup->flags &= ~FOUND;
    591        1.1       cgd 			continue;
    592        1.1       cgd 		}
    593        1.1       cgd 		qup->dqblk.dqb_bsoftlimit = 0;
    594        1.1       cgd 		qup->dqblk.dqb_bhardlimit = 0;
    595        1.1       cgd 		qup->dqblk.dqb_isoftlimit = 0;
    596        1.1       cgd 		qup->dqblk.dqb_ihardlimit = 0;
    597        1.1       cgd 	}
    598        1.1       cgd 	return (1);
    599        1.1       cgd }
    600        1.1       cgd 
    601        1.1       cgd /*
    602        1.1       cgd  * Convert a quotause list to an ASCII file of grace times.
    603        1.1       cgd  */
    604       1.12     mikel int
    605        1.1       cgd writetimes(quplist, outfd, quotatype)
    606        1.1       cgd 	struct quotause *quplist;
    607        1.1       cgd 	int outfd;
    608        1.1       cgd 	int quotatype;
    609        1.1       cgd {
    610       1.15     lukem 	struct quotause *qup;
    611        1.1       cgd 	FILE *fd;
    612        1.1       cgd 
    613        1.1       cgd 	ftruncate(outfd, 0);
    614       1.14    kleink 	(void)lseek(outfd, (off_t)0, SEEK_SET);
    615       1.15     lukem 	if ((fd = fdopen(dup(outfd), "w")) == NULL)
    616       1.15     lukem 		err(1, "fdopen `%s'", tmpfil);
    617        1.1       cgd 	fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n");
    618        1.1       cgd 	fprintf(fd, "Grace period before enforcing soft limits for %ss:\n",
    619        1.1       cgd 	    qfextension[quotatype]);
    620        1.1       cgd 	for (qup = quplist; qup; qup = qup->next) {
    621        1.1       cgd 		fprintf(fd, "%s: block grace period: %s, ",
    622        1.1       cgd 		    qup->fsname, cvtstoa(qup->dqblk.dqb_btime));
    623        1.1       cgd 		fprintf(fd, "file grace period: %s\n",
    624        1.1       cgd 		    cvtstoa(qup->dqblk.dqb_itime));
    625        1.1       cgd 	}
    626        1.1       cgd 	fclose(fd);
    627        1.1       cgd 	return (1);
    628        1.1       cgd }
    629        1.1       cgd 
    630        1.1       cgd /*
    631        1.1       cgd  * Merge changes of grace times in an ASCII file into a quotause list.
    632        1.1       cgd  */
    633       1.12     mikel int
    634        1.1       cgd readtimes(quplist, infd)
    635        1.1       cgd 	struct quotause *quplist;
    636        1.1       cgd 	int infd;
    637        1.1       cgd {
    638       1.15     lukem 	struct quotause *qup;
    639        1.1       cgd 	FILE *fd;
    640        1.1       cgd 	int cnt;
    641       1.15     lukem 	char *cp;
    642       1.15     lukem 	long litime, lbtime;
    643        1.1       cgd 	time_t itime, btime, iseconds, bseconds;
    644        1.1       cgd 	char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
    645        1.1       cgd 
    646       1.14    kleink 	(void)lseek(infd, (off_t)0, SEEK_SET);
    647        1.1       cgd 	fd = fdopen(dup(infd), "r");
    648        1.1       cgd 	if (fd == NULL) {
    649       1.15     lukem 		warnx("Can't re-read temp file!!");
    650        1.1       cgd 		return (0);
    651        1.1       cgd 	}
    652        1.1       cgd 	/*
    653        1.1       cgd 	 * Discard two title lines, then read lines to process.
    654        1.1       cgd 	 */
    655        1.1       cgd 	(void) fgets(line1, sizeof (line1), fd);
    656        1.1       cgd 	(void) fgets(line1, sizeof (line1), fd);
    657        1.1       cgd 	while (fgets(line1, sizeof (line1), fd) != NULL) {
    658        1.1       cgd 		if ((fsp = strtok(line1, " \t:")) == NULL) {
    659       1.15     lukem 			warnx("%s: bad format", line1);
    660       1.26  christos 			goto bad;
    661        1.1       cgd 		}
    662        1.1       cgd 		if ((cp = strtok((char *)0, "\n")) == NULL) {
    663       1.15     lukem 			warnx("%s: %s: bad format", fsp,
    664        1.1       cgd 			    &fsp[strlen(fsp) + 1]);
    665       1.26  christos 			goto bad;
    666        1.1       cgd 		}
    667        1.1       cgd 		cnt = sscanf(cp,
    668       1.15     lukem 		    " block grace period: %ld %s file grace period: %ld %s",
    669       1.15     lukem 		    &lbtime, bunits, &litime, iunits);
    670        1.1       cgd 		if (cnt != 4) {
    671       1.15     lukem 			warnx("%s:%s: bad format", fsp, cp);
    672       1.26  christos 			goto bad;
    673        1.1       cgd 		}
    674       1.15     lukem 		itime = (time_t)litime;
    675       1.15     lukem 		btime = (time_t)lbtime;
    676        1.1       cgd 		if (cvtatos(btime, bunits, &bseconds) == 0)
    677       1.26  christos 			goto bad;
    678       1.26  christos 		if (cvtatos(itime, iunits, &iseconds) == 0) {
    679       1.26  christos bad:
    680       1.26  christos 			(void)fclose(fd);
    681        1.1       cgd 			return (0);
    682       1.26  christos 		}
    683        1.1       cgd 		for (qup = quplist; qup; qup = qup->next) {
    684        1.1       cgd 			if (strcmp(fsp, qup->fsname))
    685        1.1       cgd 				continue;
    686        1.1       cgd 			qup->dqblk.dqb_btime = bseconds;
    687        1.1       cgd 			qup->dqblk.dqb_itime = iseconds;
    688        1.1       cgd 			qup->flags |= FOUND;
    689        1.1       cgd 			break;
    690        1.1       cgd 		}
    691        1.1       cgd 	}
    692       1.26  christos 	(void)fclose(fd);
    693        1.1       cgd 	/*
    694        1.1       cgd 	 * reset default grace periods for any filesystems
    695        1.1       cgd 	 * that have not been found.
    696        1.1       cgd 	 */
    697        1.1       cgd 	for (qup = quplist; qup; qup = qup->next) {
    698        1.1       cgd 		if (qup->flags & FOUND) {
    699        1.1       cgd 			qup->flags &= ~FOUND;
    700        1.1       cgd 			continue;
    701        1.1       cgd 		}
    702        1.1       cgd 		qup->dqblk.dqb_btime = 0;
    703        1.1       cgd 		qup->dqblk.dqb_itime = 0;
    704        1.1       cgd 	}
    705        1.1       cgd 	return (1);
    706        1.1       cgd }
    707        1.1       cgd 
    708        1.1       cgd /*
    709        1.1       cgd  * Convert seconds to ASCII times.
    710        1.1       cgd  */
    711        1.1       cgd char *
    712  1.27.18.1      yamt cvtstoa(ltime)
    713  1.27.18.1      yamt 	time_t ltime;
    714        1.1       cgd {
    715        1.1       cgd 	static char buf[20];
    716        1.1       cgd 
    717  1.27.18.1      yamt 	if (ltime % (24 * 60 * 60) == 0) {
    718  1.27.18.1      yamt 		ltime /= 24 * 60 * 60;
    719  1.27.18.1      yamt 		snprintf(buf, sizeof buf, "%ld day%s", (long)ltime,
    720  1.27.18.1      yamt 		    ltime == 1 ? "" : "s");
    721  1.27.18.1      yamt 	} else if (ltime % (60 * 60) == 0) {
    722  1.27.18.1      yamt 		ltime /= 60 * 60;
    723  1.27.18.1      yamt 		sprintf(buf, "%ld hour%s", (long)ltime, ltime == 1 ? "" : "s");
    724  1.27.18.1      yamt 	} else if (ltime % 60 == 0) {
    725  1.27.18.1      yamt 		ltime /= 60;
    726  1.27.18.1      yamt 		sprintf(buf, "%ld minute%s", (long)ltime, ltime == 1 ? "" : "s");
    727        1.1       cgd 	} else
    728  1.27.18.1      yamt 		sprintf(buf, "%ld second%s", (long)ltime, ltime == 1 ? "" : "s");
    729        1.1       cgd 	return (buf);
    730        1.1       cgd }
    731        1.1       cgd 
    732        1.1       cgd /*
    733        1.1       cgd  * Convert ASCII input times to seconds.
    734        1.1       cgd  */
    735       1.12     mikel int
    736  1.27.18.1      yamt cvtatos(ltime, units, seconds)
    737  1.27.18.1      yamt 	time_t ltime;
    738        1.1       cgd 	char *units;
    739        1.1       cgd 	time_t *seconds;
    740        1.1       cgd {
    741        1.1       cgd 
    742       1.15     lukem 	if (memcmp(units, "second", 6) == 0)
    743  1.27.18.1      yamt 		*seconds = ltime;
    744       1.15     lukem 	else if (memcmp(units, "minute", 6) == 0)
    745  1.27.18.1      yamt 		*seconds = ltime * 60;
    746       1.15     lukem 	else if (memcmp(units, "hour", 4) == 0)
    747  1.27.18.1      yamt 		*seconds = ltime * 60 * 60;
    748       1.15     lukem 	else if (memcmp(units, "day", 3) == 0)
    749  1.27.18.1      yamt 		*seconds = ltime * 24 * 60 * 60;
    750        1.1       cgd 	else {
    751        1.1       cgd 		printf("%s: bad units, specify %s\n", units,
    752        1.1       cgd 		    "days, hours, minutes, or seconds");
    753        1.1       cgd 		return (0);
    754        1.1       cgd 	}
    755        1.1       cgd 	return (1);
    756        1.1       cgd }
    757        1.1       cgd 
    758        1.1       cgd /*
    759        1.1       cgd  * Free a list of quotause structures.
    760        1.1       cgd  */
    761       1.12     mikel void
    762        1.1       cgd freeprivs(quplist)
    763        1.1       cgd 	struct quotause *quplist;
    764        1.1       cgd {
    765       1.15     lukem 	struct quotause *qup, *nextqup;
    766        1.1       cgd 
    767        1.1       cgd 	for (qup = quplist; qup; qup = nextqup) {
    768        1.1       cgd 		nextqup = qup->next;
    769        1.1       cgd 		free(qup);
    770        1.1       cgd 	}
    771        1.1       cgd }
    772        1.1       cgd 
    773        1.1       cgd /*
    774        1.1       cgd  * Check whether a string is completely composed of digits.
    775        1.1       cgd  */
    776       1.12     mikel int
    777        1.1       cgd alldigits(s)
    778  1.27.18.1      yamt 	const char *s;
    779        1.1       cgd {
    780       1.15     lukem 	int c;
    781        1.1       cgd 
    782        1.1       cgd 	c = *s++;
    783        1.1       cgd 	do {
    784        1.1       cgd 		if (!isdigit(c))
    785        1.1       cgd 			return (0);
    786       1.15     lukem 	} while ((c = *s++) != 0);
    787        1.1       cgd 	return (1);
    788        1.1       cgd }
    789        1.1       cgd 
    790        1.1       cgd /*
    791        1.1       cgd  * Check to see if a particular quota is to be enabled.
    792        1.1       cgd  */
    793       1.12     mikel int
    794        1.1       cgd hasquota(fs, type, qfnamep)
    795       1.15     lukem 	struct fstab *fs;
    796        1.1       cgd 	int type;
    797        1.1       cgd 	char **qfnamep;
    798        1.1       cgd {
    799       1.15     lukem 	char *opt;
    800       1.15     lukem 	char *cp;
    801        1.1       cgd 	static char initname, usrname[100], grpname[100];
    802        1.1       cgd 	static char buf[BUFSIZ];
    803        1.1       cgd 
    804        1.1       cgd 	if (!initname) {
    805        1.1       cgd 		sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
    806        1.1       cgd 		sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
    807        1.1       cgd 		initname = 1;
    808        1.1       cgd 	}
    809        1.1       cgd 	strcpy(buf, fs->fs_mntops);
    810       1.16       mrg 	cp = NULL;
    811        1.1       cgd 	for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
    812       1.15     lukem 		if ((cp = strchr(opt, '=')) != NULL)
    813        1.1       cgd 			*cp++ = '\0';
    814        1.1       cgd 		if (type == USRQUOTA && strcmp(opt, usrname) == 0)
    815        1.1       cgd 			break;
    816        1.1       cgd 		if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
    817        1.1       cgd 			break;
    818        1.1       cgd 	}
    819        1.1       cgd 	if (!opt)
    820        1.1       cgd 		return (0);
    821        1.1       cgd 	if (cp) {
    822        1.1       cgd 		*qfnamep = cp;
    823        1.1       cgd 		return (1);
    824        1.1       cgd 	}
    825        1.1       cgd 	(void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
    826        1.1       cgd 	*qfnamep = buf;
    827        1.1       cgd 	return (1);
    828        1.1       cgd }
    829