quotacheck.c revision 124830
11541Srgrimes/* 21541Srgrimes * Copyright (c) 1980, 1990, 1993 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * 51541Srgrimes * This code is derived from software contributed to Berkeley by 61541Srgrimes * Robert Elz at The University of Melbourne. 71541Srgrimes * 81541Srgrimes * Redistribution and use in source and binary forms, with or without 91541Srgrimes * modification, are permitted provided that the following conditions 101541Srgrimes * are met: 111541Srgrimes * 1. Redistributions of source code must retain the above copyright 121541Srgrimes * notice, this list of conditions and the following disclaimer. 131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141541Srgrimes * notice, this list of conditions and the following disclaimer in the 151541Srgrimes * documentation and/or other materials provided with the distribution. 161541Srgrimes * 3. All advertising materials mentioning features or use of this software 171541Srgrimes * must display the following acknowledgement: 181541Srgrimes * This product includes software developed by the University of 191541Srgrimes * California, Berkeley and its contributors. 201541Srgrimes * 4. Neither the name of the University nor the names of its contributors 211541Srgrimes * may be used to endorse or promote products derived from this software 221541Srgrimes * without specific prior written permission. 231541Srgrimes * 241541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3450477Speter * SUCH DAMAGE. 351541Srgrimes */ 361541Srgrimes 372168Spaul#if 0 384507Sbde#ifndef lint 392168Spaulstatic const char copyright[] = 40104355Smike"@(#) Copyright (c) 1980, 1990, 1993\n\ 41104355Smike The Regents of the University of California. All rights reserved.\n"; 4297024Siedowse#endif /* not lint */ 4379103Sbrooks 4497024Siedowse#ifndef lint 4579103Sbrooksstatic char sccsid[] = "@(#)quotacheck.c 8.3 (Berkeley) 1/29/94"; 46104355Smike#endif /* not lint */ 471541Srgrimes#endif 4834750Speter#include <sys/cdefs.h> 4972093Sasmodai__FBSDID("$FreeBSD: head/sbin/quotacheck/quotacheck.c 124830 2004-01-22 07:23:36Z grehan $"); 5034750Speter 5155205Speter/* 5234750Speter * Fix up / report on disk quotas & usage 5334750Speter */ 5434750Speter#include <sys/param.h> 5579103Sbrooks#include <sys/disklabel.h> 56104355Smike#include <sys/stat.h> 5779103Sbrooks 5834750Speter#include <ufs/ufs/dinode.h> 5979103Sbrooks#include <ufs/ufs/quota.h> 6079103Sbrooks#include <ufs/ffs/fs.h> 6179103Sbrooks 62104355Smike#include <err.h> 63104355Smike#include <errno.h> 64104355Smike#include <fcntl.h> 6592081Smux#include <fstab.h> 66104355Smike#include <grp.h> 6779103Sbrooks#include <pwd.h> 6897024Siedowse#include <stdio.h> 6979103Sbrooks#include <stdlib.h> 7079103Sbrooks#include <string.h> 7179103Sbrooks#include <unistd.h> 7279103Sbrooks 7379103Sbrookschar *qfname = QUOTAFILENAME; 7479103Sbrookschar *qfextension[] = INITQFNAMES; 7579103Sbrookschar *quotagroup = QUOTAGROUP; 7697289Sbrooks 7792081Smuxunion { 7892081Smux struct fs sblk; 7992081Smux char dummy[MAXBSIZE]; 8079103Sbrooks} sb_un; 8192081Smux#define sblock sb_un.sblk 8297289Sbrooksunion { 8379103Sbrooks struct cg cgblk; 8479103Sbrooks char dummy[MAXBSIZE]; 8597289Sbrooks} cg_un; 8697289Sbrooks#define cgblk cg_un.cgblk 8797024Siedowselong dev_bsize = 1; 8879103Sbrooksino_t maxino; 89104355Smike 90104355Smikeunion dinode { 9179103Sbrooks struct ufs1_dinode dp1; 9279103Sbrooks struct ufs2_dinode dp2; 9379103Sbrooks}; 9479103Sbrooks#define DIP(dp, field) \ 9579103Sbrooks ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ 9679103Sbrooks (dp)->dp1.field : (dp)->dp2.field) 9779103Sbrooks 9879103Sbrooksstruct quotaname { 9979103Sbrooks long flags; 10079103Sbrooks char grpqfname[PATH_MAX]; 10179103Sbrooks char usrqfname[PATH_MAX]; 10219079Sfenner}; 10319079Sfenner#define HASUSR 1 10419079Sfenner#define HASGRP 2 1059457Sjoerg 1069457Sjoergstruct fileusage { 1079457Sjoerg struct fileusage *fu_next; 1089457Sjoerg u_long fu_curinodes; 1099457Sjoerg u_long fu_curblocks; 1109457Sjoerg u_long fu_id; 11117352Swollman char fu_name[1]; 11217352Swollman /* actually bigger */ 1139457Sjoerg}; 1149457Sjoerg#define FUHASH 1024 /* must be power of two */ 1159457Sjoergstruct fileusage *fuhead[MAXQUOTAS][FUHASH]; 1169457Sjoerg 1179457Sjoergint aflag; /* all file systems */ 1189457Sjoergint gflag; /* check group quotas */ 1199457Sjoergint uflag; /* check user quotas */ 1209457Sjoergint vflag; /* verbose */ 1219457Sjoergint fi; /* open disk file descriptor */ 1229457Sjoergu_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ 1239457Sjoerg 1249457Sjoergstruct fileusage * 1259457Sjoerg addid(u_long, int, char *); 1269457Sjoergchar *blockcheck(char *); 1279457Sjoergvoid bread(ufs2_daddr_t, char *, long); 12858698Sjlemonextern int checkfstab(int, int, void * (*)(struct fstab *), 12958698Sjlemon int (*)(char *, char *, struct quotaname *)); 13016287Sgpalmerint chkquota(char *, char *, struct quotaname *); 1319457Sjoergvoid freeinodebuf(void); 1329457Sjoergunion dinode * 1331541Srgrimes getnextinode(ino_t); 1341541Srgrimesint getquotagid(void); 1351541Srgrimesint hasquota(struct fstab *, int, char **); 1361541Srgrimesstruct fileusage * 1371541Srgrimes lookup(u_long, int); 13847777Sphkvoid *needchk(struct fstab *); 1391541Srgrimesint oneof(char *, char*[], int); 1401541Srgrimesvoid setinodebuf(ino_t); 1411541Srgrimesint update(char *, char *, int); 1421541Srgrimesvoid usage(void); 1431541Srgrimes 1441541Srgrimesint 1451541Srgrimesmain(argc, argv) 1461541Srgrimes int argc; 1471541Srgrimes char *argv[]; 1483274Swollman{ 1491541Srgrimes struct fstab *fs; 15087902Sluigi struct passwd *pw; 151102099Ssobomax struct group *gr; 152104044Sphk struct quotaname *auxdata; 153120626Sru int i, argnum, maxrun, errs, ch; 15487902Sluigi long done = 0; 1551541Srgrimes char *name; 1561541Srgrimes 1571541Srgrimes errs = maxrun = 0; 158102526Ssobomax while ((ch = getopt(argc, argv, "aguvl:")) != -1) { 159102526Ssobomax switch(ch) { 1601541Srgrimes case 'a': 161106925Ssam aflag++; 162106925Ssam break; 163106925Ssam case 'g': 164106925Ssam gflag++; 165106925Ssam break; 166106925Ssam case 'u': 167106925Ssam uflag++; 168106925Ssam break; 16983624Sjlemon case 'v': 17083636Sjlemon vflag++; 17183636Sjlemon break; 17283636Sjlemon case 'l': 173106925Ssam maxrun = atoi(optarg); 174106925Ssam break; 175106925Ssam default: 17683624Sjlemon usage(); 17783636Sjlemon } 17883636Sjlemon } 1791541Srgrimes argc -= optind; 1801541Srgrimes argv += optind; 1811541Srgrimes if ((argc == 0 && !aflag) || (argc > 0 && aflag)) 1821541Srgrimes usage(); 1831541Srgrimes if (!gflag && !uflag) { 1841541Srgrimes gflag++; 1851541Srgrimes uflag++; 1861541Srgrimes } 1871541Srgrimes if (gflag) { 18872093Sasmodai setgrent(); 1891541Srgrimes while ((gr = getgrent()) != NULL) 1901541Srgrimes (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name); 1911541Srgrimes endgrent(); 1921541Srgrimes } 1931541Srgrimes if (uflag) { 1941541Srgrimes setpwent(); 1951541Srgrimes while ((pw = getpwent()) != NULL) 1961541Srgrimes (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name); 1971541Srgrimes endpwent(); 1981541Srgrimes } 1991541Srgrimes if (aflag) 2001541Srgrimes exit(checkfstab(1, maxrun, needchk, chkquota)); 2011541Srgrimes if (setfsent() == 0) 20272093Sasmodai errx(1, "%s: can't open", FSTAB); 2031541Srgrimes while ((fs = getfsent()) != NULL) { 2041541Srgrimes if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 2051541Srgrimes (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) && 2061541Srgrimes (auxdata = needchk(fs)) && 2071541Srgrimes (name = blockcheck(fs->fs_spec))) { 2081541Srgrimes done |= 1 << argnum; 2091541Srgrimes errs += chkquota(name, fs->fs_file, auxdata); 2101541Srgrimes } 21121666Swollman } 21221666Swollman endfsent(); 21321666Swollman for (i = 0; i < argc; i++) 21421666Swollman if ((done & (1 << i)) == 0) 21521666Swollman fprintf(stderr, "%s not found in %s\n", 21672093Sasmodai argv[i], FSTAB); 21721666Swollman exit(errs); 21821666Swollman} 21921666Swollman 22021666Swollmanvoid 22121666Swollmanusage() 22221666Swollman{ 22321666Swollman (void)fprintf(stderr, "%s\n%s\n", 22489498Sru "usage: quotacheck -a [-guv]", 22589498Sru " quotacheck [-guv] filesystem ..."); 22689498Sru exit(1); 22789498Sru} 22889498Sru 22989498Sruvoid * 23089498Sruneedchk(fs) 23189498Sru struct fstab *fs; 23289498Sru{ 23389498Sru struct quotaname *qnp; 23489498Sru char *qfnp; 23589498Sru 23689498Sru if (strcmp(fs->fs_vfstype, "ufs") || 23789498Sru strcmp(fs->fs_type, FSTAB_RW)) 23889498Sru return (NULL); 2391541Srgrimes if ((qnp = malloc(sizeof(*qnp))) == NULL) 2401541Srgrimes errx(1, "malloc failed"); 2411541Srgrimes qnp->flags = 0; 2421541Srgrimes if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) { 2431541Srgrimes strcpy(qnp->grpqfname, qfnp); 2441541Srgrimes qnp->flags |= HASGRP; 2451541Srgrimes } 2461541Srgrimes if (uflag && hasquota(fs, USRQUOTA, &qfnp)) { 2471541Srgrimes strcpy(qnp->usrqfname, qfnp); 2481541Srgrimes qnp->flags |= HASUSR; 2491541Srgrimes } 25044144Sphk if (qnp->flags) 25185079Sjlemon return (qnp); 2521541Srgrimes free(qnp); 2531941Sdg return (NULL); 2545184Swollman} 25525434Speter 2561541Srgrimes/* 25783624Sjlemon * Possible superblock locations ordered from most to least likely. 2581541Srgrimes */ 2591541Srgrimesstatic int sblock_try[] = SBLOCKSEARCH; 2601541Srgrimes 2611541Srgrimes/* 262102052Ssobomax * Scan the specified file system to check quota(s) present on it. 263102052Ssobomax */ 2641541Srgrimesint 2651941Sdgchkquota(fsname, mntpt, qnp) 2665187Sdg char *fsname, *mntpt; 26725434Speter struct quotaname *qnp; 2681541Srgrimes{ 26983624Sjlemon struct fileusage *fup; 27083624Sjlemon union dinode *dp; 27185079Sjlemon int cg, i, mode, errs = 0; 2721541Srgrimes ino_t ino, inosused; 2731541Srgrimes char *cp; 27432491Swollman 27532491Swollman if ((fi = open(fsname, O_RDONLY, 0)) < 0) { 27632491Swollman warn("%s", fsname); 27732491Swollman return (1); 27832491Swollman } 2791541Srgrimes if (vflag) { 2801541Srgrimes (void)printf("*** Checking "); 2811541Srgrimes if (qnp->flags & HASUSR) 2821541Srgrimes (void)printf("%s%s", qfextension[USRQUOTA], 2831541Srgrimes (qnp->flags & HASGRP) ? " and " : ""); 2841541Srgrimes if (qnp->flags & HASGRP) 2851541Srgrimes (void)printf("%s", qfextension[GRPQUOTA]); 28625434Speter (void)printf(" quotas for %s (%s)\n", fsname, mntpt); 28725434Speter } 28825434Speter sync(); 28925434Speter dev_bsize = 1; 29025434Speter for (i = 0; sblock_try[i] != -1; i++) { 29125434Speter bread(sblock_try[i], (char *)&sblock, (long)SBLOCKSIZE); 29225434Speter if ((sblock.fs_magic == FS_UFS1_MAGIC || 29325434Speter (sblock.fs_magic == FS_UFS2_MAGIC && 29425434Speter sblock.fs_sblockloc == sblock_try[i])) && 29548021Sphk sblock.fs_bsize <= MAXBSIZE && 29648021Sphk sblock.fs_bsize >= sizeof(struct fs)) 29748021Sphk break; 29848589Sbde } 29948021Sphk if (sblock_try[i] == -1) { 30048589Sbde warn("Cannot find file system superblock"); 30148021Sphk return (1); 30248021Sphk } 30348589Sbde dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); 30448021Sphk maxino = sblock.fs_ncg * sblock.fs_ipg; 30548589Sbde for (cg = 0; cg < sblock.fs_ncg; cg++) { 30648589Sbde ino = cg * sblock.fs_ipg; 30748021Sphk setinodebuf(ino); 30848021Sphk bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)(&cgblk), 3091541Srgrimes sblock.fs_cgsize); 3101541Srgrimes if (sblock.fs_magic == FS_UFS2_MAGIC) 3111541Srgrimes inosused = cgblk.cg_initediblk; 3121541Srgrimes else 3131541Srgrimes inosused = sblock.fs_ipg; 3141541Srgrimes /* 3151541Srgrimes * If we are using soft updates, then we can trust the 3161541Srgrimes * cylinder group inode allocation maps to tell us which 3171541Srgrimes * inodes are allocated. We will scan the used inode map 3181541Srgrimes * to find the inodes that are really in use, and then 3191541Srgrimes * read only those inodes in from disk. 3201541Srgrimes */ 3211541Srgrimes if (sblock.fs_flags & FS_DOSOFTDEP) { 3221541Srgrimes if (!cg_chkmagic(&cgblk)) 3231541Srgrimes errx(1, "CG %d: BAD MAGIC NUMBER\n", cg); 3241541Srgrimes cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT]; 32552904Sshin for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) { 32652904Sshin if (*cp == 0) 32752904Sshin continue; 32852904Sshin for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) { 32952904Sshin if (*cp & i) 33052904Sshin break; 33152904Sshin inosused--; 33252904Sshin } 33352904Sshin break; 33452904Sshin } 33552904Sshin if (inosused <= 0) 33652904Sshin continue; 33752904Sshin } 338104355Smike for (i = 0; i < inosused; i++, ino++) { 339104355Smike if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO || 34055205Speter (mode = DIP(dp, di_mode) & IFMT) == 0) 34130354Sphk continue; 34230354Sphk if (qnp->flags & HASGRP) { 34330354Sphk fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA, 34430354Sphk (char *)0); 34530354Sphk fup->fu_curinodes++; 34630354Sphk if (mode == IFREG || mode == IFDIR || 34755205Speter mode == IFLNK) 34852904Sshin fup->fu_curblocks += DIP(dp, di_blocks); 349104355Smike } 350104360Smike if (qnp->flags & HASUSR) { 35152904Sshin fup = addid((u_long)DIP(dp, di_uid), USRQUOTA, 35252904Sshin (char *)0); 35352904Sshin fup->fu_curinodes++; 354104360Smike if (mode == IFREG || mode == IFDIR || 355104360Smike mode == IFLNK) 356104360Smike fup->fu_curblocks += DIP(dp, di_blocks); 357104360Smike } 35852904Sshin } 35952904Sshin } 36052904Sshin freeinodebuf(); 36155205Speter if (qnp->flags & HASUSR) 36283366Sjulian errs += update(mntpt, qnp->usrqfname, USRQUOTA); 36348589Sbde if (qnp->flags & HASGRP) 36448589Sbde errs += update(mntpt, qnp->grpqfname, GRPQUOTA); 36521259Swollman close(fi); 3661541Srgrimes return (errs); 3671541Srgrimes} 3684507Sbde 369/* 370 * Update a specified quota file. 371 */ 372int 373update(fsname, quotafile, type) 374 char *fsname, *quotafile; 375 int type; 376{ 377 struct fileusage *fup; 378 FILE *qfi, *qfo; 379 u_long id, lastid; 380 off_t offset; 381 struct dqblk dqbuf; 382 static int warned = 0; 383 static struct dqblk zerodqbuf; 384 static struct fileusage zerofileusage; 385 386 if ((qfo = fopen(quotafile, "r+")) == NULL) { 387 if (errno == ENOENT) 388 qfo = fopen(quotafile, "w+"); 389 if (qfo) { 390 warnx("creating quota file %s", quotafile); 391#define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 392 (void) fchown(fileno(qfo), getuid(), getquotagid()); 393 (void) fchmod(fileno(qfo), MODE); 394 } else { 395 warn("%s", quotafile); 396 return (1); 397 } 398 } 399 if ((qfi = fopen(quotafile, "r")) == NULL) { 400 warn("%s", quotafile); 401 (void) fclose(qfo); 402 return (1); 403 } 404 if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 && 405 errno == EOPNOTSUPP && !warned && vflag) { 406 warned++; 407 (void)printf("*** Warning: %s\n", 408 "Quotas are not compiled into this kernel"); 409 } 410 for (lastid = highid[type], id = 0, offset = 0; id <= lastid; 411 id++, offset += sizeof(struct dqblk)) { 412 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 413 dqbuf = zerodqbuf; 414 if ((fup = lookup(id, type)) == 0) 415 fup = &zerofileusage; 416 if (dqbuf.dqb_curinodes == fup->fu_curinodes && 417 dqbuf.dqb_curblocks == fup->fu_curblocks) { 418 fup->fu_curinodes = 0; 419 fup->fu_curblocks = 0; 420 continue; 421 } 422 if (vflag) { 423 if (aflag) 424 printf("%s: ", fsname); 425 printf("%-8s fixed:", fup->fu_name); 426 if (dqbuf.dqb_curinodes != fup->fu_curinodes) 427 (void)printf("\tinodes %lu -> %lu", 428 (u_long)dqbuf.dqb_curinodes, 429 (u_long)fup->fu_curinodes); 430 if (dqbuf.dqb_curblocks != fup->fu_curblocks) 431 (void)printf("\tblocks %lu -> %lu", 432 (u_long)dqbuf.dqb_curblocks, 433 (u_long)fup->fu_curblocks); 434 (void)printf("\n"); 435 } 436 /* 437 * Reset time limit if have a soft limit and were 438 * previously under it, but are now over it. 439 */ 440 if (dqbuf.dqb_bsoftlimit && 441 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 442 fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) 443 dqbuf.dqb_btime = 0; 444 if (dqbuf.dqb_isoftlimit && 445 dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit && 446 fup->fu_curblocks >= dqbuf.dqb_isoftlimit) 447 dqbuf.dqb_itime = 0; 448 dqbuf.dqb_curinodes = fup->fu_curinodes; 449 dqbuf.dqb_curblocks = fup->fu_curblocks; 450 if (fseek(qfo, offset, SEEK_SET) < 0) { 451 warn("%s: seek failed", quotafile); 452 return(1); 453 } 454 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 455 (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, 456 (caddr_t)&dqbuf); 457 fup->fu_curinodes = 0; 458 fup->fu_curblocks = 0; 459 } 460 fclose(qfi); 461 fflush(qfo); 462 ftruncate(fileno(qfo), 463 (((off_t)highid[type] + 1) * sizeof(struct dqblk))); 464 fclose(qfo); 465 return (0); 466} 467 468/* 469 * Check to see if target appears in list of size cnt. 470 */ 471int 472oneof(target, list, cnt) 473 char *target, *list[]; 474 int cnt; 475{ 476 int i; 477 478 for (i = 0; i < cnt; i++) 479 if (strcmp(target, list[i]) == 0) 480 return (i); 481 return (-1); 482} 483 484/* 485 * Determine the group identifier for quota files. 486 */ 487int 488getquotagid() 489{ 490 struct group *gr; 491 492 if ((gr = getgrnam(quotagroup)) != NULL) 493 return (gr->gr_gid); 494 return (-1); 495} 496 497/* 498 * Check to see if a particular quota is to be enabled. 499 */ 500int 501hasquota(fs, type, qfnamep) 502 struct fstab *fs; 503 int type; 504 char **qfnamep; 505{ 506 char *opt; 507 char *cp; 508 static char initname, usrname[100], grpname[100]; 509 static char buf[BUFSIZ]; 510 511 if (!initname) { 512 (void)snprintf(usrname, sizeof(usrname), 513 "%s%s", qfextension[USRQUOTA], qfname); 514 (void)snprintf(grpname, sizeof(grpname), 515 "%s%s", qfextension[GRPQUOTA], qfname); 516 initname = 1; 517 } 518 strcpy(buf, fs->fs_mntops); 519 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 520 if ((cp = index(opt, '=')) != NULL) 521 *cp++ = '\0'; 522 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 523 break; 524 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 525 break; 526 } 527 if (!opt) 528 return (0); 529 if (cp) 530 *qfnamep = cp; 531 else { 532 (void)snprintf(buf, sizeof(buf), 533 "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 534 *qfnamep = buf; 535 } 536 return (1); 537} 538 539/* 540 * Routines to manage the file usage table. 541 * 542 * Lookup an id of a specific type. 543 */ 544struct fileusage * 545lookup(id, type) 546 u_long id; 547 int type; 548{ 549 struct fileusage *fup; 550 551 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 552 if (fup->fu_id == id) 553 return (fup); 554 return (NULL); 555} 556 557/* 558 * Add a new file usage id if it does not already exist. 559 */ 560struct fileusage * 561addid(id, type, name) 562 u_long id; 563 int type; 564 char *name; 565{ 566 struct fileusage *fup, **fhp; 567 int len; 568 569 if ((fup = lookup(id, type)) != NULL) 570 return (fup); 571 if (name) 572 len = strlen(name); 573 else 574 len = 10; 575 if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) 576 errx(1, "calloc failed"); 577 fhp = &fuhead[type][id & (FUHASH - 1)]; 578 fup->fu_next = *fhp; 579 *fhp = fup; 580 fup->fu_id = id; 581 if (id > highid[type]) 582 highid[type] = id; 583 if (name) 584 bcopy(name, fup->fu_name, len + 1); 585 else { 586 (void)sprintf(fup->fu_name, "%lu", id); 587 if (vflag) 588 printf("unknown %cid: %lu\n", 589 type == USRQUOTA ? 'u' : 'g', id); 590 } 591 return (fup); 592} 593 594/* 595 * Special purpose version of ginode used to optimize pass 596 * over all the inodes in numerical order. 597 */ 598static ino_t nextino, lastinum, lastvalidinum; 599static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 600static caddr_t inodebuf; 601#define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ 602 603union dinode * 604getnextinode(ino_t inumber) 605{ 606 long size; 607 ufs2_daddr_t dblk; 608 union dinode *dp; 609 static caddr_t nextinop; 610 611 if (inumber != nextino++ || inumber > lastvalidinum) 612 errx(1, "bad inode number %d to nextinode", inumber); 613 if (inumber >= lastinum) { 614 readcnt++; 615 dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); 616 if (readcnt % readpercg == 0) { 617 size = partialsize; 618 lastinum += partialcnt; 619 } else { 620 size = inobufsize; 621 lastinum += fullcnt; 622 } 623 /* 624 * If bread returns an error, it will already have zeroed 625 * out the buffer, so we do not need to do so here. 626 */ 627 bread(dblk, inodebuf, size); 628 nextinop = inodebuf; 629 } 630 dp = (union dinode *)nextinop; 631 if (sblock.fs_magic == FS_UFS1_MAGIC) 632 nextinop += sizeof(struct ufs1_dinode); 633 else 634 nextinop += sizeof(struct ufs2_dinode); 635 return (dp); 636} 637 638/* 639 * Prepare to scan a set of inodes. 640 */ 641void 642setinodebuf(ino_t inum) 643{ 644 645 if (inum % sblock.fs_ipg != 0) 646 errx(1, "bad inode number %d to setinodebuf", inum); 647 lastvalidinum = inum + sblock.fs_ipg - 1; 648 nextino = inum; 649 lastinum = inum; 650 readcnt = 0; 651 if (inodebuf != NULL) 652 return; 653 inobufsize = blkroundup(&sblock, INOBUFSIZE); 654 fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ? 655 sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 656 readpercg = sblock.fs_ipg / fullcnt; 657 partialcnt = sblock.fs_ipg % fullcnt; 658 partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ? 659 sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 660 if (partialcnt != 0) { 661 readpercg++; 662 } else { 663 partialcnt = fullcnt; 664 partialsize = inobufsize; 665 } 666 if ((inodebuf = malloc((unsigned)inobufsize)) == NULL) 667 errx(1, "cannot allocate space for inode buffer"); 668} 669 670/* 671 * Free up data structures used to scan inodes. 672 */ 673void 674freeinodebuf() 675{ 676 677 if (inodebuf != NULL) 678 free(inodebuf); 679 inodebuf = NULL; 680} 681 682/* 683 * Read specified disk blocks. 684 */ 685void 686bread(bno, buf, cnt) 687 ufs2_daddr_t bno; 688 char *buf; 689 long cnt; 690{ 691 692 if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 || 693 read(fi, buf, cnt) != cnt) 694 errx(1, "bread failed on block %ld", (long)bno); 695} 696