1 /* 2 * Copyright (c) 1980, 1990 Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Robert Elz at The University of Melbourne. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 char copyright[] = 39 "@(#) Copyright (c) 1980, 1990 Regents of the University of California.\n\ 40 All rights reserved.\n"; 41 #endif /* not lint */ 42 43 #ifndef lint 44 static char sccsid[] = "@(#)quotacheck.c 5.16 (Berkeley) 2/28/91"; 45 #endif /* not lint */ 46 47 /* 48 * Fix up / report on disk quotas & usage 49 */ 50 #include <sys/param.h> 51 #include <sys/stat.h> 52 #include <ufs/dinode.h> 53 #include <ufs/fs.h> 54 #include <ufs/quota.h> 55 #include <fcntl.h> 56 #include <fstab.h> 57 #include <pwd.h> 58 #include <grp.h> 59 #include <errno.h> 60 #include <unistd.h> 61 #include <stdio.h> 62 #include <stdlib.h> 63 #include <string.h> 64 65 char *qfname = QUOTAFILENAME; 66 char *qfextension[] = INITQFNAMES; 67 char *quotagroup = QUOTAGROUP; 68 69 union { 70 struct fs sblk; 71 char dummy[MAXBSIZE]; 72 } un; 73 #define sblock un.sblk 74 long dev_bsize; 75 long maxino; 76 77 struct quotaname { 78 long flags; 79 char grpqfname[MAXPATHLEN + 1]; 80 char usrqfname[MAXPATHLEN + 1]; 81 }; 82 #define HASUSR 1 83 #define HASGRP 2 84 85 struct fileusage { 86 struct fileusage *fu_next; 87 u_long fu_curinodes; 88 u_long fu_curblocks; 89 u_long fu_id; 90 char fu_name[1]; 91 /* actually bigger */ 92 }; 93 #define FUHASH 1024 /* must be power of two */ 94 struct fileusage *fuhead[MAXQUOTAS][FUHASH]; 95 struct fileusage *lookup(); 96 struct fileusage *addid(); 97 struct dinode *getnextinode(); 98 99 int aflag; /* all file systems */ 100 int gflag; /* check group quotas */ 101 int uflag; /* check user quotas */ 102 int vflag; /* verbose */ 103 int fi; /* open disk file descriptor */ 104 u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */ 105 106 main(argc, argv) 107 int argc; 108 char **argv; 109 { 110 register struct fstab *fs; 111 register struct passwd *pw; 112 register struct group *gr; 113 int i, argnum, maxrun = 0, errs = 0; 114 long auxdata, done = 0; 115 char ch, *name, *blockcheck(); 116 int needchk(), chkquota(); 117 extern char *optarg; 118 extern int optind; 119 120 while ((ch = getopt(argc, argv, "aguvl:")) != EOF) { 121 switch(ch) { 122 case 'a': 123 aflag++; 124 break; 125 case 'g': 126 gflag++; 127 break; 128 case 'u': 129 uflag++; 130 break; 131 case 'v': 132 vflag++; 133 break; 134 case 'l': 135 maxrun = atoi(optarg); 136 break; 137 default: 138 usage(); 139 } 140 } 141 argc -= optind; 142 argv += optind; 143 if ((argc == 0 && !aflag) || (argc > 0 && aflag)) 144 usage(); 145 if (!gflag && !uflag) { 146 gflag++; 147 uflag++; 148 } 149 if (gflag) { 150 setgrent(); 151 while ((gr = getgrent()) != 0) 152 (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name); 153 endgrent(); 154 } 155 if (uflag) { 156 setpwent(); 157 while ((pw = getpwent()) != 0) 158 (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name); 159 endpwent(); 160 } 161 if (aflag) 162 exit(checkfstab(1, maxrun, needchk, chkquota)); 163 if (setfsent() == 0) { 164 fprintf(stderr, "Can't open "); 165 perror(FSTAB); 166 exit(8); 167 } 168 while ((fs = getfsent()) != NULL) { 169 if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 || 170 (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) && 171 (auxdata = needchk(fs)) && 172 (name = blockcheck(fs->fs_spec))) { 173 done |= 1 << argnum; 174 errs += chkquota(name, fs->fs_file, auxdata); 175 } 176 } 177 endfsent(); 178 for (i = 0; i < argc; i++) 179 if ((done & (1 << i)) == 0) 180 fprintf(stderr, "%s not found in %s\n", 181 argv[i], FSTAB); 182 exit(errs); 183 } 184 185 usage() 186 { 187 (void) fprintf(stderr, "usage:\t%s\n\t%s\n", 188 "quotacheck -a [-guv]", 189 "quotacheck [-guv] filesys ..."); 190 exit(1); 191 } 192 193 needchk(fs) 194 register struct fstab *fs; 195 { 196 register struct quotaname *qnp; 197 char *qfnp; 198 199 if (strcmp(fs->fs_vfstype, "ufs") || 200 strcmp(fs->fs_type, FSTAB_RW)) 201 return (0); 202 if ((qnp = (struct quotaname *)malloc(sizeof *qnp)) == 0) { 203 fprintf(stderr, "out of memory for quota structures\n"); 204 exit(1); 205 } 206 qnp->flags = 0; 207 if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) { 208 strcpy(qnp->grpqfname, qfnp); 209 qnp->flags |= HASGRP; 210 } 211 if (uflag && hasquota(fs, USRQUOTA, &qfnp)) { 212 strcpy(qnp->usrqfname, qfnp); 213 qnp->flags |= HASUSR; 214 } 215 if (qnp->flags) 216 return ((int)qnp); 217 free((char *)qnp); 218 return (0); 219 } 220 221 /* 222 * Scan the specified filesystem to check quota(s) present on it. 223 */ 224 chkquota(fsname, mntpt, qnp) 225 char *fsname, *mntpt; 226 register struct quotaname *qnp; 227 { 228 register struct fileusage *fup; 229 register struct dinode *dp; 230 int cg, i, mode, errs = 0; 231 ino_t ino; 232 233 if ((fi = open(fsname, 0)) < 0) { 234 perror(fsname); 235 return (1); 236 } 237 if (vflag) { 238 fprintf(stdout, "*** Checking "); 239 if (qnp->flags & HASUSR) 240 fprintf(stdout, "%s%s", qfextension[USRQUOTA], 241 (qnp->flags & HASGRP) ? " and " : ""); 242 if (qnp->flags & HASGRP) 243 fprintf(stdout, "%s", qfextension[GRPQUOTA]); 244 fprintf(stdout, " quotas for %s (%s)\n", fsname, mntpt); 245 } 246 sync(); 247 dev_bsize = 1; 248 bread(SBOFF, (char *)&sblock, (long)SBSIZE); 249 dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1); 250 maxino = sblock.fs_ncg * sblock.fs_ipg; 251 resetinodebuf(); 252 for (ino = 0, cg = 0; cg < sblock.fs_ncg; cg++) { 253 for (i = 0; i < sblock.fs_ipg; i++, ino++) { 254 if (ino < ROOTINO) 255 continue; 256 if ((dp = getnextinode(ino)) == NULL) 257 continue; 258 if ((mode = dp->di_mode & IFMT) == 0) 259 continue; 260 if (qnp->flags & HASGRP) { 261 fup = addid((u_long)dp->di_gid, GRPQUOTA, 262 (char *)0); 263 fup->fu_curinodes++; 264 if (mode == IFREG || mode == IFDIR || 265 mode == IFLNK) 266 fup->fu_curblocks += dp->di_blocks; 267 } 268 if (qnp->flags & HASUSR) { 269 fup = addid((u_long)dp->di_uid, USRQUOTA, 270 (char *)0); 271 fup->fu_curinodes++; 272 if (mode == IFREG || mode == IFDIR || 273 mode == IFLNK) 274 fup->fu_curblocks += dp->di_blocks; 275 } 276 } 277 } 278 freeinodebuf(); 279 if (qnp->flags & HASUSR) 280 errs += update(mntpt, qnp->usrqfname, USRQUOTA); 281 if (qnp->flags & HASGRP) 282 errs += update(mntpt, qnp->grpqfname, GRPQUOTA); 283 close(fi); 284 return (errs); 285 } 286 287 /* 288 * Update a specified quota file. 289 */ 290 update(fsname, quotafile, type) 291 char *fsname, *quotafile; 292 register int type; 293 { 294 register struct fileusage *fup; 295 register FILE *qfi, *qfo; 296 register u_long id, lastid; 297 struct dqblk dqbuf; 298 static int warned = 0; 299 static struct dqblk zerodqbuf; 300 static struct fileusage zerofileusage; 301 302 if ((qfo = fopen(quotafile, "r+")) == NULL) { 303 if (errno == ENOENT) 304 qfo = fopen(quotafile, "w+"); 305 if (qfo) { 306 (void) fprintf(stderr, 307 "quotacheck: creating quota file %s\n", quotafile); 308 #define MODE (S_IRUSR|S_IWUSR|S_IRGRP) 309 (void) fchown(fileno(qfo), getuid(), getquotagid()); 310 (void) fchmod(fileno(qfo), MODE); 311 } else { 312 (void) fprintf(stderr, 313 "quotacheck: %s: %s\n", quotafile, strerror(errno)); 314 return (1); 315 } 316 } 317 if ((qfi = fopen(quotafile, "r")) == NULL) { 318 (void) fprintf(stderr, 319 "quotacheck: %s: %s\n", quotafile, strerror(errno)); 320 (void) fclose(qfo); 321 return (1); 322 } 323 if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 && 324 errno == EOPNOTSUPP && !warned && vflag) { 325 warned++; 326 fprintf(stdout, "*** Warning: %s\n", 327 "Quotas are not compiled into this kernel"); 328 } 329 for (lastid = highid[type], id = 0; id <= lastid; id++) { 330 if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0) 331 dqbuf = zerodqbuf; 332 if ((fup = lookup(id, type)) == 0) 333 fup = &zerofileusage; 334 if (dqbuf.dqb_curinodes == fup->fu_curinodes && 335 dqbuf.dqb_curblocks == fup->fu_curblocks) { 336 fup->fu_curinodes = 0; 337 fup->fu_curblocks = 0; 338 fseek(qfo, (long)sizeof(struct dqblk), 1); 339 continue; 340 } 341 if (vflag) { 342 if (aflag) 343 printf("%s: ", fsname); 344 printf("%-8s fixed:", fup->fu_name); 345 if (dqbuf.dqb_curinodes != fup->fu_curinodes) 346 fprintf(stdout, "\tinodes %d -> %d", 347 dqbuf.dqb_curinodes, fup->fu_curinodes); 348 if (dqbuf.dqb_curblocks != fup->fu_curblocks) 349 fprintf(stdout, "\tblocks %d -> %d", 350 dqbuf.dqb_curblocks, fup->fu_curblocks); 351 fprintf(stdout, "\n"); 352 } 353 /* 354 * Reset time limit if have a soft limit and were 355 * previously under it, but are now over it. 356 */ 357 if (dqbuf.dqb_bsoftlimit && 358 dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit && 359 fup->fu_curblocks >= dqbuf.dqb_bsoftlimit) 360 dqbuf.dqb_btime = 0; 361 if (dqbuf.dqb_isoftlimit && 362 dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit && 363 fup->fu_curblocks >= dqbuf.dqb_isoftlimit) 364 dqbuf.dqb_itime = 0; 365 dqbuf.dqb_curinodes = fup->fu_curinodes; 366 dqbuf.dqb_curblocks = fup->fu_curblocks; 367 fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo); 368 (void) quotactl(fsname, QCMD(Q_SETUSE, type), id, 369 (caddr_t)&dqbuf); 370 fup->fu_curinodes = 0; 371 fup->fu_curblocks = 0; 372 } 373 fclose(qfi); 374 fflush(qfo); 375 ftruncate(fileno(qfo), 376 (off_t)((highid[type] + 1) * sizeof(struct dqblk))); 377 fclose(qfo); 378 return (0); 379 } 380 381 /* 382 * Check to see if target appears in list of size cnt. 383 */ 384 oneof(target, list, cnt) 385 register char *target, *list[]; 386 int cnt; 387 { 388 register int i; 389 390 for (i = 0; i < cnt; i++) 391 if (strcmp(target, list[i]) == 0) 392 return (i); 393 return (-1); 394 } 395 396 /* 397 * Determine the group identifier for quota files. 398 */ 399 getquotagid() 400 { 401 struct group *gr; 402 403 if (gr = getgrnam(quotagroup)) 404 return (gr->gr_gid); 405 return (-1); 406 } 407 408 /* 409 * Check to see if a particular quota is to be enabled. 410 */ 411 hasquota(fs, type, qfnamep) 412 register struct fstab *fs; 413 int type; 414 char **qfnamep; 415 { 416 register char *opt; 417 char *cp, *index(), *strtok(); 418 static char initname, usrname[100], grpname[100]; 419 static char buf[BUFSIZ]; 420 421 if (!initname) { 422 sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname); 423 sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname); 424 initname = 1; 425 } 426 strcpy(buf, fs->fs_mntops); 427 for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) { 428 if (cp = index(opt, '=')) 429 *cp++ = '\0'; 430 if (type == USRQUOTA && strcmp(opt, usrname) == 0) 431 break; 432 if (type == GRPQUOTA && strcmp(opt, grpname) == 0) 433 break; 434 } 435 if (!opt) 436 return (0); 437 if (cp) { 438 *qfnamep = cp; 439 return (1); 440 } 441 (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]); 442 *qfnamep = buf; 443 return (1); 444 } 445 446 /* 447 * Routines to manage the file usage table. 448 * 449 * Lookup an id of a specific type. 450 */ 451 struct fileusage * 452 lookup(id, type) 453 u_long id; 454 int type; 455 { 456 register struct fileusage *fup; 457 458 for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next) 459 if (fup->fu_id == id) 460 return (fup); 461 return ((struct fileusage *)0); 462 } 463 464 /* 465 * Add a new file usage id if it does not already exist. 466 */ 467 struct fileusage * 468 addid(id, type, name) 469 u_long id; 470 int type; 471 char *name; 472 { 473 struct fileusage *fup, **fhp; 474 int len; 475 476 if (fup = lookup(id, type)) 477 return (fup); 478 if (name) 479 len = strlen(name); 480 else 481 len = 10; 482 if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL) { 483 fprintf(stderr, "out of memory for fileusage structures\n"); 484 exit(1); 485 } 486 fhp = &fuhead[type][id & (FUHASH - 1)]; 487 fup->fu_next = *fhp; 488 *fhp = fup; 489 fup->fu_id = id; 490 if (id > highid[type]) 491 highid[type] = id; 492 if (name) { 493 bcopy(name, fup->fu_name, len + 1); 494 } else { 495 sprintf(fup->fu_name, "%u", id); 496 } 497 return (fup); 498 } 499 500 /* 501 * Special purpose version of ginode used to optimize pass 502 * over all the inodes in numerical order. 503 */ 504 ino_t nextino, lastinum; 505 long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize; 506 struct dinode *inodebuf; 507 #define INOBUFSIZE 56*1024 /* size of buffer to read inodes */ 508 509 struct dinode * 510 getnextinode(inumber) 511 ino_t inumber; 512 { 513 long size; 514 daddr_t dblk; 515 static struct dinode *dp; 516 517 if (inumber != nextino++ || inumber > maxino) { 518 fprintf(stderr, "bad inode number %d to nextinode\n", inumber); 519 exit(1); 520 } 521 if (inumber >= lastinum) { 522 readcnt++; 523 dblk = fsbtodb(&sblock, itod(&sblock, lastinum)); 524 if (readcnt % readpercg == 0) { 525 size = partialsize; 526 lastinum += partialcnt; 527 } else { 528 size = inobufsize; 529 lastinum += fullcnt; 530 } 531 bread(dblk, (char *)inodebuf, size); 532 dp = inodebuf; 533 } 534 return (dp++); 535 } 536 537 /* 538 * Prepare to scan a set of inodes. 539 */ 540 resetinodebuf() 541 { 542 543 nextino = 0; 544 lastinum = 0; 545 readcnt = 0; 546 inobufsize = blkroundup(&sblock, INOBUFSIZE); 547 fullcnt = inobufsize / sizeof(struct dinode); 548 readpercg = sblock.fs_ipg / fullcnt; 549 partialcnt = sblock.fs_ipg % fullcnt; 550 partialsize = partialcnt * sizeof(struct dinode); 551 if (partialcnt != 0) { 552 readpercg++; 553 } else { 554 partialcnt = fullcnt; 555 partialsize = inobufsize; 556 } 557 if (inodebuf == NULL && 558 (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL) { 559 fprintf(stderr, "Cannot allocate space for inode buffer\n"); 560 exit(1); 561 } 562 while (nextino < ROOTINO) 563 getnextinode(nextino); 564 } 565 566 /* 567 * Free up data structures used to scan inodes. 568 */ 569 freeinodebuf() 570 { 571 572 if (inodebuf != NULL) 573 free((char *)inodebuf); 574 inodebuf = NULL; 575 } 576 577 /* 578 * Read specified disk blocks. 579 */ 580 bread(bno, buf, cnt) 581 daddr_t bno; 582 char *buf; 583 long cnt; 584 { 585 586 if (lseek(fi, bno * dev_bsize, 0) < 0) { 587 perror("lseek"); 588 exit(1); 589 } 590 591 if (read(fi, buf, cnt) != cnt) { 592 perror("read"); 593 exit(1); 594 } 595 } 596