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