GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/quot/quot.c Lines: 0 279 0.0 %
Date: 2017-11-07 Branches: 0 285 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: quot.c,v 1.30 2017/09/07 03:24:09 tedu Exp $	*/
2
3
/*
4
 * Copyright (C) 1991, 1994 Wolfgang Solfrank.
5
 * Copyright (C) 1991, 1994 TooLs GmbH.
6
 * All rights reserved.
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 * 3. All advertising materials mentioning features or use of this software
17
 *    must display the following acknowledgement:
18
 *	This product includes software developed by TooLs GmbH.
19
 * 4. The name of TooLs GmbH may not be used to endorse or promote products
20
 *    derived from this software without specific prior written permission.
21
 *
22
 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
23
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
24
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
25
 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
 */
33
34
#include <sys/param.h>	/* DEV_BSIZE MAXBSIZE */
35
#include <sys/mount.h>
36
#include <sys/time.h>
37
#include <ufs/ufs/dinode.h>
38
#include <ufs/ffs/fs.h>
39
40
#include <stdio.h>
41
#include <stdlib.h>
42
#include <string.h>
43
#include <err.h>
44
#include <errno.h>
45
#include <fcntl.h>
46
#include <paths.h>
47
#include <pwd.h>
48
#include <unistd.h>
49
50
/* some flags of what to do: */
51
static char estimate;
52
static char count;
53
static char unused;
54
static void (*func)(int, struct fs *, char *);
55
static int cmpusers(const void *, const void *);
56
void	quot(char *, char *);
57
static long blocksize;
58
static char *header;
59
static int headerlen;
60
61
#define	SIZE(n)	(howmany(((off_t)(n)) * DEV_BSIZE, blocksize))
62
63
#define	INOCNT(fs)	((fs)->fs_ipg)
64
#define	INOSZ(fs)	(((fs)->fs_magic == FS_UFS1_MAGIC ? \
65
			    sizeof(struct ufs1_dinode) : \
66
			    sizeof(struct ufs2_dinode)) * INOCNT(fs))
67
68
union dinode {
69
	struct ufs1_dinode dp1;
70
	struct ufs2_dinode dp2;
71
};
72
#define	DIP(fs, dp, field) \
73
	(((fs)->fs_magic == FS_UFS1_MAGIC) ? \
74
	(dp)->dp1.field : (dp)->dp2.field)
75
76
static union dinode *
77
get_inode(int fd, struct fs *super, ino_t ino)
78
{
79
	static caddr_t ipbuf;
80
	static struct cg *cgp;
81
	static ino_t last;
82
	static int cg;
83
	struct ufs2_dinode *di2;
84
85
	if (fd < 0) {		/* flush cache */
86
		if (ipbuf) {
87
			free(ipbuf);
88
			ipbuf = NULL;
89
			if (super != NULL && super->fs_magic == FS_UFS2_MAGIC) {
90
				free(cgp);
91
				cgp = NULL;
92
			}
93
		}
94
		return 0;
95
	}
96
97
	if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
98
		if (super->fs_magic == FS_UFS2_MAGIC &&
99
		    (!cgp || cg != ino_to_cg(super, ino))) {
100
			cg = ino_to_cg(super, ino);
101
			if (!cgp && !(cgp = malloc(super->fs_cgsize)))
102
				errx(1, "allocate cg");
103
			if (pread(fd, cgp, super->fs_cgsize,
104
			    (off_t)cgtod(super, cg) << super->fs_fshift)
105
			    != super->fs_cgsize)
106
			if (read(fd, cgp, super->fs_cgsize) != super->fs_cgsize)
107
				err(1, "read cg");
108
			if (!cg_chkmagic(cgp))
109
				errx(1, "cg has bad magic");
110
		}
111
		if (!ipbuf && !(ipbuf = malloc(INOSZ(super))))
112
			err(1, "allocate inodes");
113
		last = (ino / INOCNT(super)) * INOCNT(super);
114
		if (lseek(fd, (off_t)ino_to_fsba(super, last)
115
		    << super->fs_fshift, SEEK_SET) < 0 ||
116
		    read(fd, ipbuf, INOSZ(super)) != INOSZ(super)) {
117
			err(1, "read inodes");
118
		}
119
	}
