1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27/*	  All Rights Reserved	*/
28
29/*
30 * University Copyright- Copyright (c) 1982, 1986, 1988
31 * The Regents of the University of California
32 * All Rights Reserved
33 *
34 * University Acknowledgment- Portions of this document are derived from
35 * software developed by the University of California, Berkeley, and its
36 * contributors.
37 */
38
39
40/*
41 * df
42 */
43#include <stdio.h>
44#include <fcntl.h>
45#include <sys/param.h>
46#include <sys/types.h>
47#include <sys/mntent.h>
48#include <sys/fs/ufs_fs.h>
49#include <sys/stat.h>
50#include <sys/vfs.h>
51#include <sys/file.h>
52#include <sys/statvfs.h>
53#include <sys/mnttab.h>
54#include <sys/mkdev.h>
55#include <locale.h>
56#include <stdarg.h>
57#include <string.h>
58#include <errno.h>
59#include <libintl.h>
60
61extern char	*getenv();
62extern char	*getcwd();
63extern char	*realpath();
64extern off_t	lseek();
65
66/*
67 * Raw name to block device name translation function.
68 * This comes from libadm.
69 */
70extern char	*getfullblkname();
71
72static  void		usage(), pheader();
73static  char		*mpath(char *);
74static  char		*zap_chroot(char *);
75static  char		*pathsuffix(char *, char *);
76static  char		*xmalloc(unsigned int);
77static  int		chroot_stat(char *, int (*)(), char *, char **);
78static  int		bread(char *, int, daddr_t, char *, int);
79static  int		subpath(char *, char *);
80static	int		abspath(char *, char *, char *);
81static  void		show_inode_usage();
82static  void		dfreedev(char *);
83static  void		dfreemnt(char *, struct mnttab *);
84static  void		print_totals();
85static  void		print_itotals();
86static  void		print_statvfs(struct statvfs64 *);
87static  int		mdev(char *, struct mnttab **);
88static struct mntlist	*mkmntlist();
89static struct mnttab	*mntdup(struct mnttab *mnt);
90static struct mntlist	*findmntent(char *, struct stat64 *, struct mntlist *);
91
92#define	bcopy(f, t, n)	memcpy(t, f, n)
93#define	bzero(s, n)	memset(s, 0, n)
94#define	bcmp(s, d, n)	memcmp(s, d, n)
95
96#define	index(s, r)	strchr(s, r)
97#define	rindex(s, r)	strrchr(s, r)
98
99#define	dbtok(x, b) \
100	((b) < (fsblkcnt64_t)1024 ? \
101	(x) / ((fsblkcnt64_t)1024 / (b)) : (x) * ((b) / (fsblkcnt64_t)1024))
102
103int	aflag = 0;		/* even the uninteresting ones */
104int	bflag = 0;		/* print only number of kilobytes free */
105int	eflag = 0;		/* print only number of file entries free */
106int	gflag = 0;		/* print entire statvfs structure */
107int	hflag = 0;		/* don't print header */
108int	iflag = 0;		/* information for inodes */
109int	nflag = 0;		/* print VFStype name */
110int	tflag = 0;		/* print totals */
111int	errflag = 0;
112int	errcode = 0;
113char	*typestr = "ufs";
114fsblkcnt64_t	t_totalblks, t_avail, t_free, t_used, t_reserved;
115int	t_inodes, t_iused, t_ifree;
116
117/*
118 * cached information recording previous chroot history.
119 */
120static	char	*chrootpath;
121
122extern	int	optind;
123extern	char	*optarg;
124
125union {
126	struct fs iu_fs;
127	char dummy[SBSIZE];
128} sb;
129#define	sblock	sb.iu_fs
130
131/*
132 * This structure is used to chain mntent structures into a list
133 * and to cache stat information for each member of the list.
134 */
135struct mntlist {
136	struct mnttab	*mntl_mnt;
137	struct mntlist	*mntl_next;
138	dev_t		mntl_dev;
139	int		mntl_devvalid;
140};
141
142char *subopts [] = {
143#define	A_FLAG		0
144	"a",
145#define	I_FLAG		1
146	"i",
147	NULL
148};
149
150int
151main(int argc, char *argv[])
152{
153	struct mnttab		mnt;
154	int			opt;
155	char			*suboptions, *value;
156
157	(void) setlocale(LC_ALL, "");
158#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
159#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
160#endif
161	(void) textdomain(TEXT_DOMAIN);
162
163	while ((opt = getopt(argc, argv, "beghkno:t")) != EOF) {
164		switch (opt) {
165
166		case 'b':	/* print only number of kilobytes free */
167			bflag++;
168			break;
169
170		case 'e':
171			eflag++; /* print only number of file entries free */
172			iflag++;
173			break;
174
175		case 'g':
176			gflag++;
177			break;
178
179		case 'n':
180			nflag++;
181			break;
182
183		case 'k':
184			break;
185
186		case 'h':
187			hflag++;
188			break;
189
190		case 'o':
191			/*
192			 * ufs specific options.
193			 */
194			suboptions = optarg;
195			while (*suboptions != '\0') {
196				switch (getsubopt(&suboptions,
197				    subopts, &value)) {
198
199				case I_FLAG:	/* information for inodes */
200					iflag++;
201					break;
202
203				default:
204					usage();
205				}
206			}
207			break;
208
209		case 't':		/* print totals */
210			tflag++;
211			break;
212
213		case 'V':		/* Print command line */
214			{
215				char			*opt_text;
216				int			opt_count;
217
218				(void) fprintf(stdout, "df -F ufs ");
219				for (opt_count = 1; opt_count < argc;
220				    opt_count++) {
221					opt_text = argv[opt_count];
222					if (opt_text)
223						(void) fprintf(stdout, " %s ",
224						    opt_text);
225				}
226				(void) fprintf(stdout, "\n");
227			}
228			break;
229
230		case '?':
231			errflag++;
232		}
233	}
234	if (errflag)
235		usage();
236	if (gflag && iflag) {
237		printf(gettext("df: '-g' and '-o i' are mutually exclusive\n"));
238		exit(1);
239	}
240	if (bflag || eflag)
241		tflag = 0;
242
243	/*
244	 * Cache CHROOT information for later use; assume that $CHROOT matches
245	 * the cumulative arguments given to chroot calls.
246	 */
247	chrootpath = getenv("CHROOT");
248	if (chrootpath != NULL && strcmp(chrootpath, "/") == 0)
249		chrootpath = NULL;
250
251	if (argc <= optind) {
252		/*
253		 * Take this path when "/usr/lib/fs/ufs/df" is specified, and
254		 * there are no mountpoints specified.
255		 * E.g., these command lines take us down this path
256		 *	/usr/lib/fs/ufs/df -o i
257		 *	/usr/lib/fs/ufs/df
258		 */
259		FILE *mtabp;
260
261		if ((mtabp = fopen(MNTTAB, "r")) == NULL) {
262			(void) fprintf(stderr, "df: ");
263			perror(MNTTAB);
264			exit(1);
265		}
266		pheader();
267		while (getmntent(mtabp, &mnt) == 0) {
268			if (strcmp(typestr, mnt.mnt_fstype) != 0) {
269				continue;
270			}
271			dfreemnt(mnt.mnt_mountp, &mnt);
272		}
273		if (tflag)
274			if (iflag)
275				print_itotals();
276			else
277				print_totals();
278		(void) fclose(mtabp);
279	} else {
280		int i;
281		struct mntlist *mntl;
282		struct stat64    *argstat;
283		char **devnames;
284		char *cp;
285
286		/* Arguments are processed till optind, adjust the pointers */
287		argv += optind;
288		argc -= optind;
289
290		/*
291		 * Obtain stat64 information for each argument before
292		 * constructing the list of mounted file systems.  This
293		 * ordering forces the automounter to establish any
294		 * mounts required to access the arguments, so that the
295		 * corresponding mount table entries will exist when
296		 * we look for them.
297		 */
298		argstat = (struct stat64 *)xmalloc(argc * sizeof (*argstat));
299		devnames = (char **)xmalloc(argc * sizeof (char *));
300		for (i = 0; i < argc; i++) {
301
302			/*
303			 * Given a raw device name, get the block device name
304			 */
305			cp = getfullblkname(argv[i]);
306			if (cp == NULL || *cp == '\0') {
307				if (cp != NULL)
308					free(cp);
309				cp = strdup(argv[i]);
310
311				if (cp == NULL) {
312					int j;
313
314					fprintf(stderr, gettext(
315					"df: memory allocation failure\n"));
316
317					for (j = 0; j < i; j++)
318						free(devnames[j]);
319					free(devnames);
320					free(argstat);
321					exit(1);
322				}
323			}
324			if (stat64(cp, &argstat[i]) < 0) {
325				errcode = errno;
326				/*
327				 * Mark as no longer interesting.
328				 */
329				argv[i] = NULL;
330				devnames[i] = NULL;
331				free(cp);
332			} else {
333				devnames[i] = cp;
334			}
335		}
336
337		pheader();
338		aflag++;
339		/*
340		 * Construct the list of mounted file systems.
341		 */
342		mntl = mkmntlist();
343
344		/*
345		 * Iterate through the argument list, reporting on each one.
346		 */
347		for (i = 0; i < argc; i++) {
348			struct mntlist *mlp;
349			int isblk;
350
351			/*
352			 * Skip if we've already determined that we can't
353			 * process it.
354			 */
355			if (argv[i] == NULL)
356				continue;
357
358			/*
359			 * If the argument names a device, report on the file
360			 * system associated with the device rather than on
361			 * the one containing the device's directory entry
362			 */
363			cp = devnames[i];
364			if ((isblk = (argstat[i].st_mode&S_IFMT) == S_IFBLK) ||
365			    (argstat[i].st_mode & S_IFMT) == S_IFCHR) {
366				if (isblk && strcmp(mpath(cp), "") != 0) {
367					struct mnttab *mp;
368					if (mdev(cp, &mp))
369						return (1);
370					dfreemnt(mp->mnt_mountp, mp);
371				} else {
372					dfreedev(cp);
373				}
374				free(cp);
375				devnames[i] = NULL;
376				continue;
377			}
378
379			/*
380			 * Get this argument's corresponding mount table
381			 * entry.
382			 */
383			mlp = findmntent(cp, &argstat[i], mntl);
384			free(cp);
385			devnames[i] = NULL;
386
387			if (mlp == NULL) {
388				(void) fprintf(stderr,
389				gettext("Could not find mount point for %s\n"),
390				    argv[i]);
391				continue;
392			}
393
394			dfreemnt(mlp->mntl_mnt->mnt_mountp, mlp->mntl_mnt);
395		}
396		free(devnames);
397		free(argstat);
398	}
399	return (0);
400}
401
402void
403pheader()
404{
405	if (hflag)
406		return;
407	if (nflag)
408		(void) printf(gettext("VFStype name - ufs\n"));
409	if (iflag) {
410		if (eflag)
411			/*
412			 * TRANSLATION_NOTE
413			 * Following string is used as a table header.
414			 * Translated items should start at the same
415			 * columns as the original items.
416			 */
417			(void) printf(gettext(
418"Filesystem             ifree\n"));
419		else {
420			/*
421			 * TRANSLATION_NOTE
422			 * Following string is used as a table header.
423			 * Translated items should start at the same
424			 * columns as the original items.
425			 */
426			(void) printf(gettext(
427"Filesystem             iused   ifree  %%iused  Mounted on\n"));
428		}
429	} else {
430		if (gflag)
431			/*
432			 * TRANSLATION_NOTE
433			 * Following string is used as a table header.
434			 * Translated items should start at the same
435			 * columns as the original items.
436			 */
437			(void) printf(gettext(
438"Filesystem        f_type f_fsize f_bfree f_bavail f_files f_ffree "
439"f_fsid f_flag f_fstr\n"));
440		else
441			if (bflag)
442				/*
443				 * TRANSLATION_NOTE
444				 * Following string is used as a table header.
445				 * Translated items should start at the same
446				 * columns as the original items.
447				 */
448				(void) printf(gettext(
449"Filesystem             avail\n"));
450			else {
451				/*
452				 * TRANSLATION_NOTE
453				 * Following string is used as a table header.
454				 * Translated items should start at the same
455				 * columns as the original items.
456				 */
457				(void) printf(gettext(
458"Filesystem            kbytes    used   avail capacity  Mounted on\n"));
459			}
460		}
461}
462
463/*
464 * Report on a block or character special device. Assumed not to be
465 * mounted.  N.B. checks for a valid UFS superblock.
466 */
467void
468dfreedev(char *file)
469{
470	fsblkcnt64_t totalblks, availblks, avail, free, used;
471	int fi;
472
473	fi = open64(file, 0);
474	if (fi < 0) {
475		(void) fprintf(stderr, "df: ");
476		perror(file);
477		return;
478	}
479	if (bread(file, fi, SBLOCK, (char *)&sblock, SBSIZE) == 0) {
480		(void) close(fi);
481		return;
482	}
483	if ((sblock.fs_magic != FS_MAGIC) &&
484	    (sblock.fs_magic != MTB_UFS_MAGIC)) {
485		(void) fprintf(stderr, gettext(
486"df: %s: not a ufs file system\n"),
487		    file);
488		(void) close(fi);
489		return;
490	}
491	if (sblock.fs_magic == FS_MAGIC &&
492	    (sblock.fs_version != UFS_EFISTYLE4NONEFI_VERSION_2 &&
493	    sblock.fs_version != UFS_VERSION_MIN)) {
494		(void) fprintf(stderr, gettext(
495"df: %s: unrecognized version of UFS: %d\n"),
496		    file, sblock.fs_version);
497		(void) close(fi);
498		return;
499	}
500	if (sblock.fs_magic == MTB_UFS_MAGIC &&
501	    (sblock.fs_version > MTB_UFS_VERSION_1 ||
502	    sblock.fs_version < MTB_UFS_VERSION_MIN)) {
503		(void) fprintf(stderr, gettext(
504"df: %s: unrecognized version of UFS: %d\n"),
505		    file, sblock.fs_version);
506		(void) close(fi);
507		return;
508	}
509	(void) printf("%-20.20s", file);
510	if (iflag) {
511		if (eflag) {
512			(void) printf("%8ld", sblock.fs_cstotal.cs_nifree);
513		} else {
514			show_inode_usage(
515			    (fsfilcnt64_t)sblock.fs_ncg *
516			    (fsfilcnt64_t)sblock.fs_ipg,
517			    (fsfilcnt64_t)sblock.fs_cstotal.cs_nifree);
518		}
519	} else {
520		totalblks = (fsblkcnt64_t)sblock.fs_dsize;
521		free =
522		    (fsblkcnt64_t)sblock.fs_cstotal.cs_nbfree *
523		    (fsblkcnt64_t)sblock.fs_frag +
524		    (fsblkcnt64_t)sblock.fs_cstotal.cs_nffree;
525		used = totalblks - free;
526		availblks = totalblks / (fsblkcnt64_t)100 *
527		    ((fsblkcnt64_t)100 - (fsblkcnt64_t)sblock.fs_minfree);
528		avail = availblks > used ? availblks - used : (fsblkcnt64_t)0;
529		if (bflag) {
530			(void) printf("%8lld\n", dbtok(avail,
531			    (fsblkcnt64_t)sblock.fs_fsize));
532		} else {
533			(void) printf(" %7lld %7lld %7lld",
534			    dbtok(totalblks, (fsblkcnt64_t)sblock.fs_fsize),
535			    dbtok(used, (fsblkcnt64_t)sblock.fs_fsize),
536			    dbtok(avail, (fsblkcnt64_t)sblock.fs_fsize));
537			(void) printf("%6.0f%%",
538			    availblks == 0 ? 0.0 :
539			    (double)used / (double)availblks * 100.0);
540			(void) printf("  ");
541		}
542		if (tflag) {
543			t_totalblks += dbtok(totalblks,
544			    (fsblkcnt64_t)sblock.fs_fsize);
545			t_used += dbtok(used, (fsblkcnt64_t)sblock.fs_fsize);
546			t_avail += dbtok(avail, (fsblkcnt64_t)sblock.fs_fsize);
547			t_free += free;
548		}
549	}
550	if ((!bflag) && (!eflag))
551		(void) printf("  %s\n", mpath(file));
552	else if (eflag)
553		(void) printf("\n");
554	(void) close(fi);
555}
556
557void
558dfreemnt(char *file, struct mnttab *mnt)
559{
560	struct statvfs64 fs;
561
562	if (statvfs64(file, &fs) < 0 &&
563	    chroot_stat(file, statvfs64, (char *)&fs, &file) < 0) {
564		(void) fprintf(stderr, "df: ");
565		perror(file);
566		return;
567	}
568
569	if (!aflag && fs.f_blocks == 0) {
570		return;
571	}
572	if (!isatty(fileno(stdout))) {
573		(void) printf("%s", mnt->mnt_special);
574	} else {
575		if (strlen(mnt->mnt_special) > (size_t)20) {
576			(void) printf("%s\n", mnt->mnt_special);
577			(void) printf("                    ");
578		} else {
579			(void) printf("%-20.20s", mnt->mnt_special);
580		}
581	}
582	if (iflag) {
583		if (eflag) {
584			(void) printf("%8lld", fs.f_ffree);
585		} else {
586			show_inode_usage(fs.f_files, fs.f_ffree);
587		}
588	} else {
589		if (gflag) {
590			print_statvfs(&fs);
591		} else {
592			fsblkcnt64_t totalblks, avail, free, used, reserved;
593
594			totalblks = fs.f_blocks;
595			free = fs.f_bfree;
596			used = totalblks - free;
597			avail = fs.f_bavail;
598			reserved = free - avail;
599			if ((long long)avail < 0)
600				avail = 0;
601			if (bflag) {
602				(void) printf("%8lld\n", dbtok(avail,
603				    (fsblkcnt64_t)fs.f_frsize));
604			} else {
605				(void) printf(" %7lld %7lld %7lld",
606				    dbtok(totalblks,
607				    (fsblkcnt64_t)fs.f_frsize),
608				    dbtok(used, (fsblkcnt64_t)fs.f_frsize),
609				    dbtok(avail, (fsblkcnt64_t)fs.f_frsize));
610				totalblks -= reserved;
611				(void) printf("%6.0f%%",
612				    totalblks == 0 ? 0.0 :
613				    (double)used / (double)totalblks * 100.0);
614				(void) printf("  ");
615				if (tflag) {
616				t_totalblks += dbtok(totalblks + reserved,
617				    (fsblkcnt64_t)fs.f_bsize);
618				t_reserved += reserved;
619				t_used += dbtok(used,
620				    (fsblkcnt64_t)fs.f_frsize);
621				t_avail += dbtok(avail,
622				    (fsblkcnt64_t)fs.f_frsize);
623				t_free += free;
624				}
625			}
626		}
627	}
628	if ((!bflag) && (!eflag) && (!gflag))
629		(void) printf("  %s\n", mnt->mnt_mountp);
630	else if (eflag)
631		(void) printf("\n");
632}
633
634static void
635show_inode_usage(fsfilcnt64_t total, fsfilcnt64_t free)
636{
637	fsfilcnt64_t used = total - free;
638	int missing_info = ((long long)total == (long long)-1 ||
639	    (long long)free == (long long)-1);
640
641	if (missing_info)
642		(void) printf("%8s", "*");
643	else
644		(void) printf("%8lld", used);
645	if ((long long)free == (long long)-1)
646		(void) printf("%8s", "*");
647	else
648		(void) printf(" %7lld", free);
649	if (missing_info)
650		(void) printf("%6s  ", "*");
651	else
652		(void) printf("%6.0f%% ", (double)used / (double)total * 100.0);
653}
654
655/*
656 * Return the suffix of path obtained by stripping off the prefix
657 * that is the value of the CHROOT environment variable.  If this
658 * value isn't obtainable or if it's not a prefix of path, return NULL.
659 */
660static char *
661zap_chroot(char *path)
662{
663	return (pathsuffix(path, chrootpath));
664}
665
666/*
667 * Stat/statfs a file after stripping off leading directory to which we are
668 * chroot'd.  Used to find the TFS mount that applies to the current
669 * activated NSE environment.
670 */
671static int
672chroot_stat(char *dir, int (*statfunc)(), char *statp, char **dirp)
673{
674	if ((dir = zap_chroot(dir)) == NULL)
675		return (-1);
676	if (dirp)
677		*dirp = dir;
678	return (*statfunc)(dir, statp);
679}
680
681/*
682 * Given a name like /dev/dsk/c1d0s2, returns the mounted path, like /usr.
683 */
684char *
685mpath(char *file)
686{
687	struct mnttab mnt;
688	FILE *mnttab;
689	struct stat64 device_stat, mount_stat;
690	char *mname;
691
692	mnttab = fopen(MNTTAB, "r");
693	if (mnttab == NULL) {
694		return ("");
695	}
696	mname = "";
697	while ((getmntent(mnttab, &mnt)) == 0) {
698		if (strcmp(mnt.mnt_fstype, MNTTYPE_UFS) != 0) {
699			continue;
700		}
701		if (strcmp(file, mnt.mnt_special) == 0) {
702			if (stat64(mnt.mnt_mountp, &mount_stat) != 0)
703				continue;
704			if (stat64(mnt.mnt_special, &device_stat) != 0)
705				continue;
706
707			if (device_stat.st_rdev == mount_stat.st_dev) {
708				mname = mnt.mnt_mountp;
709				break;
710			}
711		}
712	}
713	fclose(mnttab);
714	return (mname);
715}
716
717/*
718 * Given a special device, return mnttab entry
719 * Returns 0 on success
720 */
721
722int
723mdev(char *spec, struct mnttab **mntbp)
724{
725	FILE *mntp;
726	struct mnttab mnt;
727
728	if ((mntp = fopen(MNTTAB, "r")) == 0) {
729		(void) fprintf(stderr, "df: ");
730		perror(MNTTAB);
731		return (1);
732	}
733
734	while (getmntent(mntp, &mnt) == 0) {
735		if (strcmp(spec, mnt.mnt_special) == 0) {
736			(void) fclose(mntp);
737			*mntbp =  mntdup(&mnt);
738			return (0);
739		}
740	}
741	(void) fclose(mntp);
742	(void) fprintf(stderr, "df : couldn't find mnttab entry for %s", spec);
743	return (1);
744}
745
746/*
747 * Find the entry in mlist that corresponds to the file named by path
748 * (i.e., that names a mount table entry for the file system in which
749 * path lies).  The pstat argument must point to stat information for
750 * path.
751 *
752 * Return the entry or NULL if there's no match.
753 *
754 * As it becomes necessary to obtain stat information about previously
755 * unexamined mlist entries, gather the information and cache it with the
756 * entries.
757 *
758 * The routine's strategy is to convert path into its canonical, symlink-free
759 * representation canon (which will require accessing the file systems on the
760 * branch from the root to path and thus may cause the routine to hang if any
761 * of them are inaccessible) and to use it to search for a mount point whose
762 * name is a substring of canon and whose corresponding device matches that of
763 * canon.  This technique avoids accessing unnecessary file system resources
764 * and thus prevents the program from hanging on inaccessible resources unless
765 * those resources are necessary for accessing path.
766 */
767static struct mntlist *
768findmntent(char *path, struct stat64 *pstat, struct mntlist *mlist)
769{
770	static char		cwd[MAXPATHLEN];
771	char			canon[MAXPATHLEN];
772	char			scratch[MAXPATHLEN];
773	struct mntlist *mlp;
774
775	/*
776	 * If path is relative and we haven't already determined the current
777	 * working directory, do so now.  Calculating the working directory
778	 * here lets us do the work once, instead of (potentially) repeatedly
779	 * in realpath().
780	 */
781	if (*path != '/' && cwd[0] == '\0') {
782		if (getcwd(cwd, MAXPATHLEN) == NULL) {
783			cwd[0] = '\0';
784			return (NULL);
785		}
786	}
787
788	/*
789	 * Find an absolute pathname in the native file system name space that
790	 * corresponds to path, stuffing it into canon.
791	 *
792	 * If CHROOT is set in the environment, assume that chroot($CHROOT)
793	 * (or an equivalent series of calls) was executed and convert the
794	 * path to the equivalent name in the native file system's name space.
795	 * Doing so allows direct comparison with the names in mtab entires,
796	 * which are assumed to be recorded relative to the native name space.
797	 */
798	if (abspath(cwd, path, scratch) < 0)
799		return (NULL);
800	if (strcmp(scratch, "/") == 0 && chrootpath != NULL) {
801		/*
802		 * Force canon to be in canonical form; if the result from
803		 * abspath was "/" and chrootpath isn't the null string, we
804		 * must strip off a trailing slash.
805		 */
806		scratch[0] = '\0';
807	}
808	(void) sprintf(canon, "%s%s", chrootpath ? chrootpath : "", scratch);
809
810again:
811	for (mlp = mlist; mlp; mlp = mlp->mntl_next) {
812		struct mnttab *mnt = mlp->mntl_mnt;
813
814		/*
815		 * Ignore uninteresting mounts.
816		 */
817		if (strcmp(mnt->mnt_fstype, typestr) != 0)
818			continue;
819
820		/*
821		 * The mount entry covers some prefix of the file.
822		 * See whether it's the entry for the file system
823		 * containing the file by comparing device ids.
824		 */
825		if (mlp->mntl_dev == NODEV) {
826			struct stat64 fs_sb;
827
828			if (stat64(mnt->mnt_mountp, &fs_sb) < 0 &&
829			    chroot_stat(mnt->mnt_mountp, stat64, (char *)&fs_sb,
830			    (char **)NULL) < 0) {
831				continue;
832			}
833			mlp->mntl_dev = fs_sb.st_dev;
834		}
835
836		if (pstat->st_dev == mlp->mntl_dev)
837			return (mlp);
838	}
839
840	return (NULL);
841}
842
843/*
844 * Convert the path given in raw to canonical, absolute, symlink-free
845 * form, storing the result in the buffer named by canon, which must be
846 * at least MAXPATHLEN bytes long.  "wd" contains the current working
847 * directory; accepting this value as an argument lets our caller cache
848 * the value, so that realpath (called from this routine) doesn't have
849 * to recalculate it each time it's given a relative pathname.
850 *
851 * Return 0 on success, -1 on failure.
852 */
853static int
854abspath(char *wd, char *raw, char *canon)
855{
856	char		absbuf[MAXPATHLEN];
857
858	/*
859	 * Preliminary sanity check.
860	 */
861	if (wd == NULL || raw == NULL || canon == NULL)
862		return (-1);
863
864	/*
865	 * If the path is relative, convert it to absolute form,
866	 * using wd if it's been supplied.
867	 */
868	if (raw[0] != '/') {
869		char	*limit = absbuf + sizeof (absbuf);
870		char	*d;
871
872		/* Fill in working directory. */
873		if (strlcpy(absbuf, wd, sizeof (absbuf)) >= sizeof (absbuf))
874			return (-1);
875
876		/* Add separating slash. */
877		d = absbuf + strlen(absbuf);
878		if (d < limit)
879			*d++ = '/';
880
881		/* Glue on the relative part of the path. */
882		while (d < limit && (*d++ = *raw++))
883			continue;
884
885		raw = absbuf;
886	}
887
888	/*
889	 * Call realpath to canonicalize and resolve symlinks.
890	 */
891	return (realpath(raw, canon) == NULL ? -1 : 0);
892}
893
894/*
895 * Return a pointer to the trailing suffix of full that follows the prefix
896 * given by pref.  If pref isn't a prefix of full, return NULL.  Apply
897 * pathname semantics to the prefix test, so that pref must match at a
898 * component boundary.
899 */
900static char *
901pathsuffix(char *full, char *pref)
902{
903	int preflen;
904
905	if (full == NULL || pref == NULL)
906		return (NULL);
907
908	preflen = strlen(pref);
909	if (strncmp(pref, full, preflen) != 0)
910		return (NULL);
911
912	/*
913	 * pref is a substring of full.  To be a subpath, it cannot cover a
914	 * partial component of full.  The last clause of the test handles the
915	 * special case of the root.
916	 */
917	if (full[preflen] != '\0' && full[preflen] != '/' && preflen > 1)
918		return (NULL);
919
920	if (preflen == 1 && full[0] == '/')
921		return (full);
922	else
923		return (full + preflen);
924}
925
926/*
927 * Return zero iff the path named by sub is a leading subpath
928 * of the path named by full.
929 *
930 * Treat null paths as matching nothing.
931 */
932static int
933subpath(char *full, char *sub)
934{
935	return (pathsuffix(full, sub) == NULL);
936}
937
938offset_t llseek();
939
940int
941bread(char *file, int fi, daddr_t bno, char *buf, int cnt)
942{
943	int n;
944
945	(void) llseek(fi, (offset_t)bno * DEV_BSIZE, 0);
946	if ((n = read(fi, buf, cnt)) < 0) {
947		/* probably a dismounted disk if errno == EIO */
948		if (errno != EIO) {
949			(void) fprintf(stderr, gettext("df: read error on "));
950			perror(file);
951			(void) fprintf(stderr, "bno = %ld\n", bno);
952		} else {
953			(void) fprintf(stderr, gettext(
954"df: premature EOF on %s\n"), file);
955			(void) fprintf(stderr,
956			"bno = %ld expected = %d count = %d\n", bno, cnt, n);
957		}
958		return (0);
959	}
960	return (1);
961}
962
963char *
964xmalloc(unsigned int size)
965{
966	char *ret;
967	char *malloc();
968
969	if ((ret = (char *)malloc(size)) == NULL) {
970		(void) fprintf(stderr, gettext("umount: ran out of memory!\n"));
971		exit(1);
972	}
973	return (ret);
974}
975
976struct mnttab *
977mntdup(struct mnttab *mnt)
978{
979	struct mnttab *new;
980
981	new = (struct mnttab *)xmalloc(sizeof (*new));
982
983	new->mnt_special =
984	    (char *)xmalloc((unsigned)(strlen(mnt->mnt_special) + 1));
985	(void) strcpy(new->mnt_special, mnt->mnt_special);
986
987	new->mnt_mountp =
988	    (char *)xmalloc((unsigned)(strlen(mnt->mnt_mountp) + 1));
989	(void) strcpy(new->mnt_mountp, mnt->mnt_mountp);
990
991	new->mnt_fstype =
992	    (char *)xmalloc((unsigned)(strlen(mnt->mnt_fstype) + 1));
993	(void) strcpy(new->mnt_fstype, mnt->mnt_fstype);
994
995	if (mnt->mnt_mntopts != NULL) {
996		new->mnt_mntopts =
997		    (char *)xmalloc((unsigned)(strlen(mnt->mnt_mntopts) + 1));
998		(void) strcpy(new->mnt_mntopts, mnt->mnt_mntopts);
999	} else {
1000		new->mnt_mntopts = NULL;
1001	}
1002
1003#ifdef never
1004	new->mnt_freq = mnt->mnt_freq;
1005	new->mnt_passno = mnt->mnt_passno;
1006#endif /* never */
1007
1008	return (new);
1009}
1010
1011void
1012usage()
1013{
1014
1015	(void) fprintf(stderr, gettext(
1016"ufs usage: df [generic options] [-o i] [directory | special]\n"));
1017	exit(1);
1018}
1019
1020struct mntlist *
1021mkmntlist()
1022{
1023	FILE *mounted;
1024	struct mntlist *mntl;
1025	struct mntlist *mntst = NULL;
1026	struct extmnttab mnt;
1027
1028	if ((mounted = fopen(MNTTAB, "r")) == NULL) {
1029		(void) fprintf(stderr, "df : ");
1030		perror(MNTTAB);
1031		exit(1);
1032	}
1033	resetmnttab(mounted);
1034	while (getextmntent(mounted, &mnt, sizeof (struct extmnttab)) == 0) {
1035		mntl = (struct mntlist *)xmalloc(sizeof (*mntl));
1036		mntl->mntl_mnt = mntdup((struct mnttab *)(&mnt));
1037		mntl->mntl_next = mntst;
1038		mntl->mntl_devvalid = 1;
1039		mntl->mntl_dev = makedev(mnt.mnt_major, mnt.mnt_minor);
1040		mntst = mntl;
1041	}
1042	(void) fclose(mounted);
1043	return (mntst);
1044}
1045
1046void
1047print_statvfs(struct statvfs64 *fs)
1048{
1049	int	i;
1050
1051	for (i = 0; i < FSTYPSZ; i++)
1052		(void) printf("%c", fs->f_basetype[i]);
1053	(void) printf(" %7d %7lld %7lld",
1054	    fs->f_frsize,
1055	    fs->f_blocks,
1056	    fs->f_bavail);
1057	(void) printf(" %7lld %7lld %7d",
1058	    fs->f_files,
1059	    fs->f_ffree,
1060	    fs->f_fsid);
1061	(void) printf(" 0x%x ",
1062	    fs->f_flag);
1063	for (i = 0; i < 14; i++)
1064		(void) printf("%c",
1065		    (fs->f_fstr[i] == '\0') ? ' ' : fs->f_fstr[i]);
1066	printf("\n");
1067}
1068
1069void
1070print_totals()
1071{
1072	/*
1073	 * TRANSLATION_NOTE
1074	 * Following string is used as a table header.
1075	 * Translated items should start at the same
1076	 * columns as the original items.
1077	 */
1078	(void) printf(gettext("Totals              %8lld %7lld %7lld"),
1079	    t_totalblks, t_used, t_avail);
1080	(void) printf("%6.0f%%\n",
1081	    (t_totalblks - t_reserved) == (fsblkcnt64_t)0 ?
1082	    0.0 :
1083	    (double)t_used / (double)(t_totalblks - t_reserved) * 100.0);
1084}
1085
1086void
1087print_itotals()
1088{
1089	/*
1090	 * TRANSLATION_NOTE
1091	 * Following string is used as a table header.
1092	 * Translated items should start at the same
1093	 * columns as the original items.
1094	 */
1095	(void) printf(gettext("Totals              %8d %7d%6.0f%%\n"),
1096	    t_iused,
1097	    t_ifree,
1098	    t_inodes == 0 ? 0.0 : (double)t_iused / (double)t_inodes * 100.0);
1099}
1100