Whamcloud - gitweb
LU-8152 utils: improve “lfs df” to show device status
[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]", type, index);
2393
2394                 if (stat->os_state) {
2395                         /*
2396                          * Each character represents the matching
2397                          * OS_STATE_* bit.
2398                          */
2399                         const char state_names[] = "DRSI";
2400                         __u32      state;
2401                         __u32      i;
2402
2403                         printf(" ");
2404                         for (i = 0, state = stat->os_state;
2405                              state && i < sizeof(state_names); i++) {
2406                                 if (!(state & (1 << i)))
2407                                         continue;
2408                                 printf("%c", state_names[i]);
2409                                 state ^= 1 << i;
2410                         }
2411                 }
2412
2413                 printf("\n");
2414                 break;
2415         case -ENODATA:
2416                 printf(UUF": inactive device\n", uuid);
2417                 break;
2418         default:
2419                 printf(UUF": %s\n", uuid, strerror(-rc));
2420                 break;
2421         }
2422
2423         return 0;
2424 }
2425
2426 struct ll_stat_type {
2427         int   st_op;
2428         char *st_name;
2429 };
2430
2431 static int mntdf(char *mntdir, char *fsname, char *pool, enum mntdf_flags flags)
2432 {
2433         struct obd_statfs stat_buf, sum = { .os_bsize = 1 };
2434         struct obd_uuid uuid_buf;
2435         char *poolname = NULL;
2436         struct ll_stat_type types[] = { { LL_STATFS_LMV, "MDT" },
2437                                         { LL_STATFS_LOV, "OST" },
2438                                         { 0, NULL } };
2439         struct ll_stat_type *tp;
2440         __u64 ost_ffree = 0;
2441         __u32 index;
2442         __u32 type;
2443         int fd;
2444         int rc = 0;
2445         int rc2;
2446
2447         if (pool) {
2448                 poolname = strchr(pool, '.');
2449                 if (poolname != NULL) {
2450                         if (strncmp(fsname, pool, strlen(fsname))) {
2451                                 fprintf(stderr, "filesystem name incorrect\n");
2452                                 return -ENODEV;
2453                         }
2454                         poolname++;
2455                 } else
2456                         poolname = pool;
2457         }
2458
2459         fd = open(mntdir, O_RDONLY);
2460         if (fd < 0) {
2461                 rc = -errno;
2462                 fprintf(stderr, "%s: cannot open '%s': %s\n", progname, mntdir,
2463                         strerror(errno));
2464                 return rc;
2465         }
2466
2467         if (flags & MNTDF_INODES)
2468                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2469                        "UUID", "Inodes", "IUsed", "IFree",
2470                        "IUse%", "Mounted on");
2471         else
2472                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2473                        "UUID", flags & MNTDF_COOKED ? "bytes" : "1K-blocks",
2474                        "Used", "Available", "Use%", "Mounted on");
2475
2476         for (tp = types; tp->st_name != NULL; tp++) {
2477                 for (index = 0; ; index++) {
2478                         memset(&stat_buf, 0, sizeof(struct obd_statfs));
2479                         memset(&uuid_buf, 0, sizeof(struct obd_uuid));
2480                         type = flags & MNTDF_LAZY ?
2481                                 tp->st_op | LL_STATFS_NODELAY : tp->st_op;
2482                         rc2 = llapi_obd_fstatfs(fd, type, index,
2483                                                &stat_buf, &uuid_buf);
2484                         if (rc2 == -ENODEV)
2485                                 break;
2486                         if (rc2 == -EAGAIN)
2487                                 continue;
2488                         if (rc2 == -ENODATA) { /* Inactive device, OK. */
2489                                 if (!(flags & MNTDF_VERBOSE))
2490                                         continue;
2491                         } else if (rc2 < 0 && rc == 0) {
2492                                 rc = rc2;
2493                         }
2494
2495                         if (poolname && tp->st_op == LL_STATFS_LOV &&
2496                             llapi_search_ost(fsname, poolname,
2497                                              obd_uuid2str(&uuid_buf)) != 1)
2498                                 continue;
2499
2500                         /* the llapi_obd_statfs() call may have returned with
2501                          * an error, but if it filled in uuid_buf we will at
2502                          * lease use that to print out a message for that OBD.
2503                          * If we didn't get anything in the uuid_buf, then fill
2504                          * it in so that we can print an error message. */
2505                         if (uuid_buf.uuid[0] == '\0')
2506                                 snprintf(uuid_buf.uuid, sizeof(uuid_buf.uuid),
2507                                          "%s%04x", tp->st_name, index);
2508                         showdf(mntdir, &stat_buf, obd_uuid2str(&uuid_buf),
2509                                flags, tp->st_name, index, rc2);
2510
2511                         if (rc2 == 0) {
2512                                 if (tp->st_op == LL_STATFS_LMV) {
2513                                         sum.os_ffree += stat_buf.os_ffree;
2514                                         sum.os_files += stat_buf.os_files;
2515                                 } else /* if (tp->st_op == LL_STATFS_LOV) */ {
2516                                         sum.os_blocks += stat_buf.os_blocks *
2517                                                 stat_buf.os_bsize;
2518                                         sum.os_bfree  += stat_buf.os_bfree *
2519                                                 stat_buf.os_bsize;
2520                                         sum.os_bavail += stat_buf.os_bavail *
2521                                                 stat_buf.os_bsize;
2522                                         ost_ffree += stat_buf.os_ffree;
2523                                 }
2524                         }
2525                 }
2526         }
2527
2528         close(fd);
2529
2530         /* If we don't have as many objects free on the OST as inodes
2531          * on the MDS, we reduce the total number of inodes to
2532          * compensate, so that the "inodes in use" number is correct.
2533          * Matches ll_statfs_internal() so the results are consistent. */
2534         if (ost_ffree < sum.os_ffree) {
2535                 sum.os_files = (sum.os_files - sum.os_ffree) + ost_ffree;
2536                 sum.os_ffree = ost_ffree;
2537         }
2538         printf("\n");
2539         showdf(mntdir, &sum, "filesystem_summary:", flags, NULL, 0, 0);
2540         printf("\n");
2541
2542         return rc;
2543 }
2544
2545 static int lfs_df(int argc, char **argv)
2546 {
2547         char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
2548         enum mntdf_flags flags = 0;
2549         int c, rc = 0, index = 0;
2550         char fsname[PATH_MAX] = "", *pool_name = NULL;
2551         struct option long_opts[] = {
2552                 {"human-readable", 0, 0, 'h'},
2553                 {"inodes", 0, 0, 'i'},
2554                 {"lazy", 0, 0, 'l'},
2555                 {"pool", required_argument, 0, 'p'},
2556                 {"verbose", 0, 0, 'v'},
2557                 {0, 0, 0, 0}
2558         };
2559
2560         while ((c = getopt_long(argc, argv, "hilp:v", long_opts, NULL)) != -1) {
2561                 switch (c) {
2562                 case 'h':
2563                         flags |= MNTDF_COOKED;
2564                         break;
2565                 case 'i':
2566                         flags |= MNTDF_INODES;
2567                         break;
2568                 case 'l':
2569                         flags |= MNTDF_LAZY;
2570                         break;
2571                 case 'p':
2572                         pool_name = optarg;
2573                         break;
2574                 case 'v':
2575                         flags |= MNTDF_VERBOSE;
2576                         break;
2577                 default:
2578                         return CMD_HELP;
2579                 }
2580         }
2581         if (optind < argc && !realpath(argv[optind], path)) {
2582                 rc = -errno;
2583                 fprintf(stderr, "error: invalid path '%s': %s\n",
2584                         argv[optind], strerror(-rc));
2585                 return rc;
2586         }
2587
2588         while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2589                 /* Check if we have a mount point */
2590                 if (mntdir[0] == '\0')
2591                         continue;
2592
2593                 rc = mntdf(mntdir, fsname, pool_name, flags);
2594                 if (rc || path[0] != '\0')
2595                         break;
2596                 fsname[0] = '\0'; /* avoid matching in next loop */
2597                 mntdir[0] = '\0'; /* avoid matching in next loop */
2598         }
2599
2600         return rc;
2601 }
2602
2603 static int lfs_getname(int argc, char **argv)
2604 {
2605         char mntdir[PATH_MAX] = "", path[PATH_MAX] = "", fsname[PATH_MAX] = "";
2606         int rc = 0, index = 0, c;
2607         char buf[sizeof(struct obd_uuid)];
2608
2609         while ((c = getopt(argc, argv, "h")) != -1)
2610                 return CMD_HELP;
2611
2612         if (optind == argc) { /* no paths specified, get all paths. */
2613                 while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2614                         rc = llapi_getname(mntdir, buf, sizeof(buf));
2615                         if (rc < 0) {
2616                                 fprintf(stderr,
2617                                         "cannot get name for `%s': %s\n",
2618                                         mntdir, strerror(-rc));
2619                                 break;
2620                         }
2621
2622                         printf("%s %s\n", buf, mntdir);
2623
2624                         path[0] = fsname[0] = mntdir[0] = 0;
2625                 }
2626         } else { /* paths specified, only attempt to search these. */
2627                 for (; optind < argc; optind++) {
2628                         rc = llapi_getname(argv[optind], buf, sizeof(buf));
2629                         if (rc < 0) {
2630                                 fprintf(stderr,
2631                                         "cannot get name for `%s': %s\n",
2632                                         argv[optind], strerror(-rc));
2633                                 break;
2634                         }
2635
2636                         printf("%s %s\n", buf, argv[optind]);
2637                 }
2638         }
2639         return rc;
2640 }
2641
2642 static int lfs_check(int argc, char **argv)
2643 {
2644         int rc;
2645         char mntdir[PATH_MAX] = {'\0'};
2646         int num_types = 1;
2647         char *obd_types[2];
2648         char obd_type1[4];
2649         char obd_type2[4];
2650
2651         if (argc != 2)
2652                 return CMD_HELP;
2653
2654         obd_types[0] = obd_type1;
2655         obd_types[1] = obd_type2;
2656
2657         if (strcmp(argv[1], "osts") == 0) {
2658                 strcpy(obd_types[0], "osc");
2659         } else if (strcmp(argv[1], "mds") == 0) {
2660                 strcpy(obd_types[0], "mdc");
2661         } else if (strcmp(argv[1], "servers") == 0) {
2662                 num_types = 2;
2663                 strcpy(obd_types[0], "osc");
2664                 strcpy(obd_types[1], "mdc");
2665         } else {
2666                 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
2667                                 argv[0], argv[1]);
2668                         return CMD_HELP;
2669         }
2670
2671         rc = llapi_search_mounts(NULL, 0, mntdir, NULL);
2672         if (rc < 0 || mntdir[0] == '\0') {
2673                 fprintf(stderr, "No suitable Lustre mount found\n");
2674                 return rc;
2675         }
2676
2677         rc = llapi_target_check(num_types, obd_types, mntdir);
2678         if (rc)
2679                 fprintf(stderr, "error: %s: %s status failed\n",
2680                                 argv[0],argv[1]);
2681
2682         return rc;
2683
2684 }
2685
2686 #ifdef HAVE_SYS_QUOTA_H
2687 #define ARG2INT(nr, str, msg)                                           \
2688 do {                                                                    \
2689         char *endp;                                                     \
2690         nr = strtol(str, &endp, 0);                                     \
2691         if (*endp) {                                                    \
2692                 fprintf(stderr, "error: bad %s: %s\n", msg, str);       \
2693                 return CMD_HELP;                                        \
2694         }                                                               \
2695 } while (0)
2696
2697 #define ADD_OVERFLOW(a,b) ((a + b) < a) ? (a = ULONG_MAX) : (a = a + b)
2698
2699 /* Convert format time string "XXwXXdXXhXXmXXs" into seconds value
2700  * returns the value or ULONG_MAX on integer overflow or incorrect format
2701  * Notes:
2702  *        1. the order of specifiers is arbitrary (may be: 5w3s or 3s5w)
2703  *        2. specifiers may be encountered multiple times (2s3s is 5 seconds)
2704  *        3. empty integer value is interpreted as 0
2705  */
2706 static unsigned long str2sec(const char* timestr)
2707 {
2708         const char spec[] = "smhdw";
2709         const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2710         unsigned long val = 0;
2711         char *tail;
2712
2713         if (strpbrk(timestr, spec) == NULL) {
2714                 /* no specifiers inside the time string,
2715                    should treat it as an integer value */
2716                 val = strtoul(timestr, &tail, 10);
2717                 return *tail ? ULONG_MAX : val;
2718         }
2719
2720         /* format string is XXwXXdXXhXXmXXs */
2721         while (*timestr) {
2722                 unsigned long v;
2723                 int ind;
2724                 char* ptr;
2725
2726                 v = strtoul(timestr, &tail, 10);
2727                 if (v == ULONG_MAX || *tail == '\0')
2728                         /* value too large (ULONG_MAX or more)
2729                            or missing specifier */
2730                         goto error;
2731
2732                 ptr = strchr(spec, *tail);
2733                 if (ptr == NULL)
2734                         /* unknown specifier */
2735                         goto error;
2736
2737                 ind = ptr - spec;
2738
2739                 /* check if product will overflow the type */
2740                 if (!(v < ULONG_MAX / mult[ind]))
2741                         goto error;
2742
2743                 ADD_OVERFLOW(val, mult[ind] * v);
2744                 if (val == ULONG_MAX)
2745                         goto error;
2746
2747                 timestr = tail + 1;
2748         }
2749
2750         return val;
2751
2752 error:
2753         return ULONG_MAX;
2754 }
2755
2756 #define ARG2ULL(nr, str, def_units)                                     \
2757 do {                                                                    \
2758         unsigned long long limit, units = def_units;                    \
2759         int rc;                                                         \
2760                                                                         \
2761         rc = llapi_parse_size(str, &limit, &units, 1);                  \
2762         if (rc < 0) {                                                   \
2763                 fprintf(stderr, "error: bad limit value %s\n", str);    \
2764                 return CMD_HELP;                                        \
2765         }                                                               \
2766         nr = limit;                                                     \
2767 } while (0)
2768
2769 static inline int has_times_option(int argc, char **argv)
2770 {
2771         int i;
2772
2773         for (i = 1; i < argc; i++)
2774                 if (!strcmp(argv[i], "-t"))
2775                         return 1;
2776
2777         return 0;
2778 }
2779
2780 int lfs_setquota_times(int argc, char **argv)
2781 {
2782         int c, rc;
2783         struct if_quotactl qctl;
2784         char *mnt, *obd_type = (char *)qctl.obd_type;
2785         struct obd_dqblk *dqb = &qctl.qc_dqblk;
2786         struct obd_dqinfo *dqi = &qctl.qc_dqinfo;
2787         struct option long_opts[] = {
2788                 {"block-grace",     required_argument, 0, 'b'},
2789                 {"group",           no_argument,       0, 'g'},
2790                 {"inode-grace",     required_argument, 0, 'i'},
2791                 {"times",           no_argument,       0, 't'},
2792                 {"user",            no_argument,       0, 'u'},
2793                 {0, 0, 0, 0}
2794         };
2795
2796         memset(&qctl, 0, sizeof(qctl));
2797         qctl.qc_cmd  = LUSTRE_Q_SETINFO;
2798         qctl.qc_type = UGQUOTA;
2799
2800         while ((c = getopt_long(argc, argv, "b:gi:tu", long_opts, NULL)) != -1) {
2801                 switch (c) {
2802                 case 'u':
2803                 case 'g':
2804                         if (qctl.qc_type != UGQUOTA) {
2805                                 fprintf(stderr, "error: -u and -g can't be used "
2806                                                 "more than once\n");
2807                                 return CMD_HELP;
2808                         }
2809                         qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2810                         break;
2811                 case 'b':
2812                         if ((dqi->dqi_bgrace = str2sec(optarg)) == ULONG_MAX) {
2813                                 fprintf(stderr, "error: bad block-grace: %s\n",
2814                                         optarg);
2815                                 return CMD_HELP;
2816                         }
2817                         dqb->dqb_valid |= QIF_BTIME;
2818                         break;
2819                 case 'i':
2820                         if ((dqi->dqi_igrace = str2sec(optarg)) == ULONG_MAX) {
2821                                 fprintf(stderr, "error: bad inode-grace: %s\n",
2822                                         optarg);
2823                                 return CMD_HELP;
2824                         }
2825                         dqb->dqb_valid |= QIF_ITIME;
2826                         break;
2827                 case 't': /* Yes, of course! */
2828                         break;
2829                 default: /* getopt prints error message for us when opterr != 0 */
2830                         return CMD_HELP;
2831                 }
2832         }
2833
2834         if (qctl.qc_type == UGQUOTA) {
2835                 fprintf(stderr, "error: neither -u nor -g specified\n");
2836                 return CMD_HELP;
2837         }
2838
2839         if (optind != argc - 1) {
2840                 fprintf(stderr, "error: unexpected parameters encountered\n");
2841                 return CMD_HELP;
2842         }
2843
2844         mnt = argv[optind];
2845         rc = llapi_quotactl(mnt, &qctl);
2846         if (rc) {
2847                 if (*obd_type)
2848                         fprintf(stderr, "%s %s ", obd_type,
2849                                 obd_uuid2str(&qctl.obd_uuid));
2850                 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2851                 return rc;
2852         }
2853
2854         return 0;
2855 }
2856
2857 #define BSLIMIT (1 << 0)
2858 #define BHLIMIT (1 << 1)
2859 #define ISLIMIT (1 << 2)
2860 #define IHLIMIT (1 << 3)
2861
2862 int lfs_setquota(int argc, char **argv)
2863 {
2864         int c, rc;
2865         struct if_quotactl qctl;
2866         char *mnt, *obd_type = (char *)qctl.obd_type;
2867         struct obd_dqblk *dqb = &qctl.qc_dqblk;
2868         struct option long_opts[] = {
2869                 {"block-softlimit", required_argument, 0, 'b'},
2870                 {"block-hardlimit", required_argument, 0, 'B'},
2871                 {"group",           required_argument, 0, 'g'},
2872                 {"inode-softlimit", required_argument, 0, 'i'},
2873                 {"inode-hardlimit", required_argument, 0, 'I'},
2874                 {"user",            required_argument, 0, 'u'},
2875                 {0, 0, 0, 0}
2876         };
2877         unsigned limit_mask = 0;
2878         char *endptr;
2879
2880         if (has_times_option(argc, argv))
2881                 return lfs_setquota_times(argc, argv);
2882
2883         memset(&qctl, 0, sizeof(qctl));
2884         qctl.qc_cmd  = LUSTRE_Q_SETQUOTA;
2885         qctl.qc_type = UGQUOTA; /* UGQUOTA makes no sense for setquota,
2886                                  * so it can be used as a marker that qc_type
2887                                  * isn't reinitialized from command line */
2888
2889         while ((c = getopt_long(argc, argv, "b:B:g:i:I:u:", long_opts, NULL)) != -1) {
2890                 switch (c) {
2891                 case 'u':
2892                 case 'g':
2893                         if (qctl.qc_type != UGQUOTA) {
2894                                 fprintf(stderr, "error: -u and -g can't be used"
2895                                                 " more than once\n");
2896                                 return CMD_HELP;
2897                         }
2898                         qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2899                         rc = name2id(&qctl.qc_id, optarg,
2900                                      (qctl.qc_type == USRQUOTA) ? USER : GROUP);
2901                         if (rc) {
2902                                 qctl.qc_id = strtoul(optarg, &endptr, 10);
2903                                 if (*endptr != '\0') {
2904                                         fprintf(stderr, "error: can't find id "
2905                                                 "for name %s\n", optarg);
2906                                         return CMD_HELP;
2907                                 }
2908                         }
2909                         break;
2910                 case 'b':
2911                         ARG2ULL(dqb->dqb_bsoftlimit, optarg, 1024);
2912                         dqb->dqb_bsoftlimit >>= 10;
2913                         limit_mask |= BSLIMIT;
2914                         if (dqb->dqb_bsoftlimit &&
2915                             dqb->dqb_bsoftlimit <= 1024) /* <= 1M? */
2916                                 fprintf(stderr, "warning: block softlimit is "
2917                                         "smaller than the miminal qunit size, "
2918                                         "please see the help of setquota or "
2919                                         "Lustre manual for details.\n");
2920                         break;
2921                 case 'B':
2922                         ARG2ULL(dqb->dqb_bhardlimit, optarg, 1024);
2923                         dqb->dqb_bhardlimit >>= 10;
2924                         limit_mask |= BHLIMIT;
2925                         if (dqb->dqb_bhardlimit &&
2926                             dqb->dqb_bhardlimit <= 1024) /* <= 1M? */
2927                                 fprintf(stderr, "warning: block hardlimit is "
2928                                         "smaller than the miminal qunit size, "
2929                                         "please see the help of setquota or "
2930                                         "Lustre manual for details.\n");
2931                         break;
2932                 case 'i':
2933                         ARG2ULL(dqb->dqb_isoftlimit, optarg, 1);
2934                         limit_mask |= ISLIMIT;
2935                         if (dqb->dqb_isoftlimit &&
2936                             dqb->dqb_isoftlimit <= 1024) /* <= 1K inodes? */
2937                                 fprintf(stderr, "warning: inode softlimit is "
2938                                         "smaller than the miminal qunit size, "
2939                                         "please see the help of setquota or "
2940                                         "Lustre manual for details.\n");
2941                         break;
2942                 case 'I':
2943                         ARG2ULL(dqb->dqb_ihardlimit, optarg, 1);
2944                         limit_mask |= IHLIMIT;
2945                         if (dqb->dqb_ihardlimit &&
2946                             dqb->dqb_ihardlimit <= 1024) /* <= 1K inodes? */
2947                                 fprintf(stderr, "warning: inode hardlimit is "
2948                                         "smaller than the miminal qunit size, "
2949                                         "please see the help of setquota or "
2950                                         "Lustre manual for details.\n");
2951                         break;
2952                 default: /* getopt prints error message for us when opterr != 0 */
2953                         return CMD_HELP;
2954                 }
2955         }
2956
2957         if (qctl.qc_type == UGQUOTA) {
2958                 fprintf(stderr, "error: neither -u nor -g was specified\n");
2959                 return CMD_HELP;
2960         }
2961
2962         if (limit_mask == 0) {
2963                 fprintf(stderr, "error: at least one limit must be specified\n");
2964                 return CMD_HELP;
2965         }
2966
2967         if (optind != argc - 1) {
2968                 fprintf(stderr, "error: unexpected parameters encountered\n");
2969                 return CMD_HELP;
2970         }
2971
2972         mnt = argv[optind];
2973
2974         if ((!(limit_mask & BHLIMIT) ^ !(limit_mask & BSLIMIT)) ||
2975             (!(limit_mask & IHLIMIT) ^ !(limit_mask & ISLIMIT))) {
2976                 /* sigh, we can't just set blimits/ilimits */
2977                 struct if_quotactl tmp_qctl = {.qc_cmd  = LUSTRE_Q_GETQUOTA,
2978                                                .qc_type = qctl.qc_type,
2979                                                .qc_id   = qctl.qc_id};
2980
2981                 rc = llapi_quotactl(mnt, &tmp_qctl);
2982                 if (rc < 0) {
2983                         fprintf(stderr, "error: setquota failed while retrieving"
2984                                         " current quota settings (%s)\n",
2985                                         strerror(-rc));
2986                         return rc;
2987                 }
2988
2989                 if (!(limit_mask & BHLIMIT))
2990                         dqb->dqb_bhardlimit = tmp_qctl.qc_dqblk.dqb_bhardlimit;
2991                 if (!(limit_mask & BSLIMIT))
2992                         dqb->dqb_bsoftlimit = tmp_qctl.qc_dqblk.dqb_bsoftlimit;
2993                 if (!(limit_mask & IHLIMIT))
2994                         dqb->dqb_ihardlimit = tmp_qctl.qc_dqblk.dqb_ihardlimit;
2995                 if (!(limit_mask & ISLIMIT))
2996                         dqb->dqb_isoftlimit = tmp_qctl.qc_dqblk.dqb_isoftlimit;
2997
2998                 /* Keep grace times if we have got no softlimit arguments */
2999                 if ((limit_mask & BHLIMIT) && !(limit_mask & BSLIMIT)) {
3000                         dqb->dqb_valid |= QIF_BTIME;
3001                         dqb->dqb_btime = tmp_qctl.qc_dqblk.dqb_btime;
3002                 }
3003
3004                 if ((limit_mask & IHLIMIT) && !(limit_mask & ISLIMIT)) {
3005                         dqb->dqb_valid |= QIF_ITIME;
3006                         dqb->dqb_itime = tmp_qctl.qc_dqblk.dqb_itime;
3007                 }
3008         }
3009
3010         dqb->dqb_valid |= (limit_mask & (BHLIMIT | BSLIMIT)) ? QIF_BLIMITS : 0;
3011         dqb->dqb_valid |= (limit_mask & (IHLIMIT | ISLIMIT)) ? QIF_ILIMITS : 0;
3012
3013         rc = llapi_quotactl(mnt, &qctl);
3014         if (rc) {
3015                 if (*obd_type)
3016                         fprintf(stderr, "%s %s ", obd_type,
3017                                 obd_uuid2str(&qctl.obd_uuid));
3018                 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
3019                 return rc;
3020         }
3021
3022         return 0;
3023 }
3024
3025 static inline char *type2name(int check_type)
3026 {
3027         if (check_type == USRQUOTA)
3028                 return "user";
3029         else if (check_type == GRPQUOTA)
3030                 return "group";
3031         else
3032                 return "unknown";
3033 }
3034
3035 /* Converts seconds value into format string
3036  * result is returned in buf
3037  * Notes:
3038  *        1. result is in descenting order: 1w2d3h4m5s
3039  *        2. zero fields are not filled (except for p. 3): 5d1s
3040  *        3. zero seconds value is presented as "0s"
3041  */
3042 static char * __sec2str(time_t seconds, char *buf)
3043 {
3044         const char spec[] = "smhdw";
3045         const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
3046         unsigned long c;
3047         char *tail = buf;
3048         int i;
3049
3050         for (i = sizeof(mult) / sizeof(mult[0]) - 1 ; i >= 0; i--) {
3051                 c = seconds / mult[i];
3052
3053                 if (c > 0 || (i == 0 && buf == tail))
3054                         tail += snprintf(tail, 40-(tail-buf), "%lu%c", c, spec[i]);
3055
3056                 seconds %= mult[i];
3057         }
3058
3059         return tail;
3060 }
3061
3062 static void sec2str(time_t seconds, char *buf, int rc)
3063 {
3064         char *tail = buf;
3065
3066         if (rc)
3067                 *tail++ = '[';
3068
3069         tail = __sec2str(seconds, tail);
3070
3071         if (rc && tail - buf < 39) {
3072                 *tail++ = ']';
3073                 *tail++ = 0;
3074         }
3075 }
3076
3077 static void diff2str(time_t seconds, char *buf, time_t now)
3078 {
3079
3080         buf[0] = 0;
3081         if (!seconds)
3082                 return;
3083         if (seconds <= now) {
3084                 strcpy(buf, "none");
3085                 return;
3086         }
3087         __sec2str(seconds - now, buf);
3088 }
3089
3090 static void print_quota_title(char *name, struct if_quotactl *qctl,
3091                               bool human_readable)
3092 {
3093         printf("Disk quotas for %s %s (%cid %u):\n",
3094                type2name(qctl->qc_type), name,
3095                *type2name(qctl->qc_type), qctl->qc_id);
3096         printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n",
3097                "Filesystem", human_readable ? "used" : "kbytes",
3098                "quota", "limit", "grace",
3099                "files", "quota", "limit", "grace");
3100 }
3101
3102 static void kbytes2str(__u64 num, char *buf, int buflen, bool h)
3103 {
3104         if (!h) {
3105                 snprintf(buf, buflen, "%ju", (uintmax_t)num);
3106         } else {
3107                 if (num >> 40)
3108                         snprintf(buf, buflen, "%5.4gP",
3109                                  (double)num / ((__u64)1 << 40));
3110                 else if (num >> 30)
3111                         snprintf(buf, buflen, "%5.4gT",
3112                                  (double)num / (1 << 30));
3113                 else if (num >> 20)
3114                         snprintf(buf, buflen, "%5.4gG",
3115                                  (double)num / (1 << 20));
3116                 else if (num >> 10)
3117                         snprintf(buf, buflen, "%5.4gM",
3118                                  (double)num / (1 << 10));
3119                 else
3120                         snprintf(buf, buflen, "%ju%s", (uintmax_t)num, "k");
3121         }
3122 }
3123
3124 #define STRBUF_LEN      32
3125 static void print_quota(char *mnt, struct if_quotactl *qctl, int type,
3126                         int rc, bool h)
3127 {
3128         time_t now;
3129
3130         time(&now);
3131
3132         if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == Q_GETOQUOTA) {
3133                 int bover = 0, iover = 0;
3134                 struct obd_dqblk *dqb = &qctl->qc_dqblk;
3135                 char numbuf[3][STRBUF_LEN];
3136                 char timebuf[40];
3137                 char strbuf[STRBUF_LEN];
3138
3139                 if (dqb->dqb_bhardlimit &&
3140                     lustre_stoqb(dqb->dqb_curspace) >= dqb->dqb_bhardlimit) {
3141                         bover = 1;
3142                 } else if (dqb->dqb_bsoftlimit && dqb->dqb_btime) {
3143                         if (dqb->dqb_btime > now) {
3144                                 bover = 2;
3145                         } else {
3146                                 bover = 3;
3147                         }
3148                 }
3149
3150                 if (dqb->dqb_ihardlimit &&
3151                     dqb->dqb_curinodes >= dqb->dqb_ihardlimit) {
3152                         iover = 1;
3153                 } else if (dqb->dqb_isoftlimit && dqb->dqb_itime) {
3154                         if (dqb->dqb_itime > now) {
3155                                 iover = 2;
3156                         } else {
3157                                 iover = 3;
3158                         }
3159                 }
3160
3161
3162                 if (strlen(mnt) > 15)
3163                         printf("%s\n%15s", mnt, "");
3164                 else
3165                         printf("%15s", mnt);
3166
3167                 if (bover)
3168                         diff2str(dqb->dqb_btime, timebuf, now);
3169
3170                 kbytes2str(lustre_stoqb(dqb->dqb_curspace),
3171                            strbuf, sizeof(strbuf), h);
3172                 if (rc == -EREMOTEIO)
3173                         sprintf(numbuf[0], "%s*", strbuf);
3174                 else
3175                         sprintf(numbuf[0], (dqb->dqb_valid & QIF_SPACE) ?
3176                                 "%s" : "[%s]", strbuf);
3177
3178                 kbytes2str(dqb->dqb_bsoftlimit, strbuf, sizeof(strbuf), h);
3179                 if (type == QC_GENERAL)
3180                         sprintf(numbuf[1], (dqb->dqb_valid & QIF_BLIMITS) ?
3181                                 "%s" : "[%s]", strbuf);
3182                 else
3183                         sprintf(numbuf[1], "%s", "-");
3184
3185                 kbytes2str(dqb->dqb_bhardlimit, strbuf, sizeof(strbuf), h);
3186                 sprintf(numbuf[2], (dqb->dqb_valid & QIF_BLIMITS) ?
3187                         "%s" : "[%s]", strbuf);
3188
3189                 printf(" %7s%c %6s %7s %7s",
3190                        numbuf[0], bover ? '*' : ' ', numbuf[1],
3191                        numbuf[2], bover > 1 ? timebuf : "-");
3192
3193                 if (iover)
3194                         diff2str(dqb->dqb_itime, timebuf, now);
3195
3196                 sprintf(numbuf[0], (dqb->dqb_valid & QIF_INODES) ?
3197                         "%ju" : "[%ju]", (uintmax_t)dqb->dqb_curinodes);
3198
3199                 if (type == QC_GENERAL)
3200                         sprintf(numbuf[1], (dqb->dqb_valid & QIF_ILIMITS) ?
3201                                 "%ju" : "[%ju]",
3202                                 (uintmax_t)dqb->dqb_isoftlimit);
3203                 else
3204                         sprintf(numbuf[1], "%s", "-");
3205
3206                 sprintf(numbuf[2], (dqb->dqb_valid & QIF_ILIMITS) ?
3207                         "%ju" : "[%ju]", (uintmax_t)dqb->dqb_ihardlimit);
3208
3209                 if (type != QC_OSTIDX)
3210                         printf(" %7s%c %6s %7s %7s",
3211                                numbuf[0], iover ? '*' : ' ', numbuf[1],
3212                                numbuf[2], iover > 1 ? timebuf : "-");
3213                 else
3214                         printf(" %7s %7s %7s %7s", "-", "-", "-", "-");
3215                 printf("\n");
3216
3217         } else if (qctl->qc_cmd == LUSTRE_Q_GETINFO ||
3218                    qctl->qc_cmd == Q_GETOINFO) {
3219                 char bgtimebuf[40];
3220                 char igtimebuf[40];
3221
3222                 sec2str(qctl->qc_dqinfo.dqi_bgrace, bgtimebuf, rc);
3223                 sec2str(qctl->qc_dqinfo.dqi_igrace, igtimebuf, rc);
3224                 printf("Block grace time: %s; Inode grace time: %s\n",
3225                        bgtimebuf, igtimebuf);
3226         }
3227 }
3228
3229 static int print_obd_quota(char *mnt, struct if_quotactl *qctl, int is_mdt,
3230                            bool h, __u64 *total)
3231 {
3232         int rc = 0, rc1 = 0, count = 0;
3233         __u32 valid = qctl->qc_valid;
3234
3235         rc = llapi_get_obd_count(mnt, &count, is_mdt);
3236         if (rc) {
3237                 fprintf(stderr, "can not get %s count: %s\n",
3238                         is_mdt ? "mdt": "ost", strerror(-rc));
3239                 return rc;
3240         }
3241
3242         for (qctl->qc_idx = 0; qctl->qc_idx < count; qctl->qc_idx++) {
3243                 qctl->qc_valid = is_mdt ? QC_MDTIDX : QC_OSTIDX;
3244                 rc = llapi_quotactl(mnt, qctl);
3245                 if (rc) {
3246                         /* It is remote client case. */
3247                         if (-rc == EOPNOTSUPP) {
3248                                 rc = 0;
3249                                 goto out;
3250                         }
3251
3252                         if (!rc1)
3253                                 rc1 = rc;
3254                         fprintf(stderr, "quotactl %s%d failed.\n",
3255                                 is_mdt ? "mdt": "ost", qctl->qc_idx);
3256                         continue;
3257                 }
3258
3259                 print_quota(obd_uuid2str(&qctl->obd_uuid), qctl,
3260                             qctl->qc_valid, 0, h);
3261                 *total += is_mdt ? qctl->qc_dqblk.dqb_ihardlimit :
3262                                    qctl->qc_dqblk.dqb_bhardlimit;
3263         }
3264 out:
3265         qctl->qc_valid = valid;
3266         return rc ? : rc1;
3267 }
3268
3269 static int lfs_quota(int argc, char **argv)
3270 {
3271         int c;
3272         char *mnt, *name = NULL;
3273         struct if_quotactl qctl = { .qc_cmd = LUSTRE_Q_GETQUOTA,
3274                                     .qc_type = UGQUOTA };
3275         char *obd_type = (char *)qctl.obd_type;
3276         char *obd_uuid = (char *)qctl.obd_uuid.uuid;
3277         int rc, rc1 = 0, rc2 = 0, rc3 = 0,
3278             verbose = 0, pass = 0, quiet = 0, inacc;
3279         char *endptr;
3280         __u32 valid = QC_GENERAL, idx = 0;
3281         __u64 total_ialloc = 0, total_balloc = 0;
3282         bool human_readable = false;
3283
3284         while ((c = getopt(argc, argv, "gi:I:o:qtuvh")) != -1) {
3285                 switch (c) {
3286                 case 'u':
3287                         if (qctl.qc_type != UGQUOTA) {
3288                                 fprintf(stderr, "error: use either -u or -g\n");
3289                                 return CMD_HELP;
3290                         }
3291                         qctl.qc_type = USRQUOTA;
3292                         break;
3293                 case 'g':
3294                         if (qctl.qc_type != UGQUOTA) {
3295                                 fprintf(stderr, "error: use either -u or -g\n");
3296                                 return CMD_HELP;
3297                         }
3298                         qctl.qc_type = GRPQUOTA;
3299                         break;
3300                 case 't':
3301                         qctl.qc_cmd = LUSTRE_Q_GETINFO;
3302                         break;
3303                 case 'o':
3304                         valid = qctl.qc_valid = QC_UUID;
3305                         strlcpy(obd_uuid, optarg, sizeof(qctl.obd_uuid));
3306                         break;
3307                 case 'i':
3308                         valid = qctl.qc_valid = QC_MDTIDX;
3309                         idx = qctl.qc_idx = atoi(optarg);
3310                         break;
3311                 case 'I':
3312                         valid = qctl.qc_valid = QC_OSTIDX;
3313                         idx = qctl.qc_idx = atoi(optarg);
3314                         break;
3315                 case 'v':
3316                         verbose = 1;
3317                         break;
3318                 case 'q':
3319                         quiet = 1;
3320                         break;
3321                 case 'h':
3322                         human_readable = true;
3323                         break;
3324                 default:
3325                         fprintf(stderr, "error: %s: option '-%c' "
3326                                         "unrecognized\n", argv[0], c);
3327                         return CMD_HELP;
3328                 }
3329         }
3330
3331         /* current uid/gid info for "lfs quota /path/to/lustre/mount" */
3332         if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && qctl.qc_type == UGQUOTA &&
3333             optind == argc - 1) {
3334 ug_output:
3335                 memset(&qctl, 0, sizeof(qctl)); /* spoiled by print_*_quota */
3336                 qctl.qc_cmd = LUSTRE_Q_GETQUOTA;
3337                 qctl.qc_valid = valid;
3338                 qctl.qc_idx = idx;
3339                 if (pass++ == 0) {
3340                         qctl.qc_type = USRQUOTA;
3341                         qctl.qc_id = geteuid();
3342                 } else {
3343                         qctl.qc_type = GRPQUOTA;
3344                         qctl.qc_id = getegid();
3345                 }
3346                 rc = id2name(&name, qctl.qc_id,
3347                              (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3348                 if (rc)
3349                         name = "<unknown>";
3350         /* lfs quota -u username /path/to/lustre/mount */
3351         } else if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) {
3352                 /* options should be followed by u/g-name and mntpoint */
3353                 if (optind + 2 != argc || qctl.qc_type == UGQUOTA) {
3354                         fprintf(stderr, "error: missing quota argument(s)\n");
3355                         return CMD_HELP;
3356                 }
3357
3358                 name = argv[optind++];
3359                 rc = name2id(&qctl.qc_id, name,
3360                              (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3361                 if (rc) {
3362                         qctl.qc_id = strtoul(name, &endptr, 10);
3363                         if (*endptr != '\0') {
3364                                 fprintf(stderr, "error: can't find id for name "
3365                                         "%s\n", name);
3366                                 return CMD_HELP;
3367                         }
3368                 }
3369         } else if (optind + 1 != argc || qctl.qc_type == UGQUOTA) {
3370                 fprintf(stderr, "error: missing quota info argument(s)\n");
3371                 return CMD_HELP;
3372         }
3373
3374         mnt = argv[optind];
3375
3376         rc1 = llapi_quotactl(mnt, &qctl);
3377         if (rc1 < 0) {
3378                 switch (rc1) {
3379                 case -ESRCH:
3380                         fprintf(stderr, "%s quotas are not enabled.\n",
3381                                 qctl.qc_type == USRQUOTA ? "user" : "group");
3382                         goto out;
3383                 case -EPERM:
3384                         fprintf(stderr, "Permission denied.\n");
3385                 case -ENODEV:
3386                 case -ENOENT:
3387                         /* We already got error message. */
3388                         goto out;
3389                 default:
3390                         fprintf(stderr, "Unexpected quotactl error: %s\n",
3391                                 strerror(-rc1));
3392                 }
3393         }
3394
3395         if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && !quiet)
3396                 print_quota_title(name, &qctl, human_readable);
3397
3398         if (rc1 && *obd_type)
3399                 fprintf(stderr, "%s %s ", obd_type, obd_uuid);
3400
3401         if (qctl.qc_valid != QC_GENERAL)
3402                 mnt = "";
3403
3404         inacc = (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) &&
3405                 ((qctl.qc_dqblk.dqb_valid & (QIF_LIMITS|QIF_USAGE)) !=
3406                  (QIF_LIMITS|QIF_USAGE));
3407
3408         print_quota(mnt, &qctl, QC_GENERAL, rc1, human_readable);
3409
3410         if (qctl.qc_valid == QC_GENERAL && qctl.qc_cmd != LUSTRE_Q_GETINFO &&
3411             verbose) {
3412                 char strbuf[STRBUF_LEN];
3413
3414                 rc2 = print_obd_quota(mnt, &qctl, 1, human_readable,
3415                                       &total_ialloc);
3416                 rc3 = print_obd_quota(mnt, &qctl, 0, human_readable,
3417                                       &total_balloc);
3418                 kbytes2str(total_balloc, strbuf, sizeof(strbuf),
3419                            human_readable);
3420                 printf("Total allocated inode limit: %ju, total "
3421                        "allocated block limit: %s\n", (uintmax_t)total_ialloc,
3422                        strbuf);
3423         }
3424
3425         if (rc1 || rc2 || rc3 || inacc)
3426                 printf("Some errors happened when getting quota info. "
3427                        "Some devices may be not working or deactivated. "
3428                        "The data in \"[]\" is inaccurate.\n");
3429
3430 out:
3431         if (pass == 1)
3432                 goto ug_output;
3433
3434         return rc1;
3435 }
3436 #endif /* HAVE_SYS_QUOTA_H! */
3437
3438 static int flushctx_ioctl(char *mp)
3439 {
3440         int fd, rc;
3441
3442         fd = open(mp, O_RDONLY);
3443         if (fd == -1) {
3444                 fprintf(stderr, "flushctx: error open %s: %s\n",
3445                         mp, strerror(errno));
3446                 return -1;
3447         }
3448
3449         rc = ioctl(fd, LL_IOC_FLUSHCTX);
3450         if (rc == -1)
3451                 fprintf(stderr, "flushctx: error ioctl %s: %s\n",
3452                         mp, strerror(errno));
3453
3454         close(fd);
3455         return rc;
3456 }
3457
3458 static int lfs_flushctx(int argc, char **argv)
3459 {
3460         int     kdestroy = 0, c;
3461         char    mntdir[PATH_MAX] = {'\0'};
3462         int     index = 0;
3463         int     rc = 0;
3464
3465         while ((c = getopt(argc, argv, "k")) != -1) {
3466                 switch (c) {
3467                 case 'k':
3468                         kdestroy = 1;
3469                         break;
3470                 default:
3471                         fprintf(stderr, "error: %s: option '-%c' "
3472                                         "unrecognized\n", argv[0], c);
3473                         return CMD_HELP;
3474                 }
3475         }
3476
3477         if (kdestroy) {
3478             if ((rc = system("kdestroy > /dev/null")) != 0) {
3479                 rc = WEXITSTATUS(rc);
3480                 fprintf(stderr, "error destroying tickets: %d, continuing\n", rc);
3481             }
3482         }
3483
3484         if (optind >= argc) {
3485                 /* flush for all mounted lustre fs. */
3486                 while (!llapi_search_mounts(NULL, index++, mntdir, NULL)) {
3487                         /* Check if we have a mount point */
3488                         if (mntdir[0] == '\0')
3489                                 continue;
3490
3491                         if (flushctx_ioctl(mntdir))
3492                                 rc = -1;
3493
3494                         mntdir[0] = '\0'; /* avoid matching in next loop */
3495                 }
3496         } else {
3497                 /* flush fs as specified */
3498                 while (optind < argc) {
3499                         if (flushctx_ioctl(argv[optind++]))
3500                                 rc = -1;
3501                 }
3502         }
3503         return rc;
3504 }
3505
3506 static int lfs_cp(int argc, char **argv)
3507 {
3508         fprintf(stderr, "remote client copy file(s).\n"
3509                 "obsolete, does not support it anymore.\n");
3510         return 0;
3511 }
3512
3513 static int lfs_ls(int argc, char **argv)
3514 {
3515         fprintf(stderr, "remote client lists directory contents.\n"
3516                 "obsolete, does not support it anymore.\n");
3517         return 0;
3518 }
3519
3520 static int lfs_changelog(int argc, char **argv)
3521 {
3522         void *changelog_priv;
3523         struct changelog_rec *rec;
3524         long long startrec = 0, endrec = 0;
3525         char *mdd;
3526         struct option long_opts[] = {
3527                 {"follow", no_argument, 0, 'f'},
3528                 {0, 0, 0, 0}
3529         };
3530         char short_opts[] = "f";
3531         int rc, follow = 0;
3532
3533         while ((rc = getopt_long(argc, argv, short_opts,
3534                                 long_opts, NULL)) != -1) {
3535                 switch (rc) {
3536                 case 'f':
3537                         follow++;
3538                         break;
3539                 case '?':
3540                         return CMD_HELP;
3541                 default:
3542                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3543                                 argv[0], argv[optind - 1]);
3544                         return CMD_HELP;
3545                 }
3546         }
3547         if (optind >= argc)
3548                 return CMD_HELP;
3549
3550         mdd = argv[optind++];
3551         if (argc > optind)
3552                 startrec = strtoll(argv[optind++], NULL, 10);
3553         if (argc > optind)
3554                 endrec = strtoll(argv[optind++], NULL, 10);
3555
3556         rc = llapi_changelog_start(&changelog_priv,
3557                                    CHANGELOG_FLAG_BLOCK |
3558                                    CHANGELOG_FLAG_JOBID |
3559                                    (follow ? CHANGELOG_FLAG_FOLLOW : 0),
3560                                    mdd, startrec);
3561         if (rc < 0) {
3562                 fprintf(stderr, "Can't start changelog: %s\n",
3563                         strerror(errno = -rc));
3564                 return rc;
3565         }
3566
3567         while ((rc = llapi_changelog_recv(changelog_priv, &rec)) == 0) {
3568                 time_t secs;
3569                 struct tm ts;
3570
3571                 if (endrec && rec->cr_index > endrec) {
3572                         llapi_changelog_free(&rec);
3573                         break;
3574                 }
3575                 if (rec->cr_index < startrec) {
3576                         llapi_changelog_free(&rec);
3577                         continue;
3578                 }
3579
3580                 secs = rec->cr_time >> 30;
3581                 gmtime_r(&secs, &ts);
3582                 printf("%ju %02d%-5s %02d:%02d:%02d.%06d %04d.%02d.%02d "
3583                        "0x%x t="DFID, (uintmax_t) rec->cr_index, rec->cr_type,
3584                        changelog_type2str(rec->cr_type),
3585                        ts.tm_hour, ts.tm_min, ts.tm_sec,
3586                        (int)(rec->cr_time & ((1<<30) - 1)),
3587                        ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday,
3588                        rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
3589
3590                 if (rec->cr_flags & CLF_JOBID) {
3591                         struct changelog_ext_jobid *jid =
3592                                 changelog_rec_jobid(rec);
3593
3594                         if (jid->cr_jobid[0] != '\0')
3595                                 printf(" j=%s", jid->cr_jobid);
3596                 }
3597
3598                 if (rec->cr_namelen)
3599                         printf(" p="DFID" %.*s", PFID(&rec->cr_pfid),
3600                                rec->cr_namelen, changelog_rec_name(rec));
3601
3602                 if (rec->cr_flags & CLF_RENAME) {
3603                         struct changelog_ext_rename *rnm =
3604                                 changelog_rec_rename(rec);
3605
3606                         if (!fid_is_zero(&rnm->cr_sfid))
3607                                 printf(" s="DFID" sp="DFID" %.*s",
3608                                        PFID(&rnm->cr_sfid),
3609                                        PFID(&rnm->cr_spfid),
3610                                        (int)changelog_rec_snamelen(rec),
3611                                        changelog_rec_sname(rec));
3612                 }
3613                 printf("\n");
3614
3615                 llapi_changelog_free(&rec);
3616         }
3617
3618         llapi_changelog_fini(&changelog_priv);
3619
3620         if (rc < 0)
3621                 fprintf(stderr, "Changelog: %s\n", strerror(errno = -rc));
3622
3623         return (rc == 1 ? 0 : rc);
3624 }
3625
3626 static int lfs_changelog_clear(int argc, char **argv)
3627 {
3628         long long endrec;
3629         int rc;
3630
3631         if (argc != 4)
3632                 return CMD_HELP;
3633
3634         endrec = strtoll(argv[3], NULL, 10);
3635
3636         rc = llapi_changelog_clear(argv[1], argv[2], endrec);
3637
3638         if (rc == -EINVAL)
3639                 fprintf(stderr, "%s: record out of range: %llu\n",
3640                         argv[0], endrec);
3641         else if (rc == -ENOENT)
3642                 fprintf(stderr, "%s: no changelog user: %s\n",
3643                         argv[0], argv[2]);
3644         else if (rc)
3645                 fprintf(stderr, "%s error: %s\n", argv[0],
3646                         strerror(-rc));
3647
3648         if (rc)
3649                 errno = -rc;
3650
3651         return rc;
3652 }
3653
3654 static int lfs_fid2path(int argc, char **argv)
3655 {
3656         struct option long_opts[] = {
3657                 {"cur", no_argument, 0, 'c'},
3658                 {"link", required_argument, 0, 'l'},
3659                 {"rec", required_argument, 0, 'r'},
3660                 {0, 0, 0, 0}
3661         };
3662         char  short_opts[] = "cl:r:";
3663         char *device, *fid, *path;
3664         long long recno = -1;
3665         int linkno = -1;
3666         int lnktmp;
3667         int printcur = 0;
3668         int rc = 0;
3669
3670         while ((rc = getopt_long(argc, argv, short_opts,
3671                                 long_opts, NULL)) != -1) {
3672                 switch (rc) {
3673                 case 'c':
3674                         printcur++;
3675                         break;
3676                 case 'l':
3677                         linkno = strtol(optarg, NULL, 10);
3678                         break;
3679                 case 'r':
3680                         recno = strtoll(optarg, NULL, 10);
3681                         break;
3682                 case '?':
3683                         return CMD_HELP;
3684                 default:
3685                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3686                                 argv[0], argv[optind - 1]);
3687                         return CMD_HELP;
3688                 }
3689         }
3690
3691         if (argc < 3)
3692                 return CMD_HELP;
3693
3694         device = argv[optind++];
3695         path = calloc(1, PATH_MAX);
3696         if (path == NULL) {
3697                 fprintf(stderr, "error: Not enough memory\n");
3698                 return -errno;
3699         }
3700
3701         rc = 0;
3702         while (optind < argc) {
3703                 fid = argv[optind++];
3704
3705                 lnktmp = (linkno >= 0) ? linkno : 0;
3706                 while (1) {
3707                         int oldtmp = lnktmp;
3708                         long long rectmp = recno;
3709                         int rc2;
3710                         rc2 = llapi_fid2path(device, fid, path, PATH_MAX,
3711                                              &rectmp, &lnktmp);
3712                         if (rc2 < 0) {
3713                                 fprintf(stderr, "%s: error on FID %s: %s\n",
3714                                         argv[0], fid, strerror(errno = -rc2));
3715                                 if (rc == 0)
3716                                         rc = rc2;
3717                                 break;
3718                         }
3719
3720                         if (printcur)
3721                                 fprintf(stdout, "%lld ", rectmp);
3722                         if (device[0] == '/') {
3723                                 fprintf(stdout, "%s", device);
3724                                 if (device[strlen(device) - 1] != '/')
3725                                         fprintf(stdout, "/");
3726                         } else if (path[0] == '\0') {
3727                                 fprintf(stdout, "/");
3728                         }
3729                         fprintf(stdout, "%s\n", path);
3730
3731                         if (linkno >= 0)
3732                                 /* specified linkno */
3733                                 break;
3734                         if (oldtmp == lnktmp)
3735                                 /* no more links */
3736                                 break;
3737                 }
3738         }
3739
3740         free(path);
3741         return rc;
3742 }
3743
3744 static int lfs_path2fid(int argc, char **argv)
3745 {
3746         struct option     long_opts[] = {
3747                 {"parents", no_argument, 0, 'p'},
3748                 {0, 0, 0, 0}
3749         };
3750         char            **path;
3751         const char        short_opts[] = "p";
3752         const char       *sep = "";
3753         lustre_fid        fid;
3754         int               rc = 0;
3755         bool              show_parents = false;
3756
3757         while ((rc = getopt_long(argc, argv, short_opts,
3758                                  long_opts, NULL)) != -1) {
3759                 switch (rc) {
3760                 case 'p':
3761                         show_parents = true;
3762                         break;
3763                 default:
3764                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3765                                 argv[0], argv[optind - 1]);
3766                         return CMD_HELP;
3767                 }
3768         }
3769
3770         if (optind > argc - 1)
3771                 return CMD_HELP;
3772         else if (optind < argc - 1)
3773                 sep = ": ";
3774
3775         rc = 0;
3776         for (path = argv + optind; *path != NULL; path++) {
3777                 int err = 0;
3778                 if (!show_parents) {
3779                         err = llapi_path2fid(*path, &fid);
3780                         if (!err)
3781                                 printf("%s%s"DFID"\n",
3782                                        *sep != '\0' ? *path : "", sep,
3783                                        PFID(&fid));
3784                 } else {
3785                         char            name[NAME_MAX + 1];
3786                         unsigned int    linkno = 0;
3787
3788                         while ((err = llapi_path2parent(*path, linkno, &fid,
3789                                                 name, sizeof(name))) == 0) {
3790                                 if (*sep != '\0' && linkno == 0)
3791                                         printf("%s%s", *path, sep);
3792
3793                                 printf("%s"DFID"/%s", linkno != 0 ? "\t" : "",
3794                                        PFID(&fid), name);
3795                                 linkno++;
3796                         }
3797
3798                         /* err == -ENODATA is end-of-loop */
3799                         if (linkno > 0 && err == -ENODATA) {
3800                                 printf("\n");
3801                                 err = 0;
3802                         }
3803                 }
3804
3805                 if (err) {
3806                         fprintf(stderr, "%s: can't get %sfid for %s: %s\n",
3807                                 argv[0], show_parents ? "parent " : "", *path,
3808                                 strerror(-err));
3809                         if (rc == 0) {
3810                                 rc = err;
3811                                 errno = -err;
3812                         }
3813                 }
3814         }
3815
3816         return rc;
3817 }
3818
3819 static int lfs_data_version(int argc, char **argv)
3820 {
3821         char *path;
3822         __u64 data_version;
3823         int fd;
3824         int rc;
3825         int c;
3826         int data_version_flags = LL_DV_RD_FLUSH; /* Read by default */
3827
3828         if (argc < 2)
3829                 return CMD_HELP;
3830
3831         while ((c = getopt(argc, argv, "nrw")) != -1) {
3832                 switch (c) {
3833                 case 'n':
3834                         data_version_flags = 0;
3835                         break;
3836                 case 'r':
3837                         data_version_flags |= LL_DV_RD_FLUSH;
3838                         break;
3839                 case 'w':
3840                         data_version_flags |= LL_DV_WR_FLUSH;
3841                         break;
3842                 default:
3843                         return CMD_HELP;
3844                 }
3845         }
3846         if (optind == argc)
3847                 return CMD_HELP;
3848
3849         path = argv[optind];
3850         fd = open(path, O_RDONLY);
3851         if (fd < 0)
3852                 err(errno, "cannot open file %s", path);
3853
3854         rc = llapi_get_data_version(fd, &data_version, data_version_flags);
3855         if (rc < 0)
3856                 err(errno, "cannot get version for %s", path);
3857         else
3858                 printf("%ju" "\n", (uintmax_t)data_version);
3859
3860         close(fd);
3861         return rc;
3862 }
3863
3864 static int lfs_hsm_state(int argc, char **argv)
3865 {
3866         int rc;
3867         int i = 1;
3868         char *path;
3869         struct hsm_user_state hus;
3870
3871         if (argc < 2)
3872                 return CMD_HELP;
3873
3874         do {
3875                 path = argv[i];
3876
3877                 rc = llapi_hsm_state_get(path, &hus);
3878                 if (rc) {
3879                         fprintf(stderr, "can't get hsm state for %s: %s\n",
3880                                 path, strerror(errno = -rc));
3881                         return rc;
3882                 }
3883
3884                 /* Display path name and status flags */
3885                 printf("%s: (0x%08x)", path, hus.hus_states);
3886
3887                 if (hus.hus_states & HS_RELEASED)
3888                         printf(" released");
3889                 if (hus.hus_states & HS_EXISTS)
3890                         printf(" exists");
3891                 if (hus.hus_states & HS_DIRTY)
3892                         printf(" dirty");
3893                 if (hus.hus_states & HS_ARCHIVED)
3894                         printf(" archived");
3895                 /* Display user-settable flags */
3896                 if (hus.hus_states & HS_NORELEASE)
3897                         printf(" never_release");
3898                 if (hus.hus_states & HS_NOARCHIVE)
3899                         printf(" never_archive");
3900                 if (hus.hus_states & HS_LOST)
3901                         printf(" lost_from_hsm");
3902
3903                 if (hus.hus_archive_id != 0)
3904                         printf(", archive_id:%d", hus.hus_archive_id);
3905                 printf("\n");
3906
3907         } while (++i < argc);
3908
3909         return 0;
3910 }
3911
3912 #define LFS_HSM_SET   0
3913 #define LFS_HSM_CLEAR 1
3914
3915 /**
3916  * Generic function to set or clear HSM flags.
3917  * Used by hsm_set and hsm_clear.
3918  *
3919  * @mode  if LFS_HSM_SET, set the flags, if LFS_HSM_CLEAR, clear the flags.
3920  */
3921 static int lfs_hsm_change_flags(int argc, char **argv, int mode)
3922 {
3923         struct option long_opts[] = {
3924                 {"lost", 0, 0, 'l'},
3925                 {"norelease", 0, 0, 'r'},
3926                 {"noarchive", 0, 0, 'a'},
3927                 {"archived", 0, 0, 'A'},
3928                 {"dirty", 0, 0, 'd'},
3929                 {"exists", 0, 0, 'e'},
3930                 {0, 0, 0, 0}
3931         };
3932         char short_opts[] = "lraAde";
3933         __u64 mask = 0;
3934         int c, rc;
3935         char *path;
3936
3937         if (argc < 3)
3938                 return CMD_HELP;
3939
3940         while ((c = getopt_long(argc, argv, short_opts,
3941                                 long_opts, NULL)) != -1) {
3942                 switch (c) {
3943                 case 'l':
3944                         mask |= HS_LOST;
3945                         break;
3946                 case 'a':
3947                         mask |= HS_NOARCHIVE;
3948                         break;
3949                 case 'A':
3950                         mask |= HS_ARCHIVED;
3951                         break;
3952                 case 'r':
3953                         mask |= HS_NORELEASE;
3954                         break;
3955                 case 'd':
3956                         mask |= HS_DIRTY;
3957                         break;
3958                 case 'e':
3959                         mask |= HS_EXISTS;
3960                         break;
3961                 case '?':
3962                         return CMD_HELP;
3963                 default:
3964                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3965                                 argv[0], argv[optind - 1]);
3966                         return CMD_HELP;
3967                 }
3968         }
3969
3970         /* User should have specified a flag */
3971         if (mask == 0)
3972                 return CMD_HELP;
3973
3974         while (optind < argc) {
3975
3976                 path = argv[optind];
3977
3978                 /* If mode == 0, this means we apply the mask. */
3979                 if (mode == LFS_HSM_SET)
3980                         rc = llapi_hsm_state_set(path, mask, 0, 0);
3981                 else
3982                         rc = llapi_hsm_state_set(path, 0, mask, 0);
3983
3984                 if (rc != 0) {
3985                         fprintf(stderr, "Can't change hsm flags for %s: %s\n",
3986                                 path, strerror(errno = -rc));
3987                         return rc;
3988                 }
3989                 optind++;
3990         }
3991
3992         return 0;
3993 }
3994
3995 static int lfs_hsm_action(int argc, char **argv)
3996 {
3997         int                              rc;
3998         int                              i = 1;
3999         char                            *path;
4000         struct hsm_current_action        hca;
4001         struct hsm_extent                he;
4002         enum hsm_user_action             hua;
4003         enum hsm_progress_states         hps;
4004
4005         if (argc < 2)
4006                 return CMD_HELP;
4007
4008         do {
4009                 path = argv[i];
4010
4011                 rc = llapi_hsm_current_action(path, &hca);
4012                 if (rc) {
4013                         fprintf(stderr, "can't get hsm action for %s: %s\n",
4014                                 path, strerror(errno = -rc));
4015                         return rc;
4016                 }
4017                 he = hca.hca_location;
4018                 hua = hca.hca_action;
4019                 hps = hca.hca_state;
4020
4021                 printf("%s: %s", path, hsm_user_action2name(hua));
4022
4023                 /* Skip file without action */
4024                 if (hca.hca_action == HUA_NONE) {
4025                         printf("\n");
4026                         continue;
4027                 }
4028
4029                 printf(" %s ", hsm_progress_state2name(hps));
4030
4031                 if ((hps == HPS_RUNNING) &&
4032                     (hua == HUA_ARCHIVE || hua == HUA_RESTORE))
4033                         printf("(%llu bytes moved)\n",
4034                                (unsigned long long)he.length);
4035                 else if ((he.offset + he.length) == LUSTRE_EOF)
4036                         printf("(from %llu to EOF)\n",
4037                                (unsigned long long)he.offset);
4038                 else
4039                         printf("(from %llu to %llu)\n",
4040                                (unsigned long long)he.offset,
4041                                (unsigned long long)(he.offset + he.length));
4042
4043         } while (++i < argc);
4044
4045         return 0;
4046 }
4047
4048 static int lfs_hsm_set(int argc, char **argv)
4049 {
4050         return lfs_hsm_change_flags(argc, argv, LFS_HSM_SET);
4051 }
4052
4053 static int lfs_hsm_clear(int argc, char **argv)
4054 {
4055         return lfs_hsm_change_flags(argc, argv, LFS_HSM_CLEAR);
4056 }
4057
4058 /**
4059  * Check file state and return its fid, to be used by lfs_hsm_request().
4060  *
4061  * \param[in]     file      Path to file to check
4062  * \param[in,out] fid       Pointer to allocated lu_fid struct.
4063  * \param[in,out] last_dev  Pointer to last device id used.
4064  *
4065  * \return 0 on success.
4066  */
4067 static int lfs_hsm_prepare_file(const char *file, struct lu_fid *fid,
4068                                 dev_t *last_dev)
4069 {
4070         struct stat     st;
4071         int             rc;
4072
4073         rc = lstat(file, &st);
4074         if (rc) {
4075                 fprintf(stderr, "Cannot stat %s: %s\n", file, strerror(errno));
4076                 return -errno;
4077         }
4078         /* Checking for regular file as archiving as posix copytool
4079          * rejects archiving files other than regular files
4080          */
4081         if (!S_ISREG(st.st_mode)) {
4082                 fprintf(stderr, "error: \"%s\" is not a regular file\n", file);
4083                 return CMD_HELP;
4084         }
4085         /* A request should be ... */
4086         if (*last_dev != st.st_dev && *last_dev != 0) {
4087                 fprintf(stderr, "All files should be "
4088                         "on the same filesystem: %s\n", file);
4089                 return -EINVAL;
4090         }
4091         *last_dev = st.st_dev;
4092
4093         rc = llapi_path2fid(file, fid);
4094         if (rc) {
4095                 fprintf(stderr, "Cannot read FID of %s: %s\n",
4096                         file, strerror(-rc));
4097                 return rc;
4098         }
4099         return 0;
4100 }
4101
4102 /* Fill an HSM HUR item with a given file name.
4103  *
4104  * If mntpath is set, then the filename is actually a FID, and no
4105  * lookup on the filesystem will be performed.
4106  *
4107  * \param[in]  hur         the user request to fill
4108  * \param[in]  idx         index of the item inside the HUR to fill
4109  * \param[in]  mntpath     mountpoint of Lustre
4110  * \param[in]  fname       filename (if mtnpath is NULL)
4111  *                         or FID (if mntpath is set)
4112  * \param[in]  last_dev    pointer to last device id used
4113  *
4114  * \retval 0 on success
4115  * \retval CMD_HELP or a negative errno on error
4116  */
4117 static int fill_hur_item(struct hsm_user_request *hur, unsigned int idx,
4118                          const char *mntpath, const char *fname,
4119                          dev_t *last_dev)
4120 {
4121         struct hsm_user_item *hui = &hur->hur_user_item[idx];
4122         int rc;
4123
4124         hui->hui_extent.length = -1;
4125
4126         if (mntpath != NULL) {
4127                 if (*fname == '[')
4128                         fname++;
4129                 rc = sscanf(fname, SFID, RFID(&hui->hui_fid));
4130                 if (rc == 3) {
4131                         rc = 0;
4132                 } else {
4133                         fprintf(stderr, "hsm: '%s' is not a valid FID\n",
4134                                 fname);
4135                         rc = -EINVAL;
4136                 }
4137         } else {
4138                 rc = lfs_hsm_prepare_file(fname, &hui->hui_fid, last_dev);
4139         }
4140
4141         if (rc == 0)
4142                 hur->hur_request.hr_itemcount++;
4143
4144         return rc;
4145 }
4146
4147 static int lfs_hsm_request(int argc, char **argv, int action)
4148 {
4149         struct option            long_opts[] = {
4150                 {"filelist", 1, 0, 'l'},
4151                 {"data", 1, 0, 'D'},
4152                 {"archive", 1, 0, 'a'},
4153                 {"mntpath", 1, 0, 'm'},
4154                 {0, 0, 0, 0}
4155         };
4156         dev_t                    last_dev = 0;
4157         char                     short_opts[] = "l:D:a:m:";
4158         struct hsm_user_request *hur, *oldhur;
4159         int                      c, i;
4160         size_t                   len;
4161         int                      nbfile;
4162         char                    *line = NULL;
4163         char                    *filelist = NULL;
4164         char                     fullpath[PATH_MAX];
4165         char                    *opaque = NULL;
4166         int                      opaque_len = 0;
4167         int                      archive_id = 0;
4168         FILE                    *fp;
4169         int                      nbfile_alloc = 0;
4170         char                    *some_file = NULL;
4171         char                    *mntpath = NULL;
4172         int                      rc;
4173
4174         if (argc < 2)
4175                 return CMD_HELP;
4176
4177         while ((c = getopt_long(argc, argv, short_opts,
4178                                 long_opts, NULL)) != -1) {
4179                 switch (c) {
4180                 case 'l':
4181                         filelist = optarg;
4182                         break;
4183                 case 'D':
4184                         opaque = optarg;
4185                         break;
4186                 case 'a':
4187                         if (action != HUA_ARCHIVE &&
4188                             action != HUA_REMOVE) {
4189                                 fprintf(stderr,
4190                                         "error: -a is supported only "
4191                                         "when archiving or removing\n");
4192                                 return CMD_HELP;
4193                         }
4194                         archive_id = atoi(optarg);
4195                         break;
4196                 case 'm':
4197                         if (some_file == NULL) {
4198                                 mntpath = optarg;
4199                                 some_file = strdup(optarg);
4200                         }
4201                         break;
4202                 case '?':
4203                         return CMD_HELP;
4204                 default:
4205                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
4206                                 argv[0], argv[optind - 1]);
4207                         return CMD_HELP;
4208                 }
4209         }
4210
4211         /* All remaining args are files, so we have at least nbfile */
4212         nbfile = argc - optind;
4213
4214         if ((nbfile == 0) && (filelist == NULL))
4215                 return CMD_HELP;
4216
4217         if (opaque != NULL)
4218                 opaque_len = strlen(opaque);
4219
4220         /* Alloc the request structure with enough place to store all files
4221          * from command line. */
4222         hur = llapi_hsm_user_request_alloc(nbfile, opaque_len);
4223         if (hur == NULL) {
4224                 fprintf(stderr, "Cannot create the request: %s\n",
4225                         strerror(errno));
4226                 return errno;
4227         }
4228         nbfile_alloc = nbfile;
4229
4230         hur->hur_request.hr_action = action;
4231         hur->hur_request.hr_archive_id = archive_id;
4232         hur->hur_request.hr_flags = 0;
4233
4234         /* All remaining args are files, add them */
4235         if (nbfile != 0 && some_file == NULL)
4236                 some_file = strdup(argv[optind]);
4237
4238         for (i = 0; i < nbfile; i++) {
4239                 rc = fill_hur_item(hur, i, mntpath, argv[optind + i],
4240                                    &last_dev);
4241                 if (rc)
4242                         goto out_free;
4243         }
4244
4245         /* from here stop using nb_file, use hur->hur_request.hr_itemcount */
4246
4247         /* If a filelist was specified, read the filelist from it. */
4248         if (filelist != NULL) {
4249                 fp = fopen(filelist, "r");
4250                 if (fp == NULL) {
4251                         fprintf(stderr, "Cannot read the file list %s: %s\n",
4252                                 filelist, strerror(errno));
4253                         rc = -errno;
4254                         goto out_free;
4255                 }
4256
4257                 while ((rc = getline(&line, &len, fp)) != -1) {
4258                         /* If allocated buffer was too small, get something
4259                          * larger */
4260                         if (nbfile_alloc <= hur->hur_request.hr_itemcount) {
4261                                 ssize_t size;
4262
4263                                 nbfile_alloc = nbfile_alloc * 2 + 1;
4264                                 oldhur = hur;
4265                                 hur = llapi_hsm_user_request_alloc(nbfile_alloc,
4266                                                                    opaque_len);
4267                                 if (hur == NULL) {
4268                                         fprintf(stderr, "hsm: cannot allocate "
4269                                                 "the request: %s\n",
4270                                                 strerror(errno));
4271                                         hur = oldhur;
4272                                         rc = -errno;
4273                                         fclose(fp);
4274                                         goto out_free;
4275                                 }
4276                                 size = hur_len(oldhur);
4277                                 if (size < 0) {
4278                                         fprintf(stderr, "hsm: cannot allocate "
4279                                                 "%u files + %u bytes data\n",
4280                                             oldhur->hur_request.hr_itemcount,
4281                                             oldhur->hur_request.hr_data_len);
4282                                         free(hur);
4283                                         hur = oldhur;
4284                                         rc = -E2BIG;
4285                                         fclose(fp);
4286                                         goto out_free;
4287                                 }
4288                                 memcpy(hur, oldhur, size);
4289                                 free(oldhur);
4290                         }
4291
4292                         /* Chop CR */
4293                         if (line[strlen(line) - 1] == '\n')
4294                                 line[strlen(line) - 1] = '\0';
4295
4296                         rc = fill_hur_item(hur, hur->hur_request.hr_itemcount,
4297                                            mntpath, line, &last_dev);
4298                         if (rc) {
4299                                 fclose(fp);
4300                                 goto out_free;
4301                         }
4302
4303                         if (some_file == NULL) {
4304                                 some_file = line;
4305                                 line = NULL;
4306                         }
4307                 }
4308
4309                 rc = fclose(fp);
4310                 free(line);
4311         }
4312
4313         /* If a --data was used, add it to the request */
4314         hur->hur_request.hr_data_len = opaque_len;
4315         if (opaque != NULL)
4316                 memcpy(hur_data(hur), opaque, opaque_len);
4317
4318         /* Send the HSM request */
4319         if (realpath(some_file, fullpath) == NULL) {
4320                 fprintf(stderr, "Could not find path '%s': %s\n",
4321                         some_file, strerror(errno));
4322         }
4323         rc = llapi_hsm_request(fullpath, hur);
4324         if (rc) {
4325                 fprintf(stderr, "Cannot send HSM request (use of %s): %s\n",
4326                         some_file, strerror(-rc));
4327                 goto out_free;
4328         }
4329
4330 out_free:
4331         free(some_file);
4332         free(hur);
4333         return rc;
4334 }
4335
4336 static int lfs_hsm_archive(int argc, char **argv)
4337 {
4338         return lfs_hsm_request(argc, argv, HUA_ARCHIVE);
4339 }
4340
4341 static int lfs_hsm_restore(int argc, char **argv)
4342 {
4343         return lfs_hsm_request(argc, argv, HUA_RESTORE);
4344 }
4345
4346 static int lfs_hsm_release(int argc, char **argv)
4347 {
4348         return lfs_hsm_request(argc, argv, HUA_RELEASE);
4349 }
4350
4351 static int lfs_hsm_remove(int argc, char **argv)
4352 {
4353         return lfs_hsm_request(argc, argv, HUA_REMOVE);
4354 }
4355
4356 static int lfs_hsm_cancel(int argc, char **argv)
4357 {
4358         return lfs_hsm_request(argc, argv, HUA_CANCEL);
4359 }
4360
4361 static int lfs_swap_layouts(int argc, char **argv)
4362 {
4363         if (argc != 3)
4364                 return CMD_HELP;
4365
4366         return llapi_swap_layouts(argv[1], argv[2], 0, 0,
4367                                   SWAP_LAYOUTS_KEEP_MTIME |
4368                                   SWAP_LAYOUTS_KEEP_ATIME);
4369 }
4370
4371 static const char *const ladvise_names[] = LU_LADVISE_NAMES;
4372
4373 static enum lu_ladvise_type lfs_get_ladvice(const char *string)
4374 {
4375         enum lu_ladvise_type advice;
4376
4377         for (advice = 0;
4378              advice < ARRAY_SIZE(ladvise_names); advice++) {
4379                 if (ladvise_names[advice] == NULL)
4380                         continue;
4381                 if (strcmp(string, ladvise_names[advice]) == 0)
4382                         return advice;
4383         }
4384
4385         return LU_LADVISE_INVALID;
4386 }
4387
4388 static int lfs_ladvise(int argc, char **argv)
4389 {
4390         struct option            long_opts[] = {
4391                 {"advice",      required_argument,      0, 'a'},
4392                 {"background",  no_argument,            0, 'b'},
4393                 {"end",         required_argument,      0, 'e'},
4394                 {"start",       required_argument,      0, 's'},
4395                 {"length",      required_argument,      0, 'l'},
4396                 {0, 0, 0, 0}
4397         };
4398         char                     short_opts[] = "a:be:l:s:";
4399         int                      c;
4400         int                      rc = 0;
4401         const char              *path;
4402         int                      fd;
4403         struct llapi_lu_ladvise  advice;
4404         enum lu_ladvise_type     advice_type = LU_LADVISE_INVALID;
4405         unsigned long long       start = 0;
4406         unsigned long long       end = LUSTRE_EOF;
4407         unsigned long long       length = 0;
4408         unsigned long long       size_units;
4409         unsigned long long       flags = 0;
4410
4411         optind = 0;
4412         while ((c = getopt_long(argc, argv, short_opts,
4413                                 long_opts, NULL)) != -1) {
4414                 switch (c) {
4415                 case 'a':
4416                         advice_type = lfs_get_ladvice(optarg);
4417                         if (advice_type == LU_LADVISE_INVALID) {
4418                                 fprintf(stderr, "%s: invalid advice type "
4419                                         "'%s'\n", argv[0], optarg);
4420                                 fprintf(stderr, "Valid types:");
4421
4422                                 for (advice_type = 0;
4423                                      advice_type < ARRAY_SIZE(ladvise_names);
4424                                      advice_type++) {
4425                                         if (ladvise_names[advice_type] == NULL)
4426                                                 continue;
4427                                         fprintf(stderr, " %s",
4428                                                 ladvise_names[advice_type]);
4429                                 }
4430                                 fprintf(stderr, "\n");
4431
4432                                 return CMD_HELP;
4433                         }
4434                         break;
4435                 case 'b':
4436                         flags |= LF_ASYNC;
4437                         break;
4438                 case 'e':
4439                         size_units = 1;
4440                         rc = llapi_parse_size(optarg, &end,
4441                                               &size_units, 0);
4442                         if (rc) {
4443                                 fprintf(stderr, "%s: bad end offset '%s'\n",
4444                                         argv[0], optarg);
4445                                 return CMD_HELP;
4446                         }
4447                         break;
4448                 case 's':
4449                         size_units = 1;
4450                         rc = llapi_parse_size(optarg, &start,
4451                                               &size_units, 0);
4452                         if (rc) {
4453                                 fprintf(stderr, "%s: bad start offset "
4454                                         "'%s'\n", argv[0], optarg);
4455                                 return CMD_HELP;
4456                         }
4457                         break;
4458                 case 'l':
4459                         size_units = 1;
4460                         rc = llapi_parse_size(optarg, &length,
4461                                               &size_units, 0);
4462                         if (rc) {
4463                                 fprintf(stderr, "%s: bad length '%s'\n",
4464                                         argv[0], optarg);
4465                                 return CMD_HELP;
4466                         }
4467                         break;
4468                 case '?':
4469                         return CMD_HELP;
4470                 default:
4471                         fprintf(stderr, "%s: option '%s' unrecognized\n",
4472                                 argv[0], argv[optind - 1]);
4473                         return CMD_HELP;
4474                 }
4475         }
4476
4477         if (advice_type == LU_LADVISE_INVALID) {
4478                 fprintf(stderr, "%s: please give an advice type\n", argv[0]);
4479                 fprintf(stderr, "Valid types:");
4480                 for (advice_type = 0; advice_type < ARRAY_SIZE(ladvise_names);
4481                      advice_type++) {
4482                         if (ladvise_names[advice_type] == NULL)
4483                                 continue;
4484                         fprintf(stderr, " %s", ladvise_names[advice_type]);
4485                 }
4486                 fprintf(stderr, "\n");
4487                 return CMD_HELP;
4488         }
4489
4490         if (argc <= optind) {
4491                 fprintf(stderr, "%s: please give one or more file names\n",
4492                         argv[0]);
4493                 return CMD_HELP;
4494         }
4495
4496         if (end != LUSTRE_EOF && length != 0 && end != start + length) {
4497                 fprintf(stderr, "%s: conflicting arguments of -l and -e\n",
4498                         argv[0]);
4499                 return CMD_HELP;
4500         }
4501
4502         if (end == LUSTRE_EOF && length != 0)
4503                 end = start + length;
4504
4505         if (end <= start) {
4506                 fprintf(stderr, "%s: range [%llu, %llu] is invalid\n",
4507                         argv[0], start, end);
4508                 return CMD_HELP;
4509         }
4510
4511         while (optind < argc) {
4512                 int rc2;
4513
4514                 path = argv[optind++];
4515
4516                 fd = open(path, O_RDONLY);
4517                 if (fd < 0) {
4518                         fprintf(stderr, "%s: cannot open file '%s': %s\n",
4519                                 argv[0], path, strerror(errno));
4520                         rc2 = -errno;
4521                         goto next;
4522                 }
4523
4524                 advice.lla_start = start;
4525                 advice.lla_end = end;
4526                 advice.lla_advice = advice_type;
4527                 advice.lla_value1 = 0;
4528                 advice.lla_value2 = 0;
4529                 advice.lla_value3 = 0;
4530                 advice.lla_value4 = 0;
4531                 rc2 = llapi_ladvise(fd, flags, 1, &advice);
4532                 close(fd);
4533                 if (rc2 < 0) {
4534                         fprintf(stderr, "%s: cannot give advice '%s' to file "
4535                                 "'%s': %s\n", argv[0],
4536                                 ladvise_names[advice_type],
4537                                 path, strerror(errno));
4538                 }
4539 next:
4540                 if (rc == 0 && rc2 < 0)
4541                         rc = rc2;
4542         }
4543         return rc;
4544 }
4545
4546 static int lfs_list_commands(int argc, char **argv)
4547 {
4548         char buffer[81] = ""; /* 80 printable chars + terminating NUL */
4549
4550         Parser_list_commands(cmdlist, buffer, sizeof(buffer), NULL, 0, 4);
4551
4552         return 0;
4553 }
4554
4555 int main(int argc, char **argv)
4556 {
4557         int rc;
4558
4559         /* Ensure that liblustreapi constructor has run */
4560         if (!liblustreapi_initialized)
4561                 fprintf(stderr, "liblustreapi was not properly initialized\n");
4562
4563         setlinebuf(stdout);
4564
4565         Parser_init("lfs > ", cmdlist);
4566
4567         progname = argv[0]; /* Used in error messages */
4568         if (argc > 1) {
4569                 rc = Parser_execarg(argc - 1, argv + 1, cmdlist);
4570         } else {
4571                 rc = Parser_commands();
4572         }
4573
4574         return rc < 0 ? -rc : rc;
4575 }
4576
4577 #ifdef _LUSTRE_IDL_H_
4578 /* Everything we need here should be included by lustreapi.h. */
4579 # error "lfs should not depend on lustre_idl.h"
4580 #endif /* _LUSTRE_IDL_H_ */