glusterfs/extras/stripe-merge.c
Gluster Ant e16868dede Land part 2 of clang-format changes
Change-Id: Ia84cc24c8924e6d22d02ac15f611c10e26db99b4
Signed-off-by: Nigel Babu <nigelb@redhat.com>
2018-09-12 17:52:45 +05:30

504 lines
12 KiB
C

/*
Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
This file is part of GlusterFS.
This file is licensed to you under your choice of the GNU Lesser
General Public License, version 3 or any later version (LGPLv3 or
later), or the GNU General Public License, version 2 (GPLv2), in all
cases as published by the Free Software Foundation.
*/
/*
* stripe-merge.c
*
* This program recovers an original file based on the striped files stored on
* the individual bricks of a striped volume. The file format and stripe
* geometry is validated through the extended attributes stored in the file.
*
* TODO: Support optional xattr recovery (i.e., user xattrs). Perhaps provide a
* command-line flag to toggle this behavior.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
#include <sys/xattr.h>
#include <fnmatch.h>
#define ATTRNAME_STRIPE_INDEX "trusted.*.stripe-index"
#define ATTRNAME_STRIPE_COUNT "trusted.*.stripe-count"
#define ATTRNAME_STRIPE_SIZE "trusted.*.stripe-size"
#define ATTRNAME_STRIPE_COALESCE "trusted.*.stripe-coalesce"
#define INVALID_FD -1
#define INVALID_MODE UINT32_MAX
struct file_stripe_info {
int stripe_count;
int stripe_size;
int coalesce;
mode_t mode;
int fd[0];
};
static int
close_files(struct file_stripe_info *);
static struct file_stripe_info *
alloc_file_stripe_info(int count)
{
int i;
struct file_stripe_info *finfo;
finfo = calloc(1, sizeof(struct file_stripe_info) + (sizeof(int) * count));
if (!finfo)
return NULL;
for (i = 0; i < count; i++)
finfo->fd[i] = INVALID_FD;
finfo->mode = INVALID_MODE;
finfo->coalesce = INVALID_FD;
return finfo;
}
/*
* Search for an attribute matching the provided pattern. Return a count for
* the total number of matching entries (including 0). Allocate a buffer for
* the first matching entry found.
*/
static int
get_stripe_attr_name(const char *path, const char *pattern, char **attrname)
{
char attrbuf[4096];
char *ptr, *match = NULL;
int len, r, match_count = 0;
if (!path || !pattern || !attrname)
return -1;
len = listxattr(path, attrbuf, sizeof(attrbuf));
if (len < 0)
return len;
ptr = attrbuf;
while (ptr) {
r = fnmatch(pattern, ptr, 0);
if (!r) {
if (!match)
match = ptr;
match_count++;
} else if (r != FNM_NOMATCH) {
return -1;
}
len -= strlen(ptr) + 1;
if (len > 0)
ptr += strlen(ptr) + 1;
else
ptr = NULL;
}
if (match)
*attrname = strdup(match);
return match_count;
}
/*
* Get the integer representation of a named attribute.
*/
static int
get_stripe_attr_val(const char *path, const char *attr, int *val)
{
char attrbuf[4096];
int len;
if (!path || !attr || !val)
return -1;
len = getxattr(path, attr, attrbuf, sizeof(attrbuf));
if (len < 0)
return len;
*val = atoi(attrbuf);
return 0;
}
/*
* Get an attribute name/value (assumed to be an integer) pair based on a
* specified search pattern. A buffer is allocated for the exact attr name
* returned. Optionally, skip the pattern search if a buffer is provided
* (which should contain an attribute name).
*
* Returns the attribute count or -1 on error. The value parameter is set only
* when a single attribute is found.
*/
static int
get_attr(const char *path, const char *pattern, char **buf, int *val)
{
int count = 1;
if (!buf)
return -1;
if (!*buf) {
count = get_stripe_attr_name(path, pattern, buf);
if (count > 1) {
/* pattern isn't good enough */
fprintf(stderr,
"ERROR: duplicate attributes found "
"matching pattern: %s\n",
pattern);
free(*buf);
*buf = NULL;
return count;
} else if (count < 1) {
return count;
}
}
if (get_stripe_attr_val(path, *buf, val) < 0)
return -1;
return count;
}
/*
* validate_and_open_files()
*
* Open the provided source files and validate the extended attributes. Verify
* that the geometric attributes are consistent across all of the files and
* print a warning if any files are missing. We proceed without error in the
* latter case to support partial recovery.
*/
static struct file_stripe_info *
validate_and_open_files(char *paths[], int count)
{
int i, val, tmp;
struct stat sbuf;
char *stripe_count_attr = NULL;
char *stripe_size_attr = NULL;
char *stripe_index_attr = NULL;
char *stripe_coalesce_attr = NULL;
struct file_stripe_info *finfo = NULL;
for (i = 0; i < count; i++) {
if (!paths[i])
goto err;
/*
* Check the stripe count first so we can allocate the info
* struct with the appropriate number of fds.
*/
if (get_attr(paths[i], ATTRNAME_STRIPE_COUNT, &stripe_count_attr,
&val) != 1) {
fprintf(stderr, "ERROR: %s: attribute: '%s'\n", paths[i],
ATTRNAME_STRIPE_COUNT);
goto err;
}
if (!finfo) {
finfo = alloc_file_stripe_info(val);
if (!finfo)
goto err;
if (val != count)
fprintf(stderr,
"WARNING: %s: stripe-count "
"(%d) != file count (%d). Result may "
"be incomplete.\n",
paths[i], val, count);
finfo->stripe_count = val;
} else if (val != finfo->stripe_count) {
fprintf(stderr,
"ERROR %s: invalid stripe count: %d "
"(expected %d)\n",
paths[i], val, finfo->stripe_count);
goto err;
}
/*
* Get and validate the chunk size.
*/
if (get_attr(paths[i], ATTRNAME_STRIPE_SIZE, &stripe_size_attr, &val) !=
1) {
fprintf(stderr, "ERROR: %s: attribute: '%s'\n", paths[i],
ATTRNAME_STRIPE_SIZE);
goto err;
}
if (!finfo->stripe_size) {
finfo->stripe_size = val;
} else if (val != finfo->stripe_size) {
fprintf(stderr,
"ERROR: %s: invalid stripe size: %d "
"(expected %d)\n",
paths[i], val, finfo->stripe_size);
goto err;
}
/*
* stripe-coalesce is a backward compatible attribute. If the
* attribute does not exist, assume a value of zero for the
* traditional stripe format.
*/
tmp = get_attr(paths[i], ATTRNAME_STRIPE_COALESCE,
&stripe_coalesce_attr, &val);
if (!tmp) {
val = 0;
} else if (tmp != 1) {
fprintf(stderr, "ERROR: %s: attribute: '%s'\n", paths[i],
ATTRNAME_STRIPE_COALESCE);
goto err;
}
if (finfo->coalesce == INVALID_FD) {
finfo->coalesce = val;
} else if (val != finfo->coalesce) {
fprintf(stderr, "ERROR: %s: invalid coalesce flag\n", paths[i]);
goto err;
}
/*
* Get/validate the stripe index and open the file in the
* appropriate fd slot.
*/
if (get_attr(paths[i], ATTRNAME_STRIPE_INDEX, &stripe_index_attr,
&val) != 1) {
fprintf(stderr, "ERROR: %s: attribute: '%s'\n", paths[i],
ATTRNAME_STRIPE_INDEX);
goto err;
}
if (finfo->fd[val] != INVALID_FD) {
fprintf(stderr,
"ERROR: %s: duplicate stripe index: "
"%d\n",
paths[i], val);
goto err;
}
finfo->fd[val] = open(paths[i], O_RDONLY);
if (finfo->fd[val] < 0)
goto err;
/*
* Get the creation mode for the file.
*/
if (fstat(finfo->fd[val], &sbuf) < 0)
goto err;
if (finfo->mode == INVALID_MODE) {
finfo->mode = sbuf.st_mode;
} else if (sbuf.st_mode != finfo->mode) {
fprintf(stderr, "ERROR: %s: invalid mode\n", paths[i]);
goto err;
}
}
free(stripe_count_attr);
free(stripe_size_attr);
free(stripe_index_attr);
free(stripe_coalesce_attr);
return finfo;
err:
free(stripe_count_attr);
free(stripe_size_attr);
free(stripe_index_attr);
free(stripe_coalesce_attr);
if (finfo) {
close_files(finfo);
free(finfo);
}
return NULL;
}
static int
close_files(struct file_stripe_info *finfo)
{
int i, ret;
if (!finfo)
return -1;
for (i = 0; i < finfo->stripe_count; i++) {
if (finfo->fd[i] == INVALID_FD)
continue;
ret = close(finfo->fd[i]);
if (ret < 0)
return ret;
}
return ret;
}
/*
* Generate the original file using files striped in the coalesced format.
* Data in the striped files is stored at a coalesced offset based on the
* stripe number.
*
* Walk through the finfo fds (which are already ordered) and and iteratively
* copy stripe_size bytes from the source files to the target file. If a source
* file is missing, seek past the associated stripe_size bytes in the target
* file.
*/
static int
generate_file_coalesce(int target, struct file_stripe_info *finfo)
{
char *buf;
int ret = 0;
int r, w, i;
buf = malloc(finfo->stripe_size);
if (!buf)
return -1;
i = 0;
while (1) {
if (finfo->fd[i] == INVALID_FD) {
if (lseek(target, finfo->stripe_size, SEEK_CUR) < 0)
break;
i = (i + 1) % finfo->stripe_count;
continue;
}
r = read(finfo->fd[i], buf, finfo->stripe_size);
if (r < 0) {
ret = r;
break;
}
if (!r)
break;
w = write(target, buf, r);
if (w < 0) {
ret = w;
break;
}
i = (i + 1) % finfo->stripe_count;
}
free(buf);
return ret;
}
/*
* Generate the original file using files striped with the traditional stripe
* format. Data in the striped files is stored at the equivalent offset from
* the source file.
*/
static int
generate_file_traditional(int target, struct file_stripe_info *finfo)
{
int i, j, max_ret, ret;
char buf[finfo->stripe_count][4096];
do {
char newbuf[4096] = {
0,
};
max_ret = 0;
for (i = 0; i < finfo->stripe_count; i++) {
memset(buf[i], 0, 4096);
ret = read(finfo->fd[i], buf[i], 4096);
if (ret > max_ret)
max_ret = ret;
}
for (i = 0; i < max_ret; i++)
for (j = 0; j < finfo->stripe_count; j++)
newbuf[i] |= buf[j][i];
write(target, newbuf, max_ret);
} while (max_ret);
return 0;
}
static int
generate_file(int target, struct file_stripe_info *finfo)
{
if (finfo->coalesce)
return generate_file_coalesce(target, finfo);
return generate_file_traditional(target, finfo);
}
static void
usage(char *name)
{
fprintf(stderr,
"Usage: %s [-o <outputfile>] <inputfile1> "
"<inputfile2> ...\n",
name);
}
int
main(int argc, char *argv[])
{
int file_count, opt;
char *opath = NULL;
int targetfd;
struct file_stripe_info *finfo;
while ((opt = getopt(argc, argv, "o:")) != -1) {
switch (opt) {
case 'o':
opath = optarg;
break;
default:
usage(argv[0]);
return -1;
}
}
file_count = argc - optind;
if (!opath || !file_count) {
usage(argv[0]);
return -1;
}
finfo = validate_and_open_files(&argv[optind], file_count);
if (!finfo)
goto err;
targetfd = open(opath, O_RDWR | O_CREAT, finfo->mode);
if (targetfd < 0)
goto err;
if (generate_file(targetfd, finfo) < 0)
goto err;
if (fsync(targetfd) < 0)
fprintf(stderr, "ERROR: %s\n", strerror(errno));
if (close(targetfd) < 0)
fprintf(stderr, "ERROR: %s\n", strerror(errno));
close_files(finfo);
free(finfo);
return 0;
err:
if (finfo) {
close_files(finfo);
free(finfo);
}
return -1;
}