1 1.51 chs /* $NetBSD: quotacheck.c,v 1.51 2023/01/07 19:41:30 chs Exp $ */ 2 1.10 cgd 3 1.1 cgd /* 4 1.5 mycroft * Copyright (c) 1980, 1990, 1993 5 1.5 mycroft * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Robert Elz at The University of Melbourne. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.28 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.15 lukem #include <sys/cdefs.h> 36 1.1 cgd #ifndef lint 37 1.39 lukem __COPYRIGHT("@(#) Copyright (c) 1980, 1990, 1993\ 38 1.39 lukem The Regents of the University of California. All rights reserved."); 39 1.1 cgd #endif /* not lint */ 40 1.1 cgd 41 1.1 cgd #ifndef lint 42 1.10 cgd #if 0 43 1.15 lukem static char sccsid[] = "@(#)quotacheck.c 8.6 (Berkeley) 4/28/95"; 44 1.10 cgd #else 45 1.51 chs __RCSID("$NetBSD: quotacheck.c,v 1.51 2023/01/07 19:41:30 chs Exp $"); 46 1.10 cgd #endif 47 1.1 cgd #endif /* not lint */ 48 1.1 cgd 49 1.1 cgd /* 50 1.1 cgd * Fix up / report on disk quotas & usage 51 1.1 cgd */ 52 1.1 cgd #include <sys/param.h> 53 1.1 cgd #include <sys/stat.h> 54 1.15 lukem #include <sys/queue.h> 55 1.41 bouyer #include <sys/statvfs.h> 56 1.5 mycroft 57 1.5 mycroft #include <ufs/ufs/dinode.h> 58 1.41 bouyer #include <ufs/ufs/quota1.h> 59 1.17 bouyer #include <ufs/ufs/ufs_bswap.h> 60 1.5 mycroft #include <ufs/ffs/fs.h> 61 1.17 bouyer #include <ufs/ffs/ffs_extern.h> 62 1.5 mycroft 63 1.15 lukem #include <err.h> 64 1.1 cgd #include <fcntl.h> 65 1.1 cgd #include <fstab.h> 66 1.1 cgd #include <pwd.h> 67 1.1 cgd #include <grp.h> 68 1.1 cgd #include <errno.h> 69 1.1 cgd #include <unistd.h> 70 1.1 cgd #include <stdio.h> 71 1.1 cgd #include <stdlib.h> 72 1.1 cgd #include <string.h> 73 1.45 christos #include <util.h> 74 1.1 cgd 75 1.13 christos #include "fsutil.h" 76 1.42 christos #include "quotautil.h" 77 1.1 cgd 78 1.36 christos #ifndef FS_UFS1_MAGIC 79 1.36 christos # define FS_UFS1_MAGIC FS_MAGIC /* 0x011954 */ 80 1.36 christos # define FS_UFS1_MAGIC_SWAPPED 0x54190100 /* bswap32(0x011954) */ 81 1.36 christos # define DINODE1_SIZE sizeof(struct dinode) 82 1.36 christos # define DINODE2_SIZE 0 83 1.36 christos #else 84 1.36 christos # define HAVE_UFSv2 1 85 1.36 christos #endif 86 1.36 christos 87 1.36 christos #ifndef SBLOCKSIZE 88 1.36 christos # define SBLOCKSIZE SBSIZE 89 1.36 christos #endif 90 1.36 christos #ifndef SBLOCKSEARCH 91 1.36 christos # define SBLOCKSEARCH { SBSIZE, -1 } 92 1.36 christos #endif 93 1.36 christos 94 1.36 christos static const char *quotagroup = QUOTAGROUP; 95 1.13 christos 96 1.13 christos static union { 97 1.1 cgd struct fs sblk; 98 1.1 cgd char dummy[MAXBSIZE]; 99 1.1 cgd } un; 100 1.1 cgd #define sblock un.sblk 101 1.13 christos static long dev_bsize; 102 1.13 christos static long maxino; 103 1.1 cgd 104 1.1 cgd struct quotaname { 105 1.1 cgd long flags; 106 1.1 cgd char grpqfname[MAXPATHLEN + 1]; 107 1.1 cgd char usrqfname[MAXPATHLEN + 1]; 108 1.1 cgd }; 109 1.1 cgd #define HASUSR 1 110 1.1 cgd #define HASGRP 2 111 1.1 cgd 112 1.1 cgd struct fileusage { 113 1.1 cgd struct fileusage *fu_next; 114 1.1 cgd u_long fu_curinodes; 115 1.1 cgd u_long fu_curblocks; 116 1.42 christos uint32_t fu_id; /* uid_t, gid_t */ 117 1.1 cgd char fu_name[1]; 118 1.1 cgd /* actually bigger */ 119 1.1 cgd }; 120 1.1 cgd #define FUHASH 1024 /* must be power of two */ 121 1.13 christos static struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 122 1.1 cgd 123 1.25 fvdl 124 1.36 christos union comb_dinode { 125 1.36 christos #ifdef HAVE_UFSv2 126 1.25 fvdl struct ufs1_dinode dp1; 127 1.25 fvdl struct ufs2_dinode dp2; 128 1.36 christos #else 129 1.36 christos struct dinode dp1; 130 1.36 christos #endif 131 1.25 fvdl }; 132 1.36 christos #ifdef HAVE_UFSv2 133 1.25 fvdl #define DIP(dp, field) \ 134 1.25 fvdl (is_ufs2 ? (dp)->dp2.di_##field : (dp)->dp1.di_##field) 135 1.36 christos #else 136 1.36 christos #define DIP(dp, field) (dp)->dp1.di_##field 137 1.36 christos #endif 138 1.25 fvdl 139 1.25 fvdl 140 1.13 christos static int aflag; /* all file systems */ 141 1.13 christos static int gflag; /* check group quotas */ 142 1.13 christos static int uflag; /* check user quotas */ 143 1.13 christos static int vflag; /* verbose */ 144 1.29 christos static int qflag; /* quick but untidy mode */ 145 1.13 christos static int fi; /* open disk file descriptor */ 146 1.42 christos static uint32_t highid[MAXQUOTAS];/* highest addid()'ed identifier per type */ 147 1.17 bouyer static int needswap; /* FS is in swapped order */ 148 1.23 bouyer static int got_siginfo = 0; /* got a siginfo signal */ 149 1.25 fvdl static int is_ufs2; 150 1.13 christos 151 1.13 christos 152 1.42 christos static void usage(void) __attribute__((__noreturn__)); 153 1.42 christos static void *needchk(struct fstab *); 154 1.42 christos static int chkquota(const char *, const char *, const char *, void *, pid_t *); 155 1.42 christos static int update(const char *, const char *, int); 156 1.42 christos static uint32_t skipforward(uint32_t, uint32_t, FILE *); 157 1.42 christos static int getquotagid(void); 158 1.42 christos static struct fileusage *lookup(uint32_t, int); 159 1.42 christos static struct fileusage *addid(uint32_t, int, const char *); 160 1.42 christos static uint32_t subsequent(uint32_t, int) ; 161 1.42 christos static union comb_dinode *getnextinode(ino_t); 162 1.42 christos static void setinodebuf(ino_t); 163 1.42 christos static void freeinodebuf(void); 164 1.42 christos static void bread(daddr_t, char *, long); 165 1.42 christos static void infohandler(int sig); 166 1.36 christos static void swap_dinode1(union comb_dinode *, int); 167 1.36 christos #ifdef HAVE_UFSv2 168 1.36 christos static void swap_dinode2(union comb_dinode *, int); 169 1.36 christos #endif 170 1.5 mycroft 171 1.5 mycroft int 172 1.42 christos main(int argc, char *argv[]) 173 1.1 cgd { 174 1.13 christos struct fstab *fs; 175 1.13 christos struct passwd *pw; 176 1.13 christos struct group *gr; 177 1.5 mycroft struct quotaname *auxdata; 178 1.5 mycroft int i, argnum, maxrun, errs; 179 1.5 mycroft long done = 0; 180 1.14 christos int flags = CHECK_PREEN; 181 1.18 mycroft const char *name; 182 1.12 mark int ch; 183 1.1 cgd 184 1.5 mycroft errs = maxrun = 0; 185 1.29 christos while ((ch = getopt(argc, argv, "aguvqdl:")) != -1) { 186 1.1 cgd switch(ch) { 187 1.1 cgd case 'a': 188 1.1 cgd aflag++; 189 1.1 cgd break; 190 1.14 christos case 'd': 191 1.14 christos flags |= CHECK_DEBUG; 192 1.14 christos break; 193 1.1 cgd case 'g': 194 1.1 cgd gflag++; 195 1.1 cgd break; 196 1.1 cgd case 'u': 197 1.1 cgd uflag++; 198 1.1 cgd break; 199 1.29 christos case 'q': 200 1.29 christos qflag++; 201 1.29 christos break; 202 1.1 cgd case 'v': 203 1.1 cgd vflag++; 204 1.1 cgd break; 205 1.1 cgd case 'l': 206 1.1 cgd maxrun = atoi(optarg); 207 1.1 cgd break; 208 1.1 cgd default: 209 1.1 cgd usage(); 210 1.1 cgd } 211 1.1 cgd } 212 1.1 cgd argc -= optind; 213 1.1 cgd argv += optind; 214 1.31 christos if ((argc == 0 && !aflag) || (argc > 0 && aflag) || (!aflag && maxrun)) 215 1.1 cgd usage(); 216 1.1 cgd if (!gflag && !uflag) { 217 1.1 cgd gflag++; 218 1.1 cgd uflag++; 219 1.1 cgd } 220 1.20 abs 221 1.20 abs /* If -a, we do not want to pay the cost of processing every 222 1.20 abs * group and password entry if there are no filesystems with quotas 223 1.20 abs */ 224 1.20 abs if (aflag) { 225 1.20 abs i = 0; 226 1.20 abs while ((fs = getfsent()) != NULL) { 227 1.20 abs if (needchk(fs)) 228 1.43 christos i = 1; 229 1.20 abs } 230 1.20 abs endfsent(); 231 1.20 abs if (!i) /* No filesystems with quotas */ 232 1.43 christos return 0; 233 1.20 abs } 234 1.20 abs 235 1.1 cgd if (gflag) { 236 1.1 cgd setgrent(); 237 1.1 cgd while ((gr = getgrent()) != 0) 238 1.42 christos (void) addid((uint32_t)gr->gr_gid, GRPQUOTA, gr->gr_name); 239 1.1 cgd endgrent(); 240 1.1 cgd } 241 1.1 cgd if (uflag) { 242 1.1 cgd setpwent(); 243 1.1 cgd while ((pw = getpwent()) != 0) 244 1.42 christos (void) addid((uint32_t)pw->pw_uid, USRQUOTA, pw->pw_name); 245 1.1 cgd endpwent(); 246 1.1 cgd } 247 1.1 cgd if (aflag) 248 1.14 christos exit(checkfstab(flags, maxrun, needchk, chkquota)); 249 1.5 mycroft if (setfsent() == 0) 250 1.6 mycroft err(1, "%s: can't open", FSTAB); 251 1.1 cgd while ((fs = getfsent()) != NULL) { 252 1.45 christos const char *fsspec; 253 1.45 christos char buf[MAXPATHLEN]; 254 1.45 christos fsspec = getfsspecname(buf, sizeof(buf), fs->fs_spec); 255 1.45 christos if (fsspec == NULL) { 256 1.45 christos warn("%s", buf); 257 1.45 christos continue; 258 1.45 christos } 259 1.1 cgd if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 260 1.45 christos (argnum = oneof(fsspec, argv, argc)) >= 0) && 261 1.1 cgd (auxdata = needchk(fs)) && 262 1.45 christos (name = blockcheck(fsspec))) { 263 1.1 cgd done |= 1 << argnum; 264 1.13 christos errs += chkquota(fs->fs_type, name, fs->fs_file, 265 1.14 christos auxdata, NULL); 266 1.1 cgd } 267 1.1 cgd } 268 1.1 cgd endfsent(); 269 1.1 cgd for (i = 0; i < argc; i++) 270 1.1 cgd if ((done & (1 << i)) == 0) 271 1.49 christos warnx("%s not found in %s", argv[i], FSTAB); 272 1.43 christos return errs; 273 1.1 cgd } 274 1.1 cgd 275 1.13 christos static void 276 1.43 christos usage(void) 277 1.1 cgd { 278 1.43 christos const char *p = getprogname(); 279 1.43 christos (void)fprintf(stderr, "Usage: %s -a [-gquv] [-l <maxparallel>]\n" 280 1.43 christos "\t%s [-gquv] <filesys> ...\n", p, p); 281 1.1 cgd exit(1); 282 1.1 cgd } 283 1.1 cgd 284 1.13 christos static void * 285 1.43 christos needchk(struct fstab *fs) 286 1.1 cgd { 287 1.13 christos struct quotaname *qnp; 288 1.42 christos char qfnp[MAXPATHLEN]; 289 1.1 cgd 290 1.11 jtc if (strcmp(fs->fs_vfstype, "ffs") || 291 1.1 cgd strcmp(fs->fs_type, FSTAB_RW)) 292 1.5 mycroft return (NULL); 293 1.5 mycroft if ((qnp = malloc(sizeof(*qnp))) == NULL) 294 1.6 mycroft err(1, "%s", strerror(errno)); 295 1.1 cgd qnp->flags = 0; 296 1.42 christos if (gflag && hasquota(qfnp, sizeof(qfnp), fs, GRPQUOTA)) { 297 1.27 itojun strlcpy(qnp->grpqfname, qfnp, sizeof(qnp->grpqfname)); 298 1.1 cgd qnp->flags |= HASGRP; 299 1.1 cgd } 300 1.42 christos if (uflag && hasquota(qfnp, sizeof(qfnp), fs, USRQUOTA)) { 301 1.27 itojun strlcpy(qnp->usrqfname, qfnp, sizeof(qnp->usrqfname)); 302 1.1 cgd qnp->flags |= HASUSR; 303 1.1 cgd } 304 1.1 cgd if (qnp->flags) 305 1.5 mycroft return (qnp); 306 1.5 mycroft free(qnp); 307 1.5 mycroft return (NULL); 308 1.1 cgd } 309 1.1 cgd 310 1.43 christos static off_t sblock_try[] = SBLOCKSEARCH; 311 1.25 fvdl 312 1.1 cgd /* 313 1.1 cgd * Scan the specified filesystem to check quota(s) present on it. 314 1.1 cgd */ 315 1.13 christos static int 316 1.43 christos chkquota(const char *type, const char *fsname, const char *mntpt, void *v, 317 1.43 christos pid_t *pid) 318 1.13 christos { 319 1.13 christos struct quotaname *qnp = v; 320 1.13 christos struct fileusage *fup; 321 1.36 christos union comb_dinode *dp; 322 1.51 chs int i, mode, errs = 0, inosused; 323 1.51 chs uint32_t cg; 324 1.1 cgd ino_t ino; 325 1.25 fvdl struct cg *cgp; 326 1.29 christos char msgbuf[4096]; 327 1.1 cgd 328 1.14 christos if (pid != NULL) { 329 1.29 christos fflush(stdout); 330 1.14 christos switch ((*pid = fork())) { 331 1.14 christos default: 332 1.29 christos return 0; 333 1.29 christos case 0: 334 1.14 christos break; 335 1.14 christos case -1: 336 1.14 christos err(1, "Cannot fork"); 337 1.14 christos } 338 1.29 christos setvbuf(stdout, msgbuf, _IOFBF, sizeof msgbuf); 339 1.14 christos } 340 1.14 christos 341 1.5 mycroft if ((fi = open(fsname, O_RDONLY, 0)) < 0) { 342 1.14 christos warn("Cannot open %s", fsname); 343 1.29 christos if (pid != NULL) 344 1.29 christos exit(1); 345 1.25 fvdl return 1; 346 1.1 cgd } 347 1.1 cgd if (vflag) { 348 1.5 mycroft (void)printf("*** Checking "); 349 1.1 cgd if (qnp->flags & HASUSR) 350 1.5 mycroft (void)printf("%s%s", qfextension[USRQUOTA], 351 1.1 cgd (qnp->flags & HASGRP) ? " and " : ""); 352 1.1 cgd if (qnp->flags & HASGRP) 353 1.5 mycroft (void)printf("%s", qfextension[GRPQUOTA]); 354 1.5 mycroft (void)printf(" quotas for %s (%s)\n", fsname, mntpt); 355 1.29 christos fflush(stdout); 356 1.1 cgd } 357 1.23 bouyer signal(SIGINFO, infohandler); 358 1.1 cgd sync(); 359 1.6 mycroft dev_bsize = 1; 360 1.25 fvdl 361 1.34 dsl for (i = 0; ; i++) { 362 1.34 dsl if (sblock_try[i] == -1) { 363 1.34 dsl warnx("%s: superblock not found", fsname); 364 1.34 dsl if (pid != NULL) 365 1.34 dsl exit(1); 366 1.34 dsl return 1; 367 1.34 dsl } 368 1.25 fvdl bread(sblock_try[i], (char *)&sblock, SBLOCKSIZE); 369 1.25 fvdl switch (sblock.fs_magic) { 370 1.36 christos #ifdef HAVE_UFSv2 371 1.50 chs case FS_UFS2EA_MAGIC: 372 1.50 chs sblock.fs_magic = FS_UFS2_MAGIC; 373 1.50 chs /*FALLTHROUGH*/ 374 1.25 fvdl case FS_UFS2_MAGIC: 375 1.25 fvdl is_ufs2 = 1; 376 1.25 fvdl /*FALLTHROUGH*/ 377 1.36 christos #endif 378 1.25 fvdl case FS_UFS1_MAGIC: 379 1.34 dsl break; 380 1.36 christos #ifdef HAVE_UFSv2 381 1.50 chs case FS_UFS2EA_MAGIC_SWAPPED: 382 1.50 chs sblock.fs_magic = FS_UFS2_MAGIC_SWAPPED; 383 1.50 chs /*FALLTHROUGH*/ 384 1.25 fvdl case FS_UFS2_MAGIC_SWAPPED: 385 1.25 fvdl is_ufs2 = 1; 386 1.25 fvdl /*FALLTHROUGH*/ 387 1.36 christos #endif 388 1.25 fvdl case FS_UFS1_MAGIC_SWAPPED: 389 1.17 bouyer needswap = 1; 390 1.34 dsl ffs_sb_swap(&sblock, &sblock); 391 1.34 dsl break; 392 1.25 fvdl default: 393 1.25 fvdl continue; 394 1.25 fvdl } 395 1.34 dsl 396 1.36 christos #ifdef HAVE_UFSv2 397 1.35 dsl if (is_ufs2 || sblock.fs_old_flags & FS_FLAGS_UPDATED) { 398 1.34 dsl if (sblock.fs_sblockloc != sblock_try[i]) 399 1.34 dsl continue; 400 1.34 dsl } else { 401 1.34 dsl if (sblock_try[i] == SBLOCK_UFS2) 402 1.34 dsl continue; 403 1.34 dsl } 404 1.36 christos #endif 405 1.34 dsl break; 406 1.19 ross } 407 1.34 dsl 408 1.38 christos cgp = malloc(sblock.fs_cgsize); 409 1.38 christos if (cgp == NULL) { 410 1.38 christos warn("%s: can't allocate %d bytes of cg space", fsname, 411 1.38 christos sblock.fs_cgsize); 412 1.38 christos if (pid != NULL) 413 1.38 christos exit(1); 414 1.38 christos return 1; 415 1.38 christos } 416 1.38 christos 417 1.47 dholland dev_bsize = sblock.fs_fsize / FFS_FSBTODB(&sblock, 1); 418 1.1 cgd maxino = sblock.fs_ncg * sblock.fs_ipg; 419 1.38 christos for (cg = 0; cg < sblock.fs_ncg; cg++) { 420 1.38 christos ino = cg * sblock.fs_ipg; 421 1.38 christos setinodebuf(ino); 422 1.36 christos #ifdef HAVE_UFSv2 423 1.25 fvdl if (sblock.fs_magic == FS_UFS2_MAGIC) { 424 1.47 dholland bread(FFS_FSBTODB(&sblock, cgtod(&sblock, cg)), (char *)cgp, 425 1.25 fvdl sblock.fs_cgsize); 426 1.25 fvdl if (needswap) 427 1.25 fvdl ffs_cg_swap(cgp, cgp, &sblock); 428 1.25 fvdl inosused = cgp->cg_initediblk; 429 1.25 fvdl } else 430 1.36 christos #endif 431 1.25 fvdl inosused = sblock.fs_ipg; 432 1.25 fvdl for (i = 0; i < inosused; i++, ino++) { 433 1.23 bouyer if (got_siginfo) { 434 1.23 bouyer fprintf(stderr, 435 1.23 bouyer "%s: cyl group %d of %d (%d%%)\n", 436 1.23 bouyer fsname, cg, sblock.fs_ncg, 437 1.23 bouyer cg * 100 / sblock.fs_ncg); 438 1.23 bouyer got_siginfo = 0; 439 1.23 bouyer } 440 1.46 dholland if (ino < UFS_ROOTINO) 441 1.1 cgd continue; 442 1.1 cgd if ((dp = getnextinode(ino)) == NULL) 443 1.1 cgd continue; 444 1.25 fvdl if ((mode = DIP(dp, mode) & IFMT) == 0) 445 1.1 cgd continue; 446 1.1 cgd if (qnp->flags & HASGRP) { 447 1.36 christos fup = addid(DIP(dp, gid), GRPQUOTA, 448 1.1 cgd (char *)0); 449 1.1 cgd fup->fu_curinodes++; 450 1.1 cgd if (mode == IFREG || mode == IFDIR || 451 1.1 cgd mode == IFLNK) 452 1.25 fvdl fup->fu_curblocks += DIP(dp, blocks); 453 1.1 cgd } 454 1.1 cgd if (qnp->flags & HASUSR) { 455 1.36 christos fup = addid(DIP(dp, uid), USRQUOTA, 456 1.1 cgd (char *)0); 457 1.1 cgd fup->fu_curinodes++; 458 1.1 cgd if (mode == IFREG || mode == IFDIR || 459 1.1 cgd mode == IFLNK) 460 1.25 fvdl fup->fu_curblocks += DIP(dp, blocks); 461 1.1 cgd } 462 1.1 cgd } 463 1.1 cgd } 464 1.1 cgd freeinodebuf(); 465 1.25 fvdl free(cgp); 466 1.1 cgd if (qnp->flags & HASUSR) 467 1.1 cgd errs += update(mntpt, qnp->usrqfname, USRQUOTA); 468 1.1 cgd if (qnp->flags & HASGRP) 469 1.1 cgd errs += update(mntpt, qnp->grpqfname, GRPQUOTA); 470 1.1 cgd close(fi); 471 1.29 christos if (pid != NULL) 472 1.29 christos exit(errs); 473 1.25 fvdl return errs; 474 1.1 cgd } 475 1.1 cgd 476 1.1 cgd /* 477 1.1 cgd * Update a specified quota file. 478 1.1 cgd */ 479 1.13 christos static int 480 1.43 christos update(const char *fsname, const char *quotafile, int type) 481 1.1 cgd { 482 1.13 christos struct fileusage *fup; 483 1.13 christos FILE *qfi, *qfo; 484 1.42 christos uint32_t id, lastid, nextid; 485 1.29 christos int need_seek; 486 1.1 cgd struct dqblk dqbuf; 487 1.1 cgd static struct dqblk zerodqbuf; 488 1.1 cgd static struct fileusage zerofileusage; 489 1.41 bouyer struct statvfs *fst; 490 1.41 bouyer int nfst, i; 491 1.41 bouyer 492 1.41 bouyer nfst = getmntinfo(&fst, MNT_WAIT); 493 1.41 bouyer if (nfst == 0) 494 1.41 bouyer errx(1, "no filesystems mounted!"); 495 1.41 bouyer 496 1.41 bouyer for (i = 0; i < nfst; i++) { 497 1.41 bouyer if (strncmp(fst[i].f_fstypename, "ffs", 498 1.41 bouyer sizeof(fst[i].f_fstypename)) == 0 && 499 1.41 bouyer strncmp(fst[i].f_mntonname, fsname, 500 1.41 bouyer sizeof(fst[i].f_mntonname)) == 0 && 501 1.41 bouyer (fst[i].f_flag & ST_QUOTA) != 0) { 502 1.41 bouyer warnx("filesystem %s has quotas already turned on", 503 1.41 bouyer fsname); 504 1.41 bouyer } 505 1.41 bouyer } 506 1.1 cgd 507 1.1 cgd if ((qfo = fopen(quotafile, "r+")) == NULL) { 508 1.1 cgd if (errno == ENOENT) 509 1.1 cgd qfo = fopen(quotafile, "w+"); 510 1.1 cgd if (qfo) { 511 1.1 cgd (void) fprintf(stderr, 512 1.1 cgd "quotacheck: creating quota file %s\n", quotafile); 513 1.1 cgd #define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 514 1.1 cgd (void) fchown(fileno(qfo), getuid(), getquotagid()); 515 1.1 cgd (void) fchmod(fileno(qfo), MODE); 516 1.1 cgd } else { 517 1.1 cgd (void) fprintf(stderr, 518 1.1 cgd "quotacheck: %s: %s\n", quotafile, strerror(errno)); 519 1.1 cgd return (1); 520 1.1 cgd } 521 1.1 cgd } 522 1.1 cgd if ((qfi = fopen(quotafile, "r")) == NULL) { 523 1.1 cgd (void) fprintf(stderr, 524 1.1 cgd "quotacheck: %s: %s\n", quotafile, strerror(errno)); 525 1.1 cgd (void) fclose(qfo); 526 1.1 cgd return (1); 527 1.1 cgd } 528 1.29 christos need_seek = 1; 529 1.29 christos for (lastid = highid[type], id = 0; id <= lastid; id = nextid) { 530 1.1 cgd if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 531 1.1 cgd dqbuf = zerodqbuf; 532 1.1 cgd if ((fup = lookup(id, type)) == 0) 533 1.1 cgd fup = &zerofileusage; 534 1.29 christos 535 1.29 christos nextid = subsequent(id, type); 536 1.43 christos /* watch out for id == UINT32_MAX */ 537 1.43 christos if (nextid > 0 && nextid != id + 1) 538 1.29 christos nextid = skipforward(id, nextid, qfi); 539 1.29 christos 540 1.29 christos if (got_siginfo) { 541 1.43 christos /* 542 1.43 christos * XXX this could try to show percentage through 543 1.43 christos * the ID list 544 1.43 christos */ 545 1.43 christos fprintf(stderr, "%s: updating %s quotas for id=%" 546 1.43 christos PRIu32 " (%s)\n", fsname, 547 1.29 christos qfextension[type < MAXQUOTAS ? type : MAXQUOTAS], 548 1.29 christos id, fup->fu_name); 549 1.29 christos got_siginfo = 0; 550 1.29 christos } 551 1.1 cgd if (dqbuf.dqb_curinodes == fup->fu_curinodes && 552 1.1 cgd dqbuf.dqb_curblocks == fup->fu_curblocks) { 553 1.29 christos fup->fu_curinodes = 0; /* reset usage */ 554 1.29 christos fup->fu_curblocks = 0; /* for next filesystem */ 555 1.29 christos 556 1.29 christos need_seek = 1; 557 1.43 christos /* infinite loop avoidance (OR do as "nextid < id"?) */ 558 1.43 christos if (id == UINT32_MAX || nextid == 0) { 559 1.29 christos break; 560 1.36 christos } 561 1.1 cgd continue; 562 1.1 cgd } 563 1.1 cgd if (vflag) { 564 1.1 cgd if (aflag) 565 1.1 cgd printf("%s: ", fsname); 566 1.1 cgd printf("%-8s fixed:", fup->fu_name); 567 1.1 cgd if (dqbuf.dqb_curinodes != fup->fu_curinodes) 568 1.13 christos (void)printf("\tinodes %d -> %ld", 569 1.1 cgd dqbuf.dqb_curinodes, fup->fu_curinodes); 570 1.1 cgd if (dqbuf.dqb_curblocks != fup->fu_curblocks) 571 1.13 christos (void)printf("\tblocks %d -> %ld", 572 1.1 cgd dqbuf.dqb_curblocks, fup->fu_curblocks); 573 1.5 mycroft (void)printf("\n"); 574 1.1 cgd } 575 1.1 cgd /* 576 1.1 cgd * Reset time limit if have a soft limit and were 577 1.1 cgd * previously under it, but are now over it. 578 1.1 cgd */ 579 1.1 cgd if (dqbuf.dqb_bsoftlimit && 580 1.1 cgd dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 581 1.1 cgd fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) 582 1.1 cgd dqbuf.dqb_btime = 0; 583 1.1 cgd if (dqbuf.dqb_isoftlimit && 584 1.40 christos dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit && 585 1.40 christos fup->fu_curinodes >= dqbuf.dqb_isoftlimit) 586 1.1 cgd dqbuf.dqb_itime = 0; 587 1.1 cgd dqbuf.dqb_curinodes = fup->fu_curinodes; 588 1.1 cgd dqbuf.dqb_curblocks = fup->fu_curblocks; 589 1.29 christos 590 1.29 christos if (need_seek) { 591 1.29 christos (void) fseeko(qfo, (off_t)id * sizeof(struct dqblk), 592 1.29 christos SEEK_SET); 593 1.29 christos need_seek = nextid != id + 1; 594 1.29 christos } 595 1.43 christos (void) fwrite(&dqbuf, sizeof(struct dqblk), 1, qfo); 596 1.29 christos 597 1.1 cgd fup->fu_curinodes = 0; 598 1.1 cgd fup->fu_curblocks = 0; 599 1.43 christos /* infinite loop avoidance (OR do as "nextid < id"?) */ 600 1.43 christos if (id == UINT32_MAX || nextid == 0) { 601 1.29 christos break; 602 1.36 christos } 603 1.1 cgd } 604 1.43 christos (void)fclose(qfi); 605 1.43 christos (void)fflush(qfo); 606 1.36 christos if (highid[type] != UINT32_MAX) 607 1.43 christos (void)ftruncate(fileno(qfo), 608 1.29 christos (off_t)((highid[type] + 1) * sizeof(struct dqblk))); 609 1.43 christos (void)fclose(qfo); 610 1.43 christos return 0; 611 1.1 cgd } 612 1.1 cgd 613 1.43 christos static uint32_t 614 1.43 christos skipforward(uint32_t cur, uint32_t to, FILE *qfi) 615 1.29 christos { 616 1.29 christos struct dqblk dqbuf; 617 1.29 christos 618 1.29 christos if (qflag) { 619 1.43 christos (void)fseeko(qfi, (off_t)to * sizeof(struct dqblk), SEEK_SET); 620 1.43 christos return to; 621 1.29 christos } 622 1.29 christos 623 1.29 christos while (++cur < to) { 624 1.29 christos /* 625 1.29 christos * if EOF occurs, nothing left to read, we're done 626 1.29 christos */ 627 1.29 christos if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 628 1.29 christos return (to); 629 1.29 christos 630 1.29 christos /* 631 1.29 christos * If we find an entry that shows usage, before the next 632 1.29 christos * id that has actual usage, we have to stop here, so the 633 1.29 christos * incorrect entry can be corrected in the file 634 1.29 christos */ 635 1.29 christos if (dqbuf.dqb_curinodes != 0 || dqbuf.dqb_curblocks != 0) { 636 1.29 christos (void)fseek(qfi, -(long)sizeof(struct dqblk), SEEK_CUR); 637 1.43 christos return cur; 638 1.29 christos } 639 1.29 christos } 640 1.43 christos return to; 641 1.29 christos } 642 1.29 christos 643 1.1 cgd /* 644 1.1 cgd * Determine the group identifier for quota files. 645 1.1 cgd */ 646 1.13 christos static int 647 1.42 christos getquotagid(void) 648 1.1 cgd { 649 1.1 cgd struct group *gr; 650 1.1 cgd 651 1.13 christos if ((gr = getgrnam(quotagroup)) != NULL) 652 1.42 christos return gr->gr_gid; 653 1.42 christos return -1; 654 1.1 cgd } 655 1.1 cgd 656 1.1 cgd /* 657 1.1 cgd * Routines to manage the file usage table. 658 1.1 cgd * 659 1.1 cgd * Lookup an id of a specific type. 660 1.1 cgd */ 661 1.13 christos static struct fileusage * 662 1.42 christos lookup(uint32_t id, int type) 663 1.1 cgd { 664 1.13 christos struct fileusage *fup; 665 1.1 cgd 666 1.1 cgd for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 667 1.1 cgd if (fup->fu_id == id) 668 1.42 christos return fup; 669 1.42 christos return NULL; 670 1.1 cgd } 671 1.1 cgd 672 1.1 cgd /* 673 1.1 cgd * Add a new file usage id if it does not already exist. 674 1.1 cgd */ 675 1.13 christos static struct fileusage * 676 1.42 christos addid(uint32_t id, int type, const char *name) 677 1.1 cgd { 678 1.1 cgd struct fileusage *fup, **fhp; 679 1.42 christos size_t len; 680 1.1 cgd 681 1.13 christos if ((fup = lookup(id, type)) != NULL) 682 1.42 christos return fup; 683 1.1 cgd if (name) 684 1.1 cgd len = strlen(name); 685 1.1 cgd else 686 1.1 cgd len = 10; 687 1.5 mycroft if ((fup = calloc(1, sizeof(*fup) + len)) == NULL) 688 1.6 mycroft err(1, "%s", strerror(errno)); 689 1.1 cgd fhp = &fuhead[type][id & (FUHASH - 1)]; 690 1.1 cgd fup->fu_next = *fhp; 691 1.1 cgd *fhp = fup; 692 1.1 cgd fup->fu_id = id; 693 1.1 cgd if (id > highid[type]) 694 1.1 cgd highid[type] = id; 695 1.5 mycroft if (name) 696 1.15 lukem memmove(fup->fu_name, name, len + 1); 697 1.5 mycroft else 698 1.42 christos (void)snprintf(fup->fu_name, len + 1, "%" PRIu32, id); 699 1.42 christos return fup; 700 1.1 cgd } 701 1.1 cgd 702 1.42 christos static uint32_t 703 1.42 christos subsequent(uint32_t id, int type) 704 1.29 christos { 705 1.29 christos struct fileusage *fup, **iup, **cup; 706 1.42 christos uint32_t next, offset; 707 1.29 christos 708 1.29 christos next = highid[type] + 1; 709 1.29 christos offset = 0; 710 1.29 christos cup = iup = &fuhead[type][id & (FUHASH-1)]; 711 1.29 christos do { 712 1.29 christos ++offset; 713 1.29 christos if (++cup >= &fuhead[type][FUHASH]) 714 1.29 christos cup = &fuhead[type][0]; 715 1.29 christos for (fup = *cup; fup != 0; fup = fup->fu_next) { 716 1.29 christos if (fup->fu_id > id && fup->fu_id <= id + offset) 717 1.29 christos return (fup->fu_id); 718 1.29 christos if (fup->fu_id > id && fup->fu_id < next) 719 1.29 christos next = fup->fu_id; 720 1.29 christos } 721 1.29 christos } while (cup != iup); 722 1.29 christos 723 1.29 christos return next; 724 1.29 christos } 725 1.29 christos 726 1.1 cgd /* 727 1.25 fvdl * Special purpose version of ginode used to optimize first pass 728 1.1 cgd * over all the inodes in numerical order. 729 1.1 cgd */ 730 1.25 fvdl static ino_t nextino, lastinum, lastvalidinum; 731 1.13 christos static long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 732 1.36 christos static union comb_dinode *inodebuf; 733 1.25 fvdl #define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ 734 1.1 cgd 735 1.42 christos static union comb_dinode * 736 1.42 christos getnextinode(ino_t inumber) 737 1.1 cgd { 738 1.1 cgd long size; 739 1.1 cgd daddr_t dblk; 740 1.36 christos static union comb_dinode *dp; 741 1.36 christos union comb_dinode *ret; 742 1.25 fvdl 743 1.25 fvdl if (inumber != nextino++ || inumber > lastvalidinum) { 744 1.37 christos errx(1, "bad inode number %llu to nextinode", 745 1.37 christos (unsigned long long)inumber); 746 1.25 fvdl } 747 1.1 cgd 748 1.1 cgd if (inumber >= lastinum) { 749 1.1 cgd readcnt++; 750 1.47 dholland dblk = FFS_FSBTODB(&sblock, ino_to_fsba(&sblock, lastinum)); 751 1.1 cgd if (readcnt % readpercg == 0) { 752 1.1 cgd size = partialsize; 753 1.1 cgd lastinum += partialcnt; 754 1.1 cgd } else { 755 1.1 cgd size = inobufsize; 756 1.1 cgd lastinum += fullcnt; 757 1.1 cgd } 758 1.25 fvdl (void)bread(dblk, (caddr_t)inodebuf, size); 759 1.25 fvdl if (needswap) { 760 1.36 christos #ifdef HAVE_UFSv2 761 1.25 fvdl if (is_ufs2) 762 1.25 fvdl swap_dinode2(inodebuf, lastinum - inumber); 763 1.25 fvdl else 764 1.36 christos #endif 765 1.25 fvdl swap_dinode1(inodebuf, lastinum - inumber); 766 1.25 fvdl } 767 1.36 christos dp = (union comb_dinode *)inodebuf; 768 1.1 cgd } 769 1.25 fvdl ret = dp; 770 1.36 christos dp = (union comb_dinode *) 771 1.25 fvdl ((char *)dp + (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE)); 772 1.25 fvdl return ret; 773 1.1 cgd } 774 1.1 cgd 775 1.42 christos static void 776 1.42 christos setinodebuf(ino_t inum) 777 1.1 cgd { 778 1.1 cgd 779 1.25 fvdl if (inum % sblock.fs_ipg != 0) 780 1.37 christos errx(1, "bad inode number %llu to setinodebuf", 781 1.37 christos (unsigned long long)inum); 782 1.25 fvdl 783 1.25 fvdl lastvalidinum = inum + sblock.fs_ipg - 1; 784 1.25 fvdl nextino = inum; 785 1.25 fvdl lastinum = inum; 786 1.1 cgd readcnt = 0; 787 1.25 fvdl if (inodebuf != NULL) 788 1.25 fvdl return; 789 1.48 dholland inobufsize = ffs_blkroundup(&sblock, INOBUFSIZE); 790 1.25 fvdl fullcnt = inobufsize / (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE); 791 1.1 cgd readpercg = sblock.fs_ipg / fullcnt; 792 1.1 cgd partialcnt = sblock.fs_ipg % fullcnt; 793 1.25 fvdl partialsize = partialcnt * (is_ufs2 ? DINODE2_SIZE : DINODE1_SIZE); 794 1.1 cgd if (partialcnt != 0) { 795 1.1 cgd readpercg++; 796 1.1 cgd } else { 797 1.1 cgd partialcnt = fullcnt; 798 1.1 cgd partialsize = inobufsize; 799 1.1 cgd } 800 1.1 cgd if (inodebuf == NULL && 801 1.25 fvdl (inodebuf = malloc((unsigned)inobufsize)) == NULL) 802 1.25 fvdl errx(1, "Cannot allocate space for inode buffer"); 803 1.46 dholland while (nextino < UFS_ROOTINO) 804 1.26 fvdl getnextinode(nextino); 805 1.1 cgd } 806 1.1 cgd 807 1.42 christos static void 808 1.42 christos freeinodebuf(void) 809 1.1 cgd { 810 1.1 cgd 811 1.42 christos free(inodebuf); 812 1.1 cgd inodebuf = NULL; 813 1.25 fvdl } 814 1.25 fvdl 815 1.25 fvdl 816 1.36 christos #ifdef HAVE_UFSv2 817 1.25 fvdl static void 818 1.36 christos swap_dinode1(union comb_dinode *dp, int n) 819 1.25 fvdl { 820 1.25 fvdl int i; 821 1.25 fvdl struct ufs1_dinode *dp1; 822 1.25 fvdl 823 1.25 fvdl dp1 = (struct ufs1_dinode *)&dp->dp1; 824 1.25 fvdl for (i = 0; i < n; i++, dp1++) 825 1.25 fvdl ffs_dinode1_swap(dp1, dp1); 826 1.25 fvdl } 827 1.25 fvdl 828 1.25 fvdl static void 829 1.36 christos swap_dinode2(union comb_dinode *dp, int n) 830 1.25 fvdl { 831 1.25 fvdl int i; 832 1.25 fvdl struct ufs2_dinode *dp2; 833 1.25 fvdl 834 1.25 fvdl dp2 = (struct ufs2_dinode *)&dp->dp2; 835 1.25 fvdl for (i = 0; i < n; i++, dp2++) 836 1.25 fvdl ffs_dinode2_swap(dp2, dp2); 837 1.1 cgd } 838 1.1 cgd 839 1.36 christos #else 840 1.36 christos 841 1.36 christos static void 842 1.36 christos swap_dinode1(union comb_dinode *dp, int n) 843 1.36 christos { 844 1.36 christos int i; 845 1.36 christos struct dinode *dp1; 846 1.36 christos 847 1.36 christos dp1 = (struct dinode *) &dp->dp1; 848 1.36 christos for (i = 0; i < n; i++, dp1++) 849 1.36 christos ffs_dinode_swap(dp1, dp1); 850 1.36 christos } 851 1.36 christos 852 1.36 christos #endif 853 1.36 christos 854 1.1 cgd /* 855 1.1 cgd * Read specified disk blocks. 856 1.1 cgd */ 857 1.13 christos static void 858 1.42 christos bread(daddr_t bno, char *buf, long cnt) 859 1.1 cgd { 860 1.1 cgd 861 1.5 mycroft if (lseek(fi, (off_t)bno * dev_bsize, SEEK_SET) < 0 || 862 1.5 mycroft read(fi, buf, cnt) != cnt) 863 1.24 fvdl err(1, "block %lld", (long long)bno); 864 1.1 cgd } 865 1.23 bouyer 866 1.42 christos static void 867 1.23 bouyer infohandler(int sig) 868 1.23 bouyer { 869 1.23 bouyer got_siginfo = 1; 870 1.23 bouyer } 871