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