+ while ((c = getopt(argc, argv, "gGi:I:o:pPqtuUvh")) != -1) {
+ switch (c) {
+ case 'U':
+ show_default = true;
+ case 'u':
+ qtype = USRQUOTA;
+ goto quota_type;
+ case 'G':
+ show_default = true;
+ case 'g':
+ qtype = GRPQUOTA;
+ goto quota_type;
+ case 'P':
+ show_default = true;
+ case 'p':
+ qtype = PRJQUOTA;
+quota_type:
+ if (qctl.qc_type != ALLQUOTA) {
+ fprintf(stderr,
+ "%s quota: only one of -u, -g, or -p may be specified\n",
+ progname);
+ return CMD_HELP;
+ }
+ qctl.qc_type = qtype;
+ break;
+ case 't':
+ qctl.qc_cmd = LUSTRE_Q_GETINFO;
+ break;
+ case 'o':
+ valid = qctl.qc_valid = QC_UUID;
+ snprintf(obd_uuid, sizeof(qctl.obd_uuid), "%s", optarg);
+ break;
+ case 'i':
+ valid = qctl.qc_valid = QC_MDTIDX;
+ idx = qctl.qc_idx = atoi(optarg);
+ if (idx == 0 && *optarg != '0') {
+ fprintf(stderr,
+ "%s quota: invalid MDT index '%s'\n",
+ progname, optarg);
+ return CMD_HELP;
+ }
+ break;
+ case 'I':
+ valid = qctl.qc_valid = QC_OSTIDX;
+ idx = qctl.qc_idx = atoi(optarg);
+ if (idx == 0 && *optarg != '0') {
+ fprintf(stderr,
+ "%s quota: invalid OST index '%s'\n",
+ progname, optarg);
+ return CMD_HELP;
+ }
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'h':
+ human_readable = true;
+ break;
+ default:
+ fprintf(stderr, "%s quota: unrecognized option '%s'\n",
+ progname, argv[optind - 1]);
+ return CMD_HELP;
+ }
+ }
+
+ /* current uid/gid info for "lfs quota /path/to/lustre/mount" */
+ if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && qctl.qc_type == ALLQUOTA &&
+ optind == argc - 1 && !show_default) {
+
+ qctl.qc_cmd = LUSTRE_Q_GETQUOTA;
+ qctl.qc_idx = idx;
+
+ for (qtype = USRQUOTA; qtype <= GRPQUOTA; qtype++) {
+ qctl.qc_type = qtype;
+ qctl.qc_valid = valid;
+ if (qtype == USRQUOTA) {
+ qctl.qc_id = geteuid();
+ rc = uid2name(&name, qctl.qc_id);
+ } else {
+ qctl.qc_id = getegid();
+ rc = gid2name(&name, qctl.qc_id);
+ memset(&qctl.qc_dqblk, 0,
+ sizeof(qctl.qc_dqblk));
+ }
+ if (rc)
+ name = "<unknown>";
+ mnt = argv[optind];
+ rc1 = get_print_quota(mnt, name, &qctl, verbose, quiet,
+ human_readable, show_default);
+ if (rc1 && !rc)
+ rc = rc1;
+ }
+ return rc;
+ /* lfs quota -u username /path/to/lustre/mount */
+ } else if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) {
+ /* options should be followed by u/g-name and mntpoint */
+ if ((!show_default && optind + 2 != argc) ||
+ (show_default && optind + 1 != argc) ||
+ qctl.qc_type == ALLQUOTA) {
+ fprintf(stderr,
+ "%s quota: name and mount point must be specified\n",
+ progname);
+ return CMD_HELP;
+ }
+
+ 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",
+ (unsigned long long)ef->cr_extra_flags);
+
+ if (ef->cr_extra_flags & CLFE_UIDGID) {
+ struct changelog_ext_uidgid *uidgid =
+ changelog_rec_uidgid(rec);
+
+ printf(" u=%llu:%llu",
+ (unsigned long long)uidgid->cr_uid,
+ (unsigned long long)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;
+}
+
+#define MAX_ERRNO 4095
+#define IS_ERR_VALUE(x) ((unsigned long)(x) >= (unsigned long)-MAX_ERRNO)
+
+static int lfs_rmfid_and_show_errors(const char *device, struct fid_array *fa)
+{
+ int rc, rc2 = 0, k;
+
+ rc = llapi_rmfid(device, fa);
+ if (rc) {
+ fprintf(stderr, "rmfid(): rc = %d\n", rc);
+ return rc;
+ }
+
+ for (k = 0; k < fa->fa_nr; k++) {
+ rc = (__s32)fa->fa_fids[k].f_ver;
+ if (!IS_ERR_VALUE(rc))
+ continue;
+ if (!rc2 && rc)
+ rc2 = rc;
+ if (!rc)
+ continue;
+ fa->fa_fids[k].f_ver = 0;
+ fprintf(stderr, "rmfid("DFID"): rc = %d\n",
+ PFID(&fa->fa_fids[k]), rc);
+ }
+
+ return rc2;
+}
+
+static int lfs_rmfid(int argc, char **argv)
+{
+ char *fidstr, *device;
+ int rc = 0, rc2, nr;
+ struct fid_array *fa;
+
+ if (optind > argc - 1) {
+ fprintf(stderr, "%s rmfid: missing dirname\n", progname);
+ return CMD_HELP;
+ }
+
+ device = argv[optind++];
+
+ nr = argc - optind;
+ fa = malloc(offsetof(struct fid_array, fa_fids[nr + 1]));
+ if (fa == NULL)
+ return -ENOMEM;
+
+ fa->fa_nr = 0;
+ rc = 0;
+ while (optind < argc) {
+ int found;
+
+ fidstr = argv[optind++];
+ while (*fidstr == '[')
+ fidstr++;
+ found = sscanf(fidstr, SFID, RFID(&fa->fa_fids[fa->fa_nr]));
+ if (found != 3) {
+ fprintf(stderr, "unrecognized FID: %s\n",
+ argv[optind - 1]);
+ exit(1);
+ }
+ fa->fa_nr++;
+ if (fa->fa_nr == OBD_MAX_FIDS_IN_ARRAY) {
+ /* start another batch */
+ rc2 = lfs_rmfid_and_show_errors(device, fa);
+ if (rc2 && !rc)
+ rc = rc2;
+ fa->fa_nr = 0;
+ }
+ }
+ if (fa->fa_nr) {
+ rc2 = lfs_rmfid_and_show_errors(device, fa);
+ if (rc2 && !rc)
+ rc = rc2;
+ }
+
+ 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) {