120
121
	if (super->fs_magic == FS_UFS1_MAGIC)
122
		return ((union dinode *)
123
		    &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]);
124
	di2 = &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)];
125
	/* If the inode is unused, it might be unallocated too, so zero it. */
126
	if (isclr(cg_inosused(cgp), ino % super->fs_ipg))
127
		memset(di2, 0, sizeof(*di2));
128
	return ((union dinode *)di2);
129
}
130
131
#define	actualblocks(fs, ip)	DIP(fs, dp, di_blocks)
132
133
static int
134
virtualblocks(struct fs *super, union dinode *dp)
135
{
136
	off_t nblk, sz;
137
138
	sz = DIP(super, dp, di_size);
139
140
	if (lblkno(super, sz) >= NDADDR) {
141
		nblk = blkroundup(super, sz);
142
		sz = lblkno(super, nblk);
143
		sz = howmany(sz - NDADDR, NINDIR(super));
144
		while (sz > 0) {
145
			nblk += sz * super->fs_bsize;
146
			/* One block on this level is in the inode itself */
147
			sz = howmany(sz - 1, NINDIR(super));
148
		}
149
	} else
150
		nblk = fragroundup(super, sz);
151
152
	return nblk / DEV_BSIZE;
153
}
154
155
static int
156
isfree(struct fs *super, union dinode *dp)
157
{
158
	switch (DIP(super, dp, di_mode) & IFMT) {
159
	case IFIFO:
160
	case IFLNK:		/* should check FASTSYMLINK? */
161
	case IFDIR:
162
	case IFREG:
163
		return 0;
164
	case IFCHR:
165
	case IFBLK:
166
	case IFSOCK:
167
	case 0:
168
		return 1;
169
	default:
170
		errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT);
171
	}
172
}
173
174
static struct user {
175
	uid_t uid;
176
	char *name;
177
	daddr_t space;
178
	long count;
179
	daddr_t spc30;
180
	daddr_t spc60;
181
	daddr_t spc90;
182
} *users;
183
static int nusers;
184
185
static void
186
inituser(void)
187
{
188
	int i;
189
	struct user *usr;
190
191
	if (!nusers) {
192
		nusers = 8;
193
		if (!(users = calloc(nusers, sizeof(struct user)))) {
194
			err(1, "allocate users");
195
		}
196
	} else {
197
		for (usr = users, i = nusers; --i >= 0; usr++) {
198
			usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
199
			usr->count = 0;
200
		}
201
	}
202
}
203
204
static void
205
usrrehash(void)
206
{
207
	int i;
208
	struct user *usr, *usrn;
209
	struct user *svusr;
210
211
	svusr = users;
212
	nusers <<= 1;
213
	if (!(users = calloc(nusers, sizeof(struct user))))
214
		err(1, "allocate users");
215
	for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
216
		for (usrn = users + (usr->uid&(nusers - 1));
217
		     usrn->name;
218
		    usrn--) {
219
			if (usrn <= users)
220
				usrn = users + nusers;
221
		}
222
		*usrn = *usr;
223
	}
