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