Home | History | Annotate | Line # | Download | only in quot
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