lfs-mirror-extend \- add mirror(s) to an existing file
.SH SYNOPSIS
.SY "lfs mirror extend"
-.RB [ --no-verify ]
-.RB [ --mirror-count | -N\c
-.RI [ MIRROR_COUNT ]]
-.RB [ --bandwidth-limit | -W
-.IR BANDWIDTH ]
-.RB [ --stats | --stats-interval\c
-.RI = STATS_INTERVAL ]
-.RI [ SETSTRIPE_OPTIONS |\c
-.B -f
-.IR VICTIM_FILE ]
-.IR FILENAME " ..."
+ [\fB--bandwidth-limit\fR|\fB-W\fR \fIMB_PER_SEC\fR]
+ [\fB--mirror-count\fR|\fB-N\fR[\fIMIRROR_COUNT\fR]]
+ [\fB--no-verify\fR]
+ [\fB--stats\fR|\fB--stats-interval\fR=\fISECONDS\fR]
+ [\fISETSTRIPE_OPTIONS\fR|\fB-f\fR \fIVICTIM_FILE\fR]
+ \fIFILES_FROM\fR
.YS
.SH DESCRIPTION
-This command adds mirror(s) to an existing file specified by the path name
-.IR FILENAME .
+This command adds mirror(s) to existing file(s) specified by \fIFILES_FROM\fR.
+.P
+The file to be extended can already be a mirrored file, or just a regular
+non-mirrored file. If it's a non-mirrored file, the command will convert it
+to a mirrored file.
.P
-The file
-.I FILENAME
-can already be a mirrored file, or just a regular non-mirrored file.
-If it's a non-mirrored file,
-then the command will convert it to a mirrored file.
+.I FILES_FROM
+may be:
+.TP
+.IR FILENAME " [...]"
+File names are listed on command line.
+Multiple file names are separated by space char.
+.TP
+.BR -0 ", " --null
+Read file names from stdin by default. Each file name is followed by a NUL char.
+Usually is used after a pipeline from \fBlfs find --print0\fR command.
+.TP
+.BR --files-from = \fILIST_FILE
+Read file names from file \fILIST_FILE\fR. One line for each file name.
+If \fILIST_FILE\fR is \fB-\fR, read from stdin.
+If \fB--null\fR is specified at the same time,
+file names are separated by a NUL char.
.P
The
.BR --mirror-count | -N
}
run_test 56x "lfs migration support"
+test_56xB() {
+ local td=$DIR/$tdir
+ local tf1=$td/${tfile}_1
+ local tf2=$td/${tfile}_2
+ local tf3=$td/${tfile}_3
+ local flist=/tmp/flist.$$
+
+ (( $OSTCOUNT >= 2 )) || skip "needs >= 2 OSTs"
+
+ test_mkdir $td
+ dd if=/dev/urandom of=$tf1 bs=1K count=1 || error "failed to create $tf1"
+ dd if=/dev/urandom of=$tf2 bs=1K count=1 || error "failed to create $tf2"
+ dd if=/dev/urandom of=$tf3 bs=1K count=1 || error "failed to create $tf3"
+
+ stack_trap "rm -f $flist"
+
+ $LFS find $td -type f > $flist || error "failed to generate list file"
+ $LFS migrate -o 0 --files-from=$flist ||
+ error "failed to run lfs migrate with --files-from"
+
+ $LFS find $td -type f -print0 | $LFS migrate -o 1 --null ||
+ error "failed to run lfs migrate from pipe"
+
+ $LFS find $td -type f -print0 > $flist ||
+ error "failed to generate 0-ending list file"
+ $LFS migrate -o 0 -0 --files-from=$flist ||
+ error "failed to run lfs migrate with -0 --files-from"
+}
+run_test 56xB "lfs migrate with -0, --null, --files-from arguments"
+
test_56xa() {
[[ $OSTCOUNT -lt 2 ]] && skip_env "needs >= 2 OSTs"
check_swap_layouts_support
SSM_CMD_COMMON("migrate ") \
"\t\t[--block|-b] [--non-block|-n]\n" \
"\t\t[--non-direct|-D] [--verbose|-v] FILENAME\n" \
+ "\t\t[--non-direct|-D] [--verbose|-v]\n" \
+ "\t\t-0|--null|--files-from=LIST_FILE|FILENAME ...\n"
#define SETDIRSTRIPE_USAGE \
" [--mdt-count|-c stripe_count>\n" \
"\t\t[--no-verify] [--stats|--stats-interval=STATS_INTERVAL]\n"
"\t\t[--bandwidth-limit|--W BANDWIDTH]\n"
"\t\t[-f VICTIM_FILE]\n"
- "\t\t" SSM_SETSTRIPE_OPT "]"
- " FILENAME ...\n" },
+ "\t\t" SSM_SETSTRIPE_OPT "]\n"
+ "\t\t-0|--null|--files-from=LIST_FILE|FILENAME ...\n" },
{ .pc_name = "split", .pc_func = lfs_mirror_split,
.pc_help = "Split a mirrored file.\n"
"usage: lfs mirror split {--mirror-id MIRROR_ID |\n"
LFS_QUOTA_ISOFTLIMIT_OPT,
LFS_QUOTA_IHARDLIMIT_OPT,
LFS_QUOTA_IGRACE_OPT,
+ LFS_FILES_FROM,
};
#ifndef LCME_USER_MIRROR_FLAGS
unsigned long long bandwidth_bytes_sec = 0;
unsigned long long bandwidth_unit = ONE_MB;
long stats_interval_sec = 0;
+ bool null_mode = false;
+ const char *files_from = NULL;
+ FILE *files_from_fp = NULL;
+ int delim = '\n';
+ char *buf = NULL;
+ size_t bufsize = 0;
struct option long_opts[] = {
-/* find { .val = '0', .name = "null", .has_arg = no_argument }, */
-/* find { .val = 'A', .name = "atime", .has_arg = required_argument }*/
- /* --block is only valid in migrate mode */
- { .val = 'b', .name = "block", .has_arg = no_argument },
-/* find { .val = 'B', .name = "btime", .has_arg = required_argument }*/
{ .val = LFS_COMP_ADD_OPT,
.name = "comp-add", .has_arg = no_argument },
{ .val = LFS_COMP_ADD_OPT,
{ .val = LFS_STATS_INTERVAL_OPT,
.name = "stats-interval",
.has_arg = required_argument},
+ { .val = LFS_FILES_FROM,
+ .name = "files-from", .has_arg = required_argument},
+ { .val = '0', .name = "null", .has_arg = no_argument },
+ /* find { .val = 'A', .name = "atime", .has_arg = required_argument }*/
+ /* --block is only valid in migrate mode */
+ { .val = 'b', .name = "block", .has_arg = no_argument },
+ /* find { .val = 'B', .name = "btime", .has_arg = required_argument }*/
{ .val = 'c', .name = "stripe-count", .has_arg = required_argument},
{ .val = 'c', .name = "stripe_count", .has_arg = required_argument},
{ .val = 'c', .name = "mdt-count", .has_arg = required_argument},
snprintf(cmd, sizeof(cmd), "%s %s", progname, argv[0]);
progname = cmd;
while ((c = getopt_long(argc, argv,
- "bc:C:dDE:f:hH:i:I:m:N::no:p:L:s:S:vx:W:y:z:",
+ "0bc:C:dDE:f:hH:i:I:m:N::no:p:L:s:S:vx:W:y:z:",
long_opts, NULL)) >= 0) {
size_units = 1;
switch (c) {
}
clear_hash_fixed = true;
break;
+ case LFS_FILES_FROM:
+ files_from = optarg;
+ break;
+ case '0':
+ null_mode = true;
+ break;
case 'b':
if (!migrate_mode) {
fprintf(stderr,
fname = argv[optind];
- if (optind == argc) {
+ /* for 'lfs migrate' and 'lfs mirror extend' command,
+ * at least one of FILE/--null/--files-from=LIST_FILE must be specified.
+ * If both --null and --files-from=LIST_FILE are specified, read
+ * filenames from LIST_FILE and use '\0' as delimiter.
+ */
+ if (opc == SO_MIGRATE || opc == SO_MIRROR_EXTEND) {
+ int num = 0;
+
+ if (optind < argc)
+ num++;
+ if (null_mode) {
+ if (files_from_fp == NULL)
+ files_from_fp = stdin;
+ delim = 0;
+ num++;
+ }
+ if (files_from != NULL) {
+ if (strcmp("-", files_from) == 0)
+ files_from_fp = stdin;
+ else
+ files_from_fp = fopen(files_from, "r");
+ if (files_from_fp == NULL) {
+ result = -errno;
+ fprintf(stderr, "%s %s: failed to open filelist file '%s'\n",
+ progname, argv[0], files_from);
+ goto error;
+ }
+ num++;
+ }
+ if (num < 1) {
+ fprintf(stderr, "%s %s: at least one of FILE/--null/--files-from=LIST_FILE must be specified\n",
+ progname, argv[0]);
+ goto usage_error;
+ }
+ } else if (optind == argc) {
fprintf(stderr, "%s %s: FILE must be specified\n",
progname, argv[0]);
goto usage_error;
}
}
- for (fname = argv[optind]; (optind < argc) && (fname != NULL);
- fname = argv[++optind]) {
+ while (true) {
+ if (files_from_fp == NULL) {
+ /* file names from arguments */
+ fname = argv[optind++];
+ if (optind > argc || fname == NULL)
+ break;
+ } else {
+ /* file names from file/stdin */
+ ssize_t len;
+
+ errno = 0;
+ len = getdelim(&buf, &bufsize, delim, files_from_fp);
+ if (len == -1) {
+ if (errno != 0) { /* error */
+ result = -errno;
+ fprintf(stderr, "%s %s: failed to read from list file\n",
+ progname, argv[0]);
+ goto error;
+ } else { /* EOF */
+ break;
+ }
+ }
+ /* remove possible trailing '\n' */
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ fname = buf;
+ }
+
if (from_copy) {
layout = layout_get_by_name_or_fid(template ?: fname,
fname, 0, O_RDONLY);
error:
llapi_layout_free(layout);
lfs_mirror_list_free(mirror_list);
+ if (files_from_fp != NULL && files_from_fp != stdin)
+ fclose(files_from_fp);
+ free(buf);
return result;
}