xref: /netbsd-src/bin/df/df.c (revision bcc8ec9959e7b01e313d813067bfb43a3ad70551)
1 /*	$NetBSD: df.c,v 1.38 2001/01/07 05:37:10 christos Exp $	*/
2 
3 /*
4  * Copyright (c) 1980, 1990, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  * (c) UNIX System Laboratories, Inc.
7  * All or some portions of this file are derived from material licensed
8  * to the University of California by American Telephone and Telegraph
9  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
10  * the permission of UNIX System Laboratories, Inc.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40 
41 #include <sys/cdefs.h>
42 #ifndef lint
43 __COPYRIGHT(
44 "@(#) Copyright (c) 1980, 1990, 1993, 1994\n\
45 	The Regents of the University of California.  All rights reserved.\n");
46 #endif /* not lint */
47 
48 #ifndef lint
49 #if 0
50 static char sccsid[] = "@(#)df.c	8.7 (Berkeley) 4/2/94";
51 #else
52 __RCSID("$NetBSD: df.c,v 1.38 2001/01/07 05:37:10 christos Exp $");
53 #endif
54 #endif /* not lint */
55 
56 #include <sys/param.h>
57 #include <sys/stat.h>
58 #include <sys/mount.h>
59 
60 #include <ufs/ufs/ufsmount.h>
61 
62 #include <err.h>
63 #include <errno.h>
64 #include <fcntl.h>
65 #include <stdio.h>
66 #include <stdlib.h>
67 #include <string.h>
68 #include <unistd.h>
69 
70 extern char * strpct __P((u_long num, u_long denom, u_int digits));
71 extern char *__progname;
72 
73 int	 main __P((int, char *[]));
74 int	 bread __P((off_t, void *, int));
75 char	*getmntpt __P((char *));
76 void	 prtstat __P((struct statfs *, int));
77 int	 ufs_df __P((char *, struct statfs *));
78 int	 selected __P((const char *));
79 void	 maketypelist __P((char *));
80 long	 regetmntinfo __P((struct statfs **, long));
81 void	 usage __P((void));
82 
83 int	aflag, iflag, kflag, lflag, mflag, nflag, Pflag;
84 char	**typelist = NULL;
85 struct	ufs_args mdev;
86 
87 int
88 main(argc, argv)
89 	int argc;
90 	char *argv[];
91 {
92 	struct stat stbuf;
93 	struct statfs *mntbuf;
94 	long mntsize;
95 	int ch, i, maxwidth, width;
96 	char *mntpt;
97 
98 	while ((ch = getopt(argc, argv, "aiklmnPt:")) != -1)
99 		switch (ch) {
100 		case 'a':
101 			aflag = 1;
102 			break;
103 		case 'i':
104 			iflag = 1;
105 			break;
106 		case 'k':
107 			kflag = 1;
108 			break;
109 		case 'l':
110 			lflag = 1;
111 			break;
112 		case 'm':
113 			mflag = 1;
114 			break;
115 		case 'n':
116 			nflag = 1;
117 			break;
118 		case 'P':
119 			Pflag = 1;
120 			break;
121 		case 't':
122 			if (typelist != NULL)
123 				errx(1, "only one -t option may be specified.");
124 			maketypelist(optarg);
125 			break;
126 		case '?':
127 		default:
128 			usage();
129 		}
130 	argc -= optind;
131 	argv += optind;
132 
133 	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
134 	if (mntsize == 0)
135 	        err(1, "retrieving information on mounted file systems");
136 
137 	if (!*argv) {
138 		mntsize = regetmntinfo(&mntbuf, mntsize);
139 	} else {
140 		mntbuf = malloc(argc * sizeof(struct statfs));
141 		mntsize = 0;
142 		for (; *argv; argv++) {
143 			if (stat(*argv, &stbuf) < 0) {
144 				if ((mntpt = getmntpt(*argv)) == 0) {
145 					warn("%s", *argv);
146 					continue;
147 				}
148 			} else if (S_ISCHR(stbuf.st_mode)) {
149 				if (!ufs_df(*argv, &mntbuf[mntsize]))
150 					++mntsize;
151 				continue;
152 			} else if (S_ISBLK(stbuf.st_mode)) {
153 				if ((mntpt = getmntpt(*argv)) == 0) {
154 					mntpt = strdup("/tmp/df.XXXXXX");
155 					if (!mkdtemp(mntpt)) {
156 						warn("%s", mntpt);
157 						continue;
158 					}
159 					mdev.fspec = *argv;
160 					if (mount(MOUNT_FFS, mntpt, MNT_RDONLY,
161 					    &mdev) != 0) {
162 						(void)rmdir(mntpt);
163 						if (!ufs_df(*argv,
164 						    &mntbuf[mntsize]))
165 							++mntsize;
166 						continue;
167 					} else if (!statfs(mntpt,
168 					    &mntbuf[mntsize])) {
169 						mntbuf[mntsize].f_mntonname[0] =
170 						    '\0';
171 						++mntsize;
172 					} else
173 						warn("%s", *argv);
174 					(void)unmount(mntpt, 0);
175 					(void)rmdir(mntpt);
176 					continue;
177 				}
178 			} else
179 				mntpt = *argv;
180 			/*
181 			 * Statfs does not take a `wait' flag, so we cannot
182 			 * implement nflag here.
183 			 */
184 			if (!statfs(mntpt, &mntbuf[mntsize]))
185 				if (lflag &&
186 				    (mntbuf[mntsize].f_flags & MNT_LOCAL) == 0)
187 					warnx("Warning: %s is not a local %s",
188 					    *argv, "file system");
189 				else if
190 				    (!selected(mntbuf[mntsize].f_fstypename))
191 					warnx("Warning: %s mounted as a %s %s",
192 					    *argv,
193 					    mntbuf[mntsize].f_fstypename,
194 					    "file system");
195 				else
196 					++mntsize;
197 			else
198 				warn("%s", *argv);
199 		}
200 	}
201 
202 	maxwidth = 0;
203 	for (i = 0; i < mntsize; i++) {
204 		width = strlen(mntbuf[i].f_mntfromname);
205 		if (width > maxwidth)
206 			maxwidth = width;
207 	}
208 	for (i = 0; i < mntsize; i++)
209 		prtstat(&mntbuf[i], maxwidth);
210 	exit(0);
211 	/* NOTREACHED */
212 }
213 
214 char *
215 getmntpt(name)
216 	char *name;
217 {
218 	long mntsize, i;
219 	struct statfs *mntbuf;
220 
221 	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
222 	for (i = 0; i < mntsize; i++) {
223 		if (!strcmp(mntbuf[i].f_mntfromname, name))
224 			return (mntbuf[i].f_mntonname);
225 	}
226 	return (0);
227 }
228 
229 static enum { IN_LIST, NOT_IN_LIST } which;
230 
231 int
232 selected(type)
233 	const char *type;
234 {
235 	char **av;
236 
237 	/* If no type specified, it's always selected. */
238 	if (typelist == NULL)
239 		return (1);
240 	for (av = typelist; *av != NULL; ++av)
241 		if (!strncmp(type, *av, MFSNAMELEN))
242 			return (which == IN_LIST ? 1 : 0);
243 	return (which == IN_LIST ? 0 : 1);
244 }
245 
246 void
247 maketypelist(fslist)
248 	char *fslist;
249 {
250 	int i;
251 	char *nextcp, **av;
252 
253 	if ((fslist == NULL) || (fslist[0] == '\0'))
254 		errx(1, "empty type list");
255 
256 	/*
257 	 * XXX
258 	 * Note: the syntax is "noxxx,yyy" for no xxx's and
259 	 * no yyy's, not the more intuitive "noyyy,noyyy".
260 	 */
261 	if (fslist[0] == 'n' && fslist[1] == 'o') {
262 		fslist += 2;
263 		which = NOT_IN_LIST;
264 	} else
265 		which = IN_LIST;
266 
267 	/* Count the number of types. */
268 	for (i = 1, nextcp = fslist;
269 	    (nextcp = strchr(nextcp, ',')) != NULL; i++)
270 		++nextcp;
271 
272 	/* Build an array of that many types. */
273 	if ((av = typelist = malloc((i + 1) * sizeof(char *))) == NULL)
274 		err(1, "can't allocate type array");
275 	av[0] = fslist;
276 	for (i = 1, nextcp = fslist;
277 	    (nextcp = strchr(nextcp, ',')) != NULL; i++) {
278 		*nextcp = '\0';
279 		av[i] = ++nextcp;
280 	}
281 	/* Terminate the array. */
282 	av[i] = NULL;
283 }
284 
285 /*
286  * Make a pass over the filesystem info in ``mntbuf'' filtering out
287  * filesystem types not in ``fsmask'' and possibly re-stating to get
288  * current (not cached) info.  Returns the new count of valid statfs bufs.
289  */
290 long
291 regetmntinfo(mntbufp, mntsize)
292 	struct statfs **mntbufp;
293 	long mntsize;
294 {
295 	int i, j;
296 	struct statfs *mntbuf;
297 
298 	if (!lflag && typelist == NULL)
299 		return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT));
300 
301 	mntbuf = *mntbufp;
302 	j = 0;
303 	for (i = 0; i < mntsize; i++) {
304 		if (!aflag && (mntbuf[i].f_flags & MNT_IGNORE) != 0)
305 			continue;
306 		if (lflag && (mntbuf[i].f_flags & MNT_LOCAL) == 0)
307 			continue;
308 		if (!selected(mntbuf[i].f_fstypename))
309 			continue;
310 		if (nflag)
311 			mntbuf[j] = mntbuf[i];
312 		else {
313 			struct statfs layerbuf = mntbuf[i];
314 			(void)statfs(mntbuf[i].f_mntonname, &mntbuf[j]);
315 			/*
316 			 * If the FS name changed, then new data is for
317 			 * a different layer and we don't want it.
318 			 */
319 			if(memcmp(layerbuf.f_mntfromname,
320 				 mntbuf[j].f_mntfromname, MNAMELEN))
321 				 mntbuf[j] = layerbuf;
322 		}
323 		j++;
324 	}
325 	return (j);
326 }
327 
328 /*
329  * Convert statfs returned filesystem size into BLOCKSIZE units.
330  * Attempts to avoid overflow for large filesystems.
331  */
332 #define fsbtoblk(num, fsbs, bs) \
333 	(((fsbs) != 0 && (fsbs) < (bs)) ? \
334 		(num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
335 
336 /*
337  * Print out status about a filesystem.
338  */
339 void
340 prtstat(sfsp, maxwidth)
341 	struct statfs *sfsp;
342 	int maxwidth;
343 {
344 	static long blocksize;
345 	static int headerlen, timesthrough;
346 	static char *header;
347 	long used, availblks, inodes;
348 	static char     *full = "100%";
349 
350 	if (maxwidth < 11)
351 		maxwidth = 11;
352 	if (++timesthrough == 1) {
353 		if (kflag) {
354 			blocksize = 1024;
355 			header = Pflag ? "1024-blocks" : "1K-blocks";
356 			headerlen = strlen(header);
357 		} else if (mflag) {
358 			blocksize = 1024 * 1024;
359 			header = Pflag ? "1048576-blocks" : "1M-blocks";
360 			headerlen = strlen(header);
361 		} else
362 			header = getbsize(&headerlen, &blocksize);
363 		(void)printf("%-*.*s %s     Used %9s Capacity",
364 		    maxwidth, maxwidth, "Filesystem", header,
365 		    Pflag ? "Available" : "Avail");
366 		if (iflag)
367 			(void)printf(" iused   ifree  %%iused");
368 		(void)printf("  Mounted on\n");
369 	}
370 	(void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname);
371 	used = sfsp->f_blocks - sfsp->f_bfree;
372 	availblks = sfsp->f_bavail + used;
373 	(void)printf(" %*ld %8ld %9ld", headerlen,
374 	    fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize),
375 	    fsbtoblk(used, sfsp->f_bsize, blocksize),
376 	    fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize));
377 	(void)printf("%9s",
378 	    availblks == 0 ? full : strpct((u_long)used, (u_long)availblks, 0));
379 	if (iflag) {
380 		inodes = sfsp->f_files;
381 		used = inodes - sfsp->f_ffree;
382 		(void)printf(" %7ld %7ld %6s ", used, sfsp->f_ffree,
383 		   inodes == 0 ? full : strpct((u_long)used, (u_long)inodes, 0));
384 	} else
385 		(void)printf("  ");
386 	(void)printf("  %s\n", sfsp->f_mntonname);
387 }
388 
389 /*
390  * This code constitutes the pre-system call Berkeley df code for extracting
391  * information from filesystem superblocks.
392  */
393 #include <ufs/ufs/dinode.h>
394 #include <ufs/ffs/fs.h>
395 #include <errno.h>
396 #include <fstab.h>
397 
398 union {
399 	struct fs iu_fs;
400 	char dummy[SBSIZE];
401 } sb;
402 #define sblock sb.iu_fs
403 
404 int	rfd;
405 
406 int
407 ufs_df(file, sfsp)
408 	char *file;
409 	struct statfs *sfsp;
410 {
411 	char *mntpt;
412 	static int synced;
413 
414 	if (synced++ == 0)
415 		sync();
416 
417 	if ((rfd = open(file, O_RDONLY)) < 0) {
418 		warn("%s", file);
419 		return (-1);
420 	}
421 	if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) {
422 		(void)close(rfd);
423 		return (-1);
424 	}
425 	sfsp->f_type = 0;
426 	sfsp->f_flags = 0;
427 	sfsp->f_bsize = sblock.fs_fsize;
428 	sfsp->f_iosize = sblock.fs_bsize;
429 	sfsp->f_blocks = sblock.fs_dsize;
430 	sfsp->f_bfree = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag +
431 		sblock.fs_cstotal.cs_nffree;
432 	sfsp->f_bavail = ((int64_t)sblock.fs_dsize * (100 - sblock.fs_minfree) / 100) -
433 		(sblock.fs_dsize - sfsp->f_bfree);
434 	if (sfsp->f_bavail < 0)
435 		sfsp->f_bavail = 0;
436 	sfsp->f_files =  sblock.fs_ncg * sblock.fs_ipg;
437 	sfsp->f_ffree = sblock.fs_cstotal.cs_nifree;
438 	sfsp->f_fsid.val[0] = 0;
439 	sfsp->f_fsid.val[1] = 0;
440 	if ((mntpt = getmntpt(file)) == 0)
441 		mntpt = "";
442 	(void)memmove(&sfsp->f_mntonname[0], mntpt, MNAMELEN);
443 	(void)memmove(&sfsp->f_mntfromname[0], file, MNAMELEN);
444 	(void)strncpy(sfsp->f_fstypename, MOUNT_FFS, MFSNAMELEN);
445 	(void)close(rfd);
446 	return (0);
447 }
448 
449 int
450 bread(off, buf, cnt)
451 	off_t off;
452 	void *buf;
453 	int cnt;
454 {
455 	int nr;
456 
457 	(void)lseek(rfd, off, SEEK_SET);
458 	if ((nr = read(rfd, buf, cnt)) != cnt) {
459 		/* Probably a dismounted disk if errno == EIO. */
460 		if (errno != EIO)
461 			(void)fprintf(stderr, "\ndf: %lld: %s\n",
462 			    (long long)off, strerror(nr > 0 ? EIO : errno));
463 		return (0);
464 	}
465 	return (1);
466 }
467 
468 void
469 usage()
470 {
471 	(void)fprintf(stderr,
472 	    "Usage: %s [-aiklmnP] [-t type] [file | file_system ...]\n",
473 	    __progname);
474 	exit(1);
475 	/* NOTREACHED */
476 }
477