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