]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - usr.sbin/quot/quot.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / usr.sbin / quot / quot.c
1 /*
2  * Copyright (C) 1991, 1994 Wolfgang Solfrank.
3  * Copyright (C) 1991, 1994 TooLs GmbH.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. All advertising materials mentioning features or use of this software
15  *    must display the following acknowledgement:
16  *      This product includes software developed by TooLs GmbH.
17  * 4. The name of TooLs GmbH may not be used to endorse or promote products
18  *    derived from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
21  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
23  * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
26  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
27  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
28  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
29  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34
35 #include <sys/param.h>
36 #include <sys/stdint.h>
37 #include <sys/mount.h>
38 #include <sys/disklabel.h>
39 #include <sys/time.h>
40 #include <ufs/ufs/dinode.h>
41 #include <ufs/ffs/fs.h>
42
43 #include <err.h>
44 #include <fcntl.h>
45 #include <fstab.h>
46 #include <errno.h>
47 #include <paths.h>
48 #include <pwd.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <string.h>
52 #include <unistd.h>
53
54 /* some flags of what to do: */
55 static char estimate;
56 static char count;
57 static char unused;
58 static void (*func)(int, struct fs *, char *);
59 static long blocksize;
60 static char *header;
61 static int headerlen;
62
63 static union dinode *get_inode(int, struct fs *, ino_t);
64 static int      virtualblocks(struct fs *, union dinode *);
65 static int      isfree(struct fs *, union dinode *);
66 static void     inituser(void);
67 static void     usrrehash(void);
68 static struct user *user(uid_t);
69 static int      cmpusers(const void *, const void *);
70 static void     uses(uid_t, daddr_t, time_t);
71 static void     initfsizes(void);
72 static void     dofsizes(int, struct fs *, char *);
73 static void     douser(int, struct fs *, char *);
74 static void     donames(int, struct fs *, char *);
75 static void     usage(void);
76 static void     quot(char *, char *);
77
78 /*
79  * Original BSD quot doesn't round to number of frags/blocks,
80  * doesn't account for indirection blocks and gets it totally
81  * wrong if the size is a multiple of the blocksize.
82  * The new code always counts the number of 512 byte blocks
83  * instead of the number of kilobytes and converts them to
84  * kByte when done (on request).
85  *
86  * Due to the size of modern disks, we must cast intermediate
87  * values to 64 bits to prevent potential overflows.
88  */
89 #ifdef  COMPAT
90 #define SIZE(n) (n)
91 #else
92 #define SIZE(n) ((int)(((quad_t)(n) * 512 + blocksize - 1)/blocksize))
93 #endif
94
95 #define INOCNT(fs)      ((fs)->fs_ipg)
96 #define INOSZ(fs) \
97         (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \
98         sizeof(struct ufs2_dinode)) * INOCNT(fs))
99
100 union dinode {
101         struct ufs1_dinode dp1;
102         struct ufs2_dinode dp2;
103 };
104 #define DIP(fs, dp, field) \
105         (((fs)->fs_magic == FS_UFS1_MAGIC) ? \
106         (dp)->dp1.field : (dp)->dp2.field)
107
108 static union dinode *
109 get_inode(fd,super,ino)
110         int fd;
111         struct fs *super;
112         ino_t ino;
113 {
114         static caddr_t ipbuf;
115         static struct cg *cgp;
116         static ino_t last;
117         static int cg;
118         struct ufs2_dinode *di2;
119         
120         if (fd < 0) {           /* flush cache */
121                 if (ipbuf) {
122                         free(ipbuf);
123                         ipbuf = 0;
124                         if (super != NULL && super->fs_magic == FS_UFS2_MAGIC) {
125                                 free(cgp);
126                                 cgp = 0;
127                         }
128                 }
129                 return 0;
130         }
131         
132         if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
133                 if (super->fs_magic == FS_UFS2_MAGIC &&
134                     (!cgp || cg != ino_to_cg(super, ino))) {
135                         cg = ino_to_cg(super, ino);
136                         if (!cgp && !(cgp = malloc(super->fs_cgsize)))
137                                 errx(1, "allocate cg");
138                         if (lseek(fd, (off_t)cgtod(super, cg) << super->fs_fshift, 0) < 0)
139                                 err(1, "lseek cg");
140                         if (read(fd, cgp, super->fs_cgsize) != super->fs_cgsize)
141                                 err(1, "read cg");
142                         if (!cg_chkmagic(cgp))
143                                 errx(1, "cg has bad magic");
144                 }
145                 if (!ipbuf
146                     && !(ipbuf = malloc(INOSZ(super))))
147                         errx(1, "allocate inodes");
148                 last = (ino / INOCNT(super)) * INOCNT(super);
149                 if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0
150                     || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super))
151                         err(1, "read inodes");
152         }
153         
154         if (super->fs_magic == FS_UFS1_MAGIC)
155                 return ((union dinode *)
156                     &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]);
157         di2 = &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)];
158         /* If the inode is unused, it might be unallocated too, so zero it. */
159         if (isclr(cg_inosused(cgp), ino % super->fs_ipg))
160                 bzero(di2, sizeof (*di2));
161         return ((union dinode *)di2);
162 }
163
164 #ifdef  COMPAT
165 #define actualblocks(fs, dp)    (DIP(fs, dp, di_blocks) / 2)
166 #else
167 #define actualblocks(fs, dp)    DIP(fs, dp, di_blocks)
168 #endif
169
170 static int virtualblocks(super, dp)
171         struct fs *super;
172         union dinode *dp;
173 {
174         register off_t nblk, sz;
175         
176         sz = DIP(super, dp, di_size);
177 #ifdef  COMPAT
178         if (lblkno(super,sz) >= NDADDR) {
179                 nblk = blkroundup(super,sz);
180                 if (sz == nblk)
181                         nblk += super->fs_bsize;
182         }
183         
184         return sz / 1024;
185         
186 #else   /* COMPAT */
187         
188         if (lblkno(super,sz) >= NDADDR) {
189                 nblk = blkroundup(super,sz);
190                 sz = lblkno(super,nblk);
191                 sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super);
192                 while (sz > 0) {
193                         nblk += sz * super->fs_bsize;
194                         /* sz - 1 rounded up */
195                         sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super);
196                 }
197         } else
198                 nblk = fragroundup(super,sz);
199         
200         return nblk / 512;
201 #endif  /* COMPAT */
202 }
203
204 static int
205 isfree(super, dp)
206         struct fs *super;
207         union dinode *dp;
208 {
209 #ifdef  COMPAT
210         return (DIP(super, dp, di_mode) & IFMT) == 0;
211 #else   /* COMPAT */
212         
213         switch (DIP(super, dp, di_mode) & IFMT) {
214         case IFIFO:
215         case IFLNK:             /* should check FASTSYMLINK? */
216         case IFDIR:
217         case IFREG:
218                 return 0;
219         case IFCHR:
220         case IFBLK:
221         case IFSOCK:
222         case IFWHT:
223         case 0:
224                 return 1;
225         default:
226                 errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT);
227         }
228 #endif
229 }
230
231 static struct user {
232         uid_t uid;
233         char *name;
234         daddr_t space;
235         long count;
236         daddr_t spc30;
237         daddr_t spc60;
238         daddr_t spc90;
239 } *users;
240 static int nusers;
241
242 static void
243 inituser()
244 {
245         register int i;
246         register struct user *usr;
247         
248         if (!nusers) {
249                 nusers = 8;
250                 if (!(users =
251                     (struct user *)calloc(nusers,sizeof(struct user))))
252                         errx(1, "allocate users");
253         } else {
254                 for (usr = users, i = nusers; --i >= 0; usr++) {
255                         usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
256                         usr->count = 0;
257                 }
258         }
259 }
260
261 static void
262 usrrehash()
263 {
264         register int i;
265         register struct user *usr, *usrn;
266         struct user *svusr;
267         
268         svusr = users;
269         nusers <<= 1;
270         if (!(users = (struct user *)calloc(nusers,sizeof(struct user))))
271                 errx(1, "allocate users");
272         for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
273                 for (usrn = users + (usr->uid&(nusers - 1)); usrn->name;
274                     usrn--) {
275                         if (usrn <= users)
276                                 usrn = users + nusers;
277                 }
278                 *usrn = *usr;
279         }
280 }
281
282 static struct user *
283 user(uid)
284         uid_t uid;
285 {
286         register struct user *usr;
287         register int i;
288         struct passwd *pwd;
289         
290         while (1) {
291                 for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0;
292                     usr--) {
293                         if (!usr->name) {
294                                 usr->uid = uid;
295                                 
296                                 if (!(pwd = getpwuid(uid))) {
297                                         if ((usr->name = (char *)malloc(7)))
298                                                 sprintf(usr->name,"#%d",uid);
299                                 } else {
300                                         if ((usr->name = (char *)
301                                             malloc(strlen(pwd->pw_name) + 1)))
302                                                 strcpy(usr->name,pwd->pw_name);
303                                 }
304                                 if (!usr->name)
305                                         errx(1, "allocate users");
306                                 
307                                 return usr;
308                                 
309                         } else if (usr->uid == uid)
310                                 return usr;
311
312                         if (usr <= users)
313                                 usr = users + nusers;
314                 }
315                 usrrehash();
316         }
317 }
318
319 static int
320 cmpusers(v1,v2)
321         const void *v1, *v2;
322 {
323         const struct user *u1, *u2;
324         u1 = (const struct user *)v1;
325         u2 = (const struct user *)v2;
326
327         return u2->space - u1->space;
328 }
329
330 #define sortusers(users)        (qsort((users),nusers,sizeof(struct user), \
331                                     cmpusers))
332
333 static void
334 uses(uid,blks,act)
335         uid_t uid;
336         daddr_t blks;
337         time_t act;
338 {
339         static time_t today;
340         register struct user *usr;
341         
342         if (!today)
343                 time(&today);
344         
345         usr = user(uid);
346         usr->count++;
347         usr->space += blks;
348         
349         if (today - act > 90L * 24L * 60L * 60L)
350                 usr->spc90 += blks;
351         if (today - act > 60L * 24L * 60L * 60L)
352                 usr->spc60 += blks;
353         if (today - act > 30L * 24L * 60L * 60L)
354                 usr->spc30 += blks;
355 }
356
357 #ifdef  COMPAT
358 #define FSZCNT  500
359 #else
360 #define FSZCNT  512
361 #endif
362 struct fsizes {
363         struct fsizes *fsz_next;
364         daddr_t fsz_first, fsz_last;
365         ino_t fsz_count[FSZCNT];
366         daddr_t fsz_sz[FSZCNT];
367 } *fsizes;
368
369 static void
370 initfsizes()
371 {
372         register struct fsizes *fp;
373         register int i;
374         
375         for (fp = fsizes; fp; fp = fp->fsz_next) {
376                 for (i = FSZCNT; --i >= 0;) {
377                         fp->fsz_count[i] = 0;
378                         fp->fsz_sz[i] = 0;
379                 }
380         }
381 }
382
383 static void
384 dofsizes(fd, super, name)
385         int fd;
386         struct fs *super;
387         char *name;
388 {
389         ino_t inode, maxino;
390         union dinode *dp;
391         daddr_t sz, ksz;
392         struct fsizes *fp, **fsp;
393         register int i;
394         
395         maxino = super->fs_ncg * super->fs_ipg - 1;
396 #ifdef  COMPAT
397         if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes))))
398                 errx(1, "allocate fsize structure");
399 #endif  /* COMPAT */
400         for (inode = 0; inode < maxino; inode++) {
401                 errno = 0;
402                 if ((dp = get_inode(fd,super,inode))
403 #ifdef  COMPAT
404                     && ((DIP(super, dp, di_mode) & IFMT) == IFREG
405                         || (DIP(super, dp, di_mode) & IFMT) == IFDIR)
406 #else   /* COMPAT */
407                     && !isfree(super, dp)
408 #endif  /* COMPAT */
409                     ) {
410                         sz = estimate ? virtualblocks(super, dp) :
411                             actualblocks(super, dp);
412 #ifdef  COMPAT
413                         if (sz >= FSZCNT) {
414                                 fsizes->fsz_count[FSZCNT-1]++;
415                                 fsizes->fsz_sz[FSZCNT-1] += sz;
416                         } else {
417                                 fsizes->fsz_count[sz]++;
418                                 fsizes->fsz_sz[sz] += sz;
419                         }
420 #else   /* COMPAT */
421                         ksz = SIZE(sz);
422                         for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) {
423                                 if (ksz < fp->fsz_last)
424                                         break;
425                         }
426                         if (!fp || ksz < fp->fsz_first) {
427                                 if (!(fp = (struct fsizes *)
428                                     malloc(sizeof(struct fsizes))))
429                                         errx(1, "allocate fsize structure");
430                                 fp->fsz_next = *fsp;
431                                 *fsp = fp;
432                                 fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
433                                 fp->fsz_last = fp->fsz_first + FSZCNT;
434                                 for (i = FSZCNT; --i >= 0;) {
435                                         fp->fsz_count[i] = 0;
436                                         fp->fsz_sz[i] = 0;
437                                 }
438                         }
439                         fp->fsz_count[ksz % FSZCNT]++;
440                         fp->fsz_sz[ksz % FSZCNT] += sz;
441 #endif  /* COMPAT */
442                 } else if (errno) {
443                         err(1, "%s", name);
444                 }
445         }
446         sz = 0;
447         for (fp = fsizes; fp; fp = fp->fsz_next) {
448                 for (i = 0; i < FSZCNT; i++) {
449                         if (fp->fsz_count[i])
450                                 printf("%jd\t%jd\t%d\n",
451                                     (intmax_t)(fp->fsz_first + i),
452                                     (intmax_t)fp->fsz_count[i],
453                                     SIZE(sz += fp->fsz_sz[i]));
454                 }
455         }
456 }
457
458 static void
459 douser(fd, super, name)
460         int fd;
461         struct fs *super;
462         char *name;
463 {
464         ino_t inode, maxino;
465         struct user *usr, *usrs;
466         union dinode *dp;
467         register int n;
468         
469         maxino = super->fs_ncg * super->fs_ipg - 1;
470         for (inode = 0; inode < maxino; inode++) {
471                 errno = 0;
472                 if ((dp = get_inode(fd,super,inode))
473                     && !isfree(super, dp))
474                         uses(DIP(super, dp, di_uid),
475                             estimate ? virtualblocks(super, dp) :
476                                 actualblocks(super, dp),
477                             DIP(super, dp, di_atime));
478                 else if (errno) {
479                         err(1, "%s", name);
480                 }
481         }
482         if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user))))
483                 errx(1, "allocate users");
484         bcopy(users,usrs,nusers * sizeof(struct user));
485         sortusers(usrs);
486         for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
487                 printf("%5d",SIZE(usr->space));
488                 if (count)
489                         printf("\t%5ld",usr->count);
490                 printf("\t%-8s",usr->name);
491                 if (unused)
492                         printf("\t%5d\t%5d\t%5d",
493                                SIZE(usr->spc30),
494                                SIZE(usr->spc60),
495                                SIZE(usr->spc90));
496                 printf("\n");
497         }
498         free(usrs);
499 }
500
501 static void
502 donames(fd, super, name)
503         int fd;
504         struct fs *super;
505         char *name;
506 {
507         int c;
508         ino_t inode;
509         ino_t maxino;
510         union dinode *dp;
511         
512         maxino = super->fs_ncg * super->fs_ipg - 1;
513         /* first skip the name of the filesystem */
514         while ((c = getchar()) != EOF && (c < '0' || c > '9'))
515                 while ((c = getchar()) != EOF && c != '\n');
516         ungetc(c,stdin);
517         while (scanf("%u",&inode) == 1) {
518                 if (inode > maxino) {
519                         warnx("illegal inode %d",inode);
520                         return;
521                 }
522                 errno = 0;
523                 if ((dp = get_inode(fd,super,inode))
524                     && !isfree(super, dp)) {
525                         printf("%s\t",user(DIP(super, dp, di_uid))->name);
526                         /* now skip whitespace */
527                         while ((c = getchar()) == ' ' || c == '\t');
528                         /* and print out the remainder of the input line */
529                         while (c != EOF && c != '\n') {
530                                 putchar(c);
531                                 c = getchar();
532                         }
533                         putchar('\n');
534                 } else {
535                         if (errno) {
536                                 err(1, "%s", name);
537                         }
538                         /* skip this line */
539                         while ((c = getchar()) != EOF && c != '\n');
540                 }
541                 if (c == EOF)
542                         break;
543         }
544 }
545
546 static void
547 usage()
548 {
549 #ifdef  COMPAT
550         fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n");
551 #else   /* COMPAT */
552         fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n");
553 #endif  /* COMPAT */
554         exit(1);
555 }
556
557 /*
558  * Possible superblock locations ordered from most to least likely.
559  */
560 static int sblock_try[] = SBLOCKSEARCH;
561 static char superblock[SBLOCKSIZE];
562
563 void
564 quot(name,mp)
565         char *name, *mp;
566 {
567         int i, fd;
568         struct fs *fs;
569         
570         get_inode(-1, NULL, 0);         /* flush cache */
571         inituser();
572         initfsizes();
573         if ((fd = open(name,0)) < 0) {
574                 warn("%s", name);
575                 close(fd);
576                 return;
577         }
578         for (i = 0; sblock_try[i] != -1; i++) {
579                 if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) {
580                         close(fd);
581                         return;
582                 }
583                 if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) {
584                         close(fd);
585                         return;
586                 }
587                 fs = (struct fs *)superblock;
588                 if ((fs->fs_magic == FS_UFS1_MAGIC ||
589                      (fs->fs_magic == FS_UFS2_MAGIC &&
590                       fs->fs_sblockloc == sblock_try[i])) &&
591                     fs->fs_bsize <= MAXBSIZE &&
592                     fs->fs_bsize >= sizeof(struct fs))
593                         break;
594         }
595         if (sblock_try[i] == -1) {
596                 warnx("%s: not a BSD filesystem",name);
597                 close(fd);
598                 return;
599         }
600         printf("%s:",name);
601         if (mp)
602                 printf(" (%s)",mp);
603         putchar('\n');
604         (*func)(fd, fs, name);
605         close(fd);
606 }
607
608 int
609 main(argc,argv)
610         int argc;
611         char **argv;
612 {
613         char all = 0;
614         struct statfs *mp;
615         struct fstab *fs;
616         char dev[MNAMELEN + 1];
617         char *nm;
618         int cnt;
619         
620         func = douser;
621 #ifndef COMPAT
622         header = getbsize(&headerlen,&blocksize);
623 #endif
624         while (--argc > 0 && **++argv == '-') {
625                 while (*++*argv) {
626                         switch (**argv) {
627                         case 'n':
628                                 func = donames;
629                                 break;
630                         case 'c':
631                                 func = dofsizes;
632                                 break;
633                         case 'a':
634                                 all = 1;
635                                 break;
636                         case 'f':
637                                 count = 1;
638                                 break;
639                         case 'h':
640                                 estimate = 1;
641                                 break;
642 #ifndef COMPAT
643                         case 'k':
644                                 blocksize = 1024;
645                                 break;
646 #endif  /* COMPAT */
647                         case 'v':
648                                 unused = 1;
649                                 break;
650                         default:
651                                 usage();
652                         }
653                 }
654         }
655         if (all) {
656                 cnt = getmntinfo(&mp,MNT_NOWAIT);
657                 for (; --cnt >= 0; mp++) {
658                         if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) {
659                                 if ((nm = strrchr(mp->f_mntfromname,'/'))) {
660                                         sprintf(dev,"%s%s",_PATH_DEV,nm + 1);
661                                         nm = dev;
662                                 } else
663                                         nm = mp->f_mntfromname;
664                                 quot(nm,mp->f_mntonname);
665                         }
666                 }
667         }
668         while (--argc >= 0) {
669                 if ((fs = getfsfile(*argv)) != NULL)
670                         quot(fs->fs_spec, 0);
671                 else
672                         quot(*argv,0);
673                 argv++;
674         }
675         return 0;
676 }