xref: /freebsd/bin/df/df.c (revision 1130b656e5fe4c2d1ba299e024d1b40eaeebd380)
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  *	$FreeBSD$
39  */
40 
41 #ifndef lint
42 static char const 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 const sccsid[] = "@(#)df.c	8.9 (Berkeley) 5/8/95";
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 	{"isofs",	MT(MOUNT_CD9660)},
99 	{"cd9660",	MT(MOUNT_CD9660)},
100 	{"cdfs",	MT(MOUNT_CD9660)},
101 	{"misc",	MT(MOUNT_LOFS)|MT(MOUNT_FDESC)|MT(MOUNT_PORTAL)|
102 			MT(MOUNT_KERNFS)|MT(MOUNT_PROCFS)},
103 	{NULL,		0}
104 
105 };
106 
107 long	addtype __P((long, char *));
108 long	regetmntinfo __P((struct statfs **, long, long));
109 int	 bread __P((off_t, void *, int));
110 char	*getmntpt __P((char *));
111 void	 prtstat __P((struct statfs *, int));
112 void	 ufs_df __P((char *, int));
113 void	 usage __P((void));
114 
115 int	iflag, nflag, tflag;
116 struct	ufs_args mdev;
117 
118 int
119 main(argc, argv)
120 	int argc;
121 	char *argv[];
122 {
123 	struct stat stbuf;
124 	struct statfs statfsbuf, *mntbuf;
125 	long fsmask, mntsize;
126 	int ch, err, i, maxwidth, width;
127 	char *mntpt;
128 
129 	iflag = nflag = tflag = 0;
130 	fsmask = MT_NONE;
131 
132 	while ((ch = getopt(argc, argv, "iknt:")) != EOF)
133 		switch (ch) {
134 		case 'i':
135 			iflag = 1;
136 			break;
137 		case 'k':
138 			putenv("BLOCKSIZE=1k");
139 			break;
140 		case 'n':
141 			nflag = 1;
142 			break;
143 		case 't':
144 			fsmask = addtype(fsmask, optarg);
145 			tflag = 1;
146 			break;
147 		case '?':
148 		default:
149 			usage();
150 		}
151 	argc -= optind;
152 	argv += optind;
153 
154 	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
155 	maxwidth = 0;
156 	for (i = 0; i < mntsize; i++) {
157 		width = strlen(mntbuf[i].f_mntfromname);
158 		if (width > maxwidth)
159 			maxwidth = width;
160 	}
161 
162 	if (!*argv) {
163 		if (!tflag)
164 			fsmask = MT_DEFAULT;
165 		mntsize = regetmntinfo(&mntbuf, mntsize, fsmask);
166 		if (fsmask != MT_ALL) {
167 			maxwidth = 0;
168 			for (i = 0; i < mntsize; i++) {
169 				width = strlen(mntbuf[i].f_mntfromname);
170 				if (width > maxwidth)
171 					maxwidth = width;
172 			}
173 		}
174 		for (i = 0; i < mntsize; i++)
175 			prtstat(&mntbuf[i], maxwidth);
176 		exit(0);
177 	}
178 
179 	for (; *argv; argv++) {
180 		if (stat(*argv, &stbuf) < 0) {
181 			err = errno;
182 			if ((mntpt = getmntpt(*argv)) == 0) {
183 				warn("%s", *argv);
184 				continue;
185 			}
186 		} else if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
187 			ufs_df(*argv, maxwidth);
188 			continue;
189 		} else if ((stbuf.st_mode & S_IFMT) == S_IFBLK) {
190 			if ((mntpt = getmntpt(*argv)) == 0) {
191 				mntpt = mktemp(strdup("/tmp/df.XXXXXX"));
192 				mdev.fspec = *argv;
193 				if (mkdir(mntpt, DEFFILEMODE) != 0) {
194 					warn("%s", mntpt);
195 					continue;
196 				}
197 				if (mount(MOUNT_UFS, mntpt, MNT_RDONLY,
198 				    &mdev) != 0) {
199 					ufs_df(*argv, maxwidth);
200 					(void)rmdir(mntpt);
201 					continue;
202 				} else if (statfs(mntpt, &statfsbuf)) {
203 					statfsbuf.f_mntonname[0] = '\0';
204 					prtstat(&statfsbuf, maxwidth);
205 				} else
206 					warn("%s", *argv);
207 				(void)unmount(mntpt, 0);
208 				(void)rmdir(mntpt);
209 				continue;
210 			}
211 		} else
212 			mntpt = *argv;
213 		/*
214 		 * Statfs does not take a `wait' flag, so we cannot
215 		 * implement nflag here.
216 		 */
217 		if (statfs(mntpt, &statfsbuf) < 0) {
218 			warn("%s", mntpt);
219 			continue;
220 		}
221 		if (argc == 1)
222 			maxwidth = strlen(statfsbuf.f_mntfromname) + 1;
223 		prtstat(&statfsbuf, maxwidth);
224 	}
225 	return (0);
226 }
227 
228 char *
229 getmntpt(name)
230 	char *name;
231 {
232 	long mntsize, i;
233 	struct statfs *mntbuf;
234 
235 	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
236 	for (i = 0; i < mntsize; i++) {
237 		if (!strcmp(mntbuf[i].f_mntfromname, name))
238 			return (mntbuf[i].f_mntonname);
239 	}
240 	return (0);
241 }
242 
243 long
244 addtype(omask, str)
245 	long omask;
246 	char *str;
247 {
248 	struct typetab *tp;
249 
250 	/*
251 	 * If it is one of our known types, add it to the current mask
252 	 */
253 	for (tp = typetab; tp->str; tp++)
254 		if (strcmp(str, tp->str) == 0)
255 			return (tp->types | (tflag ? omask : MT_NONE));
256 	/*
257 	 * See if it is the negation of one of the known values
258 	 */
259 	if (strlen(str) > 2 && str[0] == 'n' && str[1] == 'o')
260 		for (tp = typetab; tp->str; tp++)
261 			if (strcmp(str+2, tp->str) == 0)
262 				return (~tp->types & (tflag ? omask : MT_ALL));
263 	errx(1, "unknown type `%s'", str);
264 }
265 
266 /*
267  * Make a pass over the filesystem info in ``mntbuf'' filtering out
268  * filesystem types not in ``fsmask'' and possibly re-stating to get
269  * current (not cached) info.  Returns the new count of valid statfs bufs.
270  */
271 long
272 regetmntinfo(mntbufp, mntsize, fsmask)
273 	struct statfs **mntbufp;
274 	long mntsize, fsmask;
275 {
276 	int i, j;
277 	struct statfs *mntbuf;
278 
279 	if (fsmask == MT_ALL)
280 		return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT));
281 
282 	mntbuf = *mntbufp;
283 	j = 0;
284 	for (i = 0; i < mntsize; i++) {
285 		if (fsmask & MT(mntbuf[i].f_type)) {
286 			if (!nflag)
287 				(void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]);
288 			else if (i != j)
289 				mntbuf[j] = mntbuf[i];
290 			j++;
291 		}
292 	}
293 	return (j);
294 }
295 
296 /*
297  * Convert statfs returned filesystem size into BLOCKSIZE units.
298  * Attempts to avoid overflow for large filesystems.
299  */
300 #define fsbtoblk(num, fsbs, bs) \
301 	(((fsbs) != 0 && (fsbs) < (bs)) ? \
302 		(num) / ((bs) / (fsbs)) : (num) * ((fsbs) / (bs)))
303 
304 /*
305  * Print out status about a filesystem.
306  */
307 void
308 prtstat(sfsp, maxwidth)
309 	struct statfs *sfsp;
310 	int maxwidth;
311 {
312 	static long blocksize;
313 	static int headerlen, timesthrough;
314 	static char *header;
315 	long used, availblks, inodes;
316 
317 	if (maxwidth < 11)
318 		maxwidth = 11;
319 	if (++timesthrough == 1) {
320 		header = getbsize(&headerlen, &blocksize);
321 		(void)printf("%-*.*s %s     Used    Avail Capacity",
322 		    maxwidth, maxwidth, "Filesystem", header);
323 		if (iflag)
324 			(void)printf(" iused   ifree  %%iused");
325 		(void)printf("  Mounted on\n");
326 	}
327 	(void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname);
328 	used = sfsp->f_blocks - sfsp->f_bfree;
329 	availblks = sfsp->f_bavail + used;
330 	(void)printf(" %*ld %8ld %8ld", headerlen,
331 	    fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize),
332 	    fsbtoblk(used, sfsp->f_bsize, blocksize),
333 	    fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize));
334 	(void)printf(" %5.0f%%",
335 	    availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0);
336 	if (iflag) {
337 		inodes = sfsp->f_files;
338 		used = inodes - sfsp->f_ffree;
339 		(void)printf(" %7ld %7ld %5.0f%% ", used, sfsp->f_ffree,
340 		   inodes == 0 ? 100.0 : (double)used / (double)inodes * 100.0);
341 	} else
342 		(void)printf("  ");
343 	(void)printf("  %s\n", sfsp->f_mntonname);
344 }
345 
346 /*
347  * This code constitutes the pre-system call Berkeley df code for extracting
348  * information from filesystem superblocks.
349  */
350 #include <ufs/ffs/fs.h>
351 #include <errno.h>
352 #include <fstab.h>
353 
354 union {
355 	struct fs iu_fs;
356 	char dummy[SBSIZE];
357 } sb;
358 #define sblock sb.iu_fs
359 
360 int	rfd;
361 
362 void
363 ufs_df(file, maxwidth)
364 	char *file;
365 	int maxwidth;
366 {
367 	struct statfs statfsbuf;
368 	struct statfs *sfsp;
369 	char *mntpt;
370 	static int synced;
371 
372 	if (synced++ == 0)
373 		sync();
374 
375 	if ((rfd = open(file, O_RDONLY)) < 0) {
376 		warn("%s", file);
377 		return;
378 	}
379 	if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) {
380 		(void)close(rfd);
381 		return;
382 	}
383 	sfsp = &statfsbuf;
384 	sfsp->f_type = MOUNT_UFS;
385 	sfsp->f_flags = 0;
386 	sfsp->f_bsize = sblock.fs_fsize;
387 	sfsp->f_iosize = sblock.fs_bsize;
388 	sfsp->f_blocks = sblock.fs_dsize;
389 	sfsp->f_bfree = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag +
390 		sblock.fs_cstotal.cs_nffree;
391 	sfsp->f_bavail = freespace(&sblock, sblock.fs_minfree);
392 	sfsp->f_files =  sblock.fs_ncg * sblock.fs_ipg;
393 	sfsp->f_ffree = sblock.fs_cstotal.cs_nifree;
394 	sfsp->f_fsid.val[0] = 0;
395 	sfsp->f_fsid.val[1] = 0;
396 	if ((mntpt = getmntpt(file)) == 0)
397 		mntpt = "";
398 	memmove(&sfsp->f_mntonname[0], mntpt, MNAMELEN);
399 	memmove(&sfsp->f_mntfromname[0], file, MNAMELEN);
400 	prtstat(sfsp, maxwidth);
401 	(void)close(rfd);
402 }
403 
404 int
405 bread(off, buf, cnt)
406 	off_t off;
407 	void *buf;
408 	int cnt;
409 {
410 	int nr;
411 
412 	(void)lseek(rfd, off, SEEK_SET);
413 	if ((nr = read(rfd, buf, cnt)) != cnt) {
414 		/* Probably a dismounted disk if errno == EIO. */
415 		if (errno != EIO)
416 			(void)fprintf(stderr, "\ndf: %qd: %s\n",
417 			    off, strerror(nr > 0 ? EIO : errno));
418 		return (0);
419 	}
420 	return (1);
421 }
422 
423 void
424 usage()
425 {
426 	fprintf(stderr,
427 		"usage: df [-ikn] [-t fstype] [file | file_system ...]\n");
428 	exit(1);
429 }
430