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