1*d5b07100Sjsg /* $OpenBSD: quotacheck.c,v 1.27 2012/03/24 15:39:54 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 */ 39df930be7Sderaadt #include <sys/param.h> 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> 532507a217Skrw #include <util.h> 54df930be7Sderaadt #include <stdio.h> 55df930be7Sderaadt #include <stdlib.h> 56df930be7Sderaadt #include <string.h> 57df930be7Sderaadt #include <err.h> 5835635556Smickey #include "fsutil.h" 59df930be7Sderaadt 60df930be7Sderaadt char *qfname = QUOTAFILENAME; 61df930be7Sderaadt char *qfextension[] = INITQFNAMES; 62df930be7Sderaadt char *quotagroup = QUOTAGROUP; 63df930be7Sderaadt 64df930be7Sderaadt union { 65df930be7Sderaadt struct fs sblk; 66df930be7Sderaadt char dummy[MAXBSIZE]; 675cb887c0Smillert } sb_un; 685cb887c0Smillert #define sblock sb_un.sblk 695cb887c0Smillert union { 705cb887c0Smillert struct cg cgblk; 715cb887c0Smillert char dummy[MAXBSIZE]; 725cb887c0Smillert } cg_un; 735cb887c0Smillert #define cgblk cg_un.cgblk 745cb887c0Smillert 75df930be7Sderaadt long dev_bsize; 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; 88df930be7Sderaadt char grpqfname[MAXPATHLEN + 1]; 89df930be7Sderaadt char usrqfname[MAXPATHLEN + 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 *); 11450c87d91Sderaadt void bread(daddr64_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 */ 139f3bae140Sderaadt char *name; 140df930be7Sderaadt 141df930be7Sderaadt errs = maxrun = 0; 14235635556Smickey while ((ch = getopt(argc, argv, "adguvl:")) != -1) { 143df930be7Sderaadt switch(ch) { 144df930be7Sderaadt case 'a': 14535635556Smickey flags |= CHECK_PREEN; 14635635556Smickey break; 14735635556Smickey case 'd': 14835635556Smickey flags |= CHECK_DEBUG; 149df930be7Sderaadt break; 150df930be7Sderaadt case 'g': 151df930be7Sderaadt gflag++; 152df930be7Sderaadt break; 15335635556Smickey case 'l': 15435635556Smickey maxrun = atoi(optarg); 15535635556Smickey break; 156df930be7Sderaadt case 'u': 157df930be7Sderaadt uflag++; 158df930be7Sderaadt break; 159df930be7Sderaadt case 'v': 16035635556Smickey flags |= CHECK_VERBOSE; 161df930be7Sderaadt break; 162df930be7Sderaadt default: 163df930be7Sderaadt usage(); 164df930be7Sderaadt } 165df930be7Sderaadt } 166df930be7Sderaadt argc -= optind; 167df930be7Sderaadt argv += optind; 16835635556Smickey if ((argc == 0 && !(flags&CHECK_PREEN)) || 16935635556Smickey (argc > 0 && (flags&CHECK_PREEN))) 170df930be7Sderaadt usage(); 171df930be7Sderaadt if (!gflag && !uflag) { 172df930be7Sderaadt gflag++; 173df930be7Sderaadt uflag++; 174df930be7Sderaadt } 175df930be7Sderaadt if (gflag) { 176df930be7Sderaadt setgrent(); 177df930be7Sderaadt while ((gr = getgrent()) != 0) 17850c87d91Sderaadt (void) addid(gr->gr_gid, GRPQUOTA, gr->gr_name); 179df930be7Sderaadt endgrent(); 180df930be7Sderaadt } 181df930be7Sderaadt if (uflag) { 182df930be7Sderaadt setpwent(); 183df930be7Sderaadt while ((pw = getpwent()) != 0) 18450c87d91Sderaadt (void) addid(pw->pw_uid, USRQUOTA, pw->pw_name); 185df930be7Sderaadt endpwent(); 186df930be7Sderaadt } 18735635556Smickey if (flags&CHECK_PREEN) 18835635556Smickey exit(checkfstab(flags, maxrun, needchk, chkquota)); 189df930be7Sderaadt if (setfsent() == 0) 190df930be7Sderaadt err(1, "%s: can't open", FSTAB); 191df930be7Sderaadt while ((fs = getfsent()) != NULL) { 1922507a217Skrw if (((argnum = oneof_realpath(fs->fs_file, argv, argc)) >= 0 || 1932507a217Skrw (argnum = oneof_specname(fs->fs_spec, argv, argc)) >= 0) && 194df930be7Sderaadt (auxdata = needchk(fs)) && 195df930be7Sderaadt (name = blockcheck(fs->fs_spec))) { 196df930be7Sderaadt done |= 1 << argnum; 19735635556Smickey errs += chkquota(fs->fs_vfstype, name, 19835635556Smickey fs->fs_file, auxdata, NULL); 199df930be7Sderaadt } 200df930be7Sderaadt } 201df930be7Sderaadt endfsent(); 202df930be7Sderaadt for (i = 0; i < argc; i++) 203df930be7Sderaadt if ((done & (1 << i)) == 0) 204df930be7Sderaadt fprintf(stderr, "%s not found in %s\n", 205df930be7Sderaadt argv[i], FSTAB); 206df930be7Sderaadt exit(errs); 207df930be7Sderaadt } 208df930be7Sderaadt 209df930be7Sderaadt void 210bc52e260Sderaadt usage(void) 211df930be7Sderaadt { 21231ed6db9Sjmc extern char *__progname; 21331ed6db9Sjmc (void)fprintf(stderr, "usage: %s [-adguv] [-l maxparallel] " 21431ed6db9Sjmc "filesystem ...\n", __progname); 215df930be7Sderaadt exit(1); 216df930be7Sderaadt } 217df930be7Sderaadt 218df930be7Sderaadt void * 219bc52e260Sderaadt needchk(struct fstab *fs) 220df930be7Sderaadt { 221e073c79dSmpech struct quotaname *qnp; 222df930be7Sderaadt char *qfnp; 223df930be7Sderaadt 22435635556Smickey if (fs->fs_passno == 0) 22535635556Smickey return NULL; 226e5cb37d1Sderaadt if (strcmp(fs->fs_type, FSTAB_RW)) 227e5cb37d1Sderaadt return (NULL); 228e5cb37d1Sderaadt if (strcmp(fs->fs_vfstype, "ffs") && 229dde99675Sderaadt strcmp(fs->fs_vfstype, "ufs") && 230dde99675Sderaadt strcmp(fs->fs_vfstype, "mfs")) 231df930be7Sderaadt return (NULL); 232df930be7Sderaadt if ((qnp = malloc(sizeof(*qnp))) == NULL) 233df930be7Sderaadt err(1, "%s", strerror(errno)); 234df930be7Sderaadt qnp->flags = 0; 235df930be7Sderaadt if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) { 2369e405e78Sderaadt strlcpy(qnp->grpqfname, qfnp, sizeof qnp->grpqfname); 237df930be7Sderaadt qnp->flags |= HASGRP; 238df930be7Sderaadt } 239df930be7Sderaadt if (uflag && hasquota(fs, USRQUOTA, &qfnp)) { 2409e405e78Sderaadt strlcpy(qnp->usrqfname, qfnp, sizeof qnp->usrqfname); 241df930be7Sderaadt qnp->flags |= HASUSR; 242df930be7Sderaadt } 243df930be7Sderaadt if (qnp->flags) 244df930be7Sderaadt return (qnp); 245df930be7Sderaadt free(qnp); 246df930be7Sderaadt return (NULL); 247df930be7Sderaadt } 248df930be7Sderaadt 249df930be7Sderaadt /* 2505cb887c0Smillert * Possible superblock locations ordered from most to least likely. 2515cb887c0Smillert */ 2525cb887c0Smillert static int sblock_try[] = SBLOCKSEARCH; 2535cb887c0Smillert 2545cb887c0Smillert /* 255df930be7Sderaadt * Scan the specified file system to check quota(s) present on it. 256df930be7Sderaadt */ 257df930be7Sderaadt int 258bc52e260Sderaadt chkquota(const char *vfstype, const char *fsname, const char *mntpt, 259bc52e260Sderaadt void *auxarg, pid_t *pidp) 260df930be7Sderaadt { 261e073c79dSmpech struct quotaname *qnp = auxarg; 262e073c79dSmpech struct fileusage *fup; 2635cb887c0Smillert union dinode *dp; 26435635556Smickey int cg, i, mode, errs = 0, status; 2655cb887c0Smillert ino_t ino, inosused; 26635635556Smickey pid_t pid; 2675cb887c0Smillert char *cp; 268df930be7Sderaadt 26935635556Smickey switch (pid = fork()) { 27035635556Smickey case -1: /* error */ 27135635556Smickey warn("fork"); 27235635556Smickey return 1; 27335635556Smickey case 0: /* child */ 27435635556Smickey if ((fi = open(fsname, O_RDONLY, 0)) < 0) 2757bf18b22Sderaadt err(1, "%s", fsname); 276df930be7Sderaadt sync(); 277df930be7Sderaadt dev_bsize = 1; 2785cb887c0Smillert for (i = 0; sblock_try[i] != -1; i++) { 2795cb887c0Smillert bread(sblock_try[i], (char *)&sblock, (long)SBLOCKSIZE); 2805cb887c0Smillert if ((sblock.fs_magic == FS_UFS1_MAGIC || 2815cb887c0Smillert (sblock.fs_magic == FS_UFS2_MAGIC && 2824ec93eb9Sotto sblock.fs_sblockloc == sblock_try[i])) && 2835cb887c0Smillert sblock.fs_bsize <= MAXBSIZE && 2845cb887c0Smillert sblock.fs_bsize >= sizeof(struct fs)) 2855cb887c0Smillert break; 2865cb887c0Smillert } 2875cb887c0Smillert if (sblock_try[i] == -1) { 2885cb887c0Smillert warn("Cannot find file system superblock"); 2895cb887c0Smillert return (1); 2905cb887c0Smillert } 291df930be7Sderaadt dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); 292df930be7Sderaadt maxino = sblock.fs_ncg * sblock.fs_ipg; 2935cb887c0Smillert for (cg = 0; cg < sblock.fs_ncg; cg++) { 2945cb887c0Smillert ino = cg * sblock.fs_ipg; 2955cb887c0Smillert setinodebuf(ino); 2965cb887c0Smillert bread(fsbtodb(&sblock, cgtod(&sblock, cg)), 2975cb887c0Smillert (char *)(&cgblk), sblock.fs_cgsize); 2985cb887c0Smillert if (sblock.fs_magic == FS_UFS2_MAGIC) 2995cb887c0Smillert inosused = cgblk.cg_initediblk; 3005cb887c0Smillert else 3015cb887c0Smillert inosused = sblock.fs_ipg; 3025cb887c0Smillert /* 3035cb887c0Smillert * If we are using soft updates, then we can trust the 3045cb887c0Smillert * cylinder group inode allocation maps to tell us which 3055cb887c0Smillert * inodes are allocated. We will scan the used inode map 3065cb887c0Smillert * to find the inodes that are really in use, and then 3075cb887c0Smillert * read only those inodes in from disk. 3085cb887c0Smillert */ 3095cb887c0Smillert if (sblock.fs_flags & FS_DOSOFTDEP) { 3105cb887c0Smillert if (!cg_chkmagic(&cgblk)) 3115cb887c0Smillert errx(1, "CG %d: BAD MAGIC NUMBER\n", cg); 3125cb887c0Smillert cp = &cg_inosused(&cgblk)[(inosused - 1) / CHAR_BIT]; 3135cb887c0Smillert for ( ; inosused > 0; inosused -= CHAR_BIT, cp--) { 3145cb887c0Smillert if (*cp == 0) 315df930be7Sderaadt continue; 3165cb887c0Smillert for (i = 1 << (CHAR_BIT - 1); i > 0; i >>= 1) { 3175cb887c0Smillert if (*cp & i) 3185cb887c0Smillert break; 3195cb887c0Smillert inosused--; 3205cb887c0Smillert } 3215cb887c0Smillert break; 3225cb887c0Smillert } 3235cb887c0Smillert if (inosused <= 0) 324df930be7Sderaadt continue; 3255cb887c0Smillert } 3265cb887c0Smillert for (i = 0; i < inosused; i++, ino++) { 3275cb887c0Smillert if ((dp = getnextinode(ino)) == NULL || 3285cb887c0Smillert ino < ROOTINO || 3295cb887c0Smillert (mode = DIP(dp, di_mode) & IFMT) == 0) 330df930be7Sderaadt continue; 331df930be7Sderaadt if (qnp->flags & HASGRP) { 3325cb887c0Smillert fup = addid(DIP(dp, di_gid), 3337674422fSkstailey GRPQUOTA, NULL); 334df930be7Sderaadt fup->fu_curinodes++; 335df930be7Sderaadt if (mode == IFREG || mode == IFDIR || 336df930be7Sderaadt mode == IFLNK) 33735635556Smickey fup->fu_curblocks += 3385cb887c0Smillert DIP(dp, di_blocks); 339df930be7Sderaadt } 340df930be7Sderaadt if (qnp->flags & HASUSR) { 3415cb887c0Smillert fup = addid(DIP(dp, di_uid), 3427674422fSkstailey USRQUOTA, NULL); 343df930be7Sderaadt fup->fu_curinodes++; 344df930be7Sderaadt if (mode == IFREG || mode == IFDIR || 345df930be7Sderaadt mode == IFLNK) 34635635556Smickey fup->fu_curblocks += 3475cb887c0Smillert DIP(dp, di_blocks); 348df930be7Sderaadt } 349df930be7Sderaadt } 350df930be7Sderaadt } 351df930be7Sderaadt freeinodebuf(); 35235635556Smickey if (flags&(CHECK_DEBUG|CHECK_VERBOSE)) { 35335635556Smickey (void)printf("*** Checking "); 35435635556Smickey if (qnp->flags & HASUSR) { 35535635556Smickey (void)printf("%s", qfextension[USRQUOTA]); 35635635556Smickey if (qnp->flags & HASGRP) 35735635556Smickey (void)printf(" and "); 35835635556Smickey } 35935635556Smickey if (qnp->flags & HASGRP) 36035635556Smickey (void)printf("%s", qfextension[GRPQUOTA]); 36135635556Smickey (void)printf(" quotas for %s (%s), %swait\n", 36235635556Smickey fsname, mntpt, pidp? "no" : ""); 36335635556Smickey } 364df930be7Sderaadt if (qnp->flags & HASUSR) 365df930be7Sderaadt errs += update(mntpt, qnp->usrqfname, USRQUOTA); 366df930be7Sderaadt if (qnp->flags & HASGRP) 367df930be7Sderaadt errs += update(mntpt, qnp->grpqfname, GRPQUOTA); 368df930be7Sderaadt close(fi); 36935635556Smickey exit (errs); 37035635556Smickey break; 37135635556Smickey default: /* parent */ 37235635556Smickey if (pidp != NULL) { 37335635556Smickey *pidp = pid; 37435635556Smickey return 0; 37535635556Smickey } 37635635556Smickey if (waitpid(pid, &status, 0) < 0) { 37735635556Smickey warn("waitpid"); 37835635556Smickey return 1; 37935635556Smickey } 38035635556Smickey if (WIFEXITED(status)) { 38135635556Smickey if (WEXITSTATUS(status) != 0) 38235635556Smickey return WEXITSTATUS(status); 38335635556Smickey } else if (WIFSIGNALED(status)) { 38435635556Smickey warnx("%s: %s", fsname, strsignal(WTERMSIG(status))); 38535635556Smickey return 1; 38635635556Smickey } 38735635556Smickey break; 38835635556Smickey } 38935635556Smickey return (0); 390df930be7Sderaadt } 391df930be7Sderaadt 392df930be7Sderaadt /* 393df930be7Sderaadt * Update a specified quota file. 394df930be7Sderaadt */ 395df930be7Sderaadt int 396bc52e260Sderaadt update(const char *fsname, const char *quotafile, int type) 397df930be7Sderaadt { 398e073c79dSmpech struct fileusage *fup; 399e073c79dSmpech FILE *qfi, *qfo; 40050c87d91Sderaadt u_int32_t id, lastid; 401df930be7Sderaadt struct dqblk dqbuf; 402df930be7Sderaadt static int warned = 0; 403df930be7Sderaadt static struct dqblk zerodqbuf; 404df930be7Sderaadt static struct fileusage zerofileusage; 405df930be7Sderaadt 40635635556Smickey if (flags&CHECK_DEBUG) 40735635556Smickey printf("updating: %s\n", quotafile); 40835635556Smickey 40935635556Smickey if ((qfo = fopen(quotafile, (flags&CHECK_DEBUG)? "r" : "r+")) == NULL) { 410df930be7Sderaadt if (errno == ENOENT) 411df930be7Sderaadt qfo = fopen(quotafile, "w+"); 412df930be7Sderaadt if (qfo) { 41335635556Smickey warnx("creating quota file: %s", quotafile); 414df930be7Sderaadt #define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 415df930be7Sderaadt (void) fchown(fileno(qfo), getuid(), getquotagid()); 416df930be7Sderaadt (void) fchmod(fileno(qfo), MODE); 417df930be7Sderaadt } else { 41835635556Smickey warn("%s", quotafile); 419df930be7Sderaadt return (1); 420df930be7Sderaadt } 421df930be7Sderaadt } 422df930be7Sderaadt if ((qfi = fopen(quotafile, "r")) == NULL) { 42335635556Smickey warn("%s", quotafile); 424df930be7Sderaadt (void) fclose(qfo); 425df930be7Sderaadt return (1); 426df930be7Sderaadt } 42750c87d91Sderaadt if (quotactl(fsname, QCMD(Q_SYNC, type), 0, (caddr_t)0) < 0 && 42835635556Smickey errno == EOPNOTSUPP && !warned && 42935635556Smickey (flags&(CHECK_DEBUG|CHECK_VERBOSE))) { 430df930be7Sderaadt warned++; 431df930be7Sderaadt (void)printf("*** Warning: %s\n", 432df930be7Sderaadt "Quotas are not compiled into this kernel"); 433df930be7Sderaadt } 434df930be7Sderaadt for (lastid = highid[type], id = 0; id <= lastid; id++) { 435df930be7Sderaadt if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 436df930be7Sderaadt dqbuf = zerodqbuf; 437df930be7Sderaadt if ((fup = lookup(id, type)) == 0) 438df930be7Sderaadt fup = &zerofileusage; 439df930be7Sderaadt if (dqbuf.dqb_curinodes == fup->fu_curinodes && 440df930be7Sderaadt dqbuf.dqb_curblocks == fup->fu_curblocks) { 441df930be7Sderaadt fup->fu_curinodes = 0; 442df930be7Sderaadt fup->fu_curblocks = 0; 44350c87d91Sderaadt fseek(qfo, (long)sizeof(struct dqblk), SEEK_CUR); 444df930be7Sderaadt continue; 445df930be7Sderaadt } 44635635556Smickey if (flags&(CHECK_DEBUG|CHECK_VERBOSE)) { 44735635556Smickey if (flags&CHECK_PREEN) 448df930be7Sderaadt printf("%s: ", fsname); 449df930be7Sderaadt printf("%-8s fixed:", fup->fu_name); 450df930be7Sderaadt if (dqbuf.dqb_curinodes != fup->fu_curinodes) 4515cb887c0Smillert (void)printf("\tinodes %d -> %u", 452df930be7Sderaadt dqbuf.dqb_curinodes, fup->fu_curinodes); 453df930be7Sderaadt if (dqbuf.dqb_curblocks != fup->fu_curblocks) 4545cb887c0Smillert (void)printf("\tblocks %u -> %u", 455df930be7Sderaadt dqbuf.dqb_curblocks, fup->fu_curblocks); 456df930be7Sderaadt (void)printf("\n"); 457df930be7Sderaadt } 458df930be7Sderaadt /* 459df930be7Sderaadt * Reset time limit if have a soft limit and were 460df930be7Sderaadt * previously under it, but are now over it. 461df930be7Sderaadt */ 462df930be7Sderaadt if (dqbuf.dqb_bsoftlimit && 463df930be7Sderaadt dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 464df930be7Sderaadt fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) 465df930be7Sderaadt dqbuf.dqb_btime = 0; 466df930be7Sderaadt if (dqbuf.dqb_isoftlimit && 467df930be7Sderaadt dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit && 468df930be7Sderaadt fup->fu_curblocks >= dqbuf.dqb_isoftlimit) 469df930be7Sderaadt dqbuf.dqb_itime = 0; 470df930be7Sderaadt dqbuf.dqb_curinodes = fup->fu_curinodes; 471df930be7Sderaadt dqbuf.dqb_curblocks = fup->fu_curblocks; 47235635556Smickey if (!(flags & CHECK_DEBUG)) { 473df930be7Sderaadt fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 474df930be7Sderaadt (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, 475df930be7Sderaadt (caddr_t)&dqbuf); 47635635556Smickey } 477df930be7Sderaadt fup->fu_curinodes = 0; 478df930be7Sderaadt fup->fu_curblocks = 0; 479df930be7Sderaadt } 480df930be7Sderaadt fclose(qfi); 481df930be7Sderaadt fflush(qfo); 48235635556Smickey if (!(flags & CHECK_DEBUG)) 483df930be7Sderaadt ftruncate(fileno(qfo), 484df930be7Sderaadt (off_t)((highid[type] + 1) * sizeof(struct dqblk))); 485df930be7Sderaadt fclose(qfo); 486df930be7Sderaadt return (0); 487df930be7Sderaadt } 488df930be7Sderaadt 489df930be7Sderaadt /* 4902507a217Skrw * Check to see if realpath(target) matches a realpath() in list of size cnt. 491df930be7Sderaadt */ 492df930be7Sderaadt int 4932507a217Skrw oneof_realpath(char *target, char *list[], int cnt) 494df930be7Sderaadt { 495e073c79dSmpech int i; 4962507a217Skrw char realtarget[PATH_MAX], realargv[PATH_MAX]; 4972507a217Skrw char *rv; 498df930be7Sderaadt 4992507a217Skrw rv = realpath(target, realtarget); 5002507a217Skrw if (rv == NULL) 5012507a217Skrw return (-1); 5022507a217Skrw 5032507a217Skrw for (i = 0; i < cnt; i++) { 5042507a217Skrw rv = realpath(list[i], realargv); 5052507a217Skrw if (rv && strcmp(realtarget, realargv) == 0) 5062507a217Skrw break; 5072507a217Skrw } 5082507a217Skrw 5092507a217Skrw if (i < cnt) 510df930be7Sderaadt return (i); 5112507a217Skrw else 5122507a217Skrw return (-1); 5132507a217Skrw } 5142507a217Skrw 5152507a217Skrw /* 5162507a217Skrw * Check to see if opendev(target) matches a opendev() in list of size cnt. 5172507a217Skrw */ 5182507a217Skrw int 5192507a217Skrw oneof_specname(char *target, char *list[], int cnt) 5202507a217Skrw { 5212507a217Skrw int i, fd; 5222507a217Skrw char *tmp, *targetdev, *argvdev; 5232507a217Skrw 5242507a217Skrw fd = opendev(target, O_RDONLY, 0, &tmp); 5252507a217Skrw if (fd == -1) 5262507a217Skrw return (-1); 5272507a217Skrw close(fd); 5282507a217Skrw targetdev = strdup(tmp); 5292507a217Skrw 5302507a217Skrw for (i = 0; i < cnt; i++) { 5312507a217Skrw fd = opendev(list[i], O_RDONLY, 0, &argvdev); 5322507a217Skrw if (fd == -1) 5332507a217Skrw continue; 5342507a217Skrw close(fd); 5352507a217Skrw if (strcmp(targetdev, argvdev) == 0) 5362507a217Skrw break; 5372507a217Skrw } 5382507a217Skrw 539*d5b07100Sjsg free(targetdev); 540*d5b07100Sjsg 5412507a217Skrw if (i < cnt) 5422507a217Skrw return (i); 5432507a217Skrw else 544df930be7Sderaadt return (-1); 545df930be7Sderaadt } 546df930be7Sderaadt 547df930be7Sderaadt /* 548df930be7Sderaadt * Determine the group identifier for quota files. 549df930be7Sderaadt */ 550df930be7Sderaadt int 551bc52e260Sderaadt getquotagid(void) 552df930be7Sderaadt { 553df930be7Sderaadt struct group *gr; 554df930be7Sderaadt 55535635556Smickey if ((gr = getgrnam(quotagroup)) != NULL) 556df930be7Sderaadt return (gr->gr_gid); 557df930be7Sderaadt return (-1); 558df930be7Sderaadt } 559df930be7Sderaadt 560df930be7Sderaadt /* 561df930be7Sderaadt * Check to see if a particular quota is to be enabled. 562df930be7Sderaadt */ 563df930be7Sderaadt int 564bc52e260Sderaadt hasquota(struct fstab *fs, int type, char **qfnamep) 565df930be7Sderaadt { 566a7c59635Sderaadt char *opt, *cp; 567df930be7Sderaadt static char initname, usrname[100], grpname[100]; 568df930be7Sderaadt static char buf[BUFSIZ]; 569df930be7Sderaadt 570df930be7Sderaadt if (!initname) { 571df930be7Sderaadt (void)snprintf(usrname, sizeof(usrname), 572df930be7Sderaadt "%s%s", qfextension[USRQUOTA], qfname); 573df930be7Sderaadt (void)snprintf(grpname, sizeof(grpname), 574df930be7Sderaadt "%s%s", qfextension[GRPQUOTA], qfname); 575df930be7Sderaadt initname = 1; 576df930be7Sderaadt } 5778f25adf2Sderaadt (void)strlcpy(buf, fs->fs_mntops, sizeof(buf)); 578df930be7Sderaadt for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 57935635556Smickey if ((cp = strchr(opt, '=')) != NULL) 580df930be7Sderaadt *cp++ = '\0'; 581df930be7Sderaadt if (type == USRQUOTA && strcmp(opt, usrname) == 0) 582df930be7Sderaadt break; 583df930be7Sderaadt if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 584df930be7Sderaadt break; 585df930be7Sderaadt } 586df930be7Sderaadt if (!opt) 587df930be7Sderaadt return (0); 588df930be7Sderaadt if (cp) 589df930be7Sderaadt *qfnamep = cp; 590df930be7Sderaadt else { 591df930be7Sderaadt (void)snprintf(buf, sizeof(buf), 592df930be7Sderaadt "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 593df930be7Sderaadt *qfnamep = buf; 594df930be7Sderaadt } 595df930be7Sderaadt return (1); 596df930be7Sderaadt } 597df930be7Sderaadt 598df930be7Sderaadt /* 599df930be7Sderaadt * Routines to manage the file usage table. 600df930be7Sderaadt * 601df930be7Sderaadt * Lookup an id of a specific type. 602df930be7Sderaadt */ 603df930be7Sderaadt struct fileusage * 60450c87d91Sderaadt lookup(u_int32_t id, int type) 605df930be7Sderaadt { 606e073c79dSmpech struct fileusage *fup; 607df930be7Sderaadt 608df930be7Sderaadt for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 609df930be7Sderaadt if (fup->fu_id == id) 610df930be7Sderaadt return (fup); 611df930be7Sderaadt return (NULL); 612df930be7Sderaadt } 613df930be7Sderaadt 614df930be7Sderaadt /* 615df930be7Sderaadt * Add a new file usage id if it does not already exist. 616df930be7Sderaadt */ 617df930be7Sderaadt struct fileusage * 61850c87d91Sderaadt addid(u_int32_t id, int type, char *name) 619df930be7Sderaadt { 620df930be7Sderaadt struct fileusage *fup, **fhp; 621df930be7Sderaadt int len; 622df930be7Sderaadt 62335635556Smickey if ((fup = lookup(id, type)) != NULL) 624df930be7Sderaadt return (fup); 625df930be7Sderaadt if (name) 626df930be7Sderaadt len = strlen(name); 627df930be7Sderaadt else 628df930be7Sderaadt len = 10; 629df930be7Sderaadt if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) 630df930be7Sderaadt err(1, "%s", strerror(errno)); 631df930be7Sderaadt fhp = &fuhead[type][id & (FUHASH - 1)]; 632df930be7Sderaadt fup->fu_next = *fhp; 633df930be7Sderaadt *fhp = fup; 634df930be7Sderaadt fup->fu_id = id; 635df930be7Sderaadt if (id > highid[type]) 636df930be7Sderaadt highid[type] = id; 637df930be7Sderaadt if (name) 638df930be7Sderaadt memcpy(fup->fu_name, name, len + 1); 639df930be7Sderaadt else 6405cb887c0Smillert (void)snprintf(fup->fu_name, len, "%u", id); 641df930be7Sderaadt return (fup); 642df930be7Sderaadt } 643df930be7Sderaadt 644df930be7Sderaadt /* 645df930be7Sderaadt * Special purpose version of ginode used to optimize pass 646df930be7Sderaadt * over all the inodes in numerical order. 647df930be7Sderaadt */ 6485cb887c0Smillert static ino_t nextino, lastinum, lastvalidinum; 6495cb887c0Smillert static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 6505cb887c0Smillert static caddr_t inodebuf; 651df930be7Sderaadt #define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ 652df930be7Sderaadt 6535cb887c0Smillert union dinode * 654bc52e260Sderaadt getnextinode(ino_t inumber) 655df930be7Sderaadt { 656df930be7Sderaadt long size; 65750c87d91Sderaadt daddr64_t dblk; 6585cb887c0Smillert union dinode *dp; 6595cb887c0Smillert static caddr_t nextinop; 660df930be7Sderaadt 6615cb887c0Smillert if (inumber != nextino++ || inumber > lastvalidinum) 66250c87d91Sderaadt err(1, "bad inode number %u to nextinode", inumber); 663df930be7Sderaadt if (inumber >= lastinum) { 664df930be7Sderaadt readcnt++; 665df930be7Sderaadt dblk = fsbtodb(&sblock, ino_to_fsba(&sblock, lastinum)); 666df930be7Sderaadt if (readcnt % readpercg == 0) { 667df930be7Sderaadt size = partialsize; 668df930be7Sderaadt lastinum += partialcnt; 669df930be7Sderaadt } else { 670df930be7Sderaadt size = inobufsize; 671df930be7Sderaadt lastinum += fullcnt; 672df930be7Sderaadt } 6735cb887c0Smillert /* 6745cb887c0Smillert * If bread returns an error, it will already have zeroed 6755cb887c0Smillert * out the buffer, so we do not need to do so here. 6765cb887c0Smillert */ 6775cb887c0Smillert bread(dblk, inodebuf, size); 6785cb887c0Smillert nextinop = inodebuf; 679df930be7Sderaadt } 6805cb887c0Smillert dp = (union dinode *)nextinop; 6815cb887c0Smillert if (sblock.fs_magic == FS_UFS1_MAGIC) 6825cb887c0Smillert nextinop += sizeof(struct ufs1_dinode); 6835cb887c0Smillert else 6845cb887c0Smillert nextinop += sizeof(struct ufs2_dinode); 6855cb887c0Smillert return (dp); 686df930be7Sderaadt } 687df930be7Sderaadt 688df930be7Sderaadt /* 689df930be7Sderaadt * Prepare to scan a set of inodes. 690df930be7Sderaadt */ 691df930be7Sderaadt void 6925cb887c0Smillert setinodebuf(ino_t inum) 693df930be7Sderaadt { 694df930be7Sderaadt 6955cb887c0Smillert if (inum % sblock.fs_ipg != 0) 6965cb887c0Smillert errx(1, "bad inode number %d to setinodebuf", inum); 6975cb887c0Smillert lastvalidinum = inum + sblock.fs_ipg - 1; 6985cb887c0Smillert nextino = inum; 6995cb887c0Smillert lastinum = inum; 700df930be7Sderaadt readcnt = 0; 7015cb887c0Smillert if (inodebuf != NULL) 7025cb887c0Smillert return; 703df930be7Sderaadt inobufsize = blkroundup(&sblock, INOBUFSIZE); 7045cb887c0Smillert fullcnt = inobufsize / ((sblock.fs_magic == FS_UFS1_MAGIC) ? 7055cb887c0Smillert sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 706df930be7Sderaadt readpercg = sblock.fs_ipg / fullcnt; 707df930be7Sderaadt partialcnt = sblock.fs_ipg % fullcnt; 7085cb887c0Smillert partialsize = partialcnt * ((sblock.fs_magic == FS_UFS1_MAGIC) ? 7095cb887c0Smillert sizeof(struct ufs1_dinode) : sizeof(struct ufs2_dinode)); 710df930be7Sderaadt if (partialcnt != 0) { 711df930be7Sderaadt readpercg++; 712df930be7Sderaadt } else { 713df930be7Sderaadt partialcnt = fullcnt; 714df930be7Sderaadt partialsize = inobufsize; 715df930be7Sderaadt } 7165cb887c0Smillert if ((inodebuf = malloc((unsigned)inobufsize)) == NULL) 7175cb887c0Smillert errx(1, "cannot allocate space for inode buffer"); 718df930be7Sderaadt } 719df930be7Sderaadt 720df930be7Sderaadt /* 721df930be7Sderaadt * Free up data structures used to scan inodes. 722df930be7Sderaadt */ 723df930be7Sderaadt void 724bc52e260Sderaadt freeinodebuf(void) 725df930be7Sderaadt { 726df930be7Sderaadt 727df930be7Sderaadt if (inodebuf != NULL) 728df930be7Sderaadt free(inodebuf); 729df930be7Sderaadt inodebuf = NULL; 730df930be7Sderaadt } 731df930be7Sderaadt 732df930be7Sderaadt /* 733df930be7Sderaadt * Read specified disk blocks. 734df930be7Sderaadt */ 735df930be7Sderaadt void 73650c87d91Sderaadt bread(daddr64_t bno, char *buf, long cnt) 737df930be7Sderaadt { 738df930be7Sderaadt 739df930be7Sderaadt if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 || 740df930be7Sderaadt read(fi, buf, cnt) != cnt) 7415cb887c0Smillert err(1, "bread failed on block %lld", (long long)bno); 742df930be7Sderaadt } 743