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
usage()113 usage()
114 {
115 (void) fprintf(stderr, "ufs usage: quot [-nfcvha] [filesystem ...]\n");
116 }
117
118 int
main(int argc,char * argv[])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
quotall()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
check(char * file,char * fsdir)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
qacct(struct dinode * ip)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
bread(int fd,diskaddr_t bno,char * buf,int cnt)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
qcmp(const void * arg1,const void * arg2)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
report()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
getdev(char ** devpp)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 *
lookup(uid_t uid)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 *
adduid(uid_t uid)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
sortprep()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
cleanup()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