Whamcloud - gitweb
LU-7503 utils: add “--verbose|-v” option to “lfs migrate -m”
[fs/lustre-release.git] / lustre / utils / lfs.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  *
30  * Copyright (c) 2011, 2015, Intel Corporation.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lustre/utils/lfs.c
37  *
38  * Author: Peter J. Braam <braam@clusterfs.com>
39  * Author: Phil Schwan <phil@clusterfs.com>
40  * Author: Robert Read <rread@clusterfs.com>
41  */
42
43 /* for O_DIRECTORY */
44 #ifndef _GNU_SOURCE
45 #define _GNU_SOURCE
46 #endif
47
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <getopt.h>
51 #include <string.h>
52 #include <mntent.h>
53 #include <unistd.h>
54 #include <errno.h>
55 #include <err.h>
56 #include <pwd.h>
57 #include <grp.h>
58 #include <sys/ioctl.h>
59 #include <sys/quota.h>
60 #include <sys/time.h>
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <fcntl.h>
64 #include <dirent.h>
65 #include <time.h>
66 #include <ctype.h>
67 #ifdef HAVE_SYS_QUOTA_H
68 # include <sys/quota.h>
69 #endif
70
71 #include <libcfs/util/string.h>
72 #include <libcfs/util/ioctl.h>
73 #include <libcfs/util/parser.h>
74 #include <lustre/lustreapi.h>
75 #include <lustre_ver.h>
76 #include <lustre_param.h>
77
78 /* all functions */
79 static int lfs_setstripe(int argc, char **argv);
80 static int lfs_find(int argc, char **argv);
81 static int lfs_getstripe(int argc, char **argv);
82 static int lfs_getdirstripe(int argc, char **argv);
83 static int lfs_setdirstripe(int argc, char **argv);
84 static int lfs_rmentry(int argc, char **argv);
85 static int lfs_osts(int argc, char **argv);
86 static int lfs_mdts(int argc, char **argv);
87 static int lfs_df(int argc, char **argv);
88 static int lfs_getname(int argc, char **argv);
89 static int lfs_check(int argc, char **argv);
90 #ifdef HAVE_SYS_QUOTA_H
91 static int lfs_setquota(int argc, char **argv);
92 static int lfs_quota(int argc, char **argv);
93 #endif
94 static int lfs_flushctx(int argc, char **argv);
95 static int lfs_join(int argc, char **argv);
96 static int lfs_lsetfacl(int argc, char **argv);
97 static int lfs_lgetfacl(int argc, char **argv);
98 static int lfs_rsetfacl(int argc, char **argv);
99 static int lfs_rgetfacl(int argc, char **argv);
100 static int lfs_cp(int argc, char **argv);
101 static int lfs_ls(int argc, char **argv);
102 static int lfs_poollist(int argc, char **argv);
103 static int lfs_changelog(int argc, char **argv);
104 static int lfs_changelog_clear(int argc, char **argv);
105 static int lfs_fid2path(int argc, char **argv);
106 static int lfs_path2fid(int argc, char **argv);
107 static int lfs_data_version(int argc, char **argv);
108 static int lfs_hsm_state(int argc, char **argv);
109 static int lfs_hsm_set(int argc, char **argv);
110 static int lfs_hsm_clear(int argc, char **argv);
111 static int lfs_hsm_action(int argc, char **argv);
112 static int lfs_hsm_archive(int argc, char **argv);
113 static int lfs_hsm_restore(int argc, char **argv);
114 static int lfs_hsm_release(int argc, char **argv);
115 static int lfs_hsm_remove(int argc, char **argv);
116 static int lfs_hsm_cancel(int argc, char **argv);
117 static int lfs_swap_layouts(int argc, char **argv);
118 static int lfs_mv(int argc, char **argv);
119
120 /* Setstripe and migrate share mostly the same parameters */
121 #define SSM_CMD_COMMON(cmd) \
122         "usage: "cmd" [--stripe-count|-c <stripe_count>]\n"             \
123         "                 [--stripe-index|-i <start_ost_idx>]\n"        \
124         "                 [--stripe-size|-S <stripe_size>]\n"           \
125         "                 [--pool|-p <pool_name>]\n"                    \
126         "                 [--ost-list|-o <ost_indices>]\n"
127
128 #define SSM_HELP_COMMON \
129         "\tstripe_size:  Number of bytes on each OST (0 filesystem default)\n" \
130         "\t              Can be specified with k, m or g (in KB, MB and GB\n" \
131         "\t              respectively)\n"                               \
132         "\tstart_ost_idx: OST index of first stripe (-1 default)\n"     \
133         "\tstripe_count: Number of OSTs to stripe over (0 default, -1 all)\n" \
134         "\tpool_name:    Name of OST pool to use (default none)\n"      \
135         "\tost_indices:  List of OST indices, can be repeated multiple times\n"\
136         "\t              Indices be specified in a format of:\n"        \
137         "\t                -o <ost_1>,<ost_i>-<ost_j>,<ost_n>\n"        \
138         "\t              Or:\n"                                         \
139         "\t                -o <ost_1> -o <ost_i>-<ost_j> -o <ost_n>\n"  \
140         "\t              If --pool is set with --ost-list, then the OSTs\n" \
141         "\t              must be the members of the pool."
142
143 #define SETSTRIPE_USAGE                                         \
144         SSM_CMD_COMMON("setstripe")                             \
145         "                 <directory|filename>\n"               \
146         SSM_HELP_COMMON
147
148 #define MIGRATE_USAGE                                                   \
149         SSM_CMD_COMMON("migrate  ")                                     \
150         "                 [--block|-b]\n"                               \
151         "                 [--non-block|-n]\n"                           \
152         "                 <filename>\n"                                 \
153         SSM_HELP_COMMON                                                 \
154         "\n"                                                            \
155         "\tblock:        Block file access during data migration (default)\n" \
156         "\tnon-block:    Abort migrations if concurrent access is detected\n" \
157
158 static const char       *progname;
159 static bool              file_lease_supported = true;
160
161 /* all available commands */
162 command_t cmdlist[] = {
163         {"setstripe", lfs_setstripe, 0,
164          "Create a new file with a specific striping pattern or\n"
165          "set the default striping pattern on an existing directory or\n"
166          "delete the default striping pattern from an existing directory\n"
167          "usage: setstripe -d <directory>   (to delete default striping)\n"\
168          " or\n"
169          SETSTRIPE_USAGE},
170         {"getstripe", lfs_getstripe, 0,
171          "To list the striping info for a given file or files in a\n"
172          "directory or recursively for all files in a directory tree.\n"
173          "usage: getstripe [--ost|-O <uuid>] [--quiet | -q] [--verbose | -v]\n"
174          "                 [--stripe-count|-c] [--stripe-index|-i]\n"
175          "                 [--pool|-p] [--stripe-size|-S] [--directory|-d]\n"
176          "                 [--mdt-index|-M] [--recursive|-r] [--raw|-R]\n"
177          "                 [--layout|-L]\n"
178          "                 <directory|filename> ..."},
179         {"setdirstripe", lfs_setdirstripe, 0,
180          "To create a striped directory on a specified MDT. This can only\n"
181          "be done on MDT0 with the right of administrator.\n"
182          "usage: setdirstripe <--count|-c stripe_count>\n"
183          "              [--index|-i mdt_index] [--hash-type|-t hash_type]\n"
184          "              [--default_stripe|-D ] [--mode|-m mode] <dir>\n"
185          "\tstripe_count: stripe count of the striped directory\n"
186          "\tmdt_index:  MDT index of first stripe\n"
187          "\thash_type:  hash type of the striped directory. Hash types:\n"
188          "      fnv_1a_64 FNV-1a hash algorithm (default)\n"
189          "      all_char  sum of characters % MDT_COUNT (not recommended)\n"
190          "\tdefault_stripe: set default dirstripe of the directory\n"
191          "\tmode: the mode of the directory\n"},
192         {"getdirstripe", lfs_getdirstripe, 0,
193          "To list the striping info for a given directory\n"
194          "or recursively for all directories in a directory tree.\n"
195          "usage: getdirstripe [--obd|-O <uuid>] [--quiet|-q] [--verbose|-v]\n"
196          "               [--count|-c ] [--index|-i ] [--raw|-R]\n"
197          "               [--recursive | -r] [ --default_stripe | -D ] <dir> "},
198         {"mkdir", lfs_setdirstripe, 0,
199          "To create a striped directory on a specified MDT. This can only\n"
200          "be done on MDT0 with the right of administrator.\n"
201          "usage: mkdir <--count|-c stripe_count>\n"
202          "              [--index|-i mdt_index] [--hash-type|-t hash_type]\n"
203          "              [--default_stripe|-D ] [--mode|-m mode] <dir>\n"
204          "\tstripe_count: stripe count of the striped directory\n"
205          "\tmdt_index:  MDT index of first stripe\n"
206          "\thash_type:  hash type of the striped directory. Hash types:\n"
207          "      fnv_1a_64 FNV-1a hash algorithm (default)\n"
208          "      all_char  sum of characters % MDT_COUNT (not recommended)\n"
209          "\tdefault_stripe: set default dirstripe of the directory\n"
210          "\tmode: the mode of the directory\n"},
211         {"rm_entry", lfs_rmentry, 0,
212          "To remove the name entry of the remote directory. Note: This\n"
213          "command will only delete the name entry, i.e. the remote directory\n"
214          "will become inaccessable after this command. This can only be done\n"
215          "by the administrator\n"
216          "usage: rm_entry <dir>\n"},
217         {"pool_list", lfs_poollist, 0,
218          "List pools or pool OSTs\n"
219          "usage: pool_list <fsname>[.<pool>] | <pathname>\n"},
220         {"find", lfs_find, 0,
221          "find files matching given attributes recursively in directory tree.\n"
222          "usage: find <directory|filename> ...\n"
223          "     [[!] --atime|-A [+-]N] [[!] --ctime|-C [+-]N]\n"
224          "     [[!] --mtime|-M [+-]N] [[!] --mdt|-m <uuid|index,...>]\n"
225          "     [--maxdepth|-D N] [[!] --name|-n <pattern>]\n"
226          "     [[!] --ost|-O <uuid|index,...>] [--print|-p] [--print0|-P]\n"
227          "     [[!] --size|-s [+-]N[bkMGTPE]]\n"
228          "     [[!] --stripe-count|-c [+-]<stripes>]\n"
229          "     [[!] --stripe-index|-i <index,...>]\n"
230          "     [[!] --stripe-size|-S [+-]N[kMGT]] [[!] --type|-t <filetype>]\n"
231          "     [[!] --gid|-g|--group|-G <gid>|<gname>]\n"
232          "     [[!] --uid|-u|--user|-U <uid>|<uname>] [[!] --pool <pool>]\n"
233          "     [[!] --layout|-L released,raid0]\n"
234          "\t !: used before an option indicates 'NOT' requested attribute\n"
235          "\t -: used before a value indicates 'AT MOST' requested value\n"
236          "\t +: used before a value indicates 'AT LEAST' requested value\n"},
237         {"check", lfs_check, 0,
238          "Display the status of MDS or OSTs (as specified in the command)\n"
239          "or all the servers (MDS and OSTs).\n"
240          "usage: check <osts|mds|servers>"},
241         {"join", lfs_join, 0,
242          "join two lustre files into one.\n"
243          "obsolete, HEAD does not support it anymore.\n"},
244         {"osts", lfs_osts, 0, "list OSTs connected to client "
245          "[for specified path only]\n" "usage: osts [path]"},
246         {"mdts", lfs_mdts, 0, "list MDTs connected to client "
247          "[for specified path only]\n" "usage: mdts [path]"},
248         {"df", lfs_df, 0,
249          "report filesystem disk space usage or inodes usage"
250          "of each MDS and all OSDs or a batch belonging to a specific pool .\n"
251          "Usage: df [-i] [-h] [--lazy|-l] [--pool|-p <fsname>[.<pool>] [path]"},
252         {"getname", lfs_getname, 0, "list instances and specified mount points "
253          "[for specified path only]\n"
254          "Usage: getname [-h]|[path ...] "},
255 #ifdef HAVE_SYS_QUOTA_H
256         {"setquota", lfs_setquota, 0, "Set filesystem quotas.\n"
257          "usage: setquota <-u|-g> <uname>|<uid>|<gname>|<gid>\n"
258          "                -b <block-softlimit> -B <block-hardlimit>\n"
259          "                -i <inode-softlimit> -I <inode-hardlimit> <filesystem>\n"
260          "       setquota <-u|--user|-g|--group> <uname>|<uid>|<gname>|<gid>\n"
261          "                [--block-softlimit <block-softlimit>]\n"
262          "                [--block-hardlimit <block-hardlimit>]\n"
263          "                [--inode-softlimit <inode-softlimit>]\n"
264          "                [--inode-hardlimit <inode-hardlimit>] <filesystem>\n"
265          "       setquota [-t] <-u|--user|-g|--group>\n"
266          "                [--block-grace <block-grace>]\n"
267          "                [--inode-grace <inode-grace>] <filesystem>\n"
268          "       -b can be used instead of --block-softlimit/--block-grace\n"
269          "       -B can be used instead of --block-hardlimit\n"
270          "       -i can be used instead of --inode-softlimit/--inode-grace\n"
271          "       -I can be used instead of --inode-hardlimit\n\n"
272          "Note: The total quota space will be split into many qunits and\n"
273          "      balanced over all server targets, the minimal qunit size is\n"
274          "      1M bytes for block space and 1K inodes for inode space.\n\n"
275          "      Quota space rebalancing process will stop when this mininum\n"
276          "      value is reached. As a result, quota exceeded can be returned\n"
277          "      while many targets still have 1MB or 1K inodes of spare\n"
278          "      quota space."},
279         {"quota", lfs_quota, 0, "Display disk usage and limits.\n"
280          "usage: quota [-q] [-v] [-h] [-o <obd_uuid>|-i <mdt_idx>|-I "
281                        "<ost_idx>]\n"
282          "             [<-u|-g> <uname>|<uid>|<gname>|<gid>] <filesystem>\n"
283          "       quota [-o <obd_uuid>|-i <mdt_idx>|-I <ost_idx>] -t <-u|-g> <filesystem>"},
284 #endif
285         {"flushctx", lfs_flushctx, 0, "Flush security context for current user.\n"
286          "usage: flushctx [-k] [mountpoint...]"},
287         {"lsetfacl", lfs_lsetfacl, 0,
288          "Remote user setfacl for user/group on the same remote client.\n"
289          "usage: lsetfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ..."},
290         {"lgetfacl", lfs_lgetfacl, 0,
291          "Remote user getfacl for user/group on the same remote client.\n"
292          "usage: lgetfacl [-dRLPvh] file ..."},
293         {"rsetfacl", lfs_rsetfacl, 0,
294          "Remote user setfacl for user/group on other clients.\n"
295          "usage: rsetfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ..."},
296         {"rgetfacl", lfs_rgetfacl, 0,
297          "Remote user getfacl for user/group on other clients.\n"
298          "usage: rgetfacl [-dRLPvh] file ..."},
299         {"cp", lfs_cp, 0,
300          "Remote user copy files and directories.\n"
301          "usage: cp [OPTION]... [-T] SOURCE DEST\n\tcp [OPTION]... SOURCE... DIRECTORY\n\tcp [OPTION]... -t DIRECTORY SOURCE..."},
302         {"ls", lfs_ls, 0,
303          "Remote user list directory contents.\n"
304          "usage: ls [OPTION]... [FILE]..."},
305         {"changelog", lfs_changelog, 0,
306          "Show the metadata changes on an MDT."
307          "\nusage: changelog <mdtname> [startrec [endrec]]"},
308         {"changelog_clear", lfs_changelog_clear, 0,
309          "Indicate that old changelog records up to <endrec> are no longer of "
310          "interest to consumer <id>, allowing the system to free up space.\n"
311          "An <endrec> of 0 means all records.\n"
312          "usage: changelog_clear <mdtname> <id> <endrec>"},
313         {"fid2path", lfs_fid2path, 0,
314          "Resolve the full path(s) for given FID(s). For a specific hardlink "
315          "specify link number <linkno>.\n"
316         /* "For a historical link name, specify changelog record <recno>.\n" */
317          "usage: fid2path [--link <linkno>] <fsname|rootpath> <fid> ..."
318                 /* [ --rec <recno> ] */ },
319         {"path2fid", lfs_path2fid, 0, "Display the fid(s) for a given path(s).\n"
320          "usage: path2fid [--parents] <path> ..."},
321         {"data_version", lfs_data_version, 0, "Display file data version for "
322          "a given path.\n" "usage: data_version -[n|r|w] <path>"},
323         {"hsm_state", lfs_hsm_state, 0, "Display the HSM information (states, "
324          "undergoing actions) for given files.\n usage: hsm_state <file> ..."},
325         {"hsm_set", lfs_hsm_set, 0, "Set HSM user flag on specified files.\n"
326          "usage: hsm_set [--norelease] [--noarchive] [--dirty] [--exists] "
327          "[--archived] [--lost] <file> ..."},
328         {"hsm_clear", lfs_hsm_clear, 0, "Clear HSM user flag on specified "
329          "files.\n"
330          "usage: hsm_clear [--norelease] [--noarchive] [--dirty] [--exists] "
331          "[--archived] [--lost] <file> ..."},
332         {"hsm_action", lfs_hsm_action, 0, "Display current HSM request for "
333          "given files.\n" "usage: hsm_action <file> ..."},
334         {"hsm_archive", lfs_hsm_archive, 0,
335          "Archive file to external storage.\n"
336          "usage: hsm_archive [--filelist FILELIST] [--data DATA] [--archive NUM] "
337          "<file> ..."},
338         {"hsm_restore", lfs_hsm_restore, 0,
339          "Restore file from external storage.\n"
340          "usage: hsm_restore [--filelist FILELIST] [--data DATA] <file> ..."},
341         {"hsm_release", lfs_hsm_release, 0,
342          "Release files from Lustre.\n"
343          "usage: hsm_release [--filelist FILELIST] [--data DATA] <file> ..."},
344         {"hsm_remove", lfs_hsm_remove, 0,
345          "Remove file copy from external storage.\n"
346          "usage: hsm_remove [--filelist FILELIST] [--data DATA]\n"
347          "                  [--mntpath MOUNTPATH] [--archive NUM] <file|FID> ...\n"
348          "\n"
349          "Note: To remove files from the archive that have been deleted on\n"
350          "Lustre, set mntpath and optionally archive. In that case, all the\n"
351          "positional arguments and entries in the file list must be FIDs."
352         },
353         {"hsm_cancel", lfs_hsm_cancel, 0,
354          "Cancel requests related to specified files.\n"
355          "usage: hsm_cancel [--filelist FILELIST] [--data DATA] <file> ..."},
356         {"swap_layouts", lfs_swap_layouts, 0, "Swap layouts between 2 files.\n"
357          "usage: swap_layouts <path1> <path2>"},
358         {"migrate", lfs_setstripe, 0,
359          "migrate file/directory between MDTs, or migrate file from one OST "
360          "layout\nto another (may be not safe with concurent writes).\n"
361          "usage: migrate   [--mdt-index|-m <mdt_idx>] [--verbose|-v] "
362          "<directory|filename>\n"
363          "\tmdt_idx:      MDT index to migrate to\n"
364          " or\n"
365          MIGRATE_USAGE},
366         {"mv", lfs_mv, 0,
367          "To move directories between MDTs. This command is deprecated, "
368          "use \"migrate\" instead.\n"
369          "usage: mv <directory|filename> [--mdt-index|-M] <mdt_index> "
370          "[--verbose|-v]\n"},
371         {"help", Parser_help, 0, "help"},
372         {"exit", Parser_quit, 0, "quit"},
373         {"quit", Parser_quit, 0, "quit"},
374         {"--version", Parser_version, 0,
375          "output build version of the utility and exit"},
376         { 0, 0, 0, NULL }
377 };
378
379
380 #define MIGRATION_NONBLOCK      1
381
382 /**
383  * Internal helper for migrate_copy_data(). Check lease and report error if
384  * need be.
385  *
386  * \param[in]  fd           File descriptor on which to check the lease.
387  * \param[out] lease_broken Set to true if the lease was broken.
388  * \param[in]  group_locked Whether a group lock was taken or not.
389  * \param[in]  path         Name of the file being processed, for error
390  *                          reporting
391  *
392  * \retval 0       Migration can keep on going.
393  * \retval -errno  Error occurred, abort migration.
394  */
395 static int check_lease(int fd, bool *lease_broken, bool group_locked,
396                        const char *path)
397 {
398         int rc;
399
400         if (!file_lease_supported)
401                 return 0;
402
403         rc = llapi_lease_check(fd);
404         if (rc > 0)
405                 return 0; /* llapi_check_lease returns > 0 on success. */
406
407         if (!group_locked) {
408                 fprintf(stderr, "%s: cannot migrate '%s': file busy\n",
409                         progname, path);
410                 rc = rc ? rc : -EAGAIN;
411         } else {
412                 fprintf(stderr, "%s: external attempt to access file '%s' "
413                         "blocked until migration ends.\n", progname, path);
414                 rc = 0;
415         }
416         *lease_broken = true;
417         return rc;
418 }
419
420 static int migrate_copy_data(int fd_src, int fd_dst, size_t buf_size,
421                              bool group_locked, const char *fname)
422 {
423         void    *buf = NULL;
424         ssize_t  rsize = -1;
425         ssize_t  wsize = 0;
426         size_t   rpos = 0;
427         size_t   wpos = 0;
428         off_t    bufoff = 0;
429         int      rc;
430         bool     lease_broken = false;
431
432         /* Use a page-aligned buffer for direct I/O */
433         rc = posix_memalign(&buf, getpagesize(), buf_size);
434         if (rc != 0)
435                 return -rc;
436
437         while (1) {
438                 /* read new data only if we have written all
439                  * previously read data */
440                 if (wpos == rpos) {
441                         if (!lease_broken) {
442                                 rc = check_lease(fd_src, &lease_broken,
443                                                  group_locked, fname);
444                                 if (rc < 0)
445                                         goto out;
446                         }
447                         rsize = read(fd_src, buf, buf_size);
448                         if (rsize < 0) {
449                                 rc = -errno;
450                                 fprintf(stderr, "%s: %s: read failed: %s\n",
451                                         progname, fname, strerror(-rc));
452                                 goto out;
453                         }
454                         rpos += rsize;
455                         bufoff = 0;
456                 }
457                 /* eof ? */
458                 if (rsize == 0)
459                         break;
460
461                 wsize = write(fd_dst, buf + bufoff, rpos - wpos);
462                 if (wsize < 0) {
463                         rc = -errno;
464                         fprintf(stderr,
465                                 "%s: %s: write failed on volatile: %s\n",
466                                 progname, fname, strerror(-rc));
467                         goto out;
468                 }
469                 wpos += wsize;
470                 bufoff += wsize;
471         }
472
473         rc = fsync(fd_dst);
474         if (rc < 0) {
475                 rc = -errno;
476                 fprintf(stderr, "%s: %s: fsync failed: %s\n",
477                         progname, fname, strerror(-rc));
478         }
479
480 out:
481         free(buf);
482         return rc;
483 }
484
485 static int migrate_copy_timestamps(int fdv, const struct stat *st)
486 {
487         struct timeval  tv[2] = {
488                 {.tv_sec = st->st_atime},
489                 {.tv_sec = st->st_mtime}
490         };
491
492         return futimes(fdv, tv);
493 }
494
495 static int migrate_block(int fd, int fdv, const struct stat *st,
496                          size_t buf_size, const char *name)
497 {
498         __u64   dv1;
499         int     gid;
500         int     rc;
501         int     rc2;
502
503         rc = llapi_get_data_version(fd, &dv1, LL_DV_RD_FLUSH);
504         if (rc < 0) {
505                 fprintf(stderr, "%s: %s: cannot get dataversion: %s\n",
506                         progname, name, strerror(-rc));
507                 return rc;
508         }
509
510         do
511                 gid = random();
512         while (gid == 0);
513
514         /* The grouplock blocks all concurrent accesses to the file.
515          * It has to be taken after llapi_get_data_version as it would
516          * block it too. */
517         rc = llapi_group_lock(fd, gid);
518         if (rc < 0) {
519                 fprintf(stderr, "%s: %s: cannot get group lock: %s\n",
520                         progname, name, strerror(-rc));
521                 return rc;
522         }
523
524         rc = migrate_copy_data(fd, fdv, buf_size, true, name);
525         if (rc < 0) {
526                 fprintf(stderr, "%s: %s: data copy failed\n", progname, name);
527                 goto out_unlock;
528         }
529
530         /* Make sure we keep original atime/mtime values */
531         rc = migrate_copy_timestamps(fdv, st);
532         if (rc < 0) {
533                 fprintf(stderr, "%s: %s: timestamp copy failed\n",
534                         progname, name);
535                 goto out_unlock;
536         }
537
538         /* swap layouts
539          * for a migration we need to check data version on file did
540          * not change.
541          *
542          * Pass in gid=0 since we already own grouplock. */
543         rc = llapi_fswap_layouts_grouplock(fd, fdv, dv1, 0, 0,
544                                            SWAP_LAYOUTS_CHECK_DV1);
545         if (rc == -EAGAIN) {
546                 fprintf(stderr, "%s: %s: dataversion changed during copy, "
547                         "migration aborted\n", progname, name);
548                 goto out_unlock;
549         } else if (rc < 0) {
550                 fprintf(stderr, "%s: %s: cannot swap layouts: %s\n", progname,
551                         name, strerror(-rc));
552                 goto out_unlock;
553         }
554
555 out_unlock:
556         rc2 = llapi_group_unlock(fd, gid);
557         if (rc2 < 0 && rc == 0) {
558                 fprintf(stderr, "%s: %s: putting group lock failed: %s\n",
559                         progname, name, strerror(-rc2));
560                 rc = rc2;
561         }
562
563         return rc;
564 }
565
566 static int migrate_nonblock(int fd, int fdv, const struct stat *st,
567                             size_t buf_size, const char *name)
568 {
569         __u64   dv1;
570         __u64   dv2;
571         int     rc;
572
573         rc = llapi_get_data_version(fd, &dv1, LL_DV_RD_FLUSH);
574         if (rc < 0) {
575                 fprintf(stderr, "%s: %s: cannot get data version: %s\n",
576                         progname, name, strerror(-rc));
577                 return rc;
578         }
579
580         rc = migrate_copy_data(fd, fdv, buf_size, false, name);
581         if (rc < 0) {
582                 fprintf(stderr, "%s: %s: data copy failed\n", progname, name);
583                 return rc;
584         }
585
586         rc = llapi_get_data_version(fd, &dv2, LL_DV_RD_FLUSH);
587         if (rc != 0) {
588                 fprintf(stderr, "%s: %s: cannot get data version: %s\n",
589                         progname, name, strerror(-rc));
590                 return rc;
591         }
592
593         if (dv1 != dv2) {
594                 rc = -EAGAIN;
595                 fprintf(stderr, "%s: %s: data version changed during "
596                                 "migration\n",
597                         progname, name);
598                 return rc;
599         }
600
601         /* Make sure we keep original atime/mtime values */
602         rc = migrate_copy_timestamps(fdv, st);
603         if (rc < 0) {
604                 fprintf(stderr, "%s: %s: timestamp copy failed\n",
605                         progname, name);
606                 return rc;
607         }
608
609         /* Atomically put lease, swap layouts and close.
610          * for a migration we need to check data version on file did
611          * not change. */
612         rc = llapi_fswap_layouts(fd, fdv, 0, 0, SWAP_LAYOUTS_CLOSE);
613         if (rc < 0) {
614                 fprintf(stderr, "%s: %s: cannot swap layouts: %s\n",
615                         progname, name, strerror(-rc));
616                 return rc;
617         }
618
619         return 0;
620 }
621
622 static int lfs_migrate(char *name, __u64 migration_flags,
623                        struct llapi_stripe_param *param)
624 {
625         int                      fd = -1;
626         int                      fdv = -1;
627         char                     parent[PATH_MAX];
628         int                      mdt_index;
629         int                      random_value;
630         char                     volatile_file[sizeof(parent) +
631                                                LUSTRE_VOLATILE_HDR_LEN +
632                                                2 * sizeof(mdt_index) +
633                                                2 * sizeof(random_value) + 4];
634         char                    *ptr;
635         int                      rc;
636         struct lov_user_md      *lum = NULL;
637         int                      lum_size;
638         int                      buf_size;
639         bool                     have_lease_rdlck = false;
640         struct stat              st;
641         struct stat              stv;
642
643         /* find the right size for the IO and allocate the buffer */
644         lum_size = lov_user_md_size(LOV_MAX_STRIPE_COUNT, LOV_USER_MAGIC_V3);
645         lum = malloc(lum_size);
646         if (lum == NULL) {
647                 rc = -ENOMEM;
648                 goto free;
649         }
650
651         rc = llapi_file_get_stripe(name, lum);
652         /* failure can happen for many reasons and some may be not real errors
653          * (eg: no stripe)
654          * in case of a real error, a later call will fail with better
655          * error management */
656         if (rc < 0)
657                 buf_size = 1024 * 1024;
658         else
659                 buf_size = lum->lmm_stripe_size;
660
661         /* open file, direct io */
662         /* even if the file is only read, WR mode is nedeed to allow
663          * layout swap on fd */
664         fd = open(name, O_RDWR | O_DIRECT);
665         if (fd == -1) {
666                 rc = -errno;
667                 fprintf(stderr, "%s: %s: cannot open: %s\n", progname, name,
668                         strerror(-rc));
669                 goto free;
670         }
671
672         if (file_lease_supported) {
673                 rc = llapi_lease_get(fd, LL_LEASE_RDLCK);
674                 if (rc == -EOPNOTSUPP) {
675                         /* Older servers do not support file lease.
676                          * Disable related checks. This opens race conditions
677                          * as explained in LU-4840 */
678                         file_lease_supported = false;
679                 } else if (rc < 0) {
680                         fprintf(stderr, "%s: %s: cannot get open lease: %s\n",
681                                 progname, name, strerror(-rc));
682                         goto error;
683                 } else {
684                         have_lease_rdlck = true;
685                 }
686         }
687
688         /* search for file directory pathname */
689         if (strlen(name) > sizeof(parent)-1) {
690                 rc = -E2BIG;
691                 goto error;
692         }
693         strncpy(parent, name, sizeof(parent));
694         ptr = strrchr(parent, '/');
695         if (ptr == NULL) {
696                 if (getcwd(parent, sizeof(parent)) == NULL) {
697                         rc = -errno;
698                         goto error;
699                 }
700         } else {
701                 if (ptr == parent)
702                         strcpy(parent, "/");
703                 else
704                         *ptr = '\0';
705         }
706
707         rc = llapi_file_fget_mdtidx(fd, &mdt_index);
708         if (rc < 0) {
709                 fprintf(stderr, "%s: %s: cannot get MDT index: %s\n",
710                         progname, name, strerror(-rc));
711                 goto error;
712         }
713
714         do {
715                 random_value = random();
716                 rc = snprintf(volatile_file, sizeof(volatile_file),
717                               "%s/%s:%.4X:%.4X", parent, LUSTRE_VOLATILE_HDR,
718                               mdt_index, random_value);
719                 if (rc >= sizeof(volatile_file)) {
720                         rc = -E2BIG;
721                         goto error;
722                 }
723
724                 /* create, open a volatile file, use caching (ie no directio) */
725                 fdv = llapi_file_open_param(volatile_file,
726                                 O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW,
727                                             S_IRUSR | S_IWUSR, param);
728         } while (fdv == -EEXIST);
729
730         if (fdv < 0) {
731                 rc = fdv;
732                 fprintf(stderr, "%s: %s: cannot create volatile file in"
733                                 " directory: %s\n",
734                         progname, parent, strerror(-rc));
735                 goto error;
736         }
737
738         /* Not-owner (root?) special case.
739          * Need to set owner/group of volatile file like original.
740          * This will allow to pass related check during layout_swap.
741          */
742         rc = fstat(fd, &st);
743         if (rc != 0) {
744                 rc = -errno;
745                 fprintf(stderr, "%s: %s: cannot stat: %s\n", progname, name,
746                         strerror(errno));
747                 goto error;
748         }
749         rc = fstat(fdv, &stv);
750         if (rc != 0) {
751                 rc = -errno;
752                 fprintf(stderr, "%s: %s: cannot stat: %s\n", progname,
753                         volatile_file, strerror(errno));
754                 goto error;
755         }
756         if (st.st_uid != stv.st_uid || st.st_gid != stv.st_gid) {
757                 rc = fchown(fdv, st.st_uid, st.st_gid);
758                 if (rc != 0) {
759                         rc = -errno;
760                         fprintf(stderr, "%s: %s: cannot chown: %s\n", progname,
761                                 name, strerror(errno));
762                         goto error;
763                 }
764         }
765
766         if (migration_flags & MIGRATION_NONBLOCK && file_lease_supported) {
767                 rc = migrate_nonblock(fd, fdv, &st, buf_size, name);
768                 if (rc == 0) {
769                         have_lease_rdlck = false;
770                         fdv = -1; /* The volatile file is closed as we put the
771                                    * lease in non-blocking mode. */
772                 }
773         } else {
774                 /* Blocking mode (forced if servers do not support file lease).
775                  * It is also the default mode, since we cannot distinguish
776                  * between a broken lease and a server that does not support
777                  * atomic swap/close (LU-6785) */
778                 rc = migrate_block(fd, fdv, &st, buf_size, name);
779         }
780
781 error:
782         if (have_lease_rdlck)
783                 llapi_lease_put(fd);
784
785         if (fd >= 0)
786                 close(fd);
787
788         if (fdv >= 0)
789                 close(fdv);
790
791 free:
792         if (lum)
793                 free(lum);
794
795         return rc;
796 }
797
798 /**
799  * Parse a string containing an OST index list into an array of integers.
800  *
801  * The input string contains a comma delimited list of individual
802  * indices and ranges, for example "1,2-4,7". Add the indices into the
803  * \a osts array and remove duplicates.
804  *
805  * \param[out] osts    array to store indices in
806  * \param[in] size     size of \a osts array
807  * \param[in] offset   starting index in \a osts
808  * \param[in] arg      string containing OST index list
809  *
810  * \retval positive    number of indices in \a osts
811  * \retval -EINVAL     unable to parse \a arg
812  */
813 static int parse_targets(__u32 *osts, int size, int offset, char *arg)
814 {
815         int rc;
816         int nr = offset;
817         int slots = size - offset;
818         char *ptr = NULL;
819         bool end_of_loop;
820
821         if (arg == NULL)
822                 return -EINVAL;
823
824         end_of_loop = false;
825         while (!end_of_loop) {
826                 int start_index;
827                 int end_index;
828                 int i;
829                 char *endptr = NULL;
830
831                 rc = -EINVAL;
832
833                 ptr = strchrnul(arg, ',');
834
835                 end_of_loop = *ptr == '\0';
836                 *ptr = '\0';
837
838                 start_index = strtol(arg, &endptr, 0);
839                 if (endptr == arg) /* no data at all */
840                         break;
841                 if (*endptr != '-' && *endptr != '\0') /* has invalid data */
842                         break;
843                 if (start_index < 0)
844                         break;
845
846                 end_index = start_index;
847                 if (*endptr == '-') {
848                         end_index = strtol(endptr + 1, &endptr, 0);
849                         if (*endptr != '\0')
850                                 break;
851                         if (end_index < start_index)
852                                 break;
853                 }
854
855                 for (i = start_index; i <= end_index && slots > 0; i++) {
856                         int j;
857
858                         /* remove duplicate */
859                         for (j = 0; j < offset; j++) {
860                                 if (osts[j] == i)
861                                         break;
862                         }
863                         if (j == offset) { /* no duplicate */
864                                 osts[nr++] = i;
865                                 --slots;
866                         }
867                 }
868                 if (slots == 0 && i < end_index)
869                         break;
870
871                 *ptr = ',';
872                 arg = ++ptr;
873                 offset = nr;
874                 rc = 0;
875         }
876         if (!end_of_loop && ptr != NULL)
877                 *ptr = ',';
878
879         return rc < 0 ? rc : nr;
880 }
881
882 /* functions */
883 static int lfs_setstripe(int argc, char **argv)
884 {
885         struct llapi_stripe_param       *param = NULL;
886         struct find_param                migrate_mdt_param = {
887                 .fp_max_depth = -1,
888                 .fp_mdt_index = -1,
889         };
890         char                            *fname;
891         int                              result;
892         int                              result2 = 0;
893         unsigned long long               st_size;
894         int                              st_offset, st_count;
895         char                            *end;
896         int                              c;
897         int                              delete = 0;
898         char                            *stripe_size_arg = NULL;
899         char                            *stripe_off_arg = NULL;
900         char                            *stripe_count_arg = NULL;
901         char                            *pool_name_arg = NULL;
902         char                            *mdt_idx_arg = NULL;
903         unsigned long long               size_units = 1;
904         bool                             migrate_mode = false;
905         bool                             migration_block = false;
906         __u64                            migration_flags = 0;
907         __u32                            osts[LOV_MAX_STRIPE_COUNT] = { 0 };
908         int                              nr_osts = 0;
909
910         struct option            long_opts[] = {
911                 /* --block is only valid in migrate mode */
912                 {"block",        no_argument,       0, 'b'},
913 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
914                 /* This formerly implied "stripe-count", but was explicitly
915                  * made "stripe-count" for consistency with other options,
916                  * and to separate it from "mdt-count" when DNE arrives. */
917                 {"count",        required_argument, 0, 'c'},
918 #endif
919                 {"stripe-count", required_argument, 0, 'c'},
920                 {"stripe_count", required_argument, 0, 'c'},
921                 {"delete",       no_argument,       0, 'd'},
922 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
923                 /* This formerly implied "stripe-index", but was explicitly
924                  * made "stripe-index" for consistency with other options,
925                  * and to separate it from "mdt-index" when DNE arrives. */
926                 {"index",        required_argument, 0, 'i'},
927 #endif
928                 {"stripe-index", required_argument, 0, 'i'},
929                 {"stripe_index", required_argument, 0, 'i'},
930                 {"mdt-index",    required_argument, 0, 'm'},
931                 {"mdt_index",    required_argument, 0, 'm'},
932                 /* --non-block is only valid in migrate mode */
933                 {"non-block",    no_argument,       0, 'n'},
934                 {"ost-list",     required_argument, 0, 'o'},
935                 {"ost_list",     required_argument, 0, 'o'},
936                 {"pool",         required_argument, 0, 'p'},
937 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
938                 /* This formerly implied "--stripe-size", but was confusing
939                  * with "lfs find --size|-s", which means "file size", so use
940                  * the consistent "--stripe-size|-S" for all commands. */
941                 {"size",         required_argument, 0, 's'},
942 #endif
943                 {"stripe-size",  required_argument, 0, 'S'},
944                 {"stripe_size",  required_argument, 0, 'S'},
945                 /* --verbose is only valid in migrate mode */
946                 {"verbose",      no_argument,       0, 'v'},
947                 {0, 0, 0, 0}
948         };
949
950         st_size = 0;
951         st_offset = -1;
952         st_count = 0;
953
954         if (strcmp(argv[0], "migrate") == 0)
955                 migrate_mode = true;
956
957         while ((c = getopt_long(argc, argv, "bc:di:m:no:p:s:S:v",
958                                 long_opts, NULL)) >= 0) {
959                 switch (c) {
960                 case 0:
961                         /* Long options. */
962                         break;
963                 case 'b':
964                         if (!migrate_mode) {
965                                 fprintf(stderr, "--block is valid only for"
966                                                 " migrate mode\n");
967                                 return CMD_HELP;
968                         }
969                         migration_block = true;
970                         break;
971                 case 'c':
972 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
973                         if (strcmp(argv[optind - 1], "--count") == 0)
974                                 fprintf(stderr, "warning: '--count' deprecated"
975                                         ", use '--stripe-count' instead\n");
976 #endif
977                         stripe_count_arg = optarg;
978                         break;
979                 case 'd':
980                         /* delete the default striping pattern */
981                         delete = 1;
982                         break;
983                 case 'o':
984                         nr_osts = parse_targets(osts,
985                                                 sizeof(osts) / sizeof(__u32),
986                                                 nr_osts, optarg);
987                         if (nr_osts < 0) {
988                                 fprintf(stderr,
989                                         "error: %s: bad OST indices '%s'\n",
990                                         argv[0], optarg);
991                                 return CMD_HELP;
992                         }
993
994                         if (st_offset == -1) /* first in the command line */
995                                 st_offset = osts[0];
996                         break;
997                 case 'i':
998 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
999                         if (strcmp(argv[optind - 1], "--index") == 0)
1000                                 fprintf(stderr, "warning: '--index' deprecated"
1001                                         ", use '--stripe-index' instead\n");
1002 #endif
1003                         stripe_off_arg = optarg;
1004                         break;
1005                 case 'm':
1006                         if (!migrate_mode) {
1007                                 fprintf(stderr, "--mdt-index is valid only for"
1008                                                 " migrate mode\n");
1009                                 return CMD_HELP;
1010                         }
1011                         mdt_idx_arg = optarg;
1012                         break;
1013                 case 'n':
1014                         if (!migrate_mode) {
1015                                 fprintf(stderr, "--non-block is valid only for"
1016                                                 " migrate mode\n");
1017                                 return CMD_HELP;
1018                         }
1019                         migration_flags |= MIGRATION_NONBLOCK;
1020                         break;
1021 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1022                 case 's':
1023 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1024                         fprintf(stderr, "warning: '--size|-s' deprecated, "
1025                                 "use '--stripe-size|-S' instead\n");
1026 #endif
1027 #endif /* LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0) */
1028                 case 'S':
1029                         stripe_size_arg = optarg;
1030                         break;
1031                 case 'p':
1032                         pool_name_arg = optarg;
1033                         break;
1034                 case 'v':
1035                         if (!migrate_mode) {
1036                                 fprintf(stderr, "--verbose is valid only for"
1037                                                 " migrate mode\n");
1038                                 return CMD_HELP;
1039                         }
1040                         migrate_mdt_param.fp_verbose = VERBOSE_DETAIL;
1041                         break;
1042                 default:
1043                         return CMD_HELP;
1044                 }
1045         }
1046
1047         fname = argv[optind];
1048
1049         if (delete &&
1050             (stripe_size_arg != NULL || stripe_off_arg != NULL ||
1051              stripe_count_arg != NULL || pool_name_arg != NULL)) {
1052                 fprintf(stderr, "error: %s: cannot specify -d with "
1053                         "-s, -c, -o, or -p options\n",
1054                         argv[0]);
1055                 return CMD_HELP;
1056         }
1057
1058         if (optind == argc) {
1059                 fprintf(stderr, "error: %s: missing filename|dirname\n",
1060                         argv[0]);
1061                 return CMD_HELP;
1062         }
1063
1064         if (mdt_idx_arg != NULL && optind > 3) {
1065                 fprintf(stderr, "error: %s: cannot specify -m with other "
1066                         "options\n", argv[0]);
1067                 return CMD_HELP;
1068         }
1069
1070         if ((migration_flags & MIGRATION_NONBLOCK) && migration_block) {
1071                 fprintf(stderr,
1072                         "error: %s: cannot specify --non-block and --block\n",
1073                         argv[0]);
1074                 return CMD_HELP;
1075         }
1076
1077         if (pool_name_arg != NULL) {
1078                 char    *ptr;
1079                 int     rc;
1080
1081                 ptr = strchr(pool_name_arg, '.');
1082                 if (ptr == NULL) {
1083                         ptr = pool_name_arg;
1084                 } else {
1085                         if ((ptr - pool_name_arg) == 0) {
1086                                 fprintf(stderr, "error: %s: fsname is empty "
1087                                         "in pool name '%s'\n",
1088                                         argv[0], pool_name_arg);
1089                                 return CMD_HELP;
1090                         }
1091
1092                         ++ptr;
1093                 }
1094
1095                 rc = lustre_is_poolname_valid(ptr, 1, LOV_MAXPOOLNAME);
1096                 if (rc == -1) {
1097                         fprintf(stderr, "error: %s: poolname '%s' is "
1098                                 "empty\n",
1099                                 argv[0], pool_name_arg);
1100                         return CMD_HELP;
1101                 } else if (rc == -2) {
1102                         fprintf(stderr, "error: %s: pool name '%s' is too long "
1103                                 "(max is %d characters)\n",
1104                                 argv[0], pool_name_arg, LOV_MAXPOOLNAME);
1105                         return CMD_HELP;
1106                 } else if (rc > 0) {
1107                         fprintf(stderr, "error: %s: char '%c' not allowed in "
1108                                 "pool name '%s'\n",
1109                                 argv[0], rc, pool_name_arg);
1110                         return CMD_HELP;
1111                 }
1112         }
1113
1114         /* get the stripe size */
1115         if (stripe_size_arg != NULL) {
1116                 result = llapi_parse_size(stripe_size_arg, &st_size,
1117                                           &size_units, 0);
1118                 if (result) {
1119                         fprintf(stderr, "error: %s: bad stripe size '%s'\n",
1120                                 argv[0], stripe_size_arg);
1121                         return CMD_HELP;
1122                 }
1123         }
1124         /* get the stripe offset */
1125         if (stripe_off_arg != NULL) {
1126                 st_offset = strtol(stripe_off_arg, &end, 0);
1127                 if (*end != '\0') {
1128                         fprintf(stderr, "error: %s: bad stripe offset '%s'\n",
1129                                 argv[0], stripe_off_arg);
1130                         return CMD_HELP;
1131                 }
1132         }
1133         /* get the stripe count */
1134         if (stripe_count_arg != NULL) {
1135                 st_count = strtoul(stripe_count_arg, &end, 0);
1136                 if (*end != '\0') {
1137                         fprintf(stderr, "error: %s: bad stripe count '%s'\n",
1138                                 argv[0], stripe_count_arg);
1139                         return CMD_HELP;
1140                 }
1141         }
1142
1143         if (mdt_idx_arg != NULL) {
1144                 /* initialize migrate mdt parameters */
1145                 migrate_mdt_param.fp_mdt_index = strtoul(mdt_idx_arg, &end, 0);
1146                 if (*end != '\0') {
1147                         fprintf(stderr, "error: %s: bad MDT index '%s'\n",
1148                                 argv[0], mdt_idx_arg);
1149                         return CMD_HELP;
1150                 }
1151                 migrate_mdt_param.fp_migrate = 1;
1152         } else {
1153                 /* initialize stripe parameters */
1154                 param = calloc(1, offsetof(typeof(*param), lsp_osts[nr_osts]));
1155                 if (param == NULL) {
1156                         fprintf(stderr, "error: %s: run out of memory\n",
1157                                 argv[0]);
1158                         return CMD_HELP;
1159                 }
1160
1161                 param->lsp_stripe_size = st_size;
1162                 param->lsp_stripe_offset = st_offset;
1163                 param->lsp_stripe_count = st_count;
1164                 param->lsp_stripe_pattern = 0;
1165                 param->lsp_pool = pool_name_arg;
1166                 param->lsp_is_specific = false;
1167                 if (nr_osts > 0) {
1168                         if (st_count > 0 && nr_osts != st_count) {
1169                                 fprintf(stderr, "error: %s: stripe count '%d' "
1170                                         "doesn't match the number of OSTs: %d\n"
1171                                         , argv[0], st_count, nr_osts);
1172                                 free(param);
1173                                 return CMD_HELP;
1174                         }
1175
1176                         param->lsp_is_specific = true;
1177                         param->lsp_stripe_count = nr_osts;
1178                         memcpy(param->lsp_osts, osts, sizeof(*osts) * nr_osts);
1179                 }
1180         }
1181
1182         for (fname = argv[optind]; fname != NULL; fname = argv[++optind]) {
1183                 if (!migrate_mode) {
1184                         result = llapi_file_open_param(fname,
1185                                                        O_CREAT | O_WRONLY,
1186                                                        0644, param);
1187                         if (result >= 0) {
1188                                 close(result);
1189                                 result = 0;
1190                         }
1191                 } else if (mdt_idx_arg != NULL) {
1192                         result = llapi_migrate_mdt(fname, &migrate_mdt_param);
1193                 } else {
1194                         result = lfs_migrate(fname, migration_flags, param);
1195                 }
1196                 if (result) {
1197                         /* Save the first error encountered. */
1198                         if (result2 == 0)
1199                                 result2 = result;
1200                         fprintf(stderr,
1201                                 "error: %s: %s file '%s' failed\n",
1202                                 argv[0], migrate_mode ? "migrate" : "create",
1203                                 fname);
1204                         continue;
1205                 }
1206         }
1207
1208         free(param);
1209         return result2;
1210 }
1211
1212 static int lfs_poollist(int argc, char **argv)
1213 {
1214         if (argc != 2)
1215                 return CMD_HELP;
1216
1217         return llapi_poollist(argv[1]);
1218 }
1219
1220 static int set_time(time_t *time, time_t *set, char *str)
1221 {
1222         time_t t;
1223         int res = 0;
1224
1225         if (str[0] == '+')
1226                 res = 1;
1227         else if (str[0] == '-')
1228                 res = -1;
1229
1230         if (res)
1231                 str++;
1232
1233         t = strtol(str, NULL, 0);
1234         if (*time < t * 24 * 60 * 60) {
1235                 if (res)
1236                         str--;
1237                 fprintf(stderr, "Wrong time '%s' is specified.\n", str);
1238                 return INT_MAX;
1239         }
1240
1241         *set = *time - t * 24 * 60 * 60;
1242         return res;
1243 }
1244
1245 #define USER 0
1246 #define GROUP 1
1247
1248 static int name2id(unsigned int *id, char *name, int type)
1249 {
1250         if (type == USER) {
1251                 struct passwd *entry;
1252
1253                 if (!(entry = getpwnam(name))) {
1254                         if (!errno)
1255                                 errno = ENOENT;
1256                         return -1;
1257                 }
1258
1259                 *id = entry->pw_uid;
1260         } else {
1261                 struct group *entry;
1262
1263                 if (!(entry = getgrnam(name))) {
1264                         if (!errno)
1265                                 errno = ENOENT;
1266                         return -1;
1267                 }
1268
1269                 *id = entry->gr_gid;
1270         }
1271
1272         return 0;
1273 }
1274
1275 static int id2name(char **name, unsigned int id, int type)
1276 {
1277         if (type == USER) {
1278                 struct passwd *entry;
1279
1280                 if (!(entry = getpwuid(id))) {
1281                         if (!errno)
1282                                 errno = ENOENT;
1283                         return -1;
1284                 }
1285
1286                 *name = entry->pw_name;
1287         } else {
1288                 struct group *entry;
1289
1290                 if (!(entry = getgrgid(id))) {
1291                         if (!errno)
1292                                 errno = ENOENT;
1293                         return -1;
1294                 }
1295
1296                 *name = entry->gr_name;
1297         }
1298
1299         return 0;
1300 }
1301
1302 static int name2layout(__u32 *layout, char *name)
1303 {
1304         char *ptr, *lyt;
1305
1306         *layout = 0;
1307         for (ptr = name; ; ptr = NULL) {
1308                 lyt = strtok(ptr, ",");
1309                 if (lyt == NULL)
1310                         break;
1311                 if (strcmp(lyt, "released") == 0)
1312                         *layout |= LOV_PATTERN_F_RELEASED;
1313                 else if (strcmp(lyt, "raid0") == 0)
1314                         *layout |= LOV_PATTERN_RAID0;
1315                 else
1316                         return -1;
1317         }
1318         return 0;
1319 }
1320
1321 #define FIND_POOL_OPT 3
1322 static int lfs_find(int argc, char **argv)
1323 {
1324         int c, rc;
1325         int ret = 0;
1326         time_t t;
1327         struct find_param param = {
1328                 .fp_max_depth = -1,
1329                 .fp_quiet = 1,
1330         };
1331         struct option long_opts[] = {
1332                 {"atime",        required_argument, 0, 'A'},
1333                 {"stripe-count", required_argument, 0, 'c'},
1334                 {"stripe_count", required_argument, 0, 'c'},
1335                 {"ctime",        required_argument, 0, 'C'},
1336                 {"maxdepth",     required_argument, 0, 'D'},
1337                 {"gid",          required_argument, 0, 'g'},
1338                 {"group",        required_argument, 0, 'G'},
1339                 {"stripe-index", required_argument, 0, 'i'},
1340                 {"stripe_index", required_argument, 0, 'i'},
1341                 {"layout",       required_argument, 0, 'L'},
1342                 {"mdt",          required_argument, 0, 'm'},
1343                 {"mtime",        required_argument, 0, 'M'},
1344                 {"name",         required_argument, 0, 'n'},
1345      /* reserve {"or",           no_argument,     , 0, 'o'}, to match find(1) */
1346                 {"obd",          required_argument, 0, 'O'},
1347                 {"ost",          required_argument, 0, 'O'},
1348                 /* no short option for pool, p/P already used */
1349                 {"pool",         required_argument, 0, FIND_POOL_OPT},
1350                 {"print0",       no_argument,       0, 'p'},
1351                 {"print",        no_argument,       0, 'P'},
1352                 {"size",         required_argument, 0, 's'},
1353                 {"stripe-size",  required_argument, 0, 'S'},
1354                 {"stripe_size",  required_argument, 0, 'S'},
1355                 {"type",         required_argument, 0, 't'},
1356                 {"uid",          required_argument, 0, 'u'},
1357                 {"user",         required_argument, 0, 'U'},
1358                 {0, 0, 0, 0}
1359         };
1360         int pathstart = -1;
1361         int pathend = -1;
1362         int neg_opt = 0;
1363         time_t *xtime;
1364         int *xsign;
1365         int isoption;
1366         char *endptr;
1367
1368         time(&t);
1369
1370         /* when getopt_long_only() hits '!' it returns 1, puts "!" in optarg */
1371         while ((c = getopt_long_only(argc, argv,
1372                                      "-A:c:C:D:g:G:i:L:m:M:n:O:Ppqrs:S:t:u:U:v",
1373                                      long_opts, NULL)) >= 0) {
1374                 xtime = NULL;
1375                 xsign = NULL;
1376                 if (neg_opt)
1377                         --neg_opt;
1378                 /* '!' is part of option */
1379                 /* when getopt_long_only() finds a string which is not
1380                  * an option nor a known option argument it returns 1
1381                  * in that case if we already have found pathstart and pathend
1382                  * (i.e. we have the list of pathnames),
1383                  * the only supported value is "!"
1384                  */
1385                 isoption = (c != 1) || (strcmp(optarg, "!") == 0);
1386                 if (!isoption && pathend != -1) {
1387                         fprintf(stderr, "err: %s: filename|dirname must either "
1388                                         "precede options or follow options\n",
1389                                         argv[0]);
1390                         ret = CMD_HELP;
1391                         goto err;
1392                 }
1393                 if (!isoption && pathstart == -1)
1394                         pathstart = optind - 1;
1395                 if (isoption && pathstart != -1 && pathend == -1)
1396                         pathend = optind - 2;
1397                 switch (c) {
1398                 case 0:
1399                         /* Long options. */
1400                         break;
1401                 case 1:
1402                         /* unknown; opt is "!" or path component,
1403                          * checking done above.
1404                          */
1405                         if (strcmp(optarg, "!") == 0)
1406                                 neg_opt = 2;
1407                         break;
1408                 case 'A':
1409                         xtime = &param.fp_atime;
1410                         xsign = &param.fp_asign;
1411                         param.fp_exclude_atime = !!neg_opt;
1412                         /* no break, this falls through to 'C' for ctime */
1413                 case 'C':
1414                         if (c == 'C') {
1415                                 xtime = &param.fp_ctime;
1416                                 xsign = &param.fp_csign;
1417                                 param.fp_exclude_ctime = !!neg_opt;
1418                         }
1419                         /* no break, this falls through to 'M' for mtime */
1420                 case 'M':
1421                         if (c == 'M') {
1422                                 xtime = &param.fp_mtime;
1423                                 xsign = &param.fp_msign;
1424                                 param.fp_exclude_mtime = !!neg_opt;
1425                         }
1426                         rc = set_time(&t, xtime, optarg);
1427                         if (rc == INT_MAX) {
1428                                 ret = -1;
1429                                 goto err;
1430                         }
1431                         if (rc)
1432                                 *xsign = rc;
1433                         break;
1434                 case 'c':
1435                         if (optarg[0] == '+') {
1436                                 param.fp_stripe_count_sign = -1;
1437                                 optarg++;
1438                         } else if (optarg[0] == '-') {
1439                                 param.fp_stripe_count_sign =  1;
1440                                 optarg++;
1441                         }
1442
1443                         param.fp_stripe_count = strtoul(optarg, &endptr, 0);
1444                         if (*endptr != '\0') {
1445                                 fprintf(stderr,"error: bad stripe_count '%s'\n",
1446                                         optarg);
1447                                 ret = -1;
1448                                 goto err;
1449                         }
1450                         param.fp_check_stripe_count = 1;
1451                         param.fp_exclude_stripe_count = !!neg_opt;
1452                         break;
1453                 case 'D':
1454                         param.fp_max_depth = strtol(optarg, 0, 0);
1455                         break;
1456                 case 'g':
1457                 case 'G':
1458                         rc = name2id(&param.fp_gid, optarg, GROUP);
1459                         if (rc) {
1460                                 param.fp_gid = strtoul(optarg, &endptr, 10);
1461                                 if (*endptr != '\0') {
1462                                         fprintf(stderr, "Group/GID: %s cannot "
1463                                                 "be found.\n", optarg);
1464                                         ret = -1;
1465                                         goto err;
1466                                 }
1467                         }
1468                         param.fp_exclude_gid = !!neg_opt;
1469                         param.fp_check_gid = 1;
1470                         break;
1471                 case 'L':
1472                         ret = name2layout(&param.fp_layout, optarg);
1473                         if (ret)
1474                                 goto err;
1475                         param.fp_exclude_layout = !!neg_opt;
1476                         param.fp_check_layout = 1;
1477                         break;
1478                 case 'u':
1479                 case 'U':
1480                         rc = name2id(&param.fp_uid, optarg, USER);
1481                         if (rc) {
1482                                 param.fp_uid = strtoul(optarg, &endptr, 10);
1483                                 if (*endptr != '\0') {
1484                                         fprintf(stderr, "User/UID: %s cannot "
1485                                                 "be found.\n", optarg);
1486                                         ret = -1;
1487                                         goto err;
1488                                 }
1489                         }
1490                         param.fp_exclude_uid = !!neg_opt;
1491                         param.fp_check_uid = 1;
1492                         break;
1493                 case FIND_POOL_OPT:
1494                         if (strlen(optarg) > LOV_MAXPOOLNAME) {
1495                                 fprintf(stderr,
1496                                         "Pool name %s is too long"
1497                                         " (max is %d)\n", optarg,
1498                                         LOV_MAXPOOLNAME);
1499                                 ret = -1;
1500                                 goto err;
1501                         }
1502                         /* we do check for empty pool because empty pool
1503                          * is used to find V1 lov attributes */
1504                         strncpy(param.fp_poolname, optarg, LOV_MAXPOOLNAME);
1505                         param.fp_poolname[LOV_MAXPOOLNAME] = '\0';
1506                         param.fp_exclude_pool = !!neg_opt;
1507                         param.fp_check_pool = 1;
1508                         break;
1509                 case 'n':
1510                         param.fp_pattern = (char *)optarg;
1511                         param.fp_exclude_pattern = !!neg_opt;
1512                         break;
1513                 case 'm':
1514                 case 'i':
1515                 case 'O': {
1516                         char *buf, *token, *next, *p;
1517                         int len = 1;
1518                         void *tmp;
1519
1520                         buf = strdup(optarg);
1521                         if (buf == NULL) {
1522                                 ret = -ENOMEM;
1523                                 goto err;
1524                         }
1525
1526                         param.fp_exclude_obd = !!neg_opt;
1527
1528                         token = buf;
1529                         while (token && *token) {
1530                                 token = strchr(token, ',');
1531                                 if (token) {
1532                                         len++;
1533                                         token++;
1534                                 }
1535                         }
1536                         if (c == 'm') {
1537                                 param.fp_exclude_mdt = !!neg_opt;
1538                                 param.fp_num_alloc_mdts += len;
1539                                 tmp = realloc(param.fp_mdt_uuid,
1540                                               param.fp_num_alloc_mdts *
1541                                               sizeof(*param.fp_mdt_uuid));
1542                                 if (tmp == NULL) {
1543                                         ret = -ENOMEM;
1544                                         goto err_free;
1545                                 }
1546
1547                                 param.fp_mdt_uuid = tmp;
1548                         } else {
1549                                 param.fp_exclude_obd = !!neg_opt;
1550                                 param.fp_num_alloc_obds += len;
1551                                 tmp = realloc(param.fp_obd_uuid,
1552                                               param.fp_num_alloc_obds *
1553                                               sizeof(*param.fp_obd_uuid));
1554                                 if (tmp == NULL) {
1555                                         ret = -ENOMEM;
1556                                         goto err_free;
1557                                 }
1558
1559                                 param.fp_obd_uuid = tmp;
1560                         }
1561                         for (token = buf; token && *token; token = next) {
1562                                 struct obd_uuid *puuid;
1563                                 if (c == 'm') {
1564                                         puuid =
1565                                         &param.fp_mdt_uuid[param.fp_num_mdts++];
1566                                 } else {
1567                                         puuid =
1568                                         &param.fp_obd_uuid[param.fp_num_obds++];
1569                                 }
1570                                 p = strchr(token, ',');
1571                                 next = 0;
1572                                 if (p) {
1573                                         *p = 0;
1574                                         next = p+1;
1575                                 }
1576
1577                                 if (strlen(token) > sizeof(puuid->uuid) - 1) {
1578                                         ret = -E2BIG;
1579                                         goto err_free;
1580                                 }
1581
1582                                 strncpy(puuid->uuid, token,
1583                                         sizeof(puuid->uuid));
1584                         }
1585 err_free:
1586                         if (buf)
1587                                 free(buf);
1588                         break;
1589                 }
1590                 case 'p':
1591                         param.fp_zero_end = 1;
1592                         break;
1593                 case 'P':
1594                         break;
1595                 case 's':
1596                         if (optarg[0] == '+') {
1597                                 param.fp_size_sign = -1;
1598                                 optarg++;
1599                         } else if (optarg[0] == '-') {
1600                                 param.fp_size_sign =  1;
1601                                 optarg++;
1602                         }
1603
1604                         ret = llapi_parse_size(optarg, &param.fp_size,
1605                                                &param.fp_size_units, 0);
1606                         if (ret) {
1607                                 fprintf(stderr, "error: bad file size '%s'\n",
1608                                         optarg);
1609                                 goto err;
1610                         }
1611                         param.fp_check_size = 1;
1612                         param.fp_exclude_size = !!neg_opt;
1613                         break;
1614                 case 'S':
1615                         if (optarg[0] == '+') {
1616                                 param.fp_stripe_size_sign = -1;
1617                                 optarg++;
1618                         } else if (optarg[0] == '-') {
1619                                 param.fp_stripe_size_sign =  1;
1620                                 optarg++;
1621                         }
1622
1623                         ret = llapi_parse_size(optarg, &param.fp_stripe_size,
1624                                                &param.fp_stripe_size_units, 0);
1625                         if (ret) {
1626                                 fprintf(stderr, "error: bad stripe_size '%s'\n",
1627                                         optarg);
1628                                 goto err;
1629                         }
1630                         param.fp_check_stripe_size = 1;
1631                         param.fp_exclude_stripe_size = !!neg_opt;
1632                         break;
1633                 case 't':
1634                         param.fp_exclude_type = !!neg_opt;
1635                         switch (optarg[0]) {
1636                         case 'b':
1637                                 param.fp_type = S_IFBLK;
1638                                 break;
1639                         case 'c':
1640                                 param.fp_type = S_IFCHR;
1641                                 break;
1642                         case 'd':
1643                                 param.fp_type = S_IFDIR;
1644                                 break;
1645                         case 'f':
1646                                 param.fp_type = S_IFREG;
1647                                 break;
1648                         case 'l':
1649                                 param.fp_type = S_IFLNK;
1650                                 break;
1651                         case 'p':
1652                                 param.fp_type = S_IFIFO;
1653                                 break;
1654                         case 's':
1655                                 param.fp_type = S_IFSOCK;
1656                                 break;
1657                         default:
1658                                 fprintf(stderr, "error: %s: bad type '%s'\n",
1659                                         argv[0], optarg);
1660                                 ret = CMD_HELP;
1661                                 goto err;
1662                         };
1663                         break;
1664                 default:
1665                         ret = CMD_HELP;
1666                         goto err;
1667                 };
1668         }
1669
1670         if (pathstart == -1) {
1671                 fprintf(stderr, "error: %s: no filename|pathname\n",
1672                         argv[0]);
1673                 ret = CMD_HELP;
1674                 goto err;
1675         } else if (pathend == -1) {
1676                 /* no options */
1677                 pathend = argc;
1678         }
1679
1680         do {
1681                 rc = llapi_find(argv[pathstart], &param);
1682                 if (rc != 0 && ret == 0)
1683                         ret = rc;
1684         } while (++pathstart < pathend);
1685
1686         if (ret)
1687                 fprintf(stderr, "error: %s failed for %s.\n",
1688                         argv[0], argv[optind - 1]);
1689 err:
1690         if (param.fp_obd_uuid && param.fp_num_alloc_obds)
1691                 free(param.fp_obd_uuid);
1692
1693         if (param.fp_mdt_uuid && param.fp_num_alloc_mdts)
1694                 free(param.fp_mdt_uuid);
1695
1696         return ret;
1697 }
1698
1699 static int lfs_getstripe_internal(int argc, char **argv,
1700                                   struct find_param *param)
1701 {
1702         struct option long_opts[] = {
1703 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1704                 /* This formerly implied "stripe-count", but was explicitly
1705                  * made "stripe-count" for consistency with other options,
1706                  * and to separate it from "mdt-count" when DNE arrives. */
1707                 {"count",               no_argument,            0, 'c'},
1708 #endif
1709                 {"stripe-count",        no_argument,            0, 'c'},
1710                 {"stripe_count",        no_argument,            0, 'c'},
1711                 {"directory",           no_argument,            0, 'd'},
1712                 {"default",             no_argument,            0, 'D'},
1713                 {"generation",          no_argument,            0, 'g'},
1714 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1715                 /* This formerly implied "stripe-index", but was explicitly
1716                  * made "stripe-index" for consistency with other options,
1717                  * and to separate it from "mdt-index" when DNE arrives. */
1718                 {"index",               no_argument,            0, 'i'},
1719 #endif
1720                 {"stripe-index",        no_argument,            0, 'i'},
1721                 {"stripe_index",        no_argument,            0, 'i'},
1722                 {"layout",              no_argument,            0, 'L'},
1723                 {"mdt-index",           no_argument,            0, 'M'},
1724                 {"mdt_index",           no_argument,            0, 'M'},
1725 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1726                 /* This formerly implied "stripe-index", but was confusing
1727                  * with "file offset" (which will eventually be needed for
1728                  * with different layouts by offset), so deprecate it. */
1729                 {"offset",              no_argument,            0, 'o'},
1730 #endif
1731                 {"obd",                 required_argument,      0, 'O'},
1732                 {"ost",                 required_argument,      0, 'O'},
1733                 {"pool",                no_argument,            0, 'p'},
1734                 {"quiet",               no_argument,            0, 'q'},
1735                 {"recursive",           no_argument,            0, 'r'},
1736                 {"raw",                 no_argument,            0, 'R'},
1737 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1738                 /* This formerly implied "--stripe-size", but was confusing
1739                  * with "lfs find --size|-s", which means "file size", so use
1740                  * the consistent "--stripe-size|-S" for all commands. */
1741                 {"size",                no_argument,            0, 's'},
1742 #endif
1743                 {"stripe-size",         no_argument,            0, 'S'},
1744                 {"stripe_size",         no_argument,            0, 'S'},
1745                 {"verbose",             no_argument,            0, 'v'},
1746                 {0, 0, 0, 0}
1747         };
1748         int c, rc;
1749
1750         param->fp_max_depth = 1;
1751         while ((c = getopt_long(argc, argv, "cdDghiLMoO:pqrRsSv",
1752                                 long_opts, NULL)) != -1) {
1753                 switch (c) {
1754                 case 'O':
1755                         if (param->fp_obd_uuid) {
1756                                 fprintf(stderr,
1757                                         "error: %s: only one obduuid allowed",
1758                                         argv[0]);
1759                                 return CMD_HELP;
1760                         }
1761                         param->fp_obd_uuid = (struct obd_uuid *)optarg;
1762                         break;
1763                 case 'q':
1764                         param->fp_quiet++;
1765                         break;
1766                 case 'd':
1767                         param->fp_max_depth = 0;
1768                         break;
1769                 case 'D':
1770                         param->fp_get_default_lmv = 1;
1771                         break;
1772                 case 'r':
1773                         param->fp_recursive = 1;
1774                         break;
1775                 case 'v':
1776                         param->fp_verbose = VERBOSE_ALL | VERBOSE_DETAIL;
1777                         break;
1778                 case 'c':
1779 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1780                         if (strcmp(argv[optind - 1], "--count") == 0)
1781                                 fprintf(stderr, "warning: '--count' deprecated,"
1782                                         " use '--stripe-count' instead\n");
1783 #endif
1784                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1785                                 param->fp_verbose |= VERBOSE_COUNT;
1786                                 param->fp_max_depth = 0;
1787                         }
1788                         break;
1789 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1790                 case 's':
1791 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1792                         fprintf(stderr, "warning: '--size|-s' deprecated, "
1793                                 "use '--stripe-size|-S' instead\n");
1794 #endif
1795 #endif /* LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0) */
1796                 case 'S':
1797                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1798                                 param->fp_verbose |= VERBOSE_SIZE;
1799                                 param->fp_max_depth = 0;
1800                         }
1801                         break;
1802 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1803                 case 'o':
1804                         fprintf(stderr, "warning: '--offset|-o' deprecated, "
1805                                 "use '--stripe-index|-i' instead\n");
1806 #endif
1807                 case 'i':
1808 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1809                         if (strcmp(argv[optind - 1], "--index") == 0)
1810                                 fprintf(stderr, "warning: '--index' deprecated"
1811                                         ", use '--stripe-index' instead\n");
1812 #endif
1813                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1814                                 param->fp_verbose |= VERBOSE_OFFSET;
1815                                 param->fp_max_depth = 0;
1816                         }
1817                         break;
1818                 case 'p':
1819                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1820                                 param->fp_verbose |= VERBOSE_POOL;
1821                                 param->fp_max_depth = 0;
1822                         }
1823                         break;
1824                 case 'g':
1825                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1826                                 param->fp_verbose |= VERBOSE_GENERATION;
1827                                 param->fp_max_depth = 0;
1828                         }
1829                         break;
1830                 case 'L':
1831                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1832                                 param->fp_verbose |= VERBOSE_LAYOUT;
1833                                 param->fp_max_depth = 0;
1834                         }
1835                         break;
1836                 case 'M':
1837                         if (!(param->fp_verbose & VERBOSE_DETAIL))
1838                                 param->fp_max_depth = 0;
1839                         param->fp_verbose |= VERBOSE_MDTINDEX;
1840                         break;
1841                 case 'R':
1842                         param->fp_raw = 1;
1843                         break;
1844                 default:
1845                         return CMD_HELP;
1846                 }
1847         }
1848
1849         if (optind >= argc)
1850                 return CMD_HELP;
1851
1852         if (param->fp_recursive)
1853                 param->fp_max_depth = -1;
1854
1855         if (!param->fp_verbose)
1856                 param->fp_verbose = VERBOSE_ALL;
1857         if (param->fp_quiet)
1858                 param->fp_verbose = VERBOSE_OBJID;
1859
1860         do {
1861                 rc = llapi_getstripe(argv[optind], param);
1862         } while (++optind < argc && !rc);
1863
1864         if (rc)
1865                 fprintf(stderr, "error: %s failed for %s.\n",
1866                         argv[0], argv[optind - 1]);
1867         return rc;
1868 }
1869
1870 static int lfs_tgts(int argc, char **argv)
1871 {
1872         char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
1873         struct find_param param;
1874         int index = 0, rc=0;
1875
1876         if (argc > 2)
1877                 return CMD_HELP;
1878
1879         if (argc == 2 && !realpath(argv[1], path)) {
1880                 rc = -errno;
1881                 fprintf(stderr, "error: invalid path '%s': %s\n",
1882                         argv[1], strerror(-rc));
1883                 return rc;
1884         }
1885
1886         while (!llapi_search_mounts(path, index++, mntdir, NULL)) {
1887                 /* Check if we have a mount point */
1888                 if (mntdir[0] == '\0')
1889                         continue;
1890
1891                 memset(&param, 0, sizeof(param));
1892                 if (!strcmp(argv[0], "mdts"))
1893                         param.fp_get_lmv = 1;
1894
1895                 rc = llapi_ostlist(mntdir, &param);
1896                 if (rc) {
1897                         fprintf(stderr, "error: %s: failed on %s\n",
1898                                 argv[0], mntdir);
1899                 }
1900                 if (path[0] != '\0')
1901                         break;
1902                 memset(mntdir, 0, PATH_MAX);
1903         }
1904
1905         return rc;
1906 }
1907
1908 static int lfs_getstripe(int argc, char **argv)
1909 {
1910         struct find_param param = { 0 };
1911         return lfs_getstripe_internal(argc, argv, &param);
1912 }
1913
1914 /* functions */
1915 static int lfs_getdirstripe(int argc, char **argv)
1916 {
1917         struct find_param param = { 0 };
1918
1919         param.fp_get_lmv = 1;
1920         return lfs_getstripe_internal(argc, argv, &param);
1921 }
1922
1923 /* functions */
1924 static int lfs_setdirstripe(int argc, char **argv)
1925 {
1926         char                    *dname;
1927         int                     result;
1928         unsigned int            stripe_offset = -1;
1929         unsigned int            stripe_count = 1;
1930         enum lmv_hash_type      hash_type;
1931         char                    *end;
1932         int                     c;
1933         char                    *stripe_offset_opt = NULL;
1934         char                    *stripe_count_opt = NULL;
1935         char                    *stripe_hash_opt = NULL;
1936         char                    *mode_opt = NULL;
1937         bool                    default_stripe = false;
1938         mode_t                  mode = S_IRWXU | S_IRWXG | S_IRWXO;
1939         mode_t                  previous_mode = 0;
1940         bool                    delete = false;
1941
1942         struct option long_opts[] = {
1943                 {"count",       required_argument, 0, 'c'},
1944                 {"delete",      no_argument, 0, 'd'},
1945                 {"index",       required_argument, 0, 'i'},
1946                 {"mode",        required_argument, 0, 'm'},
1947                 {"hash-type",   required_argument, 0, 't'},
1948                 {"default_stripe", no_argument, 0, 'D'},
1949                 {0, 0, 0, 0}
1950         };
1951
1952         while ((c = getopt_long(argc, argv, "c:dDi:m:t:", long_opts,
1953                                 NULL)) >= 0) {
1954                 switch (c) {
1955                 case 0:
1956                         /* Long options. */
1957                         break;
1958                 case 'c':
1959                         stripe_count_opt = optarg;
1960                         break;
1961                 case 'd':
1962                         delete = true;
1963                         default_stripe = true;
1964                         break;
1965                 case 'D':
1966                         default_stripe = true;
1967                         break;
1968                 case 'i':
1969                         stripe_offset_opt = optarg;
1970                         break;
1971                 case 'm':
1972                         mode_opt = optarg;
1973                         break;
1974                 case 't':
1975                         stripe_hash_opt = optarg;
1976                         break;
1977                 default:
1978                         fprintf(stderr, "error: %s: option '%s' "
1979                                         "unrecognized\n",
1980                                         argv[0], argv[optind - 1]);
1981                         return CMD_HELP;
1982                 }
1983         }
1984
1985         if (optind == argc) {
1986                 fprintf(stderr, "error: %s: missing dirname\n",
1987                         argv[0]);
1988                 return CMD_HELP;
1989         }
1990
1991         if (!delete && stripe_offset_opt == NULL && stripe_count_opt == NULL) {
1992                 fprintf(stderr, "error: %s: missing stripe offset and count.\n",
1993                         argv[0]);
1994                 return CMD_HELP;
1995         }
1996
1997         if (stripe_offset_opt != NULL) {
1998                 /* get the stripe offset */
1999                 stripe_offset = strtoul(stripe_offset_opt, &end, 0);
2000                 if (*end != '\0') {
2001                         fprintf(stderr, "error: %s: bad stripe offset '%s'\n",
2002                                 argv[0], stripe_offset_opt);
2003                         return CMD_HELP;
2004                 }
2005         }
2006
2007         if (delete) {
2008                 if (stripe_offset_opt != NULL || stripe_count_opt != NULL) {
2009                         fprintf(stderr, "error: %s: cannot specify -d with -s,"
2010                                 " or -i options.\n", argv[0]);
2011                         return CMD_HELP;
2012                 } else {
2013                         stripe_count = 0;
2014                 }
2015         }
2016
2017
2018         if (mode_opt != NULL) {
2019                 mode = strtoul(mode_opt, &end, 8);
2020                 if (*end != '\0') {
2021                         fprintf(stderr, "error: %s: bad mode '%s'\n",
2022                                 argv[0], mode_opt);
2023                         return CMD_HELP;
2024                 }
2025                 previous_mode = umask(0);
2026         }
2027
2028         if (stripe_hash_opt == NULL ||
2029             strcmp(stripe_hash_opt, LMV_HASH_NAME_FNV_1A_64) == 0) {
2030                 hash_type = LMV_HASH_TYPE_FNV_1A_64;
2031         } else if (strcmp(stripe_hash_opt, LMV_HASH_NAME_ALL_CHARS) == 0) {
2032                 hash_type = LMV_HASH_TYPE_ALL_CHARS;
2033         } else {
2034                 fprintf(stderr, "error: %s: bad stripe hash type '%s'\n",
2035                         argv[0], stripe_hash_opt);
2036                 return CMD_HELP;
2037         }
2038
2039         /* get the stripe count */
2040         if (stripe_count_opt != NULL) {
2041                 stripe_count = strtoul(stripe_count_opt, &end, 0);
2042                 if (*end != '\0') {
2043                         fprintf(stderr, "error: %s: bad stripe count '%s'\n",
2044                                 argv[0], stripe_count_opt);
2045                         return CMD_HELP;
2046                 }
2047         }
2048
2049         dname = argv[optind];
2050         do {
2051                 if (default_stripe) {
2052                         result = llapi_dir_set_default_lmv_stripe(dname,
2053                                                     stripe_offset, stripe_count,
2054                                                     hash_type, NULL);
2055                 } else {
2056                         result = llapi_dir_create_pool(dname, mode,
2057                                                        stripe_offset,
2058                                                        stripe_count, hash_type,
2059                                                        NULL);
2060                 }
2061
2062                 if (result) {
2063                         fprintf(stderr, "error: %s: create stripe dir '%s' "
2064                                 "failed\n", argv[0], dname);
2065                         break;
2066                 }
2067                 dname = argv[++optind];
2068         } while (dname != NULL);
2069
2070         if (mode_opt != NULL)
2071                 umask(previous_mode);
2072
2073         return result;
2074 }
2075
2076 /* functions */
2077 static int lfs_rmentry(int argc, char **argv)
2078 {
2079         char *dname;
2080         int   index;
2081         int   result = 0;
2082
2083         if (argc <= 1) {
2084                 fprintf(stderr, "error: %s: missing dirname\n",
2085                         argv[0]);
2086                 return CMD_HELP;
2087         }
2088
2089         index = 1;
2090         dname = argv[index];
2091         while (dname != NULL) {
2092                 result = llapi_direntry_remove(dname);
2093                 if (result) {
2094                         fprintf(stderr, "error: %s: remove dir entry '%s' "
2095                                 "failed\n", argv[0], dname);
2096                         break;
2097                 }
2098                 dname = argv[++index];
2099         }
2100         return result;
2101 }
2102
2103 static int lfs_mv(int argc, char **argv)
2104 {
2105         struct  find_param param = {
2106                 .fp_max_depth = -1,
2107                 .fp_mdt_index = -1,
2108         };
2109         char   *end;
2110         int     c;
2111         int     rc = 0;
2112         struct option long_opts[] = {
2113                 {"mdt-index", required_argument, 0, 'M'},
2114                 {"verbose",     no_argument,       0, 'v'},
2115                 {0, 0, 0, 0}
2116         };
2117
2118         while ((c = getopt_long(argc, argv, "M:v", long_opts, NULL)) != -1) {
2119                 switch (c) {
2120                 case 'M': {
2121                         param.fp_mdt_index = strtoul(optarg, &end, 0);
2122                         if (*end != '\0') {
2123                                 fprintf(stderr, "%s: invalid MDT index'%s'\n",
2124                                         argv[0], optarg);
2125                                 return CMD_HELP;
2126                         }
2127                         break;
2128                 }
2129                 case 'v': {
2130                         param.fp_verbose = VERBOSE_DETAIL;
2131                         break;
2132                 }
2133                 default:
2134                         fprintf(stderr, "error: %s: unrecognized option '%s'\n",
2135                                 argv[0], argv[optind - 1]);
2136                         return CMD_HELP;
2137                 }
2138         }
2139
2140         if (param.fp_mdt_index == -1) {
2141                 fprintf(stderr, "%s: MDT index must be specified\n", argv[0]);
2142                 return CMD_HELP;
2143         }
2144
2145         if (optind >= argc) {
2146                 fprintf(stderr, "%s: missing operand path\n", argv[0]);
2147                 return CMD_HELP;
2148         }
2149
2150         param.fp_migrate = 1;
2151         rc = llapi_migrate_mdt(argv[optind], &param);
2152         if (rc != 0)
2153                 fprintf(stderr, "%s: cannot migrate '%s' to MDT%04x: %s\n",
2154                         argv[0], argv[optind], param.fp_mdt_index,
2155                         strerror(-rc));
2156         return rc;
2157 }
2158
2159 static int lfs_osts(int argc, char **argv)
2160 {
2161         return lfs_tgts(argc, argv);
2162 }
2163
2164 static int lfs_mdts(int argc, char **argv)
2165 {
2166         return lfs_tgts(argc, argv);
2167 }
2168
2169 #define COOK(value)                                                     \
2170 ({                                                                      \
2171         int radix = 0;                                                  \
2172         while (value > 1024) {                                          \
2173                 value /= 1024;                                          \
2174                 radix++;                                                \
2175         }                                                               \
2176         radix;                                                          \
2177 })
2178 #define UUF     "%-20s"
2179 #define CSF     "%11s"
2180 #define CDF     "%11llu"
2181 #define HDF     "%8.1f%c"
2182 #define RSF     "%4s"
2183 #define RDF     "%3d%%"
2184
2185 static int showdf(char *mntdir, struct obd_statfs *stat,
2186                   char *uuid, int ishow, int cooked,
2187                   char *type, int index, int rc)
2188 {
2189         long long avail, used, total;
2190         double ratio = 0;
2191         char *suffix = "KMGTPEZY";
2192         /* Note if we have >2^64 bytes/fs these buffers will need to be grown */
2193         char tbuf[3 * sizeof(__u64)];
2194         char ubuf[3 * sizeof(__u64)];
2195         char abuf[3 * sizeof(__u64)];
2196         char rbuf[3 * sizeof(__u64)];
2197
2198         if (!uuid || !stat)
2199                 return -EINVAL;
2200
2201         switch (rc) {
2202         case 0:
2203                 if (ishow) {
2204                         avail = stat->os_ffree;
2205                         used = stat->os_files - stat->os_ffree;
2206                         total = stat->os_files;
2207                 } else {
2208                         int shift = cooked ? 0 : 10;
2209
2210                         avail = (stat->os_bavail * stat->os_bsize) >> shift;
2211                         used  = ((stat->os_blocks - stat->os_bfree) *
2212                                  stat->os_bsize) >> shift;
2213                         total = (stat->os_blocks * stat->os_bsize) >> shift;
2214                 }
2215
2216                 if ((used + avail) > 0)
2217                         ratio = (double)used / (double)(used + avail);
2218
2219                 if (cooked) {
2220                         int i;
2221                         double cook_val;
2222
2223                         cook_val = (double)total;
2224                         i = COOK(cook_val);
2225                         if (i > 0)
2226                                 sprintf(tbuf, HDF, cook_val, suffix[i - 1]);
2227                         else
2228                                 sprintf(tbuf, CDF, total);
2229
2230                         cook_val = (double)used;
2231                         i = COOK(cook_val);
2232                         if (i > 0)
2233                                 sprintf(ubuf, HDF, cook_val, suffix[i - 1]);
2234                         else
2235                                 sprintf(ubuf, CDF, used);
2236
2237                         cook_val = (double)avail;
2238                         i = COOK(cook_val);
2239                         if (i > 0)
2240                                 sprintf(abuf, HDF, cook_val, suffix[i - 1]);
2241                         else
2242                                 sprintf(abuf, CDF, avail);
2243                 } else {
2244                         sprintf(tbuf, CDF, total);
2245                         sprintf(ubuf, CDF, used);
2246                         sprintf(abuf, CDF, avail);
2247                 }
2248
2249                 sprintf(rbuf, RDF, (int)(ratio * 100 + 0.5));
2250                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s",
2251                        uuid, tbuf, ubuf, abuf, rbuf, mntdir);
2252                 if (type)
2253                         printf("[%s:%d]\n", type, index);
2254                 else
2255                         printf("\n");
2256
2257                 break;
2258         case -ENODATA:
2259                 printf(UUF": inactive device\n", uuid);
2260                 break;
2261         default:
2262                 printf(UUF": %s\n", uuid, strerror(-rc));
2263                 break;
2264         }
2265
2266         return 0;
2267 }
2268
2269 struct ll_stat_type {
2270         int   st_op;
2271         char *st_name;
2272 };
2273
2274 static int mntdf(char *mntdir, char *fsname, char *pool, int ishow,
2275                 int cooked, int lazy)
2276 {
2277         struct obd_statfs stat_buf, sum = { .os_bsize = 1 };
2278         struct obd_uuid uuid_buf;
2279         char *poolname = NULL;
2280         struct ll_stat_type types[] = { { LL_STATFS_LMV, "MDT" },
2281                                         { LL_STATFS_LOV, "OST" },
2282                                         { 0, NULL } };
2283         struct ll_stat_type *tp;
2284         __u64 ost_ffree = 0;
2285         __u32 index;
2286         __u32 type;
2287         int rc;
2288
2289         if (pool) {
2290                 poolname = strchr(pool, '.');
2291                 if (poolname != NULL) {
2292                         if (strncmp(fsname, pool, strlen(fsname))) {
2293                                 fprintf(stderr, "filesystem name incorrect\n");
2294                                 return -ENODEV;
2295                         }
2296                         poolname++;
2297                 } else
2298                         poolname = pool;
2299         }
2300
2301         if (ishow)
2302                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2303                        "UUID", "Inodes", "IUsed", "IFree",
2304                        "IUse%", "Mounted on");
2305         else
2306                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2307                        "UUID", cooked ? "bytes" : "1K-blocks",
2308                        "Used", "Available", "Use%", "Mounted on");
2309
2310         for (tp = types; tp->st_name != NULL; tp++) {
2311                 for (index = 0; ; index++) {
2312                         memset(&stat_buf, 0, sizeof(struct obd_statfs));
2313                         memset(&uuid_buf, 0, sizeof(struct obd_uuid));
2314                         type = lazy ? tp->st_op | LL_STATFS_NODELAY : tp->st_op;
2315                         rc = llapi_obd_statfs(mntdir, type, index,
2316                                               &stat_buf, &uuid_buf);
2317                         if (rc == -ENODEV)
2318                                 break;
2319
2320                         if (rc == -EAGAIN)
2321                                 continue;
2322
2323                         if (poolname && tp->st_op == LL_STATFS_LOV &&
2324                             llapi_search_ost(fsname, poolname,
2325                                              obd_uuid2str(&uuid_buf)) != 1)
2326                                 continue;
2327
2328                         /* the llapi_obd_statfs() call may have returned with
2329                          * an error, but if it filled in uuid_buf we will at
2330                          * lease use that to print out a message for that OBD.
2331                          * If we didn't get anything in the uuid_buf, then fill
2332                          * it in so that we can print an error message. */
2333                         if (uuid_buf.uuid[0] == '\0')
2334                                 sprintf(uuid_buf.uuid, "%s%04x",
2335                                         tp->st_name, index);
2336                         showdf(mntdir, &stat_buf, obd_uuid2str(&uuid_buf),
2337                                ishow, cooked, tp->st_name, index, rc);
2338
2339                         if (rc == 0) {
2340                                 if (tp->st_op == LL_STATFS_LMV) {
2341                                         sum.os_ffree += stat_buf.os_ffree;
2342                                         sum.os_files += stat_buf.os_files;
2343                                 } else /* if (tp->st_op == LL_STATFS_LOV) */ {
2344                                         sum.os_blocks += stat_buf.os_blocks *
2345                                                 stat_buf.os_bsize;
2346                                         sum.os_bfree  += stat_buf.os_bfree *
2347                                                 stat_buf.os_bsize;
2348                                         sum.os_bavail += stat_buf.os_bavail *
2349                                                 stat_buf.os_bsize;
2350                                         ost_ffree += stat_buf.os_ffree;
2351                                 }
2352                         } else if (rc == -EINVAL || rc == -EFAULT) {
2353                                 break;
2354                         }
2355                 }
2356         }
2357
2358         /* If we don't have as many objects free on the OST as inodes
2359          * on the MDS, we reduce the total number of inodes to
2360          * compensate, so that the "inodes in use" number is correct.
2361          * Matches ll_statfs_internal() so the results are consistent. */
2362         if (ost_ffree < sum.os_ffree) {
2363                 sum.os_files = (sum.os_files - sum.os_ffree) + ost_ffree;
2364                 sum.os_ffree = ost_ffree;
2365         }
2366         printf("\n");
2367         showdf(mntdir, &sum, "filesystem summary:", ishow, cooked, NULL, 0, 0);
2368         printf("\n");
2369         return 0;
2370 }
2371
2372 static int lfs_df(int argc, char **argv)
2373 {
2374         char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
2375         int ishow = 0, cooked = 0;
2376         int lazy = 0;
2377         int c, rc = 0, index = 0;
2378         char fsname[PATH_MAX] = "", *pool_name = NULL;
2379         struct option long_opts[] = {
2380                 {"pool", required_argument, 0, 'p'},
2381                 {"lazy", 0, 0, 'l'},
2382                 {0, 0, 0, 0}
2383         };
2384
2385         while ((c = getopt_long(argc, argv, "hilp:", long_opts, NULL)) != -1) {
2386                 switch (c) {
2387                 case 'i':
2388                         ishow = 1;
2389                         break;
2390                 case 'h':
2391                         cooked = 1;
2392                         break;
2393                 case 'l':
2394                         lazy = 1;
2395                         break;
2396                 case 'p':
2397                         pool_name = optarg;
2398                         break;
2399                 default:
2400                         return CMD_HELP;
2401                 }
2402         }
2403         if (optind < argc && !realpath(argv[optind], path)) {
2404                 rc = -errno;
2405                 fprintf(stderr, "error: invalid path '%s': %s\n",
2406                         argv[optind], strerror(-rc));
2407                 return rc;
2408         }
2409
2410         while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2411                 /* Check if we have a mount point */
2412                 if (mntdir[0] == '\0')
2413                         continue;
2414
2415                 rc = mntdf(mntdir, fsname, pool_name, ishow, cooked, lazy);
2416                 if (rc || path[0] != '\0')
2417                         break;
2418                 fsname[0] = '\0'; /* avoid matching in next loop */
2419                 mntdir[0] = '\0'; /* avoid matching in next loop */
2420         }
2421
2422         return rc;
2423 }
2424
2425 static int lfs_getname(int argc, char **argv)
2426 {
2427         char mntdir[PATH_MAX] = "", path[PATH_MAX] = "", fsname[PATH_MAX] = "";
2428         int rc = 0, index = 0, c;
2429         char buf[sizeof(struct obd_uuid)];
2430
2431         while ((c = getopt(argc, argv, "h")) != -1)
2432                 return CMD_HELP;
2433
2434         if (optind == argc) { /* no paths specified, get all paths. */
2435                 while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2436                         rc = llapi_getname(mntdir, buf, sizeof(buf));
2437                         if (rc < 0) {
2438                                 fprintf(stderr,
2439                                         "cannot get name for `%s': %s\n",
2440                                         mntdir, strerror(-rc));
2441                                 break;
2442                         }
2443
2444                         printf("%s %s\n", buf, mntdir);
2445
2446                         path[0] = fsname[0] = mntdir[0] = 0;
2447                 }
2448         } else { /* paths specified, only attempt to search these. */
2449                 for (; optind < argc; optind++) {
2450                         rc = llapi_getname(argv[optind], buf, sizeof(buf));
2451                         if (rc < 0) {
2452                                 fprintf(stderr,
2453                                         "cannot get name for `%s': %s\n",
2454                                         argv[optind], strerror(-rc));
2455                                 break;
2456                         }
2457
2458                         printf("%s %s\n", buf, argv[optind]);
2459                 }
2460         }
2461         return rc;
2462 }
2463
2464 static int lfs_check(int argc, char **argv)
2465 {
2466         int rc;
2467         char mntdir[PATH_MAX] = {'\0'};
2468         int num_types = 1;
2469         char *obd_types[2];
2470         char obd_type1[4];
2471         char obd_type2[4];
2472
2473         if (argc != 2)
2474                 return CMD_HELP;
2475
2476         obd_types[0] = obd_type1;
2477         obd_types[1] = obd_type2;
2478
2479         if (strcmp(argv[1], "osts") == 0) {
2480                 strcpy(obd_types[0], "osc");
2481         } else if (strcmp(argv[1], "mds") == 0) {
2482                 strcpy(obd_types[0], "mdc");
2483         } else if (strcmp(argv[1], "servers") == 0) {
2484                 num_types = 2;
2485                 strcpy(obd_types[0], "osc");
2486                 strcpy(obd_types[1], "mdc");
2487         } else {
2488                 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
2489                                 argv[0], argv[1]);
2490                         return CMD_HELP;
2491         }
2492
2493         rc = llapi_search_mounts(NULL, 0, mntdir, NULL);
2494         if (rc < 0 || mntdir[0] == '\0') {
2495                 fprintf(stderr, "No suitable Lustre mount found\n");
2496                 return rc;
2497         }
2498
2499         rc = llapi_target_check(num_types, obd_types, mntdir);
2500         if (rc)
2501                 fprintf(stderr, "error: %s: %s status failed\n",
2502                                 argv[0],argv[1]);
2503
2504         return rc;
2505
2506 }
2507
2508 static int lfs_join(int argc, char **argv)
2509 {
2510         fprintf(stderr, "join two lustre files into one.\n"
2511                         "obsolete, HEAD does not support it anymore.\n");
2512         return 0;
2513 }
2514
2515 #ifdef HAVE_SYS_QUOTA_H
2516 #define ARG2INT(nr, str, msg)                                           \
2517 do {                                                                    \
2518         char *endp;                                                     \
2519         nr = strtol(str, &endp, 0);                                     \
2520         if (*endp) {                                                    \
2521                 fprintf(stderr, "error: bad %s: %s\n", msg, str);       \
2522                 return CMD_HELP;                                        \
2523         }                                                               \
2524 } while (0)
2525
2526 #define ADD_OVERFLOW(a,b) ((a + b) < a) ? (a = ULONG_MAX) : (a = a + b)
2527
2528 /* Convert format time string "XXwXXdXXhXXmXXs" into seconds value
2529  * returns the value or ULONG_MAX on integer overflow or incorrect format
2530  * Notes:
2531  *        1. the order of specifiers is arbitrary (may be: 5w3s or 3s5w)
2532  *        2. specifiers may be encountered multiple times (2s3s is 5 seconds)
2533  *        3. empty integer value is interpreted as 0
2534  */
2535 static unsigned long str2sec(const char* timestr)
2536 {
2537         const char spec[] = "smhdw";
2538         const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2539         unsigned long val = 0;
2540         char *tail;
2541
2542         if (strpbrk(timestr, spec) == NULL) {
2543                 /* no specifiers inside the time string,
2544                    should treat it as an integer value */
2545                 val = strtoul(timestr, &tail, 10);
2546                 return *tail ? ULONG_MAX : val;
2547         }
2548
2549         /* format string is XXwXXdXXhXXmXXs */
2550         while (*timestr) {
2551                 unsigned long v;
2552                 int ind;
2553                 char* ptr;
2554
2555                 v = strtoul(timestr, &tail, 10);
2556                 if (v == ULONG_MAX || *tail == '\0')
2557                         /* value too large (ULONG_MAX or more)
2558                            or missing specifier */
2559                         goto error;
2560
2561                 ptr = strchr(spec, *tail);
2562                 if (ptr == NULL)
2563                         /* unknown specifier */
2564                         goto error;
2565
2566                 ind = ptr - spec;
2567
2568                 /* check if product will overflow the type */
2569                 if (!(v < ULONG_MAX / mult[ind]))
2570                         goto error;
2571
2572                 ADD_OVERFLOW(val, mult[ind] * v);
2573                 if (val == ULONG_MAX)
2574                         goto error;
2575
2576                 timestr = tail + 1;
2577         }
2578
2579         return val;
2580
2581 error:
2582         return ULONG_MAX;
2583 }
2584
2585 #define ARG2ULL(nr, str, def_units)                                     \
2586 do {                                                                    \
2587         unsigned long long limit, units = def_units;                    \
2588         int rc;                                                         \
2589                                                                         \
2590         rc = llapi_parse_size(str, &limit, &units, 1);                  \
2591         if (rc < 0) {                                                   \
2592                 fprintf(stderr, "error: bad limit value %s\n", str);    \
2593                 return CMD_HELP;                                        \
2594         }                                                               \
2595         nr = limit;                                                     \
2596 } while (0)
2597
2598 static inline int has_times_option(int argc, char **argv)
2599 {
2600         int i;
2601
2602         for (i = 1; i < argc; i++)
2603                 if (!strcmp(argv[i], "-t"))
2604                         return 1;
2605
2606         return 0;
2607 }
2608
2609 int lfs_setquota_times(int argc, char **argv)
2610 {
2611         int c, rc;
2612         struct if_quotactl qctl;
2613         char *mnt, *obd_type = (char *)qctl.obd_type;
2614         struct obd_dqblk *dqb = &qctl.qc_dqblk;
2615         struct obd_dqinfo *dqi = &qctl.qc_dqinfo;
2616         struct option long_opts[] = {
2617                 {"block-grace",     required_argument, 0, 'b'},
2618                 {"group",           no_argument,       0, 'g'},
2619                 {"inode-grace",     required_argument, 0, 'i'},
2620                 {"times",           no_argument,       0, 't'},
2621                 {"user",            no_argument,       0, 'u'},
2622                 {0, 0, 0, 0}
2623         };
2624
2625         memset(&qctl, 0, sizeof(qctl));
2626         qctl.qc_cmd  = LUSTRE_Q_SETINFO;
2627         qctl.qc_type = UGQUOTA;
2628
2629         while ((c = getopt_long(argc, argv, "b:gi:tu", long_opts, NULL)) != -1) {
2630                 switch (c) {
2631                 case 'u':
2632                 case 'g':
2633                         if (qctl.qc_type != UGQUOTA) {
2634                                 fprintf(stderr, "error: -u and -g can't be used "
2635                                                 "more than once\n");
2636                                 return CMD_HELP;
2637                         }
2638                         qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2639                         break;
2640                 case 'b':
2641                         if ((dqi->dqi_bgrace = str2sec(optarg)) == ULONG_MAX) {
2642                                 fprintf(stderr, "error: bad block-grace: %s\n",
2643                                         optarg);
2644                                 return CMD_HELP;
2645                         }
2646                         dqb->dqb_valid |= QIF_BTIME;
2647                         break;
2648                 case 'i':
2649                         if ((dqi->dqi_igrace = str2sec(optarg)) == ULONG_MAX) {
2650                                 fprintf(stderr, "error: bad inode-grace: %s\n",
2651                                         optarg);
2652                                 return CMD_HELP;
2653                         }
2654                         dqb->dqb_valid |= QIF_ITIME;
2655                         break;
2656                 case 't': /* Yes, of course! */
2657                         break;
2658                 default: /* getopt prints error message for us when opterr != 0 */
2659                         return CMD_HELP;
2660                 }
2661         }
2662
2663         if (qctl.qc_type == UGQUOTA) {
2664                 fprintf(stderr, "error: neither -u nor -g specified\n");
2665                 return CMD_HELP;
2666         }
2667
2668         if (optind != argc - 1) {
2669                 fprintf(stderr, "error: unexpected parameters encountered\n");
2670                 return CMD_HELP;
2671         }
2672
2673         mnt = argv[optind];
2674         rc = llapi_quotactl(mnt, &qctl);
2675         if (rc) {
2676                 if (*obd_type)
2677                         fprintf(stderr, "%s %s ", obd_type,
2678                                 obd_uuid2str(&qctl.obd_uuid));
2679                 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2680                 return rc;
2681         }
2682
2683         return 0;
2684 }
2685
2686 #define BSLIMIT (1 << 0)
2687 #define BHLIMIT (1 << 1)
2688 #define ISLIMIT (1 << 2)
2689 #define IHLIMIT (1 << 3)
2690
2691 int lfs_setquota(int argc, char **argv)
2692 {
2693         int c, rc;
2694         struct if_quotactl qctl;
2695         char *mnt, *obd_type = (char *)qctl.obd_type;
2696         struct obd_dqblk *dqb = &qctl.qc_dqblk;
2697         struct option long_opts[] = {
2698                 {"block-softlimit", required_argument, 0, 'b'},
2699                 {"block-hardlimit", required_argument, 0, 'B'},
2700                 {"group",           required_argument, 0, 'g'},
2701                 {"inode-softlimit", required_argument, 0, 'i'},
2702                 {"inode-hardlimit", required_argument, 0, 'I'},
2703                 {"user",            required_argument, 0, 'u'},
2704                 {0, 0, 0, 0}
2705         };
2706         unsigned limit_mask = 0;
2707         char *endptr;
2708
2709         if (has_times_option(argc, argv))
2710                 return lfs_setquota_times(argc, argv);
2711
2712         memset(&qctl, 0, sizeof(qctl));
2713         qctl.qc_cmd  = LUSTRE_Q_SETQUOTA;
2714         qctl.qc_type = UGQUOTA; /* UGQUOTA makes no sense for setquota,
2715                                  * so it can be used as a marker that qc_type
2716                                  * isn't reinitialized from command line */
2717
2718         while ((c = getopt_long(argc, argv, "b:B:g:i:I:u:", long_opts, NULL)) != -1) {
2719                 switch (c) {
2720                 case 'u':
2721                 case 'g':
2722                         if (qctl.qc_type != UGQUOTA) {
2723                                 fprintf(stderr, "error: -u and -g can't be used"
2724                                                 " more than once\n");
2725                                 return CMD_HELP;
2726                         }
2727                         qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2728                         rc = name2id(&qctl.qc_id, optarg,
2729                                      (qctl.qc_type == USRQUOTA) ? USER : GROUP);
2730                         if (rc) {
2731                                 qctl.qc_id = strtoul(optarg, &endptr, 10);
2732                                 if (*endptr != '\0') {
2733                                         fprintf(stderr, "error: can't find id "
2734                                                 "for name %s\n", optarg);
2735                                         return CMD_HELP;
2736                                 }
2737                         }
2738                         break;
2739                 case 'b':
2740                         ARG2ULL(dqb->dqb_bsoftlimit, optarg, 1024);
2741                         dqb->dqb_bsoftlimit >>= 10;
2742                         limit_mask |= BSLIMIT;
2743                         if (dqb->dqb_bsoftlimit &&
2744                             dqb->dqb_bsoftlimit <= 1024) /* <= 1M? */
2745                                 fprintf(stderr, "warning: block softlimit is "
2746                                         "smaller than the miminal qunit size, "
2747                                         "please see the help of setquota or "
2748                                         "Lustre manual for details.\n");
2749                         break;
2750                 case 'B':
2751                         ARG2ULL(dqb->dqb_bhardlimit, optarg, 1024);
2752                         dqb->dqb_bhardlimit >>= 10;
2753                         limit_mask |= BHLIMIT;
2754                         if (dqb->dqb_bhardlimit &&
2755                             dqb->dqb_bhardlimit <= 1024) /* <= 1M? */
2756                                 fprintf(stderr, "warning: block hardlimit is "
2757                                         "smaller than the miminal qunit size, "
2758                                         "please see the help of setquota or "
2759                                         "Lustre manual for details.\n");
2760                         break;
2761                 case 'i':
2762                         ARG2ULL(dqb->dqb_isoftlimit, optarg, 1);
2763                         limit_mask |= ISLIMIT;
2764                         if (dqb->dqb_isoftlimit &&
2765                             dqb->dqb_isoftlimit <= 1024) /* <= 1K inodes? */
2766                                 fprintf(stderr, "warning: inode softlimit is "
2767                                         "smaller than the miminal qunit size, "
2768                                         "please see the help of setquota or "
2769                                         "Lustre manual for details.\n");
2770                         break;
2771                 case 'I':
2772                         ARG2ULL(dqb->dqb_ihardlimit, optarg, 1);
2773                         limit_mask |= IHLIMIT;
2774                         if (dqb->dqb_ihardlimit &&
2775                             dqb->dqb_ihardlimit <= 1024) /* <= 1K inodes? */
2776                                 fprintf(stderr, "warning: inode hardlimit is "
2777                                         "smaller than the miminal qunit size, "
2778                                         "please see the help of setquota or "
2779                                         "Lustre manual for details.\n");
2780                         break;
2781                 default: /* getopt prints error message for us when opterr != 0 */
2782                         return CMD_HELP;
2783                 }
2784         }
2785
2786         if (qctl.qc_type == UGQUOTA) {
2787                 fprintf(stderr, "error: neither -u nor -g was specified\n");
2788                 return CMD_HELP;
2789         }
2790
2791         if (limit_mask == 0) {
2792                 fprintf(stderr, "error: at least one limit must be specified\n");
2793                 return CMD_HELP;
2794         }
2795
2796         if (optind != argc - 1) {
2797                 fprintf(stderr, "error: unexpected parameters encountered\n");
2798                 return CMD_HELP;
2799         }
2800
2801         mnt = argv[optind];
2802
2803         if ((!(limit_mask & BHLIMIT) ^ !(limit_mask & BSLIMIT)) ||
2804             (!(limit_mask & IHLIMIT) ^ !(limit_mask & ISLIMIT))) {
2805                 /* sigh, we can't just set blimits/ilimits */
2806                 struct if_quotactl tmp_qctl = {.qc_cmd  = LUSTRE_Q_GETQUOTA,
2807                                                .qc_type = qctl.qc_type,
2808                                                .qc_id   = qctl.qc_id};
2809
2810                 rc = llapi_quotactl(mnt, &tmp_qctl);
2811                 if (rc < 0) {
2812                         fprintf(stderr, "error: setquota failed while retrieving"
2813                                         " current quota settings (%s)\n",
2814                                         strerror(-rc));
2815                         return rc;
2816                 }
2817
2818                 if (!(limit_mask & BHLIMIT))
2819                         dqb->dqb_bhardlimit = tmp_qctl.qc_dqblk.dqb_bhardlimit;
2820                 if (!(limit_mask & BSLIMIT))
2821                         dqb->dqb_bsoftlimit = tmp_qctl.qc_dqblk.dqb_bsoftlimit;
2822                 if (!(limit_mask & IHLIMIT))
2823                         dqb->dqb_ihardlimit = tmp_qctl.qc_dqblk.dqb_ihardlimit;
2824                 if (!(limit_mask & ISLIMIT))
2825                         dqb->dqb_isoftlimit = tmp_qctl.qc_dqblk.dqb_isoftlimit;
2826
2827                 /* Keep grace times if we have got no softlimit arguments */
2828                 if ((limit_mask & BHLIMIT) && !(limit_mask & BSLIMIT)) {
2829                         dqb->dqb_valid |= QIF_BTIME;
2830                         dqb->dqb_btime = tmp_qctl.qc_dqblk.dqb_btime;
2831                 }
2832
2833                 if ((limit_mask & IHLIMIT) && !(limit_mask & ISLIMIT)) {
2834                         dqb->dqb_valid |= QIF_ITIME;
2835                         dqb->dqb_itime = tmp_qctl.qc_dqblk.dqb_itime;
2836                 }
2837         }
2838
2839         dqb->dqb_valid |= (limit_mask & (BHLIMIT | BSLIMIT)) ? QIF_BLIMITS : 0;
2840         dqb->dqb_valid |= (limit_mask & (IHLIMIT | ISLIMIT)) ? QIF_ILIMITS : 0;
2841
2842         rc = llapi_quotactl(mnt, &qctl);
2843         if (rc) {
2844                 if (*obd_type)
2845                         fprintf(stderr, "%s %s ", obd_type,
2846                                 obd_uuid2str(&qctl.obd_uuid));
2847                 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2848                 return rc;
2849         }
2850
2851         return 0;
2852 }
2853
2854 static inline char *type2name(int check_type)
2855 {
2856         if (check_type == USRQUOTA)
2857                 return "user";
2858         else if (check_type == GRPQUOTA)
2859                 return "group";
2860         else
2861                 return "unknown";
2862 }
2863
2864 /* Converts seconds value into format string
2865  * result is returned in buf
2866  * Notes:
2867  *        1. result is in descenting order: 1w2d3h4m5s
2868  *        2. zero fields are not filled (except for p. 3): 5d1s
2869  *        3. zero seconds value is presented as "0s"
2870  */
2871 static char * __sec2str(time_t seconds, char *buf)
2872 {
2873         const char spec[] = "smhdw";
2874         const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2875         unsigned long c;
2876         char *tail = buf;
2877         int i;
2878
2879         for (i = sizeof(mult) / sizeof(mult[0]) - 1 ; i >= 0; i--) {
2880                 c = seconds / mult[i];
2881
2882                 if (c > 0 || (i == 0 && buf == tail))
2883                         tail += snprintf(tail, 40-(tail-buf), "%lu%c", c, spec[i]);
2884
2885                 seconds %= mult[i];
2886         }
2887
2888         return tail;
2889 }
2890
2891 static void sec2str(time_t seconds, char *buf, int rc)
2892 {
2893         char *tail = buf;
2894
2895         if (rc)
2896                 *tail++ = '[';
2897
2898         tail = __sec2str(seconds, tail);
2899
2900         if (rc && tail - buf < 39) {
2901                 *tail++ = ']';
2902                 *tail++ = 0;
2903         }
2904 }
2905
2906 static void diff2str(time_t seconds, char *buf, time_t now)
2907 {
2908
2909         buf[0] = 0;
2910         if (!seconds)
2911                 return;
2912         if (seconds <= now) {
2913                 strcpy(buf, "none");
2914                 return;
2915         }
2916         __sec2str(seconds - now, buf);
2917 }
2918
2919 static void print_quota_title(char *name, struct if_quotactl *qctl,
2920                               bool human_readable)
2921 {
2922         printf("Disk quotas for %s %s (%cid %u):\n",
2923                type2name(qctl->qc_type), name,
2924                *type2name(qctl->qc_type), qctl->qc_id);
2925         printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n",
2926                "Filesystem", human_readable ? "used" : "kbytes",
2927                "quota", "limit", "grace",
2928                "files", "quota", "limit", "grace");
2929 }
2930
2931 static void kbytes2str(__u64 num, char *buf, int buflen, bool h)
2932 {
2933         if (!h) {
2934                 snprintf(buf, buflen, LPU64, num);
2935         } else {
2936                 if (num >> 40)
2937                         snprintf(buf, buflen, "%5.4gP",
2938                                  (double)num / ((__u64)1 << 40));
2939                 else if (num >> 30)
2940                         snprintf(buf, buflen, "%5.4gT",
2941                                  (double)num / (1 << 30));
2942                 else if (num >> 20)
2943                         snprintf(buf, buflen, "%5.4gG",
2944                                  (double)num / (1 << 20));
2945                 else if (num >> 10)
2946                         snprintf(buf, buflen, "%5.4gM",
2947                                  (double)num / (1 << 10));
2948                 else
2949                         snprintf(buf, buflen, LPU64"%s", num, "k");
2950         }
2951 }
2952
2953 #define STRBUF_LEN      32
2954 static void print_quota(char *mnt, struct if_quotactl *qctl, int type,
2955                         int rc, bool h)
2956 {
2957         time_t now;
2958
2959         time(&now);
2960
2961         if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == Q_GETOQUOTA) {
2962                 int bover = 0, iover = 0;
2963                 struct obd_dqblk *dqb = &qctl->qc_dqblk;
2964                 char numbuf[3][STRBUF_LEN];
2965                 char timebuf[40];
2966                 char strbuf[STRBUF_LEN];
2967
2968                 if (dqb->dqb_bhardlimit &&
2969                     lustre_stoqb(dqb->dqb_curspace) >= dqb->dqb_bhardlimit) {
2970                         bover = 1;
2971                 } else if (dqb->dqb_bsoftlimit && dqb->dqb_btime) {
2972                         if (dqb->dqb_btime > now) {
2973                                 bover = 2;
2974                         } else {
2975                                 bover = 3;
2976                         }
2977                 }
2978
2979                 if (dqb->dqb_ihardlimit &&
2980                     dqb->dqb_curinodes >= dqb->dqb_ihardlimit) {
2981                         iover = 1;
2982                 } else if (dqb->dqb_isoftlimit && dqb->dqb_itime) {
2983                         if (dqb->dqb_itime > now) {
2984                                 iover = 2;
2985                         } else {
2986                                 iover = 3;
2987                         }
2988                 }
2989
2990
2991                 if (strlen(mnt) > 15)
2992                         printf("%s\n%15s", mnt, "");
2993                 else
2994                         printf("%15s", mnt);
2995
2996                 if (bover)
2997                         diff2str(dqb->dqb_btime, timebuf, now);
2998
2999                 kbytes2str(lustre_stoqb(dqb->dqb_curspace),
3000                            strbuf, sizeof(strbuf), h);
3001                 if (rc == -EREMOTEIO)
3002                         sprintf(numbuf[0], "%s*", strbuf);
3003                 else
3004                         sprintf(numbuf[0], (dqb->dqb_valid & QIF_SPACE) ?
3005                                 "%s" : "[%s]", strbuf);
3006
3007                 kbytes2str(dqb->dqb_bsoftlimit, strbuf, sizeof(strbuf), h);
3008                 if (type == QC_GENERAL)
3009                         sprintf(numbuf[1], (dqb->dqb_valid & QIF_BLIMITS) ?
3010                                 "%s" : "[%s]", strbuf);
3011                 else
3012                         sprintf(numbuf[1], "%s", "-");
3013
3014                 kbytes2str(dqb->dqb_bhardlimit, strbuf, sizeof(strbuf), h);
3015                 sprintf(numbuf[2], (dqb->dqb_valid & QIF_BLIMITS) ?
3016                         "%s" : "[%s]", strbuf);
3017
3018                 printf(" %7s%c %6s %7s %7s",
3019                        numbuf[0], bover ? '*' : ' ', numbuf[1],
3020                        numbuf[2], bover > 1 ? timebuf : "-");
3021
3022                 if (iover)
3023                         diff2str(dqb->dqb_itime, timebuf, now);
3024
3025                 sprintf(numbuf[0], (dqb->dqb_valid & QIF_INODES) ?
3026                         LPU64 : "["LPU64"]", dqb->dqb_curinodes);
3027
3028                 if (type == QC_GENERAL)
3029                         sprintf(numbuf[1], (dqb->dqb_valid & QIF_ILIMITS) ?
3030                                 LPU64 : "["LPU64"]", dqb->dqb_isoftlimit);
3031                 else
3032                         sprintf(numbuf[1], "%s", "-");
3033
3034                 sprintf(numbuf[2], (dqb->dqb_valid & QIF_ILIMITS) ?
3035                         LPU64 : "["LPU64"]", dqb->dqb_ihardlimit);
3036
3037                 if (type != QC_OSTIDX)
3038                         printf(" %7s%c %6s %7s %7s",
3039                                numbuf[0], iover ? '*' : ' ', numbuf[1],
3040                                numbuf[2], iover > 1 ? timebuf : "-");
3041                 else
3042                         printf(" %7s %7s %7s %7s", "-", "-", "-", "-");
3043                 printf("\n");
3044
3045         } else if (qctl->qc_cmd == LUSTRE_Q_GETINFO ||
3046                    qctl->qc_cmd == Q_GETOINFO) {
3047                 char bgtimebuf[40];
3048                 char igtimebuf[40];
3049
3050                 sec2str(qctl->qc_dqinfo.dqi_bgrace, bgtimebuf, rc);
3051                 sec2str(qctl->qc_dqinfo.dqi_igrace, igtimebuf, rc);
3052                 printf("Block grace time: %s; Inode grace time: %s\n",
3053                        bgtimebuf, igtimebuf);
3054         }
3055 }
3056
3057 static int print_obd_quota(char *mnt, struct if_quotactl *qctl, int is_mdt,
3058                            bool h, __u64 *total)
3059 {
3060         int rc = 0, rc1 = 0, count = 0;
3061         __u32 valid = qctl->qc_valid;
3062
3063         rc = llapi_get_obd_count(mnt, &count, is_mdt);
3064         if (rc) {
3065                 fprintf(stderr, "can not get %s count: %s\n",
3066                         is_mdt ? "mdt": "ost", strerror(-rc));
3067                 return rc;
3068         }
3069
3070         for (qctl->qc_idx = 0; qctl->qc_idx < count; qctl->qc_idx++) {
3071                 qctl->qc_valid = is_mdt ? QC_MDTIDX : QC_OSTIDX;
3072                 rc = llapi_quotactl(mnt, qctl);
3073                 if (rc) {
3074                         /* It is remote client case. */
3075                         if (-rc == EOPNOTSUPP) {
3076                                 rc = 0;
3077                                 goto out;
3078                         }
3079
3080                         if (!rc1)
3081                                 rc1 = rc;
3082                         fprintf(stderr, "quotactl %s%d failed.\n",
3083                                 is_mdt ? "mdt": "ost", qctl->qc_idx);
3084                         continue;
3085                 }
3086
3087                 print_quota(obd_uuid2str(&qctl->obd_uuid), qctl,
3088                             qctl->qc_valid, 0, h);
3089                 *total += is_mdt ? qctl->qc_dqblk.dqb_ihardlimit :
3090                                    qctl->qc_dqblk.dqb_bhardlimit;
3091         }
3092 out:
3093         qctl->qc_valid = valid;
3094         return rc ? : rc1;
3095 }
3096
3097 static int lfs_quota(int argc, char **argv)
3098 {
3099         int c;
3100         char *mnt, *name = NULL;
3101         struct if_quotactl qctl = { .qc_cmd = LUSTRE_Q_GETQUOTA,
3102                                     .qc_type = UGQUOTA };
3103         char *obd_type = (char *)qctl.obd_type;
3104         char *obd_uuid = (char *)qctl.obd_uuid.uuid;
3105         int rc, rc1 = 0, rc2 = 0, rc3 = 0,
3106             verbose = 0, pass = 0, quiet = 0, inacc;
3107         char *endptr;
3108         __u32 valid = QC_GENERAL, idx = 0;
3109         __u64 total_ialloc = 0, total_balloc = 0;
3110         bool human_readable = false;
3111
3112         while ((c = getopt(argc, argv, "gi:I:o:qtuvh")) != -1) {
3113                 switch (c) {
3114                 case 'u':
3115                         if (qctl.qc_type != UGQUOTA) {
3116                                 fprintf(stderr, "error: use either -u or -g\n");
3117                                 return CMD_HELP;
3118                         }
3119                         qctl.qc_type = USRQUOTA;
3120                         break;
3121                 case 'g':
3122                         if (qctl.qc_type != UGQUOTA) {
3123                                 fprintf(stderr, "error: use either -u or -g\n");
3124                                 return CMD_HELP;
3125                         }
3126                         qctl.qc_type = GRPQUOTA;
3127                         break;
3128                 case 't':
3129                         qctl.qc_cmd = LUSTRE_Q_GETINFO;
3130                         break;
3131                 case 'o':
3132                         valid = qctl.qc_valid = QC_UUID;
3133                         strlcpy(obd_uuid, optarg, sizeof(qctl.obd_uuid));
3134                         break;
3135                 case 'i':
3136                         valid = qctl.qc_valid = QC_MDTIDX;
3137                         idx = qctl.qc_idx = atoi(optarg);
3138                         break;
3139                 case 'I':
3140                         valid = qctl.qc_valid = QC_OSTIDX;
3141                         idx = qctl.qc_idx = atoi(optarg);
3142                         break;
3143                 case 'v':
3144                         verbose = 1;
3145                         break;
3146                 case 'q':
3147                         quiet = 1;
3148                         break;
3149                 case 'h':
3150                         human_readable = true;
3151                         break;
3152                 default:
3153                         fprintf(stderr, "error: %s: option '-%c' "
3154                                         "unrecognized\n", argv[0], c);
3155                         return CMD_HELP;
3156                 }
3157         }
3158
3159         /* current uid/gid info for "lfs quota /path/to/lustre/mount" */
3160         if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && qctl.qc_type == UGQUOTA &&
3161             optind == argc - 1) {
3162 ug_output:
3163                 memset(&qctl, 0, sizeof(qctl)); /* spoiled by print_*_quota */
3164                 qctl.qc_cmd = LUSTRE_Q_GETQUOTA;
3165                 qctl.qc_valid = valid;
3166                 qctl.qc_idx = idx;
3167                 if (pass++ == 0) {
3168                         qctl.qc_type = USRQUOTA;
3169                         qctl.qc_id = geteuid();
3170                 } else {
3171                         qctl.qc_type = GRPQUOTA;
3172                         qctl.qc_id = getegid();
3173                 }
3174                 rc = id2name(&name, qctl.qc_id,
3175                              (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3176                 if (rc)
3177                         name = "<unknown>";
3178         /* lfs quota -u username /path/to/lustre/mount */
3179         } else if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) {
3180                 /* options should be followed by u/g-name and mntpoint */
3181                 if (optind + 2 != argc || qctl.qc_type == UGQUOTA) {
3182                         fprintf(stderr, "error: missing quota argument(s)\n");
3183                         return CMD_HELP;
3184                 }
3185
3186                 name = argv[optind++];
3187                 rc = name2id(&qctl.qc_id, name,
3188                              (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3189                 if (rc) {
3190                         qctl.qc_id = strtoul(name, &endptr, 10);
3191                         if (*endptr != '\0') {
3192                                 fprintf(stderr, "error: can't find id for name "
3193                                         "%s\n", name);
3194                                 return CMD_HELP;
3195                         }
3196                 }
3197         } else if (optind + 1 != argc || qctl.qc_type == UGQUOTA) {
3198                 fprintf(stderr, "error: missing quota info argument(s)\n");
3199                 return CMD_HELP;
3200         }
3201
3202         mnt = argv[optind];
3203
3204         rc1 = llapi_quotactl(mnt, &qctl);
3205         if (rc1 < 0) {
3206                 switch (rc1) {
3207                 case -ESRCH:
3208                         fprintf(stderr, "%s quotas are not enabled.\n",
3209                                 qctl.qc_type == USRQUOTA ? "user" : "group");
3210                         goto out;
3211                 case -EPERM:
3212                         fprintf(stderr, "Permission denied.\n");
3213                 case -ENOENT:
3214                         /* We already got a "No such file..." message. */
3215                         goto out;
3216                 default:
3217                         fprintf(stderr, "Unexpected quotactl error: %s\n",
3218                                 strerror(-rc1));
3219                 }
3220         }
3221
3222         if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && !quiet)
3223                 print_quota_title(name, &qctl, human_readable);
3224
3225         if (rc1 && *obd_type)
3226                 fprintf(stderr, "%s %s ", obd_type, obd_uuid);
3227
3228         if (qctl.qc_valid != QC_GENERAL)
3229                 mnt = "";
3230
3231         inacc = (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) &&
3232                 ((qctl.qc_dqblk.dqb_valid & (QIF_LIMITS|QIF_USAGE)) !=
3233                  (QIF_LIMITS|QIF_USAGE));
3234
3235         print_quota(mnt, &qctl, QC_GENERAL, rc1, human_readable);
3236
3237         if (qctl.qc_valid == QC_GENERAL && qctl.qc_cmd != LUSTRE_Q_GETINFO &&
3238             verbose) {
3239                 char strbuf[STRBUF_LEN];
3240
3241                 rc2 = print_obd_quota(mnt, &qctl, 1, human_readable,
3242                                       &total_ialloc);
3243                 rc3 = print_obd_quota(mnt, &qctl, 0, human_readable,
3244                                       &total_balloc);
3245                 kbytes2str(total_balloc, strbuf, sizeof(strbuf),
3246                            human_readable);
3247                 printf("Total allocated inode limit: "LPU64", total "
3248                        "allocated block limit: %s\n", total_ialloc, strbuf);
3249         }
3250
3251         if (rc1 || rc2 || rc3 || inacc)
3252                 printf("Some errors happened when getting quota info. "
3253                        "Some devices may be not working or deactivated. "
3254                        "The data in \"[]\" is inaccurate.\n");
3255
3256 out:
3257         if (pass == 1)
3258                 goto ug_output;
3259
3260         return rc1;
3261 }
3262 #endif /* HAVE_SYS_QUOTA_H! */
3263
3264 static int flushctx_ioctl(char *mp)
3265 {
3266         int fd, rc;
3267
3268         fd = open(mp, O_RDONLY);
3269         if (fd == -1) {
3270                 fprintf(stderr, "flushctx: error open %s: %s\n",
3271                         mp, strerror(errno));
3272                 return -1;
3273         }
3274
3275         rc = ioctl(fd, LL_IOC_FLUSHCTX);
3276         if (rc == -1)
3277                 fprintf(stderr, "flushctx: error ioctl %s: %s\n",
3278                         mp, strerror(errno));
3279
3280         close(fd);
3281         return rc;
3282 }
3283
3284 static int lfs_flushctx(int argc, char **argv)
3285 {
3286         int     kdestroy = 0, c;
3287         char    mntdir[PATH_MAX] = {'\0'};
3288         int     index = 0;
3289         int     rc = 0;
3290
3291         while ((c = getopt(argc, argv, "k")) != -1) {
3292                 switch (c) {
3293                 case 'k':
3294                         kdestroy = 1;
3295                         break;
3296                 default:
3297                         fprintf(stderr, "error: %s: option '-%c' "
3298                                         "unrecognized\n", argv[0], c);
3299                         return CMD_HELP;
3300                 }
3301         }
3302
3303         if (kdestroy) {
3304             if ((rc = system("kdestroy > /dev/null")) != 0) {
3305                 rc = WEXITSTATUS(rc);
3306                 fprintf(stderr, "error destroying tickets: %d, continuing\n", rc);
3307             }
3308         }
3309
3310         if (optind >= argc) {
3311                 /* flush for all mounted lustre fs. */
3312                 while (!llapi_search_mounts(NULL, index++, mntdir, NULL)) {
3313                         /* Check if we have a mount point */
3314                         if (mntdir[0] == '\0')
3315                                 continue;
3316
3317                         if (flushctx_ioctl(mntdir))
3318                                 rc = -1;
3319
3320                         mntdir[0] = '\0'; /* avoid matching in next loop */
3321                 }
3322         } else {
3323                 /* flush fs as specified */
3324                 while (optind < argc) {
3325                         if (flushctx_ioctl(argv[optind++]))
3326                                 rc = -1;
3327                 }
3328         }
3329         return rc;
3330 }
3331
3332 static int lfs_lsetfacl(int argc, char **argv)
3333 {
3334         argv[0]++;
3335         return(llapi_lsetfacl(argc, argv));
3336 }
3337
3338 static int lfs_lgetfacl(int argc, char **argv)
3339 {
3340         argv[0]++;
3341         return(llapi_lgetfacl(argc, argv));
3342 }
3343
3344 static int lfs_rsetfacl(int argc, char **argv)
3345 {
3346         argv[0]++;
3347         return(llapi_rsetfacl(argc, argv));
3348 }
3349
3350 static int lfs_rgetfacl(int argc, char **argv)
3351 {
3352         argv[0]++;
3353         return(llapi_rgetfacl(argc, argv));
3354 }
3355
3356 static int lfs_cp(int argc, char **argv)
3357 {
3358         return(llapi_cp(argc, argv));
3359 }
3360
3361 static int lfs_ls(int argc, char **argv)
3362 {
3363         return(llapi_ls(argc, argv));
3364 }
3365
3366 static int lfs_changelog(int argc, char **argv)
3367 {
3368         void *changelog_priv;
3369         struct changelog_rec *rec;
3370         long long startrec = 0, endrec = 0;
3371         char *mdd;
3372         struct option long_opts[] = {
3373                 {"follow", no_argument, 0, 'f'},
3374                 {0, 0, 0, 0}
3375         };
3376         char short_opts[] = "f";
3377         int rc, follow = 0;
3378
3379         while ((rc = getopt_long(argc, argv, short_opts,
3380                                 long_opts, NULL)) != -1) {
3381                 switch (rc) {
3382                 case 'f':
3383                         follow++;
3384                         break;
3385                 case '?':
3386                         return CMD_HELP;
3387                 default:
3388                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3389                                 argv[0], argv[optind - 1]);
3390                         return CMD_HELP;
3391                 }
3392         }
3393         if (optind >= argc)
3394                 return CMD_HELP;
3395
3396         mdd = argv[optind++];
3397         if (argc > optind)
3398                 startrec = strtoll(argv[optind++], NULL, 10);
3399         if (argc > optind)
3400                 endrec = strtoll(argv[optind++], NULL, 10);
3401
3402         rc = llapi_changelog_start(&changelog_priv,
3403                                    CHANGELOG_FLAG_BLOCK |
3404                                    CHANGELOG_FLAG_JOBID |
3405                                    (follow ? CHANGELOG_FLAG_FOLLOW : 0),
3406                                    mdd, startrec);
3407         if (rc < 0) {
3408                 fprintf(stderr, "Can't start changelog: %s\n",
3409                         strerror(errno = -rc));
3410                 return rc;
3411         }
3412
3413         while ((rc = llapi_changelog_recv(changelog_priv, &rec)) == 0) {
3414                 time_t secs;
3415                 struct tm ts;
3416
3417                 if (endrec && rec->cr_index > endrec) {
3418                         llapi_changelog_free(&rec);
3419                         break;
3420                 }
3421                 if (rec->cr_index < startrec) {
3422                         llapi_changelog_free(&rec);
3423                         continue;
3424                 }
3425
3426                 secs = rec->cr_time >> 30;
3427                 gmtime_r(&secs, &ts);
3428                 printf(LPU64" %02d%-5s %02d:%02d:%02d.%06d %04d.%02d.%02d "
3429                        "0x%x t="DFID, rec->cr_index, rec->cr_type,
3430                        changelog_type2str(rec->cr_type),
3431                        ts.tm_hour, ts.tm_min, ts.tm_sec,
3432                        (int)(rec->cr_time & ((1<<30) - 1)),
3433                        ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday,
3434                        rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
3435
3436                 if (rec->cr_flags & CLF_JOBID) {
3437                         struct changelog_ext_jobid *jid =
3438                                 changelog_rec_jobid(rec);
3439
3440                         if (jid->cr_jobid[0] != '\0')
3441                                 printf(" j=%s", jid->cr_jobid);
3442                 }
3443
3444                 if (rec->cr_namelen)
3445                         printf(" p="DFID" %.*s", PFID(&rec->cr_pfid),
3446                                rec->cr_namelen, changelog_rec_name(rec));
3447
3448                 if (rec->cr_flags & CLF_RENAME) {
3449                         struct changelog_ext_rename *rnm =
3450                                 changelog_rec_rename(rec);
3451
3452                         if (!fid_is_zero(&rnm->cr_sfid))
3453                                 printf(" s="DFID" sp="DFID" %.*s",
3454                                        PFID(&rnm->cr_sfid),
3455                                        PFID(&rnm->cr_spfid),
3456                                        (int)changelog_rec_snamelen(rec),
3457                                        changelog_rec_sname(rec));
3458                 }
3459                 printf("\n");
3460
3461                 llapi_changelog_free(&rec);
3462         }
3463
3464         llapi_changelog_fini(&changelog_priv);
3465
3466         if (rc < 0)
3467                 fprintf(stderr, "Changelog: %s\n", strerror(errno = -rc));
3468
3469         return (rc == 1 ? 0 : rc);
3470 }
3471
3472 static int lfs_changelog_clear(int argc, char **argv)
3473 {
3474         long long endrec;
3475         int rc;
3476
3477         if (argc != 4)
3478                 return CMD_HELP;
3479
3480         endrec = strtoll(argv[3], NULL, 10);
3481
3482         rc = llapi_changelog_clear(argv[1], argv[2], endrec);
3483         if (rc)
3484                 fprintf(stderr, "%s error: %s\n", argv[0],
3485                         strerror(errno = -rc));
3486         return rc;
3487 }
3488
3489 static int lfs_fid2path(int argc, char **argv)
3490 {
3491         struct option long_opts[] = {
3492                 {"cur", no_argument, 0, 'c'},
3493                 {"link", required_argument, 0, 'l'},
3494                 {"rec", required_argument, 0, 'r'},
3495                 {0, 0, 0, 0}
3496         };
3497         char  short_opts[] = "cl:r:";
3498         char *device, *fid, *path;
3499         long long recno = -1;
3500         int linkno = -1;
3501         int lnktmp;
3502         int printcur = 0;
3503         int rc = 0;
3504
3505         while ((rc = getopt_long(argc, argv, short_opts,
3506                                 long_opts, NULL)) != -1) {
3507                 switch (rc) {
3508                 case 'c':
3509                         printcur++;
3510                         break;
3511                 case 'l':
3512                         linkno = strtol(optarg, NULL, 10);
3513                         break;
3514                 case 'r':
3515                         recno = strtoll(optarg, NULL, 10);
3516                         break;
3517                 case '?':
3518                         return CMD_HELP;
3519                 default:
3520                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3521                                 argv[0], argv[optind - 1]);
3522                         return CMD_HELP;
3523                 }
3524         }
3525
3526         if (argc < 3)
3527                 return CMD_HELP;
3528
3529         device = argv[optind++];
3530         path = calloc(1, PATH_MAX);
3531         if (path == NULL) {
3532                 fprintf(stderr, "error: Not enough memory\n");
3533                 return -errno;
3534         }
3535
3536         rc = 0;
3537         while (optind < argc) {
3538                 fid = argv[optind++];
3539
3540                 lnktmp = (linkno >= 0) ? linkno : 0;
3541                 while (1) {
3542                         int oldtmp = lnktmp;
3543                         long long rectmp = recno;
3544                         int rc2;
3545                         rc2 = llapi_fid2path(device, fid, path, PATH_MAX,
3546                                              &rectmp, &lnktmp);
3547                         if (rc2 < 0) {
3548                                 fprintf(stderr, "%s: error on FID %s: %s\n",
3549                                         argv[0], fid, strerror(errno = -rc2));
3550                                 if (rc == 0)
3551                                         rc = rc2;
3552                                 break;
3553                         }
3554
3555                         if (printcur)
3556                                 fprintf(stdout, "%lld ", rectmp);
3557                         if (device[0] == '/') {
3558                                 fprintf(stdout, "%s", device);
3559                                 if (device[strlen(device) - 1] != '/')
3560                                         fprintf(stdout, "/");
3561                         } else if (path[0] == '\0') {
3562                                 fprintf(stdout, "/");
3563                         }
3564                         fprintf(stdout, "%s\n", path);
3565
3566                         if (linkno >= 0)
3567                                 /* specified linkno */
3568                                 break;
3569                         if (oldtmp == lnktmp)
3570                                 /* no more links */
3571                                 break;
3572                 }
3573         }
3574
3575         free(path);
3576         return rc;
3577 }
3578
3579 static int lfs_path2fid(int argc, char **argv)
3580 {
3581         struct option     long_opts[] = {
3582                 {"parents", no_argument, 0, 'p'},
3583                 {0, 0, 0, 0}
3584         };
3585         char            **path;
3586         const char        short_opts[] = "p";
3587         const char       *sep = "";
3588         lustre_fid        fid;
3589         int               rc = 0;
3590         bool              show_parents = false;
3591
3592         while ((rc = getopt_long(argc, argv, short_opts,
3593                                  long_opts, NULL)) != -1) {
3594                 switch (rc) {
3595                 case 'p':
3596                         show_parents = true;
3597                         break;
3598                 default:
3599                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3600                                 argv[0], argv[optind - 1]);
3601                         return CMD_HELP;
3602                 }
3603         }
3604
3605         if (optind > argc - 1)
3606                 return CMD_HELP;
3607         else if (optind < argc - 1)
3608                 sep = ": ";
3609
3610         rc = 0;
3611         for (path = argv + optind; *path != NULL; path++) {
3612                 int err = 0;
3613                 if (!show_parents) {
3614                         err = llapi_path2fid(*path, &fid);
3615                         if (!err)
3616                                 printf("%s%s"DFID"\n",
3617                                        *sep != '\0' ? *path : "", sep,
3618                                        PFID(&fid));
3619                 } else {
3620                         char            name[NAME_MAX + 1];
3621                         unsigned int    linkno = 0;
3622
3623                         while ((err = llapi_path2parent(*path, linkno, &fid,
3624                                                 name, sizeof(name))) == 0) {
3625                                 if (*sep != '\0' && linkno == 0)
3626                                         printf("%s%s", *path, sep);
3627
3628                                 printf("%s"DFID"/%s", linkno != 0 ? "\t" : "",
3629                                        PFID(&fid), name);
3630                                 linkno++;
3631                         }
3632
3633                         /* err == -ENODATA is end-of-loop */
3634                         if (linkno > 0 && err == -ENODATA) {
3635                                 printf("\n");
3636                                 err = 0;
3637                         }
3638                 }
3639
3640                 if (err) {
3641                         fprintf(stderr, "%s: can't get %sfid for %s: %s\n",
3642                                 argv[0], show_parents ? "parent " : "", *path,
3643                                 strerror(-err));
3644                         if (rc == 0) {
3645                                 rc = err;
3646                                 errno = -err;
3647                         }
3648                 }
3649         }
3650
3651         return rc;
3652 }
3653
3654 static int lfs_data_version(int argc, char **argv)
3655 {
3656         char *path;
3657         __u64 data_version;
3658         int fd;
3659         int rc;
3660         int c;
3661         int data_version_flags = LL_DV_RD_FLUSH; /* Read by default */
3662
3663         if (argc < 2)
3664                 return CMD_HELP;
3665
3666         while ((c = getopt(argc, argv, "nrw")) != -1) {
3667                 switch (c) {
3668                 case 'n':
3669                         data_version_flags = 0;
3670                         break;
3671                 case 'r':
3672                         data_version_flags |= LL_DV_RD_FLUSH;
3673                         break;
3674                 case 'w':
3675                         data_version_flags |= LL_DV_WR_FLUSH;
3676                         break;
3677                 default:
3678                         return CMD_HELP;
3679                 }
3680         }
3681         if (optind == argc)
3682                 return CMD_HELP;
3683
3684         path = argv[optind];
3685         fd = open(path, O_RDONLY);
3686         if (fd < 0)
3687                 err(errno, "cannot open file %s", path);
3688
3689         rc = llapi_get_data_version(fd, &data_version, data_version_flags);
3690         if (rc < 0)
3691                 err(errno, "cannot get version for %s", path);
3692         else
3693                 printf(LPU64 "\n", data_version);
3694
3695         close(fd);
3696         return rc;
3697 }
3698
3699 static int lfs_hsm_state(int argc, char **argv)
3700 {
3701         int rc;
3702         int i = 1;
3703         char *path;
3704         struct hsm_user_state hus;
3705
3706         if (argc < 2)
3707                 return CMD_HELP;
3708
3709         do {
3710                 path = argv[i];
3711
3712                 rc = llapi_hsm_state_get(path, &hus);
3713                 if (rc) {
3714                         fprintf(stderr, "can't get hsm state for %s: %s\n",
3715                                 path, strerror(errno = -rc));
3716                         return rc;
3717                 }
3718
3719                 /* Display path name and status flags */
3720                 printf("%s: (0x%08x)", path, hus.hus_states);
3721
3722                 if (hus.hus_states & HS_RELEASED)
3723                         printf(" released");
3724                 if (hus.hus_states & HS_EXISTS)
3725                         printf(" exists");
3726                 if (hus.hus_states & HS_DIRTY)
3727                         printf(" dirty");
3728                 if (hus.hus_states & HS_ARCHIVED)
3729                         printf(" archived");
3730                 /* Display user-settable flags */
3731                 if (hus.hus_states & HS_NORELEASE)
3732                         printf(" never_release");
3733                 if (hus.hus_states & HS_NOARCHIVE)
3734                         printf(" never_archive");
3735                 if (hus.hus_states & HS_LOST)
3736                         printf(" lost_from_hsm");
3737
3738                 if (hus.hus_archive_id != 0)
3739                         printf(", archive_id:%d", hus.hus_archive_id);
3740                 printf("\n");
3741
3742         } while (++i < argc);
3743
3744         return 0;
3745 }
3746
3747 #define LFS_HSM_SET   0
3748 #define LFS_HSM_CLEAR 1
3749
3750 /**
3751  * Generic function to set or clear HSM flags.
3752  * Used by hsm_set and hsm_clear.
3753  *
3754  * @mode  if LFS_HSM_SET, set the flags, if LFS_HSM_CLEAR, clear the flags.
3755  */
3756 static int lfs_hsm_change_flags(int argc, char **argv, int mode)
3757 {
3758         struct option long_opts[] = {
3759                 {"lost", 0, 0, 'l'},
3760                 {"norelease", 0, 0, 'r'},
3761                 {"noarchive", 0, 0, 'a'},
3762                 {"archived", 0, 0, 'A'},
3763                 {"dirty", 0, 0, 'd'},
3764                 {"exists", 0, 0, 'e'},
3765                 {0, 0, 0, 0}
3766         };
3767         char short_opts[] = "lraAde";
3768         __u64 mask = 0;
3769         int c, rc;
3770         char *path;
3771
3772         if (argc < 3)
3773                 return CMD_HELP;
3774
3775         while ((c = getopt_long(argc, argv, short_opts,
3776                                 long_opts, NULL)) != -1) {
3777                 switch (c) {
3778                 case 'l':
3779                         mask |= HS_LOST;
3780                         break;
3781                 case 'a':
3782                         mask |= HS_NOARCHIVE;
3783                         break;
3784                 case 'A':
3785                         mask |= HS_ARCHIVED;
3786                         break;
3787                 case 'r':
3788                         mask |= HS_NORELEASE;
3789                         break;
3790                 case 'd':
3791                         mask |= HS_DIRTY;
3792                         break;
3793                 case 'e':
3794                         mask |= HS_EXISTS;
3795                         break;
3796                 case '?':
3797                         return CMD_HELP;
3798                 default:
3799                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3800                                 argv[0], argv[optind - 1]);
3801                         return CMD_HELP;
3802                 }
3803         }
3804
3805         /* User should have specified a flag */
3806         if (mask == 0)
3807                 return CMD_HELP;
3808
3809         while (optind < argc) {
3810
3811                 path = argv[optind];
3812
3813                 /* If mode == 0, this means we apply the mask. */
3814                 if (mode == LFS_HSM_SET)
3815                         rc = llapi_hsm_state_set(path, mask, 0, 0);
3816                 else
3817                         rc = llapi_hsm_state_set(path, 0, mask, 0);
3818
3819                 if (rc != 0) {
3820                         fprintf(stderr, "Can't change hsm flags for %s: %s\n",
3821                                 path, strerror(errno = -rc));
3822                         return rc;
3823                 }
3824                 optind++;
3825         }
3826
3827         return 0;
3828 }
3829
3830 static int lfs_hsm_action(int argc, char **argv)
3831 {
3832         int                              rc;
3833         int                              i = 1;
3834         char                            *path;
3835         struct hsm_current_action        hca;
3836         struct hsm_extent                he;
3837         enum hsm_user_action             hua;
3838         enum hsm_progress_states         hps;
3839
3840         if (argc < 2)
3841                 return CMD_HELP;
3842
3843         do {
3844                 path = argv[i];
3845
3846                 rc = llapi_hsm_current_action(path, &hca);
3847                 if (rc) {
3848                         fprintf(stderr, "can't get hsm action for %s: %s\n",
3849                                 path, strerror(errno = -rc));
3850                         return rc;
3851                 }
3852                 he = hca.hca_location;
3853                 hua = hca.hca_action;
3854                 hps = hca.hca_state;
3855
3856                 printf("%s: %s", path, hsm_user_action2name(hua));
3857
3858                 /* Skip file without action */
3859                 if (hca.hca_action == HUA_NONE) {
3860                         printf("\n");
3861                         continue;
3862                 }
3863
3864                 printf(" %s ", hsm_progress_state2name(hps));
3865
3866                 if ((hps == HPS_RUNNING) &&
3867                     (hua == HUA_ARCHIVE || hua == HUA_RESTORE))
3868                         printf("(%llu bytes moved)\n",
3869                                (unsigned long long)he.length);
3870                 else if ((he.offset + he.length) == LUSTRE_EOF)
3871                         printf("(from %llu to EOF)\n",
3872                                (unsigned long long)he.offset);
3873                 else
3874                         printf("(from %llu to %llu)\n",
3875                                (unsigned long long)he.offset,
3876                                (unsigned long long)(he.offset + he.length));
3877
3878         } while (++i < argc);
3879
3880         return 0;
3881 }
3882
3883 static int lfs_hsm_set(int argc, char **argv)
3884 {
3885         return lfs_hsm_change_flags(argc, argv, LFS_HSM_SET);
3886 }
3887
3888 static int lfs_hsm_clear(int argc, char **argv)
3889 {
3890         return lfs_hsm_change_flags(argc, argv, LFS_HSM_CLEAR);
3891 }
3892
3893 /**
3894  * Check file state and return its fid, to be used by lfs_hsm_request().
3895  *
3896  * \param[in]     file      Path to file to check
3897  * \param[in,out] fid       Pointer to allocated lu_fid struct.
3898  * \param[in,out] last_dev  Pointer to last device id used.
3899  *
3900  * \return 0 on success.
3901  */
3902 static int lfs_hsm_prepare_file(const char *file, struct lu_fid *fid,
3903                                 dev_t *last_dev)
3904 {
3905         struct stat     st;
3906         int             rc;
3907
3908         rc = lstat(file, &st);
3909         if (rc) {
3910                 fprintf(stderr, "Cannot stat %s: %s\n", file, strerror(errno));
3911                 return -errno;
3912         }
3913         /* Checking for regular file as archiving as posix copytool
3914          * rejects archiving files other than regular files
3915          */
3916         if (!S_ISREG(st.st_mode)) {
3917                 fprintf(stderr, "error: \"%s\" is not a regular file\n", file);
3918                 return CMD_HELP;
3919         }
3920         /* A request should be ... */
3921         if (*last_dev != st.st_dev && *last_dev != 0) {
3922                 fprintf(stderr, "All files should be "
3923                         "on the same filesystem: %s\n", file);
3924                 return -EINVAL;
3925         }
3926         *last_dev = st.st_dev;
3927
3928         rc = llapi_path2fid(file, fid);
3929         if (rc) {
3930                 fprintf(stderr, "Cannot read FID of %s: %s\n",
3931                         file, strerror(-rc));
3932                 return rc;
3933         }
3934         return 0;
3935 }
3936
3937 /* Fill an HSM HUR item with a given file name.
3938  *
3939  * If mntpath is set, then the filename is actually a FID, and no
3940  * lookup on the filesystem will be performed.
3941  *
3942  * \param[in]  hur         the user request to fill
3943  * \param[in]  idx         index of the item inside the HUR to fill
3944  * \param[in]  mntpath     mountpoint of Lustre
3945  * \param[in]  fname       filename (if mtnpath is NULL)
3946  *                         or FID (if mntpath is set)
3947  * \param[in]  last_dev    pointer to last device id used
3948  *
3949  * \retval 0 on success
3950  * \retval CMD_HELP or a negative errno on error
3951  */
3952 static int fill_hur_item(struct hsm_user_request *hur, unsigned int idx,
3953                          const char *mntpath, const char *fname,
3954                          dev_t *last_dev)
3955 {
3956         struct hsm_user_item *hui = &hur->hur_user_item[idx];
3957         int rc;
3958
3959         hui->hui_extent.length = -1;
3960
3961         if (mntpath != NULL) {
3962                 if (*fname == '[')
3963                         fname++;
3964                 rc = sscanf(fname, SFID, RFID(&hui->hui_fid));
3965                 if (rc == 3) {
3966                         rc = 0;
3967                 } else {
3968                         fprintf(stderr, "hsm: '%s' is not a valid FID\n",
3969                                 fname);
3970                         rc = -EINVAL;
3971                 }
3972         } else {
3973                 rc = lfs_hsm_prepare_file(fname, &hui->hui_fid, last_dev);
3974         }
3975
3976         if (rc == 0)
3977                 hur->hur_request.hr_itemcount++;
3978
3979         return rc;
3980 }
3981
3982 static int lfs_hsm_request(int argc, char **argv, int action)
3983 {
3984         struct option            long_opts[] = {
3985                 {"filelist", 1, 0, 'l'},
3986                 {"data", 1, 0, 'D'},
3987                 {"archive", 1, 0, 'a'},
3988                 {"mntpath", 1, 0, 'm'},
3989                 {0, 0, 0, 0}
3990         };
3991         dev_t                    last_dev = 0;
3992         char                     short_opts[] = "l:D:a:m:";
3993         struct hsm_user_request *hur, *oldhur;
3994         int                      c, i;
3995         size_t                   len;
3996         int                      nbfile;
3997         char                    *line = NULL;
3998         char                    *filelist = NULL;
3999         char                     fullpath[PATH_MAX];
4000         char                    *opaque = NULL;
4001         int                      opaque_len = 0;
4002         int                      archive_id = 0;
4003         FILE                    *fp;
4004         int                      nbfile_alloc = 0;
4005         char                    *some_file = NULL;
4006         char                    *mntpath = NULL;
4007         int                      rc;
4008
4009         if (argc < 2)
4010                 return CMD_HELP;
4011
4012         while ((c = getopt_long(argc, argv, short_opts,
4013                                 long_opts, NULL)) != -1) {
4014                 switch (c) {
4015                 case 'l':
4016                         filelist = optarg;
4017                         break;
4018                 case 'D':
4019                         opaque = optarg;
4020                         break;
4021                 case 'a':
4022                         if (action != HUA_ARCHIVE &&
4023                             action != HUA_REMOVE) {
4024                                 fprintf(stderr,
4025                                         "error: -a is supported only "
4026                                         "when archiving or removing\n");
4027                                 return CMD_HELP;
4028                         }
4029                         archive_id = atoi(optarg);
4030                         break;
4031                 case 'm':
4032                         if (some_file == NULL) {
4033                                 mntpath = optarg;
4034                                 some_file = strdup(optarg);
4035                         }
4036                         break;
4037                 case '?':
4038                         return CMD_HELP;
4039                 default:
4040                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
4041                                 argv[0], argv[optind - 1]);
4042                         return CMD_HELP;
4043                 }
4044         }
4045
4046         /* All remaining args are files, so we have at least nbfile */
4047         nbfile = argc - optind;
4048
4049         if ((nbfile == 0) && (filelist == NULL))
4050                 return CMD_HELP;
4051
4052         if (opaque != NULL)
4053                 opaque_len = strlen(opaque);
4054
4055         /* Alloc the request structure with enough place to store all files
4056          * from command line. */
4057         hur = llapi_hsm_user_request_alloc(nbfile, opaque_len);
4058         if (hur == NULL) {
4059                 fprintf(stderr, "Cannot create the request: %s\n",
4060                         strerror(errno));
4061                 return errno;
4062         }
4063         nbfile_alloc = nbfile;
4064
4065         hur->hur_request.hr_action = action;
4066         hur->hur_request.hr_archive_id = archive_id;
4067         hur->hur_request.hr_flags = 0;
4068
4069         /* All remaining args are files, add them */
4070         if (nbfile != 0 && some_file == NULL)
4071                 some_file = strdup(argv[optind]);
4072
4073         for (i = 0; i < nbfile; i++) {
4074                 rc = fill_hur_item(hur, i, mntpath, argv[optind + i],
4075                                    &last_dev);
4076                 if (rc)
4077                         goto out_free;
4078         }
4079
4080         /* from here stop using nb_file, use hur->hur_request.hr_itemcount */
4081
4082         /* If a filelist was specified, read the filelist from it. */
4083         if (filelist != NULL) {
4084                 fp = fopen(filelist, "r");
4085                 if (fp == NULL) {
4086                         fprintf(stderr, "Cannot read the file list %s: %s\n",
4087                                 filelist, strerror(errno));
4088                         rc = -errno;
4089                         goto out_free;
4090                 }
4091
4092                 while ((rc = getline(&line, &len, fp)) != -1) {
4093                         /* If allocated buffer was too small, get something
4094                          * larger */
4095                         if (nbfile_alloc <= hur->hur_request.hr_itemcount) {
4096                                 ssize_t size;
4097
4098                                 nbfile_alloc = nbfile_alloc * 2 + 1;
4099                                 oldhur = hur;
4100                                 hur = llapi_hsm_user_request_alloc(nbfile_alloc,
4101                                                                    opaque_len);
4102                                 if (hur == NULL) {
4103                                         fprintf(stderr, "hsm: cannot allocate "
4104                                                 "the request: %s\n",
4105                                                 strerror(errno));
4106                                         hur = oldhur;
4107                                         rc = -errno;
4108                                         fclose(fp);
4109                                         goto out_free;
4110                                 }
4111                                 size = hur_len(oldhur);
4112                                 if (size < 0) {
4113                                         fprintf(stderr, "hsm: cannot allocate "
4114                                                 "%u files + %u bytes data\n",
4115                                             oldhur->hur_request.hr_itemcount,
4116                                             oldhur->hur_request.hr_data_len);
4117                                         free(hur);
4118                                         hur = oldhur;
4119                                         rc = -E2BIG;
4120                                         fclose(fp);
4121                                         goto out_free;
4122                                 }
4123                                 memcpy(hur, oldhur, size);
4124                                 free(oldhur);
4125                         }
4126
4127                         /* Chop CR */
4128                         if (line[strlen(line) - 1] == '\n')
4129                                 line[strlen(line) - 1] = '\0';
4130
4131                         rc = fill_hur_item(hur, hur->hur_request.hr_itemcount,
4132                                            mntpath, line, &last_dev);
4133                         if (rc) {
4134                                 fclose(fp);
4135                                 goto out_free;
4136                         }
4137
4138                         if (some_file == NULL) {
4139                                 some_file = line;
4140                                 line = NULL;
4141                         }
4142                 }
4143
4144                 rc = fclose(fp);
4145                 free(line);
4146         }
4147
4148         /* If a --data was used, add it to the request */
4149         hur->hur_request.hr_data_len = opaque_len;
4150         if (opaque != NULL)
4151                 memcpy(hur_data(hur), opaque, opaque_len);
4152
4153         /* Send the HSM request */
4154         if (realpath(some_file, fullpath) == NULL) {
4155                 fprintf(stderr, "Could not find path '%s': %s\n",
4156                         some_file, strerror(errno));
4157         }
4158         rc = llapi_hsm_request(fullpath, hur);
4159         if (rc) {
4160                 fprintf(stderr, "Cannot send HSM request (use of %s): %s\n",
4161                         some_file, strerror(-rc));
4162                 goto out_free;
4163         }
4164
4165 out_free:
4166         free(some_file);
4167         free(hur);
4168         return rc;
4169 }
4170
4171 static int lfs_hsm_archive(int argc, char **argv)
4172 {
4173         return lfs_hsm_request(argc, argv, HUA_ARCHIVE);
4174 }
4175
4176 static int lfs_hsm_restore(int argc, char **argv)
4177 {
4178         return lfs_hsm_request(argc, argv, HUA_RESTORE);
4179 }
4180
4181 static int lfs_hsm_release(int argc, char **argv)
4182 {
4183         return lfs_hsm_request(argc, argv, HUA_RELEASE);
4184 }
4185
4186 static int lfs_hsm_remove(int argc, char **argv)
4187 {
4188         return lfs_hsm_request(argc, argv, HUA_REMOVE);
4189 }
4190
4191 static int lfs_hsm_cancel(int argc, char **argv)
4192 {
4193         return lfs_hsm_request(argc, argv, HUA_CANCEL);
4194 }
4195
4196 static int lfs_swap_layouts(int argc, char **argv)
4197 {
4198         if (argc != 3)
4199                 return CMD_HELP;
4200
4201         return llapi_swap_layouts(argv[1], argv[2], 0, 0,
4202                                   SWAP_LAYOUTS_KEEP_MTIME |
4203                                   SWAP_LAYOUTS_KEEP_ATIME);
4204 }
4205
4206 int main(int argc, char **argv)
4207 {
4208         int rc;
4209
4210         /* Ensure that liblustreapi constructor has run */
4211         if (!liblustreapi_initialized)
4212                 fprintf(stderr, "liblustreapi was not properly initialized\n");
4213
4214         setlinebuf(stdout);
4215
4216         Parser_init("lfs > ", cmdlist);
4217
4218         progname = argv[0]; /* Used in error messages */
4219         if (argc > 1) {
4220                 rc = Parser_execarg(argc - 1, argv + 1, cmdlist);
4221         } else {
4222                 rc = Parser_commands();
4223         }
4224
4225         return rc < 0 ? -rc : rc;
4226 }
4227
4228 #ifdef _LUSTRE_IDL_H_
4229 /* Everything we need here should be included by lustreapi.h. */
4230 # error "lfs should not depend on lustre_idl.h"
4231 #endif /* _LUSTRE_IDL_H_ */