1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30 #include <strings.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <sys/ioctl.h>
36 #include <sys/fssnap_if.h>
37 #include <sys/filio.h>
38 #include <setjmp.h>
39 #include <stdarg.h>
40 #include <kstat.h>
41 #include <libintl.h>
42 #include <libdevinfo.h>
43 #include <sys/sysmacros.h>
44 #include <sys/fs/ufs_fs.h>
45 #include <sys/fs/ufs_snap.h>
46
47 #define SNAP_CTL_PATH "/dev/" SNAP_CTL_NAME
48
49 #define MAX_SUFFIX 6 /* '.' + 4 chars of number + trailing '\0' */
50
51 #define POWEROF2(num) (((num) & ((num) - 1)) == 0)
52
53 static int max_uniq = 9999;
54
55 void create_snap(int, char *, u_offset_t, uint_t, int, int);
56 void delete_snap(int);
57 void stats_snap(char *, char *);
58
59 int open_backpath(int, u_offset_t, char **, char **, int **);
60 u_offset_t spec_to_bytes(char *);
61 void gen_backing_store_path(char *basepath, int num, char **outpath);
62 void unlink_all(char *, int);
63 void close_all(char *, int, int *);
64 int open_multi_backfile(char *, int, int **, int);
65
66 void die_perror(char *);
67 void die_errno(int, char *, ...);
68 void die_create_error(int error);
69 void die_usage(void);
70 void die(char *, ...);
71 void warn_errno(int, char *, ...);
72 void usage(void);
73
74 static char *subopts[] = {
75 #define BACKPATH (0)
76 "backing-store",
77 #define BACKPATH2 (1)
78 "bs",
79 #define BACKPATH3 (2)
80 "bf",
81 #define MAXSIZE (3)
82 "maxsize",
83 #define CHUNKSIZE (4)
84 "chunksize",
85 #define RAWFILE (5)
86 "raw",
87 #define UNLINK (6)
88 "unlink",
89 NULL
90 };
91
92 static jmp_buf err_main;
93 static char *progname = NULL;
94 static int backout_snap_fd = -1;
95
96 extern void fssnap_show_status(char *mountpoint, char *opts, int labels,
97 int brief); /* in ../../fssnapsup.c */
98
99 int
main(int argc,char * argv[])100 main(int argc, char *argv[])
101 {
102 int c;
103 char *suboptions = NULL;
104 char *value;
105 int longjmp_return;
106
107 char *volatile mountpoint = NULL;
108 int volatile mountfd = -1;
109 char *volatile backpath = NULL;
110
111 int volatile delete = 0;
112 int volatile stats = 0;
113 u_offset_t volatile maxsize = 0;
114 uint_t volatile chunksize = 0;
115 int volatile rawfile = 0;
116 int volatile dounlink = 0;
117
118 if ((progname = strrchr(argv[0], '/')) != NULL)
119 ++progname;
120 else
121 progname = argv[0];
122
123 if ((longjmp_return = setjmp(err_main)) != 0) {
124 if (backout_snap_fd >= 0) {
125 mountfd = backout_snap_fd;
126 backout_snap_fd = -1; /* prevent infinite loop */
127 delete_snap(mountfd);
128 }
129
130 return (longjmp_return);
131 }
132
133 while ((c = getopt(argc, argv, "dio:")) != EOF) {
134 switch (c) {
135 case 'd':
136 ++delete;
137 break;
138
139 case 'i':
140 ++stats;
141 break;
142
143 case 'o':
144 suboptions = optarg;
145 break;
146
147 default:
148 die_usage();
149 }
150 }
151
152 /* if -i or -d are not specified then interpret the create options */
153 if ((stats == 0) && (delete == 0) && (suboptions != NULL)) {
154 while (*suboptions != '\0') {
155
156 switch ((getsubopt(&suboptions, subopts, &value))) {
157 case BACKPATH:
158 case BACKPATH2:
159 case BACKPATH3:
160 if (value == NULL)
161 die_usage();
162 backpath = strdup(value);
163 if (backpath == NULL) {
164 die_perror("strdup");
165 }
166 break;
167
168 case MAXSIZE:
169 maxsize = spec_to_bytes(value);
170 break;
171
172 case CHUNKSIZE:
173 chunksize = spec_to_bytes(value);
174 break;
175
176 case RAWFILE:
177 ++rawfile;
178 break;
179
180 case UNLINK:
181 ++dounlink;
182 break;
183
184 default:
185 die_usage();
186 }
187 }
188 }
189
190 /* -d and -i can not be specified together or more than once each */
191 if ((delete + stats) > 1)
192 die_usage();
193
194 /* If no mount point is specified then -i is the only valid option. */
195 if ((optind >= argc) && (stats == 0))
196 die_usage();
197
198 /*
199 * If anything but the mount point or device is specified at the end
200 * it's an error.
201 */
202 if (optind != (argc - 1)) {
203 if (!stats)
204 die_usage();
205 } else {
206 /* Otherwise, the last option is the mountpoint. */
207 mountpoint = argv[optind];
208 if ((mountfd = open(mountpoint, O_RDONLY)) < 0)
209 die_perror(mountpoint);
210 }
211
212 if (stats != 0) {
213 stats_snap(mountpoint, suboptions);
214 } else if (delete != 0) {
215 delete_snap(mountfd);
216 } else {
217 /*
218 * backpath may be invalid upon return of create_snap call.
219 */
220 create_snap(mountfd, backpath, maxsize, chunksize,
221 rawfile, dounlink);
222 }
223
224 return (0);
225 }
226
227 void
create_snap(int mountfd,char * backpath,u_offset_t maxsize,uint_t chunksize,int rawfile,int dounlink)228 create_snap(int mountfd, char *backpath, u_offset_t maxsize, uint_t chunksize,
229 int rawfile, int dounlink)
230 {
231 struct fiosnapcreate_multi *enable;
232 int backcount;
233 int ctlfd;
234 char *unlinkpath = NULL;
235 di_devlink_handle_t hdl;
236 int *fd_array;
237 u_offset_t max_bf_size;
238 int save_errno;
239
240 /*
241 * If chunksize is not a power of 2, the maximum size of a
242 * backing store file might not be UFS_MAX_SNAPBACKFILESIZE,
243 * since the size of the backing store files must be an
244 * integral number of chunks (except for the last one). So
245 * calculate the actual maximum backing store file size.
246 * (It would be nice if we could assume that the chunksize
247 * was a power of 2, but we can't.)
248 */
249
250 if (chunksize != 0 && !POWEROF2(chunksize))
251 max_bf_size = (UFS_MAX_SNAPBACKFILESIZE/chunksize) * chunksize;
252 else
253 max_bf_size = UFS_MAX_SNAPBACKFILESIZE;
254
255 /*
256 * open_backpath() only returns on success, and
257 * can change the value of backpath when backpath
258 * references a directory.
259 */
260 if (backpath == NULL)
261 die(gettext("No backing store path specified.\n"));
262 backcount = open_backpath(mountfd, max_bf_size, &backpath,
263 &unlinkpath, &fd_array);
264
265 /*
266 * Only need backcount - 1 spaces for fd's since
267 * fiosnapcreate_multi struct contains space for the
268 * first one.
269 */
270 if ((enable = calloc(1, sizeof (struct fiosnapcreate_multi) +
271 (backcount - 1) * sizeof (int))) == NULL)
272 die(gettext("Insufficient memory.\n"));
273
274 enable->backfilecount = backcount;
275 bcopy(fd_array, &(enable->backfiledesc), backcount * sizeof (int));
276
277 enable->rootfiledesc = mountfd;
278
279 enable->maxsize = maxsize;
280 enable->chunksize = chunksize;
281 enable->backfilesize = max_bf_size;
282
283 /*
284 * enable.backfilename is advisory only. So, we don't overflow
285 * the buffer, but we don't give an error if the backpath does not
286 * fit. Instead, it is truncated, and the kstat shows all it can.
287 */
288 if (backpath != NULL) {
289 if (dounlink)
290 (void) snprintf(enable->backfilename,
291 sizeof (enable->backfilename) - 1, "%s <UNLINKED>",
292 backpath);
293 else
294 (void) strncpy(enable->backfilename, backpath,
295 sizeof (enable->backfilename) - 1);
296 enable->backfilename[sizeof (enable->backfilename)-1] = '\0';
297 }
298
299 if ((ctlfd = open(SNAP_CTL_PATH, O_RDONLY | O_EXCL)) == -1) {
300 unlink_all(unlinkpath, backcount);
301 die_perror(SNAP_CTL_PATH);
302 }
303
304 if (ioctl(ctlfd, _FIOSNAPSHOTCREATE_MULTI, enable) == -1) {
305 unlink_all(unlinkpath, backcount);
306 if (enable->error != 0) {
307 die_create_error(enable->error);
308 } else {
309 die_perror("ioctl");
310 }
311 }
312
313 backout_snap_fd = mountfd;
314 if (dounlink != 0)
315 unlink_all(unlinkpath, backcount);
316
317 if (close(ctlfd) != 0) {
318 save_errno = errno;
319 die_errno(save_errno, gettext("close of control file (%s)"),
320 SNAP_CTL_PATH);
321 }
322
323 close_all(unlinkpath, backcount, fd_array);
324
325 if ((hdl = di_devlink_init("fssnap", DI_MAKE_LINK)) == NULL) {
326 save_errno = errno;
327 warn_errno(save_errno,
328 gettext("/dev/%s/%d may not be immediately available\n"),
329 (rawfile) ? SNAP_CHAR_NAME : SNAP_BLOCK_NAME,
330 enable->snapshotnumber);
331 } else {
332 (void) di_devlink_fini(&hdl);
333 }
334
335 /* intentionally not internationalized */
336 printf("/dev/%s/%d\n", (rawfile) ? SNAP_CHAR_NAME : SNAP_BLOCK_NAME,
337 enable->snapshotnumber);
338
339 free(enable);
340 }
341
342 void
delete_snap(int mountfd)343 delete_snap(int mountfd)
344 {
345 struct fiosnapdelete disable;
346 int ctlfd;
347 int save_errno;
348
349 bzero(&disable, sizeof (disable));
350 if ((ctlfd = open(SNAP_CTL_PATH, O_RDONLY | O_EXCL)) == -1)
351 die_perror(SNAP_CTL_PATH);
352
353 disable.rootfiledesc = mountfd;
354 if (ioctl(ctlfd, _FIOSNAPSHOTDELETE, &disable) == -1) {
355 if (disable.error) {
356 die(gettext("error %d"), disable.error);
357 } else {
358 die_perror("ioctl");
359 }
360 }
361
362 if (close(ctlfd) != 0) {
363 save_errno = errno;
364 die_errno(save_errno, gettext("close of control file (%s)"),
365 SNAP_CTL_PATH);
366 }
367
368 printf(gettext("Deleted snapshot %d.\n"), disable.snapshotnumber);
369 }
370
371 void
stats_snap(char * mountpath,char * opts)372 stats_snap(char *mountpath, char *opts)
373 {
374 fssnap_show_status(mountpath, opts, ((opts != NULL) ? 0 : 1), 0);
375 }
376
377 /*
378 * Open as many backing files as necessary for this snapshot.
379 * There will be one backing file for each max_bf_size
380 * number of bytes in the file system being snapped.
381 * The array of file descriptors for the backing files is returned in
382 * fd_array. The number of backing files is the return value of the
383 * function. The name of the first backing file is returned in
384 * unlinkpath. The subsequent backing files are assumed to have the
385 * same name as the first, but with suffixes, .2, .3, etc.
386 */
387 int
open_backpath(int mountfd,u_offset_t max_bf_size,char ** path,char ** unlinkpath,int ** fd_array)388 open_backpath(int mountfd, u_offset_t max_bf_size, char **path,
389 char **unlinkpath, int **fd_array)
390 {
391 struct stat st;
392 struct statvfs vfs;
393 int fd, uniq, len;
394 int ret_errno, i, num_back_files;
395 offset_t fssize, backfilesize;
396 char *locpath = NULL;
397 int save_errno;
398
399 *unlinkpath = NULL;
400
401 /* determine size of the file system to be snapped */
402 if (fstatvfs(mountfd, &vfs) == -1)
403 die_perror("statvfs");
404
405 fssize = vfs.f_blocks * vfs.f_frsize;
406 num_back_files = howmany(fssize, max_bf_size);
407
408 if (stat(*path, &st) < 0) {
409 /*
410 * Since we set the file_exists_is_fatal argument to 1,
411 * if we return at all, it will be with all the backing
412 * files successfully created and opened.
413 */
414 (void) open_multi_backfile(*path, num_back_files, fd_array, 1);
415 *unlinkpath = strdup(*path);
416 if (unlinkpath == NULL)
417 die_perror("strdup");
418 } else if (S_ISDIR(st.st_mode)) {
419 char temppath[MAXPATHLEN];
420
421 /* remove a trailing slash from the name */
422 len = strlen(*path) - 1;
423 if ((*path)[len] == '/')
424 (*path)[len] = '\0';
425
426 /* find a unique name */
427 for (uniq = 0; uniq <= max_uniq; uniq++) {
428 /* cannot use tempnam, since TMPDIR overrides path */
429 (void) snprintf(temppath, MAXPATHLEN, "%s/snapshot%d",
430 *path, uniq);
431 ret_errno = open_multi_backfile(temppath,
432 num_back_files, fd_array, 0);
433 if (ret_errno == 0)
434 break;
435 }
436 if (uniq > max_uniq) {
437 die(gettext("Could not find unique name in %s"), *path);
438 }
439 *unlinkpath = strdup(temppath);
440 free(*path);
441 *path = *unlinkpath;
442 } else if (S_ISREG(st.st_mode)) {
443 die(gettext("%s already exists."), *path);
444 } else {
445 die(gettext("%s: must be either the name of a file to create "
446 "or a directory."), *path);
447 }
448
449 /*
450 * write a block to the end to bump up the file size and ensure the
451 * entire range needed can be written to.
452 */
453 for (i = 0; i < num_back_files; i++) {
454 fd = (*fd_array)[i];
455 if (i == num_back_files - 1 && fssize % max_bf_size != 0)
456 backfilesize = fssize % max_bf_size;
457 else
458 backfilesize = max_bf_size;
459 if (llseek(fd, backfilesize - 1, SEEK_SET) == -1) {
460 unlink_all(*unlinkpath, num_back_files);
461 die_perror("llseek");
462 }
463
464 if (write(fd, "0", 1) == -1) {
465 save_errno = errno;
466 unlink_all(*unlinkpath, num_back_files);
467 if (save_errno == EFBIG)
468 die(gettext("File system %s "
469 "does not support large files.\n"), *path);
470 else
471 die_perror("write");
472 }
473 }
474 return (num_back_files);
475 }
476
477 u_offset_t
spec_to_bytes(char * spec)478 spec_to_bytes(char *spec)
479 {
480 u_offset_t base;
481
482 base = strtoull(spec, NULL, 10);
483 if ((base == 0LL) && (spec[0] != '0'))
484 die(gettext("Numeric option value expected"));
485
486 spec += strspn(spec, "0123456789");
487
488 if ((spec == NULL) || strlen(spec) != 1)
489 die(gettext("Only one of b, k, m, or g may be used"));
490
491 switch (spec[0]) {
492 case 'B':
493 case 'b':
494 base *= 512;
495 break;
496 case 'K':
497 case 'k':
498 base *= 1024;
499 break;
500 case 'M':
501 case 'm':
502 base *= 1024 * 1024;
503 break;
504 case 'G':
505 case 'g':
506 base *= 1024 * 1024 * 1024;
507 break;
508 default:
509 die(gettext("Must specify one of b, k, m, or g on size"));
510 }
511
512 return (base);
513 }
514
515 /*
516 * Make sure that the first call to gen_backing_store() in a loop
517 * starts with a null pointer in the outpath argument
518 * and continues to pass in that same argument until
519 * the loop is complete, at which point the string
520 * pointed to by that argument must be freed by the caller.
521 */
522 void
gen_backing_store_path(char * basepath,int num,char ** outpath)523 gen_backing_store_path(char *basepath, int num, char **outpath)
524 {
525 if (*outpath == NULL) {
526 *outpath = malloc(strlen(basepath) + MAX_SUFFIX);
527 if (*outpath == NULL)
528 die_perror("malloc");
529 }
530
531 /*
532 * Security note: We use strcpy here, instead of the safer
533 * strncpy, because the string pointed to by outpath has
534 * been generated by THIS code, above. Hence it is impossible
535 * for the strcpy to overrun the buffer.
536 */
537 if (num == 1)
538 (void) strcpy(*outpath, basepath);
539 else
540 (void) sprintf(*outpath, "%s.%d", basepath, num);
541 }
542
543 void
unlink_all(char * unlinkpath,int count)544 unlink_all(char *unlinkpath, int count)
545 {
546 char *bspath = NULL;
547 int i;
548 int save_errno;
549
550 for (i = 1; i <= count; i++) {
551 /*
552 * Make sure that the first call to gen_backing_store()
553 * starts with a null pointer in the third argument
554 * and continues to pass in that same argument until
555 * the loop is complete, at which point the string
556 * pointed to by that argument must be freed.
557 */
558 gen_backing_store_path(unlinkpath, i, &bspath);
559 if (unlink(bspath) < 0) {
560 save_errno = errno;
561 warn_errno(save_errno,
562 gettext("could not unlink %s"), bspath);
563 }
564 }
565 free(bspath);
566 }
567
568 void
close_all(char * closepath,int count,int * fd_array)569 close_all(char *closepath, int count, int *fd_array)
570 {
571 char *bspath = NULL;
572 int i;
573 int save_errno;
574
575 for (i = 1; i <= count; i++) {
576 if (close(fd_array[i - 1]) != 0) {
577 save_errno = errno;
578 /*
579 * Make sure that the first call to gen_backing_store()
580 * starts with a null pointer in the third argument
581 * and continues to pass in that same argument until
582 * the loop is complete, at which point the string
583 * pointed to by that argument must be freed.
584 */
585 gen_backing_store_path(closepath, i, &bspath);
586 die_errno(save_errno, gettext(
587 "close of backing-store (%s)"), bspath);
588 }
589 }
590 if (bspath != NULL)
591 free(bspath);
592 }
593
594 /*
595 * Create "count" files starting with name backpath ("backpath",
596 * "backpath".2, "backpath".3, etc. When this function returns,
597 * either all of the files will exist and be opened (and their
598 * file descriptors will be in fd_array), or NONE of will exist
599 * (if they had to be created) and opened (that is, if we created a file,
600 * and then failed to create a later file, the earlier files will
601 * be closed and unlinked.)
602 *
603 * If file_exists_is_fatal is set, it is a fatal error (resulting in
604 * an error message and termination) if any of the backing files to
605 * be created already exists. Otherwise, if one of the backing
606 * files already exists, we close and unlink all the files we already
607 * created, and return an error to the caller, but we don't print
608 * an error or terminate.
609 *
610 * If there is any failure other than EEXIST when attempting to
611 * create the file, the routine prints an error and terminates the
612 * program, regardless of the setting of file_exists_is_fatal.
613 */
614 int
open_multi_backfile(char * backpath,int count,int ** fd_array,int file_exists_is_fatal)615 open_multi_backfile(char *backpath, int count, int **fd_array,
616 int file_exists_is_fatal)
617 {
618 char *wpath = NULL; /* working path */
619 int i, j, fd;
620 struct stat st;
621 int stat_succeeded = 0;
622 int save_errno;
623
624 *fd_array = (int *)malloc(count * sizeof (int));
625 if (*fd_array == NULL)
626 die_perror("malloc");
627
628 for (i = 0; i < count; i++) {
629 /*
630 * Make sure that the first call to gen_backing_store()
631 * starts with a null pointer in the third argument
632 * and continues to pass in that same argument until
633 * the loop is complete, at which point the string
634 * pointed to by that argument must be freed.
635 */
636 gen_backing_store_path(backpath, i + 1, &wpath);
637 if (stat(wpath, &st) == 0)
638 stat_succeeded = 1;
639 else
640 fd = open(wpath, O_RDWR | O_CREAT | O_EXCL, 0600);
641 if (stat_succeeded || fd < 0) {
642 if (i > 0) {
643 for (j = 0; j < i - 1; j++)
644 (void) close((*fd_array)[j]);
645 /*
646 * unlink_all's second argument is the number
647 * of files to be removed, NOT the offset
648 * into the array of fd's of the last
649 * successfully created file.
650 */
651 unlink_all(backpath, i);
652 }
653 if (stat_succeeded || errno == EEXIST) {
654 if (file_exists_is_fatal)
655 die(gettext("%s exists, please specify"
656 " a nonexistent backing store."),
657 wpath);
658 else
659 return (1);
660 } else {
661 save_errno = errno;
662 die_errno(save_errno,
663 gettext("Could not create"
664 " backing file %s"), wpath);
665 }
666 }
667 (*fd_array)[i] = fd;
668 }
669 if (wpath != NULL)
670 free(wpath);
671 return (0);
672 }
673
674 void
die_perror(char * string)675 die_perror(char *string)
676 {
677 int en = errno;
678 char *errstr;
679
680 if (string == NULL) {
681 string = gettext("Fatal");
682 }
683 errstr = strerror(en);
684 if (errstr == NULL) {
685 errstr = gettext("Unknown error");
686 }
687
688 fprintf(stderr, gettext("%s: %s: error %d: %s\n"),
689 progname, string, en, errstr);
690
691 longjmp(err_main, 2);
692 }
693
694 void
die_usage(void)695 die_usage(void)
696 {
697 usage();
698
699 longjmp(err_main, 1);
700 }
701
702 void
warn_errno(int en,char * fmt,...)703 warn_errno(int en, char *fmt, ...)
704 {
705 va_list ap;
706 char *errstr;
707
708 errstr = strerror(en);
709 if (errstr == NULL) {
710 errstr = gettext("Unknown error");
711 }
712
713 va_start(ap, fmt);
714 fprintf(stderr, gettext("%s: Warning: "), progname);
715 vfprintf(stderr, fmt, ap);
716 fprintf(stderr, ": %s\n", errstr);
717 va_end(ap);
718 }
719
720 void
die_errno(int en,char * fmt,...)721 die_errno(int en, char *fmt, ...)
722 {
723 va_list ap;
724 char *errstr;
725
726 errstr = strerror(en);
727 if (errstr == NULL) {
728 errstr = gettext("Unknown error");
729 }
730
731 va_start(ap, fmt);
732 fprintf(stderr, gettext("%s: Fatal: "), progname);
733 vfprintf(stderr, fmt, ap);
734 fprintf(stderr, ": %s\n", errstr);
735 va_end(ap);
736
737 longjmp(err_main, 2);
738 }
739
740 void
die_create_error(int error)741 die_create_error(int error)
742 {
743 fprintf(stderr, gettext("snapshot error: "));
744 switch (error) {
745 case FIOCOW_EREADONLY:
746 fprintf(stderr, gettext("Read only file system\n"));
747 break;
748 case FIOCOW_EBUSY:
749 fprintf(stderr, gettext("Snapshot already enabled\n"));
750 break;
751 case FIOCOW_EULOCK:
752 fprintf(stderr, gettext("File system is locked\n"));
753 break;
754 case FIOCOW_EWLOCK:
755 fprintf(stderr,
756 gettext("File system could not be write locked\n"));
757 break;
758 case FIOCOW_EFLUSH:
759 fprintf(stderr, gettext("File system could not be flushed\n"));
760 break;
761 case FIOCOW_ECLEAN:
762 fprintf(stderr, gettext("File system may not be stable\n"));
763 break;
764 case FIOCOW_ENOULOCK:
765 fprintf(stderr, gettext("File system could not be unlocked\n"));
766 break;
767 case FIOCOW_ECHUNKSZ:
768 fprintf(stderr, gettext("Chunk size must be a multiple of the "
769 "fragment size\n"));
770 break;
771 case FIOCOW_ECREATE:
772 fprintf(stderr, gettext("Could not allocate or create "
773 "a new snapshot\n"));
774 break;
775 case FIOCOW_EBITMAP:
776 fprintf(stderr,
777 gettext("Error scanning file system bitmaps\n"));
778 break;
779 case FIOCOW_EBACKFILE:
780 fprintf(stderr, gettext("Invalid backing file path\n"));
781 break;
782 default:
783 fprintf(stderr, gettext("Unknown create error\n"));
784 break;
785 }
786
787 longjmp(err_main, 2);
788 }
789
790 void
die(char * fmt,...)791 die(char *fmt, ...)
792 {
793 va_list ap;
794
795 va_start(ap, fmt);
796 fprintf(stderr, gettext("%s: Fatal: "), progname);
797 vfprintf(stderr, fmt, ap);
798 fprintf(stderr, "\n");
799 va_end(ap);
800
801 longjmp(err_main, 2);
802 }
803
804 void
usage(void)805 usage(void)
806 {
807 int i;
808 char *use_str[] = {
809 " %s [-F ufs] [-V] -o backing-store=path,[special_options] "
810 "/mount/point\n",
811 " %s -d [-F ufs] [-V] /mount/point | dev\n",
812 " %s -i [-F ufS] [-V] [-o special-options] /mount/point "
813 "| dev\n",
814 NULL
815 };
816 fprintf(stderr, gettext("Usage:\n"));
817 for (i = 0; use_str[i] != NULL; i++)
818 fprintf(stderr, gettext(use_str[i]), progname);
819 }
820