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