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