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