10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
5*1051Smaheshvs * Common Development and Distribution License (the "License").
6*1051Smaheshvs * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
210Sstevel@tonic-gate /*
22*1051Smaheshvs * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
230Sstevel@tonic-gate * Use is subject to license terms.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
270Sstevel@tonic-gate
280Sstevel@tonic-gate #include <stdio.h>
290Sstevel@tonic-gate #include <stdlib.h>
300Sstevel@tonic-gate #include <unistd.h>
310Sstevel@tonic-gate #include <string.h>
320Sstevel@tonic-gate #include <strings.h>
330Sstevel@tonic-gate #include <fcntl.h>
340Sstevel@tonic-gate #include <errno.h>
350Sstevel@tonic-gate #include <sys/types.h>
360Sstevel@tonic-gate #include <sys/stat.h>
370Sstevel@tonic-gate #include <sys/ioctl.h>
380Sstevel@tonic-gate #include <sys/fssnap_if.h>
390Sstevel@tonic-gate #include <sys/filio.h>
400Sstevel@tonic-gate #include <setjmp.h>
410Sstevel@tonic-gate #include <stdarg.h>
420Sstevel@tonic-gate #include <kstat.h>
430Sstevel@tonic-gate #include <libintl.h>
440Sstevel@tonic-gate #include <libdevinfo.h>
450Sstevel@tonic-gate #include <sys/sysmacros.h>
460Sstevel@tonic-gate #include <sys/fs/ufs_fs.h>
470Sstevel@tonic-gate #include <sys/fs/ufs_snap.h>
480Sstevel@tonic-gate
490Sstevel@tonic-gate #define SNAP_CTL_PATH "/dev/" SNAP_CTL_NAME
500Sstevel@tonic-gate
510Sstevel@tonic-gate #define MAX_SUFFIX 6 /* '.' + 4 chars of number + trailing '\0' */
520Sstevel@tonic-gate
530Sstevel@tonic-gate #define POWEROF2(num) (((num) & ((num) - 1)) == 0)
540Sstevel@tonic-gate
550Sstevel@tonic-gate static int max_uniq = 9999;
560Sstevel@tonic-gate
570Sstevel@tonic-gate void create_snap(int, char *, u_offset_t, uint_t, int, int);
580Sstevel@tonic-gate void delete_snap(int);
590Sstevel@tonic-gate void stats_snap(char *, char *);
600Sstevel@tonic-gate
610Sstevel@tonic-gate int open_backpath(int, u_offset_t, char **, char **, int **);
620Sstevel@tonic-gate u_offset_t spec_to_bytes(char *);
630Sstevel@tonic-gate void gen_backing_store_path(char *basepath, int num, char **outpath);
640Sstevel@tonic-gate void unlink_all(char *, int);
650Sstevel@tonic-gate void close_all(char *, int, int *);
660Sstevel@tonic-gate int open_multi_backfile(char *, int, int **, int);
670Sstevel@tonic-gate
680Sstevel@tonic-gate void die_perror(char *);
690Sstevel@tonic-gate void die_errno(int, char *, ...);
700Sstevel@tonic-gate void die_create_error(int error);
710Sstevel@tonic-gate void die_usage(void);
720Sstevel@tonic-gate void die(char *, ...);
730Sstevel@tonic-gate void warn_errno(int, char *, ...);
740Sstevel@tonic-gate void usage(void);
750Sstevel@tonic-gate
760Sstevel@tonic-gate static char *subopts[] = {
770Sstevel@tonic-gate #define BACKPATH (0)
780Sstevel@tonic-gate "backing-store",
790Sstevel@tonic-gate #define BACKPATH2 (1)
800Sstevel@tonic-gate "bs",
810Sstevel@tonic-gate #define BACKPATH3 (2)
820Sstevel@tonic-gate "bf",
830Sstevel@tonic-gate #define MAXSIZE (3)
840Sstevel@tonic-gate "maxsize",
850Sstevel@tonic-gate #define CHUNKSIZE (4)
860Sstevel@tonic-gate "chunksize",
870Sstevel@tonic-gate #define RAWFILE (5)
880Sstevel@tonic-gate "raw",
890Sstevel@tonic-gate #define UNLINK (6)
900Sstevel@tonic-gate "unlink",
910Sstevel@tonic-gate NULL
920Sstevel@tonic-gate };
930Sstevel@tonic-gate
940Sstevel@tonic-gate static jmp_buf err_main;
950Sstevel@tonic-gate static char *progname = NULL;
96*1051Smaheshvs static int backout_snap_fd = -1;
970Sstevel@tonic-gate
980Sstevel@tonic-gate extern void fssnap_show_status(char *mountpoint, char *opts, int labels,
990Sstevel@tonic-gate int brief); /* in ../../fssnapsup.c */
1000Sstevel@tonic-gate
1010Sstevel@tonic-gate int
main(int argc,char * argv[])1020Sstevel@tonic-gate main(int argc, char *argv[])
1030Sstevel@tonic-gate {
1040Sstevel@tonic-gate int c;
1050Sstevel@tonic-gate char *suboptions = NULL;
1060Sstevel@tonic-gate char *value;
1070Sstevel@tonic-gate int longjmp_return;
1080Sstevel@tonic-gate
1090Sstevel@tonic-gate char *mountpoint = NULL;
1100Sstevel@tonic-gate int mountfd = -1;
1110Sstevel@tonic-gate char *backpath = NULL;
1120Sstevel@tonic-gate
1130Sstevel@tonic-gate int delete = 0;
1140Sstevel@tonic-gate int stats = 0;
1150Sstevel@tonic-gate u_offset_t maxsize = 0;
1160Sstevel@tonic-gate uint_t chunksize = 0;
1170Sstevel@tonic-gate int rawfile = 0;
1180Sstevel@tonic-gate int dounlink = 0;
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate if ((progname = strrchr(argv[0], '/')) != NULL)
1210Sstevel@tonic-gate ++progname;
1220Sstevel@tonic-gate else
1230Sstevel@tonic-gate progname = argv[0];
1240Sstevel@tonic-gate
1250Sstevel@tonic-gate if ((longjmp_return = setjmp(err_main)) != 0) {
1260Sstevel@tonic-gate if (backout_snap_fd >= 0) {
1270Sstevel@tonic-gate mountfd = backout_snap_fd;
1280Sstevel@tonic-gate backout_snap_fd = -1; /* prevent infinite loop */
1290Sstevel@tonic-gate delete_snap(mountfd);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate
1320Sstevel@tonic-gate return (longjmp_return);
1330Sstevel@tonic-gate }
1340Sstevel@tonic-gate
1350Sstevel@tonic-gate while ((c = getopt(argc, argv, "dio:")) != EOF) {
1360Sstevel@tonic-gate switch (c) {
1370Sstevel@tonic-gate case 'd':
1380Sstevel@tonic-gate ++delete;
1390Sstevel@tonic-gate break;
1400Sstevel@tonic-gate
1410Sstevel@tonic-gate case 'i':
1420Sstevel@tonic-gate ++stats;
1430Sstevel@tonic-gate break;
1440Sstevel@tonic-gate
1450Sstevel@tonic-gate case 'o':
1460Sstevel@tonic-gate suboptions = optarg;
1470Sstevel@tonic-gate break;
1480Sstevel@tonic-gate
1490Sstevel@tonic-gate default:
1500Sstevel@tonic-gate die_usage();
1510Sstevel@tonic-gate }
1520Sstevel@tonic-gate }
1530Sstevel@tonic-gate
1540Sstevel@tonic-gate /* if -i or -d are not specified then interpret the create options */
1550Sstevel@tonic-gate if ((stats == 0) && (delete == 0) && (suboptions != NULL)) {
1560Sstevel@tonic-gate while (*suboptions != '\0') {
1570Sstevel@tonic-gate
1580Sstevel@tonic-gate switch ((getsubopt(&suboptions, subopts, &value))) {
1590Sstevel@tonic-gate case BACKPATH:
1600Sstevel@tonic-gate case BACKPATH2:
1610Sstevel@tonic-gate case BACKPATH3:
1620Sstevel@tonic-gate if (value == NULL)
1630Sstevel@tonic-gate die_usage();
1640Sstevel@tonic-gate backpath = strdup(value);
1650Sstevel@tonic-gate if (backpath == NULL) {
1660Sstevel@tonic-gate die_perror("strdup");
1670Sstevel@tonic-gate }
1680Sstevel@tonic-gate break;
1690Sstevel@tonic-gate
1700Sstevel@tonic-gate case MAXSIZE:
1710Sstevel@tonic-gate maxsize = spec_to_bytes(value);
1720Sstevel@tonic-gate break;
1730Sstevel@tonic-gate
1740Sstevel@tonic-gate case CHUNKSIZE:
1750Sstevel@tonic-gate chunksize = spec_to_bytes(value);
1760Sstevel@tonic-gate break;
1770Sstevel@tonic-gate
1780Sstevel@tonic-gate case RAWFILE:
1790Sstevel@tonic-gate ++rawfile;
1800Sstevel@tonic-gate break;
1810Sstevel@tonic-gate
1820Sstevel@tonic-gate case UNLINK:
1830Sstevel@tonic-gate ++dounlink;
1840Sstevel@tonic-gate break;
1850Sstevel@tonic-gate
1860Sstevel@tonic-gate default:
1870Sstevel@tonic-gate die_usage();
1880Sstevel@tonic-gate }
1890Sstevel@tonic-gate }
1900Sstevel@tonic-gate }
1910Sstevel@tonic-gate
1920Sstevel@tonic-gate /* -d and -i can not be specified together or more than once each */
1930Sstevel@tonic-gate if ((delete + stats) > 1)
1940Sstevel@tonic-gate die_usage();
1950Sstevel@tonic-gate
1960Sstevel@tonic-gate /* If no mount point is specified then -i is the only valid option. */
1970Sstevel@tonic-gate if ((optind >= argc) && (stats == 0))
1980Sstevel@tonic-gate die_usage();
1990Sstevel@tonic-gate
2000Sstevel@tonic-gate /*
2010Sstevel@tonic-gate * If anything but the mount point or device is specified at the end
2020Sstevel@tonic-gate * it's an error.
2030Sstevel@tonic-gate */
2040Sstevel@tonic-gate if (optind != (argc - 1)) {
2050Sstevel@tonic-gate if (!stats)
2060Sstevel@tonic-gate die_usage();
2070Sstevel@tonic-gate } else {
2080Sstevel@tonic-gate /* Otherwise, the last option is the mountpoint. */
2090Sstevel@tonic-gate mountpoint = argv[optind];
2100Sstevel@tonic-gate if ((mountfd = open(mountpoint, O_RDONLY)) < 0)
2110Sstevel@tonic-gate die_perror(mountpoint);
2120Sstevel@tonic-gate }
2130Sstevel@tonic-gate
2140Sstevel@tonic-gate if (stats != 0) {
2150Sstevel@tonic-gate stats_snap(mountpoint, suboptions);
2160Sstevel@tonic-gate } else if (delete != 0) {
2170Sstevel@tonic-gate delete_snap(mountfd);
2180Sstevel@tonic-gate } else {
2190Sstevel@tonic-gate /*
2200Sstevel@tonic-gate * backpath may be invalid upon return of create_snap call.
2210Sstevel@tonic-gate */
2220Sstevel@tonic-gate create_snap(mountfd, backpath, maxsize, chunksize,
2230Sstevel@tonic-gate rawfile, dounlink);
2240Sstevel@tonic-gate }
2250Sstevel@tonic-gate
2260Sstevel@tonic-gate return (0);
2270Sstevel@tonic-gate }
2280Sstevel@tonic-gate
2290Sstevel@tonic-gate void
create_snap(int mountfd,char * backpath,u_offset_t maxsize,uint_t chunksize,int rawfile,int dounlink)2300Sstevel@tonic-gate create_snap(int mountfd, char *backpath, u_offset_t maxsize, uint_t chunksize,
2310Sstevel@tonic-gate int rawfile, int dounlink)
2320Sstevel@tonic-gate {
2330Sstevel@tonic-gate struct fiosnapcreate_multi *enable;
2340Sstevel@tonic-gate int backcount;
2350Sstevel@tonic-gate int ctlfd;
2360Sstevel@tonic-gate char *unlinkpath = NULL;
2370Sstevel@tonic-gate di_devlink_handle_t hdl;
2380Sstevel@tonic-gate int *fd_array;
2390Sstevel@tonic-gate u_offset_t max_bf_size;
2400Sstevel@tonic-gate int save_errno;
2410Sstevel@tonic-gate
2420Sstevel@tonic-gate /*
2430Sstevel@tonic-gate * If chunksize is not a power of 2, the maximum size of a
2440Sstevel@tonic-gate * backing store file might not be UFS_MAX_SNAPBACKFILESIZE,
2450Sstevel@tonic-gate * since the size of the backing store files must be an
2460Sstevel@tonic-gate * integral number of chunks (except for the last one). So
2470Sstevel@tonic-gate * calculate the actual maximum backing store file size.
2480Sstevel@tonic-gate * (It would be nice if we could assume that the chunksize
2490Sstevel@tonic-gate * was a power of 2, but we can't.)
2500Sstevel@tonic-gate */
2510Sstevel@tonic-gate
2520Sstevel@tonic-gate if (chunksize != 0 && !POWEROF2(chunksize))
2530Sstevel@tonic-gate max_bf_size = (UFS_MAX_SNAPBACKFILESIZE/chunksize) * chunksize;
2540Sstevel@tonic-gate else
2550Sstevel@tonic-gate max_bf_size = UFS_MAX_SNAPBACKFILESIZE;
2560Sstevel@tonic-gate
2570Sstevel@tonic-gate /*
2580Sstevel@tonic-gate * open_backpath() only returns on success, and
2590Sstevel@tonic-gate * can change the value of backpath when backpath
2600Sstevel@tonic-gate * references a directory.
2610Sstevel@tonic-gate */
2620Sstevel@tonic-gate if (backpath == NULL)
2630Sstevel@tonic-gate die(gettext("No backing store path specified.\n"));
2640Sstevel@tonic-gate backcount = open_backpath(mountfd, max_bf_size, &backpath,
2650Sstevel@tonic-gate &unlinkpath, &fd_array);
2660Sstevel@tonic-gate
2670Sstevel@tonic-gate /*
2680Sstevel@tonic-gate * Only need backcount - 1 spaces for fd's since
2690Sstevel@tonic-gate * fiosnapcreate_multi struct contains space for the
2700Sstevel@tonic-gate * first one.
2710Sstevel@tonic-gate */
2720Sstevel@tonic-gate if ((enable = calloc(1, sizeof (struct fiosnapcreate_multi) +
2730Sstevel@tonic-gate (backcount - 1) * sizeof (int))) == NULL)
2740Sstevel@tonic-gate die(gettext("Insufficient memory.\n"));
2750Sstevel@tonic-gate
2760Sstevel@tonic-gate enable->backfilecount = backcount;
2770Sstevel@tonic-gate bcopy(fd_array, &(enable->backfiledesc), backcount * sizeof (int));
2780Sstevel@tonic-gate
2790Sstevel@tonic-gate enable->rootfiledesc = mountfd;
2800Sstevel@tonic-gate
2810Sstevel@tonic-gate enable->maxsize = maxsize;
2820Sstevel@tonic-gate enable->chunksize = chunksize;
2830Sstevel@tonic-gate enable->backfilesize = max_bf_size;
2840Sstevel@tonic-gate
2850Sstevel@tonic-gate /*
2860Sstevel@tonic-gate * enable.backfilename is advisory only. So, we don't overflow
2870Sstevel@tonic-gate * the buffer, but we don't give an error if the backpath does not
2880Sstevel@tonic-gate * fit. Instead, it is truncated, and the kstat shows all it can.
2890Sstevel@tonic-gate */
2900Sstevel@tonic-gate if (backpath != NULL) {
2910Sstevel@tonic-gate if (dounlink)
2920Sstevel@tonic-gate (void) snprintf(enable->backfilename,
2930Sstevel@tonic-gate sizeof (enable->backfilename) - 1, "%s <UNLINKED>",
2940Sstevel@tonic-gate backpath);
2950Sstevel@tonic-gate else
2960Sstevel@tonic-gate (void) strncpy(enable->backfilename, backpath,
2970Sstevel@tonic-gate sizeof (enable->backfilename) - 1);
2980Sstevel@tonic-gate enable->backfilename[sizeof (enable->backfilename)-1] = '\0';
2990Sstevel@tonic-gate }
3000Sstevel@tonic-gate
3010Sstevel@tonic-gate if ((ctlfd = open(SNAP_CTL_PATH, O_RDONLY | O_EXCL)) == -1) {
3020Sstevel@tonic-gate unlink_all(unlinkpath, backcount);
3030Sstevel@tonic-gate die_perror(SNAP_CTL_PATH);
3040Sstevel@tonic-gate }
3050Sstevel@tonic-gate
3060Sstevel@tonic-gate if (ioctl(ctlfd, _FIOSNAPSHOTCREATE_MULTI, enable) == -1) {
3070Sstevel@tonic-gate unlink_all(unlinkpath, backcount);
3080Sstevel@tonic-gate if (enable->error != 0) {
3090Sstevel@tonic-gate die_create_error(enable->error);
3100Sstevel@tonic-gate } else {
3110Sstevel@tonic-gate die_perror("ioctl");
3120Sstevel@tonic-gate }
3130Sstevel@tonic-gate }
3140Sstevel@tonic-gate
3150Sstevel@tonic-gate backout_snap_fd = mountfd;
3160Sstevel@tonic-gate if (dounlink != 0)
3170Sstevel@tonic-gate unlink_all(unlinkpath, backcount);
3180Sstevel@tonic-gate
3190Sstevel@tonic-gate if (close(ctlfd) != 0) {
3200Sstevel@tonic-gate save_errno = errno;
3210Sstevel@tonic-gate die_errno(save_errno, gettext("close of control file (%s)"),
3220Sstevel@tonic-gate SNAP_CTL_PATH);
3230Sstevel@tonic-gate }
3240Sstevel@tonic-gate
3250Sstevel@tonic-gate close_all(unlinkpath, backcount, fd_array);
3260Sstevel@tonic-gate
3270Sstevel@tonic-gate if ((hdl = di_devlink_init("fssnap", DI_MAKE_LINK)) == NULL) {
3280Sstevel@tonic-gate save_errno = errno;
3290Sstevel@tonic-gate warn_errno(save_errno,
3300Sstevel@tonic-gate gettext("/dev/%s/%d may not be immediately available\n"),
3310Sstevel@tonic-gate (rawfile) ? SNAP_CHAR_NAME : SNAP_BLOCK_NAME,
3320Sstevel@tonic-gate enable->snapshotnumber);
3330Sstevel@tonic-gate } else {
3340Sstevel@tonic-gate (void) di_devlink_fini(&hdl);
3350Sstevel@tonic-gate }
3360Sstevel@tonic-gate
3370Sstevel@tonic-gate /* intentionally not internationalized */
3380Sstevel@tonic-gate printf("/dev/%s/%d\n", (rawfile) ? SNAP_CHAR_NAME : SNAP_BLOCK_NAME,
3390Sstevel@tonic-gate enable->snapshotnumber);
3400Sstevel@tonic-gate
3410Sstevel@tonic-gate free(enable);
3420Sstevel@tonic-gate }
3430Sstevel@tonic-gate
3440Sstevel@tonic-gate void
delete_snap(int mountfd)3450Sstevel@tonic-gate delete_snap(int mountfd)
3460Sstevel@tonic-gate {
3470Sstevel@tonic-gate struct fiosnapdelete disable;
3480Sstevel@tonic-gate int ctlfd;
3490Sstevel@tonic-gate int save_errno;
3500Sstevel@tonic-gate
3510Sstevel@tonic-gate bzero(&disable, sizeof (disable));
3520Sstevel@tonic-gate if ((ctlfd = open(SNAP_CTL_PATH, O_RDONLY | O_EXCL)) == -1)
3530Sstevel@tonic-gate die_perror(SNAP_CTL_PATH);
3540Sstevel@tonic-gate
3550Sstevel@tonic-gate disable.rootfiledesc = mountfd;
3560Sstevel@tonic-gate if (ioctl(ctlfd, _FIOSNAPSHOTDELETE, &disable) == -1) {
3570Sstevel@tonic-gate if (disable.error) {
3580Sstevel@tonic-gate die(gettext("error %d"), disable.error);
3590Sstevel@tonic-gate } else {
3600Sstevel@tonic-gate die_perror("ioctl");
3610Sstevel@tonic-gate }
3620Sstevel@tonic-gate }
3630Sstevel@tonic-gate
3640Sstevel@tonic-gate if (close(ctlfd) != 0) {
3650Sstevel@tonic-gate save_errno = errno;
3660Sstevel@tonic-gate die_errno(save_errno, gettext("close of control file (%s)"),
3670Sstevel@tonic-gate SNAP_CTL_PATH);
3680Sstevel@tonic-gate }
3690Sstevel@tonic-gate
3700Sstevel@tonic-gate printf(gettext("Deleted snapshot %d.\n"), disable.snapshotnumber);
3710Sstevel@tonic-gate }
3720Sstevel@tonic-gate
3730Sstevel@tonic-gate void
stats_snap(char * mountpath,char * opts)3740Sstevel@tonic-gate stats_snap(char *mountpath, char *opts)
3750Sstevel@tonic-gate {
3760Sstevel@tonic-gate fssnap_show_status(mountpath, opts, ((opts != NULL) ? 0 : 1), 0);
3770Sstevel@tonic-gate }
3780Sstevel@tonic-gate
3790Sstevel@tonic-gate /*
3800Sstevel@tonic-gate * Open as many backing files as necessary for this snapshot.
3810Sstevel@tonic-gate * There will be one backing file for each max_bf_size
3820Sstevel@tonic-gate * number of bytes in the file system being snapped.
3830Sstevel@tonic-gate * The array of file descriptors for the backing files is returned in
3840Sstevel@tonic-gate * fd_array. The number of backing files is the return value of the
3850Sstevel@tonic-gate * function. The name of the first backing file is returned in
3860Sstevel@tonic-gate * unlinkpath. The subsequent backing files are assumed to have the
3870Sstevel@tonic-gate * same name as the first, but with suffixes, .2, .3, etc.
3880Sstevel@tonic-gate */
3890Sstevel@tonic-gate int
open_backpath(int mountfd,u_offset_t max_bf_size,char ** path,char ** unlinkpath,int ** fd_array)3900Sstevel@tonic-gate open_backpath(int mountfd, u_offset_t max_bf_size, char **path,
3910Sstevel@tonic-gate char **unlinkpath, int **fd_array)
3920Sstevel@tonic-gate {
3930Sstevel@tonic-gate struct stat st;
3940Sstevel@tonic-gate struct statvfs vfs;
3950Sstevel@tonic-gate int fd, uniq, len;
3960Sstevel@tonic-gate int ret_errno, i, num_back_files;
3970Sstevel@tonic-gate offset_t fssize, backfilesize;
3980Sstevel@tonic-gate char *locpath = NULL;
3990Sstevel@tonic-gate int save_errno;
4000Sstevel@tonic-gate
4010Sstevel@tonic-gate *unlinkpath = NULL;
4020Sstevel@tonic-gate
4030Sstevel@tonic-gate /* determine size of the file system to be snapped */
4040Sstevel@tonic-gate if (fstatvfs(mountfd, &vfs) == -1)
4050Sstevel@tonic-gate die_perror("statvfs");
4060Sstevel@tonic-gate
4070Sstevel@tonic-gate fssize = vfs.f_blocks * vfs.f_frsize;
4080Sstevel@tonic-gate num_back_files = howmany(fssize, max_bf_size);
4090Sstevel@tonic-gate
4100Sstevel@tonic-gate if (stat(*path, &st) < 0) {
4110Sstevel@tonic-gate /*
4120Sstevel@tonic-gate * Since we set the file_exists_is_fatal argument to 1,
4130Sstevel@tonic-gate * if we return at all, it will be with all the backing
4140Sstevel@tonic-gate * files successfully created and opened.
4150Sstevel@tonic-gate */
4160Sstevel@tonic-gate (void) open_multi_backfile(*path, num_back_files, fd_array, 1);
4170Sstevel@tonic-gate *unlinkpath = strdup(*path);
4180Sstevel@tonic-gate if (unlinkpath == NULL)
4190Sstevel@tonic-gate die_perror("strdup");
4200Sstevel@tonic-gate } else if (S_ISDIR(st.st_mode)) {
4210Sstevel@tonic-gate char temppath[MAXPATHLEN];
4220Sstevel@tonic-gate
4230Sstevel@tonic-gate /* remove a trailing slash from the name */
4240Sstevel@tonic-gate len = strlen(*path) - 1;
4250Sstevel@tonic-gate if ((*path)[len] == '/')
4260Sstevel@tonic-gate (*path)[len] = '\0';
4270Sstevel@tonic-gate
4280Sstevel@tonic-gate /* find a unique name */
4290Sstevel@tonic-gate for (uniq = 0; uniq <= max_uniq; uniq++) {
4300Sstevel@tonic-gate /* cannot use tempnam, since TMPDIR overrides path */
4310Sstevel@tonic-gate (void) snprintf(temppath, MAXPATHLEN, "%s/snapshot%d",
4320Sstevel@tonic-gate *path, uniq);
4330Sstevel@tonic-gate ret_errno = open_multi_backfile(temppath,
4340Sstevel@tonic-gate num_back_files, fd_array, 0);
4350Sstevel@tonic-gate if (ret_errno == 0)
4360Sstevel@tonic-gate break;
4370Sstevel@tonic-gate }
4380Sstevel@tonic-gate if (uniq > max_uniq) {
4390Sstevel@tonic-gate die(gettext("Could not find unique name in %s"), *path);
4400Sstevel@tonic-gate }
4410Sstevel@tonic-gate *unlinkpath = strdup(temppath);
4420Sstevel@tonic-gate free(*path);
4430Sstevel@tonic-gate *path = *unlinkpath;
4440Sstevel@tonic-gate } else if (S_ISREG(st.st_mode)) {
4450Sstevel@tonic-gate die(gettext("%s already exists."), *path);
4460Sstevel@tonic-gate } else {
4470Sstevel@tonic-gate die(gettext("%s: must be either the name of a file to create "
4480Sstevel@tonic-gate "or a directory."), *path);
4490Sstevel@tonic-gate }
4500Sstevel@tonic-gate
4510Sstevel@tonic-gate /*
4520Sstevel@tonic-gate * write a block to the end to bump up the file size and ensure the
4530Sstevel@tonic-gate * entire range needed can be written to.
4540Sstevel@tonic-gate */
4550Sstevel@tonic-gate for (i = 0; i < num_back_files; i++) {
4560Sstevel@tonic-gate fd = (*fd_array)[i];
4570Sstevel@tonic-gate if (i == num_back_files - 1 && fssize % max_bf_size != 0)
4580Sstevel@tonic-gate backfilesize = fssize % max_bf_size;
4590Sstevel@tonic-gate else
4600Sstevel@tonic-gate backfilesize = max_bf_size;
4610Sstevel@tonic-gate if (llseek(fd, backfilesize - 1, SEEK_SET) == -1) {
4620Sstevel@tonic-gate unlink_all(*unlinkpath, num_back_files);
4630Sstevel@tonic-gate die_perror("llseek");
4640Sstevel@tonic-gate }
4650Sstevel@tonic-gate
4660Sstevel@tonic-gate if (write(fd, "0", 1) == -1) {
4670Sstevel@tonic-gate save_errno = errno;
4680Sstevel@tonic-gate unlink_all(*unlinkpath, num_back_files);
4690Sstevel@tonic-gate if (save_errno == EFBIG)
4700Sstevel@tonic-gate die(gettext("File system %s "
4710Sstevel@tonic-gate "does not support large files.\n"), *path);
4720Sstevel@tonic-gate else
4730Sstevel@tonic-gate die_perror("write");
4740Sstevel@tonic-gate }
4750Sstevel@tonic-gate }
4760Sstevel@tonic-gate return (num_back_files);
4770Sstevel@tonic-gate }
4780Sstevel@tonic-gate
4790Sstevel@tonic-gate u_offset_t
spec_to_bytes(char * spec)4800Sstevel@tonic-gate spec_to_bytes(char *spec)
4810Sstevel@tonic-gate {
4820Sstevel@tonic-gate u_offset_t base;
4830Sstevel@tonic-gate
4840Sstevel@tonic-gate base = strtoull(spec, NULL, 10);
4850Sstevel@tonic-gate if ((base == 0LL) && (spec[0] != '0'))
4860Sstevel@tonic-gate die(gettext("Numeric option value expected"));
4870Sstevel@tonic-gate
4880Sstevel@tonic-gate spec += strspn(spec, "0123456789");
4890Sstevel@tonic-gate
4900Sstevel@tonic-gate if ((spec == NULL) || strlen(spec) != 1)
4910Sstevel@tonic-gate die(gettext("Only one of b, k, m, or g may be used"));
4920Sstevel@tonic-gate
4930Sstevel@tonic-gate switch (spec[0]) {
4940Sstevel@tonic-gate case 'B':
4950Sstevel@tonic-gate case 'b':
4960Sstevel@tonic-gate base *= 512;
4970Sstevel@tonic-gate break;
4980Sstevel@tonic-gate case 'K':
4990Sstevel@tonic-gate case 'k':
5000Sstevel@tonic-gate base *= 1024;
5010Sstevel@tonic-gate break;
5020Sstevel@tonic-gate case 'M':
5030Sstevel@tonic-gate case 'm':
5040Sstevel@tonic-gate base *= 1024 * 1024;
5050Sstevel@tonic-gate break;
5060Sstevel@tonic-gate case 'G':
5070Sstevel@tonic-gate case 'g':
5080Sstevel@tonic-gate base *= 1024 * 1024 * 1024;
5090Sstevel@tonic-gate break;
5100Sstevel@tonic-gate default:
5110Sstevel@tonic-gate die(gettext("Must specify one of b, k, m, or g on size"));
5120Sstevel@tonic-gate }
5130Sstevel@tonic-gate
5140Sstevel@tonic-gate return (base);
5150Sstevel@tonic-gate }
5160Sstevel@tonic-gate
5170Sstevel@tonic-gate /*
5180Sstevel@tonic-gate * Make sure that the first call to gen_backing_store() in a loop
5190Sstevel@tonic-gate * starts with a null pointer in the outpath argument
5200Sstevel@tonic-gate * and continues to pass in that same argument until
5210Sstevel@tonic-gate * the loop is complete, at which point the string
5220Sstevel@tonic-gate * pointed to by that argument must be freed by the caller.
5230Sstevel@tonic-gate */
5240Sstevel@tonic-gate void
gen_backing_store_path(char * basepath,int num,char ** outpath)5250Sstevel@tonic-gate gen_backing_store_path(char *basepath, int num, char **outpath)
5260Sstevel@tonic-gate {
5270Sstevel@tonic-gate if (*outpath == NULL) {
5280Sstevel@tonic-gate *outpath = malloc(strlen(basepath) + MAX_SUFFIX);
5290Sstevel@tonic-gate if (*outpath == NULL)
5300Sstevel@tonic-gate die_perror("malloc");
5310Sstevel@tonic-gate }
5320Sstevel@tonic-gate
5330Sstevel@tonic-gate /*
5340Sstevel@tonic-gate * Security note: We use strcpy here, instead of the safer
5350Sstevel@tonic-gate * strncpy, because the string pointed to by outpath has
5360Sstevel@tonic-gate * been generated by THIS code, above. Hence it is impossible
5370Sstevel@tonic-gate * for the strcpy to overrun the buffer.
5380Sstevel@tonic-gate */
5390Sstevel@tonic-gate if (num == 1)
5400Sstevel@tonic-gate (void) strcpy(*outpath, basepath);
5410Sstevel@tonic-gate else
5420Sstevel@tonic-gate (void) sprintf(*outpath, "%s.%d", basepath, num);
5430Sstevel@tonic-gate }
5440Sstevel@tonic-gate
5450Sstevel@tonic-gate void
unlink_all(char * unlinkpath,int count)5460Sstevel@tonic-gate unlink_all(char *unlinkpath, int count)
5470Sstevel@tonic-gate {
5480Sstevel@tonic-gate char *bspath = NULL;
5490Sstevel@tonic-gate int i;
5500Sstevel@tonic-gate int save_errno;
5510Sstevel@tonic-gate
5520Sstevel@tonic-gate for (i = 1; i <= count; i++) {
5530Sstevel@tonic-gate /*
5540Sstevel@tonic-gate * Make sure that the first call to gen_backing_store()
5550Sstevel@tonic-gate * starts with a null pointer in the third argument
5560Sstevel@tonic-gate * and continues to pass in that same argument until
5570Sstevel@tonic-gate * the loop is complete, at which point the string
5580Sstevel@tonic-gate * pointed to by that argument must be freed.
5590Sstevel@tonic-gate */
5600Sstevel@tonic-gate gen_backing_store_path(unlinkpath, i, &bspath);
5610Sstevel@tonic-gate if (unlink(bspath) < 0) {
5620Sstevel@tonic-gate save_errno = errno;
5630Sstevel@tonic-gate warn_errno(save_errno,
5640Sstevel@tonic-gate gettext("could not unlink %s"), bspath);
5650Sstevel@tonic-gate }
5660Sstevel@tonic-gate }
5670Sstevel@tonic-gate free(bspath);
5680Sstevel@tonic-gate }
5690Sstevel@tonic-gate
5700Sstevel@tonic-gate void
close_all(char * closepath,int count,int * fd_array)5710Sstevel@tonic-gate close_all(char *closepath, int count, int *fd_array)
5720Sstevel@tonic-gate {
5730Sstevel@tonic-gate char *bspath = NULL;
5740Sstevel@tonic-gate int i;
5750Sstevel@tonic-gate int save_errno;
5760Sstevel@tonic-gate
5770Sstevel@tonic-gate for (i = 1; i <= count; i++) {
5780Sstevel@tonic-gate if (close(fd_array[i - 1]) != 0) {
5790Sstevel@tonic-gate save_errno = errno;
5800Sstevel@tonic-gate /*
5810Sstevel@tonic-gate * Make sure that the first call to gen_backing_store()
5820Sstevel@tonic-gate * starts with a null pointer in the third argument
5830Sstevel@tonic-gate * and continues to pass in that same argument until
5840Sstevel@tonic-gate * the loop is complete, at which point the string
5850Sstevel@tonic-gate * pointed to by that argument must be freed.
5860Sstevel@tonic-gate */
5870Sstevel@tonic-gate gen_backing_store_path(closepath, i, &bspath);
5880Sstevel@tonic-gate die_errno(save_errno, gettext(
5890Sstevel@tonic-gate "close of backing-store (%s)"), bspath);
5900Sstevel@tonic-gate }
5910Sstevel@tonic-gate }
5920Sstevel@tonic-gate if (bspath != NULL)
5930Sstevel@tonic-gate free(bspath);
5940Sstevel@tonic-gate }
5950Sstevel@tonic-gate
5960Sstevel@tonic-gate /*
5970Sstevel@tonic-gate * Create "count" files starting with name backpath ("backpath",
5980Sstevel@tonic-gate * "backpath".2, "backpath".3, etc. When this function returns,
5990Sstevel@tonic-gate * either all of the files will exist and be opened (and their
6000Sstevel@tonic-gate * file descriptors will be in fd_array), or NONE of will exist
6010Sstevel@tonic-gate * (if they had to be created) and opened (that is, if we created a file,
6020Sstevel@tonic-gate * and then failed to create a later file, the earlier files will
6030Sstevel@tonic-gate * be closed and unlinked.)
6040Sstevel@tonic-gate *
6050Sstevel@tonic-gate * If file_exists_is_fatal is set, it is a fatal error (resulting in
6060Sstevel@tonic-gate * an error message and termination) if any of the backing files to
6070Sstevel@tonic-gate * be created already exists. Otherwise, if one of the backing
6080Sstevel@tonic-gate * files already exists, we close and unlink all the files we already
6090Sstevel@tonic-gate * created, and return an error to the caller, but we don't print
6100Sstevel@tonic-gate * an error or terminate.
6110Sstevel@tonic-gate *
6120Sstevel@tonic-gate * If there is any failure other than EEXIST when attempting to
6130Sstevel@tonic-gate * create the file, the routine prints an error and terminates the
6140Sstevel@tonic-gate * program, regardless of the setting of file_exists_is_fatal.
6150Sstevel@tonic-gate */
6160Sstevel@tonic-gate int
open_multi_backfile(char * backpath,int count,int ** fd_array,int file_exists_is_fatal)6170Sstevel@tonic-gate open_multi_backfile(char *backpath, int count, int **fd_array,
6180Sstevel@tonic-gate int file_exists_is_fatal)
6190Sstevel@tonic-gate {
6200Sstevel@tonic-gate char *wpath = NULL; /* working path */
6210Sstevel@tonic-gate int i, j, fd;
6220Sstevel@tonic-gate struct stat st;
6230Sstevel@tonic-gate int stat_succeeded = 0;
6240Sstevel@tonic-gate int save_errno;
6250Sstevel@tonic-gate
6260Sstevel@tonic-gate *fd_array = (int *)malloc(count * sizeof (int));
6270Sstevel@tonic-gate if (*fd_array == NULL)
6280Sstevel@tonic-gate die_perror("malloc");
6290Sstevel@tonic-gate
6300Sstevel@tonic-gate for (i = 0; i < count; i++) {
6310Sstevel@tonic-gate /*
6320Sstevel@tonic-gate * Make sure that the first call to gen_backing_store()
6330Sstevel@tonic-gate * starts with a null pointer in the third argument
6340Sstevel@tonic-gate * and continues to pass in that same argument until
6350Sstevel@tonic-gate * the loop is complete, at which point the string
6360Sstevel@tonic-gate * pointed to by that argument must be freed.
6370Sstevel@tonic-gate */
6380Sstevel@tonic-gate gen_backing_store_path(backpath, i + 1, &wpath);
6390Sstevel@tonic-gate if (stat(wpath, &st) == 0)
6400Sstevel@tonic-gate stat_succeeded = 1;
6410Sstevel@tonic-gate else
6420Sstevel@tonic-gate fd = open(wpath, O_RDWR | O_CREAT | O_EXCL, 0600);
6430Sstevel@tonic-gate if (stat_succeeded || fd < 0) {
6440Sstevel@tonic-gate if (i > 0) {
6450Sstevel@tonic-gate for (j = 0; j < i - 1; j++)
6460Sstevel@tonic-gate (void) close((*fd_array)[j]);
6470Sstevel@tonic-gate /*
6480Sstevel@tonic-gate * unlink_all's second argument is the number
6490Sstevel@tonic-gate * of files to be removed, NOT the offset
6500Sstevel@tonic-gate * into the array of fd's of the last
6510Sstevel@tonic-gate * successfully created file.
6520Sstevel@tonic-gate */
6530Sstevel@tonic-gate unlink_all(backpath, i);
6540Sstevel@tonic-gate }
6550Sstevel@tonic-gate if (stat_succeeded || errno == EEXIST) {
6560Sstevel@tonic-gate if (file_exists_is_fatal)
6570Sstevel@tonic-gate die(gettext("%s exists, please specify"
6580Sstevel@tonic-gate " a nonexistent backing store."),
6590Sstevel@tonic-gate wpath);
6600Sstevel@tonic-gate else
6610Sstevel@tonic-gate return (1);
6620Sstevel@tonic-gate } else {
6630Sstevel@tonic-gate save_errno = errno;
6640Sstevel@tonic-gate die_errno(save_errno,
6650Sstevel@tonic-gate gettext("Could not create"
6660Sstevel@tonic-gate " backing file %s"), wpath);
6670Sstevel@tonic-gate }
6680Sstevel@tonic-gate }
6690Sstevel@tonic-gate (*fd_array)[i] = fd;
6700Sstevel@tonic-gate }
6710Sstevel@tonic-gate if (wpath != NULL)
6720Sstevel@tonic-gate free(wpath);
6730Sstevel@tonic-gate return (0);
6740Sstevel@tonic-gate }
6750Sstevel@tonic-gate
6760Sstevel@tonic-gate void
die_perror(char * string)6770Sstevel@tonic-gate die_perror(char *string)
6780Sstevel@tonic-gate {
6790Sstevel@tonic-gate int en = errno;
6800Sstevel@tonic-gate char *errstr;
6810Sstevel@tonic-gate
6820Sstevel@tonic-gate if (string == NULL) {
6830Sstevel@tonic-gate string = gettext("Fatal");
6840Sstevel@tonic-gate }
6850Sstevel@tonic-gate errstr = strerror(en);
6860Sstevel@tonic-gate if (errstr == NULL) {
6870Sstevel@tonic-gate errstr = gettext("Unknown error");
6880Sstevel@tonic-gate }
6890Sstevel@tonic-gate
6900Sstevel@tonic-gate fprintf(stderr, gettext("%s: %s: error %d: %s\n"),
6910Sstevel@tonic-gate progname, string, en, errstr);
6920Sstevel@tonic-gate
6930Sstevel@tonic-gate longjmp(err_main, 2);
6940Sstevel@tonic-gate }
6950Sstevel@tonic-gate
6960Sstevel@tonic-gate void
die_usage(void)6970Sstevel@tonic-gate die_usage(void)
6980Sstevel@tonic-gate {
6990Sstevel@tonic-gate usage();
7000Sstevel@tonic-gate
7010Sstevel@tonic-gate longjmp(err_main, 1);
7020Sstevel@tonic-gate }
7030Sstevel@tonic-gate
7040Sstevel@tonic-gate void
warn_errno(int en,char * fmt,...)7050Sstevel@tonic-gate warn_errno(int en, char *fmt, ...)
7060Sstevel@tonic-gate {
7070Sstevel@tonic-gate va_list ap;
7080Sstevel@tonic-gate char *errstr;
7090Sstevel@tonic-gate
7100Sstevel@tonic-gate errstr = strerror(en);
7110Sstevel@tonic-gate if (errstr == NULL) {
7120Sstevel@tonic-gate errstr = gettext("Unknown error");
7130Sstevel@tonic-gate }
7140Sstevel@tonic-gate
7150Sstevel@tonic-gate va_start(ap, fmt);
7160Sstevel@tonic-gate fprintf(stderr, gettext("%s: Warning: "), progname);
7170Sstevel@tonic-gate vfprintf(stderr, fmt, ap);
7180Sstevel@tonic-gate fprintf(stderr, ": %s\n", errstr);
7190Sstevel@tonic-gate va_end(ap);
7200Sstevel@tonic-gate }
7210Sstevel@tonic-gate
7220Sstevel@tonic-gate void
die_errno(int en,char * fmt,...)7230Sstevel@tonic-gate die_errno(int en, char *fmt, ...)
7240Sstevel@tonic-gate {
7250Sstevel@tonic-gate va_list ap;
7260Sstevel@tonic-gate char *errstr;
7270Sstevel@tonic-gate
7280Sstevel@tonic-gate errstr = strerror(en);
7290Sstevel@tonic-gate if (errstr == NULL) {
7300Sstevel@tonic-gate errstr = gettext("Unknown error");
7310Sstevel@tonic-gate }
7320Sstevel@tonic-gate
7330Sstevel@tonic-gate va_start(ap, fmt);
7340Sstevel@tonic-gate fprintf(stderr, gettext("%s: Fatal: "), progname);
7350Sstevel@tonic-gate vfprintf(stderr, fmt, ap);
7360Sstevel@tonic-gate fprintf(stderr, ": %s\n", errstr);
7370Sstevel@tonic-gate va_end(ap);
7380Sstevel@tonic-gate
7390Sstevel@tonic-gate longjmp(err_main, 2);
7400Sstevel@tonic-gate }
7410Sstevel@tonic-gate
7420Sstevel@tonic-gate void
die_create_error(int error)7430Sstevel@tonic-gate die_create_error(int error)
7440Sstevel@tonic-gate {
7450Sstevel@tonic-gate fprintf(stderr, gettext("snapshot error: "));
7460Sstevel@tonic-gate switch (error) {
7470Sstevel@tonic-gate case FIOCOW_EREADONLY:
7480Sstevel@tonic-gate fprintf(stderr, gettext("Read only file system\n"));
7490Sstevel@tonic-gate break;
7500Sstevel@tonic-gate case FIOCOW_EBUSY:
7510Sstevel@tonic-gate fprintf(stderr, gettext("Snapshot already enabled\n"));
7520Sstevel@tonic-gate break;
7530Sstevel@tonic-gate case FIOCOW_EULOCK:
7540Sstevel@tonic-gate fprintf(stderr, gettext("File system is locked\n"));
7550Sstevel@tonic-gate break;
7560Sstevel@tonic-gate case FIOCOW_EWLOCK:
7570Sstevel@tonic-gate fprintf(stderr,
7580Sstevel@tonic-gate gettext("File system could not be write locked\n"));
7590Sstevel@tonic-gate break;
7600Sstevel@tonic-gate case FIOCOW_EFLUSH:
7610Sstevel@tonic-gate fprintf(stderr, gettext("File system could not be flushed\n"));
7620Sstevel@tonic-gate break;
7630Sstevel@tonic-gate case FIOCOW_ECLEAN:
7640Sstevel@tonic-gate fprintf(stderr, gettext("File system may not be stable\n"));
7650Sstevel@tonic-gate break;
7660Sstevel@tonic-gate case FIOCOW_ENOULOCK:
7670Sstevel@tonic-gate fprintf(stderr, gettext("File system could not be unlocked\n"));
7680Sstevel@tonic-gate break;
7690Sstevel@tonic-gate case FIOCOW_ECHUNKSZ:
7700Sstevel@tonic-gate fprintf(stderr, gettext("Chunk size must be a multiple of the "
7710Sstevel@tonic-gate "fragment size\n"));
7720Sstevel@tonic-gate break;
7730Sstevel@tonic-gate case FIOCOW_ECREATE:
7740Sstevel@tonic-gate fprintf(stderr, gettext("Could not allocate or create "
7750Sstevel@tonic-gate "a new snapshot\n"));
7760Sstevel@tonic-gate break;
7770Sstevel@tonic-gate case FIOCOW_EBITMAP:
7780Sstevel@tonic-gate fprintf(stderr,
7790Sstevel@tonic-gate gettext("Error scanning file system bitmaps\n"));
7800Sstevel@tonic-gate break;
7810Sstevel@tonic-gate case FIOCOW_EBACKFILE:
7820Sstevel@tonic-gate fprintf(stderr, gettext("Invalid backing file path\n"));
7830Sstevel@tonic-gate break;
7840Sstevel@tonic-gate default:
7850Sstevel@tonic-gate fprintf(stderr, gettext("Unknown create error\n"));
7860Sstevel@tonic-gate break;
7870Sstevel@tonic-gate }
7880Sstevel@tonic-gate
7890Sstevel@tonic-gate longjmp(err_main, 2);
7900Sstevel@tonic-gate }
7910Sstevel@tonic-gate
7920Sstevel@tonic-gate void
die(char * fmt,...)7930Sstevel@tonic-gate die(char *fmt, ...)
7940Sstevel@tonic-gate {
7950Sstevel@tonic-gate va_list ap;
7960Sstevel@tonic-gate
7970Sstevel@tonic-gate va_start(ap, fmt);
7980Sstevel@tonic-gate fprintf(stderr, gettext("%s: Fatal: "), progname);
7990Sstevel@tonic-gate vfprintf(stderr, fmt, ap);
8000Sstevel@tonic-gate fprintf(stderr, "\n");
8010Sstevel@tonic-gate va_end(ap);
8020Sstevel@tonic-gate
8030Sstevel@tonic-gate longjmp(err_main, 2);
8040Sstevel@tonic-gate }
8050Sstevel@tonic-gate
8060Sstevel@tonic-gate void
usage(void)8070Sstevel@tonic-gate usage(void)
8080Sstevel@tonic-gate {
8090Sstevel@tonic-gate int i;
8100Sstevel@tonic-gate char *use_str[] = {
8110Sstevel@tonic-gate " %s [-F ufs] [-V] -o backing-store=path,[special_options] "
8120Sstevel@tonic-gate "/mount/point\n",
8130Sstevel@tonic-gate " %s -d [-F ufs] [-V] /mount/point | dev\n",
8140Sstevel@tonic-gate " %s -i [-F ufS] [-V] [-o special-options] /mount/point "
8150Sstevel@tonic-gate "| dev\n",
8160Sstevel@tonic-gate NULL
8170Sstevel@tonic-gate };
8180Sstevel@tonic-gate fprintf(stderr, gettext("Usage:\n"));
8190Sstevel@tonic-gate for (i = 0; use_str[i] != NULL; i++)
8200Sstevel@tonic-gate fprintf(stderr, gettext(use_str[i]), progname);
8210Sstevel@tonic-gate }
822