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.
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.
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]
21 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
22 /* All Rights Reserved */
26 * Copyright (c) 1989, 2010, Oracle and/or its affiliates. All rights reserved.
30 * Copyright 2016 Jason King
42 #include <sys/types.h>
44 #include <sys/statvfs.h>
46 #include <sys/param.h>
47 #include <sys/mnttab.h>
48 #include <sys/mntent.h>
49 #include <sys/vfstab.h>
51 #include <sys/mkdev.h>
52 #include <sys/int_limits.h>
54 #include <sys/debug.h>
56 #include <libcmdutils.h>
60 extern char *default_fstype(char *);
64 * String pointers in this code may point to statically allocated memory
65 * or dynamically allocated memory. Furthermore, a dynamically allocated
66 * string may be pointed to by more than one pointer. This does not pose
67 * a problem because malloc'ed memory is never free'd (so we don't need
68 * to remember which pointers point to malloc'ed memory).
73 * Only strings passed as arguments to the TRANSLATE macro need to
78 #define MNTTYPE_LOFS "lofs"
81 #define EQ(s1, s2) (strcmp(s1, s2) == 0)
82 #define NEW(type) xmalloc(sizeof (type))
83 #define CLEAR(var) (void) memset(&(var), 0, sizeof (var))
84 #define MAX(a, b) ((a) > (b) ? (a) : (b))
85 #define MAX3(a, b, c) MAX(a, MAX(b, c))
86 #define TRANSLATE(s) new_string(gettext(s))
88 #define MAX_OPTIONS 36
90 #define MOUNT_TABLE_ENTRIES 40 /* initial allocation */
91 #define MSGBUF_SIZE 1024
92 #define LINEBUF_SIZE 256 /* either input or output lines */
94 #define BLOCK_SIZE 512 /* when reporting in terms of blocks */
96 #define DEVNM_CMD "devnm"
97 #define FS_LIBPATH "/usr/lib/fs/"
98 #define MOUNT_TAB "/etc/mnttab"
99 #define VFS_TAB "/etc/vfstab"
100 #define REMOTE_FS "/etc/dfs/fstypes"
107 * Formatting constants
109 #define IBCS2_FILESYSTEM_WIDTH 15 /* Truncate to match ISC/SCO */
110 #define IBCS2_MOUNT_POINT_WIDTH 10 /* Truncate to match ISC/SCO */
111 #define FILESYSTEM_WIDTH 20
112 #define MOUNT_POINT_WIDTH 19
113 #define SPECIAL_DEVICE_WIDTH 18
114 #define FSTYPE_WIDTH 8
115 #define BLOCK_WIDTH 8
116 #define NFILES_WIDTH 8
117 #define KBYTE_WIDTH 11
118 #define AVAILABLE_WIDTH 10
119 #define SCALED_WIDTH 6
120 #define CAPACITY_WIDTH 9
121 #define BSIZE_WIDTH 6
122 #define FRAGSIZE_WIDTH 7
125 #define NAMELEN_WIDTH 7
126 #define MNT_SPEC_WIDTH MOUNT_POINT_WIDTH + SPECIAL_DEVICE_WIDTH + 2
129 * Flags for the errmsg() function
131 #define ERR_NOFLAGS 0x0
132 #define ERR_NONAME 0x1 /* don't include the program name */
134 #define ERR_FATAL 0x2 /* call exit after printing the */
136 #define ERR_PERROR 0x4 /* append an errno explanation to */
138 #define ERR_USAGE 0x8 /* print the usage line after the */
141 #define NUMBER_WIDTH 40
142 CTASSERT(NUMBER_WIDTH
>= NN_NUMBUF_SZ
);
145 * A numbuf_t is used when converting a number to a string representation
147 typedef char numbuf_t
[ NUMBER_WIDTH
];
150 * We use bool_int instead of int to make clear which variables are
151 * supposed to be boolean
153 typedef int bool_int
;
156 bool_int mte_dev_is_valid
;
158 bool_int mte_ignore
; /* the "ignore" option was set */
159 struct extmnttab
*mte_mount
;
165 char *dfr_cmd_arg
; /* what the user specified */
166 struct mtab_entry
*dfr_mte
;
168 int dfr_index
; /* to make qsort stable */
171 #define DFR_MOUNT_POINT(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_mountp
172 #define DFR_SPECIAL(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_special
173 #define DFR_FSTYPE(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_fstype
174 #define DFR_ISMOUNTEDFS(dfrp) ((dfrp)->dfr_mte != NULL)
176 #define DFRP(p) ((struct df_request *)(p))
178 typedef void (*output_func
)(struct df_request
*, struct statvfs64
*);
181 output_func dfo_func
; /* function that will do the output */
188 #define DFO_NOFLAGS 0x0
189 #define DFO_HEADER 0x1 /* output preceded by header */
190 #define DFO_STATVFS 0x2 /* must do a statvfs64(2) */
193 static char *program_name
;
194 static char df_options
[MAX_OPTIONS
] = "-";
195 static size_t df_options_len
= 1;
196 static char *o_option_arg
; /* arg to the -o option */
198 static char *remote_fstypes
[N_FSTYPES
+1]; /* allocate an extra one */
199 /* to use as a terminator */
202 * The following three variables support an in-memory copy of the mount table
203 * to speedup searches.
205 static struct mtab_entry
*mount_table
; /* array of mtab_entry's */
206 static size_t mount_table_entries
;
207 static size_t mount_table_allocated_entries
;
209 static bool_int F_option
;
210 static bool_int V_option
;
211 static bool_int P_option
; /* Added for XCU4 compliance */
212 static bool_int Z_option
;
213 static bool_int v_option
;
214 static bool_int a_option
;
215 static bool_int b_option
;
216 static bool_int e_option
;
217 static bool_int g_option
;
218 static bool_int h_option
;
219 static bool_int k_option
;
220 static bool_int l_option
;
221 static bool_int m_option
;
222 static bool_int n_option
;
223 static bool_int t_option
;
224 static bool_int o_option
;
226 static bool_int tty_output
;
227 static bool_int use_scaling
;
229 static void usage(void);
230 static void do_devnm(int, char **);
231 static void do_df(int, char **) __NORETURN
;
232 static void parse_options(int, char **);
233 static char *basename(char *);
235 static libzfs_handle_t
*(*_libzfs_init
)(void);
236 static zfs_handle_t
*(*_zfs_open
)(libzfs_handle_t
*, const char *, int);
237 static void (*_zfs_close
)(zfs_handle_t
*);
238 static uint64_t (*_zfs_prop_get_int
)(zfs_handle_t
*, zfs_prop_t
);
239 static libzfs_handle_t
*g_zfs
;
242 * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs
243 * packages. A basic utility such as df shouldn't depend on optional
251 if (_libzfs_init
!= NULL
)
252 return (g_zfs
!= NULL
);
254 if ((hdl
= dlopen("libzfs.so", RTLD_LAZY
)) != NULL
) {
255 _libzfs_init
= (libzfs_handle_t
*(*)(void))dlsym(hdl
,
257 _zfs_open
= (zfs_handle_t
*(*)())dlsym(hdl
, "zfs_open");
258 _zfs_close
= (void (*)())dlsym(hdl
, "zfs_close");
259 _zfs_prop_get_int
= (uint64_t (*)())
260 dlsym(hdl
, "zfs_prop_get_int");
262 if (_libzfs_init
!= NULL
) {
263 assert(_zfs_open
!= NULL
);
264 assert(_zfs_close
!= NULL
);
265 assert(_zfs_prop_get_int
!= NULL
);
267 g_zfs
= _libzfs_init();
271 return (g_zfs
!= NULL
);
275 main(int argc
, char *argv
[])
277 (void) setlocale(LC_ALL
, "");
279 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
280 #define TEXT_DOMAIN "SYS_TEST"
282 (void) textdomain(TEXT_DOMAIN
);
284 program_name
= basename(argv
[0]);
286 if (EQ(program_name
, DEVNM_CMD
))
287 do_devnm(argc
, argv
);
289 parse_options(argc
, argv
);
292 * The k_option implies SunOS 4.x compatibility: when the special
293 * device name is too long the line will be split except when the
294 * output has been redirected.
295 * This is also valid for the -h option.
298 if (use_scaling
|| k_option
|| P_option
|| v_option
)
299 tty_output
= isatty(1);
301 do_df(argc
- optind
, &argv
[optind
]);
307 * Prints an error message to stderr.
311 errmsg(int flags
, char *fmt
, ...)
313 char buf
[MSGBUF_SIZE
];
318 if (flags
& ERR_NONAME
)
321 offset
= sprintf(buf
, "%s: ", program_name
);
324 cc
= vsprintf(&buf
[offset
], gettext(fmt
), ap
);
328 if (flags
& ERR_PERROR
) {
329 if (buf
[offset
-1] != ' ')
330 (void) strcat(buf
, " ");
331 (void) strcat(buf
, strerror(errno
));
333 (void) fprintf(stderr
, "%s\n", buf
);
334 if (flags
& ERR_USAGE
)
336 if (flags
& ERR_FATAL
)
345 "Usage: %s [-F FSType] [-abeghklmntPVvZ]"
346 " [-o FSType-specific_options]"
347 " [directory | block_device | resource]", program_name
);
362 errmsg(ERR_FATAL
, "out of memory");
370 * Allocate memory using malloc but terminate if the allocation fails
375 void *p
= malloc(size
);
379 errmsg(ERR_FATAL
, "out of memory");
386 * Allocate memory using realloc but terminate if the allocation fails
389 xrealloc(void *ptr
, size_t size
)
391 void *p
= realloc(ptr
, size
);
395 errmsg(ERR_FATAL
, "out of memory");
402 * fopen the specified file for reading but terminate if the fopen fails
407 FILE *fp
= fopen(file
, "r");
410 errmsg(ERR_FATAL
+ ERR_PERROR
, "failed to open %s:", file
);
416 * Read remote file system types from REMOTE_FS into the
417 * remote_fstypes array.
423 char line_buf
[LINEBUF_SIZE
];
424 size_t fstype_index
= 0;
426 if ((fp
= fopen(REMOTE_FS
, "r")) == NULL
) {
428 "Warning: can't open %s, ignored", REMOTE_FS
);
432 while (fgets(line_buf
, sizeof (line_buf
), fp
) != NULL
) {
433 char buf
[LINEBUF_SIZE
];
435 (void) sscanf(line_buf
, "%s", buf
);
436 remote_fstypes
[fstype_index
++] = new_string(buf
);
438 if (fstype_index
== N_FSTYPES
)
446 * Returns TRUE if fstype is a remote file system type;
447 * otherwise, returns FALSE.
450 is_remote_fs(char *fstype
)
453 static bool_int remote_fs_initialized
;
455 if (! remote_fs_initialized
) {
457 remote_fs_initialized
= TRUE
;
460 for (p
= remote_fstypes
; *p
; p
++)
470 char *p
= strrchr(s
, '/');
472 return (p
? p
+1 : s
);
477 * Create a new "struct extmnttab" and make sure that its fields point
478 * to malloc'ed memory
480 static struct extmnttab
*
481 mntdup(struct extmnttab
*old
)
483 struct extmnttab
*new = NEW(struct extmnttab
);
485 new->mnt_special
= new_string(old
->mnt_special
);
486 new->mnt_mountp
= new_string(old
->mnt_mountp
);
487 new->mnt_fstype
= new_string(old
->mnt_fstype
);
488 new->mnt_mntopts
= new_string(old
->mnt_mntopts
);
489 new->mnt_time
= new_string(old
->mnt_time
);
490 new->mnt_major
= old
->mnt_major
;
491 new->mnt_minor
= old
->mnt_minor
;
497 mtab_error(char *mtab_file
, int status
)
499 if (status
== MNT_TOOLONG
)
500 errmsg(ERR_NOFLAGS
, "a line in %s exceeds %d characters",
501 mtab_file
, MNT_LINE_MAX
);
502 else if (status
== MNT_TOOMANY
)
504 "a line in %s has too many fields", mtab_file
);
505 else if (status
== MNT_TOOFEW
)
507 "a line in %s has too few fields", mtab_file
);
510 "error while reading %s: %d", mtab_file
, status
);
517 * Read the mount table from the specified file.
518 * We keep the table in memory for faster lookups.
523 char *mtab_file
= MOUNT_TAB
;
525 struct extmnttab mtab
;
528 fp
= xfopen(mtab_file
);
531 mount_table_allocated_entries
= MOUNT_TABLE_ENTRIES
;
532 mount_table_entries
= 0;
533 mount_table
= xmalloc(
534 mount_table_allocated_entries
* sizeof (struct mtab_entry
));
536 while ((status
= getextmntent(fp
, &mtab
, sizeof (struct extmnttab
)))
538 struct mtab_entry
*mtep
;
540 if (mount_table_entries
== mount_table_allocated_entries
) {
541 mount_table_allocated_entries
+= MOUNT_TABLE_ENTRIES
;
542 mount_table
= xrealloc(mount_table
,
543 mount_table_allocated_entries
*
544 sizeof (struct mtab_entry
));
546 mtep
= &mount_table
[mount_table_entries
++];
547 mtep
->mte_mount
= mntdup(&mtab
);
548 mtep
->mte_dev_is_valid
= FALSE
;
549 mtep
->mte_ignore
= (hasmntopt((struct mnttab
*)&mtab
,
550 MNTOPT_IGNORE
) != NULL
);
555 if (status
== -1) /* reached EOF */
557 mtab_error(mtab_file
, status
);
563 * We use this macro when we want to record the option for the purpose of
564 * passing it to the FS-specific df
566 #define SET_OPTION(opt) opt##_option = TRUE, \
567 df_options[df_options_len++] = arg
570 parse_options(int argc
, char *argv
[])
574 opterr
= 0; /* getopt shouldn't complain about unknown options */
576 while ((arg
= getopt(argc
, argv
, "F:o:abehkVtgnlmPvZ")) != EOF
) {
579 errmsg(ERR_FATAL
+ ERR_USAGE
,
580 "more than one FSType specified");
583 } else if (arg
== 'V' && ! V_option
) {
585 } else if (arg
== 'v' && ! v_option
) {
587 } else if (arg
== 'P' && ! P_option
) {
589 } else if (arg
== 'a' && ! a_option
) {
591 } else if (arg
== 'b' && ! b_option
) {
593 } else if (arg
== 'e' && ! e_option
) {
595 } else if (arg
== 'g' && ! g_option
) {
597 } else if (arg
== 'h') {
599 } else if (arg
== 'k' && ! k_option
) {
601 } else if (arg
== 'l' && ! l_option
) {
603 } else if (arg
== 'm' && ! m_option
) {
605 } else if (arg
== 'n' && ! n_option
) {
607 } else if (arg
== 't' && ! t_option
) {
609 } else if (arg
== 'o') {
611 errmsg(ERR_FATAL
+ ERR_USAGE
,
612 "the -o option can only be specified once");
614 o_option_arg
= optarg
;
615 } else if (arg
== 'Z') {
617 } else if (arg
== '?') {
618 errmsg(ERR_USAGE
, "unknown option: %c", optopt
);
623 * Option sanity checks
625 if (g_option
&& o_option
)
626 errmsg(ERR_FATAL
, "-o and -g options are incompatible");
627 if (l_option
&& o_option
)
628 errmsg(ERR_FATAL
, "-o and -l options are incompatible");
629 if (n_option
&& o_option
)
630 errmsg(ERR_FATAL
, "-o and -n options are incompatible");
631 if (use_scaling
&& o_option
)
632 errmsg(ERR_FATAL
, "-o and -h options are incompatible");
638 * Check if the user-specified argument is a resource name.
639 * A resource name is whatever is placed in the mnt_special field of
640 * struct mnttab. In the case of NFS, a resource name has the form
642 * We try to find an exact match between the user-specified argument
643 * and the mnt_special field of a mount table entry.
644 * We also use the heuristic of removing the basename from the user-specified
645 * argument and repeating the test until we get a match. This works
646 * fine for NFS but may fail for other remote file system types. However,
647 * it is guaranteed that the function will not fail if the user specifies
648 * the exact resource name.
649 * If successful, this function sets the 'dfr_mte' field of '*dfrp'
652 resource_mount_entry(struct df_request
*dfrp
)
657 * We need our own copy since we will modify the string
659 name
= new_string(dfrp
->dfr_cmd_arg
);
666 * Compare against all known mount points.
667 * We start from the most recent mount, which is at the
670 for (i
= mount_table_entries
- 1; i
>= 0; i
--) {
671 struct mtab_entry
*mtep
= &mount_table
[i
];
673 if (EQ(name
, mtep
->mte_mount
->mnt_special
)) {
674 dfrp
->dfr_mte
= mtep
;
680 * Remove the last component of the pathname.
681 * If there is no such component, this is not a resource name.
683 p
= strrchr(name
, '/');
693 * Try to match the command line argument which is a block special device
694 * with the special device of one of the mounted file systems.
695 * If one is found, set the appropriate field of 'dfrp' to the mount
699 bdev_mount_entry(struct df_request
*dfrp
)
702 char *special
= dfrp
->dfr_cmd_arg
;
705 * Compare against all known mount points.
706 * We start from the most recent mount, which is at the
709 for (i
= mount_table_entries
- 1; i
>= 0; i
--) {
710 struct mtab_entry
*mtep
= &mount_table
[i
];
712 if (EQ(special
, mtep
->mte_mount
->mnt_special
)) {
713 dfrp
->dfr_mte
= mtep
;
719 static struct mtab_entry
*
720 devid_matches(int i
, dev_t devno
)
722 struct mtab_entry
*mtep
= &mount_table
[i
];
723 struct extmnttab
*mtp
= mtep
->mte_mount
;
724 /* int len = strlen(mtp->mnt_mountp); */
726 if (EQ(mtp
->mnt_fstype
, MNTTYPE_SWAP
))
729 * check if device numbers match. If there is a cached device number
730 * in the mtab_entry, use it, otherwise get the device number
731 * either from the mnttab entry or by stat'ing the mount point.
733 if (! mtep
->mte_dev_is_valid
) {
737 dev
= makedev(mtp
->mnt_major
, mtp
->mnt_minor
);
741 if (stat64(mtp
->mnt_mountp
, &st
) == -1) {
748 mtep
->mte_dev_is_valid
= TRUE
;
750 if (mtep
->mte_dev
== devno
) {
757 * Find the mount point under which the user-specified path resides
758 * and set the 'dfr_mte' field of '*dfrp' to point to the mount table entry.
761 path_mount_entry(struct df_request
*dfrp
, dev_t devno
)
763 char dirpath
[MAXPATHLEN
];
764 char *dir
= dfrp
->dfr_cmd_arg
;
765 struct mtab_entry
*match
, *tmatch
;
769 * Expand the given path to get a canonical version (i.e. an absolute
770 * path without symbolic links).
772 if (realpath(dir
, dirpath
) == NULL
) {
773 errmsg(ERR_PERROR
, "cannot canonicalize %s:", dir
);
777 * If the mnt point is lofs, search from the top of entries from
778 * /etc/mnttab and return the entry that best matches the pathname.
779 * For non-lofs mount points, return the first entry from the bottom
780 * of the entries in /etc/mnttab that matches on the devid field
783 if (dfrp
->dfr_fstype
&& EQ(dfrp
->dfr_fstype
, MNTTYPE_LOFS
)) {
784 struct extmnttab
*entryp
;
791 for (i
= 0; i
< mount_table_entries
; i
++) {
792 entryp
= mount_table
[i
].mte_mount
;
794 if (!EQ(entryp
->mnt_fstype
, MNTTYPE_LOFS
))
798 mountp
= entryp
->mnt_mountp
;
801 * Count the number of matching characters
802 * until either path or mountpoint is exhausted
804 while ((p
= *path
++) == (m
= *mountp
++)) {
807 if (p
== '\0' || m
== '\0')
811 /* Both exhausted so we have a match */
812 if (p
== '\0' && m
== '\0') {
818 * We have exhausted the mountpoint and the current
819 * character in the path is a '/' hence the full path
820 * traverses this mountpoint.
821 * Record this as the best candidate so far.
823 if (p
== '/' && m
== '\0') {
824 if (score
> best_score
) {
832 match
= &mount_table
[best_index
];
834 for (i
= mount_table_entries
- 1; i
>= 0; i
--) {
835 if (tmatch
= devid_matches(i
, devno
)) {
837 * If executing in a zone, there might be lofs
838 * mounts for which the real mount point is
839 * invisible; accept the "best fit" for this
843 if (!EQ(match
->mte_mount
->mnt_fstype
,
852 "Could not find mount point for %s", dir
);
855 dfrp
->dfr_mte
= match
;
859 * Execute a single FS-specific df command for all given requests
860 * Return 0 if successful, 1 otherwise.
863 run_fs_specific_df(struct df_request request_list
[], int entries
)
871 char cmd_path
[MAXPATHLEN
];
877 fstype
= request_list
[0].dfr_fstype
;
879 if (F_option
&& ! EQ(FSType
, fstype
))
882 (void) sprintf(cmd_path
, "%s%s/df", FS_LIBPATH
, fstype
);
887 * 1 for the generic options that we propagate
888 * 1 for the terminating NULL pointer
889 * n for the number of user-specified arguments
891 size
= (5 + entries
) * sizeof (char *);
892 argv
= xmalloc(size
);
893 (void) memset(argv
, 0, size
);
898 argv
[argv_index
++] = "-o";
899 argv
[argv_index
++] = o_option_arg
;
903 * Check if we need to propagate any generic options
905 if (df_options_len
> 1)
906 argv
[argv_index
++] = df_options
;
909 * If there is a user-specified path, we pass that to the
910 * FS-specific df. Otherwise, we are guaranteed to have a mount
911 * point, since a request without a user path implies that
912 * we are reporting only on mounted file systems.
914 for (i
= 0; i
< entries
; i
++) {
915 struct df_request
*dfrp
= &request_list
[i
];
917 argv
[argv_index
++] = (dfrp
->dfr_cmd_arg
== NULL
)
918 ? DFR_MOUNT_POINT(dfrp
)
923 for (i
= 0; i
< argv_index
-1; i
++)
924 (void) printf("%s ", argv
[i
]);
925 (void) printf("%s\n", argv
[i
]);
932 errmsg(ERR_PERROR
, "cannot fork process:");
934 } else if (pid
== 0) {
935 (void) execv(cmd_path
, argv
);
938 "operation not applicable for FSType %s",
941 errmsg(ERR_PERROR
, "cannot execute %s:", cmd_path
);
949 pid_t wpid
= waitpid(pid
, &status
, 0);
955 errmsg(ERR_PERROR
, "waitpid error:");
962 return ((WIFEXITED(status
) && WEXITSTATUS(status
) == 0) ? 0 : 1);
968 * Remove from the request list all requests that do not apply.
969 * Notice that the subsequent processing of the requests depends on
970 * the sanity checking performed by this function.
973 prune_list(struct df_request request_list
[],
974 size_t n_requests
, size_t *valid_requests
)
980 for (i
= 0; i
< n_requests
; i
++) {
981 struct df_request
*dfrp
= &request_list
[i
];
984 * Skip file systems that are not mounted if either the
985 * -l or -n options were specified. If none of these options
986 * are present, the appropriate FS-specific df will be invoked.
988 if (! DFR_ISMOUNTEDFS(dfrp
)) {
989 if (l_option
|| n_option
) {
991 "%s option incompatible with unmounted "
992 "special device (%s)",
993 l_option
? "-l" : "-n", dfrp
->dfr_cmd_arg
);
994 dfrp
->dfr_valid
= FALSE
;
1003 * Check for inconsistency between the argument of -F and
1004 * the actual file system type.
1005 * If there is an inconsistency and the user specified a
1006 * path, this is an error since we are asked to interpret
1007 * the path using the wrong file system type. If there is
1008 * no path associated with this request, we quietly ignore it.
1010 if (F_option
&& ! EQ(dfrp
->dfr_fstype
, FSType
)) {
1011 dfrp
->dfr_valid
= FALSE
;
1012 if (dfrp
->dfr_cmd_arg
!= NULL
) {
1014 "Warning: %s mounted as a %s file system",
1015 dfrp
->dfr_cmd_arg
, dfrp
->dfr_fstype
);
1022 * Skip remote file systems if the -l option is present
1024 if (l_option
&& is_remote_fs(dfrp
->dfr_fstype
)) {
1025 if (dfrp
->dfr_cmd_arg
!= NULL
) {
1027 "Warning: %s is not a local file system",
1031 dfrp
->dfr_valid
= FALSE
;
1036 * Skip file systems mounted as "ignore" unless the -a option
1037 * is present, or the user explicitly specified them on
1040 if (dfrp
->dfr_mte
->mte_ignore
&&
1041 ! (a_option
|| dfrp
->dfr_cmd_arg
)) {
1042 dfrp
->dfr_valid
= FALSE
;
1048 *valid_requests
= n_valid
;
1054 * Print the appropriate header for the requested output format.
1055 * Options are checked in order of their precedence.
1060 if (use_scaling
) { /* this comes from the -h option */
1063 (void) printf("%-*s %*s %*s %*s %-*s %s\n",
1064 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1065 SCALED_WIDTH
, TRANSLATE("Size"),
1066 SCALED_WIDTH
, TRANSLATE("Used"),
1067 AVAILABLE_WIDTH
, TRANSLATE("Available"),
1068 CAPACITY_WIDTH
, TRANSLATE("Capacity"),
1069 TRANSLATE("Mounted on"));
1076 (void) printf(gettext("%-*s %*s %*s %*s %-*s %s\n"),
1077 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1078 KBYTE_WIDTH
, TRANSLATE("1024-blocks"),
1079 KBYTE_WIDTH
, TRANSLATE("Used"),
1080 KBYTE_WIDTH
, TRANSLATE("Available"),
1081 CAPACITY_WIDTH
, TRANSLATE("Capacity"),
1082 TRANSLATE("Mounted on"));
1089 (void) printf(gettext("%-*s %*s %*s %*s %-*s %s\n"),
1090 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1091 KBYTE_WIDTH
, TRANSLATE("1M-blocks"),
1092 KBYTE_WIDTH
, TRANSLATE("Used"),
1093 KBYTE_WIDTH
, TRANSLATE("Available"),
1094 CAPACITY_WIDTH
, TRANSLATE("Capacity"),
1095 TRANSLATE("Mounted on"));
1099 /* Added for XCU4 compliance */
1103 (void) printf(gettext("%-*s %*s %*s %*s %-*s %s\n"),
1104 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1105 KBYTE_WIDTH
, TRANSLATE("512-blocks"),
1106 KBYTE_WIDTH
, TRANSLATE("Used"),
1107 KBYTE_WIDTH
, TRANSLATE("Available"),
1108 CAPACITY_WIDTH
, TRANSLATE("Capacity"),
1109 TRANSLATE("Mounted on"));
1116 (void) printf("%-*s %-*s %*s %*s %*s %-*s\n",
1117 IBCS2_MOUNT_POINT_WIDTH
, TRANSLATE("Mount Dir"),
1118 IBCS2_FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1119 BLOCK_WIDTH
, TRANSLATE("blocks"),
1120 BLOCK_WIDTH
, TRANSLATE("used"),
1121 BLOCK_WIDTH
, TRANSLATE("free"),
1122 CAPACITY_WIDTH
, TRANSLATE(" %used"));
1126 (void) printf(gettext("%-*s %*s\n"),
1127 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1128 BLOCK_WIDTH
, TRANSLATE("ifree"));
1132 (void) printf(gettext("%-*s %*s\n"),
1133 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1134 BLOCK_WIDTH
, TRANSLATE("avail"));
1141 * Convert an unsigned long long to a string representation and place the
1142 * result in the caller-supplied buffer.
1143 * The given number is in units of "unit_from" size, but the
1144 * converted number will be in units of "unit_to" size. The unit sizes
1145 * must be powers of 2.
1146 * The value "(unsigned long long)-1" is a special case and is always
1147 * converted to "-1".
1148 * Returns a pointer to the caller-supplied buffer.
1152 char *buf
, /* put the result here */
1153 unsigned long long number
, /* convert this number */
1154 int unit_from
, /* from units of this size */
1155 int unit_to
) /* to units of this size */
1157 if ((long long)number
== (long long)-1)
1158 (void) strcpy(buf
, "-1");
1160 if (unit_from
== unit_to
)
1161 (void) sprintf(buf
, "%llu", number
);
1162 else if (unit_from
< unit_to
)
1163 (void) sprintf(buf
, "%llu",
1164 number
/ (unsigned long long)(unit_to
/ unit_from
));
1166 (void) sprintf(buf
, "%llu",
1167 number
* (unsigned long long)(unit_from
/ unit_to
));
1173 * The statvfs() implementation allows us to return only two values, the total
1174 * number of blocks and the number of blocks free. The equation 'used = total -
1175 * free' will not work for ZFS filesystems, due to the nature of pooled storage.
1176 * We choose to return values in the statvfs structure that will produce correct
1177 * results for 'used' and 'available', but not 'total'. This function will open
1178 * the underlying ZFS dataset if necessary and get the real value.
1181 adjust_total_blocks(struct df_request
*dfrp
, fsblkcnt64_t
*total
,
1184 char *dataset
, *slash
;
1185 boolean_t first
= TRUE
;
1188 if (strcmp(DFR_FSTYPE(dfrp
), MNTTYPE_ZFS
) != 0 || !load_libzfs())
1192 * We want to get the total size for this filesystem as bounded by any
1193 * quotas. In order to do this, we start at the current filesystem and
1194 * work upwards looking for the smallest quota. When we reach the
1195 * pool itself, the quota is the amount used plus the amount
1198 if ((dataset
= strdup(DFR_SPECIAL(dfrp
))) == NULL
)
1201 slash
= dataset
+ strlen(dataset
);
1202 while (slash
!= NULL
) {
1204 uint64_t this_quota
;
1208 zhp
= _zfs_open(g_zfs
, dataset
, ZFS_TYPE_DATASET
);
1212 /* true at first iteration of loop */
1214 quota
= _zfs_prop_get_int(zhp
, ZFS_PROP_REFQUOTA
);
1220 this_quota
= _zfs_prop_get_int(zhp
, ZFS_PROP_QUOTA
);
1221 if (this_quota
&& this_quota
< quota
)
1224 /* true at last iteration of loop */
1225 if ((slash
= strrchr(dataset
, '/')) == NULL
) {
1228 size
= _zfs_prop_get_int(zhp
, ZFS_PROP_USED
) +
1229 _zfs_prop_get_int(zhp
, ZFS_PROP_AVAILABLE
);
1238 * Modify total only if we managed to get some stats from libzfs.
1241 *total
= quota
/ blocksize
;
1246 * The output will appear properly columnized regardless of the names of
1247 * the various fields
1250 g_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1252 fsblkcnt64_t available_blocks
= fsp
->f_bavail
;
1253 fsblkcnt64_t total_blocks
= fsp
->f_blocks
;
1254 numbuf_t total_blocks_buf
;
1255 numbuf_t total_files_buf
;
1256 numbuf_t free_blocks_buf
;
1257 numbuf_t available_blocks_buf
;
1258 numbuf_t free_files_buf
;
1262 #define DEFINE_STR_LEN(var) \
1263 static char *var##_str; \
1264 static size_t var##_len
1266 #define SET_STR_LEN(name, var)\
1268 var##_str = TRANSLATE(name); \
1269 var##_len = strlen(var##_str); \
1272 DEFINE_STR_LEN(block_size
);
1273 DEFINE_STR_LEN(frag_size
);
1274 DEFINE_STR_LEN(total_blocks
);
1275 DEFINE_STR_LEN(free_blocks
);
1276 DEFINE_STR_LEN(available
);
1277 DEFINE_STR_LEN(total_files
);
1278 DEFINE_STR_LEN(free_files
);
1279 DEFINE_STR_LEN(fstype
);
1280 DEFINE_STR_LEN(fsys_id
);
1281 DEFINE_STR_LEN(fname
);
1282 DEFINE_STR_LEN(flag
);
1286 * The first argument of each of the following macro invocations is a
1287 * string that needs to be translated.
1289 SET_STR_LEN("block size", block_size
);
1290 SET_STR_LEN("frag size", frag_size
);
1291 SET_STR_LEN("total blocks", total_blocks
);
1292 SET_STR_LEN("free blocks", free_blocks
);
1293 SET_STR_LEN("available", available
);
1294 SET_STR_LEN("total files", total_files
);
1295 SET_STR_LEN("free files", free_files
);
1296 SET_STR_LEN("fstype", fstype
);
1297 SET_STR_LEN("filesys id", fsys_id
);
1298 SET_STR_LEN("filename length", fname
);
1299 SET_STR_LEN("flag", flag
);
1301 #define NCOL1_WIDTH (int)MAX3(BLOCK_WIDTH, NFILES_WIDTH, FSTYPE_WIDTH)
1302 #define NCOL2_WIDTH (int)MAX3(BLOCK_WIDTH, FSID_WIDTH, FLAG_WIDTH) + 2
1303 #define NCOL3_WIDTH (int)MAX3(BSIZE_WIDTH, BLOCK_WIDTH, NAMELEN_WIDTH)
1304 #define NCOL4_WIDTH (int)MAX(FRAGSIZE_WIDTH, NFILES_WIDTH)
1306 #define SCOL1_WIDTH (int)MAX3(total_blocks_len, free_files_len, fstype_len)
1307 #define SCOL2_WIDTH (int)MAX3(free_blocks_len, fsys_id_len, flag_len)
1308 #define SCOL3_WIDTH (int)MAX3(block_size_len, available_len, fname_len)
1309 #define SCOL4_WIDTH (int)MAX(frag_size_len, total_files_len)
1312 MAX(MOUNT_POINT_WIDTH
, strlen(DFR_MOUNT_POINT(dfrp
)))
1313 + MAX(SPECIAL_DEVICE_WIDTH
, strlen(DFR_SPECIAL(dfrp
)))
1314 + 20); /* plus slop - nulls & formatting */
1315 (void) sprintf(temp_buf
, "%-*s(%-*s):",
1316 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1317 SPECIAL_DEVICE_WIDTH
, DFR_SPECIAL(dfrp
));
1319 (void) printf("%-*s %*lu %-*s %*lu %-*s\n",
1320 NCOL1_WIDTH
+ 1 + SCOL1_WIDTH
+ 1 + NCOL2_WIDTH
+ 1 + SCOL2_WIDTH
,
1322 NCOL3_WIDTH
, fsp
->f_bsize
, SCOL3_WIDTH
, block_size_str
,
1323 NCOL4_WIDTH
, fsp
->f_frsize
, SCOL4_WIDTH
, frag_size_str
);
1327 * Adjust available_blocks value - it can be less than 0 on
1328 * a 4.x file system. Reset it to 0 in order to avoid printing
1331 if ((long long)available_blocks
< (long long)0)
1332 available_blocks
= (fsblkcnt64_t
)0;
1334 adjust_total_blocks(dfrp
, &total_blocks
, fsp
->f_frsize
);
1336 (void) printf("%*s %-*s %*s %-*s %*s %-*s %*s %-*s\n",
1337 NCOL1_WIDTH
, number_to_string(total_blocks_buf
,
1338 total_blocks
, fsp
->f_frsize
, 512),
1339 SCOL1_WIDTH
, total_blocks_str
,
1340 NCOL2_WIDTH
, number_to_string(free_blocks_buf
,
1341 fsp
->f_bfree
, fsp
->f_frsize
, 512),
1342 SCOL2_WIDTH
, free_blocks_str
,
1343 NCOL3_WIDTH
, number_to_string(available_blocks_buf
,
1344 available_blocks
, fsp
->f_frsize
, 512),
1345 SCOL3_WIDTH
, available_str
,
1346 NCOL4_WIDTH
, number_to_string(total_files_buf
,
1347 fsp
->f_files
, 1, 1),
1348 SCOL4_WIDTH
, total_files_str
);
1350 (void) printf("%*s %-*s %*lu %-*s %s\n",
1351 NCOL1_WIDTH
, number_to_string(free_files_buf
,
1352 fsp
->f_ffree
, 1, 1),
1353 SCOL1_WIDTH
, free_files_str
,
1354 NCOL2_WIDTH
, fsp
->f_fsid
, SCOL2_WIDTH
, fsys_id_str
,
1357 (void) printf("%*s %-*s %#*.*lx %-*s %*s %-*s\n\n",
1358 NCOL1_WIDTH
, fsp
->f_basetype
, SCOL1_WIDTH
, fstype_str
,
1359 NCOL2_WIDTH
, NCOL2_WIDTH
-2, fsp
->f_flag
, SCOL2_WIDTH
, flag_str
,
1360 NCOL3_WIDTH
, number_to_string(fname_buf
,
1361 (unsigned long long)fsp
->f_namemax
, 1, 1),
1362 SCOL3_WIDTH
, fname_str
);
1367 k_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1369 fsblkcnt64_t total_blocks
= fsp
->f_blocks
;
1370 fsblkcnt64_t free_blocks
= fsp
->f_bfree
;
1371 fsblkcnt64_t available_blocks
= fsp
->f_bavail
;
1372 fsblkcnt64_t used_blocks
;
1373 char *file_system
= DFR_SPECIAL(dfrp
);
1374 numbuf_t total_blocks_buf
;
1375 numbuf_t used_blocks_buf
;
1376 numbuf_t available_blocks_buf
;
1377 char capacity_buf
[LINEBUF_SIZE
];
1380 * If the free block count is -1, don't trust anything but the total
1383 if (free_blocks
== (fsblkcnt64_t
)-1) {
1384 used_blocks
= (fsblkcnt64_t
)-1;
1385 (void) strcpy(capacity_buf
, " 100%");
1387 fsblkcnt64_t reserved_blocks
= free_blocks
- available_blocks
;
1389 used_blocks
= total_blocks
- free_blocks
;
1392 * The capacity estimation is bogus when available_blocks is 0
1393 * and the super-user has allocated more space. The reason
1394 * is that reserved_blocks is inaccurate in that case, because
1395 * when the super-user allocates space, free_blocks is updated
1396 * but available_blocks is not (since it can't drop below 0).
1398 * XCU4 and POSIX.2 require that any fractional result of the
1399 * capacity estimation be rounded to the next highest integer,
1400 * hence the addition of 0.5.
1402 (void) sprintf(capacity_buf
, "%5.0f%%",
1403 (total_blocks
== 0) ? 0.0 :
1404 ((double)used_blocks
/
1405 (double)(total_blocks
- reserved_blocks
))
1410 * The available_blocks can be less than 0 on a 4.x file system.
1411 * Reset it to 0 in order to avoid printing negative numbers.
1413 if ((long long)available_blocks
< (long long)0)
1414 available_blocks
= (fsblkcnt64_t
)0;
1416 * Print long special device names (usually NFS mounts) in a line
1417 * by themselves when the output is directed to a terminal.
1419 if (tty_output
&& strlen(file_system
) > (size_t)FILESYSTEM_WIDTH
) {
1420 (void) printf("%s\n", file_system
);
1424 adjust_total_blocks(dfrp
, &total_blocks
, fsp
->f_frsize
);
1426 if (use_scaling
) { /* comes from the -h option */
1427 nicenum_scale(total_blocks
, fsp
->f_frsize
,
1428 total_blocks_buf
, sizeof (total_blocks_buf
), 0);
1429 nicenum_scale(used_blocks
, fsp
->f_frsize
,
1430 used_blocks_buf
, sizeof (used_blocks_buf
), 0);
1431 nicenum_scale(available_blocks
, fsp
->f_frsize
,
1432 available_blocks_buf
, sizeof (available_blocks_buf
), 0);
1434 (void) printf("%-*s %*s %*s %*s %-*s %-s\n",
1435 FILESYSTEM_WIDTH
, file_system
,
1436 SCALED_WIDTH
, total_blocks_buf
,
1437 SCALED_WIDTH
, used_blocks_buf
,
1438 AVAILABLE_WIDTH
, available_blocks_buf
,
1439 CAPACITY_WIDTH
, capacity_buf
, DFR_MOUNT_POINT(dfrp
));
1444 (void) printf("%-*.*s %-*.*s %*lld %*lld %*lld %-.*s\n",
1445 IBCS2_MOUNT_POINT_WIDTH
, IBCS2_MOUNT_POINT_WIDTH
,
1446 DFR_MOUNT_POINT(dfrp
),
1447 IBCS2_FILESYSTEM_WIDTH
, IBCS2_FILESYSTEM_WIDTH
, file_system
,
1448 BLOCK_WIDTH
, total_blocks
,
1449 BLOCK_WIDTH
, used_blocks
,
1450 BLOCK_WIDTH
, available_blocks
,
1451 CAPACITY_WIDTH
, capacity_buf
);
1455 if (P_option
&& !k_option
&& !m_option
) {
1456 (void) printf("%-*s %*s %*s %*s %-*s %-s\n",
1457 FILESYSTEM_WIDTH
, file_system
,
1458 KBYTE_WIDTH
, number_to_string(total_blocks_buf
,
1459 total_blocks
, fsp
->f_frsize
, 512),
1460 KBYTE_WIDTH
, number_to_string(used_blocks_buf
,
1461 used_blocks
, fsp
->f_frsize
, 512),
1462 KBYTE_WIDTH
, number_to_string(available_blocks_buf
,
1463 available_blocks
, fsp
->f_frsize
, 512),
1464 CAPACITY_WIDTH
, capacity_buf
,
1465 DFR_MOUNT_POINT(dfrp
));
1466 } else if (m_option
) {
1467 (void) printf("%-*s %*s %*s %*s %-*s %-s\n",
1468 FILESYSTEM_WIDTH
, file_system
,
1469 KBYTE_WIDTH
, number_to_string(total_blocks_buf
,
1470 total_blocks
, fsp
->f_frsize
, 1024*1024),
1471 KBYTE_WIDTH
, number_to_string(used_blocks_buf
,
1472 used_blocks
, fsp
->f_frsize
, 1024*1024),
1473 KBYTE_WIDTH
, number_to_string(available_blocks_buf
,
1474 available_blocks
, fsp
->f_frsize
, 1024*1024),
1475 CAPACITY_WIDTH
, capacity_buf
,
1476 DFR_MOUNT_POINT(dfrp
));
1478 (void) printf("%-*s %*s %*s %*s %-*s %-s\n",
1479 FILESYSTEM_WIDTH
, file_system
,
1480 KBYTE_WIDTH
, number_to_string(total_blocks_buf
,
1481 total_blocks
, fsp
->f_frsize
, 1024),
1482 KBYTE_WIDTH
, number_to_string(used_blocks_buf
,
1483 used_blocks
, fsp
->f_frsize
, 1024),
1484 KBYTE_WIDTH
, number_to_string(available_blocks_buf
,
1485 available_blocks
, fsp
->f_frsize
, 1024),
1486 CAPACITY_WIDTH
, capacity_buf
,
1487 DFR_MOUNT_POINT(dfrp
));
1492 * The following is for internationalization support.
1494 static bool_int strings_initialized
;
1495 static char *files_str
;
1496 static char *blocks_str
;
1497 static char *total_str
;
1498 static char *kilobytes_str
;
1503 total_str
= TRANSLATE("total");
1504 files_str
= TRANSLATE("files");
1505 blocks_str
= TRANSLATE("blocks");
1506 kilobytes_str
= TRANSLATE("kilobytes");
1507 strings_initialized
= TRUE
;
1510 #define STRINGS_INIT() if (!strings_initialized) strings_init()
1514 t_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1516 fsblkcnt64_t total_blocks
= fsp
->f_blocks
;
1517 numbuf_t total_blocks_buf
;
1518 numbuf_t total_files_buf
;
1519 numbuf_t free_blocks_buf
;
1520 numbuf_t free_files_buf
;
1524 adjust_total_blocks(dfrp
, &total_blocks
, fsp
->f_frsize
);
1526 (void) printf("%-*s(%-*s): %*s %s %*s %s\n",
1527 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1528 SPECIAL_DEVICE_WIDTH
, DFR_SPECIAL(dfrp
),
1529 BLOCK_WIDTH
, number_to_string(free_blocks_buf
,
1530 fsp
->f_bfree
, fsp
->f_frsize
, 512),
1532 NFILES_WIDTH
, number_to_string(free_files_buf
,
1533 fsp
->f_ffree
, 1, 1),
1536 * The total column used to use the same space as the mnt pt & special
1537 * dev fields. However, this doesn't work with massive special dev
1538 * fields * (eg > 500 chars) causing an enormous amount of white space
1539 * before the total column (see bug 4100411). So the code was
1540 * simplified to set the total column at the usual gap.
1541 * This had the side effect of fixing a bug where the previously
1542 * used static buffer was overflowed by the same massive special dev.
1544 (void) printf("%*s: %*s %s %*s %s\n",
1545 MNT_SPEC_WIDTH
, total_str
,
1546 BLOCK_WIDTH
, number_to_string(total_blocks_buf
,
1547 total_blocks
, fsp
->f_frsize
, 512),
1549 NFILES_WIDTH
, number_to_string(total_files_buf
,
1550 fsp
->f_files
, 1, 1),
1556 eb_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1558 numbuf_t free_files_buf
;
1559 numbuf_t free_kbytes_buf
;
1563 (void) printf("%-*s(%-*s): %*s %s\n",
1564 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1565 SPECIAL_DEVICE_WIDTH
, DFR_SPECIAL(dfrp
),
1566 MAX(KBYTE_WIDTH
, NFILES_WIDTH
),
1567 number_to_string(free_kbytes_buf
,
1568 fsp
->f_bfree
, fsp
->f_frsize
, 1024),
1570 (void) printf("%-*s(%-*s): %*s %s\n",
1571 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1572 SPECIAL_DEVICE_WIDTH
, DFR_SPECIAL(dfrp
),
1573 MAX(NFILES_WIDTH
, NFILES_WIDTH
),
1574 number_to_string(free_files_buf
, fsp
->f_ffree
, 1, 1),
1580 e_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1582 numbuf_t free_files_buf
;
1584 (void) printf("%-*s %*s\n",
1585 FILESYSTEM_WIDTH
, DFR_SPECIAL(dfrp
),
1587 number_to_string(free_files_buf
, fsp
->f_ffree
, 1, 1));
1592 b_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1594 numbuf_t free_blocks_buf
;
1596 (void) printf("%-*s %*s\n",
1597 FILESYSTEM_WIDTH
, DFR_SPECIAL(dfrp
),
1598 BLOCK_WIDTH
, number_to_string(free_blocks_buf
,
1599 fsp
->f_bfree
, fsp
->f_frsize
, 1024));
1605 n_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1607 (void) printf("%-*s: %-*s\n",
1608 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1609 FSTYPE_WIDTH
, dfrp
->dfr_fstype
);
1614 default_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1616 numbuf_t free_blocks_buf
;
1617 numbuf_t free_files_buf
;
1621 (void) printf("%-*s(%-*s):%*s %s %*s %s\n",
1622 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1623 SPECIAL_DEVICE_WIDTH
, DFR_SPECIAL(dfrp
),
1624 BLOCK_WIDTH
, number_to_string(free_blocks_buf
,
1625 fsp
->f_bfree
, fsp
->f_frsize
, 512),
1627 NFILES_WIDTH
, number_to_string(free_files_buf
,
1628 fsp
->f_ffree
, 1, 1),
1635 V_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1637 char temp_buf
[LINEBUF_SIZE
];
1639 if (df_options_len
> 1)
1640 (void) strcat(strcpy(temp_buf
, df_options
), " ");
1644 (void) printf("%s -F %s %s%s\n",
1645 program_name
, dfrp
->dfr_fstype
, temp_buf
,
1646 dfrp
->dfr_cmd_arg
? dfrp
->dfr_cmd_arg
: DFR_SPECIAL(dfrp
));
1651 * This function is used to sort the array of df_requests according to fstype
1654 df_reqcomp(const void *p1
, const void *p2
)
1656 int v
= strcmp(DFRP(p1
)->dfr_fstype
, DFRP(p2
)->dfr_fstype
);
1661 return (DFRP(p1
)->dfr_index
- DFRP(p2
)->dfr_index
);
1666 vfs_error(char *file
, int status
)
1668 if (status
== VFS_TOOLONG
)
1669 errmsg(ERR_NOFLAGS
, "a line in %s exceeds %d characters",
1670 file
, MNT_LINE_MAX
);
1671 else if (status
== VFS_TOOMANY
)
1672 errmsg(ERR_NOFLAGS
, "a line in %s has too many fields", file
);
1673 else if (status
== VFS_TOOFEW
)
1674 errmsg(ERR_NOFLAGS
, "a line in %s has too few fields", file
);
1676 errmsg(ERR_NOFLAGS
, "error while reading %s: %d", file
, status
);
1681 * Try to determine the fstype for the specified block device.
1682 * Return in order of decreasing preference:
1683 * file system type from vfstab
1684 * file system type as specified by -F option
1685 * default file system type
1688 find_fstype(char *special
)
1693 char *vfstab_file
= VFS_TAB
;
1695 fp
= xfopen(vfstab_file
);
1696 status
= getvfsspec(fp
, &vtab
, special
);
1699 vfs_error(vfstab_file
, status
);
1702 if (F_option
&& ! EQ(FSType
, vtab
.vfs_fstype
))
1704 "warning: %s is of type %s", special
, vtab
.vfs_fstype
);
1705 return (new_string(vtab
.vfs_fstype
));
1708 return (F_option
? FSType
: default_fstype(special
));
1712 * When this function returns, the following fields are filled for all
1713 * valid entries in the requests[] array:
1714 * dfr_mte (if the file system is mounted)
1718 * The function returns the number of errors that occurred while building
1722 create_request_list(
1725 struct df_request
*requests_p
[],
1726 size_t *request_count
)
1728 struct df_request
*requests
;
1729 struct df_request
*dfrp
;
1732 size_t request_index
= 0;
1733 size_t max_requests
;
1737 * If no args, use the mounted file systems, otherwise use the
1738 * user-specified arguments.
1742 max_requests
= mount_table_entries
;
1744 max_requests
= argc
;
1746 size
= max_requests
* sizeof (struct df_request
);
1747 requests
= xmalloc(size
);
1748 (void) memset(requests
, 0, size
);
1752 * If -Z wasn't specified, we skip mounts in other
1753 * zones. This obviously is a noop in a non-global
1756 boolean_t showall
= (getzoneid() != GLOBAL_ZONEID
) || Z_option
;
1757 struct zone_summary
*zsp
;
1760 zsp
= fs_get_zone_summaries();
1763 "unable to retrieve list of zones");
1766 for (i
= 0; i
< mount_table_entries
; i
++) {
1767 struct extmnttab
*mtp
= mount_table
[i
].mte_mount
;
1769 if (EQ(mtp
->mnt_fstype
, MNTTYPE_SWAP
))
1773 if (fs_mount_in_other_zone(zsp
,
1777 dfrp
= &requests
[request_index
++];
1778 dfrp
->dfr_mte
= &mount_table
[i
];
1779 dfrp
->dfr_fstype
= mtp
->mnt_fstype
;
1780 dfrp
->dfr_index
= i
;
1781 dfrp
->dfr_valid
= TRUE
;
1784 struct stat64
*arg_stat
; /* array of stat structures */
1785 bool_int
*valid_stat
; /* which structures are valid */
1787 arg_stat
= xmalloc(argc
* sizeof (struct stat64
));
1788 valid_stat
= xmalloc(argc
* sizeof (bool_int
));
1791 * Obtain stat64 information for each argument before
1792 * constructing the list of mounted file systems. By
1793 * touching all these places we force the automounter
1794 * to establish any mounts required to access the arguments,
1795 * so that the corresponding mount table entries will exist
1796 * when we look for them.
1797 * It is still possible that the automounter may timeout
1798 * mounts between the time we read the mount table and the
1799 * time we process the request. Even in that case, when
1800 * we issue the statvfs64(2) for the mount point, the file
1801 * system will be mounted again. The only problem will
1802 * occur if the automounter maps change in the meantime
1803 * and the mount point is eliminated.
1805 for (i
= 0; i
< argc
; i
++)
1806 valid_stat
[i
] = (stat64(argv
[i
], &arg_stat
[i
]) == 0);
1810 for (i
= 0; i
< argc
; i
++) {
1811 char *arg
= argv
[i
];
1813 dfrp
= &requests
[request_index
];
1815 dfrp
->dfr_index
= request_index
;
1816 dfrp
->dfr_cmd_arg
= arg
;
1818 if (valid_stat
[i
]) {
1819 dfrp
->dfr_fstype
= arg_stat
[i
].st_fstype
;
1820 if (S_ISBLK(arg_stat
[i
].st_mode
)) {
1821 bdev_mount_entry(dfrp
);
1822 dfrp
->dfr_valid
= TRUE
;
1823 } else if (S_ISDIR(arg_stat
[i
].st_mode
) ||
1824 S_ISREG(arg_stat
[i
].st_mode
) ||
1825 S_ISFIFO(arg_stat
[i
].st_mode
)) {
1826 path_mount_entry(dfrp
,
1827 arg_stat
[i
].st_dev
);
1828 if (! DFR_ISMOUNTEDFS(dfrp
)) {
1832 dfrp
->dfr_valid
= TRUE
;
1835 resource_mount_entry(dfrp
);
1836 dfrp
->dfr_valid
= DFR_ISMOUNTEDFS(dfrp
);
1840 * If we haven't managed to verify that the request
1841 * is valid, we must have gotten a bad argument.
1843 if (!dfrp
->dfr_valid
) {
1845 "(%-10s) not a block device, directory or "
1846 "mounted resource", arg
);
1852 * Determine the file system type.
1854 if (DFR_ISMOUNTEDFS(dfrp
))
1856 dfrp
->dfr_mte
->mte_mount
->mnt_fstype
;
1859 find_fstype(dfrp
->dfr_cmd_arg
);
1864 *requests_p
= requests
;
1865 *request_count
= request_index
;
1871 * Select the appropriate function and flags to use for output.
1872 * Notice that using both -e and -b options produces a different form of
1873 * output than either of those two options alone; this is the behavior of
1876 static struct df_output
*
1879 static struct df_output dfo
;
1882 * The order of checking options follows the option precedence
1883 * rules as they are listed in the man page.
1885 if (use_scaling
) { /* comes from the -h option */
1886 dfo
.dfo_func
= k_output
;
1887 dfo
.dfo_flags
= DFO_HEADER
+ DFO_STATVFS
;
1888 } else if (V_option
) {
1889 dfo
.dfo_func
= V_output
;
1890 dfo
.dfo_flags
= DFO_NOFLAGS
;
1891 } else if (g_option
) {
1892 dfo
.dfo_func
= g_output
;
1893 dfo
.dfo_flags
= DFO_STATVFS
;
1894 } else if (k_option
|| m_option
|| P_option
|| v_option
) {
1895 dfo
.dfo_func
= k_output
;
1896 dfo
.dfo_flags
= DFO_HEADER
+ DFO_STATVFS
;
1897 } else if (t_option
) {
1898 dfo
.dfo_func
= t_output
;
1899 dfo
.dfo_flags
= DFO_STATVFS
;
1900 } else if (b_option
&& e_option
) {
1901 dfo
.dfo_func
= eb_output
;
1902 dfo
.dfo_flags
= DFO_STATVFS
;
1903 } else if (b_option
) {
1904 dfo
.dfo_func
= b_output
;
1905 dfo
.dfo_flags
= DFO_HEADER
+ DFO_STATVFS
;
1906 } else if (e_option
) {
1907 dfo
.dfo_func
= e_output
;
1908 dfo
.dfo_flags
= DFO_HEADER
+ DFO_STATVFS
;
1909 } else if (n_option
) {
1910 dfo
.dfo_func
= n_output
;
1911 dfo
.dfo_flags
= DFO_NOFLAGS
;
1913 dfo
.dfo_func
= default_output
;
1914 dfo
.dfo_flags
= DFO_STATVFS
;
1921 * The (argc,argv) pair contains all the non-option arguments
1924 do_df(int argc
, char *argv
[])
1927 struct df_request
*requests
; /* array of requests */
1929 struct df_request
*dfrp
;
1932 errors
= create_request_list(argc
, argv
, &requests
, &n_requests
);
1934 if (n_requests
== 0)
1938 * If we are going to run the FSType-specific df command,
1939 * rearrange the requests so that we can issue a single command
1940 * per file system type.
1946 * qsort is not a stable sorting method (i.e. requests of
1947 * the same file system type may be swapped, and hence appear
1948 * in the output in a different order from the one in which
1949 * they were listed in the command line). In order to force
1950 * stability, we use the dfr_index field which is unique
1954 n_requests
, sizeof (struct df_request
), df_reqcomp
);
1955 for (i
= 0; i
< n_requests
; i
= j
) {
1956 char *fstype
= requests
[i
].dfr_fstype
;
1958 for (j
= i
+1; j
< n_requests
; j
++)
1959 if (! EQ(fstype
, requests
[j
].dfr_fstype
))
1963 * At this point, requests in the range [i,j) are
1966 * If the -F option was used, and the user specified
1967 * arguments, the filesystem types must match
1969 * XXX: the alternative of doing this check here is to
1970 * invoke prune_list, but then we have to
1971 * modify this code to ignore invalid requests.
1973 if (F_option
&& ! EQ(fstype
, FSType
)) {
1976 for (k
= i
; k
< j
; k
++) {
1977 dfrp
= &requests
[k
];
1978 if (dfrp
->dfr_cmd_arg
!= NULL
) {
1980 "Warning: %s mounted as a "
1988 errors
+= run_fs_specific_df(&requests
[i
], j
-i
);
1991 size_t valid_requests
;
1994 * We have to prune the request list to avoid printing a header
1995 * if there are no valid requests
1997 errors
+= prune_list(requests
, n_requests
, &valid_requests
);
1999 if (valid_requests
) {
2000 struct df_output
*dfop
= select_output();
2002 /* indicates if we already printed out a header line */
2003 int printed_header
= 0;
2005 for (i
= 0; i
< n_requests
; i
++) {
2006 dfrp
= &requests
[i
];
2007 if (! dfrp
->dfr_valid
)
2011 * If we don't have a mount point,
2012 * this must be a block device.
2014 if (DFR_ISMOUNTEDFS(dfrp
)) {
2015 struct statvfs64 stvfs
;
2017 if ((dfop
->dfo_flags
& DFO_STATVFS
) &&
2018 statvfs64(DFR_MOUNT_POINT(dfrp
),
2021 "cannot statvfs %s:",
2022 DFR_MOUNT_POINT(dfrp
));
2026 if ((!printed_header
) &&
2027 (dfop
->dfo_flags
& DFO_HEADER
)) {
2032 (*dfop
->dfo_func
)(dfrp
, &stvfs
);
2035 * -h option only works for
2036 * mounted filesystems
2040 "-h option incompatible with unmounted special device (%s)",
2045 errors
+= run_fs_specific_df(dfrp
, 1);
2055 * The rest of this file implements the devnm command
2059 find_dev_name(char *file
, dev_t dev
)
2061 struct df_request dfreq
;
2063 dfreq
.dfr_cmd_arg
= file
;
2064 dfreq
.dfr_fstype
= 0;
2065 dfreq
.dfr_mte
= NULL
;
2066 path_mount_entry(&dfreq
, dev
);
2067 return (DFR_ISMOUNTEDFS(&dfreq
) ? DFR_SPECIAL(&dfreq
) : NULL
);
2072 do_devnm(int argc
, char *argv
[])
2079 errmsg(ERR_NONAME
, "Usage: %s name ...", DEVNM_CMD
);
2083 for (arg
= 1; arg
< argc
; arg
++) {
2084 char *file
= argv
[arg
];
2087 if (stat64(file
, &st
) == -1) {
2088 errmsg(ERR_PERROR
, "%s: ", file
);
2093 if (! is_remote_fs(st
.st_fstype
) &&
2094 ! EQ(st
.st_fstype
, MNTTYPE_TMPFS
) &&
2095 (dev_name
= find_dev_name(file
, st
.st_dev
)))
2096 (void) printf("%s %s\n", dev_name
, file
);
2099 "%s not found", file
);