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