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