+static int lfs_data_version(int argc, char **argv)
+{
+ char *path;
+ __u64 data_version;
+ int fd;
+ int rc;
+ int c;
+ int nolock = 0;
+
+ if (argc < 2)
+ return CMD_HELP;
+
+ optind = 0;
+ while ((c = getopt(argc, argv, "n")) != -1) {
+ switch (c) {
+ case 'n':
+ nolock = LL_DV_NOFLUSH;
+ break;
+ default:
+ return CMD_HELP;
+ }
+ }
+ if (optind == argc)
+ return CMD_HELP;
+
+ path = argv[optind];
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "can't open %s: %s\n", path,
+ strerror(errno));
+ return errno;
+ }
+
+ rc = llapi_get_data_version(fd, &data_version, nolock);
+ if (rc) {
+ fprintf(stderr, "can't get version for %s: %s\n", path,
+ strerror(errno = -rc));
+ } else
+ printf(LPU64 "\n", 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[] = {
+ {"lost", 0, 0, 'l'},
+ {"norelease", 0, 0, 'r'},
+ {"noarchive", 0, 0, 'a'},
+ {"archived", 0, 0, 'A'},
+ {"dirty", 0, 0, 'd'},
+ {"exists", 0, 0, 'e'},
+ {0, 0, 0, 0}
+ };
+ char short_opts[] = "lraAde";
+ __u64 mask = 0;
+ int c, rc;
+ char *path;
+
+ if (argc < 3)
+ return CMD_HELP;
+
+ optind = 0;
+ 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 '?':
+ 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, 0);
+ 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("("LPX64 " bytes moved)\n", he.length);
+ else if ((he.offset + he.length) == OBD_OBJECT_EOF)
+ printf("(from "LPX64 " to EOF)\n", he.offset);
+ else
+ printf("(from "LPX64 " to "LPX64")\n",
+ he.offset, 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(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;
+ }
+ /* 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;
+}
+
+static int lfs_hsm_request(int argc, char **argv, int action)
+{
+ struct option long_opts[] = {
+ {"filelist", 1, 0, 'l'},
+ {"data", 1, 0, 'D'},
+ {"archive", 1, 0, 'a'},
+ {0, 0, 0, 0}
+ };
+ dev_t last_dev = 0;
+ char short_opts[] = "l:D:a:";
+ 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[PATH_MAX+1] = "";
+ int rc;
+
+ if (argc < 2)
+ return CMD_HELP;
+
+ optind = 0;
+ 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) {
+ fprintf(stderr,
+ "error: -a is supported only "
+ "when archiving\n");
+ return CMD_HELP;
+ }
+ archive_id = atoi(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)
+ strcpy(some_file, argv[optind]);
+
+ for (i = 0; i < nbfile; i++) {
+ hur->hur_user_item[i].hui_extent.length = -1;
+ rc = lfs_hsm_prepare_file(argv[optind + i],
+ &hur->hur_user_item[i].hui_fid,
+ &last_dev);
+ hur->hur_request.hr_itemcount++;
+ 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) {
+ struct hsm_user_item *hui;
+
+ /* If allocated buffer was too small, gets something
+ * bigger */
+ if (nbfile_alloc <= hur->hur_request.hr_itemcount) {
+ nbfile_alloc = nbfile_alloc * 2 + 1;
+ oldhur = hur;
+ hur = llapi_hsm_user_request_alloc(nbfile_alloc,
+ opaque_len);
+ if (hur == NULL) {
+ fprintf(stderr, "Cannot allocate "
+ "the request: %s\n",
+ strerror(errno));
+ hur = oldhur;
+ rc = -errno;
+ goto out_free;
+ }
+ memcpy(hur, oldhur, hur_len(oldhur));
+ free(oldhur);
+ }
+
+ /* Chop CR */
+ if (line[strlen(line) - 1] == '\n')
+ line[strlen(line) - 1] = '\0';
+
+ hui =
+ &hur->hur_user_item[hur->hur_request.hr_itemcount];
+ hui->hui_extent.length = -1;
+ rc = lfs_hsm_prepare_file(line, &hui->hui_fid,
+ &last_dev);
+ hur->hur_request.hr_itemcount++;
+ if (rc)
+ goto out_free;
+
+ if ((some_file[0] == '\0') &&
+ (strlen(line) < sizeof(some_file)))
+ strcpy(some_file, line);
+ }
+
+ rc = fclose(fp);
+ if (line)
+ 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(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);
+}
+