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