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.
39 #include <sys/types.h>
41 #include <sys/statvfs.h>
43 #include <sys/param.h>
44 #include <sys/mnttab.h>
45 #include <sys/mntent.h>
46 #include <sys/vfstab.h>
48 #include <sys/mkdev.h>
49 #include <sys/int_limits.h>
55 extern char *default_fstype(char *);
59 * String pointers in this code may point to statically allocated memory
60 * or dynamically allocated memory. Furthermore, a dynamically allocated
61 * string may be pointed to by more than one pointer. This does not pose
62 * a problem because malloc'ed memory is never free'd (so we don't need
63 * to remember which pointers point to malloc'ed memory).
68 * Only strings passed as arguments to the TRANSLATE macro need to
73 #define MNTTYPE_LOFS "lofs"
76 #define EQ(s1, s2) (strcmp(s1, s2) == 0)
77 #define NEW(type) xmalloc(sizeof (type))
78 #define CLEAR(var) (void) memset(&(var), 0, sizeof (var))
79 #define MAX(a, b) ((a) > (b) ? (a) : (b))
80 #define MAX3(a, b, c) MAX(a, MAX(b, c))
81 #define TRANSLATE(s) new_string(gettext(s))
83 #define MAX_OPTIONS 36
85 #define MOUNT_TABLE_ENTRIES 40 /* initial allocation */
86 #define MSGBUF_SIZE 1024
87 #define LINEBUF_SIZE 256 /* either input or output lines */
89 #define BLOCK_SIZE 512 /* when reporting in terms of blocks */
91 #define DEVNM_CMD "devnm"
92 #define FS_LIBPATH "/usr/lib/fs/"
93 #define MOUNT_TAB "/etc/mnttab"
94 #define VFS_TAB "/etc/vfstab"
95 #define REMOTE_FS "/etc/dfs/fstypes"
102 * Formatting constants
104 #define IBCS2_FILESYSTEM_WIDTH 15 /* Truncate to match ISC/SCO */
105 #define IBCS2_MOUNT_POINT_WIDTH 10 /* Truncate to match ISC/SCO */
106 #define FILESYSTEM_WIDTH 20
107 #define MOUNT_POINT_WIDTH 19
108 #define SPECIAL_DEVICE_WIDTH 18
109 #define FSTYPE_WIDTH 8
110 #define BLOCK_WIDTH 8
111 #define NFILES_WIDTH 8
112 #define KBYTE_WIDTH 11
113 #define AVAILABLE_WIDTH 10
114 #define SCALED_WIDTH 6
115 #define CAPACITY_WIDTH 9
116 #define BSIZE_WIDTH 6
117 #define FRAGSIZE_WIDTH 7
120 #define NAMELEN_WIDTH 7
121 #define MNT_SPEC_WIDTH MOUNT_POINT_WIDTH + SPECIAL_DEVICE_WIDTH + 2
124 * Flags for the errmsg() function
126 #define ERR_NOFLAGS 0x0
127 #define ERR_NONAME 0x1 /* don't include the program name */
129 #define ERR_FATAL 0x2 /* call exit after printing the */
131 #define ERR_PERROR 0x4 /* append an errno explanation to */
133 #define ERR_USAGE 0x8 /* print the usage line after the */
136 #define NUMBER_WIDTH 40
139 * A numbuf_t is used when converting a number to a string representation
141 typedef char numbuf_t
[ NUMBER_WIDTH
];
144 * We use bool_int instead of int to make clear which variables are
145 * supposed to be boolean
147 typedef int bool_int
;
150 bool_int mte_dev_is_valid
;
152 bool_int mte_ignore
; /* the "ignore" option was set */
153 struct extmnttab
*mte_mount
;
159 char *dfr_cmd_arg
; /* what the user specified */
160 struct mtab_entry
*dfr_mte
;
162 int dfr_index
; /* to make qsort stable */
165 #define DFR_MOUNT_POINT(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_mountp
166 #define DFR_SPECIAL(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_special
167 #define DFR_FSTYPE(dfrp) (dfrp)->dfr_mte->mte_mount->mnt_fstype
168 #define DFR_ISMOUNTEDFS(dfrp) ((dfrp)->dfr_mte != NULL)
170 #define DFRP(p) ((struct df_request *)(p))
172 typedef void (*output_func
)(struct df_request
*, struct statvfs64
*);
175 output_func dfo_func
; /* function that will do the output */
182 #define DFO_NOFLAGS 0x0
183 #define DFO_HEADER 0x1 /* output preceded by header */
184 #define DFO_STATVFS 0x2 /* must do a statvfs64(2) */
187 static char *program_name
;
188 static char df_options
[MAX_OPTIONS
] = "-";
189 static size_t df_options_len
= 1;
190 static char *o_option_arg
; /* arg to the -o option */
192 static char *remote_fstypes
[N_FSTYPES
+1]; /* allocate an extra one */
193 /* to use as a terminator */
196 * The following three variables support an in-memory copy of the mount table
197 * to speedup searches.
199 static struct mtab_entry
*mount_table
; /* array of mtab_entry's */
200 static size_t mount_table_entries
;
201 static size_t mount_table_allocated_entries
;
203 static bool_int F_option
;
204 static bool_int V_option
;
205 static bool_int P_option
; /* Added for XCU4 compliance */
206 static bool_int Z_option
;
207 static bool_int v_option
;
208 static bool_int a_option
;
209 static bool_int b_option
;
210 static bool_int e_option
;
211 static bool_int g_option
;
212 static bool_int h_option
;
213 static bool_int k_option
;
214 static bool_int l_option
;
215 static bool_int m_option
;
216 static bool_int n_option
;
217 static bool_int t_option
;
218 static bool_int o_option
;
220 static bool_int tty_output
;
221 static bool_int use_scaling
;
224 static void usage(void);
225 static void do_devnm(int, char **);
226 static void do_df(int, char **) __NORETURN
;
227 static void parse_options(int, char **);
228 static char *basename(char *);
230 static libzfs_handle_t
*(*_libzfs_init
)(void);
231 static zfs_handle_t
*(*_zfs_open
)(libzfs_handle_t
*, const char *, int);
232 static void (*_zfs_close
)(zfs_handle_t
*);
233 static uint64_t (*_zfs_prop_get_int
)(zfs_handle_t
*, zfs_prop_t
);
234 static libzfs_handle_t
*g_zfs
;
237 * Dynamically check for libzfs, in case the user hasn't installed the SUNWzfs
238 * packages. A basic utility such as df shouldn't depend on optional
246 if (_libzfs_init
!= NULL
)
247 return (g_zfs
!= NULL
);
249 if ((hdl
= dlopen("libzfs.so", RTLD_LAZY
)) != NULL
) {
250 _libzfs_init
= (libzfs_handle_t
*(*)(void))dlsym(hdl
,
252 _zfs_open
= (zfs_handle_t
*(*)())dlsym(hdl
, "zfs_open");
253 _zfs_close
= (void (*)())dlsym(hdl
, "zfs_close");
254 _zfs_prop_get_int
= (uint64_t (*)())
255 dlsym(hdl
, "zfs_prop_get_int");
257 if (_libzfs_init
!= NULL
) {
258 assert(_zfs_open
!= NULL
);
259 assert(_zfs_close
!= NULL
);
260 assert(_zfs_prop_get_int
!= NULL
);
262 g_zfs
= _libzfs_init();
266 return (g_zfs
!= NULL
);
270 main(int argc
, char *argv
[])
272 (void) setlocale(LC_ALL
, "");
274 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
275 #define TEXT_DOMAIN "SYS_TEST"
277 (void) textdomain(TEXT_DOMAIN
);
279 program_name
= basename(argv
[0]);
281 if (EQ(program_name
, DEVNM_CMD
))
282 do_devnm(argc
, argv
);
284 parse_options(argc
, argv
);
287 * The k_option implies SunOS 4.x compatibility: when the special
288 * device name is too long the line will be split except when the
289 * output has been redirected.
290 * This is also valid for the -h option.
293 if (use_scaling
|| k_option
|| P_option
|| v_option
)
294 tty_output
= isatty(1);
296 do_df(argc
- optind
, &argv
[optind
]);
302 * Prints an error message to stderr.
306 errmsg(int flags
, char *fmt
, ...)
308 char buf
[MSGBUF_SIZE
];
313 if (flags
& ERR_NONAME
)
316 offset
= sprintf(buf
, "%s: ", program_name
);
319 cc
= vsprintf(&buf
[offset
], gettext(fmt
), ap
);
323 if (flags
& ERR_PERROR
) {
324 if (buf
[offset
-1] != ' ')
325 (void) strcat(buf
, " ");
326 (void) strcat(buf
, strerror(errno
));
328 (void) fprintf(stderr
, "%s\n", buf
);
329 if (flags
& ERR_USAGE
)
331 if (flags
& ERR_FATAL
)
340 "Usage: %s [-F FSType] [-abeghklmntPVvZ]"
341 " [-o FSType-specific_options]"
342 " [directory | block_device | resource]", program_name
);
357 errmsg(ERR_FATAL
, "out of memory");
365 * Allocate memory using malloc but terminate if the allocation fails
370 void *p
= malloc(size
);
374 errmsg(ERR_FATAL
, "out of memory");
381 * Allocate memory using realloc but terminate if the allocation fails
384 xrealloc(void *ptr
, size_t size
)
386 void *p
= realloc(ptr
, size
);
390 errmsg(ERR_FATAL
, "out of memory");
397 * fopen the specified file for reading but terminate if the fopen fails
402 FILE *fp
= fopen(file
, "r");
405 errmsg(ERR_FATAL
+ ERR_PERROR
, "failed to open %s:", file
);
411 * Read remote file system types from REMOTE_FS into the
412 * remote_fstypes array.
418 char line_buf
[LINEBUF_SIZE
];
419 size_t fstype_index
= 0;
421 if ((fp
= fopen(REMOTE_FS
, "r")) == NULL
) {
423 "Warning: can't open %s, ignored", REMOTE_FS
);
427 while (fgets(line_buf
, sizeof (line_buf
), fp
) != NULL
) {
428 char buf
[LINEBUF_SIZE
];
430 (void) sscanf(line_buf
, "%s", buf
);
431 remote_fstypes
[fstype_index
++] = new_string(buf
);
433 if (fstype_index
== N_FSTYPES
)
441 * Returns TRUE if fstype is a remote file system type;
442 * otherwise, returns FALSE.
445 is_remote_fs(char *fstype
)
448 static bool_int remote_fs_initialized
;
450 if (! remote_fs_initialized
) {
452 remote_fs_initialized
= TRUE
;
455 for (p
= remote_fstypes
; *p
; p
++)
465 char *p
= strrchr(s
, '/');
467 return (p
? p
+1 : s
);
472 * Create a new "struct extmnttab" and make sure that its fields point
473 * to malloc'ed memory
475 static struct extmnttab
*
476 mntdup(struct extmnttab
*old
)
478 struct extmnttab
*new = NEW(struct extmnttab
);
480 new->mnt_special
= new_string(old
->mnt_special
);
481 new->mnt_mountp
= new_string(old
->mnt_mountp
);
482 new->mnt_fstype
= new_string(old
->mnt_fstype
);
483 new->mnt_mntopts
= new_string(old
->mnt_mntopts
);
484 new->mnt_time
= new_string(old
->mnt_time
);
485 new->mnt_major
= old
->mnt_major
;
486 new->mnt_minor
= old
->mnt_minor
;
492 mtab_error(char *mtab_file
, int status
)
494 if (status
== MNT_TOOLONG
)
495 errmsg(ERR_NOFLAGS
, "a line in %s exceeds %d characters",
496 mtab_file
, MNT_LINE_MAX
);
497 else if (status
== MNT_TOOMANY
)
499 "a line in %s has too many fields", mtab_file
);
500 else if (status
== MNT_TOOFEW
)
502 "a line in %s has too few fields", mtab_file
);
505 "error while reading %s: %d", mtab_file
, status
);
512 * Read the mount table from the specified file.
513 * We keep the table in memory for faster lookups.
518 char *mtab_file
= MOUNT_TAB
;
520 struct extmnttab mtab
;
523 fp
= xfopen(mtab_file
);
526 mount_table_allocated_entries
= MOUNT_TABLE_ENTRIES
;
527 mount_table_entries
= 0;
528 mount_table
= xmalloc(
529 mount_table_allocated_entries
* sizeof (struct mtab_entry
));
531 while ((status
= getextmntent(fp
, &mtab
, sizeof (struct extmnttab
)))
533 struct mtab_entry
*mtep
;
535 if (mount_table_entries
== mount_table_allocated_entries
) {
536 mount_table_allocated_entries
+= MOUNT_TABLE_ENTRIES
;
537 mount_table
= xrealloc(mount_table
,
538 mount_table_allocated_entries
*
539 sizeof (struct mtab_entry
));
541 mtep
= &mount_table
[mount_table_entries
++];
542 mtep
->mte_mount
= mntdup(&mtab
);
543 mtep
->mte_dev_is_valid
= FALSE
;
544 mtep
->mte_ignore
= (hasmntopt((struct mnttab
*)&mtab
,
545 MNTOPT_IGNORE
) != NULL
);
550 if (status
== -1) /* reached EOF */
552 mtab_error(mtab_file
, status
);
558 * We use this macro when we want to record the option for the purpose of
559 * passing it to the FS-specific df
561 #define SET_OPTION(opt) opt##_option = TRUE, \
562 df_options[df_options_len++] = arg
565 parse_options(int argc
, char *argv
[])
569 opterr
= 0; /* getopt shouldn't complain about unknown options */
571 while ((arg
= getopt(argc
, argv
, "F:o:abehkVtgnlmPvZ")) != EOF
) {
574 errmsg(ERR_FATAL
+ ERR_USAGE
,
575 "more than one FSType specified");
578 } else if (arg
== 'V' && ! V_option
) {
580 } else if (arg
== 'v' && ! v_option
) {
582 } else if (arg
== 'P' && ! P_option
) {
584 } else if (arg
== 'a' && ! a_option
) {
586 } else if (arg
== 'b' && ! b_option
) {
588 } else if (arg
== 'e' && ! e_option
) {
590 } else if (arg
== 'g' && ! g_option
) {
592 } else if (arg
== 'h') {
595 } else if (arg
== 'k' && ! k_option
) {
597 } else if (arg
== 'l' && ! l_option
) {
599 } else if (arg
== 'm' && ! m_option
) {
601 } else if (arg
== 'n' && ! n_option
) {
603 } else if (arg
== 't' && ! t_option
) {
605 } else if (arg
== 'o') {
607 errmsg(ERR_FATAL
+ ERR_USAGE
,
608 "the -o option can only be specified once");
610 o_option_arg
= optarg
;
611 } else if (arg
== 'Z') {
613 } else if (arg
== '?') {
614 errmsg(ERR_USAGE
, "unknown option: %c", optopt
);
619 * Option sanity checks
621 if (g_option
&& o_option
)
622 errmsg(ERR_FATAL
, "-o and -g options are incompatible");
623 if (l_option
&& o_option
)
624 errmsg(ERR_FATAL
, "-o and -l options are incompatible");
625 if (n_option
&& o_option
)
626 errmsg(ERR_FATAL
, "-o and -n options are incompatible");
627 if (use_scaling
&& o_option
)
628 errmsg(ERR_FATAL
, "-o and -h options are incompatible");
634 * Check if the user-specified argument is a resource name.
635 * A resource name is whatever is placed in the mnt_special field of
636 * struct mnttab. In the case of NFS, a resource name has the form
638 * We try to find an exact match between the user-specified argument
639 * and the mnt_special field of a mount table entry.
640 * We also use the heuristic of removing the basename from the user-specified
641 * argument and repeating the test until we get a match. This works
642 * fine for NFS but may fail for other remote file system types. However,
643 * it is guaranteed that the function will not fail if the user specifies
644 * the exact resource name.
645 * If successful, this function sets the 'dfr_mte' field of '*dfrp'
648 resource_mount_entry(struct df_request
*dfrp
)
653 * We need our own copy since we will modify the string
655 name
= new_string(dfrp
->dfr_cmd_arg
);
662 * Compare against all known mount points.
663 * We start from the most recent mount, which is at the
666 for (i
= mount_table_entries
- 1; i
>= 0; i
--) {
667 struct mtab_entry
*mtep
= &mount_table
[i
];
669 if (EQ(name
, mtep
->mte_mount
->mnt_special
)) {
670 dfrp
->dfr_mte
= mtep
;
676 * Remove the last component of the pathname.
677 * If there is no such component, this is not a resource name.
679 p
= strrchr(name
, '/');
689 * Try to match the command line argument which is a block special device
690 * with the special device of one of the mounted file systems.
691 * If one is found, set the appropriate field of 'dfrp' to the mount
695 bdev_mount_entry(struct df_request
*dfrp
)
698 char *special
= dfrp
->dfr_cmd_arg
;
701 * Compare against all known mount points.
702 * We start from the most recent mount, which is at the
705 for (i
= mount_table_entries
- 1; i
>= 0; i
--) {
706 struct mtab_entry
*mtep
= &mount_table
[i
];
708 if (EQ(special
, mtep
->mte_mount
->mnt_special
)) {
709 dfrp
->dfr_mte
= mtep
;
715 static struct mtab_entry
*
716 devid_matches(int i
, dev_t devno
)
718 struct mtab_entry
*mtep
= &mount_table
[i
];
719 struct extmnttab
*mtp
= mtep
->mte_mount
;
720 /* int len = strlen(mtp->mnt_mountp); */
722 if (EQ(mtp
->mnt_fstype
, MNTTYPE_SWAP
))
725 * check if device numbers match. If there is a cached device number
726 * in the mtab_entry, use it, otherwise get the device number
727 * either from the mnttab entry or by stat'ing the mount point.
729 if (! mtep
->mte_dev_is_valid
) {
733 dev
= makedev(mtp
->mnt_major
, mtp
->mnt_minor
);
737 if (stat64(mtp
->mnt_mountp
, &st
) == -1) {
744 mtep
->mte_dev_is_valid
= TRUE
;
746 if (mtep
->mte_dev
== devno
) {
753 * Find the mount point under which the user-specified path resides
754 * and set the 'dfr_mte' field of '*dfrp' to point to the mount table entry.
757 path_mount_entry(struct df_request
*dfrp
, dev_t devno
)
759 char dirpath
[MAXPATHLEN
];
760 char *dir
= dfrp
->dfr_cmd_arg
;
761 struct mtab_entry
*match
, *tmatch
;
765 * Expand the given path to get a canonical version (i.e. an absolute
766 * path without symbolic links).
768 if (realpath(dir
, dirpath
) == NULL
) {
769 errmsg(ERR_PERROR
, "cannot canonicalize %s:", dir
);
773 * If the mnt point is lofs, search from the top of entries from
774 * /etc/mnttab and return the entry that best matches the pathname.
775 * For non-lofs mount points, return the first entry from the bottom
776 * of the entries in /etc/mnttab that matches on the devid field
779 if (dfrp
->dfr_fstype
&& EQ(dfrp
->dfr_fstype
, MNTTYPE_LOFS
)) {
780 struct extmnttab
*entryp
;
787 for (i
= 0; i
< mount_table_entries
; i
++) {
788 entryp
= mount_table
[i
].mte_mount
;
790 if (!EQ(entryp
->mnt_fstype
, MNTTYPE_LOFS
))
794 mountp
= entryp
->mnt_mountp
;
797 * Count the number of matching characters
798 * until either path or mountpoint is exhausted
800 while ((p
= *path
++) == (m
= *mountp
++)) {
803 if (p
== '\0' || m
== '\0')
807 /* Both exhausted so we have a match */
808 if (p
== '\0' && m
== '\0') {
814 * We have exhausted the mountpoint and the current
815 * character in the path is a '/' hence the full path
816 * traverses this mountpoint.
817 * Record this as the best candidate so far.
819 if (p
== '/' && m
== '\0') {
820 if (score
> best_score
) {
828 match
= &mount_table
[best_index
];
830 for (i
= mount_table_entries
- 1; i
>= 0; i
--) {
831 if (tmatch
= devid_matches(i
, devno
)) {
833 * If executing in a zone, there might be lofs
834 * mounts for which the real mount point is
835 * invisible; accept the "best fit" for this
839 if (!EQ(match
->mte_mount
->mnt_fstype
,
848 "Could not find mount point for %s", dir
);
851 dfrp
->dfr_mte
= match
;
855 * Execute a single FS-specific df command for all given requests
856 * Return 0 if successful, 1 otherwise.
859 run_fs_specific_df(struct df_request request_list
[], int entries
)
867 char cmd_path
[MAXPATHLEN
];
873 fstype
= request_list
[0].dfr_fstype
;
875 if (F_option
&& ! EQ(FSType
, fstype
))
878 (void) sprintf(cmd_path
, "%s%s/df", FS_LIBPATH
, fstype
);
883 * 1 for the generic options that we propagate
884 * 1 for the terminating NULL pointer
885 * n for the number of user-specified arguments
887 size
= (5 + entries
) * sizeof (char *);
888 argv
= xmalloc(size
);
889 (void) memset(argv
, 0, size
);
894 argv
[argv_index
++] = "-o";
895 argv
[argv_index
++] = o_option_arg
;
899 * Check if we need to propagate any generic options
901 if (df_options_len
> 1)
902 argv
[argv_index
++] = df_options
;
905 * If there is a user-specified path, we pass that to the
906 * FS-specific df. Otherwise, we are guaranteed to have a mount
907 * point, since a request without a user path implies that
908 * we are reporting only on mounted file systems.
910 for (i
= 0; i
< entries
; i
++) {
911 struct df_request
*dfrp
= &request_list
[i
];
913 argv
[argv_index
++] = (dfrp
->dfr_cmd_arg
== NULL
)
914 ? DFR_MOUNT_POINT(dfrp
)
919 for (i
= 0; i
< argv_index
-1; i
++)
920 (void) printf("%s ", argv
[i
]);
921 (void) printf("%s\n", argv
[i
]);
928 errmsg(ERR_PERROR
, "cannot fork process:");
930 } else if (pid
== 0) {
931 (void) execv(cmd_path
, argv
);
934 "operation not applicable for FSType %s",
937 errmsg(ERR_PERROR
, "cannot execute %s:", cmd_path
);
945 pid_t wpid
= waitpid(pid
, &status
, 0);
951 errmsg(ERR_PERROR
, "waitpid error:");
958 return ((WIFEXITED(status
) && WEXITSTATUS(status
) == 0) ? 0 : 1);
964 * Remove from the request list all requests that do not apply.
965 * Notice that the subsequent processing of the requests depends on
966 * the sanity checking performed by this function.
969 prune_list(struct df_request request_list
[],
971 size_t *valid_requests
)
977 for (i
= 0; i
< n_requests
; i
++) {
978 struct df_request
*dfrp
= &request_list
[i
];
981 * Skip file systems that are not mounted if either the
982 * -l or -n options were specified. If none of these options
983 * are present, the appropriate FS-specific df will be invoked.
985 if (! DFR_ISMOUNTEDFS(dfrp
)) {
986 if (l_option
|| n_option
) {
988 "%s option incompatible with unmounted "
989 "special device (%s)",
990 l_option
? "-l" : "-n", dfrp
->dfr_cmd_arg
);
991 dfrp
->dfr_valid
= FALSE
;
1000 * Check for inconsistency between the argument of -F and
1001 * the actual file system type.
1002 * If there is an inconsistency and the user specified a
1003 * path, this is an error since we are asked to interpret
1004 * the path using the wrong file system type. If there is
1005 * no path associated with this request, we quietly ignore it.
1007 if (F_option
&& ! EQ(dfrp
->dfr_fstype
, FSType
)) {
1008 dfrp
->dfr_valid
= FALSE
;
1009 if (dfrp
->dfr_cmd_arg
!= NULL
) {
1011 "Warning: %s mounted as a %s file system",
1012 dfrp
->dfr_cmd_arg
, dfrp
->dfr_fstype
);
1019 * Skip remote file systems if the -l option is present
1021 if (l_option
&& is_remote_fs(dfrp
->dfr_fstype
)) {
1022 if (dfrp
->dfr_cmd_arg
!= NULL
) {
1024 "Warning: %s is not a local file system",
1028 dfrp
->dfr_valid
= FALSE
;
1033 * Skip file systems mounted as "ignore" unless the -a option
1034 * is present, or the user explicitly specified them on
1037 if (dfrp
->dfr_mte
->mte_ignore
&&
1038 ! (a_option
|| dfrp
->dfr_cmd_arg
)) {
1039 dfrp
->dfr_valid
= FALSE
;
1045 *valid_requests
= n_valid
;
1051 * Print the appropriate header for the requested output format.
1052 * Options are checked in order of their precedence.
1057 if (use_scaling
) { /* this comes from the -h option */
1060 (void) printf("%-*s %*s %*s %*s %-*s %s\n",
1061 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1062 SCALED_WIDTH
, TRANSLATE("Size"),
1063 SCALED_WIDTH
, TRANSLATE("Used"),
1064 AVAILABLE_WIDTH
, TRANSLATE("Available"),
1065 CAPACITY_WIDTH
, TRANSLATE("Capacity"),
1066 TRANSLATE("Mounted on"));
1073 (void) printf(gettext("%-*s %*s %*s %*s %-*s %s\n"),
1074 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1075 KBYTE_WIDTH
, TRANSLATE("1024-blocks"),
1076 KBYTE_WIDTH
, TRANSLATE("Used"),
1077 KBYTE_WIDTH
, TRANSLATE("Available"),
1078 CAPACITY_WIDTH
, TRANSLATE("Capacity"),
1079 TRANSLATE("Mounted on"));
1086 (void) printf(gettext("%-*s %*s %*s %*s %-*s %s\n"),
1087 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1088 KBYTE_WIDTH
, TRANSLATE("1M-blocks"),
1089 KBYTE_WIDTH
, TRANSLATE("Used"),
1090 KBYTE_WIDTH
, TRANSLATE("Available"),
1091 CAPACITY_WIDTH
, TRANSLATE("Capacity"),
1092 TRANSLATE("Mounted on"));
1096 /* Added for XCU4 compliance */
1100 (void) printf(gettext("%-*s %*s %*s %*s %-*s %s\n"),
1101 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1102 KBYTE_WIDTH
, TRANSLATE("512-blocks"),
1103 KBYTE_WIDTH
, TRANSLATE("Used"),
1104 KBYTE_WIDTH
, TRANSLATE("Available"),
1105 CAPACITY_WIDTH
, TRANSLATE("Capacity"),
1106 TRANSLATE("Mounted on"));
1113 (void) printf("%-*s %-*s %*s %*s %*s %-*s\n",
1114 IBCS2_MOUNT_POINT_WIDTH
, TRANSLATE("Mount Dir"),
1115 IBCS2_FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1116 BLOCK_WIDTH
, TRANSLATE("blocks"),
1117 BLOCK_WIDTH
, TRANSLATE("used"),
1118 BLOCK_WIDTH
, TRANSLATE("free"),
1119 CAPACITY_WIDTH
, TRANSLATE(" %used"));
1123 (void) printf(gettext("%-*s %*s\n"),
1124 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1125 BLOCK_WIDTH
, TRANSLATE("ifree"));
1129 (void) printf(gettext("%-*s %*s\n"),
1130 FILESYSTEM_WIDTH
, TRANSLATE("Filesystem"),
1131 BLOCK_WIDTH
, TRANSLATE("avail"));
1138 * Convert an unsigned long long to a string representation and place the
1139 * result in the caller-supplied buffer.
1140 * The given number is in units of "unit_from" size, but the
1141 * converted number will be in units of "unit_to" size. The unit sizes
1142 * must be powers of 2.
1143 * The value "(unsigned long long)-1" is a special case and is always
1144 * converted to "-1".
1145 * Returns a pointer to the caller-supplied buffer.
1149 char *buf
, /* put the result here */
1150 unsigned long long number
, /* convert this number */
1151 int unit_from
, /* from units of this size */
1152 int unit_to
) /* to units of this size */
1154 if ((long long)number
== (long long)-1)
1155 (void) strcpy(buf
, "-1");
1157 if (unit_from
== unit_to
)
1158 (void) sprintf(buf
, "%llu", number
);
1159 else if (unit_from
< unit_to
)
1160 (void) sprintf(buf
, "%llu",
1161 number
/ (unsigned long long)(unit_to
/ unit_from
));
1163 (void) sprintf(buf
, "%llu",
1164 number
* (unsigned long long)(unit_from
/ unit_to
));
1170 * Convert an unsigned long long to a string representation and place the
1171 * result in the caller-supplied buffer.
1172 * The given number is in units of "unit_from" size,
1173 * this will first be converted to a number in 1024 or 1000 byte size,
1174 * depending on the scaling factor.
1175 * Then the number is scaled down until it is small enough to be in a good
1176 * human readable format i.e. in the range 0 thru scale-1.
1177 * If it's smaller than 10 there's room enough to provide one decimal place.
1178 * The value "(unsigned long long)-1" is a special case and is always
1179 * converted to "-1".
1180 * Returns a pointer to the caller-supplied buffer.
1183 number_to_scaled_string(
1184 numbuf_t buf
, /* put the result here */
1185 unsigned long long number
, /* convert this number */
1189 unsigned long long save
= 0;
1190 char *M
= "KMGTPE"; /* Measurement: kilo, mega, giga, tera, peta, exa */
1191 char *uom
= M
; /* unit of measurement, initially 'K' (=M[0]) */
1193 if ((long long)number
== (long long)-1) {
1194 (void) strcpy(buf
, "-1");
1199 * Convert number from unit_from to given scale (1024 or 1000).
1200 * This means multiply number by unit_from and divide by scale.
1202 * Would like to multiply by unit_from and then divide by scale,
1203 * but if the first multiplication would overflow, then need to
1204 * divide by scale and then multiply by unit_from.
1206 if (number
> (UINT64_MAX
/ (unsigned long long)unit_from
)) {
1207 number
= (number
/ (unsigned long long)scale
) *
1208 (unsigned long long)unit_from
;
1210 number
= (number
* (unsigned long long)unit_from
) /
1211 (unsigned long long)scale
;
1215 * Now we have number as a count of scale units.
1216 * Stop scaling when we reached exa bytes, then something is
1217 * probably wrong with our number.
1220 while ((number
>= scale
) && (*uom
!= 'E')) {
1221 uom
++; /* next unit of measurement */
1223 number
= (number
+ (scale
/ 2)) / scale
;
1225 /* check if we should output a decimal place after the point */
1226 if (save
&& ((save
/ scale
) < 10)) {
1227 /* sprintf() will round for us */
1228 float fnum
= (float)save
/ scale
;
1229 (void) sprintf(buf
, "%2.1f%c", fnum
, *uom
);
1231 (void) sprintf(buf
, "%4llu%c", number
, *uom
);
1237 * The statvfs() implementation allows us to return only two values, the total
1238 * number of blocks and the number of blocks free. The equation 'used = total -
1239 * free' will not work for ZFS filesystems, due to the nature of pooled storage.
1240 * We choose to return values in the statvfs structure that will produce correct
1241 * results for 'used' and 'available', but not 'total'. This function will open
1242 * the underlying ZFS dataset if necessary and get the real value.
1245 adjust_total_blocks(struct df_request
*dfrp
, fsblkcnt64_t
*total
,
1248 char *dataset
, *slash
;
1249 boolean_t first
= TRUE
;
1252 if (strcmp(DFR_FSTYPE(dfrp
), MNTTYPE_ZFS
) != 0 || !load_libzfs())
1256 * We want to get the total size for this filesystem as bounded by any
1257 * quotas. In order to do this, we start at the current filesystem and
1258 * work upwards looking for the smallest quota. When we reach the
1259 * pool itself, the quota is the amount used plus the amount
1262 if ((dataset
= strdup(DFR_SPECIAL(dfrp
))) == NULL
)
1265 slash
= dataset
+ strlen(dataset
);
1266 while (slash
!= NULL
) {
1268 uint64_t this_quota
;
1272 zhp
= _zfs_open(g_zfs
, dataset
, ZFS_TYPE_DATASET
);
1276 /* true at first iteration of loop */
1278 quota
= _zfs_prop_get_int(zhp
, ZFS_PROP_REFQUOTA
);
1284 this_quota
= _zfs_prop_get_int(zhp
, ZFS_PROP_QUOTA
);
1285 if (this_quota
&& this_quota
< quota
)
1288 /* true at last iteration of loop */
1289 if ((slash
= strrchr(dataset
, '/')) == NULL
) {
1292 size
= _zfs_prop_get_int(zhp
, ZFS_PROP_USED
) +
1293 _zfs_prop_get_int(zhp
, ZFS_PROP_AVAILABLE
);
1302 * Modify total only if we managed to get some stats from libzfs.
1305 *total
= quota
/ blocksize
;
1310 * The output will appear properly columnized regardless of the names of
1311 * the various fields
1314 g_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1316 fsblkcnt64_t available_blocks
= fsp
->f_bavail
;
1317 fsblkcnt64_t total_blocks
= fsp
->f_blocks
;
1318 numbuf_t total_blocks_buf
;
1319 numbuf_t total_files_buf
;
1320 numbuf_t free_blocks_buf
;
1321 numbuf_t available_blocks_buf
;
1322 numbuf_t free_files_buf
;
1326 #define DEFINE_STR_LEN(var) \
1327 static char *var##_str; \
1328 static size_t var##_len
1330 #define SET_STR_LEN(name, var)\
1332 var##_str = TRANSLATE(name); \
1333 var##_len = strlen(var##_str); \
1336 DEFINE_STR_LEN(block_size
);
1337 DEFINE_STR_LEN(frag_size
);
1338 DEFINE_STR_LEN(total_blocks
);
1339 DEFINE_STR_LEN(free_blocks
);
1340 DEFINE_STR_LEN(available
);
1341 DEFINE_STR_LEN(total_files
);
1342 DEFINE_STR_LEN(free_files
);
1343 DEFINE_STR_LEN(fstype
);
1344 DEFINE_STR_LEN(fsys_id
);
1345 DEFINE_STR_LEN(fname
);
1346 DEFINE_STR_LEN(flag
);
1350 * The first argument of each of the following macro invocations is a
1351 * string that needs to be translated.
1353 SET_STR_LEN("block size", block_size
);
1354 SET_STR_LEN("frag size", frag_size
);
1355 SET_STR_LEN("total blocks", total_blocks
);
1356 SET_STR_LEN("free blocks", free_blocks
);
1357 SET_STR_LEN("available", available
);
1358 SET_STR_LEN("total files", total_files
);
1359 SET_STR_LEN("free files", free_files
);
1360 SET_STR_LEN("fstype", fstype
);
1361 SET_STR_LEN("filesys id", fsys_id
);
1362 SET_STR_LEN("filename length", fname
);
1363 SET_STR_LEN("flag", flag
);
1365 #define NCOL1_WIDTH (int)MAX3(BLOCK_WIDTH, NFILES_WIDTH, FSTYPE_WIDTH)
1366 #define NCOL2_WIDTH (int)MAX3(BLOCK_WIDTH, FSID_WIDTH, FLAG_WIDTH) + 2
1367 #define NCOL3_WIDTH (int)MAX3(BSIZE_WIDTH, BLOCK_WIDTH, NAMELEN_WIDTH)
1368 #define NCOL4_WIDTH (int)MAX(FRAGSIZE_WIDTH, NFILES_WIDTH)
1370 #define SCOL1_WIDTH (int)MAX3(total_blocks_len, free_files_len, fstype_len)
1371 #define SCOL2_WIDTH (int)MAX3(free_blocks_len, fsys_id_len, flag_len)
1372 #define SCOL3_WIDTH (int)MAX3(block_size_len, available_len, fname_len)
1373 #define SCOL4_WIDTH (int)MAX(frag_size_len, total_files_len)
1376 MAX(MOUNT_POINT_WIDTH
, strlen(DFR_MOUNT_POINT(dfrp
)))
1377 + MAX(SPECIAL_DEVICE_WIDTH
, strlen(DFR_SPECIAL(dfrp
)))
1378 + 20); /* plus slop - nulls & formatting */
1379 (void) sprintf(temp_buf
, "%-*s(%-*s):",
1380 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1381 SPECIAL_DEVICE_WIDTH
, DFR_SPECIAL(dfrp
));
1383 (void) printf("%-*s %*lu %-*s %*lu %-*s\n",
1384 NCOL1_WIDTH
+ 1 + SCOL1_WIDTH
+ 1 + NCOL2_WIDTH
+ 1 + SCOL2_WIDTH
,
1386 NCOL3_WIDTH
, fsp
->f_bsize
, SCOL3_WIDTH
, block_size_str
,
1387 NCOL4_WIDTH
, fsp
->f_frsize
, SCOL4_WIDTH
, frag_size_str
);
1391 * Adjust available_blocks value - it can be less than 0 on
1392 * a 4.x file system. Reset it to 0 in order to avoid printing
1395 if ((long long)available_blocks
< (long long)0)
1396 available_blocks
= (fsblkcnt64_t
)0;
1398 adjust_total_blocks(dfrp
, &total_blocks
, fsp
->f_frsize
);
1400 (void) printf("%*s %-*s %*s %-*s %*s %-*s %*s %-*s\n",
1401 NCOL1_WIDTH
, number_to_string(total_blocks_buf
,
1402 total_blocks
, fsp
->f_frsize
, 512),
1403 SCOL1_WIDTH
, total_blocks_str
,
1404 NCOL2_WIDTH
, number_to_string(free_blocks_buf
,
1405 fsp
->f_bfree
, fsp
->f_frsize
, 512),
1406 SCOL2_WIDTH
, free_blocks_str
,
1407 NCOL3_WIDTH
, number_to_string(available_blocks_buf
,
1408 available_blocks
, fsp
->f_frsize
, 512),
1409 SCOL3_WIDTH
, available_str
,
1410 NCOL4_WIDTH
, number_to_string(total_files_buf
,
1411 fsp
->f_files
, 1, 1),
1412 SCOL4_WIDTH
, total_files_str
);
1414 (void) printf("%*s %-*s %*lu %-*s %s\n",
1415 NCOL1_WIDTH
, number_to_string(free_files_buf
,
1416 fsp
->f_ffree
, 1, 1),
1417 SCOL1_WIDTH
, free_files_str
,
1418 NCOL2_WIDTH
, fsp
->f_fsid
, SCOL2_WIDTH
, fsys_id_str
,
1421 (void) printf("%*s %-*s %#*.*lx %-*s %*s %-*s\n\n",
1422 NCOL1_WIDTH
, fsp
->f_basetype
, SCOL1_WIDTH
, fstype_str
,
1423 NCOL2_WIDTH
, NCOL2_WIDTH
-2, fsp
->f_flag
, SCOL2_WIDTH
, flag_str
,
1424 NCOL3_WIDTH
, number_to_string(fname_buf
,
1425 (unsigned long long)fsp
->f_namemax
, 1, 1),
1426 SCOL3_WIDTH
, fname_str
);
1431 k_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1433 fsblkcnt64_t total_blocks
= fsp
->f_blocks
;
1434 fsblkcnt64_t free_blocks
= fsp
->f_bfree
;
1435 fsblkcnt64_t available_blocks
= fsp
->f_bavail
;
1436 fsblkcnt64_t used_blocks
;
1437 char *file_system
= DFR_SPECIAL(dfrp
);
1438 numbuf_t total_blocks_buf
;
1439 numbuf_t used_blocks_buf
;
1440 numbuf_t available_blocks_buf
;
1441 char capacity_buf
[LINEBUF_SIZE
];
1444 * If the free block count is -1, don't trust anything but the total
1447 if (free_blocks
== (fsblkcnt64_t
)-1) {
1448 used_blocks
= (fsblkcnt64_t
)-1;
1449 (void) strcpy(capacity_buf
, " 100%");
1451 fsblkcnt64_t reserved_blocks
= free_blocks
- available_blocks
;
1453 used_blocks
= total_blocks
- free_blocks
;
1456 * The capacity estimation is bogus when available_blocks is 0
1457 * and the super-user has allocated more space. The reason
1458 * is that reserved_blocks is inaccurate in that case, because
1459 * when the super-user allocates space, free_blocks is updated
1460 * but available_blocks is not (since it can't drop below 0).
1462 * XCU4 and POSIX.2 require that any fractional result of the
1463 * capacity estimation be rounded to the next highest integer,
1464 * hence the addition of 0.5.
1466 (void) sprintf(capacity_buf
, "%5.0f%%",
1467 (total_blocks
== 0) ? 0.0 :
1468 ((double)used_blocks
/
1469 (double)(total_blocks
- reserved_blocks
))
1474 * The available_blocks can be less than 0 on a 4.x file system.
1475 * Reset it to 0 in order to avoid printing negative numbers.
1477 if ((long long)available_blocks
< (long long)0)
1478 available_blocks
= (fsblkcnt64_t
)0;
1480 * Print long special device names (usually NFS mounts) in a line
1481 * by themselves when the output is directed to a terminal.
1483 if (tty_output
&& strlen(file_system
) > (size_t)FILESYSTEM_WIDTH
) {
1484 (void) printf("%s\n", file_system
);
1488 adjust_total_blocks(dfrp
, &total_blocks
, fsp
->f_frsize
);
1490 if (use_scaling
) { /* comes from the -h option */
1491 (void) printf("%-*s %*s %*s %*s %-*s %-s\n",
1492 FILESYSTEM_WIDTH
, file_system
,
1493 SCALED_WIDTH
, number_to_scaled_string(total_blocks_buf
,
1494 total_blocks
, fsp
->f_frsize
, scale
),
1495 SCALED_WIDTH
, number_to_scaled_string(used_blocks_buf
,
1496 used_blocks
, fsp
->f_frsize
, scale
),
1497 AVAILABLE_WIDTH
, number_to_scaled_string(available_blocks_buf
,
1498 available_blocks
, fsp
->f_frsize
, scale
),
1499 CAPACITY_WIDTH
, capacity_buf
,
1500 DFR_MOUNT_POINT(dfrp
));
1505 (void) printf("%-*.*s %-*.*s %*lld %*lld %*lld %-.*s\n",
1506 IBCS2_MOUNT_POINT_WIDTH
, IBCS2_MOUNT_POINT_WIDTH
,
1507 DFR_MOUNT_POINT(dfrp
),
1508 IBCS2_FILESYSTEM_WIDTH
, IBCS2_FILESYSTEM_WIDTH
, file_system
,
1509 BLOCK_WIDTH
, total_blocks
,
1510 BLOCK_WIDTH
, used_blocks
,
1511 BLOCK_WIDTH
, available_blocks
,
1512 CAPACITY_WIDTH
, capacity_buf
);
1516 if (P_option
&& !k_option
&& !m_option
) {
1517 (void) printf("%-*s %*s %*s %*s %-*s %-s\n",
1518 FILESYSTEM_WIDTH
, file_system
,
1519 KBYTE_WIDTH
, number_to_string(total_blocks_buf
,
1520 total_blocks
, fsp
->f_frsize
, 512),
1521 KBYTE_WIDTH
, number_to_string(used_blocks_buf
,
1522 used_blocks
, fsp
->f_frsize
, 512),
1523 KBYTE_WIDTH
, number_to_string(available_blocks_buf
,
1524 available_blocks
, fsp
->f_frsize
, 512),
1525 CAPACITY_WIDTH
, capacity_buf
,
1526 DFR_MOUNT_POINT(dfrp
));
1527 } else if (m_option
) {
1528 (void) printf("%-*s %*s %*s %*s %-*s %-s\n",
1529 FILESYSTEM_WIDTH
, file_system
,
1530 KBYTE_WIDTH
, number_to_string(total_blocks_buf
,
1531 total_blocks
, fsp
->f_frsize
, 1024*1024),
1532 KBYTE_WIDTH
, number_to_string(used_blocks_buf
,
1533 used_blocks
, fsp
->f_frsize
, 1024*1024),
1534 KBYTE_WIDTH
, number_to_string(available_blocks_buf
,
1535 available_blocks
, fsp
->f_frsize
, 1024*1024),
1536 CAPACITY_WIDTH
, capacity_buf
,
1537 DFR_MOUNT_POINT(dfrp
));
1539 (void) printf("%-*s %*s %*s %*s %-*s %-s\n",
1540 FILESYSTEM_WIDTH
, file_system
,
1541 KBYTE_WIDTH
, number_to_string(total_blocks_buf
,
1542 total_blocks
, fsp
->f_frsize
, 1024),
1543 KBYTE_WIDTH
, number_to_string(used_blocks_buf
,
1544 used_blocks
, fsp
->f_frsize
, 1024),
1545 KBYTE_WIDTH
, number_to_string(available_blocks_buf
,
1546 available_blocks
, fsp
->f_frsize
, 1024),
1547 CAPACITY_WIDTH
, capacity_buf
,
1548 DFR_MOUNT_POINT(dfrp
));
1553 * The following is for internationalization support.
1555 static bool_int strings_initialized
;
1556 static char *files_str
;
1557 static char *blocks_str
;
1558 static char *total_str
;
1559 static char *kilobytes_str
;
1564 total_str
= TRANSLATE("total");
1565 files_str
= TRANSLATE("files");
1566 blocks_str
= TRANSLATE("blocks");
1567 kilobytes_str
= TRANSLATE("kilobytes");
1568 strings_initialized
= TRUE
;
1571 #define STRINGS_INIT() if (!strings_initialized) strings_init()
1575 t_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1577 fsblkcnt64_t total_blocks
= fsp
->f_blocks
;
1578 numbuf_t total_blocks_buf
;
1579 numbuf_t total_files_buf
;
1580 numbuf_t free_blocks_buf
;
1581 numbuf_t free_files_buf
;
1585 adjust_total_blocks(dfrp
, &total_blocks
, fsp
->f_frsize
);
1587 (void) printf("%-*s(%-*s): %*s %s %*s %s\n",
1588 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1589 SPECIAL_DEVICE_WIDTH
, DFR_SPECIAL(dfrp
),
1590 BLOCK_WIDTH
, number_to_string(free_blocks_buf
,
1591 fsp
->f_bfree
, fsp
->f_frsize
, 512),
1593 NFILES_WIDTH
, number_to_string(free_files_buf
,
1594 fsp
->f_ffree
, 1, 1),
1597 * The total column used to use the same space as the mnt pt & special
1598 * dev fields. However, this doesn't work with massive special dev
1599 * fields * (eg > 500 chars) causing an enormous amount of white space
1600 * before the total column (see bug 4100411). So the code was
1601 * simplified to set the total column at the usual gap.
1602 * This had the side effect of fixing a bug where the previously
1603 * used static buffer was overflowed by the same massive special dev.
1605 (void) printf("%*s: %*s %s %*s %s\n",
1606 MNT_SPEC_WIDTH
, total_str
,
1607 BLOCK_WIDTH
, number_to_string(total_blocks_buf
,
1608 total_blocks
, fsp
->f_frsize
, 512),
1610 NFILES_WIDTH
, number_to_string(total_files_buf
,
1611 fsp
->f_files
, 1, 1),
1617 eb_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1619 numbuf_t free_files_buf
;
1620 numbuf_t free_kbytes_buf
;
1624 (void) printf("%-*s(%-*s): %*s %s\n",
1625 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1626 SPECIAL_DEVICE_WIDTH
, DFR_SPECIAL(dfrp
),
1627 MAX(KBYTE_WIDTH
, NFILES_WIDTH
),
1628 number_to_string(free_kbytes_buf
,
1629 fsp
->f_bfree
, fsp
->f_frsize
, 1024),
1631 (void) printf("%-*s(%-*s): %*s %s\n",
1632 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1633 SPECIAL_DEVICE_WIDTH
, DFR_SPECIAL(dfrp
),
1634 MAX(NFILES_WIDTH
, NFILES_WIDTH
),
1635 number_to_string(free_files_buf
, fsp
->f_ffree
, 1, 1),
1641 e_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1643 numbuf_t free_files_buf
;
1645 (void) printf("%-*s %*s\n",
1646 FILESYSTEM_WIDTH
, DFR_SPECIAL(dfrp
),
1648 number_to_string(free_files_buf
, fsp
->f_ffree
, 1, 1));
1653 b_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1655 numbuf_t free_blocks_buf
;
1657 (void) printf("%-*s %*s\n",
1658 FILESYSTEM_WIDTH
, DFR_SPECIAL(dfrp
),
1659 BLOCK_WIDTH
, number_to_string(free_blocks_buf
,
1660 fsp
->f_bfree
, fsp
->f_frsize
, 1024));
1666 n_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1668 (void) printf("%-*s: %-*s\n",
1669 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1670 FSTYPE_WIDTH
, dfrp
->dfr_fstype
);
1675 default_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1677 numbuf_t free_blocks_buf
;
1678 numbuf_t free_files_buf
;
1682 (void) printf("%-*s(%-*s):%*s %s %*s %s\n",
1683 MOUNT_POINT_WIDTH
, DFR_MOUNT_POINT(dfrp
),
1684 SPECIAL_DEVICE_WIDTH
, DFR_SPECIAL(dfrp
),
1685 BLOCK_WIDTH
, number_to_string(free_blocks_buf
,
1686 fsp
->f_bfree
, fsp
->f_frsize
, 512),
1688 NFILES_WIDTH
, number_to_string(free_files_buf
,
1689 fsp
->f_ffree
, 1, 1),
1696 V_output(struct df_request
*dfrp
, struct statvfs64
*fsp
)
1698 char temp_buf
[LINEBUF_SIZE
];
1700 if (df_options_len
> 1)
1701 (void) strcat(strcpy(temp_buf
, df_options
), " ");
1705 (void) printf("%s -F %s %s%s\n",
1706 program_name
, dfrp
->dfr_fstype
, temp_buf
,
1707 dfrp
->dfr_cmd_arg
? dfrp
->dfr_cmd_arg
: DFR_SPECIAL(dfrp
));
1712 * This function is used to sort the array of df_requests according to fstype
1715 df_reqcomp(const void *p1
, const void *p2
)
1717 int v
= strcmp(DFRP(p1
)->dfr_fstype
, DFRP(p2
)->dfr_fstype
);
1722 return (DFRP(p1
)->dfr_index
- DFRP(p2
)->dfr_index
);
1727 vfs_error(char *file
, int status
)
1729 if (status
== VFS_TOOLONG
)
1730 errmsg(ERR_NOFLAGS
, "a line in %s exceeds %d characters",
1731 file
, MNT_LINE_MAX
);
1732 else if (status
== VFS_TOOMANY
)
1733 errmsg(ERR_NOFLAGS
, "a line in %s has too many fields", file
);
1734 else if (status
== VFS_TOOFEW
)
1735 errmsg(ERR_NOFLAGS
, "a line in %s has too few fields", file
);
1737 errmsg(ERR_NOFLAGS
, "error while reading %s: %d", file
, status
);
1742 * Try to determine the fstype for the specified block device.
1743 * Return in order of decreasing preference:
1744 * file system type from vfstab
1745 * file system type as specified by -F option
1746 * default file system type
1749 find_fstype(char *special
)
1754 char *vfstab_file
= VFS_TAB
;
1756 fp
= xfopen(vfstab_file
);
1757 status
= getvfsspec(fp
, &vtab
, special
);
1760 vfs_error(vfstab_file
, status
);
1763 if (F_option
&& ! EQ(FSType
, vtab
.vfs_fstype
))
1765 "warning: %s is of type %s", special
, vtab
.vfs_fstype
);
1766 return (new_string(vtab
.vfs_fstype
));
1769 return (F_option
? FSType
: default_fstype(special
));
1773 * When this function returns, the following fields are filled for all
1774 * valid entries in the requests[] array:
1775 * dfr_mte (if the file system is mounted)
1779 * The function returns the number of errors that occurred while building
1783 create_request_list(
1786 struct df_request
*requests_p
[],
1787 size_t *request_count
)
1789 struct df_request
*requests
;
1790 struct df_request
*dfrp
;
1793 size_t request_index
= 0;
1794 size_t max_requests
;
1798 * If no args, use the mounted file systems, otherwise use the
1799 * user-specified arguments.
1803 max_requests
= mount_table_entries
;
1805 max_requests
= argc
;
1807 size
= max_requests
* sizeof (struct df_request
);
1808 requests
= xmalloc(size
);
1809 (void) memset(requests
, 0, size
);
1813 * If -Z wasn't specified, we skip mounts in other
1814 * zones. This obviously is a noop in a non-global
1817 boolean_t showall
= (getzoneid() != GLOBAL_ZONEID
) || Z_option
;
1818 struct zone_summary
*zsp
;
1821 zsp
= fs_get_zone_summaries();
1824 "unable to retrieve list of zones");
1827 for (i
= 0; i
< mount_table_entries
; i
++) {
1828 struct extmnttab
*mtp
= mount_table
[i
].mte_mount
;
1830 if (EQ(mtp
->mnt_fstype
, MNTTYPE_SWAP
))
1834 if (fs_mount_in_other_zone(zsp
,
1838 dfrp
= &requests
[request_index
++];
1839 dfrp
->dfr_mte
= &mount_table
[i
];
1840 dfrp
->dfr_fstype
= mtp
->mnt_fstype
;
1841 dfrp
->dfr_index
= i
;
1842 dfrp
->dfr_valid
= TRUE
;
1845 struct stat64
*arg_stat
; /* array of stat structures */
1846 bool_int
*valid_stat
; /* which structures are valid */
1848 arg_stat
= xmalloc(argc
* sizeof (struct stat64
));
1849 valid_stat
= xmalloc(argc
* sizeof (bool_int
));
1852 * Obtain stat64 information for each argument before
1853 * constructing the list of mounted file systems. By
1854 * touching all these places we force the automounter
1855 * to establish any mounts required to access the arguments,
1856 * so that the corresponding mount table entries will exist
1857 * when we look for them.
1858 * It is still possible that the automounter may timeout
1859 * mounts between the time we read the mount table and the
1860 * time we process the request. Even in that case, when
1861 * we issue the statvfs64(2) for the mount point, the file
1862 * system will be mounted again. The only problem will
1863 * occur if the automounter maps change in the meantime
1864 * and the mount point is eliminated.
1866 for (i
= 0; i
< argc
; i
++)
1867 valid_stat
[i
] = (stat64(argv
[i
], &arg_stat
[i
]) == 0);
1871 for (i
= 0; i
< argc
; i
++) {
1872 char *arg
= argv
[i
];
1874 dfrp
= &requests
[request_index
];
1876 dfrp
->dfr_index
= request_index
;
1877 dfrp
->dfr_cmd_arg
= arg
;
1879 if (valid_stat
[i
]) {
1880 dfrp
->dfr_fstype
= arg_stat
[i
].st_fstype
;
1881 if (S_ISBLK(arg_stat
[i
].st_mode
)) {
1882 bdev_mount_entry(dfrp
);
1883 dfrp
->dfr_valid
= TRUE
;
1884 } else if (S_ISDIR(arg_stat
[i
].st_mode
) ||
1885 S_ISREG(arg_stat
[i
].st_mode
) ||
1886 S_ISFIFO(arg_stat
[i
].st_mode
)) {
1887 path_mount_entry(dfrp
,
1888 arg_stat
[i
].st_dev
);
1889 if (! DFR_ISMOUNTEDFS(dfrp
)) {
1893 dfrp
->dfr_valid
= TRUE
;
1896 resource_mount_entry(dfrp
);
1897 dfrp
->dfr_valid
= DFR_ISMOUNTEDFS(dfrp
);
1901 * If we haven't managed to verify that the request
1902 * is valid, we must have gotten a bad argument.
1904 if (!dfrp
->dfr_valid
) {
1906 "(%-10s) not a block device, directory or "
1907 "mounted resource", arg
);
1913 * Determine the file system type.
1915 if (DFR_ISMOUNTEDFS(dfrp
))
1917 dfrp
->dfr_mte
->mte_mount
->mnt_fstype
;
1920 find_fstype(dfrp
->dfr_cmd_arg
);
1925 *requests_p
= requests
;
1926 *request_count
= request_index
;
1932 * Select the appropriate function and flags to use for output.
1933 * Notice that using both -e and -b options produces a different form of
1934 * output than either of those two options alone; this is the behavior of
1937 static struct df_output
*
1940 static struct df_output dfo
;
1943 * The order of checking options follows the option precedence
1944 * rules as they are listed in the man page.
1946 if (use_scaling
) { /* comes from the -h option */
1947 dfo
.dfo_func
= k_output
;
1948 dfo
.dfo_flags
= DFO_HEADER
+ DFO_STATVFS
;
1949 } else if (V_option
) {
1950 dfo
.dfo_func
= V_output
;
1951 dfo
.dfo_flags
= DFO_NOFLAGS
;
1952 } else if (g_option
) {
1953 dfo
.dfo_func
= g_output
;
1954 dfo
.dfo_flags
= DFO_STATVFS
;
1955 } else if (k_option
|| m_option
|| P_option
|| v_option
) {
1956 dfo
.dfo_func
= k_output
;
1957 dfo
.dfo_flags
= DFO_HEADER
+ DFO_STATVFS
;
1958 } else if (t_option
) {
1959 dfo
.dfo_func
= t_output
;
1960 dfo
.dfo_flags
= DFO_STATVFS
;
1961 } else if (b_option
&& e_option
) {
1962 dfo
.dfo_func
= eb_output
;
1963 dfo
.dfo_flags
= DFO_STATVFS
;
1964 } else if (b_option
) {
1965 dfo
.dfo_func
= b_output
;
1966 dfo
.dfo_flags
= DFO_HEADER
+ DFO_STATVFS
;
1967 } else if (e_option
) {
1968 dfo
.dfo_func
= e_output
;
1969 dfo
.dfo_flags
= DFO_HEADER
+ DFO_STATVFS
;
1970 } else if (n_option
) {
1971 dfo
.dfo_func
= n_output
;
1972 dfo
.dfo_flags
= DFO_NOFLAGS
;
1974 dfo
.dfo_func
= default_output
;
1975 dfo
.dfo_flags
= DFO_STATVFS
;
1982 * The (argc,argv) pair contains all the non-option arguments
1985 do_df(int argc
, char *argv
[])
1988 struct df_request
*requests
; /* array of requests */
1990 struct df_request
*dfrp
;
1993 errors
= create_request_list(argc
, argv
, &requests
, &n_requests
);
1995 if (n_requests
== 0)
1999 * If we are going to run the FSType-specific df command,
2000 * rearrange the requests so that we can issue a single command
2001 * per file system type.
2007 * qsort is not a stable sorting method (i.e. requests of
2008 * the same file system type may be swapped, and hence appear
2009 * in the output in a different order from the one in which
2010 * they were listed in the command line). In order to force
2011 * stability, we use the dfr_index field which is unique
2015 n_requests
, sizeof (struct df_request
), df_reqcomp
);
2016 for (i
= 0; i
< n_requests
; i
= j
) {
2017 char *fstype
= requests
[i
].dfr_fstype
;
2019 for (j
= i
+1; j
< n_requests
; j
++)
2020 if (! EQ(fstype
, requests
[j
].dfr_fstype
))
2024 * At this point, requests in the range [i,j) are
2027 * If the -F option was used, and the user specified
2028 * arguments, the filesystem types must match
2030 * XXX: the alternative of doing this check here is to
2031 * invoke prune_list, but then we have to
2032 * modify this code to ignore invalid requests.
2034 if (F_option
&& ! EQ(fstype
, FSType
)) {
2037 for (k
= i
; k
< j
; k
++) {
2038 dfrp
= &requests
[k
];
2039 if (dfrp
->dfr_cmd_arg
!= NULL
) {
2041 "Warning: %s mounted as a "
2049 errors
+= run_fs_specific_df(&requests
[i
], j
-i
);
2052 size_t valid_requests
;
2055 * We have to prune the request list to avoid printing a header
2056 * if there are no valid requests
2058 errors
+= prune_list(requests
, n_requests
, &valid_requests
);
2060 if (valid_requests
) {
2061 struct df_output
*dfop
= select_output();
2063 /* indicates if we already printed out a header line */
2064 int printed_header
= 0;
2066 for (i
= 0; i
< n_requests
; i
++) {
2067 dfrp
= &requests
[i
];
2068 if (! dfrp
->dfr_valid
)
2072 * If we don't have a mount point,
2073 * this must be a block device.
2075 if (DFR_ISMOUNTEDFS(dfrp
)) {
2076 struct statvfs64 stvfs
;
2078 if ((dfop
->dfo_flags
& DFO_STATVFS
) &&
2079 statvfs64(DFR_MOUNT_POINT(dfrp
),
2082 "cannot statvfs %s:",
2083 DFR_MOUNT_POINT(dfrp
));
2087 if ((!printed_header
) &&
2088 (dfop
->dfo_flags
& DFO_HEADER
)) {
2093 (*dfop
->dfo_func
)(dfrp
, &stvfs
);
2096 * -h option only works for
2097 * mounted filesystems
2101 "-h option incompatible with unmounted special device (%s)",
2106 errors
+= run_fs_specific_df(dfrp
, 1);
2116 * The rest of this file implements the devnm command
2120 find_dev_name(char *file
, dev_t dev
)
2122 struct df_request dfreq
;
2124 dfreq
.dfr_cmd_arg
= file
;
2125 dfreq
.dfr_fstype
= 0;
2126 dfreq
.dfr_mte
= NULL
;
2127 path_mount_entry(&dfreq
, dev
);
2128 return (DFR_ISMOUNTEDFS(&dfreq
) ? DFR_SPECIAL(&dfreq
) : NULL
);
2133 do_devnm(int argc
, char *argv
[])
2140 errmsg(ERR_NONAME
, "Usage: %s name ...", DEVNM_CMD
);
2144 for (arg
= 1; arg
< argc
; arg
++) {
2145 char *file
= argv
[arg
];
2148 if (stat64(file
, &st
) == -1) {
2149 errmsg(ERR_PERROR
, "%s: ", file
);
2154 if (! is_remote_fs(st
.st_fstype
) &&
2155 ! EQ(st
.st_fstype
, MNTTYPE_TMPFS
) &&
2156 (dev_name
= find_dev_name(file
, st
.st_dev
)))
2157 (void) printf("%s %s\n", dev_name
, file
);
2160 "%s not found", file
);