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/03/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 } 447