1*f6e4162fSjsg /* $OpenBSD: quotacheck.c,v 1.43 2024/09/15 07:14:58 jsg Exp $ */ 2f3bae140Sderaadt /* $NetBSD: quotacheck.c,v 1.12 1996/03/30 22:34:25 mark Exp $ */ 3df930be7Sderaadt 4df930be7Sderaadt /* 5df930be7Sderaadt * Copyright (c) 1980, 1990, 1993 6df930be7Sderaadt * The Regents of the University of California. All rights reserved. 7df930be7Sderaadt * 8df930be7Sderaadt * This code is derived from software contributed to Berkeley by 9df930be7Sderaadt * Robert Elz at The University of Melbourne. 10df930be7Sderaadt * 11df930be7Sderaadt * Redistribution and use in source and binary forms, with or without 12df930be7Sderaadt * modification, are permitted provided that the following conditions 13df930be7Sderaadt * are met: 14df930be7Sderaadt * 1. Redistributions of source code must retain the above copyright 15df930be7Sderaadt * notice, this list of conditions and the following disclaimer. 16df930be7Sderaadt * 2. Redistributions in binary form must reproduce the above copyright 17df930be7Sderaadt * notice, this list of conditions and the following disclaimer in the 18df930be7Sderaadt * documentation and/or other materials provided with the distribution. 191ef0d710Smillert * 3. Neither the name of the University nor the names of its contributors 20df930be7Sderaadt * may be used to endorse or promote products derived from this software 21df930be7Sderaadt * without specific prior written permission. 22df930be7Sderaadt * 23df930be7Sderaadt * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 24df930be7Sderaadt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25df930be7Sderaadt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26df930be7Sderaadt * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 27df930be7Sderaadt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28df930be7Sderaadt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29df930be7Sderaadt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30df930be7Sderaadt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31df930be7Sderaadt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32df930be7Sderaadt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33df930be7Sderaadt * SUCH DAMAGE. 34df930be7Sderaadt */ 35df930be7Sderaadt 36df930be7Sderaadt /* 37df930be7Sderaadt * Fix up / report on disk quotas & usage 38df930be7Sderaadt */ 3978eb0b7eSderaadt #include <sys/param.h> /* DEV_BSIZE MAXBSIZE */ 40df930be7Sderaadt #include <sys/stat.h> 4135635556Smickey #include <sys/wait.h> 42df930be7Sderaadt 43df930be7Sderaadt #include <ufs/ufs/dinode.h> 44df930be7Sderaadt #include <ufs/ufs/quota.h> 45df930be7Sderaadt #include <ufs/ffs/fs.h> 46df930be7Sderaadt 47df930be7Sderaadt #include <fcntl.h> 48df930be7Sderaadt #include <fstab.h> 49df930be7Sderaadt #include <pwd.h> 50df930be7Sderaadt #include <grp.h> 51df930be7Sderaadt #include <errno.h> 52df930be7Sderaadt #include <unistd.h> 53b9fc9a72Sderaadt #include <limits.h> 542507a217Skrw #include <util.h> 55df930be7Sderaadt #include <stdio.h> 56df930be7Sderaadt #include <stdlib.h> 57df930be7Sderaadt #include <string.h> 58df930be7Sderaadt #include <err.h> 5935635556Smickey #include "fsutil.h" 60df930be7Sderaadt 61df930be7Sderaadt char *qfname = QUOTAFILENAME; 62df930be7Sderaadt char *qfextension[] = INITQFNAMES; 63df930be7Sderaadt char *quotagroup = QUOTAGROUP; 64df930be7Sderaadt 65df930be7Sderaadt union { 66df930be7Sderaadt struct fs sblk; 67df930be7Sderaadt char dummy[MAXBSIZE]; 685cb887c0Smillert } sb_un; 695cb887c0Smillert #define sblock sb_un.sblk 705cb887c0Smillert union { 715cb887c0Smillert struct cg cgblk; 725cb887c0Smillert char dummy[MAXBSIZE]; 735cb887c0Smillert } cg_un; 745cb887c0Smillert #define cgblk cg_un.cgblk 755cb887c0Smillert 76df930be7Sderaadt long maxino; 77df930be7Sderaadt 785cb887c0Smillert union dinode { 795cb887c0Smillert struct ufs1_dinode dp1; 805cb887c0Smillert struct ufs2_dinode dp2; 815cb887c0Smillert }; 825cb887c0Smillert #define DIP(dp, field) \ 835cb887c0Smillert ((sblock.fs_magic == FS_UFS1_MAGIC) ? \ 845cb887c0Smillert (dp)->dp1.field : (dp)->dp2.field) 855cb887c0Smillert 86df930be7Sderaadt struct quotaname { 87df930be7Sderaadt long flags; 88b9fc9a72Sderaadt char grpqfname[PATH_MAX + 1]; 89b9fc9a72Sderaadt char usrqfname[PATH_MAX + 1]; 90df930be7Sderaadt }; 91df930be7Sderaadt #define HASUSR 1 92df930be7Sderaadt #define HASGRP 2 93df930be7Sderaadt 94df930be7Sderaadt struct fileusage { 95df930be7Sderaadt struct fileusage *fu_next; 9650c87d91Sderaadt u_int32_t fu_curinodes; 9750c87d91Sderaadt u_int32_t fu_curblocks; 9850c87d91Sderaadt u_int32_t fu_id; /* uid_t or gid_t */ 99df930be7Sderaadt char fu_name[1]; 100df930be7Sderaadt /* actually bigger */ 101df930be7Sderaadt }; 102df930be7Sderaadt #define FUHASH 1024 /* must be power of two */ 103df930be7Sderaadt struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 104df930be7Sderaadt 105df930be7Sderaadt int gflag; /* check group quotas */ 106df930be7Sderaadt int uflag; /* check user quotas */ 10735635556Smickey int flags; /* check flags (avd) */ 108df930be7Sderaadt int fi; /* open disk file descriptor */ 10950c87d91Sderaadt u_int32_t highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ 110df930be7Sderaadt 111df930be7Sderaadt struct fileusage * 11250c87d91Sderaadt addid(u_int32_t, int, char *); 113c72b5b24Smillert char *blockcheck(char *); 1141abdbfdeSderaadt void bread(daddr_t, char *, long); 115f3c3a9c6Smillert int chkquota(const char *, const char *, const char *, void *, pid_t *); 116c72b5b24Smillert void freeinodebuf(void); 1175cb887c0Smillert union dinode * 118c72b5b24Smillert getnextinode(ino_t); 119c72b5b24Smillert int getquotagid(void); 120c72b5b24Smillert int hasquota(struct fstab *, int, char **); 121df930be7Sderaadt struct fileusage * 12250c87d91Sderaadt lookup(u_int32_t, int); 123c72b5b24Smillert void *needchk(struct fstab *); 1242507a217Skrw int oneof_realpath(char *, char*[], int); 1252507a217Skrw int oneof_specname(char *, char*[], int); 1265cb887c0Smillert void setinodebuf(ino_t); 127c72b5b24Smillert int update(const char *, const char *, int); 128c72b5b24Smillert void usage(void); 129df930be7Sderaadt 130df930be7Sderaadt int 131bc52e260Sderaadt main(int argc, char *argv[]) 132df930be7Sderaadt { 133e073c79dSmpech struct fstab *fs; 134e073c79dSmpech struct passwd *pw; 135e073c79dSmpech struct group *gr; 136df930be7Sderaadt struct quotaname *auxdata; 137a7c59635Sderaadt int i, argnum, maxrun, errs, ch; 13850c87d91Sderaadt u_int64_t done = 0; /* XXX supports maximum 64 filesystems */ 139a47b6461Sderaadt const char *errstr; 140f3bae140Sderaadt char *name; 141df930be7Sderaadt 14276a56e18Sderaadt checkroot(); 14376a56e18Sderaadt 144df930be7Sderaadt errs = maxrun = 0; 14535635556Smickey while ((ch = getopt(argc, argv, "adguvl:")) != -1) { 146df930be7Sderaadt switch(ch) { 147df930be7Sderaadt case 'a': 14835635556Smickey flags |= CHECK_PREEN; 14935635556Smickey break; 15035635556Smickey case 'd': 15135635556Smickey flags |= CHECK_DEBUG; 152df930be7Sderaadt break; 153df930be7Sderaadt case 'g': 1543579491dSderaadt gflag = 1; 155df930be7Sderaadt break; 15635635556Smickey case 'l': 157a47b6461Sderaadt maxrun = strtonum(optarg, 0, INT_MAX, &errstr); 158a47b6461Sderaadt if (errstr) 159a47b6461Sderaadt errx(1, "-l %s: %s", optarg, errstr); 16035635556Smickey break; 161df930be7Sderaadt case 'u': 1623579491dSderaadt uflag = 1; 163df930be7Sderaadt break; 164df930be7Sderaadt case 'v': 16535635556Smickey flags |= CHECK_VERBOSE; 166df930be7Sderaadt break; 167df930be7Sderaadt default: 168df930be7Sderaadt usage(); 169df930be7Sderaadt } 170df930be7Sderaadt } 171df930be7Sderaadt argc -= optind; 172df930be7Sderaadt argv += optind; 17335635556Smickey if ((argc == 0 && !(flags&CHECK_PREEN)) || 17435635556Smickey (argc > 0 && (flags&CHECK_PREEN))) 175df930be7Sderaadt usage(); 176df930be7Sderaadt if (!gflag && !uflag) { 177df930be7Sderaadt gflag++; 178df930be7Sderaadt uflag++; 179df930be7Sderaadt } 180df930be7Sderaadt if (gflag) { 181df930be7Sderaadt setgrent(); 182df930be7Sderaadt while ((gr = getgrent()) != 0) 18350c87d91Sderaadt (void) addid(gr->gr_gid, GRPQUOTA, gr->gr_name); 184df930be7Sderaadt endgrent(); 185df930be7Sderaadt } 186df930be7Sderaadt if (uflag) { 187df930be7Sderaadt setpwent(); 188df930be7Sderaadt while ((pw = getpwent()) != 0) 18950c87d91Sderaadt (void) addid(pw->pw_uid, USRQUOTA, pw->pw_name); 190df930be7Sderaadt endpwent(); 191df930be7Sderaadt } 19235635556Smickey if (flags&CHECK_PREEN) 19335635556Smickey exit(checkfstab(flags, maxrun, needchk, chkquota)); 194df930be7Sderaadt if (setfsent() == 0) 19558ae8366Sjca err(1, "%s: can't open", _PATH_FSTAB); 196df930be7Sderaadt while ((fs = getfsent()) != NULL) { 1972507a217Skrw if (((argnum = oneof_realpath(fs->fs_file, argv, argc)) >= 0 || 1982507a217Skrw (argnum = oneof_specname(fs->fs_spec, argv, argc)) >= 0) && 199df930be7Sderaadt (auxdata = needchk(fs)) && 200df930be7Sderaadt (name = blockcheck(fs->fs_spec))) { 201df930be7Sderaadt done |= 1 << argnum; 20235635556Smickey errs += chkquota(fs->fs_vfstype, name, 20335635556Smickey fs->fs_file, auxdata, NULL); 204df930be7Sderaadt } 205df930be7Sderaadt } 206df930be7Sderaadt endfsent(); 207df930be7Sderaadt for (i = 0; i < argc; i++) 208df930be7Sderaadt if ((done & (1 << i)) == 0) 209df930be7Sderaadt fprintf(stderr, "%s not found in %s\n", 21058ae8366Sjca argv[i], _PATH_FSTAB); 211df930be7Sderaadt exit(errs); 212df930be7Sderaadt } 213df930be7Sderaadt 214df930be7Sderaadt void 215bc52e260Sderaadt usage(void) 216df930be7Sderaadt { 21731ed6db9Sjmc extern char *__progname; 21831ed6db9Sjmc (void)fprintf(stderr, "usage: %s [-adguv] [-l maxparallel] " 21931ed6db9Sjmc "filesystem ...\n", __progname); 220df930be7Sderaadt exit(1); 221df930be7Sderaadt } 222df930be7Sderaadt 223df930be7Sderaadt void * 224bc52e260Sderaadt needchk(struct fstab *fs) 225df930be7Sderaadt { 226e073c79dSmpech struct quotaname *qnp; 227df930be7Sderaadt char *qfnp; 228df930be7Sderaadt 22935635556Smickey if (fs->fs_passno == 0) 23035635556Smickey return NULL; 231e5cb37d1Sderaadt if (strcmp(fs->fs_type, FSTAB_RW)) 232e5cb37d1Sderaadt return (NULL); 233e5cb37d1Sderaadt if (strcmp(fs->fs_vfstype, "ffs") && 234dde99675Sderaadt strcmp(fs->fs_vfstype, "ufs") && 235dde99675Sderaadt strcmp(fs->fs_vfstype, "mfs")) 236df930be7Sderaadt return (NULL); 237df930be7Sderaadt if ((qnp = malloc(sizeof(*qnp))) == NULL) 238df930be7Sderaadt err(1, "%s", strerror(errno)); 239df930be7Sderaadt qnp->flags = 0; 240df930be7Sderaadt if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) { 2419e405e78Sderaadt strlcpy(qnp->grpqfname, qfnp, sizeof qnp->grpqfname); 242df930be7Sderaadt qnp->flags |= HASGRP; 243df930be7Sderaadt } 244df930be7Sderaadt if (uflag && hasquota(fs, USRQUOTA, &qfnp)) { 2459e405e78Sderaadt strlcpy(qnp->usrqfname, qfnp, sizeof qnp->usrqfname); 246df930be7Sderaadt qnp->flags |= HASUSR; 247df930be7Sderaadt } 248df930be7Sderaadt if (qnp->flags) 249df930be7Sderaadt return (qnp); 250df930be7Sderaadt free(qnp); 251df930be7Sderaadt return (NULL); 252df930be7Sderaadt } 253df930be7Sderaadt 254df930be7Sderaadt /* 2555cb887c0Smillert * Possible superblock locations ordered from most to least likely. 2565cb887c0Smillert */ 2575cb887c0Smillert static int sblock_try[] = SBLOCKSEARCH; 2585cb887c0Smillert 2595cb887c0Smillert /* 260df930be7Sderaadt * Scan the specified file system to check quota(s) present on it. 261df930be7Sderaadt */ 262df930be7Sderaadt int 263bc52e260Sderaadt chkquota(const char *vfstype, const char *fsname, const char *mntpt, 264bc52e260Sderaadt void *auxarg, pid_t *pidp) 265df930be7Sderaadt { 266e073c79dSmpech struct quotaname *qnp = auxarg; 267e073c79dSmpech struct fileusage *fup; 2685cb887c0Smillert union dinode *dp; 26935635556Smickey int cg, i, mode, errs = 0, status; 2705cb887c0Smillert ino_t ino, inosused; 27135635556Smickey pid_t pid; 272df930be7Sderaadt 27335635556Smickey switch (pid = fork()) { 27435635556Smickey case -1: /* error */ 27535635556Smickey warn("fork"); 27635635556Smickey return 1; 27735635556Smickey case 0: /* child */ 278df69c215Sderaadt if ((fi = opendev(fsname, O_RDONLY, 0, NULL)) == -1) 2797bf18b22Sderaadt err(1, "%s", fsname); 280df930be7Sderaadt sync(); 2815cb887c0Smillert for (i = 0; sblock_try[i] != -1; i++) { 282ab7a365bSkrw bread(sblock_try[i] / DEV_BSIZE, (char *)&sblock, 283ab7a365bSkrw (long)SBLOCKSIZE); 2845cb887c0Smillert if ((sblock.fs_magic == FS_UFS1_MAGIC || 2855cb887c0Smillert (sblock.fs_magic == FS_UFS2_MAGIC && 2864ec93eb9Sotto sblock.fs_sblockloc == sblock_try[i])) && 2875cb887c0Smillert sblock.fs_bsize <= MAXBSIZE && 2885cb887c0Smillert sblock.fs_bsize >= sizeof(struct fs)) 2895cb887c0Smillert break; 2905cb887c0Smillert } 2915cb887c0Smillert if (sblock_try[i] == -1) { 2925cb887c0Smillert warn("Cannot find file system superblock"); 2935cb887c0Smillert return (1); 2945cb887c0Smillert } 295df930be7Sderaadt maxino = sblock.fs_ncg * sblock.fs_ipg; 2965cb887c0Smillert for (cg = 0; cg < sblock.fs_ncg; cg++) { 2975cb887c0Smillert ino = cg * sblock.fs_ipg; 2985cb887c0Smillert setinodebuf(ino); 2995cb887c0Smillert bread(fsbtodb(&sblock, cgtod(&sblock, cg)), 3005cb887c0Smillert (char *)(&cgblk), sblock.fs_cgsize); 3015cb887c0Smillert if (sblock.fs_magic == FS_UFS2_MAGIC) 3025cb887c0Smillert inosused = cgblk.cg_initediblk; 3035cb887c0Smillert else 3045cb887c0Smillert inosused = sblock.fs_ipg; 3055cb887c0Smillert for (i = 0; i < inosused; i++, ino++) { 3065cb887c0Smillert if ((dp = getnextinode(ino)) == NULL || 3075cb887c0Smillert ino < ROOTINO || 3085cb887c0Smillert (mode = DIP(dp, di_mode) & IFMT) == 0) 309df930be7Sderaadt continue; 310df930be7Sderaadt if (qnp->flags & HASGRP) { 3115cb887c0Smillert fup = addid(DIP(dp, di_gid), 3127674422fSkstailey GRPQUOTA, NULL); 313df930be7Sderaadt fup->fu_curinodes++; 314df930be7Sderaadt if (mode == IFREG || mode == IFDIR || 315df930be7Sderaadt mode == IFLNK) 31635635556Smickey fup->fu_curblocks += 3175cb887c0Smillert DIP(dp, di_blocks); 318df930be7Sderaadt } 319df930be7Sderaadt if (qnp->flags & HASUSR) { 3205cb887c0Smillert fup = addid(DIP(dp, di_uid), 3217674422fSkstailey USRQUOTA, NULL); 322df930be7Sderaadt fup->fu_curinodes++; 323df930be7Sderaadt if (mode == IFREG || mode == IFDIR || 324df930be7Sderaadt mode == IFLNK) 32535635556Smickey fup->fu_curblocks += 3265cb887c0Smillert DIP(dp, di_blocks); 327df930be7Sderaadt } 328df930be7Sderaadt } 329df930be7Sderaadt } 330df930be7Sderaadt freeinodebuf(); 33135635556Smickey if (flags&(CHECK_DEBUG|CHECK_VERBOSE)) { 33235635556Smickey (void)printf("*** Checking "); 33335635556Smickey if (qnp->flags & HASUSR) { 33435635556Smickey (void)printf("%s", qfextension[USRQUOTA]); 33535635556Smickey if (qnp->flags & HASGRP) 33635635556Smickey (void)printf(" and "); 33735635556Smickey } 33835635556Smickey if (qnp->flags & HASGRP) 33935635556Smickey (void)printf("%s", qfextension[GRPQUOTA]); 34035635556Smickey (void)printf(" quotas for %s (%s), %swait\n", 34135635556Smickey fsname, mntpt, pidp? "no" : ""); 34235635556Smickey } 343df930be7Sderaadt if (qnp->flags & HASUSR) 344df930be7Sderaadt errs += update(mntpt, qnp->usrqfname, USRQUOTA); 345df930be7Sderaadt if (qnp->flags & HASGRP) 346df930be7Sderaadt errs += update(mntpt, qnp->grpqfname, GRPQUOTA); 347df930be7Sderaadt close(fi); 34835635556Smickey exit (errs); 34935635556Smickey break; 35035635556Smickey default: /* parent */ 35135635556Smickey if (pidp != NULL) { 35235635556Smickey *pidp = pid; 35335635556Smickey return 0; 35435635556Smickey } 355df69c215Sderaadt if (waitpid(pid, &status, 0) == -1) { 35635635556Smickey warn("waitpid"); 35735635556Smickey return 1; 35835635556Smickey } 35935635556Smickey if (WIFEXITED(status)) { 36035635556Smickey if (WEXITSTATUS(status) != 0) 36135635556Smickey return WEXITSTATUS(status); 36235635556Smickey } else if (WIFSIGNALED(status)) { 36335635556Smickey warnx("%s: %s", fsname, strsignal(WTERMSIG(status))); 36435635556Smickey return 1; 36535635556Smickey } 36635635556Smickey break; 36735635556Smickey } 36835635556Smickey return (0); 369df930be7Sderaadt } 370df930be7Sderaadt 371df930be7Sderaadt /* 372df930be7Sderaadt * Update a specified quota file. 373df930be7Sderaadt */ 374df930be7Sderaadt int 375bc52e260Sderaadt update(const char *fsname, const char *quotafile, int type) 376df930be7Sderaadt { 377e073c79dSmpech struct fileusage *fup; 378e073c79dSmpech FILE *qfi, *qfo; 37950c87d91Sderaadt u_int32_t id, lastid; 380df930be7Sderaadt struct dqblk dqbuf; 381df930be7Sderaadt static int warned = 0; 382df930be7Sderaadt static struct dqblk zerodqbuf; 383df930be7Sderaadt static struct fileusage zerofileusage; 384df930be7Sderaadt 38535635556Smickey if (flags&CHECK_DEBUG) 38635635556Smickey printf("updating: %s\n", quotafile); 38735635556Smickey 38835635556Smickey if ((qfo = fopen(quotafile, (flags&CHECK_DEBUG)? "r" : "r+")) == NULL) { 389df930be7Sderaadt if (errno == ENOENT) 390df930be7Sderaadt qfo = fopen(quotafile, "w+"); 391df930be7Sderaadt if (qfo) { 39235635556Smickey warnx("creating quota file: %s", quotafile); 393df930be7Sderaadt #define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 394df930be7Sderaadt (void) fchown(fileno(qfo), getuid(), getquotagid()); 395df930be7Sderaadt (void) fchmod(fileno(qfo), MODE); 396df930be7Sderaadt } else { 39735635556Smickey warn("%s", quotafile); 398df930be7Sderaadt return (1); 399df930be7Sderaadt } 400df930be7Sderaadt } 401df930be7Sderaadt if ((qfi = fopen(quotafile, "r")) == NULL) { 40235635556Smickey warn("%s", quotafile); 403df930be7Sderaadt (void) fclose(qfo); 404df930be7Sderaadt return (1); 405df930be7Sderaadt } 406df69c215Sderaadt if (quotactl(fsname, QCMD(Q_SYNC, type), 0, (caddr_t)0) == -1 && 40735635556Smickey errno == EOPNOTSUPP && !warned && 40835635556Smickey (flags&(CHECK_DEBUG|CHECK_VERBOSE))) { 409df930be7Sderaadt warned++; 410df930be7Sderaadt (void)printf("*** Warning: %s\n", 411df930be7Sderaadt "Quotas are not compiled into this kernel"); 412df930be7Sderaadt } 413df930be7Sderaadt for (lastid = highid[type], id = 0; id <= lastid; id++) { 414df930be7Sderaadt if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 415df930be7Sderaadt dqbuf = zerodqbuf; 416df930be7Sderaadt if ((fup = lookup(id, type)) == 0) 417df930be7Sderaadt fup = &zerofileusage; 418df930be7Sderaadt if (dqbuf.dqb_curinodes == fup->fu_curinodes && 419df930be7Sderaadt dqbuf.dqb_curblocks == fup->fu_curblocks) { 420df930be7Sderaadt fup->fu_curinodes = 0; 421df930be7Sderaadt fup->fu_curblocks = 0; 42250c87d91Sderaadt fseek(qfo, (long)sizeof(struct dqblk), SEEK_CUR); 423df930be7Sderaadt continue; 424df930be7Sderaadt } 42535635556Smickey if (flags&(CHECK_DEBUG|CHECK_VERBOSE)) { 42635635556Smickey if (flags&CHECK_PREEN) 427df930be7Sderaadt printf("%s: ", fsname); 428df930be7Sderaadt printf("%-8s fixed:", fup->fu_name); 429df930be7Sderaadt if (dqbuf.dqb_curinodes != fup->fu_curinodes) 4305cb887c0Smillert (void)printf("\tinodes %d -> %u", 431df930be7Sderaadt dqbuf.dqb_curinodes, fup->fu_curinodes); 432df930be7Sderaadt if (dqbuf.dqb_curblocks != fup->fu_curblocks) 4335cb887c0Smillert (void)printf("\tblocks %u -> %u", 434df930be7Sderaadt dqbuf.dqb_curblocks, fup->fu_curblocks); 435df930be7Sderaadt (void)printf("\n"); 436df930be7Sderaadt } 437df930be7Sderaadt /* 438df930be7Sderaadt * Reset time limit if have a soft limit and were 439df930be7Sderaadt * previously under it, but are now over it. 440df930be7Sderaadt */ 441df930be7Sderaadt if (dqbuf.dqb_bsoftlimit && 442df930be7Sderaadt dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 443df930be7Sderaadt fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) 444df930be7Sderaadt dqbuf.dqb_btime = 0; 445df930be7Sderaadt if (dqbuf.dqb_isoftlimit && 446df930be7Sderaadt dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit && 447df930be7Sderaadt fup->fu_curblocks >= dqbuf.dqb_isoftlimit) 448df930be7Sderaadt dqbuf.dqb_itime = 0; 449df930be7Sderaadt dqbuf.dqb_curinodes = fup->fu_curinodes; 450df930be7Sderaadt dqbuf.dqb_curblocks = fup->fu_curblocks; 45135635556Smickey if (!(flags & CHECK_DEBUG)) { 452df930be7Sderaadt fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 453df930be7Sderaadt (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, 454df930be7Sderaadt (caddr_t)&dqbuf); 45535635556Smickey } 456df930be7Sderaadt fup->fu_curinodes = 0; 457df930be7Sderaadt fup->fu_curblocks = 0; 458df930be7Sderaadt } 459df930be7Sderaadt fclose(qfi); 460df930be7Sderaadt fflush(qfo); 46135635556Smickey if (!(flags & CHECK_DEBUG)) 462df930be7Sderaadt ftruncate(fileno(qfo), 463df930be7Sderaadt (off_t)((highid[type] + 1) * sizeof(struct dqblk))); 464df930be7Sderaadt fclose(qfo); 465df930be7Sderaadt return (0); 466df930be7Sderaadt } 467df930be7Sderaadt 468df930be7Sderaadt /* 4692507a217Skrw * Check to see if realpath(target) matches a realpath() in list of size cnt. 470df930be7Sderaadt */ 471df930be7Sderaadt int 4722507a217Skrw oneof_realpath(char *target, char *list[], int cnt) 473df930be7Sderaadt { 474e073c79dSmpech int i; 4752507a217Skrw char realtarget[PATH_MAX], realargv[PATH_MAX]; 4762507a217Skrw char *rv; 477df930be7Sderaadt 4782507a217Skrw rv = realpath(target, realtarget); 4792507a217Skrw if (rv == NULL) 4802507a217Skrw return (-1); 4812507a217Skrw 4822507a217Skrw for (i = 0; i < cnt; i++) { 4832507a217Skrw rv = realpath(list[i], realargv); 4842507a217Skrw if (rv && strcmp(realtarget, realargv) == 0) 4852507a217Skrw break; 4862507a217Skrw } 4872507a217Skrw 4882507a217Skrw if (i < cnt) 489df930be7Sderaadt return (i); 4902507a217Skrw else 4912507a217Skrw return (-1); 4922507a217Skrw } 4932507a217Skrw 4942507a217Skrw /* 4952507a217Skrw * Check to see if opendev(target) matches a opendev() in list of size cnt. 4962507a217Skrw */ 4972507a217Skrw int 4982507a217Skrw oneof_specname(char *target, char *list[], int cnt) 4992507a217Skrw { 5002507a217Skrw int i, fd; 5012507a217Skrw char *tmp, *targetdev, *argvdev; 5022507a217Skrw 5032507a217Skrw fd = opendev(target, O_RDONLY, 0, &tmp); 5042507a217Skrw if (fd == -1) 5052507a217Skrw return (-1); 5062507a217Skrw close(fd); 5072507a217Skrw targetdev = strdup(tmp); 5082507a217Skrw 5092507a217Skrw for (i = 0; i < cnt; i++) { 5102507a217Skrw fd = opendev(list[i], O_RDONLY, 0, &argvdev); 5112507a217Skrw if (fd == -1) 5122507a217Skrw continue; 5132507a217Skrw close(fd); 5142507a217Skrw if (strcmp(targetdev, argvdev) == 0) 5152507a217Skrw break; 5162507a217Skrw } 5172507a217Skrw 518d5b07100Sjsg free(targetdev); 519d5b07100Sjsg 5202507a217Skrw if (i < cnt) 5212507a217Skrw return (i); 5222507a217Skrw else 523df930be7Sderaadt return (-1); 524df930be7Sderaadt } 525df930be7Sderaadt 526df930be7Sderaadt /* 527df930be7Sderaadt * Determine the group identifier for quota files. 528df930be7Sderaadt */ 529df930be7Sderaadt int 530bc52e260Sderaadt getquotagid(void) 531df930be7Sderaadt { 532df930be7Sderaadt struct group *gr; 533df930be7Sderaadt 53435635556Smickey if ((gr = getgrnam(quotagroup)) != NULL) 535df930be7Sderaadt return (gr->gr_gid); 536df930be7Sderaadt return (-1); 537df930be7Sderaadt } 538df930be7Sderaadt 539df930be7Sderaadt /* 540df930be7Sderaadt * Check to see if a particular quota is to be enabled. 541df930be7Sderaadt */ 542df930be7Sderaadt int 543bc52e260Sderaadt hasquota(struct fstab *fs, int type, char **qfnamep) 544df930be7Sderaadt { 545a7c59635Sderaadt char *opt, *cp; 546df930be7Sderaadt static char initname, usrname[100], grpname[100]; 547df930be7Sderaadt static char buf[BUFSIZ]; 548df930be7Sderaadt 549df930be7Sderaadt if (!initname) { 550df930be7Sderaadt (void)snprintf(usrname, sizeof(usrname), 551df930be7Sderaadt "%s%s", qfextension[USRQUOTA], qfname); 552df930be7Sderaadt (void)snprintf(grpname, sizeof(grpname), 553df930be7Sderaadt "%s%s", qfextension[GRPQUOTA], qfname); 554df930be7Sderaadt initname = 1; 555df930be7Sderaadt } 5568f25adf2Sderaadt (void)strlcpy(buf, fs->fs_mntops, sizeof(buf)); 557df930be7Sderaadt for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 55835635556Smickey if ((cp = strchr(opt, '=')) != NULL) 559df930be7Sderaadt *cp++ = '\0'; 560df930be7Sderaadt if (type == USRQUOTA && strcmp(opt, usrname) == 0) 561df930be7Sderaadt break; 562df930be7Sderaadt if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 563df930be7Sderaadt break; 564df930be7Sderaadt } 565df930be7Sderaadt if (!opt) 566df930be7Sderaadt return (0); 567df930be7Sderaadt if (cp) 568df930be7Sderaadt *qfnamep = cp; 569df930be7Sderaadt else { 570df930be7Sderaadt (void)snprintf(buf, sizeof(buf), 571df930be7Sderaadt "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 572df930be7Sderaadt *qfnamep = buf; 573df930be7Sderaadt } 574df930be7Sderaadt return (1); 575df930be7Sderaadt } 576df930be7Sderaadt 577df930be7Sderaadt /* 578df930be7Sderaadt * Routines to manage the file usage table. 579df930be7Sderaadt * 580df930be7Sderaadt * Lookup an id of a specific type. 581df930be7Sderaadt */ 582df930be7Sderaadt struct fileusage * 58350c87d91Sderaadt lookup(u_int32_t id, int type) 584df930be7Sderaadt { 585e073c79dSmpech struct fileusage *fup; 586df930be7Sderaadt 587df930be7Sderaadt for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 588df930be7Sderaadt if (fup->fu_id == id) 589df930be7Sderaadt return (fup); 590df930be7Sderaadt return (NULL); 591df930be7Sderaadt } 592df930be7Sderaadt 593df930be7Sderaadt /* 594df930be7Sderaadt * Add a new file usage id if it does not already exist. 595df930be7Sderaadt */ 596df930be7Sderaadt struct fileusage * 59750c87d91Sderaadt addid(u_int32_t id, int type, char *name) 598df930be7Sderaadt { 599df930be7Sderaadt struct fileusage *fup, **fhp; 600df930be7Sderaadt int len; 601df930be7Sderaadt 60235635556Smickey if ((fup = lookup(id, type)) != NULL) 603df930be7Sderaadt return (fup); 604df930be7Sderaadt if (name) 605df930be7Sderaadt len = strlen(name); 606df930be7Sderaadt else 607df930be7Sderaadt len = 10; 608df930be7Sderaadt if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) 609df930be7Sderaadt err(1, "%s", strerror(errno)); 610df930be7Sderaadt fhp = &fuhead[type][id & (FUHASH - 1)]; 611df930be7Sderaadt fup->fu_next = *fhp; 612df930be7Sderaadt *fhp = fup; 613df930be7Sderaadt fup->fu_id = id; 614df930be7Sderaadt if (id > highid[type]) 615df930be7Sderaadt highid[type] = id; 616df930be7Sderaadt if (name) 617df930be7Sderaadt memcpy(fup->fu_name, name, len + 1); 618df930be7Sderaadt else 6195cb887c0Smillert (void)snprintf(fup->fu_name, len, "%u", id); 620df930be7Sderaadt return (fup); 621df930be7Sderaadt } 622df930be7Sderaadt 623df930be7Sderaadt /* 624df930be7Sderaadt * Special purpose version of ginode used to optimize pass 625df930be7Sderaadt * over all the inodes in numerical order. 626df930be7Sderaadt */ 6275cb887c0Smillert static ino_t nextino, lastinum, lastvalidinum; 6285cb887c0Smillert static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 6295cb887c0Smillert static caddr_t inodebuf; 630df930be7Sderaadt #define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ 631df930be7Sderaadt 6325cb887c0Smillert union dinode * 633bc52e260Sderaadt getnextinode(ino_t inumber) 634df930be7Sderaadt { 635df930be7Sderaadt long size; 6361abdbfdeSderaadt daddr_t dblk; 6375cb887c0Smillert union dinode *dp; 6385cb887c0Smillert static caddr_t nextinop; 639df930be7Sderaadt 6405cb887c0Smillert if (inumber != nextino++ || inumber > lastvalidinum) 64124ffb0c0Sderaadt err(1, "bad inode number %llu to nextinode", 64224ffb0c0Sderaadt (unsigned long long)inumber); 643df930be7Sderaadt if (inumber >= lastinum) { 644df930be7Sderaadt readcnt++; 645df930be7Sderaadt dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); 646df930be7Sderaadt if (readcnt % readpercg == 0) { 647df930be7Sderaadt size = partialsize; 648df930be7Sderaadt lastinum += partialcnt; 649df930be7Sderaadt } else { 650df930be7Sderaadt size = inobufsize; 651df930be7Sderaadt lastinum += fullcnt; 652df930be7Sderaadt } 6535cb887c0Smillert /* 6545cb887c0Smillert * If bread returns an error, it will already have zeroed 6555cb887c0Smillert * out the buffer, so we do not need to do so here. 6565cb887c0Smillert */ 6575cb887c0Smillert bread(dblk, inodebuf, size); 6585cb887c0Smillert nextinop = inodebuf; 659df930be7Sderaadt } 6605cb887c0Smillert dp = (union dinode *)nextinop; 6615cb887c0Smillert if (sblock.fs_magic == FS_UFS1_MAGIC) 6625cb887c0Smillert nextinop += sizeof(struct ufs1_dinode); 6635cb887c0Smillert else 6645cb887c0Smillert nextinop += sizeof(struct ufs2_dinode); 6655cb887c0Smillert return (dp); 666df930be7Sderaadt } 667df930be7Sderaadt 668df930be7Sderaadt /* 669df930be7Sderaadt * Prepare to scan a set of inodes. 670df930be7Sderaadt */ 671df930be7Sderaadt void 6725cb887c0Smillert setinodebuf(ino_t inum) 673df930be7Sderaadt { 674df930be7Sderaadt 6755cb887c0Smillert if (inum % sblock.fs_ipg != 0) 67624ffb0c0Sderaadt errx(1, "bad inode number %llu to setinodebuf", 67724ffb0c0Sderaadt (unsigned long long)inum); 6785cb887c0Smillert lastvalidinum = inum + sblock.fs_ipg - 1; 6795cb887c0Smillert nextino = inum; 6805cb887c0Smillert lastinum = inum; 681df930be7Sderaadt readcnt = 0; 6825cb887c0Smillert if (inodebuf != NULL) 6835cb887c0Smillert return; 684df930be7Sderaadt inobufsize = blkroundup(&sblock, INOBUFSIZE); 6855cb887c0Smillert fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ? 6865cb887c0Smillert sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 687df930be7Sderaadt readpercg = sblock.fs_ipg / fullcnt; 688df930be7Sderaadt partialcnt = sblock.fs_ipg % fullcnt; 6895cb887c0Smillert partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ? 6905cb887c0Smillert sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 691df930be7Sderaadt if (partialcnt != 0) { 692df930be7Sderaadt readpercg++; 693df930be7Sderaadt } else { 694df930be7Sderaadt partialcnt = fullcnt; 695df930be7Sderaadt partialsize = inobufsize; 696df930be7Sderaadt } 6975bd27043Sderaadt if ((inodebuf = malloc((size_t)inobufsize)) == NULL) 6985cb887c0Smillert errx(1, "cannot allocate space for inode buffer"); 699df930be7Sderaadt } 700df930be7Sderaadt 701df930be7Sderaadt /* 702df930be7Sderaadt * Free up data structures used to scan inodes. 703df930be7Sderaadt */ 704df930be7Sderaadt void 705bc52e260Sderaadt freeinodebuf(void) 706df930be7Sderaadt { 707df930be7Sderaadt 708df930be7Sderaadt free(inodebuf); 709df930be7Sderaadt inodebuf = NULL; 710df930be7Sderaadt } 711df930be7Sderaadt 712df930be7Sderaadt /* 713df930be7Sderaadt * Read specified disk blocks. 714df930be7Sderaadt */ 715df930be7Sderaadt void 7161abdbfdeSderaadt bread(daddr_t bno, char *buf, long cnt) 717df930be7Sderaadt { 7181593d282Skrw if (pread(fi, buf, cnt, bno * DEV_BSIZE) != cnt) 7191593d282Skrw err(1, "read failed on block %lld", (long long)bno); 720df930be7Sderaadt } 721