xref: /freebsd/bin/df/df.c (revision e627b39baccd1ec9129690167cf5e6d860509655)
1 /*
2  * Copyright (c) 1980, 1990, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the University of
21  *	California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *	$Id: df.c,v 1.7 1995/05/30 00:06:42 rgrimes Exp $
39  */
40 
41 #ifndef lint
42 static char copyright[] =
43 "@(#) Copyright (c) 1980, 1990, 1993, 1994\n\
44 	The Regents of the University of California.  All rights reserved.\n";
45 #endif /* not lint */
46 
47 #ifndef lint
48 static char sccsid[] = "@(#)df.c	8.7 (Berkeley) 4/2/94";
49 #endif /* not lint */
50 
51 #include <sys/param.h>
52 #include <sys/stat.h>
53 #include <sys/mount.h>
54 
55 #include <err.h>
56 #include <errno.h>
57 #include <fcntl.h>
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <unistd.h>
62 
63 /* XXX assumes MOUNT_MAXTYPE < 32 */
64 #define MT(m)		(1 << (m))
65 
66 /* fixed values */
67 #define MT_NONE		(0)
68 #define MT_ALL		(MT(MOUNT_MAXTYPE+1)-1)
69 
70 /* subject to change */
71 #define MT_LOCAL \
72     (MT(MOUNT_UFS)|MT(MOUNT_MFS)|MT(MOUNT_LFS)|MT(MOUNT_MSDOS)|MT(MOUNT_CD9660))
73 #define MT_DEFAULT	MT_ALL
74 
75 struct typetab {
76 	char *str;
77 	long types;
78 } typetab[] = {
79 	{"ufs",		MT(MOUNT_UFS)},
80 	{"local",	MT_LOCAL},
81 	{"all",		MT_ALL},
82 	{"nfs",		MT(MOUNT_NFS)},
83 	{"mfs",		MT(MOUNT_MFS)},
84 	{"lfs",		MT(MOUNT_LFS)},
85 	{"msdos",	MT(MOUNT_MSDOS)},
86 	{"fdesc",	MT(MOUNT_FDESC)},
87 	{"portal",	MT(MOUNT_PORTAL)},
88 #if 0
89 	/* return fsid of underlying FS */
90 	{"lofs",	MT(MOUNT_LOFS)},
91 	{"null",	MT(MOUNT_NULL)},
92 	{"umap",	MT(MOUNT_UMAP)},
93 #endif
94 	{"kernfs",	MT(MOUNT_KERNFS)},
95 	{"procfs",	MT(MOUNT_PROCFS)},
96 	{"afs",		MT(MOUNT_AFS)},
97 	{"iso9660fs",	MT(MOUNT_CD9660)},
98 	{"cdfs",	MT(MOUNT_CD9660)},
99 	{"misc",	MT(MOUNT_LOFS)|MT(MOUNT_FDESC)|MT(MOUNT_PORTAL)|
100 			MT(MOUNT_KERNFS)|MT(MOUNT_PROCFS)},
101 	{NULL,		0}
102 
103 };
104 
105 long	addtype __P((long, char *));
106 long	regetmntinfo __P((struct statfs **, long, long));
107 int	 bread __P((off_t, void *, int));
108 char	*getmntpt __P((char *));
109 void	 prtstat __P((struct statfs *, int));
110 void	 ufs_df __P((char *, int));
111 void	 usage __P((void));
112 
113 int	iflag, nflag, tflag;
114 struct	ufs_args mdev;
115 
116 int
117 main(argc, argv)
118 	int argc;
119 	char *argv[];
120 {
121 	struct stat stbuf;
122 	struct statfs statfsbuf, *mntbuf;
123 	long fsmask, mntsize;
124 	int ch, err, i, maxwidth, width;
125 	char *mntpt;
126 
127 	iflag = nflag = tflag = 0;
128 	fsmask = MT_NONE;
129 
130 	while ((ch = getopt(argc, argv, "iknt:")) != EOF)
131 		switch (ch) {
132 		case 'i':
133 			iflag = 1;
134 			break;
135 		case 'k':
136 			putenv("BLOCKSIZE=1k");
137 			break;
138 		case 'n':
139 			nflag = 1;
140 			break;
141 		case 't':
142 			fsmask = addtype(fsmask, optarg);
143 			tflag = 1;
144 			break;
145 		case '?':
146 		default:
147 			usage();
148 		}
149 	argc -= optind;
150 	argv += optind;
151 
152 	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
153 	maxwidth = 0;
154 	for (i = 0; i < mntsize; i++) {
155 		width = strlen(mntbuf[i].f_mntfromname);
156 		if (width > maxwidth)
157 			maxwidth = width;
158 	}
159 
160 	if (!*argv) {
161 		if (!tflag)
162 			fsmask = MT_DEFAULT;
163 		mntsize = regetmntinfo(&mntbuf, mntsize, fsmask);
164 		if (fsmask != MT_ALL) {
165 			maxwidth = 0;
166 			for (i = 0; i < mntsize; i++) {
167 				width = strlen(mntbuf[i].f_mntfromname);
168 				if (width > maxwidth)
169 					maxwidth = width;
170 			}
171 		}
172 		for (i = 0; i < mntsize; i++)
173 			prtstat(&mntbuf[i], maxwidth);
174 		exit(0);
175 	}
176 
177 	for (; *argv; argv++) {
178 		if (stat(*argv, &stbuf) < 0) {
179 			err = errno;
180 			if ((mntpt = getmntpt(*argv)) == 0) {
181 				warn("%s", *argv);
182 				continue;
183 			}
184 		} else if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
185 			ufs_df(*argv, maxwidth);
186 			continue;
187 		} else if ((stbuf.st_mode & S_IFMT) == S_IFBLK) {
188 			if ((mntpt = getmntpt(*argv)) == 0) {
189 				mntpt = mktemp(strdup("/tmp/df.XXXXXX"));
190 				mdev.fspec = *argv;
191 				if (mkdir(mntpt, DEFFILEMODE) != 0) {
192 					warn("%s", mntpt);
193 					continue;
194 				}
195 				if (mount(MOUNT_UFS, mntpt, MNT_RDONLY,
196 				    &mdev) != 0) {
197 					ufs_df(*argv, maxwidth);
198 					(void)rmdir(mntpt);
199 					continue;
200 				} else if (statfs(mntpt, &statfsbuf)) {
201 					statfsbuf.f_mntonname[0] = '\0';
202 					prtstat(&statfsbuf, maxwidth);
203 				} else
204 					warn("%s", *argv);
205 				(void)unmount(mntpt, 0);
206 				(void)rmdir(mntpt);
207 				continue;
208 			}
209 		} else
210 			mntpt = *argv;
211 		/*
212 		 * Statfs does not take a `wait' flag, so we cannot
213 		 * implement nflag here.
214 		 */
215 		if (statfs(mntpt, &statfsbuf) < 0) {
216 			warn("%s", mntpt);
217 			continue;
218 		}
219 		if (argc == 1)
220 			maxwidth = strlen(statfsbuf.f_mntfromname) + 1;
221 		prtstat(&statfsbuf, maxwidth);
222 	}
223 	return (0);
224 }
225 
226 char *
227 getmntpt(name)
228 	char *name;
229 {
230 	long mntsize, i;
231 	struct statfs *mntbuf;
232 
233 	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
234 	for (i = 0; i < mntsize; i++) {
235 		if (!strcmp(mntbuf[i].f_mntfromname, name))
236 			return (mntbuf[i].f_mntonname);
237 	}
238 	return (0);
239 }
240 
241 long
242 addtype(omask, str)
243 	long omask;
244 	char *str;
245 {
246 	struct typetab *tp;
247 
248 	/*
249 	 * If it is one of our known types, add it to the current mask
250 	 */
251 	for (tp = typetab; tp->str; tp++)
252 		if (strcmp(str, tp->str) == 0)
253 			return (tp->types | (tflag ? omask : MT_NONE));
254 	/*
255 	 * See if it is the negation of one of the known values
256 	 */
257 	if (strlen(str) > 2 && str[0] == 'n' && str[1] == 'o')
258 		for (tp = typetab; tp->str; tp++)
259 			if (strcmp(str+2, tp->str) == 0)
260 				return (~tp->types & (tflag ? omask : MT_ALL));
261 	errx(1, "unknown type `%s'", str);
262 }
263 
264 /*
265  * Make a pass over the filesystem info in ``mntbuf'' filtering out
266  * filesystem types not in ``fsmask'' and possibly re-stating to get
267  * current (not cached) info.  Returns the new count of valid statfs bufs.
268  */
269 long
270 regetmntinfo(mntbufp, mntsize, fsmask)
271 	struct statfs **mntbufp;
272 	long mntsize, fsmask;
273 {
274 	int i, j;
275 	struct statfs *mntbuf;
276 
277 	if (fsmask == MT_ALL)
278 		return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT));
279 
280 	mntbuf = *mntbufp;
281 	j = 0;
282 	for (i = 0; i < mntsize; i++) {
283 		if (fsmask & MT(mntbuf[i].f_type)) {
284 			if (!nflag)
285 				(void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]);
286 			else if (i != j)
287 				mntbuf[j] = mntbuf[i];
288 			j++;
289 		}
290 	}
291 	return (j);
292 }
293 
294 /*
295  * Convert statfs returned filesystem size into BLOCKSIZE units.
296  * Attempts to avoid overflow for large filesystems.
297  */
298 #define fsbtoblk(num, fsbs, bs) \
299 	(((fsbs) != 0 && (fsbs) < (bs)) ? \
300 		(num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
301 
302 /*
303  * Print out status about a filesystem.
304  */
305 void
306 prtstat(sfsp, maxwidth)
307 	struct statfs *sfsp;
308 	int maxwidth;
309 {
310 	static long blocksize;
311 	static int headerlen, timesthrough;
312 	static char *header;
313 	long used, availblks, inodes;
314 
315 	if (maxwidth < 11)
316 		maxwidth = 11;
317 	if (++timesthrough == 1) {
318 		header = getbsize(&headerlen, &blocksize);
319 		(void)printf("%-*.*s %s     Used    Avail Capacity",
320 		    maxwidth, maxwidth, "Filesystem", header);
321 		if (iflag)
322 			(void)printf(" iused   ifree  %%iused");
323 		(void)printf("  Mounted on\n");
324 	}
325 	(void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname);
326 	used = sfsp->f_blocks - sfsp->f_bfree;
327 	availblks = sfsp->f_bavail + used;
328 	(void)printf(" %*ld %8ld %8ld", headerlen,
329 	    fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize),
330 	    fsbtoblk(used, sfsp->f_bsize, blocksize),
331 	    fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize));
332 	(void)printf(" %5.0f%%",
333 	    availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
334 	if (iflag) {
335 		inodes = sfsp->f_files;
336 		used = inodes - sfsp->f_ffree;
337 		(void)printf(" %7ld %7ld %5.0f%% ", used, sfsp->f_ffree,
338 		   inodes == 0 ? 100.0 : (double)used / (double)inodes * 100.0);
339 	} else
340 		(void)printf("  ");
341 	(void)printf("  %s\n", sfsp->f_mntonname);
342 }
343 
344 /*
345  * This code constitutes the pre-system call Berkeley df code for extracting
346  * information from filesystem superblocks.
347  */
348 #include <ufs/ffs/fs.h>
349 #include <errno.h>
350 #include <fstab.h>
351 
352 union {
353 	struct fs iu_fs;
354 	char dummy[SBSIZE];
355 } sb;
356 #define sblock sb.iu_fs
357 
358 int	rfd;
359 
360 void
361 ufs_df(file, maxwidth)
362 	char *file;
363 	int maxwidth;
364 {
365 	struct statfs statfsbuf;
366 	struct statfs *sfsp;
367 	char *mntpt;
368 	static int synced;
369 
370 	if (synced++ == 0)
371 		sync();
372 
373 	if ((rfd = open(file, O_RDONLY)) < 0) {
374 		warn("%s", file);
375 		return;
376 	}
377 	if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) {
378 		(void)close(rfd);
379 		return;
380 	}
381 	sfsp = &statfsbuf;
382 	sfsp->f_type = MOUNT_UFS;
383 	sfsp->f_flags = 0;
384 	sfsp->f_bsize = sblock.fs_fsize;
385 	sfsp->f_iosize = sblock.fs_bsize;
386 	sfsp->f_blocks = sblock.fs_dsize;
387 	sfsp->f_bfree = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag +
388 		sblock.fs_cstotal.cs_nffree;
389 	sfsp->f_bavail = freespace(&sblock, sblock.fs_minfree);
390 	sfsp->f_files =  sblock.fs_ncg * sblock.fs_ipg;
391 	sfsp->f_ffree = sblock.fs_cstotal.cs_nifree;
392 	sfsp->f_fsid.val[0] = 0;
393 	sfsp->f_fsid.val[1] = 0;
394 	if ((mntpt = getmntpt(file)) == 0)
395 		mntpt = "";
396 	memmove(&sfsp->f_mntonname[0], mntpt, MNAMELEN);
397 	memmove(&sfsp->f_mntfromname[0], file, MNAMELEN);
398 	prtstat(sfsp, maxwidth);
399 	(void)close(rfd);
400 }
401 
402 int
403 bread(off, buf, cnt)
404 	off_t off;
405 	void *buf;
406 	int cnt;
407 {
408 	int nr;
409 
410 	(void)lseek(rfd, off, SEEK_SET);
411 	if ((nr = read(rfd, buf, cnt)) != cnt) {
412 		/* Probably a dismounted disk if errno == EIO. */
413 		if (errno != EIO)
414 			(void)fprintf(stderr, "\ndf: %qd: %s\n",
415 			    off, strerror(nr > 0 ? EIO : errno));
416 		return (0);
417 	}
418 	return (1);
419 }
420 
421 void
422 usage()
423 {
424 	fprintf(stderr,
425 		"usage: df [-ikn] [-t fstype] [file | file_system ...]\n");
426 	exit(1);
427 }
428