+
+ if (!show_default) {
+ name = argv[optind++];
+ switch (qctl.qc_type) {
+ case USRQUOTA:
+ rc = name2uid(&qctl.qc_id, name);
+ break;
+ case GRPQUOTA:
+ rc = name2gid(&qctl.qc_id, name);
+ break;
+ case PRJQUOTA:
+ rc = name2projid(&qctl.qc_id, name);
+ break;
+ default:
+ rc = -ENOTSUP;
+ break;
+ }
+ } else {
+ qctl.qc_valid = QC_GENERAL;
+ qctl.qc_cmd = LUSTRE_Q_GETDEFAULT;
+ qctl.qc_id = 0;
+ }
+
+ if (rc) {
+ qctl.qc_id = strtoul(name, &endptr, 10);
+ if (*endptr != '\0') {
+ fprintf(stderr, "%s quota: invalid id '%s'\n",
+ progname, name);
+ return CMD_HELP;
+ }
+ }
+ } else if (optind + 1 != argc || qctl.qc_type == ALLQUOTA) {
+ fprintf(stderr, "%s quota: missing quota info argument(s)\n",
+ progname);
+ return CMD_HELP;
+ }
+
+ mnt = argv[optind];
+ rc = get_print_quota(mnt, name, &qctl, verbose, quiet,
+ human_readable, show_default);
+ return rc;
+}
+#endif /* HAVE_SYS_QUOTA_H! */
+
+static int flushctx_ioctl(char *mp)
+{
+ int fd, rc;
+
+ fd = open(mp, O_RDONLY);
+ if (fd == -1) {
+ fprintf(stderr, "flushctx: error open %s: %s\n",
+ mp, strerror(errno));
+ return -1;
+ }
+
+ rc = ioctl(fd, LL_IOC_FLUSHCTX);
+ if (rc == -1)
+ fprintf(stderr, "flushctx: error ioctl %s: %s\n",
+ mp, strerror(errno));
+
+ close(fd);
+ return rc;
+}
+
+static int lfs_flushctx(int argc, char **argv)
+{
+ int kdestroy = 0, c;
+ char mntdir[PATH_MAX] = {'\0'};
+ int index = 0;
+ int rc = 0;
+
+ while ((c = getopt(argc, argv, "k")) != -1) {
+ switch (c) {
+ case 'k':
+ kdestroy = 1;
+ break;
+ default:
+ fprintf(stderr, "error: %s: option '-%c' "
+ "unrecognized\n", argv[0], c);
+ return CMD_HELP;
+ }
+ }
+
+ if (kdestroy) {
+ if ((rc = system("kdestroy > /dev/null")) != 0) {
+ rc = WEXITSTATUS(rc);
+ fprintf(stderr, "error destroying tickets: %d, continuing\n", rc);
+ }
+ }
+
+ if (optind >= argc) {
+ /* flush for all mounted lustre fs. */
+ while (!llapi_search_mounts(NULL, index++, mntdir, NULL)) {
+ /* Check if we have a mount point */
+ if (mntdir[0] == '\0')
+ continue;
+
+ if (flushctx_ioctl(mntdir))
+ rc = -1;
+
+ mntdir[0] = '\0'; /* avoid matching in next loop */
+ }
+ } else {
+ /* flush fs as specified */
+ while (optind < argc) {
+ if (flushctx_ioctl(argv[optind++]))
+ rc = -1;
+ }
+ }
+ return rc;
+}
+
+static int lfs_changelog(int argc, char **argv)
+{
+ void *changelog_priv;
+ struct changelog_rec *rec;
+ long long startrec = 0, endrec = 0;
+ char *mdd;
+ struct option long_opts[] = {
+ { .val = 'f', .name = "follow", .has_arg = no_argument },
+ { .name = NULL } };
+ char short_opts[] = "f";
+ int rc, follow = 0;
+
+ while ((rc = getopt_long(argc, argv, short_opts,
+ long_opts, NULL)) != -1) {
+ switch (rc) {
+ case 'f':
+ follow++;
+ break;
+ default:
+ fprintf(stderr,
+ "%s changelog: unrecognized option '%s'\n",
+ progname, argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+ if (optind >= argc) {
+ fprintf(stderr, "%s changelog: mdtname must be specified\n",
+ progname);
+ return CMD_HELP;
+ }
+
+ mdd = argv[optind++];
+ if (argc > optind)
+ startrec = strtoll(argv[optind++], NULL, 10);
+ if (argc > optind)
+ endrec = strtoll(argv[optind++], NULL, 10);
+
+ rc = llapi_changelog_start(&changelog_priv,
+ CHANGELOG_FLAG_BLOCK |
+ CHANGELOG_FLAG_JOBID |
+ CHANGELOG_FLAG_EXTRA_FLAGS |
+ (follow ? CHANGELOG_FLAG_FOLLOW : 0),
+ mdd, startrec);
+ if (rc < 0) {
+ fprintf(stderr, "%s changelog: cannot start changelog: %s\n",
+ progname, strerror(errno = -rc));
+ return rc;
+ }
+
+ rc = llapi_changelog_set_xflags(changelog_priv,
+ CHANGELOG_EXTRA_FLAG_UIDGID |
+ CHANGELOG_EXTRA_FLAG_NID |
+ CHANGELOG_EXTRA_FLAG_OMODE |
+ CHANGELOG_EXTRA_FLAG_XATTR);
+ if (rc < 0) {
+ fprintf(stderr,
+ "%s changelog: cannot set xflags for changelog: %s\n",
+ progname, strerror(errno = -rc));
+ return rc;
+ }
+
+ while ((rc = llapi_changelog_recv(changelog_priv, &rec)) == 0) {
+ time_t secs;
+ struct tm ts;
+
+ if (endrec && rec->cr_index > endrec) {
+ llapi_changelog_free(&rec);
+ break;
+ }
+ if (rec->cr_index < startrec) {
+ llapi_changelog_free(&rec);
+ continue;
+ }
+
+ secs = rec->cr_time >> 30;
+ gmtime_r(&secs, &ts);
+ printf("%ju %02d%-5s %02d:%02d:%02d.%09d %04d.%02d.%02d "
+ "0x%x t="DFID, (uintmax_t)rec->cr_index, rec->cr_type,
+ changelog_type2str(rec->cr_type),
+ ts.tm_hour, ts.tm_min, ts.tm_sec,
+ (int)(rec->cr_time & ((1 << 30) - 1)),
+ ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday,
+ rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
+
+ if (rec->cr_flags & CLF_JOBID) {
+ struct changelog_ext_jobid *jid =
+ changelog_rec_jobid(rec);
+
+ if (jid->cr_jobid[0] != '\0')
+ printf(" j=%s", jid->cr_jobid);
+ }
+
+ if (rec->cr_flags & CLF_EXTRA_FLAGS) {
+ struct changelog_ext_extra_flags *ef =
+ changelog_rec_extra_flags(rec);
+
+ printf(" ef=0x%llx", ef->cr_extra_flags);
+
+ if (ef->cr_extra_flags & CLFE_UIDGID) {
+ struct changelog_ext_uidgid *uidgid =
+ changelog_rec_uidgid(rec);
+
+ printf(" u=%llu:%llu",
+ uidgid->cr_uid, uidgid->cr_gid);
+ }
+ if (ef->cr_extra_flags & CLFE_NID) {
+ struct changelog_ext_nid *nid =
+ changelog_rec_nid(rec);
+
+ printf(" nid=%s",
+ libcfs_nid2str(nid->cr_nid));
+ }
+
+ if (ef->cr_extra_flags & CLFE_OPEN) {
+ struct changelog_ext_openmode *omd =
+ changelog_rec_openmode(rec);
+ char mode[] = "---";
+
+ /* exec mode must be exclusive */
+ if (omd->cr_openflags & MDS_FMODE_EXEC) {
+ mode[2] = 'x';
+ } else {
+ if (omd->cr_openflags & MDS_FMODE_READ)
+ mode[0] = 'r';
+ if (omd->cr_openflags &
+ (MDS_FMODE_WRITE |
+ MDS_OPEN_TRUNC |
+ MDS_OPEN_APPEND))
+ mode[1] = 'w';
+ }
+
+ if (strcmp(mode, "---") != 0)
+ printf(" m=%s", mode);
+
+ }
+
+ if (ef->cr_extra_flags & CLFE_XATTR) {
+ struct changelog_ext_xattr *xattr =
+ changelog_rec_xattr(rec);
+
+ if (xattr->cr_xattr[0] != '\0')
+ printf(" x=%s", xattr->cr_xattr);
+ }
+ }
+
+ if (rec->cr_namelen)
+ printf(" p="DFID" %.*s", PFID(&rec->cr_pfid),
+ rec->cr_namelen, changelog_rec_name(rec));
+
+ if (rec->cr_flags & CLF_RENAME) {
+ struct changelog_ext_rename *rnm =
+ changelog_rec_rename(rec);
+
+ if (!fid_is_zero(&rnm->cr_sfid))
+ printf(" s="DFID" sp="DFID" %.*s",
+ PFID(&rnm->cr_sfid),
+ PFID(&rnm->cr_spfid),
+ (int)changelog_rec_snamelen(rec),
+ changelog_rec_sname(rec));
+ }
+ printf("\n");
+
+ llapi_changelog_free(&rec);
+ }
+
+ llapi_changelog_fini(&changelog_priv);
+
+ if (rc < 0)
+ fprintf(stderr, "%s changelog: cannot access changelog: %s\n",
+ progname, strerror(errno = -rc));
+
+ return (rc == 1 ? 0 : rc);
+}
+
+static int lfs_changelog_clear(int argc, char **argv)
+{
+ long long endrec;
+ int rc;
+
+ if (argc != 4)
+ return CMD_HELP;
+
+ endrec = strtoll(argv[3], NULL, 10);
+
+ rc = llapi_changelog_clear(argv[1], argv[2], endrec);
+
+ if (rc == -EINVAL)
+ fprintf(stderr, "%s: record out of range: %llu\n",
+ argv[0], endrec);
+ else if (rc == -ENOENT)
+ fprintf(stderr, "%s: no changelog user: %s\n",
+ argv[0], argv[2]);
+ else if (rc)
+ fprintf(stderr, "%s error: %s\n", argv[0],
+ strerror(-rc));
+
+ if (rc)
+ errno = -rc;
+
+ return rc;
+}
+
+static int lfs_fid2path(int argc, char **argv)
+{
+ struct option long_opts[] = {
+ { .val = 'c', .name = "cur", .has_arg = no_argument },
+ { .val = 'l', .name = "link", .has_arg = required_argument },
+ { .val = 'r', .name = "rec", .has_arg = required_argument },
+ { .name = NULL } };
+ char short_opts[] = "cl:r:";
+ char *device, *fid, *path;
+ long long recno = -1;
+ int linkno = -1;
+ int lnktmp;
+ int printcur = 0;
+ int rc = 0;
+ char *endptr = NULL;
+
+ while ((rc = getopt_long(argc, argv, short_opts,
+ long_opts, NULL)) != -1) {
+ switch (rc) {
+ case 'c':
+ printcur++;
+ break;
+ case 'l':
+ linkno = strtol(optarg, &endptr, 10);
+ if (*endptr != '\0') {
+ fprintf(stderr,
+ "%s fid2path: invalid linkno '%s'\n",
+ progname, optarg);
+ return CMD_HELP;
+ }
+ break;
+ case 'r':
+ recno = strtoll(optarg, &endptr, 10);
+ if (*endptr != '\0') {
+ fprintf(stderr,
+ "%s fid2path: invalid recno '%s'\n",
+ progname, optarg);
+ return CMD_HELP;
+ }
+ break;
+ default:
+ fprintf(stderr,
+ "%s fid2path: unrecognized option '%s'\n",
+ progname, argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+
+ if (argc < 3) {
+ fprintf(stderr,
+ "%s fid2path: <fsname|rootpath> and <fid>... must be specified\n",
+ progname);
+ return CMD_HELP;
+ }
+
+ device = argv[optind++];
+ path = calloc(1, PATH_MAX);
+ if (path == NULL) {
+ rc = -errno;
+ fprintf(stderr,
+ "%s fid2path: cannot allocate memory for path: %s\n",
+ progname, strerror(-rc));
+ return rc;
+ }
+
+ rc = 0;
+ while (optind < argc) {
+ fid = argv[optind++];
+
+ lnktmp = (linkno >= 0) ? linkno : 0;
+ while (1) {
+ int oldtmp = lnktmp;
+ long long rectmp = recno;
+ int rc2;
+ rc2 = llapi_fid2path(device, fid, path, PATH_MAX,
+ &rectmp, &lnktmp);
+ if (rc2 < 0) {
+ fprintf(stderr,
+ "%s fid2path: cannot find '%s': %s\n",
+ progname, fid, strerror(errno = -rc2));
+ if (rc == 0)
+ rc = rc2;
+ break;
+ }
+
+ if (printcur)
+ fprintf(stdout, "%lld ", rectmp);
+ if (device[0] == '/') {
+ fprintf(stdout, "%s", device);
+ if (device[strlen(device) - 1] != '/')
+ fprintf(stdout, "/");
+ } else if (path[0] == '\0') {
+ fprintf(stdout, "/");
+ }
+ fprintf(stdout, "%s\n", path);
+
+ if (linkno >= 0)
+ /* specified linkno */
+ break;
+ if (oldtmp == lnktmp)
+ /* no more links */
+ break;
+ }
+ }
+
+ free(path);
+ return rc;
+}
+
+static int lfs_path2fid(int argc, char **argv)
+{
+ struct option long_opts[] = {
+ { .val = 'p', .name = "parents", .has_arg = no_argument },
+ { .name = NULL } };
+ char **path;
+ const char short_opts[] = "p";
+ const char *sep = "";
+ struct lu_fid fid;
+ int rc = 0;
+ bool show_parents = false;
+
+ while ((rc = getopt_long(argc, argv, short_opts,
+ long_opts, NULL)) != -1) {
+ switch (rc) {
+ case 'p':
+ show_parents = true;
+ break;
+ default:
+ fprintf(stderr,
+ "%s path2fid: unrecognized option '%s'\n",
+ progname, argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+
+ if (optind > argc - 1) {
+ fprintf(stderr, "%s path2fid: FILE... must be specified\n",
+ progname);
+ return CMD_HELP;
+ }
+ else if (optind < argc - 1)
+ sep = ": ";
+
+ rc = 0;
+ for (path = argv + optind; *path != NULL; path++) {
+ int err = 0;
+ if (!show_parents) {
+ err = llapi_path2fid(*path, &fid);
+ if (!err)
+ printf("%s%s"DFID"\n",
+ *sep != '\0' ? *path : "", sep,
+ PFID(&fid));
+ } else {
+ char name[NAME_MAX + 1];
+ unsigned int linkno = 0;
+
+ while ((err = llapi_path2parent(*path, linkno, &fid,
+ name, sizeof(name))) == 0) {
+ if (*sep != '\0' && linkno == 0)
+ printf("%s%s", *path, sep);
+
+ printf("%s"DFID"/%s", linkno != 0 ? "\t" : "",
+ PFID(&fid), name);
+ linkno++;
+ }
+
+ /* err == -ENODATA is end-of-loop */
+ if (linkno > 0 && err == -ENODATA) {
+ printf("\n");
+ err = 0;
+ }
+ }
+
+ if (err) {
+ fprintf(stderr,
+ "%s path2fid: cannot get %sfid for '%s': %s\n",
+ progname, show_parents ? "parent " : "", *path,
+ strerror(-err));
+ if (rc == 0) {
+ rc = err;
+ errno = -err;
+ }
+ }
+ }
+
+ return rc;
+}
+
+static int lfs_data_version(int argc, char **argv)
+{
+ char *path;
+ __u64 data_version;
+ int fd;
+ int rc;
+ int c;
+ int data_version_flags = LL_DV_RD_FLUSH; /* Read by default */
+
+ if (argc < 2) {
+ fprintf(stderr, "%s data_version: FILE must be specified\n",
+ progname);
+ return CMD_HELP;
+ }
+
+ while ((c = getopt(argc, argv, "nrw")) != -1) {
+ switch (c) {
+ case 'n':
+ data_version_flags = 0;
+ break;
+ case 'r':
+ data_version_flags |= LL_DV_RD_FLUSH;
+ break;
+ case 'w':
+ data_version_flags |= LL_DV_WR_FLUSH;
+ break;
+ default:
+ fprintf(stderr,
+ "%s data_version: unrecognized option '%s'\n",
+ progname, argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+ if (optind == argc) {
+ fprintf(stderr, "%s data_version: FILE must be specified\n",
+ progname);
+ return CMD_HELP;
+ }
+
+ path = argv[optind];
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ rc = -errno;
+ fprintf(stderr, "%s data_version: cannot open file '%s': %s\n",
+ progname, path, strerror(-rc));
+ return rc;
+ }
+
+ rc = llapi_get_data_version(fd, &data_version, data_version_flags);
+ if (rc < 0)
+ fprintf(stderr,
+ "%s data_version: cannot get version for '%s': %s\n",
+ progname, path, strerror(-rc));
+ else
+ printf("%ju" "\n", (uintmax_t)data_version);
+
+ close(fd);
+ return rc;
+}
+
+static int lfs_hsm_state(int argc, char **argv)
+{
+ int rc;
+ int i = 1;
+ char *path;
+ struct hsm_user_state hus;
+
+ if (argc < 2)
+ return CMD_HELP;
+
+ do {
+ path = argv[i];
+
+ rc = llapi_hsm_state_get(path, &hus);
+ if (rc) {
+ fprintf(stderr, "can't get hsm state for %s: %s\n",
+ path, strerror(errno = -rc));
+ return rc;
+ }
+
+ /* Display path name and status flags */
+ printf("%s: (0x%08x)", path, hus.hus_states);
+
+ if (hus.hus_states & HS_RELEASED)
+ printf(" released");
+ if (hus.hus_states & HS_EXISTS)
+ printf(" exists");
+ if (hus.hus_states & HS_DIRTY)
+ printf(" dirty");
+ if (hus.hus_states & HS_ARCHIVED)
+ printf(" archived");
+ /* Display user-settable flags */
+ if (hus.hus_states & HS_NORELEASE)
+ printf(" never_release");
+ if (hus.hus_states & HS_NOARCHIVE)
+ printf(" never_archive");
+ if (hus.hus_states & HS_LOST)
+ printf(" lost_from_hsm");
+
+ if (hus.hus_archive_id != 0)
+ printf(", archive_id:%d", hus.hus_archive_id);
+ printf("\n");
+
+ } while (++i < argc);
+
+ return 0;
+}
+
+#define LFS_HSM_SET 0
+#define LFS_HSM_CLEAR 1
+
+/**
+ * Generic function to set or clear HSM flags.
+ * Used by hsm_set and hsm_clear.
+ *
+ * @mode if LFS_HSM_SET, set the flags, if LFS_HSM_CLEAR, clear the flags.
+ */
+static int lfs_hsm_change_flags(int argc, char **argv, int mode)
+{
+ struct option long_opts[] = {
+ { .val = 'A', .name = "archived", .has_arg = no_argument },
+ { .val = 'a', .name = "noarchive", .has_arg = no_argument },
+ { .val = 'd', .name = "dirty", .has_arg = no_argument },
+ { .val = 'e', .name = "exists", .has_arg = no_argument },
+ { .val = 'l', .name = "lost", .has_arg = no_argument },
+ { .val = 'r', .name = "norelease", .has_arg = no_argument },
+ { .val = 'i', .name = "archive-id", .has_arg = required_argument },
+ { .name = NULL } };
+ char short_opts[] = "lraAdei:";
+ __u64 mask = 0;
+ int c, rc;
+ char *path;
+ __u32 archive_id = 0;
+ char *end = NULL;
+
+ if (argc < 3)
+ return CMD_HELP;
+
+ while ((c = getopt_long(argc, argv, short_opts,
+ long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'l':
+ mask |= HS_LOST;
+ break;
+ case 'a':
+ mask |= HS_NOARCHIVE;
+ break;
+ case 'A':
+ mask |= HS_ARCHIVED;
+ break;
+ case 'r':
+ mask |= HS_NORELEASE;
+ break;
+ case 'd':
+ mask |= HS_DIRTY;
+ break;
+ case 'e':
+ mask |= HS_EXISTS;
+ break;
+ case 'i':
+ archive_id = strtol(optarg, &end, 10);
+ if (*end != '\0') {
+ fprintf(stderr, "invalid archive_id: '%s'\n",
+ end);
+ return CMD_HELP;
+ }
+ break;
+ case '?':
+ return CMD_HELP;
+ default:
+ fprintf(stderr, "error: %s: option '%s' unrecognized\n",
+ argv[0], argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+
+ /* User should have specified a flag */
+ if (mask == 0)
+ return CMD_HELP;
+
+ while (optind < argc) {
+
+ path = argv[optind];
+
+ /* If mode == 0, this means we apply the mask. */
+ if (mode == LFS_HSM_SET)
+ rc = llapi_hsm_state_set(path, mask, 0, archive_id);
+ else
+ rc = llapi_hsm_state_set(path, 0, mask, 0);
+
+ if (rc != 0) {
+ fprintf(stderr, "Can't change hsm flags for %s: %s\n",
+ path, strerror(errno = -rc));
+ return rc;
+ }
+ optind++;
+ }
+
+ return 0;
+}
+
+static int lfs_hsm_action(int argc, char **argv)
+{
+ int rc;
+ int i = 1;
+ char *path;
+ struct hsm_current_action hca;
+ struct hsm_extent he;
+ enum hsm_user_action hua;
+ enum hsm_progress_states hps;
+
+ if (argc < 2)
+ return CMD_HELP;
+
+ do {
+ path = argv[i];
+
+ rc = llapi_hsm_current_action(path, &hca);
+ if (rc) {
+ fprintf(stderr, "can't get hsm action for %s: %s\n",
+ path, strerror(errno = -rc));
+ return rc;
+ }
+ he = hca.hca_location;
+ hua = hca.hca_action;
+ hps = hca.hca_state;
+
+ printf("%s: %s", path, hsm_user_action2name(hua));
+
+ /* Skip file without action */
+ if (hca.hca_action == HUA_NONE) {
+ printf("\n");
+ continue;
+ }
+
+ printf(" %s ", hsm_progress_state2name(hps));
+
+ if ((hps == HPS_RUNNING) &&
+ (hua == HUA_ARCHIVE || hua == HUA_RESTORE))
+ printf("(%llu bytes moved)\n",
+ (unsigned long long)he.length);
+ else if ((he.offset + he.length) == LUSTRE_EOF)
+ printf("(from %llu to EOF)\n",
+ (unsigned long long)he.offset);
+ else
+ printf("(from %llu to %llu)\n",
+ (unsigned long long)he.offset,
+ (unsigned long long)(he.offset + he.length));
+
+ } while (++i < argc);
+
+ return 0;
+}
+
+static int lfs_hsm_set(int argc, char **argv)
+{
+ return lfs_hsm_change_flags(argc, argv, LFS_HSM_SET);
+}
+
+static int lfs_hsm_clear(int argc, char **argv)
+{
+ return lfs_hsm_change_flags(argc, argv, LFS_HSM_CLEAR);
+}
+
+/**
+ * Check file state and return its fid, to be used by lfs_hsm_request().
+ *
+ * \param[in] file Path to file to check
+ * \param[in,out] fid Pointer to allocated lu_fid struct.
+ * \param[in,out] last_dev Pointer to last device id used.
+ *
+ * \return 0 on success.
+ */
+static int lfs_hsm_prepare_file(const char *file, struct lu_fid *fid,
+ dev_t *last_dev)
+{
+ struct stat st;
+ int rc;
+
+ rc = lstat(file, &st);
+ if (rc) {
+ fprintf(stderr, "Cannot stat %s: %s\n", file, strerror(errno));
+ return -errno;
+ }
+ /* Checking for regular file as archiving as posix copytool
+ * rejects archiving files other than regular files
+ */
+ if (!S_ISREG(st.st_mode)) {
+ fprintf(stderr, "error: \"%s\" is not a regular file\n", file);
+ return CMD_HELP;
+ }
+ /* A request should be ... */
+ if (*last_dev != st.st_dev && *last_dev != 0) {
+ fprintf(stderr, "All files should be "
+ "on the same filesystem: %s\n", file);
+ return -EINVAL;
+ }
+ *last_dev = st.st_dev;
+
+ rc = llapi_path2fid(file, fid);
+ if (rc) {
+ fprintf(stderr, "Cannot read FID of %s: %s\n",
+ file, strerror(-rc));
+ return rc;
+ }
+ return 0;
+}
+
+/* Fill an HSM HUR item with a given file name.
+ *
+ * If mntpath is set, then the filename is actually a FID, and no
+ * lookup on the filesystem will be performed.
+ *
+ * \param[in] hur the user request to fill
+ * \param[in] idx index of the item inside the HUR to fill
+ * \param[in] mntpath mountpoint of Lustre
+ * \param[in] fname filename (if mtnpath is NULL)
+ * or FID (if mntpath is set)
+ * \param[in] last_dev pointer to last device id used
+ *
+ * \retval 0 on success
+ * \retval CMD_HELP or a negative errno on error
+ */
+static int fill_hur_item(struct hsm_user_request *hur, unsigned int idx,
+ const char *mntpath, const char *fname,
+ dev_t *last_dev)
+{
+ struct hsm_user_item *hui = &hur->hur_user_item[idx];
+ int rc;
+
+ hui->hui_extent.length = -1;
+
+ if (mntpath != NULL) {
+ if (*fname == '[')
+ fname++;
+ rc = sscanf(fname, SFID, RFID(&hui->hui_fid));
+ if (rc == 3) {
+ rc = 0;
+ } else {
+ fprintf(stderr, "hsm: '%s' is not a valid FID\n",
+ fname);
+ rc = -EINVAL;
+ }
+ } else {
+ rc = lfs_hsm_prepare_file(fname, &hui->hui_fid, last_dev);
+ }
+
+ if (rc == 0)
+ hur->hur_request.hr_itemcount++;
+
+ return rc;
+}
+
+static int lfs_hsm_request(int argc, char **argv, int action)
+{
+ struct option long_opts[] = {
+ { .val = 'a', .name = "archive", .has_arg = required_argument },
+ { .val = 'D', .name = "data", .has_arg = required_argument },
+ { .val = 'l', .name = "filelist", .has_arg = required_argument },
+ { .val = 'm', .name = "mntpath", .has_arg = required_argument },
+ { .name = NULL } };
+ dev_t last_dev = 0;
+ char short_opts[] = "l:D:a:m:";
+ struct hsm_user_request *hur, *oldhur;
+ int c, i;
+ size_t len;
+ int nbfile;
+ char *line = NULL;
+ char *filelist = NULL;
+ char fullpath[PATH_MAX];
+ char *opaque = NULL;
+ int opaque_len = 0;
+ int archive_id = 0;
+ FILE *fp;
+ int nbfile_alloc = 0;
+ char *some_file = NULL;
+ char *mntpath = NULL;
+ int rc;
+
+ if (argc < 2)
+ return CMD_HELP;
+
+ while ((c = getopt_long(argc, argv, short_opts,
+ long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'l':
+ filelist = optarg;
+ break;
+ case 'D':
+ opaque = optarg;
+ break;
+ case 'a':
+ if (action != HUA_ARCHIVE &&
+ action != HUA_REMOVE) {
+ fprintf(stderr,
+ "error: -a is supported only "
+ "when archiving or removing\n");
+ return CMD_HELP;
+ }
+ archive_id = atoi(optarg);
+ break;
+ case 'm':
+ if (some_file == NULL) {
+ mntpath = optarg;
+ some_file = strdup(optarg);
+ }
+ break;
+ case '?':
+ return CMD_HELP;
+ default:
+ fprintf(stderr, "error: %s: option '%s' unrecognized\n",
+ argv[0], argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+
+ /* All remaining args are files, so we have at least nbfile */
+ nbfile = argc - optind;
+
+ if ((nbfile == 0) && (filelist == NULL))
+ return CMD_HELP;
+
+ if (opaque != NULL)
+ opaque_len = strlen(opaque);
+
+ /* Alloc the request structure with enough place to store all files
+ * from command line. */
+ hur = llapi_hsm_user_request_alloc(nbfile, opaque_len);
+ if (hur == NULL) {
+ fprintf(stderr, "Cannot create the request: %s\n",
+ strerror(errno));
+ return errno;
+ }
+ nbfile_alloc = nbfile;
+
+ hur->hur_request.hr_action = action;
+ hur->hur_request.hr_archive_id = archive_id;
+ hur->hur_request.hr_flags = 0;
+
+ /* All remaining args are files, add them */
+ if (nbfile != 0 && some_file == NULL)
+ some_file = strdup(argv[optind]);
+
+ for (i = 0; i < nbfile; i++) {
+ rc = fill_hur_item(hur, i, mntpath, argv[optind + i],
+ &last_dev);
+ if (rc)
+ goto out_free;
+ }
+
+ /* from here stop using nb_file, use hur->hur_request.hr_itemcount */
+
+ /* If a filelist was specified, read the filelist from it. */
+ if (filelist != NULL) {
+ fp = fopen(filelist, "r");
+ if (fp == NULL) {
+ fprintf(stderr, "Cannot read the file list %s: %s\n",
+ filelist, strerror(errno));
+ rc = -errno;
+ goto out_free;
+ }
+
+ while ((rc = getline(&line, &len, fp)) != -1) {
+ /* If allocated buffer was too small, get something
+ * larger */
+ if (nbfile_alloc <= hur->hur_request.hr_itemcount) {
+ ssize_t size;
+
+ nbfile_alloc = nbfile_alloc * 2 + 1;
+ oldhur = hur;
+ hur = llapi_hsm_user_request_alloc(nbfile_alloc,
+ opaque_len);
+ if (hur == NULL) {
+ fprintf(stderr, "hsm: cannot allocate "
+ "the request: %s\n",
+ strerror(errno));
+ hur = oldhur;
+ rc = -errno;
+ fclose(fp);
+ goto out_free;
+ }
+ size = hur_len(oldhur);
+ if (size < 0) {
+ fprintf(stderr, "hsm: cannot allocate "
+ "%u files + %u bytes data\n",
+ oldhur->hur_request.hr_itemcount,
+ oldhur->hur_request.hr_data_len);
+ free(hur);
+ hur = oldhur;
+ rc = -E2BIG;
+ fclose(fp);
+ goto out_free;
+ }
+ memcpy(hur, oldhur, size);
+ free(oldhur);
+ }
+
+ /* Chop CR */
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ rc = fill_hur_item(hur, hur->hur_request.hr_itemcount,
+ mntpath, line, &last_dev);
+ if (rc) {
+ fclose(fp);
+ goto out_free;
+ }
+
+ if (some_file == NULL) {
+ some_file = line;
+ line = NULL;
+ }
+ }
+
+ rc = fclose(fp);
+ free(line);
+ }
+
+ /* If a --data was used, add it to the request */
+ hur->hur_request.hr_data_len = opaque_len;
+ if (opaque != NULL)
+ memcpy(hur_data(hur), opaque, opaque_len);
+
+ /* Send the HSM request */
+ if (realpath(some_file, fullpath) == NULL) {
+ fprintf(stderr, "Could not find path '%s': %s\n",
+ some_file, strerror(errno));
+ }
+ rc = llapi_hsm_request(fullpath, hur);
+ if (rc) {
+ fprintf(stderr, "Cannot send HSM request (use of %s): %s\n",
+ some_file, strerror(-rc));
+ goto out_free;
+ }
+
+out_free:
+ free(some_file);
+ free(hur);
+ return rc;
+}
+
+static int lfs_hsm_archive(int argc, char **argv)
+{
+ return lfs_hsm_request(argc, argv, HUA_ARCHIVE);
+}
+
+static int lfs_hsm_restore(int argc, char **argv)
+{
+ return lfs_hsm_request(argc, argv, HUA_RESTORE);
+}
+
+static int lfs_hsm_release(int argc, char **argv)
+{
+ return lfs_hsm_request(argc, argv, HUA_RELEASE);
+}
+
+static int lfs_hsm_remove(int argc, char **argv)
+{
+ return lfs_hsm_request(argc, argv, HUA_REMOVE);
+}
+
+static int lfs_hsm_cancel(int argc, char **argv)
+{
+ return lfs_hsm_request(argc, argv, HUA_CANCEL);
+}
+
+static int lfs_swap_layouts(int argc, char **argv)
+{
+ if (argc != 3)
+ return CMD_HELP;
+
+ return llapi_swap_layouts(argv[1], argv[2], 0, 0,
+ SWAP_LAYOUTS_KEEP_MTIME |
+ SWAP_LAYOUTS_KEEP_ATIME);
+}
+
+static const char *const ladvise_names[] = LU_LADVISE_NAMES;
+
+static const char *const lock_mode_names[] = LOCK_MODE_NAMES;
+
+int lfs_get_mode(const char *string)
+{
+ enum lock_mode_user mode;
+
+ for (mode = 0; mode < ARRAY_SIZE(lock_mode_names); mode++) {
+ if (lock_mode_names[mode] == NULL)
+ continue;
+ if (strcmp(string, lock_mode_names[mode]) == 0)
+ return mode;
+ }
+
+ return -EINVAL;
+}
+
+static enum lu_ladvise_type lfs_get_ladvice(const char *string)
+{
+ enum lu_ladvise_type advice;
+
+ for (advice = 0;
+ advice < ARRAY_SIZE(ladvise_names); advice++) {
+ if (ladvise_names[advice] == NULL)
+ continue;
+ if (strcmp(string, ladvise_names[advice]) == 0)
+ return advice;
+ }
+
+ return LU_LADVISE_INVALID;
+}
+
+static int lfs_ladvise(int argc, char **argv)
+{
+ struct option long_opts[] = {
+ { .val = 'a', .name = "advice", .has_arg = required_argument },
+ { .val = 'b', .name = "background", .has_arg = no_argument },
+ { .val = 'e', .name = "end", .has_arg = required_argument },
+ { .val = 'l', .name = "length", .has_arg = required_argument },
+ { .val = 'm', .name = "mode", .has_arg = required_argument },
+ { .val = 's', .name = "start", .has_arg = required_argument },
+ { .val = 'u', .name = "unset", .has_arg = no_argument },
+ { .name = NULL } };
+ char short_opts[] = "a:be:l:m:s:u";
+ int c;
+ int rc = 0;
+ const char *path;
+ int fd;
+ struct llapi_lu_ladvise advice;
+ enum lu_ladvise_type advice_type = LU_LADVISE_INVALID;
+ unsigned long long start = 0;
+ unsigned long long end = LUSTRE_EOF;
+ unsigned long long length = 0;
+ unsigned long long size_units;
+ unsigned long long flags = 0;
+ int mode = 0;
+
+ optind = 0;
+ while ((c = getopt_long(argc, argv, short_opts,
+ long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'a':
+ advice_type = lfs_get_ladvice(optarg);
+ if (advice_type == LU_LADVISE_INVALID) {
+ fprintf(stderr, "%s: invalid advice type "
+ "'%s'\n", argv[0], optarg);
+ fprintf(stderr, "Valid types:");
+
+ for (advice_type = 0;
+ advice_type < ARRAY_SIZE(ladvise_names);
+ advice_type++) {
+ if (ladvise_names[advice_type] == NULL)
+ continue;
+ fprintf(stderr, " %s",
+ ladvise_names[advice_type]);
+ }
+ fprintf(stderr, "\n");
+
+ return CMD_HELP;
+ }
+ break;
+ case 'b':
+ flags |= LF_ASYNC;
+ break;
+ case 'u':
+ flags |= LF_UNSET;
+ break;
+ case 'e':
+ size_units = 1;
+ rc = llapi_parse_size(optarg, &end,
+ &size_units, 0);
+ if (rc) {
+ fprintf(stderr, "%s: bad end offset '%s'\n",
+ argv[0], optarg);
+ return CMD_HELP;
+ }
+ break;
+ case 's':
+ size_units = 1;
+ rc = llapi_parse_size(optarg, &start,
+ &size_units, 0);
+ if (rc) {
+ fprintf(stderr, "%s: bad start offset "
+ "'%s'\n", argv[0], optarg);
+ return CMD_HELP;
+ }
+ break;
+ case 'l':
+ size_units = 1;
+ rc = llapi_parse_size(optarg, &length,
+ &size_units, 0);
+ if (rc) {
+ fprintf(stderr, "%s: bad length '%s'\n",
+ argv[0], optarg);
+ return CMD_HELP;
+ }
+ break;
+ case 'm':
+ mode = lfs_get_mode(optarg);
+ if (mode < 0) {
+ fprintf(stderr, "%s: bad mode '%s', valid "
+ "modes are READ or WRITE\n",
+ argv[0], optarg);
+ return CMD_HELP;
+ }
+ break;
+ case '?':
+ return CMD_HELP;
+ default:
+ fprintf(stderr, "%s: option '%s' unrecognized\n",
+ argv[0], argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+
+ if (advice_type == LU_LADVISE_INVALID) {
+ fprintf(stderr, "%s: please give an advice type\n", argv[0]);
+ fprintf(stderr, "Valid types:");
+ for (advice_type = 0; advice_type < ARRAY_SIZE(ladvise_names);
+ advice_type++) {
+ if (ladvise_names[advice_type] == NULL)
+ continue;
+ fprintf(stderr, " %s", ladvise_names[advice_type]);
+ }
+ fprintf(stderr, "\n");
+ return CMD_HELP;
+ }
+
+ if (advice_type == LU_LADVISE_LOCKNOEXPAND) {
+ fprintf(stderr, "%s: Lock no expand advice is a per file "
+ "descriptor advice, so when called from lfs, "
+ "it does nothing.\n", argv[0]);
+ return CMD_HELP;
+ }
+
+ if (argc <= optind) {
+ fprintf(stderr, "%s: please give one or more file names\n",
+ argv[0]);
+ return CMD_HELP;
+ }
+
+ if (end != LUSTRE_EOF && length != 0 && end != start + length) {
+ fprintf(stderr, "%s: conflicting arguments of -l and -e\n",
+ argv[0]);
+ return CMD_HELP;
+ }
+
+ if (end == LUSTRE_EOF && length != 0)
+ end = start + length;
+
+ if (end <= start) {
+ fprintf(stderr, "%s: range [%llu, %llu] is invalid\n",
+ argv[0], start, end);
+ return CMD_HELP;
+ }
+
+ if (advice_type != LU_LADVISE_LOCKAHEAD && mode != 0) {
+ fprintf(stderr, "%s: mode is only valid with lockahead\n",
+ argv[0]);
+ return CMD_HELP;
+ }
+
+ if (advice_type == LU_LADVISE_LOCKAHEAD && mode == 0) {
+ fprintf(stderr, "%s: mode is required with lockahead\n",
+ argv[0]);
+ return CMD_HELP;
+ }
+
+ while (optind < argc) {
+ int rc2;
+
+ path = argv[optind++];
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s: cannot open file '%s': %s\n",
+ argv[0], path, strerror(errno));
+ rc2 = -errno;
+ goto next;
+ }
+
+ advice.lla_start = start;
+ advice.lla_end = end;
+ advice.lla_advice = advice_type;
+ advice.lla_value1 = 0;
+ advice.lla_value2 = 0;
+ advice.lla_value3 = 0;
+ advice.lla_value4 = 0;
+ if (advice_type == LU_LADVISE_LOCKAHEAD) {
+ advice.lla_lockahead_mode = mode;
+ advice.lla_peradvice_flags = flags;
+ }
+
+ rc2 = llapi_ladvise(fd, flags, 1, &advice);
+ close(fd);
+ if (rc2 < 0) {
+ fprintf(stderr, "%s: cannot give advice '%s' to file "
+ "'%s': %s\n", argv[0],
+ ladvise_names[advice_type],
+ path, strerror(errno));
+
+ goto next;
+ }
+
+next:
+ if (rc == 0 && rc2 < 0)
+ rc = rc2;
+ }
+ return rc;
+}
+
+static const char *const heat_names[] = LU_HEAT_NAMES;
+
+static int lfs_heat_get(int argc, char **argv)
+{
+ struct lu_heat *heat;
+ int rc = 0, rc2;
+ char *path;
+ int fd;
+ int i;
+
+ if (argc <= 1)
+ return CMD_HELP;
+
+ heat = calloc(sizeof(*heat) + sizeof(__u64) * OBD_HEAT_COUNT, 1);
+ if (!heat) {
+ fprintf(stderr, "%s: memory allocation failed\n", argv[0]);
+ return -ENOMEM;
+ }
+
+ optind = 1;
+ while (optind < argc) {
+ path = argv[optind++];
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s: cannot open file '%s': %s\n",
+ argv[0], path, strerror(errno));
+ rc2 = -errno;
+ goto next;
+ }
+
+ heat->lh_count = OBD_HEAT_COUNT;
+ rc2 = llapi_heat_get(fd, heat);
+ close(fd);
+ if (rc2 < 0) {
+ fprintf(stderr, "%s: cannot get heat of file '%s'"
+ ": %s\n", argv[0], path, strerror(errno));
+ goto next;
+ }
+
+ printf("flags: %x\n", heat->lh_flags);
+ for (i = 0; i < heat->lh_count; i++)
+ printf("%s: %llu\n", heat_names[i], heat->lh_heat[i]);
+next:
+ if (rc == 0 && rc2 < 0)
+ rc = rc2;
+ }
+
+ free(heat);
+ return rc;
+}
+
+static int lfs_heat_set(int argc, char **argv)
+{
+ struct option long_opts[] = {
+ { .val = 'c', .name = "clear", .has_arg = no_argument },
+ { .val = 'o', .name = "off", .has_arg = no_argument },
+ { .val = 'O', .name = "on", .has_arg = no_argument },
+ { .name = NULL } };
+ enum lu_heat_flag flags = 0;
+ int rc = 0, rc2;
+ char *path;
+ int fd;
+ int c;
+
+ if (argc <= 1)
+ return CMD_HELP;
+
+ optind = 0;
+ while ((c = getopt_long(argc, argv, "coO", long_opts, NULL)) != -1) {
+ switch (c) {
+ case 'c':
+ flags |= LU_HEAT_FLAG_CLEAR;
+ break;
+ case 'o':
+ flags |= LU_HEAT_FLAG_CLEAR;
+ flags |= LU_HEAT_FLAG_OFF;
+ break;
+ case 'O':
+ flags &= ~LU_HEAT_FLAG_OFF;
+ break;
+ case '?':
+ return CMD_HELP;
+ default:
+ fprintf(stderr, "%s: option '%s' unrecognized\n",
+ argv[0], argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+
+ if (argc <= optind) {
+ fprintf(stderr, "%s: please give one or more file names\n",
+ argv[0]);
+ return CMD_HELP;
+ }
+
+ while (optind < argc) {
+ path = argv[optind++];
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s: cannot open file '%s': %s\n",
+ argv[0], path, strerror(errno));
+ rc2 = -errno;
+ goto next;
+ }
+
+ rc2 = llapi_heat_set(fd, flags);
+ close(fd);
+ if (rc2 < 0) {
+ fprintf(stderr, "%s: cannot setflags heat of file '%s'"
+ ": %s\n", argv[0], path, strerror(errno));
+ goto next;
+ }
+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;