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