+next:
+ if (rc == 0 && rc2 < 0)
+ rc = rc2;
+ }
+ return rc;
+}
+
+/** The input string contains a comma delimited list of component ids and
+ * ranges, for example "1,2-4,7".
+ */
+static int parse_mirror_ids(__u16 *ids, int size, char *arg)
+{
+ bool end_of_loop = false;
+ char *ptr = NULL;
+ int nr = 0;
+ int rc;
+
+ if (arg == NULL)
+ return -EINVAL;
+
+ while (!end_of_loop) {
+ int start_index;
+ int end_index;
+ int i;
+ char *endptr = NULL;
+
+ rc = -EINVAL;
+ ptr = strchrnul(arg, ',');
+ end_of_loop = *ptr == '\0';
+ *ptr = '\0';
+
+ start_index = strtol(arg, &endptr, 0);
+ if (endptr == arg) /* no data at all */
+ break;
+ if (*endptr != '-' && *endptr != '\0') /* has invalid data */
+ break;
+ if (start_index < 0)
+ break;
+
+ end_index = start_index;
+ if (*endptr == '-') {
+ end_index = strtol(endptr + 1, &endptr, 0);
+ if (*endptr != '\0')
+ break;
+ if (end_index < start_index)
+ break;
+ }
+
+ for (i = start_index; i <= end_index && size > 0; i++) {
+ int j;
+
+ /* remove duplicate */
+ for (j = 0; j < nr; j++) {
+ if (ids[j] == i)
+ break;
+ }
+ if (j == nr) { /* no duplicate */
+ ids[nr++] = i;
+ --size;
+ }
+ }
+
+ if (size == 0 && i < end_index)
+ break;
+
+ *ptr = ',';
+ arg = ++ptr;
+ rc = 0;
+ }
+ if (!end_of_loop && ptr != NULL)
+ *ptr = ',';
+
+ return rc < 0 ? rc : nr;
+}
+
+/**
+ * struct verify_mirror_id - Mirror id to be verified.
+ * @mirror_id: A specified mirror id.
+ * @is_valid_id: @mirror_id is valid or not in the mirrored file.
+ */
+struct verify_mirror_id {
+ __u16 mirror_id;
+ bool is_valid_id;
+};
+
+/**
+ * compare_mirror_ids() - Compare mirror ids.
+ * @layout: Mirror component list.
+ * @cbdata: Callback data in verify_mirror_id structure.
+ *
+ * This is a callback function called by llapi_layout_comp_iterate()
+ * to compare the specified mirror id with the one in the current
+ * component of @layout. If they are the same, then the specified
+ * mirror id is valid.
+ *
+ * Return: a negative error code on failure or
+ * LLAPI_LAYOUT_ITER_CONT: Proceed iteration
+ * LLAPI_LAYOUT_ITER_STOP: Stop iteration
+ */
+static inline
+int compare_mirror_ids(struct llapi_layout *layout, void *cbdata)
+{
+ struct verify_mirror_id *mirror_id_cbdata =
+ (struct verify_mirror_id *)cbdata;
+ uint32_t mirror_id;
+ int rc = 0;
+
+ rc = llapi_layout_mirror_id_get(layout, &mirror_id);
+ if (rc < 0) {
+ rc = -errno;
+ fprintf(stderr,
+ "%s: llapi_layout_mirror_id_get failed: %s.\n",
+ progname, strerror(errno));
+ return rc;
+ }
+
+ if (mirror_id_cbdata->mirror_id == mirror_id) {
+ mirror_id_cbdata->is_valid_id = true;
+ return LLAPI_LAYOUT_ITER_STOP;
+ }
+
+ return LLAPI_LAYOUT_ITER_CONT;
+}
+
+/**
+ * verify_mirror_ids() - Verify specified mirror ids.
+ * @fname: Mirrored file name.
+ * @mirror_ids: Specified mirror ids to be verified.
+ * @ids_nr: Number of specified mirror ids.
+ *
+ * This function verifies that specified @mirror_ids are valid
+ * in the mirrored file @fname.
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+static inline
+int verify_mirror_ids(const char *fname, __u16 *mirror_ids, int ids_nr)
+{
+ struct llapi_layout *layout = NULL;
+ struct verify_mirror_id mirror_id_cbdata = { 0 };
+ struct stat stbuf;
+ uint32_t flr_state;
+ int i;
+ int fd;
+ int rc = 0;
+ int rc2 = 0;
+
+ if (ids_nr <= 0)
+ return -EINVAL;
+
+ if (stat(fname, &stbuf) < 0) {
+ fprintf(stderr, "%s: cannot stat file '%s': %s.\n",
+ progname, fname, strerror(errno));
+ rc = -errno;
+ goto error;
+ }
+
+ if (!S_ISREG(stbuf.st_mode)) {
+ fprintf(stderr, "%s: '%s' is not a regular file.\n",
+ progname, fname);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ fd = open(fname, O_DIRECT | O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s: cannot open '%s': %s.\n",
+ progname, fname, strerror(errno));
+ rc = -errno;
+ goto error;
+ }
+
+ rc = llapi_lease_acquire(fd, LL_LEASE_RDLCK);
+ if (rc < 0) {
+ fprintf(stderr, "%s: '%s' llapi_lease_acquire failed: %s.\n",
+ progname, fname, strerror(errno));
+ goto close_fd;
+ }
+
+ layout = llapi_layout_get_by_fd(fd, 0);
+ if (layout == NULL) {
+ fprintf(stderr, "%s: '%s' llapi_layout_get_by_fd failed: %s.\n",
+ progname, fname, strerror(errno));
+ rc = -errno;
+ llapi_lease_release(fd);
+ goto close_fd;
+ }
+
+ rc = llapi_layout_flags_get(layout, &flr_state);
+ if (rc < 0) {
+ fprintf(stderr, "%s: '%s' llapi_layout_flags_get failed: %s.\n",
+ progname, fname, strerror(errno));
+ rc = -errno;
+ goto free_layout;
+ }
+
+ flr_state &= LCM_FL_FLR_MASK;
+ switch (flr_state) {
+ case LCM_FL_NONE:
+ rc = -EINVAL;
+ fprintf(stderr, "%s: '%s' file state error: %s.\n",
+ progname, fname, llapi_layout_flags_string(flr_state));
+ goto free_layout;
+ default:
+ break;
+ }
+
+ rc2 = 0;
+ for (i = 0; i < ids_nr; i++) {
+ mirror_id_cbdata.mirror_id = mirror_ids[i];
+ mirror_id_cbdata.is_valid_id = false;
+
+ rc = llapi_layout_comp_iterate(layout, compare_mirror_ids,
+ &mirror_id_cbdata);
+ if (rc < 0) {
+ rc = -errno;
+ fprintf(stderr,
+ "%s: '%s' failed to verify mirror id: %u.\n",
+ progname, fname, mirror_ids[i]);
+ goto free_layout;
+ }
+
+ if (!mirror_id_cbdata.is_valid_id) {
+ rc2 = -EINVAL;
+ fprintf(stderr,
+ "%s: '%s' invalid specified mirror id: %u.\n",
+ progname, fname, mirror_ids[i]);
+ }
+ }
+ rc = rc2;
+
+free_layout:
+ llapi_layout_free(layout);
+ llapi_lease_release(fd);
+close_fd:
+ close(fd);
+error:
+ return rc;
+}
+
+static inline
+int lfs_mirror_resync_file(const char *fname, struct ll_ioc_lease *ioc,
+ __u16 *mirror_ids, int ids_nr)
+{
+ struct llapi_resync_comp comp_array[1024] = { { 0 } };
+ struct llapi_layout *layout;
+ struct stat stbuf;
+ uint32_t flr_state;
+ uint64_t start;
+ uint64_t end;
+ int comp_size = 0;
+ int idx;
+ int fd;
+ int rc;
+
+ if (stat(fname, &stbuf) < 0) {
+ fprintf(stderr, "%s: cannot stat file '%s': %s.\n",
+ progname, fname, strerror(errno));
+ rc = -errno;
+ goto error;
+ }
+ if (!S_ISREG(stbuf.st_mode)) {
+ fprintf(stderr, "%s: '%s' is not a regular file.\n",
+ progname, fname);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ fd = open(fname, O_DIRECT | O_RDWR);
+ if (fd < 0) {
+ fprintf(stderr, "%s: cannot open '%s': %s.\n",
+ progname, fname, strerror(errno));
+ rc = -errno;
+ goto error;
+ }
+
+ layout = llapi_layout_get_by_fd(fd, 0);
+ if (layout == NULL) {
+ fprintf(stderr, "%s: '%s' llapi_layout_get_by_fd failed: %s.\n",
+ progname, fname, strerror(errno));
+ rc = -errno;
+ goto close_fd;
+ }
+
+ rc = llapi_layout_flags_get(layout, &flr_state);
+ if (rc) {
+ fprintf(stderr, "%s: '%s' llapi_layout_flags_get failed: %s.\n",
+ progname, fname, strerror(errno));
+ rc = -errno;
+ goto free_layout;
+ }
+
+ flr_state &= LCM_FL_FLR_MASK;
+ if (flr_state == LCM_FL_NONE) {
+ rc = -EINVAL;
+ fprintf(stderr, "%s: '%s' is not a FLR file.\n",
+ progname, fname);
+ goto free_layout;
+ }
+
+ /* get stale component info */
+ comp_size = llapi_mirror_find_stale(layout, comp_array,
+ ARRAY_SIZE(comp_array),
+ mirror_ids, ids_nr);
+ if (comp_size <= 0) {
+ rc = comp_size;
+ goto free_layout;
+ }
+
+ ioc->lil_mode = LL_LEASE_WRLCK;
+ ioc->lil_flags = LL_LEASE_RESYNC;
+ rc = llapi_lease_set(fd, ioc);
+ if (rc < 0) {
+ if (rc == -EALREADY)
+ rc = 0;
+ else
+ fprintf(stderr,
+ "%s: '%s' llapi_lease_get_ext resync failed: %s.\n",
+ progname, fname, strerror(errno));
+ goto free_layout;
+ }
+
+ /* get the read range [start, end) */
+ start = comp_array[0].lrc_start;
+ end = comp_array[0].lrc_end;
+ for (idx = 1; idx < comp_size; idx++) {
+ if (comp_array[idx].lrc_start < start)
+ start = comp_array[idx].lrc_start;
+ if (end < comp_array[idx].lrc_end)
+ end = comp_array[idx].lrc_end;
+ }
+
+ rc = llapi_lease_check(fd);
+ if (rc != LL_LEASE_WRLCK) {
+ fprintf(stderr, "%s: '%s' lost lease lock.\n",
+ progname, fname);
+ goto free_layout;
+ }
+
+ rc = llapi_mirror_resync_many(fd, layout, comp_array, comp_size,
+ start, end);
+ if (rc < 0)
+ fprintf(stderr, "%s: '%s' llapi_mirror_resync_many: %d.\n",
+ progname, fname, rc);
+
+ /* prepare ioc for lease put */
+ ioc->lil_mode = LL_LEASE_UNLCK;
+ ioc->lil_flags = LL_LEASE_RESYNC_DONE;
+ ioc->lil_count = 0;
+ for (idx = 0; idx < comp_size; idx++) {
+ if (comp_array[idx].lrc_synced) {
+ ioc->lil_ids[ioc->lil_count] = comp_array[idx].lrc_id;
+ ioc->lil_count++;
+ }
+ }
+
+ rc = llapi_lease_set(fd, ioc);
+ if (rc <= 0) {
+ if (rc == 0) /* lost lease lock */
+ rc = -EBUSY;
+ fprintf(stderr, "%s: resync file '%s' failed: %s.\n",
+ progname, fname, strerror(errno));
+ goto free_layout;
+ }
+ /**
+ * llapi_lease_set returns lease mode when it request to unlock
+ * the lease lock
+ */
+ rc = 0;
+
+free_layout:
+ llapi_layout_free(layout);
+close_fd:
+ close(fd);
+error:
+ return rc;
+}
+
+static inline int lfs_mirror_resync(int argc, char **argv)
+{
+ struct ll_ioc_lease *ioc = NULL;
+ __u16 mirror_ids[128] = { 0 };
+ int ids_nr = 0;
+ int c;
+ int rc = 0;
+
+ struct option long_opts[] = {
+ { .val = 'o', .name = "only", .has_arg = required_argument },
+ { .name = NULL } };
+
+ while ((c = getopt_long(argc, argv, "o:", long_opts, NULL)) >= 0) {
+ switch (c) {
+ case 'o':
+ rc = parse_mirror_ids(mirror_ids,
+ sizeof(mirror_ids) / sizeof(__u16),
+ optarg);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s: bad mirror ids '%s'.\n",
+ argv[0], optarg);
+ goto error;
+ }
+ ids_nr = rc;
+ break;
+ default:
+ fprintf(stderr, "%s: options '%s' unrecognized.\n",
+ argv[0], argv[optind - 1]);
+ rc = -EINVAL;
+ goto error;
+ }
+ }
+
+ if (argc == optind) {
+ fprintf(stderr, "%s: no file name given.\n", argv[0]);
+ rc = CMD_HELP;
+ goto error;
+ }
+
+ if (ids_nr > 0 && argc > optind + 1) {
+ fprintf(stderr,
+ "%s: option '--only' cannot be used upon multiple files.\n",
+ argv[0]);
+ rc = CMD_HELP;
+ goto error;
+
+ }
+
+ if (ids_nr > 0) {
+ rc = verify_mirror_ids(argv[optind], mirror_ids, ids_nr);
+ if (rc < 0)
+ goto error;
+ }
+
+ /* set the lease on the file */
+ ioc = calloc(sizeof(*ioc) + sizeof(__u32) * 4096, 1);
+ if (ioc == NULL) {
+ fprintf(stderr, "%s: cannot alloc id array for ioc: %s.\n",
+ argv[0], strerror(errno));
+ rc = -errno;
+ goto error;
+ }
+
+ for (; optind < argc; optind++) {
+ rc = lfs_mirror_resync_file(argv[optind], ioc,
+ mirror_ids, ids_nr);
+ /* ignore previous file's error, continue with next file */
+
+ /* reset ioc */
+ memset(ioc, 0, sizeof(*ioc) + sizeof(__u32) * 4096);
+ }
+
+ free(ioc);
+error:
+ return rc;
+}
+
+static inline int verify_mirror_id_by_fd(int fd, __u16 mirror_id)
+{
+ struct llapi_layout *layout;
+ int rc;
+
+ layout = llapi_layout_get_by_fd(fd, 0);
+ if (layout == NULL) {
+ fprintf(stderr, "could not get layout.\n");
+ return -EINVAL;
+ }
+
+ rc = llapi_layout_comp_iterate(layout, find_mirror_id, &mirror_id);
+ if (rc < 0) {
+ fprintf(stderr, "failed to iterate layout\n");
+ llapi_layout_free(layout);
+
+ return rc;
+ } else if (rc == LLAPI_LAYOUT_ITER_CONT) {
+ fprintf(stderr, "does not find mirror with ID %u\n", mirror_id);
+ llapi_layout_free(layout);
+
+ return -EINVAL;
+ }
+ llapi_layout_free(layout);
+
+ return 0;
+}
+
+/**
+ * Check whether two files are the same file
+ * \retval 0 same file
+ * \retval 1 not the same file
+ * \retval <0 error code
+ */
+static inline int check_same_file(const char *f1, const char *f2)
+{
+ struct stat stbuf1;
+ struct stat stbuf2;
+
+ if (stat(f1, &stbuf1) < 0) {
+ fprintf(stderr, "%s: cannot stat file '%s': %s\n",
+ progname, f1, strerror(errno));
+ return -errno;
+ }
+
+ if (stat(f2, &stbuf2) < 0) {
+ fprintf(stderr, "%s: cannot stat file '%s': %s\n",
+ progname, f2, strerror(errno));
+ return -errno;
+ }
+
+ if (stbuf1.st_rdev == stbuf2.st_rdev &&
+ stbuf1.st_ino == stbuf2.st_ino)
+ return 0;
+
+ return 1;
+}
+
+static inline int lfs_mirror_read(int argc, char **argv)
+{
+ int rc = CMD_HELP;
+ __u16 mirror_id = 0;
+ const char *outfile = NULL;
+ char *fname;
+ int fd = 0;
+ int outfd;
+ int c;
+ void *buf;
+ const size_t buflen = 4 << 20;
+ off_t pos;
+ struct option long_opts[] = {
+ { .val = 'N', .name = "mirror-id", .has_arg = required_argument },
+ { .val = 'o', .name = "outfile", .has_arg = required_argument },
+ { .name = NULL } };
+
+ while ((c = getopt_long(argc, argv, "N:o:", long_opts, NULL)) >= 0) {
+ char *end;
+
+ switch (c) {
+ case 'N':
+ mirror_id = strtoul(optarg, &end, 0);
+ if (*end != '\0' || mirror_id == 0) {
+ fprintf(stderr,
+ "%s %s: invalid mirror ID '%s'\n",
+ progname, argv[0], optarg);
+ return rc;
+ }
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ default:
+ fprintf(stderr, "%s: option '%s' unrecognized.\n",
+ progname, argv[optind - 1]);
+ return -EINVAL;
+ }
+ }
+
+ if (argc == optind) {
+ fprintf(stderr, "%s %s: no mirrored file provided\n",
+ progname, argv[0]);
+ return rc;
+ } else if (argc > optind + 1) {
+ fprintf(stderr, "%s %s: too many files\n", progname, argv[0]);
+ return rc;
+ }
+
+ if (mirror_id == 0) {
+ fprintf(stderr, "%s %s: no valid mirror ID is provided\n",
+ progname, argv[0]);
+ return rc;
+ }
+
+ /* open mirror file */
+ fname = argv[optind];
+
+ if (outfile) {
+ rc = check_same_file(fname, outfile);
+ if (rc == 0) {
+ fprintf(stderr,
+ "%s %s: output file cannot be the mirrored file\n",
+ progname, argv[0]);
+ return -EINVAL;
+ }
+ if (rc < 0)
+ return rc;
+ }
+
+ fd = open(fname, O_DIRECT | O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s %s: cannot open '%s': %s\n",
+ progname, argv[0], fname, strerror(errno));
+ return rc;
+ }
+
+ /* verify mirror id */
+ rc = verify_mirror_id_by_fd(fd, mirror_id);
+ if (rc) {
+ fprintf(stderr,
+ "%s %s: cannot find mirror with ID %u in '%s'\n",
+ progname, argv[0], mirror_id, fname);
+ goto close_fd;
+ }
+
+ /* open output file */
+ if (outfile) {
+ outfd = open(outfile, O_EXCL | O_WRONLY | O_CREAT, 0644);
+ if (outfd < 0) {
+ fprintf(stderr, "%s %s: cannot create file '%s': %s\n",
+ progname, argv[0], outfile, strerror(errno));
+ rc = -errno;
+ goto close_fd;
+ }
+ } else {
+ outfd = STDOUT_FILENO;
+ }
+
+ /* allocate buffer */
+ rc = posix_memalign(&buf, sysconf(_SC_PAGESIZE), buflen);
+ if (rc) {
+ fprintf(stderr, "%s %s: posix_memalign returns %d\n",
+ progname, argv[0], rc);
+ goto close_outfd;
+ }
+
+ pos = 0;
+ while (1) {
+ ssize_t bytes_read;
+ ssize_t written = 0;
+
+ bytes_read = llapi_mirror_read(fd, mirror_id, buf, buflen, pos);
+ if (bytes_read < 0) {
+ rc = bytes_read;
+ fprintf(stderr,
+ "%s %s: fail to read data from mirror %u: %s\n",
+ progname, argv[0], mirror_id, strerror(-rc));
+ goto free_buf;
+ }
+
+ /* EOF reached */
+ if (bytes_read == 0)
+ break;
+
+ while (written < bytes_read) {
+ ssize_t written2;
+
+ written2 = write(outfd, buf + written,
+ bytes_read - written);
+ if (written2 < 0) {
+ fprintf(stderr,
+ "%s %s: fail to write %s: %s\n",
+ progname, argv[0], outfile ? : "STDOUT",
+ strerror(errno));
+ rc = -errno;
+ goto free_buf;
+ }
+ written += written2;
+ }
+
+ if (written != bytes_read) {
+ fprintf(stderr,
+ "%s %s: written %ld bytes does not match with %ld read.\n",
+ progname, argv[0], written, bytes_read);
+ rc = -EIO;
+ goto free_buf;
+ }
+
+ pos += bytes_read;
+ }
+
+ fsync(outfd);
+ rc = 0;
+
+free_buf:
+ free(buf);
+close_outfd:
+ if (outfile)
+ close(outfd);
+close_fd:
+ close(fd);
+
+ return rc;
+}
+
+static inline int lfs_mirror_write(int argc, char **argv)
+{
+ int rc = CMD_HELP;
+ __u16 mirror_id = 0;
+ const char *inputfile = NULL;
+ char *fname;
+ int fd = 0;
+ int inputfd;
+ int c;
+ void *buf;
+ const size_t buflen = 4 << 20;
+ off_t pos;
+ size_t page_size = sysconf(_SC_PAGESIZE);
+ struct ll_ioc_lease_id ioc;
+
+ struct option long_opts[] = {
+ { .val = 'N', .name = "mirror-id", .has_arg = required_argument },
+ { .val = 'i', .name = "inputfile", .has_arg = required_argument },
+ { .name = NULL } };
+
+ while ((c = getopt_long(argc, argv, "N:i:", long_opts, NULL)) >= 0) {
+ char *end;
+
+ switch (c) {
+ case 'N':
+ mirror_id = strtoul(optarg, &end, 0);
+ if (*end != '\0' || mirror_id == 0) {
+ fprintf(stderr,
+ "%s %s: invalid mirror ID '%s'\n",
+ progname, argv[0], optarg);
+ return rc;
+ }
+ break;
+ case 'i':
+ inputfile = optarg;
+ break;
+ default:
+ fprintf(stderr, "%s: option '%s' unrecognized\n",
+ progname, argv[optind - 1]);
+ return -EINVAL;
+ }
+ }
+
+ if (argc == optind) {
+ fprintf(stderr, "%s %s: no mirrored file provided\n",
+ progname, argv[0]);
+ return rc;
+ } else if (argc > optind + 1) {
+ fprintf(stderr, "%s %s: too many files\n", progname, argv[0]);
+ return rc;
+ }
+
+ if (mirror_id == 0) {
+ fprintf(stderr, "%s %s: no valid mirror ID is provided\n",
+ progname, argv[0]);
+ return rc;
+ }
+
+ /* open mirror file */
+ fname = argv[optind];
+
+ if (inputfile) {
+ rc = check_same_file(fname, inputfile);
+ if (rc == 0) {
+ fprintf(stderr,
+ "%s %s: input file cannot be the mirrored file\n",
+ progname, argv[0]);
+ return -EINVAL;
+ }
+ if (rc < 0)
+ return rc;
+ }
+
+ fd = open(fname, O_DIRECT | O_WRONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s %s: cannot open '%s': %s\n",
+ progname, argv[0], fname, strerror(errno));
+ return rc;
+ }
+
+ /* verify mirror id */
+ rc = verify_mirror_id_by_fd(fd, mirror_id);
+ if (rc) {
+ fprintf(stderr,
+ "%s %s: cannot find mirror with ID %u in '%s'\n",
+ progname, argv[0], mirror_id, fname);
+ goto close_fd;
+ }
+
+ /* open input file */
+ if (inputfile) {
+ inputfd = open(inputfile, O_RDONLY, 0644);
+ if (inputfd < 0) {
+ fprintf(stderr, "%s %s: cannot open file '%s': %s\n",
+ progname, argv[0], inputfile, strerror(errno));
+ rc = -errno;
+ goto close_fd;
+ }
+ } else {
+ inputfd = STDIN_FILENO;
+ }
+
+ /* allocate buffer */
+ rc = posix_memalign(&buf, page_size, buflen);
+ if (rc) {
+ fprintf(stderr, "%s %s: posix_memalign returns %d\n",
+ progname, argv[0], rc);
+ goto close_inputfd;
+ }
+
+ /* prepare target mirror components instantiation */
+ ioc.lil_mode = LL_LEASE_WRLCK;
+ ioc.lil_flags = LL_LEASE_RESYNC;
+ ioc.lil_mirror_id = mirror_id;
+ rc = llapi_lease_set(fd, (struct ll_ioc_lease *)&ioc);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s %s: '%s' llapi_lease_get_ext failed: %s\n",
+ progname, argv[0], fname, strerror(errno));
+ goto free_buf;
+ }
+
+ pos = 0;
+ while (1) {
+ ssize_t bytes_read;
+ ssize_t written;
+ size_t to_write;
+
+ rc = llapi_lease_check(fd);
+ if (rc != LL_LEASE_WRLCK) {
+ fprintf(stderr, "%s %s: '%s' lost lease lock\n",
+ progname, argv[0], fname);
+ goto free_buf;
+ }
+
+ bytes_read = read(inputfd, buf, buflen);
+ if (bytes_read < 0) {
+ rc = bytes_read;
+ fprintf(stderr,
+ "%s %s: fail to read data from '%s': %s\n",
+ progname, argv[0], inputfile ? : "STDIN",
+ strerror(errno));
+ rc = -errno;
+ goto free_buf;
+ }
+
+ /* EOF reached */
+ if (bytes_read == 0)
+ break;
+
+ /* round up to page align to make direct IO happy. */
+ to_write = (bytes_read + page_size - 1) & ~(page_size - 1);
+
+ written = llapi_mirror_write(fd, mirror_id, buf, to_write,
+ pos);
+ if (written < 0) {
+ rc = written;
+ fprintf(stderr,
+ "%s %s: fail to write to mirror %u: %s\n",
+ progname, argv[0], mirror_id,
+ strerror(-rc));
+ goto free_buf;
+ }
+
+ pos += bytes_read;
+ }
+
+ if (pos & (page_size - 1)) {
+ rc = llapi_mirror_truncate(fd, mirror_id, pos);
+ if (rc < 0)
+ goto free_buf;
+ }
+
+ ioc.lil_mode = LL_LEASE_UNLCK;
+ ioc.lil_flags = LL_LEASE_RESYNC_DONE;
+ ioc.lil_count = 0;
+ rc = llapi_lease_set(fd, (struct ll_ioc_lease *)&ioc);
+ if (rc <= 0) {
+ if (rc == 0)
+ rc = -EBUSY;
+ fprintf(stderr,
+ "%s %s: release lease lock of '%s' failed: %s\n",
+ progname, argv[0], fname, strerror(errno));
+ goto free_buf;
+ }
+
+ rc = 0;
+
+free_buf:
+ free(buf);
+close_inputfd:
+ if (inputfile)
+ close(inputfd);
+close_fd:
+ close(fd);
+
+ return rc;
+}
+
+/**
+ * struct verify_chunk - Mirror chunk to be verified.
+ * @chunk: [start, end) of the chunk.
+ * @mirror_count: Number of mirror ids in @mirror_id array.
+ * @mirror_id: Array of valid mirror ids that cover the chunk.
+ */
+struct verify_chunk {
+ struct lu_extent chunk;
+ unsigned int mirror_count;
+ __u16 mirror_id[LUSTRE_MIRROR_COUNT_MAX];
+};
+
+/**
+ * print_chunks() - Print chunk information.
+ * @fname: Mirrored file name.
+ * @chunks: Array of chunks.
+ * @chunk_count: Number of chunks in @chunks array.
+ *
+ * This function prints [start, end) of each chunk in @chunks
+ * for mirrored file @fname, and also prints the valid mirror ids
+ * that cover the chunk.
+ *
+ * Return: void.
+ */
+static inline
+void print_chunks(const char *fname, struct verify_chunk *chunks,
+ int chunk_count)
+{
+ int i;
+ int j;
+
+ fprintf(stdout, "Chunks to be verified in %s:\n", fname);
+ for (i = 0; i < chunk_count; i++) {
+ fprintf(stdout, DEXT, PEXT(&chunks[i].chunk));
+
+ if (chunks[i].mirror_count == 0)
+ fprintf(stdout, "\t[");
+ else {
+ fprintf(stdout, "\t[%u", chunks[i].mirror_id[0]);
+ for (j = 1; j < chunks[i].mirror_count; j++)
+ fprintf(stdout, ", %u", chunks[i].mirror_id[j]);
+ }
+ fprintf(stdout, "]\t%u\n", chunks[i].mirror_count);
+ }
+ fprintf(stdout, "\n");
+}
+
+/**
+ * print_checksums() - Print CRC-32 checksum values.
+ * @chunk: A chunk and its corresponding valid mirror ids.
+ * @crc: CRC-32 checksum values on the chunk for each valid mirror.
+ *
+ * This function prints CRC-32 checksum values on @chunk for
+ * each valid mirror that covers it.
+ *
+ * Return: void.
+ */
+static inline
+void print_checksums(struct verify_chunk *chunk, unsigned long *crc)
+{
+ int i;
+
+ fprintf(stdout,
+ "CRC-32 checksum value for chunk "DEXT":\n",
+ PEXT(&chunk->chunk));
+ for (i = 0; i < chunk->mirror_count; i++)
+ fprintf(stdout, "Mirror %u:\t%#lx\n",
+ chunk->mirror_id[i], crc[i]);
+ fprintf(stdout, "\n");
+}
+
+/**
+ * filter_mirror_id() - Filter specified mirror ids.
+ * @chunks: Array of chunks.
+ * @chunk_count: Number of chunks in @chunks array.
+ * @mirror_ids: Specified mirror ids to be verified.
+ * @ids_nr: Number of specified mirror ids.
+ *
+ * This function scans valid mirror ids that cover each chunk in @chunks
+ * and filters specified mirror ids.
+ *
+ * Return: void.
+ */
+static inline
+void filter_mirror_id(struct verify_chunk *chunks, int chunk_count,
+ __u16 *mirror_ids, int ids_nr)
+{
+ int i;
+ int j;
+ int k;
+ __u16 valid_id[LUSTRE_MIRROR_COUNT_MAX] = { 0 };
+ unsigned int valid_count = 0;
+
+ for (i = 0; i < chunk_count; i++) {
+ if (chunks[i].mirror_count == 0)
+ continue;
+
+ valid_count = 0;
+ for (j = 0; j < ids_nr; j++) {
+ for (k = 0; k < chunks[i].mirror_count; k++) {
+ if (chunks[i].mirror_id[k] == mirror_ids[j]) {
+ valid_id[valid_count] = mirror_ids[j];
+ valid_count++;
+ break;
+ }
+ }
+ }
+
+ memcpy(chunks[i].mirror_id, valid_id,
+ sizeof(__u16) * valid_count);
+ chunks[i].mirror_count = valid_count;
+ }
+}
+
+/**
+ * lfs_mirror_prepare_chunk() - Find mirror chunks to be verified.
+ * @layout: Mirror component list.
+ * @chunks: Array of chunks.
+ * @chunks_size: Array size of @chunks.
+ *
+ * This function scans the components in @layout from offset 0 to LUSTRE_EOF
+ * to find out chunk segments and store them in @chunks array.
+ *
+ * The @mirror_id array in each element of @chunks will store the valid
+ * mirror ids that cover the chunk. If a mirror component covering the
+ * chunk has LCME_FL_STALE or LCME_FL_OFFLINE flag, then the mirror id
+ * will not be stored into the @mirror_id array, and the chunk for that
+ * mirror will not be verified.
+ *
+ * The @mirror_count in each element of @chunks will store the number of
+ * mirror ids in @mirror_id array. If @mirror_count is 0, it indicates the
+ * chunk is invalid in all of the mirrors. And if @mirror_count is 1, it
+ * indicates the chunk is valid in only one mirror. In both cases, the
+ * chunk will not be verified.
+ *
+ * Here is an example:
+ *
+ * 0 1M 2M 3M 4M EOF
+ * +------+-------------+--------------------+
+ * | | | S | mirror1
+ * +------+------+------+------+-------------+
+ * | | S | S | | mirror2
+ * +-------------+------+------+-------------+
+ *
+ * prepared @chunks array will contain 5 elements:
+ * (([0, 1M), [1, 2], 2),
+ * ([1M, 2M), [1, 2], 2),
+ * ([2M, 3M), [1], 1),
+ * ([3M, 4M], [], 0),
+ * ([4M, EOF), [2], 1))
+ *
+ * Return: the actual array size of @chunks on success
+ * or a negative error code on failure.
+ */
+static inline
+int lfs_mirror_prepare_chunk(struct llapi_layout *layout,
+ struct verify_chunk *chunks,
+ size_t chunks_size)
+{
+ uint64_t start;
+ uint64_t end;
+ uint32_t mirror_id;
+ uint32_t flags;
+ int idx = 0;
+ int i = 0;
+ int rc = 0;
+
+ memset(chunks, 0, sizeof(*chunks) * chunks_size);
+
+ while (1) {
+ rc = llapi_layout_comp_use(layout, LLAPI_LAYOUT_COMP_USE_FIRST);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s: move to the first layout component: %s.\n",
+ progname, strerror(errno));
+ goto error;
+ }
+
+ i = 0;
+ rc = 0;
+ chunks[idx].chunk.e_end = LUSTRE_EOF;
+ while (rc == 0) {
+ rc = llapi_layout_comp_extent_get(layout, &start, &end);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s: llapi_layout_comp_extent_get failed: %s.\n",
+ progname, strerror(errno));
+ goto error;
+ }
+
+ if (start > chunks[idx].chunk.e_start ||
+ end <= chunks[idx].chunk.e_start)
+ goto next;
+
+ if (end < chunks[idx].chunk.e_end)
+ chunks[idx].chunk.e_end = end;
+
+ rc = llapi_layout_comp_flags_get(layout, &flags);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s: llapi_layout_comp_flags_get failed: %s.\n",
+ progname, strerror(errno));
+ goto error;
+ }
+
+ if (flags & LCME_FL_STALE || flags & LCME_FL_OFFLINE)
+ goto next;
+
+ rc = llapi_layout_mirror_id_get(layout, &mirror_id);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s: llapi_layout_mirror_id_get failed: %s.\n",
+ progname, strerror(errno));
+ goto error;
+ }
+
+ chunks[idx].mirror_id[i] = mirror_id;
+ i++;
+ if (i >= ARRAY_SIZE(chunks[idx].mirror_id)) {
+ fprintf(stderr,
+ "%s: mirror_id array is too small.\n",
+ progname);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ next:
+ rc = llapi_layout_comp_use(layout,
+ LLAPI_LAYOUT_COMP_USE_NEXT);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s: move to the next layout component: %s.\n",
+ progname, strerror(errno));
+ goto error;
+ }
+ } /* loop through all components */
+
+ chunks[idx].mirror_count = i;
+
+ if (chunks[idx].chunk.e_end == LUSTRE_EOF)
+ break;
+
+ idx++;
+ if (idx >= chunks_size) {
+ fprintf(stderr, "%s: chunks array is too small.\n",
+ progname);
+ rc = -EINVAL;
+ goto error;
+ }
+
+ chunks[idx].chunk.e_start = chunks[idx - 1].chunk.e_end;