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