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