glusterfs
1/*
2Copyright (c) 2008-2012 Red Hat, Inc. <http://www.redhat.com>
3This file is part of GlusterFS.
4
5This file is licensed to you under your choice of the GNU Lesser
6General Public License, version 3 or any later version (LGPLv3 or
7later), or the GNU General Public License, version 2 (GPLv2), in all
8cases as published by the Free Software Foundation.
9*/
10
11/*
12* stripe-merge.c
13*
14* This program recovers an original file based on the striped files stored on
15* the individual bricks of a striped volume. The file format and stripe
16* geometry is validated through the extended attributes stored in the file.
17*
18* TODO: Support optional xattr recovery (i.e., user xattrs). Perhaps provide a
19* command-line flag to toggle this behavior.
20*/
21
22#include <stdio.h>23#include <sys/types.h>24#include <sys/stat.h>25#include <fcntl.h>26#include <unistd.h>27#include <stdlib.h>28#include <stdint.h>29#include <errno.h>30#include <string.h>31#include <sys/xattr.h>32#include <fnmatch.h>33
34#define ATTRNAME_STRIPE_INDEX "trusted.*.stripe-index"35#define ATTRNAME_STRIPE_COUNT "trusted.*.stripe-count"36#define ATTRNAME_STRIPE_SIZE "trusted.*.stripe-size"37#define ATTRNAME_STRIPE_COALESCE "trusted.*.stripe-coalesce"38
39#define INVALID_FD -140#define INVALID_MODE UINT32_MAX41
42struct file_stripe_info {43int stripe_count;44int stripe_size;45int coalesce;46mode_t mode;47int fd[0];48};49
50static int51close_files(struct file_stripe_info *);52
53static struct file_stripe_info *54alloc_file_stripe_info(int count)55{
56int i;57struct file_stripe_info *finfo;58
59finfo = calloc(1, sizeof(struct file_stripe_info) + (sizeof(int) * count));60if (!finfo)61return NULL;62
63for (i = 0; i < count; i++)64finfo->fd[i] = INVALID_FD;65
66finfo->mode = INVALID_MODE;67finfo->coalesce = INVALID_FD;68
69return finfo;70}
71
72/*
73* Search for an attribute matching the provided pattern. Return a count for
74* the total number of matching entries (including 0). Allocate a buffer for
75* the first matching entry found.
76*/
77static int78get_stripe_attr_name(const char *path, const char *pattern, char **attrname)79{
80char attrbuf[4096];81char *ptr, *match = NULL;82int len, r, match_count = 0;83
84if (!path || !pattern || !attrname)85return -1;86
87len = listxattr(path, attrbuf, sizeof(attrbuf));88if (len < 0)89return len;90
91ptr = attrbuf;92while (ptr) {93r = fnmatch(pattern, ptr, 0);94if (!r) {95if (!match)96match = ptr;97match_count++;98} else if (r != FNM_NOMATCH) {99return -1;100}101
102len -= strlen(ptr) + 1;103if (len > 0)104ptr += strlen(ptr) + 1;105else106ptr = NULL;107}108
109if (match)110*attrname = strdup(match);111
112return match_count;113}
114
115/*
116* Get the integer representation of a named attribute.
117*/
118static int119get_stripe_attr_val(const char *path, const char *attr, int *val)120{
121char attrbuf[4096];122int len;123
124if (!path || !attr || !val)125return -1;126
127len = getxattr(path, attr, attrbuf, sizeof(attrbuf));128if (len < 0)129return len;130
131*val = atoi(attrbuf);132
133return 0;134}
135
136/*
137* Get an attribute name/value (assumed to be an integer) pair based on a
138* specified search pattern. A buffer is allocated for the exact attr name
139* returned. Optionally, skip the pattern search if a buffer is provided
140* (which should contain an attribute name).
141*
142* Returns the attribute count or -1 on error. The value parameter is set only
143* when a single attribute is found.
144*/
145static int146get_attr(const char *path, const char *pattern, char **buf, int *val)147{
148int count = 1;149
150if (!buf)151return -1;152
153if (!*buf) {154count = get_stripe_attr_name(path, pattern, buf);155if (count > 1) {156/* pattern isn't good enough */157fprintf(stderr,158"ERROR: duplicate attributes found "159"matching pattern: %s\n",160pattern);161free(*buf);162*buf = NULL;163return count;164} else if (count < 1) {165return count;166}167}168
169if (get_stripe_attr_val(path, *buf, val) < 0)170return -1;171
172return count;173}
174
175/*
176* validate_and_open_files()
177*
178* Open the provided source files and validate the extended attributes. Verify
179* that the geometric attributes are consistent across all of the files and
180* print a warning if any files are missing. We proceed without error in the
181* latter case to support partial recovery.
182*/
183static struct file_stripe_info *184validate_and_open_files(char *paths[], int count)185{
186int i, val, tmp;187struct stat sbuf;188char *stripe_count_attr = NULL;189char *stripe_size_attr = NULL;190char *stripe_index_attr = NULL;191char *stripe_coalesce_attr = NULL;192struct file_stripe_info *finfo = NULL;193
194for (i = 0; i < count; i++) {195if (!paths[i])196goto err;197
198/*199* Check the stripe count first so we can allocate the info
200* struct with the appropriate number of fds.
201*/
202if (get_attr(paths[i], ATTRNAME_STRIPE_COUNT, &stripe_count_attr,203&val) != 1) {204fprintf(stderr, "ERROR: %s: attribute: '%s'\n", paths[i],205ATTRNAME_STRIPE_COUNT);206goto err;207}208if (!finfo) {209finfo = alloc_file_stripe_info(val);210if (!finfo)211goto err;212
213if (val != count)214fprintf(stderr,215"WARNING: %s: stripe-count "216"(%d) != file count (%d). Result may "217"be incomplete.\n",218paths[i], val, count);219
220finfo->stripe_count = val;221} else if (val != finfo->stripe_count) {222fprintf(stderr,223"ERROR %s: invalid stripe count: %d "224"(expected %d)\n",225paths[i], val, finfo->stripe_count);226goto err;227}228
229/*230* Get and validate the chunk size.
231*/
232if (get_attr(paths[i], ATTRNAME_STRIPE_SIZE, &stripe_size_attr, &val) !=2331) {234fprintf(stderr, "ERROR: %s: attribute: '%s'\n", paths[i],235ATTRNAME_STRIPE_SIZE);236goto err;237}238
239if (!finfo->stripe_size) {240finfo->stripe_size = val;241} else if (val != finfo->stripe_size) {242fprintf(stderr,243"ERROR: %s: invalid stripe size: %d "244"(expected %d)\n",245paths[i], val, finfo->stripe_size);246goto err;247}248
249/*250* stripe-coalesce is a backward compatible attribute. If the
251* attribute does not exist, assume a value of zero for the
252* traditional stripe format.
253*/
254tmp = get_attr(paths[i], ATTRNAME_STRIPE_COALESCE,255&stripe_coalesce_attr, &val);256if (!tmp) {257val = 0;258} else if (tmp != 1) {259fprintf(stderr, "ERROR: %s: attribute: '%s'\n", paths[i],260ATTRNAME_STRIPE_COALESCE);261goto err;262}263
264if (finfo->coalesce == INVALID_FD) {265finfo->coalesce = val;266} else if (val != finfo->coalesce) {267fprintf(stderr, "ERROR: %s: invalid coalesce flag\n", paths[i]);268goto err;269}270
271/*272* Get/validate the stripe index and open the file in the
273* appropriate fd slot.
274*/
275if (get_attr(paths[i], ATTRNAME_STRIPE_INDEX, &stripe_index_attr,276&val) != 1) {277fprintf(stderr, "ERROR: %s: attribute: '%s'\n", paths[i],278ATTRNAME_STRIPE_INDEX);279goto err;280}281if (finfo->fd[val] != INVALID_FD) {282fprintf(stderr,283"ERROR: %s: duplicate stripe index: "284"%d\n",285paths[i], val);286goto err;287}288
289finfo->fd[val] = open(paths[i], O_RDONLY);290if (finfo->fd[val] < 0)291goto err;292
293/*294* Get the creation mode for the file.
295*/
296if (fstat(finfo->fd[val], &sbuf) < 0)297goto err;298if (finfo->mode == INVALID_MODE) {299finfo->mode = sbuf.st_mode;300} else if (sbuf.st_mode != finfo->mode) {301fprintf(stderr, "ERROR: %s: invalid mode\n", paths[i]);302goto err;303}304}305
306free(stripe_count_attr);307free(stripe_size_attr);308free(stripe_index_attr);309free(stripe_coalesce_attr);310
311return finfo;312err:313
314free(stripe_count_attr);315free(stripe_size_attr);316free(stripe_index_attr);317free(stripe_coalesce_attr);318
319if (finfo) {320close_files(finfo);321free(finfo);322}323
324return NULL;325}
326
327static int328close_files(struct file_stripe_info *finfo)329{
330int i, ret;331
332if (!finfo)333return -1;334
335for (i = 0; i < finfo->stripe_count; i++) {336if (finfo->fd[i] == INVALID_FD)337continue;338
339ret = close(finfo->fd[i]);340if (ret < 0)341return ret;342}343
344return ret;345}
346
347/*
348* Generate the original file using files striped in the coalesced format.
349* Data in the striped files is stored at a coalesced offset based on the
350* stripe number.
351*
352* Walk through the finfo fds (which are already ordered) and and iteratively
353* copy stripe_size bytes from the source files to the target file. If a source
354* file is missing, seek past the associated stripe_size bytes in the target
355* file.
356*/
357static int358generate_file_coalesce(int target, struct file_stripe_info *finfo)359{
360char *buf;361int ret = 0;362int r, w, i;363
364buf = malloc(finfo->stripe_size);365if (!buf)366return -1;367
368i = 0;369while (1) {370if (finfo->fd[i] == INVALID_FD) {371if (lseek(target, finfo->stripe_size, SEEK_CUR) < 0)372break;373
374i = (i + 1) % finfo->stripe_count;375continue;376}377
378r = read(finfo->fd[i], buf, finfo->stripe_size);379if (r < 0) {380ret = r;381break;382}383if (!r)384break;385
386w = write(target, buf, r);387if (w < 0) {388ret = w;389break;390}391
392i = (i + 1) % finfo->stripe_count;393}394
395free(buf);396return ret;397}
398
399/*
400* Generate the original file using files striped with the traditional stripe
401* format. Data in the striped files is stored at the equivalent offset from
402* the source file.
403*/
404static int405generate_file_traditional(int target, struct file_stripe_info *finfo)406{
407int i, j, max_ret, ret;408char buf[finfo->stripe_count][4096];409
410do {411char newbuf[4096] = {4120,413};414
415max_ret = 0;416for (i = 0; i < finfo->stripe_count; i++) {417memset(buf[i], 0, 4096);418ret = read(finfo->fd[i], buf[i], 4096);419if (ret > max_ret)420max_ret = ret;421}422for (i = 0; i < max_ret; i++)423for (j = 0; j < finfo->stripe_count; j++)424newbuf[i] |= buf[j][i];425write(target, newbuf, max_ret);426} while (max_ret);427
428return 0;429}
430
431static int432generate_file(int target, struct file_stripe_info *finfo)433{
434if (finfo->coalesce)435return generate_file_coalesce(target, finfo);436
437return generate_file_traditional(target, finfo);438}
439
440static void441usage(char *name)442{
443fprintf(stderr,444"Usage: %s [-o <outputfile>] <inputfile1> "445"<inputfile2> ...\n",446name);447}
448
449int
450main(int argc, char *argv[])451{
452int file_count, opt;453char *opath = NULL;454int targetfd;455struct file_stripe_info *finfo;456
457while ((opt = getopt(argc, argv, "o:")) != -1) {458switch (opt) {459case 'o':460opath = optarg;461break;462default:463usage(argv[0]);464return -1;465}466}467
468file_count = argc - optind;469
470if (!opath || !file_count) {471usage(argv[0]);472return -1;473}474
475finfo = validate_and_open_files(&argv[optind], file_count);476if (!finfo)477goto err;478
479targetfd = open(opath, O_RDWR | O_CREAT, finfo->mode);480if (targetfd < 0)481goto err;482
483if (generate_file(targetfd, finfo) < 0)484goto err;485
486if (fsync(targetfd) < 0)487fprintf(stderr, "ERROR: %s\n", strerror(errno));488if (close(targetfd) < 0)489fprintf(stderr, "ERROR: %s\n", strerror(errno));490
491close_files(finfo);492free(finfo);493
494return 0;495
496err:497if (finfo) {498close_files(finfo);499free(finfo);500}501
502return -1;503}
504