quotacheck.c revision 164071
1298192Sdelphij/* 2298192Sdelphij * Copyright (c) 1980, 1990, 1993 3298192Sdelphij * The Regents of the University of California. All rights reserved. 4298192Sdelphij * 5298192Sdelphij * This code is derived from software contributed to Berkeley by 6298192Sdelphij * Robert Elz at The University of Melbourne. 7298192Sdelphij * 8298192Sdelphij * Redistribution and use in source and binary forms, with or without 9298192Sdelphij * modification, are permitted provided that the following conditions 10298192Sdelphij * are met: 11298192Sdelphij * 1. Redistributions of source code must retain the above copyright 12298192Sdelphij * notice, this list of conditions and the following disclaimer. 13298192Sdelphij * 2. Redistributions in binary form must reproduce the above copyright 14298192Sdelphij * notice, this list of conditions and the following disclaimer in the 15298192Sdelphij * documentation and/or other materials provided with the distribution. 16298192Sdelphij * 4. Neither the name of the University nor the names of its contributors 17298192Sdelphij * may be used to endorse or promote products derived from this software 18298192Sdelphij * without specific prior written permission. 19298192Sdelphij * 20298192Sdelphij * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21298192Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22298192Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23298192Sdelphij * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24298192Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25298192Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26298192Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27298192Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28298192Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29298192Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30298192Sdelphij * SUCH DAMAGE. 31298192Sdelphij */ 32298192Sdelphij 33298192Sdelphij#if 0 34298192Sdelphij#ifndef lint 35298192Sdelphijstatic const char copyright[] = 36298192Sdelphij"@(#) Copyright (c) 1980, 1990, 1993\n\ 37298192Sdelphij The Regents of the University of California. All rights reserved.\n"; 38298192Sdelphij#endif /* not lint */ 39298192Sdelphij 40298192Sdelphij#ifndef lint 41298192Sdelphijstatic char sccsid[] = "@(#)quotacheck.c 8.3 (Berkeley) 1/29/94"; 42298192Sdelphij#endif /* not lint */ 43298192Sdelphij#endif 44298192Sdelphij#include <sys/cdefs.h> 45298192Sdelphij__FBSDID("$FreeBSD: head/sbin/quotacheck/quotacheck.c 164071 2006-11-07 19:07:52Z ceri $"); 46298192Sdelphij 47288143Sdelphij/* 48288143Sdelphij * Fix up / report on disk quotas & usage 49288143Sdelphij */ 50288143Sdelphij#include <sys/param.h> 51288143Sdelphij#include <sys/disklabel.h> 52288143Sdelphij#include <sys/stat.h> 53288143Sdelphij 54288143Sdelphij#include <ufs/ufs/dinode.h> 55288143Sdelphij#include <ufs/ufs/quota.h> 56288143Sdelphij#include <ufs/ffs/fs.h> 57288143Sdelphij 58288143Sdelphij#include <err.h> 59288143Sdelphij#include <errno.h> 60288143Sdelphij#include <fcntl.h> 61288143Sdelphij#include <fstab.h> 62288143Sdelphij#include <grp.h> 63287453Sdelphij#include <pwd.h> 64287453Sdelphij#include <stdio.h> 65287453Sdelphij#include <stdlib.h> 66287453Sdelphij#include <string.h> 67284277Sdelphij#include <unistd.h> 68284237Sdelphij 69284277Sdelphijchar *qfname = QUOTAFILENAME; 70284277Sdelphijchar *qfextension[] = INITQFNAMES; 71284237Sdelphijchar *quotagroup = QUOTAGROUP; 72284237Sdelphij 73284237Sdelphijunion { 74284237Sdelphij struct fs sblk; 75284237Sdelphij char dummy[MAXBSIZE]; 76284237Sdelphij} sb_un; 77284237Sdelphij#define sblock sb_un.sblk 78284237Sdelphijunion { 79284237Sdelphij struct cg cgblk; 80284237Sdelphij char dummy[MAXBSIZE]; 81284237Sdelphij} cg_un; 82284237Sdelphij#define cgblk cg_un.cgblk 83284237Sdelphijlong dev_bsize = 1; 84284237Sdelphijino_t maxino; 85284237Sdelphij 86284237Sdelphijunion dinode { 87284237Sdelphij struct ufs1_dinode dp1; 88284237Sdelphij struct ufs2_dinode dp2; 89284237Sdelphij}; 90284237Sdelphij#define DIP(dp, field) \ 91284237Sdelphij ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ 92284237Sdelphij (dp)->dp1.field : (dp)->dp2.field) 93284237Sdelphij 94284237Sdelphijstruct quotaname { 95284237Sdelphij long flags; 96284237Sdelphij char grpqfname[PATH_MAX]; 97284237Sdelphij char usrqfname[PATH_MAX]; 98284237Sdelphij}; 99284237Sdelphij#define HASUSR 1 100284237Sdelphij#define HASGRP 2 101284237Sdelphij 102284237Sdelphijstruct fileusage { 103284237Sdelphij struct fileusage *fu_next; 104284237Sdelphij u_long fu_curinodes; 105284237Sdelphij u_long fu_curblocks; 106284237Sdelphij u_long fu_id; 107284237Sdelphij char fu_name[1]; 108284237Sdelphij /* actually bigger */ 109284237Sdelphij}; 110284237Sdelphij#define FUHASH 1024 /* must be power of two */ 111284237Sdelphijstruct fileusage *fuhead[MAXQUOTAS][FUHASH]; 112284237Sdelphij 113284237Sdelphijint aflag; /* all file systems */ 114284237Sdelphijint gflag; /* check group quotas */ 115284237Sdelphijint uflag; /* check user quotas */ 116284237Sdelphijint vflag; /* verbose */ 117284237Sdelphijint fi; /* open disk file descriptor */ 118284237Sdelphiju_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ 119284237Sdelphij 120276577Sdelphijstruct fileusage * 121276577Sdelphij addid(u_long, int, char *); 122276577Sdelphijchar *blockcheck(char *); 123276577Sdelphijvoid bread(ufs2_daddr_t, char *, long); 124276577Sdelphijextern int checkfstab(int, int, void * (*)(struct fstab *), 125276577Sdelphij int (*)(char *, char *, struct quotaname *)); 126276577Sdelphijint chkquota(char *, char *, struct quotaname *); 127276577Sdelphijvoid freeinodebuf(void); 128276577Sdelphijunion dinode * 129276577Sdelphij getnextinode(ino_t); 130276577Sdelphijint getquotagid(void); 131276577Sdelphijint hasquota(struct fstab *, int, char **); 132276577Sdelphijstruct fileusage * 133276577Sdelphij lookup(u_long, int); 134276577Sdelphijvoid *needchk(struct fstab *); 135276577Sdelphijint oneof(char *, char*[], int); 136276577Sdelphijvoid setinodebuf(ino_t); 137276577Sdelphijint update(char *, char *, int); 138276577Sdelphijvoid usage(void); 139276577Sdelphij 140276577Sdelphijint 141276577Sdelphijmain(argc, argv) 142276577Sdelphij int argc; 143276577Sdelphij char *argv[]; 144276577Sdelphij{ 145276577Sdelphij struct fstab *fs; 146275698Sdelphij struct passwd *pw; 147275698Sdelphij struct group *gr; 148275698Sdelphij struct quotaname *auxdata; 149275698Sdelphij int i, argnum, maxrun, errs, ch; 150275698Sdelphij long done = 0; 151275698Sdelphij char *name; 152275698Sdelphij 153275698Sdelphij errs = maxrun = 0; 154275698Sdelphij while ((ch = getopt(argc, argv, "aguvl:")) != -1) { 155275698Sdelphij switch(ch) { 156275698Sdelphij case 'a': 157275698Sdelphij aflag++; 158275698Sdelphij break; 159275698Sdelphij case 'g': 160275698Sdelphij gflag++; 161275698Sdelphij break; 162275698Sdelphij case 'u': 163275698Sdelphij uflag++; 164275698Sdelphij break; 165275698Sdelphij case 'v': 166275698Sdelphij vflag++; 167275698Sdelphij break; 168275698Sdelphij case 'l': 169275698Sdelphij maxrun = atoi(optarg); 170275698Sdelphij break; 171276577Sdelphij default: 172276577Sdelphij usage(); 173275698Sdelphij } 174275698Sdelphij } 175275698Sdelphij argc -= optind; 176275698Sdelphij argv += optind; 177275698Sdelphij if ((argc == 0 && !aflag) || (argc > 0 && aflag)) 178275698Sdelphij usage(); 179275698Sdelphij if (!gflag && !uflag) { 180275698Sdelphij gflag++; 181275698Sdelphij uflag++; 182275698Sdelphij } 183275698Sdelphij if (gflag) { 184275698Sdelphij setgrent(); 185275698Sdelphij while ((gr = getgrent()) != NULL) 186275698Sdelphij (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name); 187275698Sdelphij endgrent(); 188275698Sdelphij } 189275698Sdelphij if (uflag) { 190275698Sdelphij setpwent(); 191275698Sdelphij while ((pw = getpwent()) != NULL) 192275698Sdelphij (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name); 193275698Sdelphij endpwent(); 194275698Sdelphij } 195275698Sdelphij /* 196275698Sdelphij * Setting maxrun (-l) makes no sense without the -a flag. 197275698Sdelphij * Historically this was never an error, so we just warn. 198275698Sdelphij */ 199275698Sdelphij if (maxrun > 0 && !aflag) 200275698Sdelphij warnx("ignoring -l without -a"); 201275698Sdelphij if (aflag) 202275698Sdelphij exit(checkfstab(1, maxrun, needchk, chkquota)); 203275698Sdelphij if (setfsent() == 0) 204275698Sdelphij errx(1, "%s: can't open", FSTAB); 205275698Sdelphij while ((fs = getfsent()) != NULL) { 206275698Sdelphij if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 207275698Sdelphij (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) && 208275698Sdelphij (auxdata = needchk(fs)) && 209275698Sdelphij (name = blockcheck(fs->fs_spec))) { 210275698Sdelphij done |= 1 << argnum; 211275698Sdelphij errs += chkquota(name, fs->fs_file, auxdata); 212275698Sdelphij } 213275698Sdelphij } 214267897Sdelphij endfsent(); 215267897Sdelphij for (i = 0; i < argc; i++) 216267897Sdelphij if ((done & (1 << i)) == 0) 217267897Sdelphij fprintf(stderr, "%s not found in %s\n", 218267897Sdelphij argv[i], FSTAB); 219267897Sdelphij exit(errs); 220267897Sdelphij} 221267897Sdelphij 222267897Sdelphijvoid 223267897Sdelphijusage() 224267897Sdelphij{ 225267897Sdelphij (void)fprintf(stderr, "%s\n%s\n", 226267897Sdelphij "usage: quotacheck [-guv] [-l maxrun] -a", 227267897Sdelphij " quotacheck [-guv] filesystem ..."); 228267897Sdelphij exit(1); 229267897Sdelphij} 230267897Sdelphij 231267897Sdelphijvoid * 232267897Sdelphijneedchk(fs) 233267897Sdelphij struct fstab *fs; 234267897Sdelphij{ 235267897Sdelphij struct quotaname *qnp; 236267897Sdelphij char *qfnp; 237267897Sdelphij 238267897Sdelphij if (strcmp(fs->fs_vfstype, "ufs") || 239267897Sdelphij strcmp(fs->fs_type, FSTAB_RW)) 240267897Sdelphij return (NULL); 241267897Sdelphij if ((qnp = malloc(sizeof(*qnp))) == NULL) 242267897Sdelphij errx(1, "malloc failed"); 243267897Sdelphij qnp->flags = 0; 244267897Sdelphij if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) { 245267897Sdelphij strcpy(qnp->grpqfname, qfnp); 246267897Sdelphij qnp->flags |= HASGRP; 247267897Sdelphij } 248267897Sdelphij if (uflag && hasquota(fs, USRQUOTA, &qfnp)) { 249267897Sdelphij strcpy(qnp->usrqfname, qfnp); 250267897Sdelphij qnp->flags |= HASUSR; 251267897Sdelphij } 252267897Sdelphij if (qnp->flags) 253267897Sdelphij return (qnp); 254267897Sdelphij free(qnp); 255267897Sdelphij return (NULL); 256267897Sdelphij} 257267897Sdelphij 258267897Sdelphij/* 259267897Sdelphij * Possible superblock locations ordered from most to least likely. 260267897Sdelphij */ 261267897Sdelphijstatic int sblock_try[] = SBLOCKSEARCH; 262267897Sdelphij 263267897Sdelphij/* 264267897Sdelphij * Scan the specified file system to check quota(s) present on it. 265267897Sdelphij */ 266267897Sdelphijint 267267897Sdelphijchkquota(fsname, mntpt, qnp) 268267897Sdelphij char *fsname, *mntpt; 269267897Sdelphij struct quotaname *qnp; 270267897Sdelphij{ 271267897Sdelphij struct fileusage *fup; 272267897Sdelphij union dinode *dp; 273267897Sdelphij int cg, i, mode, errs = 0; 274267897Sdelphij ino_t ino, inosused; 275267897Sdelphij char *cp; 276267897Sdelphij 277267897Sdelphij if ((fi = open(fsname, O_RDONLY, 0)) < 0) { 278267897Sdelphij warn("%s", fsname); 279267897Sdelphij return (1); 280267897Sdelphij } 281267897Sdelphij if (vflag) { 282267897Sdelphij (void)printf("*** Checking "); 283267897Sdelphij if (qnp->flags & HASUSR) 284267897Sdelphij (void)printf("%s%s", qfextension[USRQUOTA], 285267897Sdelphij (qnp->flags & HASGRP) ? " and " : ""); 286267897Sdelphij if (qnp->flags & HASGRP) 287267897Sdelphij (void)printf("%s", qfextension[GRPQUOTA]); 288267897Sdelphij (void)printf(" quotas for %s (%s)\n", fsname, mntpt); 289267897Sdelphij } 290267897Sdelphij sync(); 291267897Sdelphij dev_bsize = 1; 292267897Sdelphij for (i = 0; sblock_try[i] != -1; i++) { 293267897Sdelphij bread(sblock_try[i], (char *)&sblock, (long)SBLOCKSIZE); 294267897Sdelphij if ((sblock.fs_magic == FS_UFS1_MAGIC || 295267897Sdelphij (sblock.fs_magic == FS_UFS2_MAGIC && 296267897Sdelphij sblock.fs_sblockloc == sblock_try[i])) && 297267897Sdelphij sblock.fs_bsize <= MAXBSIZE && 298267897Sdelphij sblock.fs_bsize >= sizeof(struct fs)) 299267897Sdelphij break; 300267897Sdelphij } 301267897Sdelphij if (sblock_try[i] == -1) { 302267897Sdelphij warn("Cannot find file system superblock"); 303267897Sdelphij return (1); 304267897Sdelphij } 305267897Sdelphij dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); 306267897Sdelphij maxino = sblock.fs_ncg * sblock.fs_ipg; 307267897Sdelphij for (cg = 0; cg < sblock.fs_ncg; cg++) { 308267897Sdelphij ino = cg * sblock.fs_ipg; 309267897Sdelphij setinodebuf(ino); 310267897Sdelphij bread(fsbtodb(&sblock, cgtod(&sblock, cg)), (char *)(&cgblk), 311267897Sdelphij sblock.fs_cgsize); 312267897Sdelphij if (sblock.fs_magic == FS_UFS2_MAGIC) 313267897Sdelphij inosused = cgblk.cg_initediblk; 314267897Sdelphij else 315267897Sdelphij inosused = sblock.fs_ipg; 316267897Sdelphij /* 317267897Sdelphij * If we are using soft updates, then we can trust the 318267897Sdelphij * cylinder group inode allocation maps to tell us which 319267897Sdelphij * inodes are allocated. We will scan the used inode map 320267897Sdelphij * to find the inodes that are really in use, and then 321267897Sdelphij * read only those inodes in from disk. 322267897Sdelphij */ 323267897Sdelphij if (sblock.fs_flags & FS_DOSOFTDEP) { 324267897Sdelphij if (!cg_chkmagic(&cgblk)) 325267897Sdelphij errx(1, "CG %d: BAD MAGIC NUMBER\n", cg); 326267897Sdelphij cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT]; 327267897Sdelphij for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) { 328267897Sdelphij if (*cp == 0) 329267897Sdelphij continue; 330267897Sdelphij for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) { 331267897Sdelphij if (*cp & i) 332267897Sdelphij break; 333267897Sdelphij inosused--; 334267897Sdelphij } 335267897Sdelphij break; 336267897Sdelphij } 337267897Sdelphij if (inosused <= 0) 338267897Sdelphij continue; 339267897Sdelphij } 340267897Sdelphij for (i = 0; i < inosused; i++, ino++) { 341267897Sdelphij if ((dp = getnextinode(ino)) == NULL || ino < ROOTINO || 342267897Sdelphij (mode = DIP(dp, di_mode) & IFMT) == 0) 343267897Sdelphij continue; 344267897Sdelphij if (qnp->flags & HASGRP) { 345267897Sdelphij fup = addid((u_long)DIP(dp, di_gid), GRPQUOTA, 346267897Sdelphij (char *)0); 347267897Sdelphij fup->fu_curinodes++; 348267897Sdelphij if (mode == IFREG || mode == IFDIR || 349267897Sdelphij mode == IFLNK) 350267897Sdelphij fup->fu_curblocks += DIP(dp, di_blocks); 351267897Sdelphij } 352267897Sdelphij if (qnp->flags & HASUSR) { 353267897Sdelphij fup = addid((u_long)DIP(dp, di_uid), USRQUOTA, 354267897Sdelphij (char *)0); 355267897Sdelphij fup->fu_curinodes++; 356267897Sdelphij if (mode == IFREG || mode == IFDIR || 357267897Sdelphij mode == IFLNK) 358267897Sdelphij fup->fu_curblocks += DIP(dp, di_blocks); 359267897Sdelphij } 360267897Sdelphij } 361267897Sdelphij } 362267897Sdelphij freeinodebuf(); 363267897Sdelphij if (qnp->flags & HASUSR) 364267897Sdelphij errs += update(mntpt, qnp->usrqfname, USRQUOTA); 365267897Sdelphij if (qnp->flags & HASGRP) 366267897Sdelphij errs += update(mntpt, qnp->grpqfname, GRPQUOTA); 367267897Sdelphij close(fi); 368267897Sdelphij return (errs); 369267897Sdelphij} 370267897Sdelphij 371267897Sdelphij/* 372267897Sdelphij * Update a specified quota file. 373267897Sdelphij */ 374267897Sdelphijint 375267897Sdelphijupdate(fsname, quotafile, type) 376267897Sdelphij char *fsname, *quotafile; 377267897Sdelphij int type; 378267897Sdelphij{ 379267897Sdelphij struct fileusage *fup; 380267897Sdelphij FILE *qfi, *qfo; 381267897Sdelphij u_long id, lastid; 382267897Sdelphij off_t offset; 383267897Sdelphij struct dqblk dqbuf; 384267897Sdelphij static int warned = 0; 385267897Sdelphij static struct dqblk zerodqbuf; 386267897Sdelphij static struct fileusage zerofileusage; 387267897Sdelphij 388267897Sdelphij if ((qfo = fopen(quotafile, "r+")) == NULL) { 389267897Sdelphij if (errno == ENOENT) 390267897Sdelphij qfo = fopen(quotafile, "w+"); 391267897Sdelphij if (qfo) { 392267897Sdelphij warnx("creating quota file %s", quotafile); 393267897Sdelphij#define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 394267897Sdelphij (void) fchown(fileno(qfo), getuid(), getquotagid()); 395267897Sdelphij (void) fchmod(fileno(qfo), MODE); 396267897Sdelphij } else { 397267897Sdelphij warn("%s", quotafile); 398267897Sdelphij return (1); 399267897Sdelphij } 400267897Sdelphij } 401267897Sdelphij if ((qfi = fopen(quotafile, "r")) == NULL) { 402267897Sdelphij warn("%s", quotafile); 403267897Sdelphij (void) fclose(qfo); 404267897Sdelphij return (1); 405267897Sdelphij } 406267897Sdelphij if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 && 407267897Sdelphij errno == EOPNOTSUPP && !warned && vflag) { 408267897Sdelphij warned++; 409267897Sdelphij (void)printf("*** Warning: %s\n", 410267897Sdelphij "Quotas are not compiled into this kernel"); 411267897Sdelphij } 412267897Sdelphij for (lastid = highid[type], id = 0, offset = 0; id <= lastid; 413267897Sdelphij id++, offset += sizeof(struct dqblk)) { 414267897Sdelphij if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 415267897Sdelphij dqbuf = zerodqbuf; 416267897Sdelphij if ((fup = lookup(id, type)) == 0) 417267897Sdelphij fup = &zerofileusage; 418267897Sdelphij if (dqbuf.dqb_curinodes == fup->fu_curinodes && 419267897Sdelphij dqbuf.dqb_curblocks == fup->fu_curblocks) { 420267897Sdelphij fup->fu_curinodes = 0; 421267897Sdelphij fup->fu_curblocks = 0; 422267897Sdelphij continue; 423267897Sdelphij } 424267897Sdelphij if (vflag) { 425267897Sdelphij if (aflag) 426267897Sdelphij printf("%s: ", fsname); 427267897Sdelphij printf("%-8s fixed:", fup->fu_name); 428267897Sdelphij if (dqbuf.dqb_curinodes != fup->fu_curinodes) 429267897Sdelphij (void)printf("\tinodes %lu -> %lu", 430267897Sdelphij (u_long)dqbuf.dqb_curinodes, 431267897Sdelphij (u_long)fup->fu_curinodes); 432267897Sdelphij if (dqbuf.dqb_curblocks != fup->fu_curblocks) 433267897Sdelphij (void)printf("\tblocks %lu -> %lu", 434267897Sdelphij (u_long)dqbuf.dqb_curblocks, 435267897Sdelphij (u_long)fup->fu_curblocks); 436267897Sdelphij (void)printf("\n"); 437267897Sdelphij } 438267897Sdelphij /* 439267897Sdelphij * Reset time limit if have a soft limit and were 440267897Sdelphij * previously under it, but are now over it. 441267897Sdelphij */ 442267897Sdelphij if (dqbuf.dqb_bsoftlimit && 443267897Sdelphij dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 444267897Sdelphij fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) 445267897Sdelphij dqbuf.dqb_btime = 0; 446267897Sdelphij if (dqbuf.dqb_isoftlimit && 447267897Sdelphij dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit && 448267897Sdelphij fup->fu_curblocks >= dqbuf.dqb_isoftlimit) 449267897Sdelphij dqbuf.dqb_itime = 0; 450267897Sdelphij dqbuf.dqb_curinodes = fup->fu_curinodes; 451267897Sdelphij dqbuf.dqb_curblocks = fup->fu_curblocks; 452267897Sdelphij if (fseek(qfo, offset, SEEK_SET) < 0) { 453267897Sdelphij warn("%s: seek failed", quotafile); 454267897Sdelphij return(1); 455267897Sdelphij } 456234449Sobrien fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 457234449Sobrien (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, 458234449Sobrien (caddr_t)&dqbuf); 459234449Sobrien fup->fu_curinodes = 0; 460234449Sobrien fup->fu_curblocks = 0; 461234449Sobrien } 462234449Sobrien fclose(qfi); 463234449Sobrien fflush(qfo); 464234449Sobrien ftruncate(fileno(qfo), 465234449Sobrien (((off_t)highid[type] + 1) * sizeof(struct dqblk))); 466234449Sobrien fclose(qfo); 467234449Sobrien return (0); 468234449Sobrien} 469234449Sobrien 470234449Sobrien/* 471234449Sobrien * Check to see if target appears in list of size cnt. 472234449Sobrien */ 473234449Sobrienint 474234449Sobrienoneof(target, list, cnt) 475234449Sobrien char *target, *list[]; 476234449Sobrien int cnt; 477234449Sobrien{ 478234449Sobrien int i; 479234449Sobrien 480234449Sobrien for (i = 0; i < cnt; i++) 481234449Sobrien if (strcmp(target, list[i]) == 0) 482234449Sobrien return (i); 483234449Sobrien return (-1); 484234449Sobrien} 485234449Sobrien 486234449Sobrien/* 487234449Sobrien * Determine the group identifier for quota files. 488234449Sobrien */ 489234449Sobrienint 490234449Sobriengetquotagid() 491234449Sobrien{ 492234449Sobrien struct group *gr; 493234449Sobrien 494234449Sobrien if ((gr = getgrnam(quotagroup)) != NULL) 495234449Sobrien return (gr->gr_gid); 496234449Sobrien return (-1); 497234449Sobrien} 498234449Sobrien 499234449Sobrien/* 500234449Sobrien * Check to see if a particular quota is to be enabled. 501234449Sobrien */ 502234449Sobrienint 503234449Sobrienhasquota(fs, type, qfnamep) 504234449Sobrien struct fstab *fs; 505234449Sobrien int type; 506234449Sobrien char **qfnamep; 507234449Sobrien{ 508234449Sobrien char *opt; 509234449Sobrien char *cp; 510234449Sobrien static char initname, usrname[100], grpname[100]; 511234449Sobrien static char buf[BUFSIZ]; 512234449Sobrien 513234449Sobrien if (!initname) { 514234449Sobrien (void)snprintf(usrname, sizeof(usrname), 515234449Sobrien "%s%s", qfextension[USRQUOTA], qfname); 516234449Sobrien (void)snprintf(grpname, sizeof(grpname), 517234449Sobrien "%s%s", qfextension[GRPQUOTA], qfname); 518234449Sobrien initname = 1; 519234449Sobrien } 520234449Sobrien strcpy(buf, fs->fs_mntops); 521234449Sobrien for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 522234449Sobrien if ((cp = index(opt, '=')) != NULL) 523234449Sobrien *cp++ = '\0'; 524234449Sobrien if (type == USRQUOTA && strcmp(opt, usrname) == 0) 525234449Sobrien break; 526234449Sobrien if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 527234449Sobrien break; 528234449Sobrien } 529234449Sobrien if (!opt) 530234449Sobrien return (0); 531234449Sobrien if (cp) 532234449Sobrien *qfnamep = cp; 533234449Sobrien else { 534234449Sobrien (void)snprintf(buf, sizeof(buf), 535234449Sobrien "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 536234449Sobrien *qfnamep = buf; 537234449Sobrien } 538234449Sobrien return (1); 539234449Sobrien} 540234449Sobrien 541234449Sobrien/* 542234449Sobrien * Routines to manage the file usage table. 543234449Sobrien * 544234449Sobrien * Lookup an id of a specific type. 545234449Sobrien */ 546234449Sobrienstruct fileusage * 547234449Sobrienlookup(id, type) 548234449Sobrien u_long id; 549234449Sobrien int type; 550234449Sobrien{ 551234449Sobrien struct fileusage *fup; 552234449Sobrien 553234449Sobrien for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 554234449Sobrien if (fup->fu_id == id) 555234449Sobrien return (fup); 556234449Sobrien return (NULL); 557234449Sobrien} 558234449Sobrien 559234449Sobrien/* 560234449Sobrien * Add a new file usage id if it does not already exist. 561234449Sobrien */ 562234449Sobrienstruct fileusage * 563234449Sobrienaddid(id, type, name) 564234449Sobrien u_long id; 565234449Sobrien int type; 566234449Sobrien char *name; 567234449Sobrien{ 568234449Sobrien struct fileusage *fup, **fhp; 569234449Sobrien int len; 570234449Sobrien 571234449Sobrien if ((fup = lookup(id, type)) != NULL) 572234449Sobrien return (fup); 573234449Sobrien if (name) 574234449Sobrien len = strlen(name); 575234449Sobrien else 576234449Sobrien len = 10; 577234449Sobrien if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) 578234449Sobrien errx(1, "calloc failed"); 579234449Sobrien fhp = &fuhead[type][id & (FUHASH - 1)]; 580234449Sobrien fup->fu_next = *fhp; 581234449Sobrien *fhp = fup; 582234449Sobrien fup->fu_id = id; 583234449Sobrien if (id > highid[type]) 584234449Sobrien highid[type] = id; 585234449Sobrien if (name) 586234449Sobrien bcopy(name, fup->fu_name, len + 1); 587234449Sobrien else { 588234449Sobrien (void)sprintf(fup->fu_name, "%lu", id); 589234449Sobrien if (vflag) 590234449Sobrien printf("unknown %cid: %lu\n", 591234449Sobrien type == USRQUOTA ? 'u' : 'g', id); 592234449Sobrien } 593234449Sobrien return (fup); 594234449Sobrien} 595234449Sobrien 596234449Sobrien/* 597234449Sobrien * Special purpose version of ginode used to optimize pass 598234449Sobrien * over all the inodes in numerical order. 599234449Sobrien */ 600234449Sobrienstatic ino_t nextino, lastinum, lastvalidinum; 601234449Sobrienstatic long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 602234449Sobrienstatic caddr_t inodebuf; 603234449Sobrien#define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ 604234449Sobrien 605234449Sobrienunion dinode * 606234449Sobriengetnextinode(ino_t inumber) 607234449Sobrien{ 608234449Sobrien long size; 609234449Sobrien ufs2_daddr_t dblk; 610234449Sobrien union dinode *dp; 611234449Sobrien static caddr_t nextinop; 612234449Sobrien 613234449Sobrien if (inumber != nextino++ || inumber > lastvalidinum) 614234449Sobrien errx(1, "bad inode number %d to nextinode", inumber); 615234449Sobrien if (inumber >= lastinum) { 616234449Sobrien readcnt++; 617234449Sobrien dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); 618234449Sobrien if (readcnt % readpercg == 0) { 619234449Sobrien size = partialsize; 620234449Sobrien lastinum += partialcnt; 621234449Sobrien } else { 622234449Sobrien size = inobufsize; 623234449Sobrien lastinum += fullcnt; 624234449Sobrien } 625234449Sobrien /* 626234449Sobrien * If bread returns an error, it will already have zeroed 627234449Sobrien * out the buffer, so we do not need to do so here. 628234449Sobrien */ 629234449Sobrien bread(dblk, inodebuf, size); 630234449Sobrien nextinop = inodebuf; 631234449Sobrien } 632234449Sobrien dp = (union dinode *)nextinop; 633234449Sobrien if (sblock.fs_magic == FS_UFS1_MAGIC) 634234449Sobrien nextinop += sizeof(struct ufs1_dinode); 635234449Sobrien else 636234449Sobrien nextinop += sizeof(struct ufs2_dinode); 637234449Sobrien return (dp); 638234449Sobrien} 639234449Sobrien 640234449Sobrien/* 641234449Sobrien * Prepare to scan a set of inodes. 642234449Sobrien */ 643234449Sobrienvoid 644234449Sobriensetinodebuf(ino_t inum) 645234449Sobrien{ 646234449Sobrien 647234449Sobrien if (inum % sblock.fs_ipg != 0) 648234449Sobrien errx(1, "bad inode number %d to setinodebuf", inum); 649234449Sobrien lastvalidinum = inum + sblock.fs_ipg - 1; 650234449Sobrien nextino = inum; 651234449Sobrien lastinum = inum; 652234449Sobrien readcnt = 0; 653234449Sobrien if (inodebuf != NULL) 654234449Sobrien return; 655234449Sobrien inobufsize = blkroundup(&sblock, INOBUFSIZE); 656234449Sobrien fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ? 657234449Sobrien sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 658234449Sobrien readpercg = sblock.fs_ipg / fullcnt; 659234449Sobrien partialcnt = sblock.fs_ipg % fullcnt; 660234449Sobrien partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ? 661234449Sobrien sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 662234449Sobrien if (partialcnt != 0) { 663234449Sobrien readpercg++; 664234449Sobrien } else { 665234449Sobrien partialcnt = fullcnt; 666234449Sobrien partialsize = inobufsize; 667234449Sobrien } 668234449Sobrien if ((inodebuf = malloc((unsigned)inobufsize)) == NULL) 669234449Sobrien errx(1, "cannot allocate space for inode buffer"); 670234449Sobrien} 671234449Sobrien 672234449Sobrien/* 673234449Sobrien * Free up data structures used to scan inodes. 674234449Sobrien */ 675234449Sobrienvoid 676234449Sobrienfreeinodebuf() 677234449Sobrien{ 678234449Sobrien 679234449Sobrien if (inodebuf != NULL) 680234449Sobrien free(inodebuf); 681234449Sobrien inodebuf = NULL; 682234449Sobrien} 683234449Sobrien 684234449Sobrien/* 685234449Sobrien * Read specified disk blocks. 686234449Sobrien */ 687234449Sobrienvoid 688234449Sobrienbread(bno, buf, cnt) 689234449Sobrien ufs2_daddr_t bno; 690234449Sobrien char *buf; 691234449Sobrien long cnt; 692234449Sobrien{ 693234449Sobrien 694234449Sobrien if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 || 695234449Sobrien read(fi, buf, cnt) != cnt) 696234449Sobrien errx(1, "bread failed on block %ld", (long)bno); 697234449Sobrien} 698234449Sobrien