1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
27 /* All Rights Reserved */
28
29 /*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39 #pragma ident "%Z%%M% %I% %E% SMI"
40
41 /*
42 * quot
43 */
44
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <ctype.h>
48 #include <string.h>
49 #include <limits.h>
50 #include <pwd.h>
51 #include <sys/mnttab.h>
52 #include <sys/param.h>
53 #include <sys/types.h>
54 #include <unistd.h>
55 #include <sys/mntent.h>
56 #include <sys/vnode.h>
57 #include <sys/fs/ufs_inode.h>
58 #include <sys/fs/ufs_fs.h>
59 #include <sys/file.h>
60 #include <sys/stat.h>
61 #include <fcntl.h>
62
63 #define ISIZ (MAXBSIZE/sizeof (struct dinode))
64 static union {
65 struct fs u_sblock;
66 char dummy[SBSIZE];
67 } sb_un;
68 #define sblock sb_un.u_sblock
69 static struct dinode *itab;
70
71 struct du {
72 struct du *next;
73 long blocks;
74 long blocks30;
75 long blocks60;
76 long blocks90;
77 long nfiles;
78 uid_t uid;
79 char *u_name;
80 };
81 static struct du **du;
82
83 #define UHASH 8209
84 static int ndu;
85 #define HASH(u) ((uint_t)(u) % UHASH)
86 static struct du *duhashtbl[UHASH];
87
88 #define TSIZE 2048
89 static int sizes[TSIZE];
90 static offset_t overflow;
91
92 static int nflg;
93 static int fflg;
94 static int cflg;
95 static int vflg;
96 static int hflg;
97 static int aflg;
98 static long now;
99
100 static unsigned ino;
101
102 static void usage(void);
103 static void quotall(void);
104 static void qacct(struct dinode *);
105 static void bread(int, diskaddr_t, char *, int);
106 static void report(void);
107 static int getdev(char **);
108 static int check(char *, char *);
109 static struct du *adduid(uid_t);
110 static struct du *lookup(uid_t);
111 static void sortprep(void);
112 static void cleanup(void);
113
114 static void
usage()115 usage()
116 {
117 (void) fprintf(stderr, "ufs usage: quot [-nfcvha] [filesystem ...]\n");
118 }
119
120 int
main(int argc,char * argv[])121 main(int argc, char *argv[])
122 {
123 int opt;
124 int i;
125
126 if (argc == 1) {
127 (void) fprintf(stderr,
128 "ufs Usage: quot [-nfcvha] [filesystem ...]\n");
129 return (32);
130 }
131
132 now = time(0);
133 while ((opt = getopt(argc, argv, "nfcvhaV")) != EOF) {
134 switch (opt) {
135 case 'n':
136 nflg++;
137 break;
138 case 'f':
139 fflg++;
140 break;
141 case 'c':
142 cflg++;
143 break;
144 case 'v':
145 vflg++;
146 break;
147 case 'h':
148 hflg++;
149 break;
150 case 'a':
151 aflg++;
152 break;
153 case 'V': /* Print command line */
154 {
155 char *opt_text;
156 int opt_count;
157
158 (void) fprintf(stdout, "quot -F UFS ");
159 for (opt_count = 1; opt_count < argc;
160 opt_count++) {
161 opt_text = argv[opt_count];
162 if (opt_text)
163 (void) fprintf(stdout, " %s ",
164 opt_text);
165 }
166 (void) fprintf(stdout, "\n");
167 }
168 break;
169 case '?':
170 usage();
171 return (32);
172 }
173 }
174
175 if (aflg) {
176 quotall();
177 }
178
179 for (i = optind; i < argc; i++) {
180 if ((getdev(&argv[i]) == 0) &&
181 (check(argv[i], (char *)NULL) == 0)) {
182 report();
183 cleanup();
184 }
185 }
186 return (0);
187 }
188
189 static void
quotall()190 quotall()
191 {
192 FILE *fstab;
193 struct mnttab mntp;
194 char *cp;
195
196 extern char *getfullrawname();
197
198 fstab = fopen(MNTTAB, "r");
199 if (fstab == NULL) {
200 (void) fprintf(stderr, "quot: no %s file\n", MNTTAB);
201 exit(32);
202 }
203 while (getmntent(fstab, &mntp) == NULL) {
204 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0)
205 continue;
206
207 if ((cp = getfullrawname(mntp.mnt_special)) == NULL)
208 continue;
209
210 if (*cp == '\0')
211 continue;
212
213 if (check(cp, mntp.mnt_mountp) == 0) {
214 report();
215 cleanup();
216 }
217
218 free(cp);
219 }
220 (void) fclose(fstab);
221 }
222
223 static int
check(char * file,char * fsdir)224 check(char *file, char *fsdir)
225 {
226 FILE *fstab;
227 int i, j;
228 int c, fd;
229
230
231 /*
232 * Initialize tables between checks;
233 * because of the qsort done in report()
234 * the hash tables must be rebuilt each time.
235 */
236 for (i = 0; i < TSIZE; i++)
237 sizes[i] = 0;
238 overflow = 0LL;
239 ndu = 0;
240 fd = open64(file, O_RDONLY);
241 if (fd < 0) {
242 (void) fprintf(stderr, "quot: ");
243 perror(file);
244 exit(32);
245 }
246 (void) printf("%s", file);
247 if (fsdir == NULL) {
248 struct mnttab mntp;
249
250 fstab = fopen(MNTTAB, "r");
251 if (fstab == NULL) {
252 (void) fprintf(stderr, "quot: no %s file\n", MNTTAB);
253 exit(32);
254 }
255 while (getmntent(fstab, &mntp) == NULL) {
256 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0)
257 continue;
258 if (strcmp(mntp.mnt_special, file) == 0) {
259 fsdir = mntp.mnt_mountp;
260 break;
261 }
262 }
263 }
264 if (fsdir != NULL && *fsdir != '\0')
265 (void) printf(" (%s)", fsdir);
266 (void) printf(":\n");
267 sync();
268 bread(fd, (diskaddr_t)SBLOCK, (char *)&sblock, SBSIZE);
269 if (nflg) {
270 if (isdigit(c = getchar()))
271 (void) ungetc(c, stdin);
272 else while (c != '\n' && c != EOF)
273 c = getchar();
274 }
275
276 itab = (struct dinode *)calloc(sblock.fs_ipg, sizeof (struct dinode));
277 if (itab == NULL) {
278 (void) fprintf(stderr,
279 "not enough memory to allocate tables\n");
280 return (1);
281 }
282
283 ino = 0;
284 for (c = 0; c < sblock.fs_ncg; c++) {
285 bread(fd, (diskaddr_t)fsbtodb(&sblock, cgimin(&sblock, c)),
286 (char *)itab,
287 (int)(sblock.fs_ipg * sizeof (struct dinode)));
288 for (j = 0; j < sblock.fs_ipg; j++, ino++) {
289 if (ino < UFSROOTINO)
290 continue;
291 qacct(&itab[j]);
292 }
293 }
294 (void) close(fd);
295 return (0);
296 }
297
298 static void
qacct(struct dinode * ip)299 qacct(struct dinode *ip)
300 {
301 struct du *dp;
302 long blks, frags, size;
303 int n;
304 static int fino;
305
306 ip->di_mode = ip->di_smode;
307 if (ip->di_suid != UID_LONG) {
308 ip->di_uid = ip->di_suid;
309 }
310 if ((ip->di_mode & IFMT) == 0)
311 return;
312 /*
313 * By default, take block count in inode. Otherwise (-h),
314 * take the size field and estimate the blocks allocated.
315 * The latter does not account for holes in files.
316 */
317 if (!hflg)
318 size = ip->di_blocks / 2;
319 else {
320 blks = lblkno(&sblock, ip->di_size);
321 frags = blks * sblock.fs_frag +
322 numfrags(&sblock, dblksize(&sblock, ip, blks));
323 /*
324 * Must cast to offset_t because for a large file,
325 * frags multiplied by sblock.fs_fsize will not fit in a long.
326 * However, when divided by 1024, the end result will fit in
327 * the 32 bit size variable (40 bit UFS).
328 */
329 size = (long)((offset_t)frags * (offset_t)sblock.fs_fsize / 1024);
330 }
331 if (cflg) {
332 if ((ip->di_mode&IFMT) != IFDIR && (ip->di_mode&IFMT) != IFREG)
333 return;
334 if (size >= TSIZE) {
335 overflow += (offset_t)size;
336 size = TSIZE-1;
337 }
338 sizes[size]++;
339 return;
340 }
341 dp = lookup(ip->di_uid);
342 if (dp == NULL)
343 return;
344 dp->blocks += size;
345 #define DAY (60 * 60 * 24) /* seconds per day */
346 if (now - ip->di_atime > 30 * DAY)
347 dp->blocks30 += size;
348 if (now - ip->di_atime > 60 * DAY)
349 dp->blocks60 += size;
350 if (now - ip->di_atime > 90 * DAY)
351 dp->blocks90 += size;
352 dp->nfiles++;
353 while (nflg) {
354 if (fino == 0)
355 if (scanf("%d", &fino) <= 0)
356 return;
357 if (fino > ino)
358 return;
359 if (fino < ino) {
360 while ((n = getchar()) != '\n' && n != EOF)
361 ;
362 fino = 0;
363 continue;
364 }
365 if (dp->u_name)
366 (void) printf("%.7s ", dp->u_name);
367 else
368 (void) printf("%ld ", (long)ip->di_uid);
369 while ((n = getchar()) == ' ' || n == '\t')
370 ;
371 (void) putchar(n);
372 while (n != EOF && n != '\n') {
373 n = getchar();
374 (void) putchar(n);
375 }
376 fino = 0;
377 break;
378 }
379 }
380
381 static void
bread(int fd,diskaddr_t bno,char * buf,int cnt)382 bread(int fd, diskaddr_t bno, char *buf, int cnt)
383 {
384 int ret;
385
386 if (llseek(fd, (offset_t)(bno * DEV_BSIZE), SEEK_SET) < 0) {
387 perror("llseek");
388 exit(32);
389 }
390
391 if ((ret = read(fd, buf, cnt)) != cnt) {
392 (void) fprintf(stderr, "quot: read returns %d (cnt = %d)\n",
393 ret, cnt);
394 (void) fprintf(stderr, "quot: read error at block %lld\n", bno);
395 perror("read");
396 exit(32);
397 }
398 }
399
400 static int
qcmp(const void * arg1,const void * arg2)401 qcmp(const void *arg1, const void *arg2)
402 {
403 struct du **p1 = (struct du **)arg1;
404 struct du **p2 = (struct du **)arg2;
405 char *s1, *s2;
406
407 if ((*p1)->blocks > (*p2)->blocks)
408 return (-1);
409 if ((*p1)->blocks < (*p2)->blocks)
410 return (1);
411 s1 = (*p1)->u_name;
412 if (s1 == NULL)
413 return (0);
414 s2 = (*p2)->u_name;
415 if (s2 == NULL)
416 return (0);
417 return (strcmp(s1, s2));
418 }
419
420 static void
report()421 report()
422 {
423 int i;
424 struct du **dp;
425 int cnt;
426
427 if (nflg)
428 return;
429 if (cflg) {
430 long t = 0;
431
432 for (i = 0; i < TSIZE - 1; i++)
433 if (sizes[i]) {
434 t += i*sizes[i];
435 (void) printf("%d %d %ld\n",
436 i, sizes[i], t);
437 }
438 if (sizes[TSIZE -1 ])
439 (void) printf("%d %d %lld\n", TSIZE - 1,
440 sizes[TSIZE - 1], overflow + (offset_t)t);
441 return;
442 }
443 sortprep();
444 qsort(du, ndu, sizeof (du[0]), qcmp);
445 for (cnt = 0, dp = &du[0]; dp && cnt != ndu; dp++, cnt++) {
446 if ((*dp)->blocks == 0)
447 return;
448 (void) printf("%5ld\t", (*dp)->blocks);
449 if (fflg)
450 (void) printf("%5ld\t", (*dp)->nfiles);
451
452 if ((*dp)->u_name)
453 (void) printf("%-8s", (*dp)->u_name);
454 else
455 (void) printf("#%-8ld", (long)(*dp)->uid);
456 if (vflg)
457 (void) printf("\t%5ld\t%5ld\t%5ld",
458 (*dp)->blocks30, (*dp)->blocks60, (*dp)->blocks90);
459 (void) printf("\n");
460 }
461 }
462
463
464
465 static int
getdev(char ** devpp)466 getdev(char **devpp)
467 {
468 struct stat64 statb;
469 FILE *fstab;
470 struct mnttab mntp;
471 char *cp; /* Pointer to raw device name */
472
473 extern char *getfullrawname();
474
475 if (stat64(*devpp, &statb) < 0) {
476 perror(*devpp);
477 exit(32);
478 }
479 if ((statb.st_mode & S_IFMT) == S_IFCHR)
480 return (0);
481 if ((statb.st_mode & S_IFMT) == S_IFBLK) {
482 /* If we can't get the raw name, keep the block name */
483 if ((cp = getfullrawname(*devpp)) != NULL)
484 *devpp = strdup(cp);
485 return (0);
486 }
487 fstab = fopen(MNTTAB, "r");
488 if (fstab == NULL) {
489 (void) fprintf(stderr, "quot: no %s file\n", MNTTAB);
490 exit(32);
491 }
492 while (getmntent(fstab, &mntp) == NULL) {
493 if (strcmp(mntp.mnt_mountp, *devpp) == 0) {
494 if (strcmp(mntp.mnt_fstype, MNTTYPE_UFS) != 0) {
495 (void) fprintf(stderr,
496 "quot: %s not ufs filesystem\n",
497 *devpp);
498 exit(32);
499 }
500 /* If we can't get the raw name, use the block name */
501 if ((cp = getfullrawname(mntp.mnt_special)) == NULL)
502 cp = mntp.mnt_special;
503 *devpp = strdup(cp);
504 (void) fclose(fstab);
505 return (0);
506 }
507 }
508 (void) fclose(fstab);
509 (void) fprintf(stderr, "quot: %s doesn't appear to be a filesystem.\n",
510 *devpp);
511 usage();
512 exit(32);
513 /* NOTREACHED */
514 }
515
516 static struct du *
lookup(uid_t uid)517 lookup(uid_t uid)
518 {
519 struct passwd *pwp;
520 struct du *up;
521
522 for (up = duhashtbl[HASH(uid)]; up != NULL; up = up->next) {
523 if (up->uid == uid)
524 return (up);
525 }
526
527 pwp = getpwuid(uid);
528
529 up = adduid(uid);
530 if (up && pwp) {
531 up->u_name = strdup(pwp->pw_name);
532 }
533 return (up);
534 }
535
536 static struct du *
adduid(uid_t uid)537 adduid(uid_t uid)
538 {
539 struct du *up, **uhp;
540
541 up = (struct du *)calloc(1, sizeof (struct du));
542 if (up == NULL) {
543 (void) fprintf(stderr,
544 "out of memory for du structures\n");
545 exit(32);
546 }
547
548 uhp = &duhashtbl[HASH(uid)];
549 up->next = *uhp;
550 *uhp = up;
551 up->uid = uid;
552 up->u_name = NULL;
553 ndu++;
554 return (up);
555 }
556
557 static void
sortprep()558 sortprep()
559 {
560 struct du **dp, *ep;
561 struct du **hp;
562 int i, cnt = 0;
563
564 dp = NULL;
565
566 dp = (struct du **)calloc(ndu, sizeof (struct du **));
567 if (dp == NULL) {
568 (void) fprintf(stderr,
569 "out of memory for du structures\n");
570 exit(32);
571 }
572
573 for (hp = duhashtbl, i = 0; i != UHASH; i++) {
574 if (hp[i] == NULL)
575 continue;
576
577 for (ep = hp[i]; ep; ep = ep->next) {
578 dp[cnt++] = ep;
579 }
580 }
581 du = dp;
582 }
583
584 static void
cleanup()585 cleanup()
586 {
587 int i;
588 struct du *ep, *next;
589
590 /*
591 * Release memory from hash table and du
592 */
593
594 if (du) {
595 free(du);
596 du = NULL;
597 }
598
599
600 for (i = 0; i != UHASH; i++) {
601 if (duhashtbl[i] == NULL)
602 continue;
603 ep = duhashtbl[i];
604 while (ep) {
605 next = ep->next;
606 if (ep->u_name) {
607 free(ep->u_name);
608 }
609 free(ep);
610 ep = next;
611 }
612 duhashtbl[i] = NULL;
613 }
614 }
615