xref: /illumos-gate/usr/src/cmd/fs.d/ufs/quot/quot.c (revision fec509a05ddbf645268fe2e537314def7d1b67c8)
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
115 usage()
116 {
117 	(void) fprintf(stderr, "ufs usage: quot [-nfcvha] [filesystem ...]\n");
118 }
119 
120 int
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
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
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
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
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
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
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
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 *
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 *
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
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
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