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