1 /*- 2 * SPDX-License-Identifier: BSD-4-Clause 3 * 4 * Copyright (C) 1991, 1994 Wolfgang Solfrank. 5 * Copyright (C) 1991, 1994 TooLs GmbH. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by TooLs GmbH. 19 * 4. The name of TooLs GmbH may not be used to endorse or promote products 20 * derived from this software without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 23 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 24 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 25 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <sys/param.h> 35 #include <sys/mount.h> 36 #include <sys/disklabel.h> 37 #include <ufs/ufs/dinode.h> 38 #include <ufs/ffs/fs.h> 39 40 #include <err.h> 41 #include <errno.h> 42 #include <fcntl.h> 43 #include <fstab.h> 44 #include <libufs.h> 45 #include <mntopts.h> 46 #include <paths.h> 47 #include <pwd.h> 48 #include <stdbool.h> 49 #include <stdint.h> 50 #include <stdio.h> 51 #include <stdlib.h> 52 #include <string.h> 53 #include <time.h> 54 #include <unistd.h> 55 56 /* some flags of what to do: */ 57 static bool all; 58 static bool count; 59 static bool noname; 60 static bool unused; 61 static void (*func)(int, struct fs *); 62 static long blocksize; 63 static char *header; 64 static int headerlen; 65 66 static union dinode *get_inode(int, struct fs *, ino_t); 67 static int isfree(struct fs *, union dinode *); 68 static void inituser(void); 69 static void usrrehash(void); 70 static struct user *user(uid_t); 71 static int cmpusers(const void *, const void *); 72 static void uses(uid_t, daddr_t, time_t); 73 static void initfsizes(void); 74 static void dofsizes(int, struct fs *); 75 static void douser(int, struct fs *); 76 static void donames(int, struct fs *); 77 static void usage(void); 78 static void quot(char *, char *); 79 80 /* 81 * Original BSD quot doesn't round to number of frags/blocks, 82 * doesn't account for indirection blocks and gets it totally 83 * wrong if the size is a multiple of the blocksize. 84 * The new code always counts the number of 512 byte blocks 85 * instead of the number of kilobytes and converts them to 86 * kByte when done (on request). 87 * 88 * Due to the size of modern disks, we must cast intermediate 89 * values to 64 bits to prevent potential overflows. 90 */ 91 #define SIZE(n) ((int)(((intmax_t)(n) * 512 + blocksize - 1) / blocksize)) 92 93 #define INOCNT(fs) ((fs)->fs_ipg) 94 #define INOSZ(fs) \ 95 (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \ 96 sizeof(struct ufs2_dinode)) * INOCNT(fs)) 97 98 #define DIP(fs, dp, field) \ 99 (((fs)->fs_magic == FS_UFS1_MAGIC) ? \ 100 (dp)->dp1.field : (dp)->dp2.field) 101 102 static union dinode * 103 get_inode(int fd, struct fs *super, ino_t ino) 104 { 105 static union dinode *ipbuf; 106 static struct cg *cgp; 107 static ino_t last; 108 static unsigned long cg; 109 struct ufs2_dinode *di2; 110 off_t off; 111 112 if (fd < 0) { /* flush cache */ 113 free(ipbuf); 114 ipbuf = NULL; 115 free(cgp); 116 cgp = NULL; 117 return (NULL); 118 } 119 120 if (ipbuf == NULL || ino < last || ino >= last + INOCNT(super)) { 121 if (super->fs_magic == FS_UFS2_MAGIC && 122 (cgp == NULL || cg != ino_to_cg(super, ino))) { 123 cg = ino_to_cg(super, ino); 124 if (cgp == NULL && (cgp = malloc(super->fs_cgsize)) == NULL) 125 errx(1, "allocate cg"); 126 if (lseek(fd, (off_t)cgtod(super, cg) << super->fs_fshift, 0) < 0) 127 err(1, "lseek cg"); 128 if (read(fd, cgp, super->fs_cgsize) != super->fs_cgsize) 129 err(1, "read cg"); 130 if (!cg_chkmagic(cgp)) 131 errx(1, "cg has bad magic"); 132 } 133 if (ipbuf == NULL && (ipbuf = malloc(INOSZ(super))) == NULL) 134 errx(1, "allocate inodes"); 135 last = rounddown(ino, INOCNT(super)); 136 off = (off_t)ino_to_fsba(super, last) << super->fs_fshift; 137 if (lseek(fd, off, SEEK_SET) != off || 138 read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super)) 139 err(1, "read inodes"); 140 } 141 142 if (super->fs_magic == FS_UFS1_MAGIC) 143 return ((union dinode *) 144 &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]); 145 di2 = &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]; 146 /* If the inode is unused, it might be unallocated too, so zero it. */ 147 if (isclr(cg_inosused(cgp), ino % super->fs_ipg)) 148 memset(di2, 0, sizeof(*di2)); 149 return ((union dinode *)di2); 150 } 151 152 static int 153 isfree(struct fs *super, union dinode *dp) 154 { 155 switch (DIP(super, dp, di_mode) & IFMT) { 156 case IFIFO: 157 case IFLNK: /* should check FASTSYMLINK? */ 158 case IFDIR: 159 case IFREG: 160 return 0; 161 case IFCHR: 162 case IFBLK: 163 case IFSOCK: 164 case IFWHT: 165 case 0: 166 return 1; 167 default: 168 errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT); 169 } 170 } 171 172 static struct user { 173 uid_t uid; 174 char *name; 175 daddr_t space; 176 long count; 177 daddr_t spc30; 178 daddr_t spc60; 179 daddr_t spc90; 180 } *users; 181 static int nusers; 182 183 static void 184 inituser(void) 185 { 186 int i; 187 struct user *usr; 188 189 if (nusers == 0) { 190 nusers = 8; 191 if ((users = calloc(nusers, sizeof(*users))) == NULL) 192 errx(1, "allocate users"); 193 } else { 194 for (usr = users, i = nusers; --i >= 0; usr++) { 195 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0; 196 usr->count = 0; 197 } 198 } 199 } 200 201 static void 202 usrrehash(void) 203 { 204 int i; 205 struct user *usr, *usrn; 206 struct user *svusr; 207 208 svusr = users; 209 nusers *= 2; 210 if ((users = calloc(nusers, sizeof(*users))) == NULL) 211 errx(1, "allocate users"); 212 for (usr = svusr, i = nusers / 2; --i >= 0; usr++) { 213 for (usrn = users + usr->uid % nusers; usrn->name; usrn--) { 214 if (usrn <= users) 215 usrn += nusers; 216 } 217 *usrn = *usr; 218 } 219 } 220 221 static struct user * 222 user(uid_t uid) 223 { 224 struct user *usr; 225 struct passwd *pwd; 226 int i; 227 228 while (1) { 229 for (usr = users + uid % nusers, i = nusers; --i >= 0; usr--) { 230 if (usr->name == NULL) { 231 usr->uid = uid; 232 if (noname || (pwd = getpwuid(uid)) == NULL) 233 asprintf(&usr->name, "#%u", uid); 234 else 235 usr->name = strdup(pwd->pw_name); 236 if (usr->name == NULL) 237 errx(1, "allocate users"); 238 } 239 if (usr->uid == uid) 240 return (usr); 241 if (usr <= users) 242 usr += nusers; 243 } 244 usrrehash(); 245 } 246 } 247 248 static int 249 cmpusers(const void *v1, const void *v2) 250 { 251 const struct user *u1 = v1, *u2 = v2; 252 253 return (u2->space > u1->space ? 1 : 254 u2->space < u1->space ? -1 : 255 u1->uid > u2->uid ? 1 : 256 u1->uid < u2->uid ? -1 : 0); 257 } 258 259 #define sortusers(users) \ 260 qsort((users), nusers, sizeof(struct user), cmpusers) 261 262 static void 263 uses(uid_t uid, daddr_t blks, time_t act) 264 { 265 static time_t today; 266 struct user *usr; 267 268 if (!today) 269 time(&today); 270 271 usr = user(uid); 272 usr->count++; 273 usr->space += blks; 274 275 if (today - act > 90L * 24L * 60L * 60L) 276 usr->spc90 += blks; 277 if (today - act > 60L * 24L * 60L * 60L) 278 usr->spc60 += blks; 279 if (today - act > 30L * 24L * 60L * 60L) 280 usr->spc30 += blks; 281 } 282 283 #define FSZCNT 512 284 static struct fsizes { 285 struct fsizes *fsz_next; 286 daddr_t fsz_first, fsz_last; 287 ino_t fsz_count[FSZCNT]; 288 daddr_t fsz_sz[FSZCNT]; 289 } *fsizes; 290 291 static void 292 initfsizes(void) 293 { 294 struct fsizes *fp; 295 int i; 296 297 for (fp = fsizes; fp; fp = fp->fsz_next) { 298 for (i = FSZCNT; --i >= 0;) { 299 fp->fsz_count[i] = 0; 300 fp->fsz_sz[i] = 0; 301 } 302 } 303 } 304 305 static void 306 dofsizes(int fd, struct fs *super) 307 { 308 ino_t inode, maxino; 309 union dinode *dp; 310 daddr_t sz, ksz; 311 struct fsizes *fp, **fsp; 312 int i; 313 314 maxino = super->fs_ncg * super->fs_ipg - 1; 315 for (inode = 0; inode < maxino; inode++) { 316 if ((dp = get_inode(fd, super, inode)) != NULL && 317 !isfree(super, dp) 318 ) { 319 sz = DIP(super, dp, di_blocks); 320 ksz = SIZE(sz); 321 for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) { 322 if (ksz < fp->fsz_last) 323 break; 324 } 325 if (fp == NULL || ksz < fp->fsz_first) { 326 if ((fp = malloc(sizeof(*fp))) == NULL) 327 errx(1, "allocate fsize structure"); 328 fp->fsz_next = *fsp; 329 *fsp = fp; 330 fp->fsz_first = rounddown(ksz, FSZCNT); 331 fp->fsz_last = fp->fsz_first + FSZCNT; 332 for (i = FSZCNT; --i >= 0;) { 333 fp->fsz_count[i] = 0; 334 fp->fsz_sz[i] = 0; 335 } 336 } 337 fp->fsz_count[ksz % FSZCNT]++; 338 fp->fsz_sz[ksz % FSZCNT] += sz; 339 } 340 } 341 sz = 0; 342 for (fp = fsizes; fp != NULL; fp = fp->fsz_next) { 343 for (i = 0; i < FSZCNT; i++) { 344 if (fp->fsz_count[i] != 0) { 345 printf("%jd\t%jd\t%d\n", 346 (intmax_t)(fp->fsz_first + i), 347 (intmax_t)fp->fsz_count[i], 348 SIZE(sz += fp->fsz_sz[i])); 349 } 350 } 351 } 352 } 353 354 static void 355 douser(int fd, struct fs *super) 356 { 357 ino_t inode, maxino; 358 struct user *usr, *usrs; 359 union dinode *dp; 360 int n; 361 362 maxino = super->fs_ncg * super->fs_ipg - 1; 363 for (inode = 0; inode < maxino; inode++) { 364 if ((dp = get_inode(fd, super, inode)) != NULL && 365 !isfree(super, dp)) { 366 uses(DIP(super, dp, di_uid), 367 DIP(super, dp, di_blocks), 368 DIP(super, dp, di_atime)); 369 } 370 } 371 if ((usrs = malloc(nusers * sizeof(*usrs))) == NULL) 372 errx(1, "allocate users"); 373 memcpy(usrs, users, nusers * sizeof(*usrs)); 374 sortusers(usrs); 375 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) { 376 printf("%5d", SIZE(usr->space)); 377 if (count) 378 printf("\t%5ld", usr->count); 379 printf("\t%-8s", usr->name); 380 if (unused) { 381 printf("\t%5d\t%5d\t%5d", 382 SIZE(usr->spc30), 383 SIZE(usr->spc60), 384 SIZE(usr->spc90)); 385 } 386 printf("\n"); 387 } 388 free(usrs); 389 } 390 391 static void 392 donames(int fd, struct fs *super) 393 { 394 int c; 395 ino_t maxino; 396 uintmax_t inode; 397 union dinode *dp; 398 399 maxino = super->fs_ncg * super->fs_ipg - 1; 400 /* first skip the name of the filesystem */ 401 while ((c = getchar()) != EOF && (c < '0' || c > '9')) 402 while ((c = getchar()) != EOF && c != '\n'); 403 ungetc(c, stdin); 404 while (scanf("%ju", &inode) == 1) { 405 if (inode > maxino) { 406 warnx("illegal inode %ju", inode); 407 return; 408 } 409 if ((dp = get_inode(fd, super, inode)) != NULL && 410 !isfree(super, dp)) { 411 printf("%s\t", user(DIP(super, dp, di_uid))->name); 412 /* now skip whitespace */ 413 while ((c = getchar()) == ' ' || c == '\t') 414 /* nothing */; 415 /* and print out the remainder of the input line */ 416 while (c != EOF && c != '\n') { 417 putchar(c); 418 c = getchar(); 419 } 420 putchar('\n'); 421 } else { 422 /* skip this line */ 423 while ((c = getchar()) != EOF && c != '\n') 424 /* nothing */; 425 } 426 if (c == EOF) 427 break; 428 } 429 } 430 431 static void 432 usage(void) 433 { 434 fprintf(stderr, "usage: quot [-cfknv] [-a | filesystem ...]\n"); 435 exit(1); 436 } 437 438 void 439 quot(char *name, char *mp) 440 { 441 int fd; 442 struct fs *fs; 443 444 get_inode(-1, NULL, 0); /* flush cache */ 445 inituser(); 446 initfsizes(); 447 if ((fd = open(name, 0)) < 0) { 448 warn("%s", name); 449 close(fd); 450 return; 451 } 452 switch (errno = sbget(fd, &fs, UFS_STDSB, UFS_NOCSUM)) { 453 case 0: 454 break; 455 case ENOENT: 456 warn("Cannot find file system superblock"); 457 close(fd); 458 return; 459 default: 460 warn("Unable to read file system superblock"); 461 close(fd); 462 return; 463 } 464 printf("%s:", name); 465 if (mp) 466 printf(" (%s)", mp); 467 putchar('\n'); 468 (*func)(fd, fs); 469 free(fs); 470 close(fd); 471 } 472 473 int 474 main(int argc, char *argv[]) 475 { 476 struct statfs *mp; 477 int ch, cnt; 478 479 func = douser; 480 header = getbsize(&headerlen, &blocksize); 481 while ((ch = getopt(argc, argv, "acfhkNnv")) != -1) { 482 switch (ch) { 483 case 'a': 484 all = true; 485 break; 486 case 'c': 487 func = dofsizes; 488 break; 489 case 'f': 490 count = true; 491 break; 492 case 'h': 493 /* ignored for backward compatibility */ 494 break; 495 case 'k': 496 blocksize = 1024; 497 break; 498 case 'N': 499 noname = true; 500 break; 501 case 'n': 502 func = donames; 503 break; 504 case 'v': 505 unused = true; 506 break; 507 default: 508 usage(); 509 } 510 } 511 argc -= optind; 512 argv += optind; 513 514 if ((argc == 0 && !all) || (all && argc)) 515 usage(); 516 517 if (all) { 518 for (cnt = getmntinfo(&mp, MNT_NOWAIT); --cnt >= 0; mp++) 519 if (strncmp(mp->f_fstypename, "ufs", MFSNAMELEN) == 0) 520 quot(mp->f_mntfromname, mp->f_mntonname); 521 } 522 while (argc-- > 0) { 523 if ((mp = getmntpoint(*argv)) != NULL) 524 quot(mp->f_mntfromname, mp->f_mntonname); 525 else 526 quot(*argv, 0); 527 argv++; 528 } 529 return (0); 530 } 531