Home | History | Annotate | Line # | Download | only in quot
quot.c revision 1.7
      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.7 1995/11/28 19:43:42 jtc 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_FFS, 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