224
}
225
226
static struct user *
227
user(uid_t uid)
228
{
229
	int i;
230
	struct passwd *pwd;
231
	struct user *usr;
232
233
	while (1) {
234
		for (usr = users + (uid&(nusers - 1)), i = nusers;
235
		     --i >= 0;
236
		    usr--) {
237
			if (!usr->name) {
238
				usr->uid = uid;
239
240
				if (!(pwd = getpwuid(uid)))
241
					asprintf(&usr->name, "#%u", uid);
242
				else
243
					usr->name = strdup(pwd->pw_name);
244
				if (!usr->name)
245
					err(1, "allocate users");
246
				return usr;
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 int
258
cmpusers(const void *v1, const void *v2)
259
{
260
	const struct user *u1 = v1, *u2 = v2;
261
262
	return u2->space - u1->space;
263
}
264
265
#define	sortusers(users)	(qsort((users), nusers, sizeof(struct user), \
266
				    cmpusers))
267
268
static void
269
uses(uid_t uid, daddr_t blks, time_t act)
270
{
271
	static time_t today;
272
	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
#define	FSZCNT	512
290
struct fsizes {
291
	struct fsizes *fsz_next;
292
	daddr_t fsz_first, fsz_last;
293
	ino_t fsz_count[FSZCNT];
294
	daddr_t fsz_sz[FSZCNT];
295
} *fsizes;
296
297
static void
298
initfsizes(void)
299
{
300
	struct fsizes *fp;
301
	int i;
302
303
	for (fp = fsizes; fp; fp = fp->fsz_next) {
304
		for (i = FSZCNT; --i >= 0;) {
305
			fp->fsz_count[i] = 0;
306
			fp->fsz_sz[i] = 0;
307
		}
308
	}
309
}
310
311
static void
312
dofsizes(int fd, struct fs *super, char *name)
313
{
314
	ino_t inode, maxino;
315
	union dinode *dp;
316
	daddr_t sz, ksz;
317
	struct fsizes *fp, **fsp;
318
	int i;
319
320
	maxino = super->fs_ncg * super->fs_ipg - 1;
321
	for (inode = 0; inode < maxino; inode++) {
322
		errno = 0;
323
		if ((dp = get_inode(fd, super, inode))
324
		    && !isfree(super, dp)
325
		    ) {
326
			sz = estimate ? virtualblocks(super, dp) :
327
			    actualblocks(super, dp);
328
			ksz = SIZE(sz);
329
			for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) {
330
				if (ksz < fp->fsz_last)
331
					break;
332
			}
333
			if (!fp || ksz < fp->fsz_first) {
334
				if (!(fp = (struct fsizes *)
335
				    malloc(sizeof(struct fsizes)))) {
336
					err(1, "alloc fsize structure");
337
				}
338
				fp->fsz_next = *fsp;
339
				*fsp = fp;
340
				fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
341
				fp->fsz_last = fp->fsz_first + FSZCNT;
342
				for (i = FSZCNT; --i >= 0;) {
343
					fp->fsz_count[i] = 0;
344
					fp->fsz_sz[i] = 0;
345
				}
346
			}
347
			fp->fsz_count[ksz % FSZCNT]++;
348
			fp->fsz_sz[ksz % FSZCNT] += sz;
349
		} else if (errno)
350
			err(1, "%s", name);
351
	}
352
	sz = 0;
353
	for (fp = fsizes; fp; fp = fp->fsz_next) {
354
		for (i = 0; i < FSZCNT; i++) {
355
			if (fp->fsz_count[i])
356
				printf("%lld\t%llu\t%lld\n",
357
				    (long long)fp->fsz_first + i,
358
				    (unsigned long long)fp->fsz_count[i],
359
				    SIZE(sz += fp->fsz_sz[i]));
360
		}
361
	}
362
}
363
364
static void
365
douser(int fd, struct fs *super, char *name)
366
{
367
	ino_t inode, maxino;
368
	struct user *usr, *usrs;
369
	union dinode *dp;
370
	int n;
371
372
	maxino = super->fs_ncg * super->fs_ipg - 1;
373
	for (inode = 0; inode < maxino; inode++) {
374
		errno = 0;
375
		if ((dp = get_inode(fd,super,inode))
376
		    && !isfree(super, dp))
377
			uses(DIP(super, dp, di_uid),
378
			    estimate ? virtualblocks(super, dp) :
379
				actualblocks(super, dp),
380
			    DIP(super, dp, di_atime));
381
		else if (errno)
382
			err(1, "%s", name);
383
	}
384
	if (!(usrs = calloc(nusers, sizeof(struct user))))
385
		err(1, "allocate users");
386
	memcpy(usrs, users, nusers * sizeof(struct user));
387
	sortusers(usrs);
388
	for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
389
		printf("%14lld", SIZE(usr->space));
390
		if (count)
391
			printf("\t%5ld", usr->count);
392
		printf("\t%-8s", usr->name);
393
		if (unused)
394
			printf("\t%14lld\t%14lld\t%14lld",
395
			       SIZE(usr->spc30),
396
			       SIZE(usr->spc60),
397
			       SIZE(usr->spc90));
398
		printf("\n");
399
	}
400
	free(usrs);
401
}
402
403
static void
404
donames(int fd, struct fs *super, char *name)
405
{
406
	int c;
407
	unsigned long long inode;
408
	ino_t inode1;
409
	ino_t maxino;
410
	union dinode *dp;
411
412
	maxino = super->fs_ncg * super->fs_ipg - 1;
413
	/* first skip the name of the filesystem */
414
	while ((c = getchar()) != EOF && (c < '0' || c > '9'))
415
		while ((c = getchar()) != EOF && c != '\n');
416
	ungetc(c, stdin);
417
	inode1 = -1;
418
	while (scanf("%llu", &inode) == 1) {
419
		if (inode > maxino) {
420
			fprintf(stderr, "invalid inode %llu\n",
421
			    (unsigned long long)inode);
422
			return;
423
		}
424
		errno = 0;
425
		if ((dp = get_inode(fd, super, inode)) && !isfree(super, dp)) {
426
			printf("%s\t", user(DIP(super, dp, di_uid))->name);
427
			/* now skip whitespace */
428
			while ((c = getchar()) == ' ' || c == '\t');
429
			/* and print out the remainder of the input line */
430
			while (c != EOF && c != '\n') {
431
				putchar(c);
432
				c = getchar();
433
			}
434
			putchar('\n');
435
			inode1 = inode;
436
		} else {
437
			if (errno)
438
				err(1, "%s", name);
439
			/* skip this line */
440
			while ((c = getchar()) != EOF && c != '\n')
441
				;
442
		}
443
		if (c == EOF)
444
			break;
445
	}
446
}
447
448
static void
449
usage(void)
450
{
451
	fprintf(stderr, "usage: quot [-acfhknv] [filesystem ...]\n");
452
	exit(1);
453
}
454
455
/*
456
 * Possible superblock locations ordered from most to least likely.
457
 */
458
static int sblock_try[] = SBLOCKSEARCH;
459
static char superblock[SBLOCKSIZE];
460
461
#define	max(a,b)	MAX((a),(b))
462
/*
463
 * Sanity checks for old file systems.
464
 * Stolen from <sys/lib/libsa/ufs.c>
465
 */
466
static void
467
ffs_oldfscompat(struct fs *fs)
468
{
469
	int i;
470
471
	fs->fs_npsect = max(fs->fs_npsect, fs->fs_nsect);	/* XXX */
472
	fs->fs_interleave = max(fs->fs_interleave, 1);		/* XXX */
473
	if (fs->fs_postblformat == FS_42POSTBLFMT)		/* XXX */
474
		fs->fs_nrpos = 8;				/* XXX */
475
	if (fs->fs_inodefmt < FS_44INODEFMT) {			/* XXX */
476
		quad_t sizepb = fs->fs_bsize;			/* XXX */
477
								/* XXX */
478
		fs->fs_maxfilesize = fs->fs_bsize * NDADDR - 1;	/* XXX */
479
		for (i = 0; i < NIADDR; i++) {			/* XXX */
480
			sizepb *= NINDIR(fs);			/* XXX */
481
			fs->fs_maxfilesize += sizepb;		/* XXX */
482
		}						/* XXX */
483
		fs->fs_qbmask = ~fs->fs_bmask;			/* XXX */
484
		fs->fs_qfmask = ~fs->fs_fmask;			/* XXX */
485
	}							/* XXX */
486
}
487
488
void
489
quot(char *name, char *mp)
490
{
491
	int i, fd;
492
	struct fs *fs;
493
494
	get_inode(-1, NULL, 0);		/* flush cache */
495
	inituser();
496
	initfsizes();
497
	/*
498
	 * XXX this is completely broken.  Of course you can't read a
499
	 * directory, well, not anymore.  How to fix this, though...
500
	 */
501
	if ((fd = open(name, O_RDONLY)) < 0) {
502
		warn("%s", name);
503
		return;
504
	}
505
	for (i = 0; sblock_try[i] != -1; i++) {
506
		if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) {
507
			close(fd);
508
			return;
509
		}
510
		if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) {
511
			close(fd);
512
			return;
513
		}
514
		fs = (struct fs *)superblock;
515
		if ((fs->fs_magic == FS_UFS1_MAGIC ||
516
		    (fs->fs_magic == FS_UFS2_MAGIC &&
517
		    fs->fs_sblockloc == sblock_try[i])) &&
518
		    fs->fs_bsize <= MAXBSIZE &&
519
		    fs->fs_bsize >= sizeof(struct fs))
520
			break;
521
	}
