quot.c revision 1.2 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 #ifndef lint
33 static char rcsid[] = "$Id: quot.c,v 1.2 1994/02/11 00:23:42 cgd Exp $";
34 #endif /* not lint */
35
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <pwd.h>
41 #include <sys/param.h>
42 #include <sys/mount.h>
43 #include <ufs/fs.h>
44 #include <ufs/quota.h>
45 #include <ufs/inode.h>
46
47 /* some flags of what to do: */
48 static char estimate;
49 static char count;
50 static char unused;
51 static int (*func)();
52 static long blocksize;
53 static char *header;
54 static int headerlen;
55
56 /*
57 * Original BSD quot doesn't round to number of frags/blocks,
58 * doesn't account for indirection blocks and gets it totally
59 * wrong if the size is a multiple of the blocksize.
60 * The new code always counts the number of 512 byte blocks
61 * instead of the number of kilobytes and converts them to
62 * kByte when done (on request).
63 */
64 #ifdef COMPAT
65 #define SIZE(n) (n)
66 #else
67 #define SIZE(n) (((n) * 512 + blocksize - 1)/blocksize)
68 #endif
69
70 #define INOCNT(fs) ((fs)->fs_ipg)
71 #define INOSZ(fs) (sizeof(struct dinode) * INOCNT(fs))
72
73 static struct dinode *get_inode(fd,super,ino)
74 struct fs *super;
75 ino_t ino;
76 {
77 static struct dinode *ip;
78 static ino_t last;
79
80 if (fd < 0) { /* flush cache */
81 if (ip) {
82 free(ip);
83 ip = 0;
84 }
85 return 0;
86 }
87
88 if (!ip || ino < last || ino >= last + INOCNT(super)) {
89 if (!ip
90 && !(ip = (struct dinode *)malloc(INOSZ(super)))) {
91 perror("allocate inodes");
92 exit(1);
93 }
94 last = (ino / INOCNT(super)) * INOCNT(super);
95 if (lseek(fd,itod(super,last) << super->fs_fshift,0) < 0
96 || read(fd,ip,INOSZ(super)) != INOSZ(super)) {
97 perror("read inodes");
98 exit(1);
99 }
100 }
101
102 return ip + ino % INOCNT(super);
103 }
104
105 #ifdef COMPAT
106 #define actualblocks(super,ip) ((ip)->di_blocks/2)
107 #else
108 #define actualblocks(super,ip) ((ip)->di_blocks)
109 #endif
110
111 static virtualblocks(super,ip)
112 struct fs *super;
113 struct dinode *ip;
114 {
115 register off_t nblk, sz;
116
117 sz = ip->di_size;
118 #ifdef COMPAT
119 if (lblkno(super,sz) >= NDADDR) {
120 nblk = blkroundup(super,sz);
121 if (sz == nblk)
122 nblk += super->fs_bsize;
123 }
124
125 return sz / 1024;
126
127 #else /* COMPAT */
128
129 if (lblkno(super,sz) >= NDADDR) {
130 nblk = blkroundup(super,sz);
131 sz = lblkno(super,nblk);
132 sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super);
133 while (sz > 0) {
134 nblk += sz * super->fs_bsize;
135 /* sz - 1 rounded up */
136 sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super);
137 }
138 } else
139 nblk = fragroundup(super,sz);
140
141 return nblk / 512;
142 #endif /* COMPAT */
143 }
144
145 static isfree(ip)
146 struct dinode *ip;
147 {
148 #ifdef COMPAT
149 return (ip->di_mode&IFMT) == 0;
150 #else /* COMPAT */
151
152 switch (ip->di_mode&IFMT) {
153 case IFIFO:
154 case IFLNK: /* should check FASTSYMLINK? */
155 case IFDIR:
156 case IFREG:
157 return 0;
158 default:
159 return 1;
160 }
161 #endif
162 }
163
164 static struct user {
165 uid_t uid;
166 char *name;
167 daddr_t space;
168 long count;
169 daddr_t spc30;
170 daddr_t spc60;
171 daddr_t spc90;
172 } *users;
173 static int nusers;
174
175 static inituser()
176 {
177 register i;
178 register struct user *usr;
179
180 if (!nusers) {
181 nusers = 8;
182 if (!(users =
183 (struct user *)calloc(nusers,sizeof(struct user)))) {
184 perror("allocate users");
185 exit(1);
186 }
187 } else {
188 for (usr = users, i = nusers; --i >= 0; usr++) {
189 usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
190 usr->count = 0;
191 }
192 }
193 }
194
195 static usrrehash()
196 {
197 register i;
198 register struct user *usr, *usrn;
199 struct user *svusr;
200
201 svusr = users;
202 nusers <<= 1;
203 if (!(users = (struct user *)calloc(nusers,sizeof(struct user)))) {
204 perror("allocate users");
205 exit(1);
206 }
207 for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
208 for (usrn = users + (usr->uid&(nusers - 1)); usrn->name;
209 usrn--) {
210 if (usrn <= users)
211 usrn = users + nusers;
212 }
213 *usrn = *usr;
214 }
215 }
216
217 static struct user *user(uid)
218 uid_t uid;
219 {
220 register struct user *usr;
221 register i;
222 struct passwd *pwd;
223
224 while (1) {
225 for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0;
226 usr--) {
227 if (!usr->name) {
228 usr->uid = uid;
229
230 if (!(pwd = getpwuid(uid))) {
231 if (usr->name = (char *)malloc(7))
232 sprintf(usr->name,"#%d",uid);
233 } else {
234 if (usr->name = (char *)
235 malloc(strlen(pwd->pw_name) + 1))
236 strcpy(usr->name,pwd->pw_name);
237 }
238 if (!usr->name) {
239 perror("allocate users");
240 exit(1);
241 }
242
243 return usr;
244
245 } else if (usr->uid == uid)
246 return usr;
247
248 if (usr <= users)
249 usr = users + nusers;
250 }
251 usrrehash();
252 }
253 }
254
255 static cmpusers(u1,u2)
256 struct user *u1, *u2;
257 {
258 return u2->space - u1->space;
259 }
260
261 #define sortusers(users) (qsort((users),nusers,sizeof(struct user), \
262 cmpusers))
263
264 static uses(uid,blks,act)
265 uid_t uid;
266 daddr_t blks;
267 time_t act;
268 {
269 static time_t today;
270 register struct user *usr;
271
272 if (!today)
273 time(&today);
274
275 usr = user(uid);
276 usr->count++;
277 usr->space += blks;
278
279 if (today - act > 90L * 24L * 60L * 60L)
280 usr->spc90 += blks;
281 if (today - act > 60L * 24L * 60L * 60L)
282 usr->spc60 += blks;
283 if (today - act > 30L * 24L * 60L * 60L)
284 usr->spc30 += blks;
285 }
286
287 #ifdef COMPAT
288 #define FSZCNT 500
289 #else
290 #define FSZCNT 512
291 #endif
292 struct fsizes {
293 struct fsizes *fsz_next;
294 daddr_t fsz_first, fsz_last;
295 ino_t fsz_count[FSZCNT];
296 daddr_t fsz_sz[FSZCNT];
297 } *fsizes;
298
299 static initfsizes()
300 {
301 register struct fsizes *fp;
302 register i;
303
304 for (fp = fsizes; fp; fp = fp->fsz_next) {
305 for (i = FSZCNT; --i >= 0;) {
306 fp->fsz_count[i] = 0;
307 fp->fsz_sz[i] = 0;
308 }
309 }
310 }
311
312 static dofsizes(fd,super,name)
313 struct fs *super;
314 char *name;
315 {
316 ino_t inode, maxino;
317 struct dinode *ip;
318 daddr_t sz, ksz;
319 struct fsizes *fp, **fsp;
320 register i;
321
322 maxino = super->fs_ncg * super->fs_ipg - 1;
323 #ifdef COMPAT
324 if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes)))) {
325 perror("alloc fsize structure");
326 exit(1);
327 }
328 #endif /* COMPAT */
329 for (inode = 0; inode < maxino; inode++) {
330 errno = 0;
331 if ((ip = get_inode(fd,super,inode))
332 #ifdef COMPAT
333 && ((ip->di_mode&IFMT) == IFREG
334 || (ip->di_mode&IFMT) == IFDIR)
335 #else /* COMPAT */
336 && !isfree(ip)
337 #endif /* COMPAT */
338 ) {
339 sz = estimate ? virtualblocks(super,ip) :
340 actualblocks(super,ip);
341 #ifdef COMPAT
342 if (sz >= FSZCNT) {
343 fsizes->fsz_count[FSZCNT-1]++;
344 fsizes->fsz_sz[FSZCNT-1] += sz;
345 } else {
346 fsizes->fsz_count[sz]++;
347 fsizes->fsz_sz[sz] += sz;
348 }
349 #else /* COMPAT */
350 ksz = SIZE(sz);
351 for (fsp = &fsizes; fp = *fsp; fsp = &fp->fsz_next) {
352 if (ksz < fp->fsz_last)
353 break;
354 }
355 if (!fp || ksz < fp->fsz_first) {
356 if (!(fp = (struct fsizes *)
357 malloc(sizeof(struct fsizes)))) {
358 perror("alloc fsize structure");
359 exit(1);
360 }
361 fp->fsz_next = *fsp;
362 *fsp = fp;
363 fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
364 fp->fsz_last = fp->fsz_first + FSZCNT;
365 for (i = FSZCNT; --i >= 0;) {
366 fp->fsz_count[i] = 0;
367 fp->fsz_sz[i] = 0;
368 }
369 }
370 fp->fsz_count[ksz % FSZCNT]++;
371 fp->fsz_sz[ksz % FSZCNT] += sz;
372 #endif /* COMPAT */
373 } else if (errno) {
374 perror(name);
375 exit(1);
376 }
377 }
378 sz = 0;
379 for (fp = fsizes; fp; fp = fp->fsz_next) {
380 for (i = 0; i < FSZCNT; i++) {
381 if (fp->fsz_count[i])
382 printf("%d\t%d\t%d\n",fp->fsz_first + i,
383 fp->fsz_count[i],
384 SIZE(sz += fp->fsz_sz[i]));
385 }
386 }
387 }
388
389 static douser(fd,super,name)
390 struct fs *super;
391 char *name;
392 {
393 ino_t inode, maxino;
394 struct user *usr, *usrs;
395 struct dinode *ip;
396 register n;
397
398 maxino = super->fs_ncg * super->fs_ipg - 1;
399 for (inode = 0; inode < maxino; inode++) {
400 errno = 0;
401 if ((ip = get_inode(fd,super,inode))
402 && !isfree(ip))
403 uses(ip->di_uid,
404 estimate ? virtualblocks(super,ip) :
405 actualblocks(super,ip),
406 ip->di_atime);
407 else if (errno) {
408 perror(name);
409 exit(1);
410 }
411 }
412 if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user)))) {
413 perror("allocate users");
414 exit(1);
415 }
416 bcopy(users,usrs,nusers * sizeof(struct user));
417 sortusers(usrs);
418 for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
419 printf("%5d",SIZE(usr->space));
420 if (count)
421 printf("\t%5d",usr->count);
422 printf("\t%-8s",usr->name);
423 if (unused)
424 printf("\t%5d\t%5d\t%5d",
425 SIZE(usr->spc30),
426 SIZE(usr->spc60),
427 SIZE(usr->spc90));
428 printf("\n");
429 }
430 free(usrs);
431 }
432
433 static donames(fd,super,name)
434 struct fs *super;
435 char *name;
436 {
437 int c;
438 ino_t inode, inode1;
439 ino_t maxino;
440 struct dinode *ip;
441
442 maxino = super->fs_ncg * super->fs_ipg - 1;
443 /* first skip the name of the filesystem */
444 while ((c = getchar()) != EOF && (c < '0' || c > '9'))
445 while ((c = getchar()) != EOF && c != '\n');
446 ungetc(c,stdin);
447 inode1 = -1;
448 while (scanf("%d",&inode) == 1) {
449 if (inode < 0 || inode > maxino) {
450 fprintf(stderr,"illegal inode %d\n",inode);
451 return;
452 }
453 errno = 0;
454 if ((ip = get_inode(fd,super,inode))
455 && !isfree(ip)) {
456 printf("%s\t",user(ip->di_uid)->name);
457 /* now skip whitespace */
458 while ((c = getchar()) == ' ' || c == '\t');
459 /* and print out the remainder of the input line */
460 while (c != EOF && c != '\n') {
461 putchar(c);
462 c = getchar();
463 }
464 putchar('\n');
465 inode1 = inode;
466 } else {
467 if (errno) {
468 perror(name);
469 exit(1);
470 }
471 /* skip this line */
472 while ((c = getchar()) != EOF && c != '\n');
473 }
474 if (c == EOF)
475 break;
476 }
477 }
478
479 static usage()
480 {
481 #ifdef COMPAT
482 fprintf(stderr,"Usage: quot [-nfcvha] [filesystem ...]\n");
483 #else /* COMPAT */
484 fprintf(stderr,"Usage: quot [ -acfhknv ] [ filesystem ... ]\n");
485 #endif /* COMPAT */
486 exit(1);
487 }
488
489 static char superblock[SBSIZE];
490
491 quot(name,mp)
492 char *name, *mp;
493 {
494 int fd;
495
496 get_inode(-1); /* flush cache */
497 inituser();
498 initfsizes();
499 if ((fd = open(name,0)) < 0
500 || lseek(fd,SBOFF,0) != SBOFF
501 || read(fd,superblock,SBSIZE) != SBSIZE) {
502 perror(name);
503 close(fd);
504 return;
505 }
506 if (((struct fs *)superblock)->fs_magic != FS_MAGIC) {
507 fprintf(stderr,"%s: not a BSD filesystem\n",name);
508 close(fd);
509 return;
510 }
511 printf("%s:",name);
512 if (mp)
513 printf(" (%s)",mp);
514 putchar('\n');
515 (*func)(fd,superblock,name);
516 close(fd);
517 }
518
519 int main(argc,argv)
520 char **argv;
521 {
522 int fd;
523 char all = 0;
524 FILE *fp;
525 struct statfs *mp;
526 char dev[MNAMELEN + 1];
527 char *nm;
528 int cnt;
529
530 func = douser;
531 #ifndef COMPAT
532 header = getbsize(&headerlen,&blocksize);
533 #endif
534 while (--argc > 0 && **++argv == '-') {
535 while (*++*argv) {
536 switch (**argv) {
537 case 'n':
538 func = donames;
539 break;
540 case 'c':
541 func = dofsizes;
542 break;
543 case 'a':
544 all = 1;
545 break;
546 case 'f':
547 count = 1;
548 break;
549 case 'h':
550 estimate = 1;
551 break;
552 #ifndef COMPAT
553 case 'k':
554 blocksize = 1024;
555 break;
556 #endif /* COMPAT */
557 case 'v':
558 unused = 1;
559 break;
560 default:
561 usage();
562 }
563 }
564 }
565 if (all) {
566 cnt = getmntinfo(&mp,MNT_NOWAIT);
567 for (; --cnt >= 0; mp++) {
568 if (mp->f_type == MOUNT_UFS) {
569 if (nm = strrchr(mp->f_mntfromname,'/')) {
570 sprintf(dev,"/dev/r%s",nm + 1);
571 nm = dev;
572 } else
573 nm = mp->f_mntfromname;
574 quot(nm,mp->f_mntonname);
575 }
576 }
577 }
578 while (--argc >= 0)
579 quot(*argv++,0);
580 return 0;
581 }
582