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