1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. The Berkeley software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #ifndef lint 8: char copyright[] = 9: "@(#) Copyright (c) 1980 Regents of the University of California.\n\ 10: All rights reserved.\n"; 11: #endif not lint 12: 13: #ifndef lint 14: static char sccsid[] = "@(#)quotacheck.c 5.6 (Berkeley) 11/3/85"; 15: #endif not lint 16: 17: /* 18: * Fix up / report on disc quotas & usage 19: */ 20: #include <stdio.h> 21: #include <ctype.h> 22: #include <signal.h> 23: #include <errno.h> 24: #include <sys/param.h> 25: #include <sys/inode.h> 26: #include <sys/fs.h> 27: #include <sys/quota.h> 28: #include <sys/stat.h> 29: #include <sys/wait.h> 30: #include <fstab.h> 31: #include <pwd.h> 32: 33: union { 34: struct fs sblk; 35: char dummy[MAXBSIZE]; 36: } un; 37: #define sblock un.sblk 38: 39: #define ITABSZ 256 40: struct dinode itab[ITABSZ]; 41: struct dinode *dp; 42: 43: #define LOGINNAMESIZE 8 44: struct fileusage { 45: struct fileusage *fu_next; 46: struct dqusage fu_usage; 47: u_short fu_uid; 48: char fu_name[LOGINNAMESIZE + 1]; 49: }; 50: #define FUHASH 997 51: struct fileusage *fuhead[FUHASH]; 52: struct fileusage *lookup(); 53: struct fileusage *adduid(); 54: int highuid; 55: 56: int fi; 57: ino_t ino; 58: long done; 59: struct passwd *getpwent(); 60: struct dinode *ginode(); 61: char *malloc(), *makerawname(); 62: 63: int vflag; /* verbose */ 64: int aflag; /* all file systems */ 65: int pflag; /* fsck like parallel check */ 66: 67: char *qfname = "quotas"; 68: char quotafile[MAXPATHLEN + 1]; 69: struct dqblk zerodqbuf; 70: struct fileusage zerofileusage; 71: 72: main(argc, argv) 73: int argc; 74: char **argv; 75: { 76: register struct fstab *fs; 77: register struct fileusage *fup; 78: register struct passwd *pw; 79: int i, errs = 0; 80: 81: again: 82: argc--, argv++; 83: if (argc > 0 && strcmp(*argv, "-v") == 0) { 84: vflag++; 85: goto again; 86: } 87: if (argc > 0 && strcmp(*argv, "-a") == 0) { 88: aflag++; 89: goto again; 90: } 91: if (argc > 0 && strcmp(*argv, "-p") == 0) { 92: pflag++; 93: goto again; 94: } 95: if (argc <= 0 && !aflag) { 96: fprintf(stderr, "Usage:\n\t%s\n\t%s\n", 97: "quotacheck [-v] [-p] -a", 98: "quotacheck [-v] [-p] filesys ..."); 99: exit(1); 100: } 101: 102: setpwent(); 103: while ((pw = getpwent()) != 0) { 104: fup = lookup(pw->pw_uid); 105: if (fup == 0) { 106: fup = adduid(pw->pw_uid); 107: strncpy(fup->fu_name, pw->pw_name, 108: sizeof(fup->fu_name)); 109: } 110: } 111: endpwent(); 112: 113: if (pflag) 114: errs = preen(argc, argv); 115: else { 116: if (setfsent() == 0) { 117: fprintf(stderr, "Can't open "); 118: perror(FSTAB); 119: exit(8); 120: } 121: while ((fs = getfsent()) != NULL) { 122: if (aflag && 123: (fs->fs_type == 0 || 124: strcmp(fs->fs_type, FSTAB_RQ) != 0)) 125: continue; 126: if (!aflag && 127: !(oneof(fs->fs_file, argv, argc) || 128: oneof(fs->fs_spec, argv, argc))) 129: continue; 130: (void) sprintf(quotafile, "%s/%s", fs->fs_file, qfname); 131: errs += chkquota(fs->fs_spec, fs->fs_file, quotafile); 132: } 133: endfsent(); 134: } 135: 136: for (i = 0; i < argc; i++) 137: if ((done & (1 << i)) == 0) 138: fprintf(stderr, "%s not found in %s\n", 139: argv[i], FSTAB); 140: exit(errs); 141: } 142: 143: preen(argc, argv) 144: int argc; 145: char **argv; 146: { 147: register struct fstab *fs; 148: register int passno, anygtr; 149: register int errs; 150: union wait status; 151: 152: passno = 1; 153: errs = 0; 154: do { 155: anygtr = 0; 156: 157: if (setfsent() == 0) { 158: fprintf(stderr, "Can't open "); 159: perror(FSTAB); 160: exit(8); 161: } 162: 163: while ((fs = getfsent()) != NULL) { 164: if (fs->fs_passno > passno) 165: anygtr = 1; 166: 167: if (aflag && 168: (fs->fs_type == 0 || 169: strcmp(fs->fs_type, FSTAB_RQ) != 0)) 170: continue; 171: 172: if (!aflag && 173: !(oneof(fs->fs_file, argv, argc) || 174: oneof(fs->fs_spec, argv, argc))) 175: continue; 176: 177: if (fs->fs_passno != passno) 178: continue; 179: 180: switch (fork()) { 181: case -1: 182: perror("fork"); 183: exit(8); 184: break; 185: 186: case 0: 187: sprintf(quotafile, "%s/%s", 188: fs->fs_file, qfname); 189: exit(chkquota(fs->fs_spec, 190: fs->fs_file, quotafile)); 191: } 192: } 193: 194: while (wait(&status) != -1) 195: errs += status.w_retcode; 196: 197: passno++; 198: } while (anygtr); 199: 200: return (errs); 201: } 202: 203: chkquota(fsdev, fsfile, qffile) 204: char *fsdev; 205: char *fsfile; 206: char *qffile; 207: { 208: register struct fileusage *fup; 209: dev_t quotadev; 210: register FILE *qfi, *qfo; 211: u_short uid; 212: int cg, i, fdo; 213: char *rawdisk; 214: struct stat statb; 215: struct dqblk dqbuf; 216: static int warned = 0; 217: extern int errno; 218: 219: rawdisk = makerawname(fsdev); 220: if (vflag) 221: fprintf(stdout, "*** Checking quotas for %s (%s)\n", rawdisk, fsfile); 222: fi = open(rawdisk, 0); 223: if (fi < 0) { 224: perror(rawdisk); 225: return (1); 226: } 227: qfi = fopen(qffile, "r"); 228: if (qfi == NULL) { 229: perror(qffile); 230: close(fi); 231: return (1); 232: } 233: if (fstat(fileno(qfi), &statb) < 0) { 234: perror(qffile); 235: fclose(qfi); 236: close(fi); 237: return (1); 238: } 239: quotadev = statb.st_dev; 240: if (stat(fsdev, &statb) < 0) { 241: perror(fsdev); 242: fclose(qfi); 243: close(fi); 244: return (1); 245: } 246: if (quotadev != statb.st_rdev) { 247: fprintf(stderr, "%s dev (0x%x) mismatch %s dev (0x%x)\n", 248: qffile, quotadev, fsdev, statb.st_rdev); 249: fclose(qfi); 250: close(fi); 251: return (1); 252: } 253: /* 254: * Must do fdopen(open(qffile, 1), "w") instead of fopen(qffile, "w") 255: * because fopen(qffile, "w") would truncate the quota file. 256: */ 257: fdo = open(qffile, 1); 258: if (fdo < 0 || (qfo = fdopen(fdo, "w")) == NULL) { 259: perror(qffile); 260: if (fdo >= 0) 261: close(fdo); 262: fclose(qfi); 263: close(fi); 264: return (1); 265: } 266: if (quota(Q_SYNC, 0, quotadev, (caddr_t)0) < 0 && 267: errno == EINVAL && !warned && vflag) { 268: warned++; 269: fprintf(stdout, 270: "*** Warning: Quotas are not compiled into this kernel\n"); 271: } 272: sync(); 273: bread(SBLOCK, (char *)&sblock, SBSIZE); 274: ino = 0; 275: for (cg = 0; cg < sblock.fs_ncg; cg++) { 276: dp = NULL; 277: for (i = 0; i < sblock.fs_ipg; i++) 278: acct(ginode()); 279: } 280: for (uid = 0; uid <= highuid; uid++) { 281: i = fread(&dqbuf, sizeof(struct dqblk), 1, qfi); 282: if (i == 0) 283: dqbuf = zerodqbuf; 284: fup = lookup(uid); 285: if (fup == 0) 286: fup = &zerofileusage; 287: if (dqbuf.dqb_curinodes == fup->fu_usage.du_curinodes && 288: dqbuf.dqb_curblocks == fup->fu_usage.du_curblocks) { 289: fup->fu_usage.du_curinodes = 0; 290: fup->fu_usage.du_curblocks = 0; 291: fseek(qfo, (long)sizeof(struct dqblk), 1); 292: continue; 293: } 294: if (vflag) { 295: if (pflag) 296: printf("%s: ", rawdisk); 297: if (fup->fu_name[0] != '\0') 298: printf("%-8s fixed:", fup->fu_name); 299: else 300: printf("#%-7d fixed:", uid); 301: if (dqbuf.dqb_curinodes != fup->fu_usage.du_curinodes) 302: fprintf(stdout, "\tinodes %d -> %d", 303: dqbuf.dqb_curinodes, fup->fu_usage.du_curinodes); 304: if (dqbuf.dqb_curblocks != fup->fu_usage.du_curblocks) 305: fprintf(stdout, "\tblocks %d -> %d", 306: dqbuf.dqb_curblocks, fup->fu_usage.du_curblocks); 307: fprintf(stdout, "\n"); 308: } 309: dqbuf.dqb_curinodes = fup->fu_usage.du_curinodes; 310: dqbuf.dqb_curblocks = fup->fu_usage.du_curblocks; 311: fwrite(&dqbuf, sizeof(struct dqblk), 1, qfo); 312: quota(Q_SETDUSE, uid, quotadev, &fup->fu_usage); 313: fup->fu_usage.du_curinodes = 0; 314: fup->fu_usage.du_curblocks = 0; 315: } 316: fflush(qfo); 317: ftruncate(fileno(qfo), (off_t)((highuid + 1) * sizeof(struct dqblk))); 318: fclose(qfi); 319: fclose(qfo); 320: close(fi); 321: return (0); 322: } 323: 324: acct(ip) 325: register struct dinode *ip; 326: { 327: register struct fileusage *fup; 328: 329: if (ip == NULL) 330: return; 331: if (ip->di_mode == 0) 332: return; 333: fup = adduid(ip->di_uid); 334: fup->fu_usage.du_curinodes++; 335: if ((ip->di_mode & IFMT) == IFCHR || (ip->di_mode & IFMT) == IFBLK) 336: return; 337: fup->fu_usage.du_curblocks += ip->di_blocks; 338: } 339: 340: oneof(target, list, n) 341: char *target, *list[]; 342: register int n; 343: { 344: register int i; 345: 346: for (i = 0; i < n; i++) 347: if (strcmp(target, list[i]) == 0) { 348: done |= 1 << i; 349: return (1); 350: } 351: return (0); 352: } 353: 354: struct dinode * 355: ginode() 356: { 357: register unsigned long iblk; 358: 359: if (dp == NULL || ++dp >= &itab[ITABSZ]) { 360: iblk = itod(&sblock, ino); 361: bread(fsbtodb(&sblock, iblk), (char *)itab, sizeof itab); 362: dp = &itab[ino % INOPB(&sblock)]; 363: } 364: if (ino++ < ROOTINO) 365: return(NULL); 366: return(dp); 367: } 368: 369: bread(bno, buf, cnt) 370: long unsigned bno; 371: char *buf; 372: { 373: extern off_t lseek(); 374: register off_t pos; 375: 376: pos = (off_t)dbtob(bno); 377: if (lseek(fi, pos, 0) != pos) { 378: perror("lseek"); 379: exit(1); 380: } 381: 382: if (read(fi, buf, cnt) != cnt) { 383: perror("read"); 384: exit(1); 385: } 386: } 387: 388: struct fileusage * 389: lookup(uid) 390: u_short uid; 391: { 392: register struct fileusage *fup; 393: 394: for (fup = fuhead[uid % FUHASH]; fup != 0; fup = fup->fu_next) 395: if (fup->fu_uid == uid) 396: return (fup); 397: return ((struct fileusage *)0); 398: } 399: 400: struct fileusage * 401: adduid(uid) 402: u_short uid; 403: { 404: struct fileusage *fup, **fhp; 405: extern char *calloc(); 406: 407: fup = lookup(uid); 408: if (fup != 0) 409: return (fup); 410: fup = (struct fileusage *)calloc(1, sizeof(struct fileusage)); 411: if (fup == 0) { 412: fprintf(stderr, "out of memory for fileusage structures\n"); 413: exit(1); 414: } 415: fhp = &fuhead[uid % FUHASH]; 416: fup->fu_next = *fhp; 417: *fhp = fup; 418: fup->fu_uid = uid; 419: if (uid > highuid) 420: highuid = uid; 421: return (fup); 422: } 423: 424: char * 425: makerawname(name) 426: char *name; 427: { 428: register char *cp; 429: char tmp, ch, *rindex(); 430: static char rawname[MAXPATHLEN]; 431: 432: strcpy(rawname, name); 433: cp = rindex(rawname, '/'); 434: if (cp == NULL) 435: return (name); 436: else 437: cp++; 438: for (ch = 'r'; *cp != '\0'; ) { 439: tmp = *cp; 440: *cp++ = ch; 441: ch = tmp; 442: } 443: *cp++ = ch; 444: *cp = '\0'; 445: return (rawname); 446: }