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