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
61 extern char *getenv();
62 extern char *getcwd();
63 extern char *realpath();
64 extern off_t lseek();
65
66 /*
67 * Raw name to block device name translation function.
68 * This comes from libadm.
69 */
70 extern char *getfullblkname();
71
72 static void usage(), pheader();
73 static char *mpath(char *);
74 static char *zap_chroot(char *);
75 static char *pathsuffix(char *, char *);
76 static char *xmalloc(unsigned int);
77 static int chroot_stat(char *, int (*)(), char *, char **);
78 static int bread(char *, int, daddr_t, char *, int);
79 static int subpath(char *, char *);
80 static int abspath(char *, char *, char *);
81 static void show_inode_usage();
82 static void dfreedev(char *);
83 static void dfreemnt(char *, struct mnttab *);
84 static void print_totals();
85 static void print_itotals();
86 static void print_statvfs(struct statvfs64 *);
87 static int mdev(char *, struct mnttab **);
88 static struct mntlist *mkmntlist();
89 static struct mnttab *mntdup(struct mnttab *mnt);
90 static 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
103 int aflag = 0; /* even the uninteresting ones */
104 int bflag = 0; /* print only number of kilobytes free */
105 int eflag = 0; /* print only number of file entries free */
106 int gflag = 0; /* print entire statvfs structure */
107 int hflag = 0; /* don't print header */
108 int iflag = 0; /* information for inodes */
109 int nflag = 0; /* print VFStype name */
110 int tflag = 0; /* print totals */
111 int errflag = 0;
112 int errcode = 0;
113 char *typestr = "ufs";
114 fsblkcnt64_t t_totalblks, t_avail, t_free, t_used, t_reserved;
115 int t_inodes, t_iused, t_ifree;
116
117 /*
118 * cached information recording previous chroot history.
119 */
120 static char *chrootpath;
121
122 extern int optind;
123 extern char *optarg;
124
125 union {
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 */
135 struct mntlist {
136 struct mnttab *mntl_mnt;
137 struct mntlist *mntl_next;
138 dev_t mntl_dev;
139 int mntl_devvalid;
140 };
141
142 char *subopts [] = {
143 #define A_FLAG 0
144 "a",
145 #define I_FLAG 1
146 "i",
147 NULL
148 };
149
150 int
main(int argc,char * argv[])151 main(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
402 void
pheader()403 pheader()
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 */
467 void
dfreedev(char * file)468 dfreedev(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
557 void
dfreemnt(char * file,struct mnttab * mnt)558 dfreemnt(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
634 static void
show_inode_usage(fsfilcnt64_t total,fsfilcnt64_t free)635 show_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 */
660 static char *
zap_chroot(char * path)661 zap_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 */
671 static int
chroot_stat(char * dir,int (* statfunc)(),char * statp,char ** dirp)672 chroot_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 */
684 char *
mpath(char * file)685 mpath(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
722 int
mdev(char * spec,struct mnttab ** mntbp)723 mdev(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 */
767 static struct mntlist *
findmntent(char * path,struct stat64 * pstat,struct mntlist * mlist)768 findmntent(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
810 again:
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 */
853 static int
abspath(char * wd,char * raw,char * canon)854 abspath(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 */
900 static char *
pathsuffix(char * full,char * pref)901 pathsuffix(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 */
932 static int
subpath(char * full,char * sub)933 subpath(char *full, char *sub)
934 {
935 return (pathsuffix(full, sub) == NULL);
936 }
937
938 offset_t llseek();
939
940 int
bread(char * file,int fi,daddr_t bno,char * buf,int cnt)941 bread(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
963 char *
xmalloc(unsigned int size)964 xmalloc(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
976 struct mnttab *
mntdup(struct mnttab * mnt)977 mntdup(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
1011 void
usage()1012 usage()
1013 {
1014
1015 (void) fprintf(stderr, gettext(
1016 "ufs usage: df [generic options] [-o i] [directory | special]\n"));
1017 exit(1);
1018 }
1019
1020 struct mntlist *
mkmntlist()1021 mkmntlist()
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)) == NULL) {
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
1046 void
print_statvfs(struct statvfs64 * fs)1047 print_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
1069 void
print_totals()1070 print_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
1086 void
print_itotals()1087 print_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