522
	if (sblock_try[i] == -1) {
523
		warnx("%s: not a BSD filesystem", name);
524
		close(fd);
525
		return;
526
	}
527
	ffs_oldfscompat(fs);
528
	printf("%s:", name);
529
	if (mp)
530
		printf(" (%s)", mp);
531
	putchar('\n');
532
	(*func)(fd, fs, name);
533
	close(fd);
534
}
535
536
int
537
main(int argc, char *argv[])
538
{
539
	int cnt, all, i;
540
	char dev[MNAMELEN], *nm, *mountpoint, *cp;
541
	struct statfs *mp;
542
543
	all = 0;
544
	func = douser;
545
	header = getbsize(&headerlen, &blocksize);
546
	while (--argc > 0 && **++argv == '-') {
547
		while (*++*argv) {
548
			switch (**argv) {
549
			case 'n':
550
				func = donames;
551
				break;
552
			case 'c':
553
				func = dofsizes;
554
				break;
555
			case 'a':
556
				all = 1;
557
				break;
558
			case 'f':
559
				count = 1;
560
				break;
561
			case 'h':
562
				estimate = 1;
563
				break;
564
			case 'k':
565
				blocksize = 1024;
566
				break;
567
			case 'v':
568
				unused = 1;
569
				break;
570
			default:
571
				usage();
572
			}
573
		}
574
	}
575
	cnt = getmntinfo(&mp, MNT_NOWAIT);
576
	if (all) {
577
		for (; --cnt >= 0; mp++) {
578
			if (strcmp(mp->f_fstypename, MOUNT_FFS) == 0 ||
579
			    strcmp(mp->f_fstypename, "ufs") == 0) {
580
				if ((nm = strrchr(mp->f_mntfromname, '/'))) {
581
					snprintf(dev, sizeof(dev), "%sr%s",
582
					    _PATH_DEV, nm + 1);
583
					nm = dev;
584
				} else
585
					nm = mp->f_mntfromname;
586
				quot(nm, mp->f_mntonname);
587
			}
588
		}
589
	}
590
	for (; --argc >= 0; argv++) {
591
		mountpoint = NULL;
592
		nm = *argv;
593
594
		/* Remove trailing slashes from name. */
595
		cp = nm + strlen(nm);
596
		while (*(--cp) == '/' && cp != nm)
597
			*cp = '\0';
598
599
		/* Look up the name in the mount table. */
600
		for (i = 0; i < cnt; i++) {
601
			/* Remove trailing slashes from name. */
602
			cp = mp[i].f_mntonname + strlen(mp[i].f_mntonname);
603
			while (*(--cp) == '/' && cp != mp[i].f_mntonname)
604
				*cp = '\0';
605
606
			if ((!strcmp(mp->f_fstypename, MOUNT_FFS) ||
607
			     !strcmp(mp->f_fstypename, MOUNT_MFS) ||
608
			     !strcmp(mp->f_fstypename, "ufs")) &&
609
			    strcmp(nm, mp[i].f_mntonname) == 0) {
610
				nm = mp[i].f_mntfromname;
611
				mountpoint = mp[i].f_mntonname;
612
				break;
613
			}
614
		}
615
616
		/* Make sure we have the raw device... */
617
		if (strncmp(nm, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0 &&
618
		    nm[sizeof(_PATH_DEV) - 1] != 'r') {
619
			snprintf(dev, sizeof(dev), "%sr%s", _PATH_DEV,
620
			    nm + sizeof(_PATH_DEV) - 1);
621
			nm = dev;
622
		}
623
		quot(nm, mountpoint);
624
	}
625
	exit(0);
626
}