]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/quot/quot.c
This commit was generated by cvs2svn to compensate for changes in r145516,
[FreeBSD/FreeBSD.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 ino_t last;
116         
117         if (fd < 0) {           /* flush cache */
118                 if (ipbuf) {
119                         free(ipbuf);
120                         ipbuf = 0;
121                 }
122                 return 0;
123         }
124         
125         if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
126                 if (!ipbuf
127                     && !(ipbuf = malloc(INOSZ(super))))
128                         errx(1, "allocate inodes");
129                 last = (ino / INOCNT(super)) * INOCNT(super);
130                 if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0
131                     || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super))
132                         err(1, "read inodes");
133         }
134         
135         if (super->fs_magic == FS_UFS1_MAGIC)
136                 return ((union dinode *)
137                     &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]);
138         return ((union dinode *)
139             &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)]);
140 }
141
142 #ifdef  COMPAT
143 #define actualblocks(fs, dp)    (DIP(fs, dp, di_blocks) / 2)
144 #else
145 #define actualblocks(fs, dp)    DIP(fs, dp, di_blocks)
146 #endif
147
148 static int virtualblocks(super, dp)
149         struct fs *super;
150         union dinode *dp;
151 {
152         register off_t nblk, sz;
153         
154         sz = DIP(super, dp, di_size);
155 #ifdef  COMPAT
156         if (lblkno(super,sz) >= NDADDR) {
157                 nblk = blkroundup(super,sz);
158                 if (sz == nblk)
159                         nblk += super->fs_bsize;
160         }
161         
162         return sz / 1024;
163         
164 #else   /* COMPAT */
165         
166         if (lblkno(super,sz) >= NDADDR) {
167                 nblk = blkroundup(super,sz);
168                 sz = lblkno(super,nblk);
169                 sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super);
170                 while (sz > 0) {
171                         nblk += sz * super->fs_bsize;
172                         /* sz - 1 rounded up */
173                         sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super);
174                 }
175         } else
176                 nblk = fragroundup(super,sz);
177         
178         return nblk / 512;
179 #endif  /* COMPAT */
180 }
181
182 static int
183 isfree(super, dp)
184         struct fs *super;
185         union dinode *dp;
186 {
187 #ifdef  COMPAT
188         return (DIP(super, dp, di_mode) & IFMT) == 0;
189 #else   /* COMPAT */
190         
191         switch (DIP(super, dp, di_mode) & IFMT) {
192         case IFIFO:
193         case IFLNK:             /* should check FASTSYMLINK? */
194         case IFDIR:
195         case IFREG:
196                 return 0;
197         default:
198                 return 1;
199         }
200 #endif
201 }
202
203 static struct user {
204         uid_t uid;
205         char *name;
206         daddr_t space;
207         long count;
208         daddr_t spc30;
209         daddr_t spc60;
210         daddr_t spc90;
211 } *users;
212 static int nusers;
213
214 static void
215 inituser()
216 {
217         register int i;
218         register struct user *usr;
219         
220         if (!nusers) {
221                 nusers = 8;
222                 if (!(users =
223                     (struct user *)calloc(nusers,sizeof(struct user))))
224                         errx(1, "allocate users");
225         } else {
226                 for (usr = users, i = nusers; --i >= 0; usr++) {
227                         usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
228                         usr->count = 0;
229                 }
230         }
231 }
232
233 static void
234 usrrehash()
235 {
236         register int i;
237         register struct user *usr, *usrn;
238         struct user *svusr;
239         
240         svusr = users;
241         nusers <<= 1;
242         if (!(users = (struct user *)calloc(nusers,sizeof(struct user))))
243                 errx(1, "allocate users");
244         for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
245                 for (usrn = users + (usr->uid&(nusers - 1)); usrn->name;
246                     usrn--) {
247                         if (usrn <= users)
248                                 usrn = users + nusers;
249                 }
250                 *usrn = *usr;
251         }
252 }
253
254 static struct user *
255 user(uid)
256         uid_t uid;
257 {
258         register struct user *usr;
259         register int i;
260         struct passwd *pwd;
261         
262         while (1) {
263                 for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0;
264                     usr--) {
265                         if (!usr->name) {
266                                 usr->uid = uid;
267                                 
268                                 if (!(pwd = getpwuid(uid))) {
269                                         if ((usr->name = (char *)malloc(7)))
270                                                 sprintf(usr->name,"#%d",uid);
271                                 } else {
272                                         if ((usr->name = (char *)
273                                             malloc(strlen(pwd->pw_name) + 1)))
274                                                 strcpy(usr->name,pwd->pw_name);
275                                 }
276                                 if (!usr->name)
277                                         errx(1, "allocate users");
278                                 
279                                 return usr;
280                                 
281                         } else if (usr->uid == uid)
282                                 return usr;
283
284                         if (usr <= users)
285                                 usr = users + nusers;
286                 }
287                 usrrehash();
288         }
289 }
290
291 static int
292 cmpusers(v1,v2)
293         const void *v1, *v2;
294 {
295         const struct user *u1, *u2;
296         u1 = (const struct user *)v1;
297         u2 = (const struct user *)v2;
298
299         return u2->space - u1->space;
300 }
301
302 #define sortusers(users)        (qsort((users),nusers,sizeof(struct user), \
303                                     cmpusers))
304
305 static void
306 uses(uid,blks,act)
307         uid_t uid;
308         daddr_t blks;
309         time_t act;
310 {
311         static time_t today;
312         register struct user *usr;
313         
314         if (!today)
315                 time(&today);
316         
317         usr = user(uid);
318         usr->count++;
319         usr->space += blks;
320         
321         if (today - act > 90L * 24L * 60L * 60L)
322                 usr->spc90 += blks;
323         if (today - act > 60L * 24L * 60L * 60L)
324                 usr->spc60 += blks;
325         if (today - act > 30L * 24L * 60L * 60L)
326                 usr->spc30 += blks;
327 }
328
329 #ifdef  COMPAT
330 #define FSZCNT  500
331 #else
332 #define FSZCNT  512
333 #endif
334 struct fsizes {
335         struct fsizes *fsz_next;
336         daddr_t fsz_first, fsz_last;
337         ino_t fsz_count[FSZCNT];
338         daddr_t fsz_sz[FSZCNT];
339 } *fsizes;
340
341 static void
342 initfsizes()
343 {
344         register struct fsizes *fp;
345         register int i;
346         
347         for (fp = fsizes; fp; fp = fp->fsz_next) {
348                 for (i = FSZCNT; --i >= 0;) {
349                         fp->fsz_count[i] = 0;
350                         fp->fsz_sz[i] = 0;
351                 }
352         }
353 }
354
355 static void
356 dofsizes(fd, super, name)
357         int fd;
358         struct fs *super;
359         char *name;
360 {
361         ino_t inode, maxino;
362         union dinode *dp;
363         daddr_t sz, ksz;
364         struct fsizes *fp, **fsp;
365         register int i;
366         
367         maxino = super->fs_ncg * super->fs_ipg - 1;
368 #ifdef  COMPAT
369         if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes))))
370                 errx(1, "allocate fsize structure");
371 #endif  /* COMPAT */
372         for (inode = 0; inode < maxino; inode++) {
373                 errno = 0;
374                 if ((dp = get_inode(fd,super,inode))
375 #ifdef  COMPAT
376                     && ((DIP(super, dp, di_mode) & IFMT) == IFREG
377                         || (DIP(super, dp, di_mode) & IFMT) == IFDIR)
378 #else   /* COMPAT */
379                     && !isfree(super, dp)
380 #endif  /* COMPAT */
381                     ) {
382                         sz = estimate ? virtualblocks(super, dp) :
383                             actualblocks(super, dp);
384 #ifdef  COMPAT
385                         if (sz >= FSZCNT) {
386                                 fsizes->fsz_count[FSZCNT-1]++;
387                                 fsizes->fsz_sz[FSZCNT-1] += sz;
388                         } else {
389                                 fsizes->fsz_count[sz]++;
390                                 fsizes->fsz_sz[sz] += sz;
391                         }
392 #else   /* COMPAT */
393                         ksz = SIZE(sz);
394                         for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) {
395                                 if (ksz < fp->fsz_last)
396                                         break;
397                         }
398                         if (!fp || ksz < fp->fsz_first) {
399                                 if (!(fp = (struct fsizes *)
400                                     malloc(sizeof(struct fsizes))))
401                                         errx(1, "allocate fsize structure");
402                                 fp->fsz_next = *fsp;
403                                 *fsp = fp;
404                                 fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
405                                 fp->fsz_last = fp->fsz_first + FSZCNT;
406                                 for (i = FSZCNT; --i >= 0;) {
407                                         fp->fsz_count[i] = 0;
408                                         fp->fsz_sz[i] = 0;
409                                 }
410                         }
411                         fp->fsz_count[ksz % FSZCNT]++;
412                         fp->fsz_sz[ksz % FSZCNT] += sz;
413 #endif  /* COMPAT */
414                 } else if (errno) {
415                         err(1, "%s", name);
416                 }
417         }
418         sz = 0;
419         for (fp = fsizes; fp; fp = fp->fsz_next) {
420                 for (i = 0; i < FSZCNT; i++) {
421                         if (fp->fsz_count[i])
422                                 printf("%jd\t%jd\t%d\n",
423                                     (intmax_t)(fp->fsz_first + i),
424                                     (intmax_t)fp->fsz_count[i],
425                                     SIZE(sz += fp->fsz_sz[i]));
426                 }
427         }
428 }
429
430 static void
431 douser(fd, super, name)
432         int fd;
433         struct fs *super;
434         char *name;
435 {
436         ino_t inode, maxino;
437         struct user *usr, *usrs;
438         union dinode *dp;
439         register int n;
440         
441         maxino = super->fs_ncg * super->fs_ipg - 1;
442         for (inode = 0; inode < maxino; inode++) {
443                 errno = 0;
444                 if ((dp = get_inode(fd,super,inode))
445                     && !isfree(super, dp))
446                         uses(DIP(super, dp, di_uid),
447                             estimate ? virtualblocks(super, dp) :
448                                 actualblocks(super, dp),
449                             DIP(super, dp, di_atime));
450                 else if (errno) {
451                         err(1, "%s", name);
452                 }
453         }
454         if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user))))
455                 errx(1, "allocate users");
456         bcopy(users,usrs,nusers * sizeof(struct user));
457         sortusers(usrs);
458         for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
459                 printf("%5d",SIZE(usr->space));
460                 if (count)
461                         printf("\t%5ld",usr->count);
462                 printf("\t%-8s",usr->name);
463                 if (unused)
464                         printf("\t%5d\t%5d\t%5d",
465                                SIZE(usr->spc30),
466                                SIZE(usr->spc60),
467                                SIZE(usr->spc90));
468                 printf("\n");
469         }
470         free(usrs);
471 }
472
473 static void
474 donames(fd, super, name)
475         int fd;
476         struct fs *super;
477         char *name;
478 {
479         int c;
480         ino_t inode;
481         ino_t maxino;
482         union dinode *dp;
483         
484         maxino = super->fs_ncg * super->fs_ipg - 1;
485         /* first skip the name of the filesystem */
486         while ((c = getchar()) != EOF && (c < '0' || c > '9'))
487                 while ((c = getchar()) != EOF && c != '\n');
488         ungetc(c,stdin);
489         while (scanf("%u",&inode) == 1) {
490                 if (inode > maxino) {
491                         warnx("illegal inode %d",inode);
492                         return;
493                 }
494                 errno = 0;
495                 if ((dp = get_inode(fd,super,inode))
496                     && !isfree(super, dp)) {
497                         printf("%s\t",user(DIP(super, dp, di_uid))->name);
498                         /* now skip whitespace */
499                         while ((c = getchar()) == ' ' || c == '\t');
500                         /* and print out the remainder of the input line */
501                         while (c != EOF && c != '\n') {
502                                 putchar(c);
503                                 c = getchar();
504                         }
505                         putchar('\n');
506                 } else {
507                         if (errno) {
508                                 err(1, "%s", name);
509                         }
510                         /* skip this line */
511                         while ((c = getchar()) != EOF && c != '\n');
512                 }
513                 if (c == EOF)
514                         break;
515         }
516 }
517
518 static void
519 usage()
520 {
521 #ifdef  COMPAT
522         fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n");
523 #else   /* COMPAT */
524         fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n");
525 #endif  /* COMPAT */
526         exit(1);
527 }
528
529 /*
530  * Possible superblock locations ordered from most to least likely.
531  */
532 static int sblock_try[] = SBLOCKSEARCH;
533 static char superblock[SBLOCKSIZE];
534
535 void
536 quot(name,mp)
537         char *name, *mp;
538 {
539         int i, fd;
540         struct fs *fs;
541         
542         get_inode(-1, NULL, 0);         /* flush cache */
543         inituser();
544         initfsizes();
545         if ((fd = open(name,0)) < 0) {
546                 warn("%s", name);
547                 close(fd);
548                 return;
549         }
550         for (i = 0; sblock_try[i] != -1; i++) {
551                 if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) {
552                         close(fd);
553                         return;
554                 }
555                 if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) {
556                         close(fd);
557                         return;
558                 }
559                 fs = (struct fs *)superblock;
560                 if ((fs->fs_magic == FS_UFS1_MAGIC ||
561                      (fs->fs_magic == FS_UFS2_MAGIC &&
562                       fs->fs_sblockloc == sblock_try[i])) &&
563                     fs->fs_bsize <= MAXBSIZE &&
564                     fs->fs_bsize >= sizeof(struct fs))
565                         break;
566         }
567         if (sblock_try[i] == -1) {
568                 warnx("%s: not a BSD filesystem",name);
569                 close(fd);
570                 return;
571         }
572         printf("%s:",name);
573         if (mp)
574                 printf(" (%s)",mp);
575         putchar('\n');
576         (*func)(fd, fs, name);
577         close(fd);
578 }
579
580 int
581 main(argc,argv)
582         int argc;
583         char **argv;
584 {
585         char all = 0;
586         struct statfs *mp;
587         struct fstab *fs;
588         char dev[MNAMELEN + 1];
589         char *nm;
590         int cnt;
591         
592         func = douser;
593 #ifndef COMPAT
594         header = getbsize(&headerlen,&blocksize);
595 #endif
596         while (--argc > 0 && **++argv == '-') {
597                 while (*++*argv) {
598                         switch (**argv) {
599                         case 'n':
600                                 func = donames;
601                                 break;
602                         case 'c':
603                                 func = dofsizes;
604                                 break;
605                         case 'a':
606                                 all = 1;
607                                 break;
608                         case 'f':
609                                 count = 1;
610                                 break;
611                         case 'h':
612                                 estimate = 1;
613                                 break;
614 #ifndef COMPAT
615                         case 'k':
616                                 blocksize = 1024;
617                                 break;
618 #endif  /* COMPAT */
619                         case 'v':
620                                 unused = 1;
621                                 break;
622                         default:
623                                 usage();
624                         }
625                 }
626         }
627         if (all) {
628                 cnt = getmntinfo(&mp,MNT_NOWAIT);
629                 for (; --cnt >= 0; mp++) {
630                         if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN)) {
631                                 if ((nm = strrchr(mp->f_mntfromname,'/'))) {
632                                         sprintf(dev,"%s%s",_PATH_DEV,nm + 1);
633                                         nm = dev;
634                                 } else
635                                         nm = mp->f_mntfromname;
636                                 quot(nm,mp->f_mntonname);
637                         }
638                 }
639         }
640         while (--argc >= 0) {
641                 if ((fs = getfsfile(*argv)) != NULL)
642                         quot(fs->fs_spec, 0);
643                 else
644                         quot(*argv,0);
645                 argv++;
646         }
647         return 0;
648 }