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