fssnap.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/fssnap_if.h>
#include <setjmp.h>
#include <stdarg.h>
#include <kstat.h>
#include <libintl.h>
#include <libdevinfo.h>
#include <sys/sysmacros.h>
static int max_uniq = 9999;
void delete_snap(int);
void stats_snap(char *, char *);
int open_backpath(int, u_offset_t, char **, char **, int **);
u_offset_t spec_to_bytes(char *);
void unlink_all(char *, int);
void close_all(char *, int, int *);
int open_multi_backfile(char *, int, int **, int);
void die_perror(char *);
void die_errno(int, char *, ...);
void die_create_error(int error);
void die_usage(void);
void die(char *, ...);
void warn_errno(int, char *, ...);
void usage(void);
static char *subopts[] = {
#define BACKPATH (0)
"backing-store",
#define BACKPATH2 (1)
"bs",
#define BACKPATH3 (2)
"bf",
#define MAXSIZE (3)
"maxsize",
#define CHUNKSIZE (4)
"chunksize",
#define RAWFILE (5)
"raw",
#define UNLINK (6)
"unlink",
};
static backout_snap_fd = -1;
int brief); /* in ../../fssnapsup.c */
int
{
int c;
char *suboptions = NULL;
char *value;
int longjmp_return;
char *mountpoint = NULL;
int mountfd = -1;
int delete = 0;
int stats = 0;
u_offset_t maxsize = 0;
int rawfile = 0;
int dounlink = 0;
++progname;
else
if (backout_snap_fd >= 0) {
}
return (longjmp_return);
}
switch (c) {
case 'd':
++delete;
break;
case 'i':
++stats;
break;
case 'o':
suboptions = optarg;
break;
default:
die_usage();
}
}
/* if -i or -d are not specified then interpret the create options */
while (*suboptions != '\0') {
case BACKPATH:
case BACKPATH2:
case BACKPATH3:
die_usage();
die_perror("strdup");
}
break;
case MAXSIZE:
break;
case CHUNKSIZE:
break;
case RAWFILE:
++rawfile;
break;
case UNLINK:
++dounlink;
break;
default:
die_usage();
}
}
}
/* -d and -i can not be specified together or more than once each */
die_usage();
/* If no mount point is specified then -i is the only valid option. */
die_usage();
/*
* If anything but the mount point or device is specified at the end
* it's an error.
*/
if (!stats)
die_usage();
} else {
/* Otherwise, the last option is the mountpoint. */
}
if (stats != 0) {
} else if (delete != 0) {
} else {
/*
* backpath may be invalid upon return of create_snap call.
*/
}
return (0);
}
void
{
struct fiosnapcreate_multi *enable;
int backcount;
int ctlfd;
char *unlinkpath = NULL;
int *fd_array;
int save_errno;
/*
* If chunksize is not a power of 2, the maximum size of a
* backing store file might not be UFS_MAX_SNAPBACKFILESIZE,
* since the size of the backing store files must be an
* integral number of chunks (except for the last one). So
* calculate the actual maximum backing store file size.
* (It would be nice if we could assume that the chunksize
* was a power of 2, but we can't.)
*/
else
/*
* open_backpath() only returns on success, and
* can change the value of backpath when backpath
* references a directory.
*/
&unlinkpath, &fd_array);
/*
* Only need backcount - 1 spaces for fd's since
* fiosnapcreate_multi struct contains space for the
* first one.
*/
/*
* enable.backfilename is advisory only. So, we don't overflow
* the buffer, but we don't give an error if the backpath does not
* fit. Instead, it is truncated, and the kstat shows all it can.
*/
if (dounlink)
backpath);
else
}
}
} else {
die_perror("ioctl");
}
}
if (dounlink != 0)
save_errno = errno;
}
save_errno = errno;
gettext("/dev/%s/%d may not be immediately available\n"),
} else {
(void) di_devlink_fini(&hdl);
}
/* intentionally not internationalized */
}
void
delete_snap(int mountfd)
{
struct fiosnapdelete disable;
int ctlfd;
int save_errno;
} else {
die_perror("ioctl");
}
}
save_errno = errno;
}
}
void
{
}
/*
* Open as many backing files as necessary for this snapshot.
* There will be one backing file for each max_bf_size
* number of bytes in the file system being snapped.
* The array of file descriptors for the backing files is returned in
* fd_array. The number of backing files is the return value of the
* function. The name of the first backing file is returned in
* unlinkpath. The subsequent backing files are assumed to have the
* same name as the first, but with suffixes, .2, .3, etc.
*/
int
char **unlinkpath, int **fd_array)
{
int ret_errno, i, num_back_files;
int save_errno;
*unlinkpath = NULL;
/* determine size of the file system to be snapped */
die_perror("statvfs");
/*
* Since we set the file_exists_is_fatal argument to 1,
* if we return at all, it will be with all the backing
* files successfully created and opened.
*/
if (unlinkpath == NULL)
die_perror("strdup");
char temppath[MAXPATHLEN];
/* remove a trailing slash from the name */
/* find a unique name */
/* cannot use tempnam, since TMPDIR overrides path */
num_back_files, fd_array, 0);
if (ret_errno == 0)
break;
}
}
*path = *unlinkpath;
} else {
"or a directory."), *path);
}
/*
* write a block to the end to bump up the file size and ensure the
* entire range needed can be written to.
*/
for (i = 0; i < num_back_files; i++) {
else
die_perror("llseek");
}
save_errno = errno;
if (save_errno == EFBIG)
"does not support large files.\n"), *path);
else
die_perror("write");
}
}
return (num_back_files);
}
spec_to_bytes(char *spec)
{
switch (spec[0]) {
case 'B':
case 'b':
base *= 512;
break;
case 'K':
case 'k':
base *= 1024;
break;
case 'M':
case 'm':
break;
case 'G':
case 'g':
break;
default:
}
return (base);
}
/*
* Make sure that the first call to gen_backing_store() in a loop
* starts with a null pointer in the outpath argument
* and continues to pass in that same argument until
* the loop is complete, at which point the string
* pointed to by that argument must be freed by the caller.
*/
void
{
die_perror("malloc");
}
/*
* Security note: We use strcpy here, instead of the safer
* strncpy, because the string pointed to by outpath has
* been generated by THIS code, above. Hence it is impossible
* for the strcpy to overrun the buffer.
*/
if (num == 1)
else
}
void
{
int i;
int save_errno;
for (i = 1; i <= count; i++) {
/*
* Make sure that the first call to gen_backing_store()
* starts with a null pointer in the third argument
* and continues to pass in that same argument until
* the loop is complete, at which point the string
* pointed to by that argument must be freed.
*/
save_errno = errno;
}
}
}
void
{
int i;
int save_errno;
for (i = 1; i <= count; i++) {
save_errno = errno;
/*
* Make sure that the first call to gen_backing_store()
* starts with a null pointer in the third argument
* and continues to pass in that same argument until
* the loop is complete, at which point the string
* pointed to by that argument must be freed.
*/
"close of backing-store (%s)"), bspath);
}
}
}
/*
* Create "count" files starting with name backpath ("backpath",
* "backpath".2, "backpath".3, etc. When this function returns,
* either all of the files will exist and be opened (and their
* file descriptors will be in fd_array), or NONE of will exist
* (if they had to be created) and opened (that is, if we created a file,
* and then failed to create a later file, the earlier files will
* be closed and unlinked.)
*
* If file_exists_is_fatal is set, it is a fatal error (resulting in
* an error message and termination) if any of the backing files to
* be created already exists. Otherwise, if one of the backing
* files already exists, we close and unlink all the files we already
* created, and return an error to the caller, but we don't print
* an error or terminate.
*
* If there is any failure other than EEXIST when attempting to
* create the file, the routine prints an error and terminates the
* program, regardless of the setting of file_exists_is_fatal.
*/
int
int file_exists_is_fatal)
{
int i, j, fd;
int stat_succeeded = 0;
int save_errno;
die_perror("malloc");
for (i = 0; i < count; i++) {
/*
* Make sure that the first call to gen_backing_store()
* starts with a null pointer in the third argument
* and continues to pass in that same argument until
* the loop is complete, at which point the string
* pointed to by that argument must be freed.
*/
stat_succeeded = 1;
else
if (stat_succeeded || fd < 0) {
if (i > 0) {
for (j = 0; j < i - 1; j++)
/*
* unlink_all's second argument is the number
* of files to be removed, NOT the offset
* into the array of fd's of the last
* successfully created file.
*/
unlink_all(backpath, i);
}
if (file_exists_is_fatal)
" a nonexistent backing store."),
wpath);
else
return (1);
} else {
save_errno = errno;
gettext("Could not create"
" backing file %s"), wpath);
}
}
}
return (0);
}
void
die_perror(char *string)
{
char *errstr;
}
}
}
void
die_usage(void)
{
usage();
}
void
{
char *errstr;
}
}
void
{
char *errstr;
}
}
void
die_create_error(int error)
{
switch (error) {
case FIOCOW_EREADONLY:
break;
case FIOCOW_EBUSY:
break;
case FIOCOW_EULOCK:
break;
case FIOCOW_EWLOCK:
gettext("File system could not be write locked\n"));
break;
case FIOCOW_EFLUSH:
break;
case FIOCOW_ECLEAN:
break;
case FIOCOW_ENOULOCK:
break;
case FIOCOW_ECHUNKSZ:
"fragment size\n"));
break;
case FIOCOW_ECREATE:
"a new snapshot\n"));
break;
case FIOCOW_EBITMAP:
gettext("Error scanning file system bitmaps\n"));
break;
case FIOCOW_EBACKFILE:
break;
default:
break;
}
}
void
{
}
void
usage(void)
{
int i;
char *use_str[] = {
" %s [-F ufs] [-V] -o backing-store=path,[special_options] "
"| dev\n",
};
}