Whamcloud - gitweb
3c5aefa2c2de6610418d910c3aeff9cd2966b1b4
[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(result));
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         param->fp_max_depth = 1;
1779         while ((c = getopt_long(argc, argv, "cdDghiLMoO:pqrRsSv",
1780                                 long_opts, NULL)) != -1) {
1781                 switch (c) {
1782                 case 'O':
1783                         if (param->fp_obd_uuid) {
1784                                 fprintf(stderr,
1785                                         "error: %s: only one obduuid allowed",
1786                                         argv[0]);
1787                                 return CMD_HELP;
1788                         }
1789                         param->fp_obd_uuid = (struct obd_uuid *)optarg;
1790                         break;
1791                 case 'q':
1792                         param->fp_quiet++;
1793                         break;
1794                 case 'd':
1795                         param->fp_max_depth = 0;
1796                         break;
1797                 case 'D':
1798                         param->fp_get_default_lmv = 1;
1799                         break;
1800                 case 'r':
1801                         param->fp_recursive = 1;
1802                         break;
1803                 case 'v':
1804                         param->fp_verbose = VERBOSE_ALL | VERBOSE_DETAIL;
1805                         break;
1806                 case 'c':
1807 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1808                         if (strcmp(argv[optind - 1], "--count") == 0)
1809                                 fprintf(stderr, "warning: '--count' deprecated,"
1810                                         " use '--stripe-count' instead\n");
1811 #endif
1812                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1813                                 param->fp_verbose |= VERBOSE_COUNT;
1814                                 param->fp_max_depth = 0;
1815                         }
1816                         break;
1817 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1818                 case 's':
1819 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1820                         fprintf(stderr, "warning: '--size|-s' deprecated, "
1821                                 "use '--stripe-size|-S' instead\n");
1822 #endif
1823 #endif /* LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0) */
1824                 case 'S':
1825                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1826                                 param->fp_verbose |= VERBOSE_SIZE;
1827                                 param->fp_max_depth = 0;
1828                         }
1829                         break;
1830 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1831                 case 'o':
1832                         fprintf(stderr, "warning: '--offset|-o' deprecated, "
1833                                 "use '--stripe-index|-i' instead\n");
1834 #endif
1835                 case 'i':
1836 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1837                         if (strcmp(argv[optind - 1], "--index") == 0)
1838                                 fprintf(stderr, "warning: '--index' deprecated"
1839                                         ", use '--stripe-index' instead\n");
1840 #endif
1841                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1842                                 param->fp_verbose |= VERBOSE_OFFSET;
1843                                 param->fp_max_depth = 0;
1844                         }
1845                         break;
1846                 case 'p':
1847                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1848                                 param->fp_verbose |= VERBOSE_POOL;
1849                                 param->fp_max_depth = 0;
1850                         }
1851                         break;
1852                 case 'g':
1853                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1854                                 param->fp_verbose |= VERBOSE_GENERATION;
1855                                 param->fp_max_depth = 0;
1856                         }
1857                         break;
1858                 case 'L':
1859                         if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1860                                 param->fp_verbose |= VERBOSE_LAYOUT;
1861                                 param->fp_max_depth = 0;
1862                         }
1863                         break;
1864                 case 'M':
1865                         if (!(param->fp_verbose & VERBOSE_DETAIL))
1866                                 param->fp_max_depth = 0;
1867                         param->fp_verbose |= VERBOSE_MDTINDEX;
1868                         break;
1869                 case 'R':
1870                         param->fp_raw = 1;
1871                         break;
1872                 default:
1873                         return CMD_HELP;
1874                 }
1875         }
1876
1877         if (optind >= argc)
1878                 return CMD_HELP;
1879
1880         if (param->fp_recursive)
1881                 param->fp_max_depth = -1;
1882
1883         if (!param->fp_verbose)
1884                 param->fp_verbose = VERBOSE_ALL;
1885         if (param->fp_quiet)
1886                 param->fp_verbose = VERBOSE_OBJID;
1887
1888         do {
1889                 rc = llapi_getstripe(argv[optind], param);
1890         } while (++optind < argc && !rc);
1891
1892         if (rc)
1893                 fprintf(stderr, "error: %s failed for %s.\n",
1894                         argv[0], argv[optind - 1]);
1895         return rc;
1896 }
1897
1898 static int lfs_tgts(int argc, char **argv)
1899 {
1900         char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
1901         struct find_param param;
1902         int index = 0, rc=0;
1903
1904         if (argc > 2)
1905                 return CMD_HELP;
1906
1907         if (argc == 2 && !realpath(argv[1], path)) {
1908                 rc = -errno;
1909                 fprintf(stderr, "error: invalid path '%s': %s\n",
1910                         argv[1], strerror(-rc));
1911                 return rc;
1912         }
1913
1914         while (!llapi_search_mounts(path, index++, mntdir, NULL)) {
1915                 /* Check if we have a mount point */
1916                 if (mntdir[0] == '\0')
1917                         continue;
1918
1919                 memset(&param, 0, sizeof(param));
1920                 if (!strcmp(argv[0], "mdts"))
1921                         param.fp_get_lmv = 1;
1922
1923                 rc = llapi_ostlist(mntdir, &param);
1924                 if (rc) {
1925                         fprintf(stderr, "error: %s: failed on %s\n",
1926                                 argv[0], mntdir);
1927                 }
1928                 if (path[0] != '\0')
1929                         break;
1930                 memset(mntdir, 0, PATH_MAX);
1931         }
1932
1933         return rc;
1934 }
1935
1936 static int lfs_getstripe(int argc, char **argv)
1937 {
1938         struct find_param param = { 0 };
1939         return lfs_getstripe_internal(argc, argv, &param);
1940 }
1941
1942 /* functions */
1943 static int lfs_getdirstripe(int argc, char **argv)
1944 {
1945         struct find_param param = { 0 };
1946
1947         param.fp_get_lmv = 1;
1948         return lfs_getstripe_internal(argc, argv, &param);
1949 }
1950
1951 /* functions */
1952 static int lfs_setdirstripe(int argc, char **argv)
1953 {
1954         char                    *dname;
1955         int                     result;
1956         unsigned int            stripe_offset = -1;
1957         unsigned int            stripe_count = 1;
1958         enum lmv_hash_type      hash_type;
1959         char                    *end;
1960         int                     c;
1961         char                    *stripe_offset_opt = NULL;
1962         char                    *stripe_count_opt = NULL;
1963         char                    *stripe_hash_opt = NULL;
1964         char                    *mode_opt = NULL;
1965         bool                    default_stripe = false;
1966         mode_t                  mode = S_IRWXU | S_IRWXG | S_IRWXO;
1967         mode_t                  previous_mode = 0;
1968         bool                    delete = false;
1969
1970         struct option long_opts[] = {
1971                 {"count",       required_argument, 0, 'c'},
1972                 {"delete",      no_argument, 0, 'd'},
1973                 {"index",       required_argument, 0, 'i'},
1974                 {"mode",        required_argument, 0, 'm'},
1975                 {"hash-type",   required_argument, 0, 't'},
1976                 {"default_stripe", no_argument, 0, 'D'},
1977                 {0, 0, 0, 0}
1978         };
1979
1980         while ((c = getopt_long(argc, argv, "c:dDi:m:t:", long_opts,
1981                                 NULL)) >= 0) {
1982                 switch (c) {
1983                 case 0:
1984                         /* Long options. */
1985                         break;
1986                 case 'c':
1987                         stripe_count_opt = optarg;
1988                         break;
1989                 case 'd':
1990                         delete = true;
1991                         default_stripe = true;
1992                         break;
1993                 case 'D':
1994                         default_stripe = true;
1995                         break;
1996                 case 'i':
1997                         stripe_offset_opt = optarg;
1998                         break;
1999                 case 'm':
2000                         mode_opt = optarg;
2001                         break;
2002                 case 't':
2003                         stripe_hash_opt = optarg;
2004                         break;
2005                 default:
2006                         fprintf(stderr, "error: %s: option '%s' "
2007                                         "unrecognized\n",
2008                                         argv[0], argv[optind - 1]);
2009                         return CMD_HELP;
2010                 }
2011         }
2012
2013         if (optind == argc) {
2014                 fprintf(stderr, "error: %s: missing dirname\n",
2015                         argv[0]);
2016                 return CMD_HELP;
2017         }
2018
2019         if (!delete && stripe_offset_opt == NULL && stripe_count_opt == NULL) {
2020                 fprintf(stderr, "error: %s: missing stripe offset and count.\n",
2021                         argv[0]);
2022                 return CMD_HELP;
2023         }
2024
2025         if (stripe_offset_opt != NULL) {
2026                 /* get the stripe offset */
2027                 stripe_offset = strtoul(stripe_offset_opt, &end, 0);
2028                 if (*end != '\0') {
2029                         fprintf(stderr, "error: %s: bad stripe offset '%s'\n",
2030                                 argv[0], stripe_offset_opt);
2031                         return CMD_HELP;
2032                 }
2033         }
2034
2035         if (delete) {
2036                 if (stripe_offset_opt != NULL || stripe_count_opt != NULL) {
2037                         fprintf(stderr, "error: %s: cannot specify -d with -s,"
2038                                 " or -i options.\n", argv[0]);
2039                         return CMD_HELP;
2040                 } else {
2041                         stripe_count = 0;
2042                 }
2043         }
2044
2045
2046         if (mode_opt != NULL) {
2047                 mode = strtoul(mode_opt, &end, 8);
2048                 if (*end != '\0') {
2049                         fprintf(stderr, "error: %s: bad mode '%s'\n",
2050                                 argv[0], mode_opt);
2051                         return CMD_HELP;
2052                 }
2053                 previous_mode = umask(0);
2054         }
2055
2056         if (stripe_hash_opt == NULL ||
2057             strcmp(stripe_hash_opt, LMV_HASH_NAME_FNV_1A_64) == 0) {
2058                 hash_type = LMV_HASH_TYPE_FNV_1A_64;
2059         } else if (strcmp(stripe_hash_opt, LMV_HASH_NAME_ALL_CHARS) == 0) {
2060                 hash_type = LMV_HASH_TYPE_ALL_CHARS;
2061         } else {
2062                 fprintf(stderr, "error: %s: bad stripe hash type '%s'\n",
2063                         argv[0], stripe_hash_opt);
2064                 return CMD_HELP;
2065         }
2066
2067         /* get the stripe count */
2068         if (stripe_count_opt != NULL) {
2069                 stripe_count = strtoul(stripe_count_opt, &end, 0);
2070                 if (*end != '\0') {
2071                         fprintf(stderr, "error: %s: bad stripe count '%s'\n",
2072                                 argv[0], stripe_count_opt);
2073                         return CMD_HELP;
2074                 }
2075         }
2076
2077         dname = argv[optind];
2078         do {
2079                 if (default_stripe) {
2080                         result = llapi_dir_set_default_lmv_stripe(dname,
2081                                                     stripe_offset, stripe_count,
2082                                                     hash_type, NULL);
2083                 } else {
2084                         result = llapi_dir_create_pool(dname, mode,
2085                                                        stripe_offset,
2086                                                        stripe_count, hash_type,
2087                                                        NULL);
2088                 }
2089
2090                 if (result) {
2091                         fprintf(stderr, "error: %s: create stripe dir '%s' "
2092                                 "failed\n", argv[0], dname);
2093                         break;
2094                 }
2095                 dname = argv[++optind];
2096         } while (dname != NULL);
2097
2098         if (mode_opt != NULL)
2099                 umask(previous_mode);
2100
2101         return result;
2102 }
2103
2104 /* functions */
2105 static int lfs_rmentry(int argc, char **argv)
2106 {
2107         char *dname;
2108         int   index;
2109         int   result = 0;
2110
2111         if (argc <= 1) {
2112                 fprintf(stderr, "error: %s: missing dirname\n",
2113                         argv[0]);
2114                 return CMD_HELP;
2115         }
2116
2117         index = 1;
2118         dname = argv[index];
2119         while (dname != NULL) {
2120                 result = llapi_direntry_remove(dname);
2121                 if (result) {
2122                         fprintf(stderr, "error: %s: remove dir entry '%s' "
2123                                 "failed\n", argv[0], dname);
2124                         break;
2125                 }
2126                 dname = argv[++index];
2127         }
2128         return result;
2129 }
2130
2131 static int lfs_mv(int argc, char **argv)
2132 {
2133         struct  find_param param = {
2134                 .fp_max_depth = -1,
2135                 .fp_mdt_index = -1,
2136         };
2137         char   *end;
2138         int     c;
2139         int     rc = 0;
2140         struct option long_opts[] = {
2141                 {"mdt-index", required_argument, 0, 'M'},
2142                 {"verbose",     no_argument,       0, 'v'},
2143                 {0, 0, 0, 0}
2144         };
2145
2146         while ((c = getopt_long(argc, argv, "M:v", long_opts, NULL)) != -1) {
2147                 switch (c) {
2148                 case 'M': {
2149                         param.fp_mdt_index = strtoul(optarg, &end, 0);
2150                         if (*end != '\0') {
2151                                 fprintf(stderr, "%s: invalid MDT index'%s'\n",
2152                                         argv[0], optarg);
2153                                 return CMD_HELP;
2154                         }
2155                         break;
2156                 }
2157                 case 'v': {
2158                         param.fp_verbose = VERBOSE_DETAIL;
2159                         break;
2160                 }
2161                 default:
2162                         fprintf(stderr, "error: %s: unrecognized option '%s'\n",
2163                                 argv[0], argv[optind - 1]);
2164                         return CMD_HELP;
2165                 }
2166         }
2167
2168         if (param.fp_mdt_index == -1) {
2169                 fprintf(stderr, "%s: MDT index must be specified\n", argv[0]);
2170                 return CMD_HELP;
2171         }
2172
2173         if (optind >= argc) {
2174                 fprintf(stderr, "%s: missing operand path\n", argv[0]);
2175                 return CMD_HELP;
2176         }
2177
2178         param.fp_migrate = 1;
2179         rc = llapi_migrate_mdt(argv[optind], &param);
2180         if (rc != 0)
2181                 fprintf(stderr, "%s: cannot migrate '%s' to MDT%04x: %s\n",
2182                         argv[0], argv[optind], param.fp_mdt_index,
2183                         strerror(-rc));
2184         return rc;
2185 }
2186
2187 static int lfs_osts(int argc, char **argv)
2188 {
2189         return lfs_tgts(argc, argv);
2190 }
2191
2192 static int lfs_mdts(int argc, char **argv)
2193 {
2194         return lfs_tgts(argc, argv);
2195 }
2196
2197 #define COOK(value)                                                     \
2198 ({                                                                      \
2199         int radix = 0;                                                  \
2200         while (value > 1024) {                                          \
2201                 value /= 1024;                                          \
2202                 radix++;                                                \
2203         }                                                               \
2204         radix;                                                          \
2205 })
2206 #define UUF     "%-20s"
2207 #define CSF     "%11s"
2208 #define CDF     "%11llu"
2209 #define HDF     "%8.1f%c"
2210 #define RSF     "%4s"
2211 #define RDF     "%3d%%"
2212
2213 static int showdf(char *mntdir, struct obd_statfs *stat,
2214                   char *uuid, int ishow, int cooked,
2215                   char *type, int index, int rc)
2216 {
2217         long long avail, used, total;
2218         double ratio = 0;
2219         char *suffix = "KMGTPEZY";
2220         /* Note if we have >2^64 bytes/fs these buffers will need to be grown */
2221         char tbuf[3 * sizeof(__u64)];
2222         char ubuf[3 * sizeof(__u64)];
2223         char abuf[3 * sizeof(__u64)];
2224         char rbuf[3 * sizeof(__u64)];
2225
2226         if (!uuid || !stat)
2227                 return -EINVAL;
2228
2229         switch (rc) {
2230         case 0:
2231                 if (ishow) {
2232                         avail = stat->os_ffree;
2233                         used = stat->os_files - stat->os_ffree;
2234                         total = stat->os_files;
2235                 } else {
2236                         int shift = cooked ? 0 : 10;
2237
2238                         avail = (stat->os_bavail * stat->os_bsize) >> shift;
2239                         used  = ((stat->os_blocks - stat->os_bfree) *
2240                                  stat->os_bsize) >> shift;
2241                         total = (stat->os_blocks * stat->os_bsize) >> shift;
2242                 }
2243
2244                 if ((used + avail) > 0)
2245                         ratio = (double)used / (double)(used + avail);
2246
2247                 if (cooked) {
2248                         int i;
2249                         double cook_val;
2250
2251                         cook_val = (double)total;
2252                         i = COOK(cook_val);
2253                         if (i > 0)
2254                                 sprintf(tbuf, HDF, cook_val, suffix[i - 1]);
2255                         else
2256                                 sprintf(tbuf, CDF, total);
2257
2258                         cook_val = (double)used;
2259                         i = COOK(cook_val);
2260                         if (i > 0)
2261                                 sprintf(ubuf, HDF, cook_val, suffix[i - 1]);
2262                         else
2263                                 sprintf(ubuf, CDF, used);
2264
2265                         cook_val = (double)avail;
2266                         i = COOK(cook_val);
2267                         if (i > 0)
2268                                 sprintf(abuf, HDF, cook_val, suffix[i - 1]);
2269                         else
2270                                 sprintf(abuf, CDF, avail);
2271                 } else {
2272                         sprintf(tbuf, CDF, total);
2273                         sprintf(ubuf, CDF, used);
2274                         sprintf(abuf, CDF, avail);
2275                 }
2276
2277                 sprintf(rbuf, RDF, (int)(ratio * 100 + 0.5));
2278                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s",
2279                        uuid, tbuf, ubuf, abuf, rbuf, mntdir);
2280                 if (type)
2281                         printf("[%s:%d]\n", type, index);
2282                 else
2283                         printf("\n");
2284
2285                 break;
2286         case -ENODATA:
2287                 printf(UUF": inactive device\n", uuid);
2288                 break;
2289         default:
2290                 printf(UUF": %s\n", uuid, strerror(-rc));
2291                 break;
2292         }
2293
2294         return 0;
2295 }
2296
2297 struct ll_stat_type {
2298         int   st_op;
2299         char *st_name;
2300 };
2301
2302 static int mntdf(char *mntdir, char *fsname, char *pool, int ishow,
2303                 int cooked, int lazy)
2304 {
2305         struct obd_statfs stat_buf, sum = { .os_bsize = 1 };
2306         struct obd_uuid uuid_buf;
2307         char *poolname = NULL;
2308         struct ll_stat_type types[] = { { LL_STATFS_LMV, "MDT" },
2309                                         { LL_STATFS_LOV, "OST" },
2310                                         { 0, NULL } };
2311         struct ll_stat_type *tp;
2312         __u64 ost_ffree = 0;
2313         __u32 index;
2314         __u32 type;
2315         int rc;
2316
2317         if (pool) {
2318                 poolname = strchr(pool, '.');
2319                 if (poolname != NULL) {
2320                         if (strncmp(fsname, pool, strlen(fsname))) {
2321                                 fprintf(stderr, "filesystem name incorrect\n");
2322                                 return -ENODEV;
2323                         }
2324                         poolname++;
2325                 } else
2326                         poolname = pool;
2327         }
2328
2329         if (ishow)
2330                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2331                        "UUID", "Inodes", "IUsed", "IFree",
2332                        "IUse%", "Mounted on");
2333         else
2334                 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2335                        "UUID", cooked ? "bytes" : "1K-blocks",
2336                        "Used", "Available", "Use%", "Mounted on");
2337
2338         for (tp = types; tp->st_name != NULL; tp++) {
2339                 for (index = 0; ; index++) {
2340                         memset(&stat_buf, 0, sizeof(struct obd_statfs));
2341                         memset(&uuid_buf, 0, sizeof(struct obd_uuid));
2342                         type = lazy ? tp->st_op | LL_STATFS_NODELAY : tp->st_op;
2343                         rc = llapi_obd_statfs(mntdir, type, index,
2344                                               &stat_buf, &uuid_buf);
2345                         if (rc == -ENODEV)
2346                                 break;
2347
2348                         if (rc == -EAGAIN)
2349                                 continue;
2350
2351                         if (poolname && tp->st_op == LL_STATFS_LOV &&
2352                             llapi_search_ost(fsname, poolname,
2353                                              obd_uuid2str(&uuid_buf)) != 1)
2354                                 continue;
2355
2356                         /* the llapi_obd_statfs() call may have returned with
2357                          * an error, but if it filled in uuid_buf we will at
2358                          * lease use that to print out a message for that OBD.
2359                          * If we didn't get anything in the uuid_buf, then fill
2360                          * it in so that we can print an error message. */
2361                         if (uuid_buf.uuid[0] == '\0')
2362                                 sprintf(uuid_buf.uuid, "%s%04x",
2363                                         tp->st_name, index);
2364                         showdf(mntdir, &stat_buf, obd_uuid2str(&uuid_buf),
2365                                ishow, cooked, tp->st_name, index, rc);
2366
2367                         if (rc == 0) {
2368                                 if (tp->st_op == LL_STATFS_LMV) {
2369                                         sum.os_ffree += stat_buf.os_ffree;
2370                                         sum.os_files += stat_buf.os_files;
2371                                 } else /* if (tp->st_op == LL_STATFS_LOV) */ {
2372                                         sum.os_blocks += stat_buf.os_blocks *
2373                                                 stat_buf.os_bsize;
2374                                         sum.os_bfree  += stat_buf.os_bfree *
2375                                                 stat_buf.os_bsize;
2376                                         sum.os_bavail += stat_buf.os_bavail *
2377                                                 stat_buf.os_bsize;
2378                                         ost_ffree += stat_buf.os_ffree;
2379                                 }
2380                         } else if (rc == -EINVAL || rc == -EFAULT) {
2381                                 break;
2382                         }
2383                 }
2384         }
2385
2386         /* If we don't have as many objects free on the OST as inodes
2387          * on the MDS, we reduce the total number of inodes to
2388          * compensate, so that the "inodes in use" number is correct.
2389          * Matches ll_statfs_internal() so the results are consistent. */
2390         if (ost_ffree < sum.os_ffree) {
2391                 sum.os_files = (sum.os_files - sum.os_ffree) + ost_ffree;
2392                 sum.os_ffree = ost_ffree;
2393         }
2394         printf("\n");
2395         showdf(mntdir, &sum, "filesystem summary:", ishow, cooked, NULL, 0, 0);
2396         printf("\n");
2397         return 0;
2398 }
2399
2400 static int lfs_df(int argc, char **argv)
2401 {
2402         char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
2403         int ishow = 0, cooked = 0;
2404         int lazy = 0;
2405         int c, rc = 0, index = 0;
2406         char fsname[PATH_MAX] = "", *pool_name = NULL;
2407         struct option long_opts[] = {
2408                 {"pool", required_argument, 0, 'p'},
2409                 {"lazy", 0, 0, 'l'},
2410                 {0, 0, 0, 0}
2411         };
2412
2413         while ((c = getopt_long(argc, argv, "hilp:", long_opts, NULL)) != -1) {
2414                 switch (c) {
2415                 case 'i':
2416                         ishow = 1;
2417                         break;
2418                 case 'h':
2419                         cooked = 1;
2420                         break;
2421                 case 'l':
2422                         lazy = 1;
2423                         break;
2424                 case 'p':
2425                         pool_name = optarg;
2426                         break;
2427                 default:
2428                         return CMD_HELP;
2429                 }
2430         }
2431         if (optind < argc && !realpath(argv[optind], path)) {
2432                 rc = -errno;
2433                 fprintf(stderr, "error: invalid path '%s': %s\n",
2434                         argv[optind], strerror(-rc));
2435                 return rc;
2436         }
2437
2438         while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2439                 /* Check if we have a mount point */
2440                 if (mntdir[0] == '\0')
2441                         continue;
2442
2443                 rc = mntdf(mntdir, fsname, pool_name, ishow, cooked, lazy);
2444                 if (rc || path[0] != '\0')
2445                         break;
2446                 fsname[0] = '\0'; /* avoid matching in next loop */
2447                 mntdir[0] = '\0'; /* avoid matching in next loop */
2448         }
2449
2450         return rc;
2451 }
2452
2453 static int lfs_getname(int argc, char **argv)
2454 {
2455         char mntdir[PATH_MAX] = "", path[PATH_MAX] = "", fsname[PATH_MAX] = "";
2456         int rc = 0, index = 0, c;
2457         char buf[sizeof(struct obd_uuid)];
2458
2459         while ((c = getopt(argc, argv, "h")) != -1)
2460                 return CMD_HELP;
2461
2462         if (optind == argc) { /* no paths specified, get all paths. */
2463                 while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2464                         rc = llapi_getname(mntdir, buf, sizeof(buf));
2465                         if (rc < 0) {
2466                                 fprintf(stderr,
2467                                         "cannot get name for `%s': %s\n",
2468                                         mntdir, strerror(-rc));
2469                                 break;
2470                         }
2471
2472                         printf("%s %s\n", buf, mntdir);
2473
2474                         path[0] = fsname[0] = mntdir[0] = 0;
2475                 }
2476         } else { /* paths specified, only attempt to search these. */
2477                 for (; optind < argc; optind++) {
2478                         rc = llapi_getname(argv[optind], buf, sizeof(buf));
2479                         if (rc < 0) {
2480                                 fprintf(stderr,
2481                                         "cannot get name for `%s': %s\n",
2482                                         argv[optind], strerror(-rc));
2483                                 break;
2484                         }
2485
2486                         printf("%s %s\n", buf, argv[optind]);
2487                 }
2488         }
2489         return rc;
2490 }
2491
2492 static int lfs_check(int argc, char **argv)
2493 {
2494         int rc;
2495         char mntdir[PATH_MAX] = {'\0'};
2496         int num_types = 1;
2497         char *obd_types[2];
2498         char obd_type1[4];
2499         char obd_type2[4];
2500
2501         if (argc != 2)
2502                 return CMD_HELP;
2503
2504         obd_types[0] = obd_type1;
2505         obd_types[1] = obd_type2;
2506
2507         if (strcmp(argv[1], "osts") == 0) {
2508                 strcpy(obd_types[0], "osc");
2509         } else if (strcmp(argv[1], "mds") == 0) {
2510                 strcpy(obd_types[0], "mdc");
2511         } else if (strcmp(argv[1], "servers") == 0) {
2512                 num_types = 2;
2513                 strcpy(obd_types[0], "osc");
2514                 strcpy(obd_types[1], "mdc");
2515         } else {
2516                 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
2517                                 argv[0], argv[1]);
2518                         return CMD_HELP;
2519         }
2520
2521         rc = llapi_search_mounts(NULL, 0, mntdir, NULL);
2522         if (rc < 0 || mntdir[0] == '\0') {
2523                 fprintf(stderr, "No suitable Lustre mount found\n");
2524                 return rc;
2525         }
2526
2527         rc = llapi_target_check(num_types, obd_types, mntdir);
2528         if (rc)
2529                 fprintf(stderr, "error: %s: %s status failed\n",
2530                                 argv[0],argv[1]);
2531
2532         return rc;
2533
2534 }
2535
2536 static int lfs_join(int argc, char **argv)
2537 {
2538         fprintf(stderr, "join two lustre files into one.\n"
2539                         "obsolete, HEAD does not support it anymore.\n");
2540         return 0;
2541 }
2542
2543 #ifdef HAVE_SYS_QUOTA_H
2544 #define ARG2INT(nr, str, msg)                                           \
2545 do {                                                                    \
2546         char *endp;                                                     \
2547         nr = strtol(str, &endp, 0);                                     \
2548         if (*endp) {                                                    \
2549                 fprintf(stderr, "error: bad %s: %s\n", msg, str);       \
2550                 return CMD_HELP;                                        \
2551         }                                                               \
2552 } while (0)
2553
2554 #define ADD_OVERFLOW(a,b) ((a + b) < a) ? (a = ULONG_MAX) : (a = a + b)
2555
2556 /* Convert format time string "XXwXXdXXhXXmXXs" into seconds value
2557  * returns the value or ULONG_MAX on integer overflow or incorrect format
2558  * Notes:
2559  *        1. the order of specifiers is arbitrary (may be: 5w3s or 3s5w)
2560  *        2. specifiers may be encountered multiple times (2s3s is 5 seconds)
2561  *        3. empty integer value is interpreted as 0
2562  */
2563 static unsigned long str2sec(const char* timestr)
2564 {
2565         const char spec[] = "smhdw";
2566         const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2567         unsigned long val = 0;
2568         char *tail;
2569
2570         if (strpbrk(timestr, spec) == NULL) {
2571                 /* no specifiers inside the time string,
2572                    should treat it as an integer value */
2573                 val = strtoul(timestr, &tail, 10);
2574                 return *tail ? ULONG_MAX : val;
2575         }
2576
2577         /* format string is XXwXXdXXhXXmXXs */
2578         while (*timestr) {
2579                 unsigned long v;
2580                 int ind;
2581                 char* ptr;
2582
2583                 v = strtoul(timestr, &tail, 10);
2584                 if (v == ULONG_MAX || *tail == '\0')
2585                         /* value too large (ULONG_MAX or more)
2586                            or missing specifier */
2587                         goto error;
2588
2589                 ptr = strchr(spec, *tail);
2590                 if (ptr == NULL)
2591                         /* unknown specifier */
2592                         goto error;
2593
2594                 ind = ptr - spec;
2595
2596                 /* check if product will overflow the type */
2597                 if (!(v < ULONG_MAX / mult[ind]))
2598                         goto error;
2599
2600                 ADD_OVERFLOW(val, mult[ind] * v);
2601                 if (val == ULONG_MAX)
2602                         goto error;
2603
2604                 timestr = tail + 1;
2605         }
2606
2607         return val;
2608
2609 error:
2610         return ULONG_MAX;
2611 }
2612
2613 #define ARG2ULL(nr, str, def_units)                                     \
2614 do {                                                                    \
2615         unsigned long long limit, units = def_units;                    \
2616         int rc;                                                         \
2617                                                                         \
2618         rc = llapi_parse_size(str, &limit, &units, 1);                  \
2619         if (rc < 0) {                                                   \
2620                 fprintf(stderr, "error: bad limit value %s\n", str);    \
2621                 return CMD_HELP;                                        \
2622         }                                                               \
2623         nr = limit;                                                     \
2624 } while (0)
2625
2626 static inline int has_times_option(int argc, char **argv)
2627 {
2628         int i;
2629
2630         for (i = 1; i < argc; i++)
2631                 if (!strcmp(argv[i], "-t"))
2632                         return 1;
2633
2634         return 0;
2635 }
2636
2637 int lfs_setquota_times(int argc, char **argv)
2638 {
2639         int c, rc;
2640         struct if_quotactl qctl;
2641         char *mnt, *obd_type = (char *)qctl.obd_type;
2642         struct obd_dqblk *dqb = &qctl.qc_dqblk;
2643         struct obd_dqinfo *dqi = &qctl.qc_dqinfo;
2644         struct option long_opts[] = {
2645                 {"block-grace",     required_argument, 0, 'b'},
2646                 {"group",           no_argument,       0, 'g'},
2647                 {"inode-grace",     required_argument, 0, 'i'},
2648                 {"times",           no_argument,       0, 't'},
2649                 {"user",            no_argument,       0, 'u'},
2650                 {0, 0, 0, 0}
2651         };
2652
2653         memset(&qctl, 0, sizeof(qctl));
2654         qctl.qc_cmd  = LUSTRE_Q_SETINFO;
2655         qctl.qc_type = UGQUOTA;
2656
2657         while ((c = getopt_long(argc, argv, "b:gi:tu", long_opts, NULL)) != -1) {
2658                 switch (c) {
2659                 case 'u':
2660                 case 'g':
2661                         if (qctl.qc_type != UGQUOTA) {
2662                                 fprintf(stderr, "error: -u and -g can't be used "
2663                                                 "more than once\n");
2664                                 return CMD_HELP;
2665                         }
2666                         qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2667                         break;
2668                 case 'b':
2669                         if ((dqi->dqi_bgrace = str2sec(optarg)) == ULONG_MAX) {
2670                                 fprintf(stderr, "error: bad block-grace: %s\n",
2671                                         optarg);
2672                                 return CMD_HELP;
2673                         }
2674                         dqb->dqb_valid |= QIF_BTIME;
2675                         break;
2676                 case 'i':
2677                         if ((dqi->dqi_igrace = str2sec(optarg)) == ULONG_MAX) {
2678                                 fprintf(stderr, "error: bad inode-grace: %s\n",
2679                                         optarg);
2680                                 return CMD_HELP;
2681                         }
2682                         dqb->dqb_valid |= QIF_ITIME;
2683                         break;
2684                 case 't': /* Yes, of course! */
2685                         break;
2686                 default: /* getopt prints error message for us when opterr != 0 */
2687                         return CMD_HELP;
2688                 }
2689         }
2690
2691         if (qctl.qc_type == UGQUOTA) {
2692                 fprintf(stderr, "error: neither -u nor -g specified\n");
2693                 return CMD_HELP;
2694         }
2695
2696         if (optind != argc - 1) {
2697                 fprintf(stderr, "error: unexpected parameters encountered\n");
2698                 return CMD_HELP;
2699         }
2700
2701         mnt = argv[optind];
2702         rc = llapi_quotactl(mnt, &qctl);
2703         if (rc) {
2704                 if (*obd_type)
2705                         fprintf(stderr, "%s %s ", obd_type,
2706                                 obd_uuid2str(&qctl.obd_uuid));
2707                 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2708                 return rc;
2709         }
2710
2711         return 0;
2712 }
2713
2714 #define BSLIMIT (1 << 0)
2715 #define BHLIMIT (1 << 1)
2716 #define ISLIMIT (1 << 2)
2717 #define IHLIMIT (1 << 3)
2718
2719 int lfs_setquota(int argc, char **argv)
2720 {
2721         int c, rc;
2722         struct if_quotactl qctl;
2723         char *mnt, *obd_type = (char *)qctl.obd_type;
2724         struct obd_dqblk *dqb = &qctl.qc_dqblk;
2725         struct option long_opts[] = {
2726                 {"block-softlimit", required_argument, 0, 'b'},
2727                 {"block-hardlimit", required_argument, 0, 'B'},
2728                 {"group",           required_argument, 0, 'g'},
2729                 {"inode-softlimit", required_argument, 0, 'i'},
2730                 {"inode-hardlimit", required_argument, 0, 'I'},
2731                 {"user",            required_argument, 0, 'u'},
2732                 {0, 0, 0, 0}
2733         };
2734         unsigned limit_mask = 0;
2735         char *endptr;
2736
2737         if (has_times_option(argc, argv))
2738                 return lfs_setquota_times(argc, argv);
2739
2740         memset(&qctl, 0, sizeof(qctl));
2741         qctl.qc_cmd  = LUSTRE_Q_SETQUOTA;
2742         qctl.qc_type = UGQUOTA; /* UGQUOTA makes no sense for setquota,
2743                                  * so it can be used as a marker that qc_type
2744                                  * isn't reinitialized from command line */
2745
2746         while ((c = getopt_long(argc, argv, "b:B:g:i:I:u:", long_opts, NULL)) != -1) {
2747                 switch (c) {
2748                 case 'u':
2749                 case 'g':
2750                         if (qctl.qc_type != UGQUOTA) {
2751                                 fprintf(stderr, "error: -u and -g can't be used"
2752                                                 " more than once\n");
2753                                 return CMD_HELP;
2754                         }
2755                         qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2756                         rc = name2id(&qctl.qc_id, optarg,
2757                                      (qctl.qc_type == USRQUOTA) ? USER : GROUP);
2758                         if (rc) {
2759                                 qctl.qc_id = strtoul(optarg, &endptr, 10);
2760                                 if (*endptr != '\0') {
2761                                         fprintf(stderr, "error: can't find id "
2762                                                 "for name %s\n", optarg);
2763                                         return CMD_HELP;
2764                                 }
2765                         }
2766                         break;
2767                 case 'b':
2768                         ARG2ULL(dqb->dqb_bsoftlimit, optarg, 1024);
2769                         dqb->dqb_bsoftlimit >>= 10;
2770                         limit_mask |= BSLIMIT;
2771                         if (dqb->dqb_bsoftlimit &&
2772                             dqb->dqb_bsoftlimit <= 1024) /* <= 1M? */
2773                                 fprintf(stderr, "warning: block softlimit is "
2774                                         "smaller than the miminal qunit size, "
2775                                         "please see the help of setquota or "
2776                                         "Lustre manual for details.\n");
2777                         break;
2778                 case 'B':
2779                         ARG2ULL(dqb->dqb_bhardlimit, optarg, 1024);
2780                         dqb->dqb_bhardlimit >>= 10;
2781                         limit_mask |= BHLIMIT;
2782                         if (dqb->dqb_bhardlimit &&
2783                             dqb->dqb_bhardlimit <= 1024) /* <= 1M? */
2784                                 fprintf(stderr, "warning: block hardlimit is "
2785                                         "smaller than the miminal qunit size, "
2786                                         "please see the help of setquota or "
2787                                         "Lustre manual for details.\n");
2788                         break;
2789                 case 'i':
2790                         ARG2ULL(dqb->dqb_isoftlimit, optarg, 1);
2791                         limit_mask |= ISLIMIT;
2792                         if (dqb->dqb_isoftlimit &&
2793                             dqb->dqb_isoftlimit <= 1024) /* <= 1K inodes? */
2794                                 fprintf(stderr, "warning: inode softlimit is "
2795                                         "smaller than the miminal qunit size, "
2796                                         "please see the help of setquota or "
2797                                         "Lustre manual for details.\n");
2798                         break;
2799                 case 'I':
2800                         ARG2ULL(dqb->dqb_ihardlimit, optarg, 1);
2801                         limit_mask |= IHLIMIT;
2802                         if (dqb->dqb_ihardlimit &&
2803                             dqb->dqb_ihardlimit <= 1024) /* <= 1K inodes? */
2804                                 fprintf(stderr, "warning: inode hardlimit is "
2805                                         "smaller than the miminal qunit size, "
2806                                         "please see the help of setquota or "
2807                                         "Lustre manual for details.\n");
2808                         break;
2809                 default: /* getopt prints error message for us when opterr != 0 */
2810                         return CMD_HELP;
2811                 }
2812         }
2813
2814         if (qctl.qc_type == UGQUOTA) {
2815                 fprintf(stderr, "error: neither -u nor -g was specified\n");
2816                 return CMD_HELP;
2817         }
2818
2819         if (limit_mask == 0) {
2820                 fprintf(stderr, "error: at least one limit must be specified\n");
2821                 return CMD_HELP;
2822         }
2823
2824         if (optind != argc - 1) {
2825                 fprintf(stderr, "error: unexpected parameters encountered\n");
2826                 return CMD_HELP;
2827         }
2828
2829         mnt = argv[optind];
2830
2831         if ((!(limit_mask & BHLIMIT) ^ !(limit_mask & BSLIMIT)) ||
2832             (!(limit_mask & IHLIMIT) ^ !(limit_mask & ISLIMIT))) {
2833                 /* sigh, we can't just set blimits/ilimits */
2834                 struct if_quotactl tmp_qctl = {.qc_cmd  = LUSTRE_Q_GETQUOTA,
2835                                                .qc_type = qctl.qc_type,
2836                                                .qc_id   = qctl.qc_id};
2837
2838                 rc = llapi_quotactl(mnt, &tmp_qctl);
2839                 if (rc < 0) {
2840                         fprintf(stderr, "error: setquota failed while retrieving"
2841                                         " current quota settings (%s)\n",
2842                                         strerror(-rc));
2843                         return rc;
2844                 }
2845
2846                 if (!(limit_mask & BHLIMIT))
2847                         dqb->dqb_bhardlimit = tmp_qctl.qc_dqblk.dqb_bhardlimit;
2848                 if (!(limit_mask & BSLIMIT))
2849                         dqb->dqb_bsoftlimit = tmp_qctl.qc_dqblk.dqb_bsoftlimit;
2850                 if (!(limit_mask & IHLIMIT))
2851                         dqb->dqb_ihardlimit = tmp_qctl.qc_dqblk.dqb_ihardlimit;
2852                 if (!(limit_mask & ISLIMIT))
2853                         dqb->dqb_isoftlimit = tmp_qctl.qc_dqblk.dqb_isoftlimit;
2854
2855                 /* Keep grace times if we have got no softlimit arguments */
2856                 if ((limit_mask & BHLIMIT) && !(limit_mask & BSLIMIT)) {
2857                         dqb->dqb_valid |= QIF_BTIME;
2858                         dqb->dqb_btime = tmp_qctl.qc_dqblk.dqb_btime;
2859                 }
2860
2861                 if ((limit_mask & IHLIMIT) && !(limit_mask & ISLIMIT)) {
2862                         dqb->dqb_valid |= QIF_ITIME;
2863                         dqb->dqb_itime = tmp_qctl.qc_dqblk.dqb_itime;
2864                 }
2865         }
2866
2867         dqb->dqb_valid |= (limit_mask & (BHLIMIT | BSLIMIT)) ? QIF_BLIMITS : 0;
2868         dqb->dqb_valid |= (limit_mask & (IHLIMIT | ISLIMIT)) ? QIF_ILIMITS : 0;
2869
2870         rc = llapi_quotactl(mnt, &qctl);
2871         if (rc) {
2872                 if (*obd_type)
2873                         fprintf(stderr, "%s %s ", obd_type,
2874                                 obd_uuid2str(&qctl.obd_uuid));
2875                 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2876                 return rc;
2877         }
2878
2879         return 0;
2880 }
2881
2882 static inline char *type2name(int check_type)
2883 {
2884         if (check_type == USRQUOTA)
2885                 return "user";
2886         else if (check_type == GRPQUOTA)
2887                 return "group";
2888         else
2889                 return "unknown";
2890 }
2891
2892 /* Converts seconds value into format string
2893  * result is returned in buf
2894  * Notes:
2895  *        1. result is in descenting order: 1w2d3h4m5s
2896  *        2. zero fields are not filled (except for p. 3): 5d1s
2897  *        3. zero seconds value is presented as "0s"
2898  */
2899 static char * __sec2str(time_t seconds, char *buf)
2900 {
2901         const char spec[] = "smhdw";
2902         const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2903         unsigned long c;
2904         char *tail = buf;
2905         int i;
2906
2907         for (i = sizeof(mult) / sizeof(mult[0]) - 1 ; i >= 0; i--) {
2908                 c = seconds / mult[i];
2909
2910                 if (c > 0 || (i == 0 && buf == tail))
2911                         tail += snprintf(tail, 40-(tail-buf), "%lu%c", c, spec[i]);
2912
2913                 seconds %= mult[i];
2914         }
2915
2916         return tail;
2917 }
2918
2919 static void sec2str(time_t seconds, char *buf, int rc)
2920 {
2921         char *tail = buf;
2922
2923         if (rc)
2924                 *tail++ = '[';
2925
2926         tail = __sec2str(seconds, tail);
2927
2928         if (rc && tail - buf < 39) {
2929                 *tail++ = ']';
2930                 *tail++ = 0;
2931         }
2932 }
2933
2934 static void diff2str(time_t seconds, char *buf, time_t now)
2935 {
2936
2937         buf[0] = 0;
2938         if (!seconds)
2939                 return;
2940         if (seconds <= now) {
2941                 strcpy(buf, "none");
2942                 return;
2943         }
2944         __sec2str(seconds - now, buf);
2945 }
2946
2947 static void print_quota_title(char *name, struct if_quotactl *qctl,
2948                               bool human_readable)
2949 {
2950         printf("Disk quotas for %s %s (%cid %u):\n",
2951                type2name(qctl->qc_type), name,
2952                *type2name(qctl->qc_type), qctl->qc_id);
2953         printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n",
2954                "Filesystem", human_readable ? "used" : "kbytes",
2955                "quota", "limit", "grace",
2956                "files", "quota", "limit", "grace");
2957 }
2958
2959 static void kbytes2str(__u64 num, char *buf, int buflen, bool h)
2960 {
2961         if (!h) {
2962                 snprintf(buf, buflen, "%ju", (uintmax_t)num);
2963         } else {
2964                 if (num >> 40)
2965                         snprintf(buf, buflen, "%5.4gP",
2966                                  (double)num / ((__u64)1 << 40));
2967                 else if (num >> 30)
2968                         snprintf(buf, buflen, "%5.4gT",
2969                                  (double)num / (1 << 30));
2970                 else if (num >> 20)
2971                         snprintf(buf, buflen, "%5.4gG",
2972                                  (double)num / (1 << 20));
2973                 else if (num >> 10)
2974                         snprintf(buf, buflen, "%5.4gM",
2975                                  (double)num / (1 << 10));
2976                 else
2977                         snprintf(buf, buflen, "%ju%s", (uintmax_t)num, "k");
2978         }
2979 }
2980
2981 #define STRBUF_LEN      32
2982 static void print_quota(char *mnt, struct if_quotactl *qctl, int type,
2983                         int rc, bool h)
2984 {
2985         time_t now;
2986
2987         time(&now);
2988
2989         if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == Q_GETOQUOTA) {
2990                 int bover = 0, iover = 0;
2991                 struct obd_dqblk *dqb = &qctl->qc_dqblk;
2992                 char numbuf[3][STRBUF_LEN];
2993                 char timebuf[40];
2994                 char strbuf[STRBUF_LEN];
2995
2996                 if (dqb->dqb_bhardlimit &&
2997                     lustre_stoqb(dqb->dqb_curspace) >= dqb->dqb_bhardlimit) {
2998                         bover = 1;
2999                 } else if (dqb->dqb_bsoftlimit && dqb->dqb_btime) {
3000                         if (dqb->dqb_btime > now) {
3001                                 bover = 2;
3002                         } else {
3003                                 bover = 3;
3004                         }
3005                 }
3006
3007                 if (dqb->dqb_ihardlimit &&
3008                     dqb->dqb_curinodes >= dqb->dqb_ihardlimit) {
3009                         iover = 1;
3010                 } else if (dqb->dqb_isoftlimit && dqb->dqb_itime) {
3011                         if (dqb->dqb_itime > now) {
3012                                 iover = 2;
3013                         } else {
3014                                 iover = 3;
3015                         }
3016                 }
3017
3018
3019                 if (strlen(mnt) > 15)
3020                         printf("%s\n%15s", mnt, "");
3021                 else
3022                         printf("%15s", mnt);
3023
3024                 if (bover)
3025                         diff2str(dqb->dqb_btime, timebuf, now);
3026
3027                 kbytes2str(lustre_stoqb(dqb->dqb_curspace),
3028                            strbuf, sizeof(strbuf), h);
3029                 if (rc == -EREMOTEIO)
3030                         sprintf(numbuf[0], "%s*", strbuf);
3031                 else
3032                         sprintf(numbuf[0], (dqb->dqb_valid & QIF_SPACE) ?
3033                                 "%s" : "[%s]", strbuf);
3034
3035                 kbytes2str(dqb->dqb_bsoftlimit, strbuf, sizeof(strbuf), h);
3036                 if (type == QC_GENERAL)
3037                         sprintf(numbuf[1], (dqb->dqb_valid & QIF_BLIMITS) ?
3038                                 "%s" : "[%s]", strbuf);
3039                 else
3040                         sprintf(numbuf[1], "%s", "-");
3041
3042                 kbytes2str(dqb->dqb_bhardlimit, strbuf, sizeof(strbuf), h);
3043                 sprintf(numbuf[2], (dqb->dqb_valid & QIF_BLIMITS) ?
3044                         "%s" : "[%s]", strbuf);
3045
3046                 printf(" %7s%c %6s %7s %7s",
3047                        numbuf[0], bover ? '*' : ' ', numbuf[1],
3048                        numbuf[2], bover > 1 ? timebuf : "-");
3049
3050                 if (iover)
3051                         diff2str(dqb->dqb_itime, timebuf, now);
3052
3053                 sprintf(numbuf[0], (dqb->dqb_valid & QIF_INODES) ?
3054                         "%ju" : "[%ju]", (uintmax_t)dqb->dqb_curinodes);
3055
3056                 if (type == QC_GENERAL)
3057                         sprintf(numbuf[1], (dqb->dqb_valid & QIF_ILIMITS) ?
3058                                 "%ju" : "[%ju]",
3059                                 (uintmax_t)dqb->dqb_isoftlimit);
3060                 else
3061                         sprintf(numbuf[1], "%s", "-");
3062
3063                 sprintf(numbuf[2], (dqb->dqb_valid & QIF_ILIMITS) ?
3064                         "%ju" : "[%ju]", (uintmax_t)dqb->dqb_ihardlimit);
3065
3066                 if (type != QC_OSTIDX)
3067                         printf(" %7s%c %6s %7s %7s",
3068                                numbuf[0], iover ? '*' : ' ', numbuf[1],
3069                                numbuf[2], iover > 1 ? timebuf : "-");
3070                 else
3071                         printf(" %7s %7s %7s %7s", "-", "-", "-", "-");
3072                 printf("\n");
3073
3074         } else if (qctl->qc_cmd == LUSTRE_Q_GETINFO ||
3075                    qctl->qc_cmd == Q_GETOINFO) {
3076                 char bgtimebuf[40];
3077                 char igtimebuf[40];
3078
3079                 sec2str(qctl->qc_dqinfo.dqi_bgrace, bgtimebuf, rc);
3080                 sec2str(qctl->qc_dqinfo.dqi_igrace, igtimebuf, rc);
3081                 printf("Block grace time: %s; Inode grace time: %s\n",
3082                        bgtimebuf, igtimebuf);
3083         }
3084 }
3085
3086 static int print_obd_quota(char *mnt, struct if_quotactl *qctl, int is_mdt,
3087                            bool h, __u64 *total)
3088 {
3089         int rc = 0, rc1 = 0, count = 0;
3090         __u32 valid = qctl->qc_valid;
3091
3092         rc = llapi_get_obd_count(mnt, &count, is_mdt);
3093         if (rc) {
3094                 fprintf(stderr, "can not get %s count: %s\n",
3095                         is_mdt ? "mdt": "ost", strerror(-rc));
3096                 return rc;
3097         }
3098
3099         for (qctl->qc_idx = 0; qctl->qc_idx < count; qctl->qc_idx++) {
3100                 qctl->qc_valid = is_mdt ? QC_MDTIDX : QC_OSTIDX;
3101                 rc = llapi_quotactl(mnt, qctl);
3102                 if (rc) {
3103                         /* It is remote client case. */
3104                         if (-rc == EOPNOTSUPP) {
3105                                 rc = 0;
3106                                 goto out;
3107                         }
3108
3109                         if (!rc1)
3110                                 rc1 = rc;
3111                         fprintf(stderr, "quotactl %s%d failed.\n",
3112                                 is_mdt ? "mdt": "ost", qctl->qc_idx);
3113                         continue;
3114                 }
3115
3116                 print_quota(obd_uuid2str(&qctl->obd_uuid), qctl,
3117                             qctl->qc_valid, 0, h);
3118                 *total += is_mdt ? qctl->qc_dqblk.dqb_ihardlimit :
3119                                    qctl->qc_dqblk.dqb_bhardlimit;
3120         }
3121 out:
3122         qctl->qc_valid = valid;
3123         return rc ? : rc1;
3124 }
3125
3126 static int lfs_quota(int argc, char **argv)
3127 {
3128         int c;
3129         char *mnt, *name = NULL;
3130         struct if_quotactl qctl = { .qc_cmd = LUSTRE_Q_GETQUOTA,
3131                                     .qc_type = UGQUOTA };
3132         char *obd_type = (char *)qctl.obd_type;
3133         char *obd_uuid = (char *)qctl.obd_uuid.uuid;
3134         int rc, rc1 = 0, rc2 = 0, rc3 = 0,
3135             verbose = 0, pass = 0, quiet = 0, inacc;
3136         char *endptr;
3137         __u32 valid = QC_GENERAL, idx = 0;
3138         __u64 total_ialloc = 0, total_balloc = 0;
3139         bool human_readable = false;
3140
3141         while ((c = getopt(argc, argv, "gi:I:o:qtuvh")) != -1) {
3142                 switch (c) {
3143                 case 'u':
3144                         if (qctl.qc_type != UGQUOTA) {
3145                                 fprintf(stderr, "error: use either -u or -g\n");
3146                                 return CMD_HELP;
3147                         }
3148                         qctl.qc_type = USRQUOTA;
3149                         break;
3150                 case 'g':
3151                         if (qctl.qc_type != UGQUOTA) {
3152                                 fprintf(stderr, "error: use either -u or -g\n");
3153                                 return CMD_HELP;
3154                         }
3155                         qctl.qc_type = GRPQUOTA;
3156                         break;
3157                 case 't':
3158                         qctl.qc_cmd = LUSTRE_Q_GETINFO;
3159                         break;
3160                 case 'o':
3161                         valid = qctl.qc_valid = QC_UUID;
3162                         strlcpy(obd_uuid, optarg, sizeof(qctl.obd_uuid));
3163                         break;
3164                 case 'i':
3165                         valid = qctl.qc_valid = QC_MDTIDX;
3166                         idx = qctl.qc_idx = atoi(optarg);
3167                         break;
3168                 case 'I':
3169                         valid = qctl.qc_valid = QC_OSTIDX;
3170                         idx = qctl.qc_idx = atoi(optarg);
3171                         break;
3172                 case 'v':
3173                         verbose = 1;
3174                         break;
3175                 case 'q':
3176                         quiet = 1;
3177                         break;
3178                 case 'h':
3179                         human_readable = true;
3180                         break;
3181                 default:
3182                         fprintf(stderr, "error: %s: option '-%c' "
3183                                         "unrecognized\n", argv[0], c);
3184                         return CMD_HELP;
3185                 }
3186         }
3187
3188         /* current uid/gid info for "lfs quota /path/to/lustre/mount" */
3189         if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && qctl.qc_type == UGQUOTA &&
3190             optind == argc - 1) {
3191 ug_output:
3192                 memset(&qctl, 0, sizeof(qctl)); /* spoiled by print_*_quota */
3193                 qctl.qc_cmd = LUSTRE_Q_GETQUOTA;
3194                 qctl.qc_valid = valid;
3195                 qctl.qc_idx = idx;
3196                 if (pass++ == 0) {
3197                         qctl.qc_type = USRQUOTA;
3198                         qctl.qc_id = geteuid();
3199                 } else {
3200                         qctl.qc_type = GRPQUOTA;
3201                         qctl.qc_id = getegid();
3202                 }
3203                 rc = id2name(&name, qctl.qc_id,
3204                              (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3205                 if (rc)
3206                         name = "<unknown>";
3207         /* lfs quota -u username /path/to/lustre/mount */
3208         } else if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) {
3209                 /* options should be followed by u/g-name and mntpoint */
3210                 if (optind + 2 != argc || qctl.qc_type == UGQUOTA) {
3211                         fprintf(stderr, "error: missing quota argument(s)\n");
3212                         return CMD_HELP;
3213                 }
3214
3215                 name = argv[optind++];
3216                 rc = name2id(&qctl.qc_id, name,
3217                              (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3218                 if (rc) {
3219                         qctl.qc_id = strtoul(name, &endptr, 10);
3220                         if (*endptr != '\0') {
3221                                 fprintf(stderr, "error: can't find id for name "
3222                                         "%s\n", name);
3223                                 return CMD_HELP;
3224                         }
3225                 }
3226         } else if (optind + 1 != argc || qctl.qc_type == UGQUOTA) {
3227                 fprintf(stderr, "error: missing quota info argument(s)\n");
3228                 return CMD_HELP;
3229         }
3230
3231         mnt = argv[optind];
3232
3233         rc1 = llapi_quotactl(mnt, &qctl);
3234         if (rc1 < 0) {
3235                 switch (rc1) {
3236                 case -ESRCH:
3237                         fprintf(stderr, "%s quotas are not enabled.\n",
3238                                 qctl.qc_type == USRQUOTA ? "user" : "group");
3239                         goto out;
3240                 case -EPERM:
3241                         fprintf(stderr, "Permission denied.\n");
3242                 case -ENOENT:
3243                         /* We already got a "No such file..." message. */
3244                         goto out;
3245                 default:
3246                         fprintf(stderr, "Unexpected quotactl error: %s\n",
3247                                 strerror(-rc1));
3248                 }
3249         }
3250
3251         if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && !quiet)
3252                 print_quota_title(name, &qctl, human_readable);
3253
3254         if (rc1 && *obd_type)
3255                 fprintf(stderr, "%s %s ", obd_type, obd_uuid);
3256
3257         if (qctl.qc_valid != QC_GENERAL)
3258                 mnt = "";
3259
3260         inacc = (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) &&
3261                 ((qctl.qc_dqblk.dqb_valid & (QIF_LIMITS|QIF_USAGE)) !=
3262                  (QIF_LIMITS|QIF_USAGE));
3263
3264         print_quota(mnt, &qctl, QC_GENERAL, rc1, human_readable);
3265
3266         if (qctl.qc_valid == QC_GENERAL && qctl.qc_cmd != LUSTRE_Q_GETINFO &&
3267             verbose) {
3268                 char strbuf[STRBUF_LEN];
3269
3270                 rc2 = print_obd_quota(mnt, &qctl, 1, human_readable,
3271                                       &total_ialloc);
3272                 rc3 = print_obd_quota(mnt, &qctl, 0, human_readable,
3273                                       &total_balloc);
3274                 kbytes2str(total_balloc, strbuf, sizeof(strbuf),
3275                            human_readable);
3276                 printf("Total allocated inode limit: %ju, total "
3277                        "allocated block limit: %s\n", (uintmax_t)total_ialloc,
3278                        strbuf);
3279         }
3280
3281         if (rc1 || rc2 || rc3 || inacc)
3282                 printf("Some errors happened when getting quota info. "
3283                        "Some devices may be not working or deactivated. "
3284                        "The data in \"[]\" is inaccurate.\n");
3285
3286 out:
3287         if (pass == 1)
3288                 goto ug_output;
3289
3290         return rc1;
3291 }
3292 #endif /* HAVE_SYS_QUOTA_H! */
3293
3294 static int flushctx_ioctl(char *mp)
3295 {
3296         int fd, rc;
3297
3298         fd = open(mp, O_RDONLY);
3299         if (fd == -1) {
3300                 fprintf(stderr, "flushctx: error open %s: %s\n",
3301                         mp, strerror(errno));
3302                 return -1;
3303         }
3304
3305         rc = ioctl(fd, LL_IOC_FLUSHCTX);
3306         if (rc == -1)
3307                 fprintf(stderr, "flushctx: error ioctl %s: %s\n",
3308                         mp, strerror(errno));
3309
3310         close(fd);
3311         return rc;
3312 }
3313
3314 static int lfs_flushctx(int argc, char **argv)
3315 {
3316         int     kdestroy = 0, c;
3317         char    mntdir[PATH_MAX] = {'\0'};
3318         int     index = 0;
3319         int     rc = 0;
3320
3321         while ((c = getopt(argc, argv, "k")) != -1) {
3322                 switch (c) {
3323                 case 'k':
3324                         kdestroy = 1;
3325                         break;
3326                 default:
3327                         fprintf(stderr, "error: %s: option '-%c' "
3328                                         "unrecognized\n", argv[0], c);
3329                         return CMD_HELP;
3330                 }
3331         }
3332
3333         if (kdestroy) {
3334             if ((rc = system("kdestroy > /dev/null")) != 0) {
3335                 rc = WEXITSTATUS(rc);
3336                 fprintf(stderr, "error destroying tickets: %d, continuing\n", rc);
3337             }
3338         }
3339
3340         if (optind >= argc) {
3341                 /* flush for all mounted lustre fs. */
3342                 while (!llapi_search_mounts(NULL, index++, mntdir, NULL)) {
3343                         /* Check if we have a mount point */
3344                         if (mntdir[0] == '\0')
3345                                 continue;
3346
3347                         if (flushctx_ioctl(mntdir))
3348                                 rc = -1;
3349
3350                         mntdir[0] = '\0'; /* avoid matching in next loop */
3351                 }
3352         } else {
3353                 /* flush fs as specified */
3354                 while (optind < argc) {
3355                         if (flushctx_ioctl(argv[optind++]))
3356                                 rc = -1;
3357                 }
3358         }
3359         return rc;
3360 }
3361
3362 static int lfs_lsetfacl(int argc, char **argv)
3363 {
3364         fprintf(stderr, "local client sets facl for remote client.\n"
3365                 "obsolete, does not support it anymore.\n");
3366         return 0;
3367 }
3368
3369 static int lfs_lgetfacl(int argc, char **argv)
3370 {
3371         fprintf(stderr, "local client gets facl for remote client.\n"
3372                 "obsolete, does not support it anymore.\n");
3373         return 0;
3374 }
3375
3376 static int lfs_rsetfacl(int argc, char **argv)
3377 {
3378         fprintf(stderr, "remote client sets facl for remote client.\n"
3379                 "obsolete, does not support it anymore.\n");
3380         return 0;
3381 }
3382
3383 static int lfs_rgetfacl(int argc, char **argv)
3384 {
3385         fprintf(stderr, "remote client gets facl for remote client.\n"
3386                 "obsolete, does not support it anymore.\n");
3387         return 0;
3388 }
3389
3390 static int lfs_cp(int argc, char **argv)
3391 {
3392         fprintf(stderr, "remote client copy file(s).\n"
3393                 "obsolete, does not support it anymore.\n");
3394         return 0;
3395 }
3396
3397 static int lfs_ls(int argc, char **argv)
3398 {
3399         fprintf(stderr, "remote client lists directory contents.\n"
3400                 "obsolete, does not support it anymore.\n");
3401         return 0;
3402 }
3403
3404 static int lfs_changelog(int argc, char **argv)
3405 {
3406         void *changelog_priv;
3407         struct changelog_rec *rec;
3408         long long startrec = 0, endrec = 0;
3409         char *mdd;
3410         struct option long_opts[] = {
3411                 {"follow", no_argument, 0, 'f'},
3412                 {0, 0, 0, 0}
3413         };
3414         char short_opts[] = "f";
3415         int rc, follow = 0;
3416
3417         while ((rc = getopt_long(argc, argv, short_opts,
3418                                 long_opts, NULL)) != -1) {
3419                 switch (rc) {
3420                 case 'f':
3421                         follow++;
3422                         break;
3423                 case '?':
3424                         return CMD_HELP;
3425                 default:
3426                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3427                                 argv[0], argv[optind - 1]);
3428                         return CMD_HELP;
3429                 }
3430         }
3431         if (optind >= argc)
3432                 return CMD_HELP;
3433
3434         mdd = argv[optind++];
3435         if (argc > optind)
3436                 startrec = strtoll(argv[optind++], NULL, 10);
3437         if (argc > optind)
3438                 endrec = strtoll(argv[optind++], NULL, 10);
3439
3440         rc = llapi_changelog_start(&changelog_priv,
3441                                    CHANGELOG_FLAG_BLOCK |
3442                                    CHANGELOG_FLAG_JOBID |
3443                                    (follow ? CHANGELOG_FLAG_FOLLOW : 0),
3444                                    mdd, startrec);
3445         if (rc < 0) {
3446                 fprintf(stderr, "Can't start changelog: %s\n",
3447                         strerror(errno = -rc));
3448                 return rc;
3449         }
3450
3451         while ((rc = llapi_changelog_recv(changelog_priv, &rec)) == 0) {
3452                 time_t secs;
3453                 struct tm ts;
3454
3455                 if (endrec && rec->cr_index > endrec) {
3456                         llapi_changelog_free(&rec);
3457                         break;
3458                 }
3459                 if (rec->cr_index < startrec) {
3460                         llapi_changelog_free(&rec);
3461                         continue;
3462                 }
3463
3464                 secs = rec->cr_time >> 30;
3465                 gmtime_r(&secs, &ts);
3466                 printf("%ju %02d%-5s %02d:%02d:%02d.%06d %04d.%02d.%02d "
3467                        "0x%x t="DFID, (uintmax_t) rec->cr_index, rec->cr_type,
3468                        changelog_type2str(rec->cr_type),
3469                        ts.tm_hour, ts.tm_min, ts.tm_sec,
3470                        (int)(rec->cr_time & ((1<<30) - 1)),
3471                        ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday,
3472                        rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
3473
3474                 if (rec->cr_flags & CLF_JOBID) {
3475                         struct changelog_ext_jobid *jid =
3476                                 changelog_rec_jobid(rec);
3477
3478                         if (jid->cr_jobid[0] != '\0')
3479                                 printf(" j=%s", jid->cr_jobid);
3480                 }
3481
3482                 if (rec->cr_namelen)
3483                         printf(" p="DFID" %.*s", PFID(&rec->cr_pfid),
3484                                rec->cr_namelen, changelog_rec_name(rec));
3485
3486                 if (rec->cr_flags & CLF_RENAME) {
3487                         struct changelog_ext_rename *rnm =
3488                                 changelog_rec_rename(rec);
3489
3490                         if (!fid_is_zero(&rnm->cr_sfid))
3491                                 printf(" s="DFID" sp="DFID" %.*s",
3492                                        PFID(&rnm->cr_sfid),
3493                                        PFID(&rnm->cr_spfid),
3494                                        (int)changelog_rec_snamelen(rec),
3495                                        changelog_rec_sname(rec));
3496                 }
3497                 printf("\n");
3498
3499                 llapi_changelog_free(&rec);
3500         }
3501
3502         llapi_changelog_fini(&changelog_priv);
3503
3504         if (rc < 0)
3505                 fprintf(stderr, "Changelog: %s\n", strerror(errno = -rc));
3506
3507         return (rc == 1 ? 0 : rc);
3508 }
3509
3510 static int lfs_changelog_clear(int argc, char **argv)
3511 {
3512         long long endrec;
3513         int rc;
3514
3515         if (argc != 4)
3516                 return CMD_HELP;
3517
3518         endrec = strtoll(argv[3], NULL, 10);
3519
3520         rc = llapi_changelog_clear(argv[1], argv[2], endrec);
3521         if (rc)
3522                 fprintf(stderr, "%s error: %s\n", argv[0],
3523                         strerror(errno = -rc));
3524         return rc;
3525 }
3526
3527 static int lfs_fid2path(int argc, char **argv)
3528 {
3529         struct option long_opts[] = {
3530                 {"cur", no_argument, 0, 'c'},
3531                 {"link", required_argument, 0, 'l'},
3532                 {"rec", required_argument, 0, 'r'},
3533                 {0, 0, 0, 0}
3534         };
3535         char  short_opts[] = "cl:r:";
3536         char *device, *fid, *path;
3537         long long recno = -1;
3538         int linkno = -1;
3539         int lnktmp;
3540         int printcur = 0;
3541         int rc = 0;
3542
3543         while ((rc = getopt_long(argc, argv, short_opts,
3544                                 long_opts, NULL)) != -1) {
3545                 switch (rc) {
3546                 case 'c':
3547                         printcur++;
3548                         break;
3549                 case 'l':
3550                         linkno = strtol(optarg, NULL, 10);
3551                         break;
3552                 case 'r':
3553                         recno = strtoll(optarg, NULL, 10);
3554                         break;
3555                 case '?':
3556                         return CMD_HELP;
3557                 default:
3558                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3559                                 argv[0], argv[optind - 1]);
3560                         return CMD_HELP;
3561                 }
3562         }
3563
3564         if (argc < 3)
3565                 return CMD_HELP;
3566
3567         device = argv[optind++];
3568         path = calloc(1, PATH_MAX);
3569         if (path == NULL) {
3570                 fprintf(stderr, "error: Not enough memory\n");
3571                 return -errno;
3572         }
3573
3574         rc = 0;
3575         while (optind < argc) {
3576                 fid = argv[optind++];
3577
3578                 lnktmp = (linkno >= 0) ? linkno : 0;
3579                 while (1) {
3580                         int oldtmp = lnktmp;
3581                         long long rectmp = recno;
3582                         int rc2;
3583                         rc2 = llapi_fid2path(device, fid, path, PATH_MAX,
3584                                              &rectmp, &lnktmp);
3585                         if (rc2 < 0) {
3586                                 fprintf(stderr, "%s: error on FID %s: %s\n",
3587                                         argv[0], fid, strerror(errno = -rc2));
3588                                 if (rc == 0)
3589                                         rc = rc2;
3590                                 break;
3591                         }
3592
3593                         if (printcur)
3594                                 fprintf(stdout, "%lld ", rectmp);
3595                         if (device[0] == '/') {
3596                                 fprintf(stdout, "%s", device);
3597                                 if (device[strlen(device) - 1] != '/')
3598                                         fprintf(stdout, "/");
3599                         } else if (path[0] == '\0') {
3600                                 fprintf(stdout, "/");
3601                         }
3602                         fprintf(stdout, "%s\n", path);
3603
3604                         if (linkno >= 0)
3605                                 /* specified linkno */
3606                                 break;
3607                         if (oldtmp == lnktmp)
3608                                 /* no more links */
3609                                 break;
3610                 }
3611         }
3612
3613         free(path);
3614         return rc;
3615 }
3616
3617 static int lfs_path2fid(int argc, char **argv)
3618 {
3619         struct option     long_opts[] = {
3620                 {"parents", no_argument, 0, 'p'},
3621                 {0, 0, 0, 0}
3622         };
3623         char            **path;
3624         const char        short_opts[] = "p";
3625         const char       *sep = "";
3626         lustre_fid        fid;
3627         int               rc = 0;
3628         bool              show_parents = false;
3629
3630         while ((rc = getopt_long(argc, argv, short_opts,
3631                                  long_opts, NULL)) != -1) {
3632                 switch (rc) {
3633                 case 'p':
3634                         show_parents = true;
3635                         break;
3636                 default:
3637                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3638                                 argv[0], argv[optind - 1]);
3639                         return CMD_HELP;
3640                 }
3641         }
3642
3643         if (optind > argc - 1)
3644                 return CMD_HELP;
3645         else if (optind < argc - 1)
3646                 sep = ": ";
3647
3648         rc = 0;
3649         for (path = argv + optind; *path != NULL; path++) {
3650                 int err = 0;
3651                 if (!show_parents) {
3652                         err = llapi_path2fid(*path, &fid);
3653                         if (!err)
3654                                 printf("%s%s"DFID"\n",
3655                                        *sep != '\0' ? *path : "", sep,
3656                                        PFID(&fid));
3657                 } else {
3658                         char            name[NAME_MAX + 1];
3659                         unsigned int    linkno = 0;
3660
3661                         while ((err = llapi_path2parent(*path, linkno, &fid,
3662                                                 name, sizeof(name))) == 0) {
3663                                 if (*sep != '\0' && linkno == 0)
3664                                         printf("%s%s", *path, sep);
3665
3666                                 printf("%s"DFID"/%s", linkno != 0 ? "\t" : "",
3667                                        PFID(&fid), name);
3668                                 linkno++;
3669                         }
3670
3671                         /* err == -ENODATA is end-of-loop */
3672                         if (linkno > 0 && err == -ENODATA) {
3673                                 printf("\n");
3674                                 err = 0;
3675                         }
3676                 }
3677
3678                 if (err) {
3679                         fprintf(stderr, "%s: can't get %sfid for %s: %s\n",
3680                                 argv[0], show_parents ? "parent " : "", *path,
3681                                 strerror(-err));
3682                         if (rc == 0) {
3683                                 rc = err;
3684                                 errno = -err;
3685                         }
3686                 }
3687         }
3688
3689         return rc;
3690 }
3691
3692 static int lfs_data_version(int argc, char **argv)
3693 {
3694         char *path;
3695         __u64 data_version;
3696         int fd;
3697         int rc;
3698         int c;
3699         int data_version_flags = LL_DV_RD_FLUSH; /* Read by default */
3700
3701         if (argc < 2)
3702                 return CMD_HELP;
3703
3704         while ((c = getopt(argc, argv, "nrw")) != -1) {
3705                 switch (c) {
3706                 case 'n':
3707                         data_version_flags = 0;
3708                         break;
3709                 case 'r':
3710                         data_version_flags |= LL_DV_RD_FLUSH;
3711                         break;
3712                 case 'w':
3713                         data_version_flags |= LL_DV_WR_FLUSH;
3714                         break;
3715                 default:
3716                         return CMD_HELP;
3717                 }
3718         }
3719         if (optind == argc)
3720                 return CMD_HELP;
3721
3722         path = argv[optind];
3723         fd = open(path, O_RDONLY);
3724         if (fd < 0)
3725                 err(errno, "cannot open file %s", path);
3726
3727         rc = llapi_get_data_version(fd, &data_version, data_version_flags);
3728         if (rc < 0)
3729                 err(errno, "cannot get version for %s", path);
3730         else
3731                 printf("%ju" "\n", (uintmax_t)data_version);
3732
3733         close(fd);
3734         return rc;
3735 }
3736
3737 static int lfs_hsm_state(int argc, char **argv)
3738 {
3739         int rc;
3740         int i = 1;
3741         char *path;
3742         struct hsm_user_state hus;
3743
3744         if (argc < 2)
3745                 return CMD_HELP;
3746
3747         do {
3748                 path = argv[i];
3749
3750                 rc = llapi_hsm_state_get(path, &hus);
3751                 if (rc) {
3752                         fprintf(stderr, "can't get hsm state for %s: %s\n",
3753                                 path, strerror(errno = -rc));
3754                         return rc;
3755                 }
3756
3757                 /* Display path name and status flags */
3758                 printf("%s: (0x%08x)", path, hus.hus_states);
3759
3760                 if (hus.hus_states & HS_RELEASED)
3761                         printf(" released");
3762                 if (hus.hus_states & HS_EXISTS)
3763                         printf(" exists");
3764                 if (hus.hus_states & HS_DIRTY)
3765                         printf(" dirty");
3766                 if (hus.hus_states & HS_ARCHIVED)
3767                         printf(" archived");
3768                 /* Display user-settable flags */
3769                 if (hus.hus_states & HS_NORELEASE)
3770                         printf(" never_release");
3771                 if (hus.hus_states & HS_NOARCHIVE)
3772                         printf(" never_archive");
3773                 if (hus.hus_states & HS_LOST)
3774                         printf(" lost_from_hsm");
3775
3776                 if (hus.hus_archive_id != 0)
3777                         printf(", archive_id:%d", hus.hus_archive_id);
3778                 printf("\n");
3779
3780         } while (++i < argc);
3781
3782         return 0;
3783 }
3784
3785 #define LFS_HSM_SET   0
3786 #define LFS_HSM_CLEAR 1
3787
3788 /**
3789  * Generic function to set or clear HSM flags.
3790  * Used by hsm_set and hsm_clear.
3791  *
3792  * @mode  if LFS_HSM_SET, set the flags, if LFS_HSM_CLEAR, clear the flags.
3793  */
3794 static int lfs_hsm_change_flags(int argc, char **argv, int mode)
3795 {
3796         struct option long_opts[] = {
3797                 {"lost", 0, 0, 'l'},
3798                 {"norelease", 0, 0, 'r'},
3799                 {"noarchive", 0, 0, 'a'},
3800                 {"archived", 0, 0, 'A'},
3801                 {"dirty", 0, 0, 'd'},
3802                 {"exists", 0, 0, 'e'},
3803                 {0, 0, 0, 0}
3804         };
3805         char short_opts[] = "lraAde";
3806         __u64 mask = 0;
3807         int c, rc;
3808         char *path;
3809
3810         if (argc < 3)
3811                 return CMD_HELP;
3812
3813         while ((c = getopt_long(argc, argv, short_opts,
3814                                 long_opts, NULL)) != -1) {
3815                 switch (c) {
3816                 case 'l':
3817                         mask |= HS_LOST;
3818                         break;
3819                 case 'a':
3820                         mask |= HS_NOARCHIVE;
3821                         break;
3822                 case 'A':
3823                         mask |= HS_ARCHIVED;
3824                         break;
3825                 case 'r':
3826                         mask |= HS_NORELEASE;
3827                         break;
3828                 case 'd':
3829                         mask |= HS_DIRTY;
3830                         break;
3831                 case 'e':
3832                         mask |= HS_EXISTS;
3833                         break;
3834                 case '?':
3835                         return CMD_HELP;
3836                 default:
3837                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3838                                 argv[0], argv[optind - 1]);
3839                         return CMD_HELP;
3840                 }
3841         }
3842
3843         /* User should have specified a flag */
3844         if (mask == 0)
3845                 return CMD_HELP;
3846
3847         while (optind < argc) {
3848
3849                 path = argv[optind];
3850
3851                 /* If mode == 0, this means we apply the mask. */
3852                 if (mode == LFS_HSM_SET)
3853                         rc = llapi_hsm_state_set(path, mask, 0, 0);
3854                 else
3855                         rc = llapi_hsm_state_set(path, 0, mask, 0);
3856
3857                 if (rc != 0) {
3858                         fprintf(stderr, "Can't change hsm flags for %s: %s\n",
3859                                 path, strerror(errno = -rc));
3860                         return rc;
3861                 }
3862                 optind++;
3863         }
3864
3865         return 0;
3866 }
3867
3868 static int lfs_hsm_action(int argc, char **argv)
3869 {
3870         int                              rc;
3871         int                              i = 1;
3872         char                            *path;
3873         struct hsm_current_action        hca;
3874         struct hsm_extent                he;
3875         enum hsm_user_action             hua;
3876         enum hsm_progress_states         hps;
3877
3878         if (argc < 2)
3879                 return CMD_HELP;
3880
3881         do {
3882                 path = argv[i];
3883
3884                 rc = llapi_hsm_current_action(path, &hca);
3885                 if (rc) {
3886                         fprintf(stderr, "can't get hsm action for %s: %s\n",
3887                                 path, strerror(errno = -rc));
3888                         return rc;
3889                 }
3890                 he = hca.hca_location;
3891                 hua = hca.hca_action;
3892                 hps = hca.hca_state;
3893
3894                 printf("%s: %s", path, hsm_user_action2name(hua));
3895
3896                 /* Skip file without action */
3897                 if (hca.hca_action == HUA_NONE) {
3898                         printf("\n");
3899                         continue;
3900                 }
3901
3902                 printf(" %s ", hsm_progress_state2name(hps));
3903
3904                 if ((hps == HPS_RUNNING) &&
3905                     (hua == HUA_ARCHIVE || hua == HUA_RESTORE))
3906                         printf("(%llu bytes moved)\n",
3907                                (unsigned long long)he.length);
3908                 else if ((he.offset + he.length) == LUSTRE_EOF)
3909                         printf("(from %llu to EOF)\n",
3910                                (unsigned long long)he.offset);
3911                 else
3912                         printf("(from %llu to %llu)\n",
3913                                (unsigned long long)he.offset,
3914                                (unsigned long long)(he.offset + he.length));
3915
3916         } while (++i < argc);
3917
3918         return 0;
3919 }
3920
3921 static int lfs_hsm_set(int argc, char **argv)
3922 {
3923         return lfs_hsm_change_flags(argc, argv, LFS_HSM_SET);
3924 }
3925
3926 static int lfs_hsm_clear(int argc, char **argv)
3927 {
3928         return lfs_hsm_change_flags(argc, argv, LFS_HSM_CLEAR);
3929 }
3930
3931 /**
3932  * Check file state and return its fid, to be used by lfs_hsm_request().
3933  *
3934  * \param[in]     file      Path to file to check
3935  * \param[in,out] fid       Pointer to allocated lu_fid struct.
3936  * \param[in,out] last_dev  Pointer to last device id used.
3937  *
3938  * \return 0 on success.
3939  */
3940 static int lfs_hsm_prepare_file(const char *file, struct lu_fid *fid,
3941                                 dev_t *last_dev)
3942 {
3943         struct stat     st;
3944         int             rc;
3945
3946         rc = lstat(file, &st);
3947         if (rc) {
3948                 fprintf(stderr, "Cannot stat %s: %s\n", file, strerror(errno));
3949                 return -errno;
3950         }
3951         /* Checking for regular file as archiving as posix copytool
3952          * rejects archiving files other than regular files
3953          */
3954         if (!S_ISREG(st.st_mode)) {
3955                 fprintf(stderr, "error: \"%s\" is not a regular file\n", file);
3956                 return CMD_HELP;
3957         }
3958         /* A request should be ... */
3959         if (*last_dev != st.st_dev && *last_dev != 0) {
3960                 fprintf(stderr, "All files should be "
3961                         "on the same filesystem: %s\n", file);
3962                 return -EINVAL;
3963         }
3964         *last_dev = st.st_dev;
3965
3966         rc = llapi_path2fid(file, fid);
3967         if (rc) {
3968                 fprintf(stderr, "Cannot read FID of %s: %s\n",
3969                         file, strerror(-rc));
3970                 return rc;
3971         }
3972         return 0;
3973 }
3974
3975 /* Fill an HSM HUR item with a given file name.
3976  *
3977  * If mntpath is set, then the filename is actually a FID, and no
3978  * lookup on the filesystem will be performed.
3979  *
3980  * \param[in]  hur         the user request to fill
3981  * \param[in]  idx         index of the item inside the HUR to fill
3982  * \param[in]  mntpath     mountpoint of Lustre
3983  * \param[in]  fname       filename (if mtnpath is NULL)
3984  *                         or FID (if mntpath is set)
3985  * \param[in]  last_dev    pointer to last device id used
3986  *
3987  * \retval 0 on success
3988  * \retval CMD_HELP or a negative errno on error
3989  */
3990 static int fill_hur_item(struct hsm_user_request *hur, unsigned int idx,
3991                          const char *mntpath, const char *fname,
3992                          dev_t *last_dev)
3993 {
3994         struct hsm_user_item *hui = &hur->hur_user_item[idx];
3995         int rc;
3996
3997         hui->hui_extent.length = -1;
3998
3999         if (mntpath != NULL) {
4000                 if (*fname == '[')
4001                         fname++;
4002                 rc = sscanf(fname, SFID, RFID(&hui->hui_fid));
4003                 if (rc == 3) {
4004                         rc = 0;
4005                 } else {
4006                         fprintf(stderr, "hsm: '%s' is not a valid FID\n",
4007                                 fname);
4008                         rc = -EINVAL;
4009                 }
4010         } else {
4011                 rc = lfs_hsm_prepare_file(fname, &hui->hui_fid, last_dev);
4012         }
4013
4014         if (rc == 0)
4015                 hur->hur_request.hr_itemcount++;
4016
4017         return rc;
4018 }
4019
4020 static int lfs_hsm_request(int argc, char **argv, int action)
4021 {
4022         struct option            long_opts[] = {
4023                 {"filelist", 1, 0, 'l'},
4024                 {"data", 1, 0, 'D'},
4025                 {"archive", 1, 0, 'a'},
4026                 {"mntpath", 1, 0, 'm'},
4027                 {0, 0, 0, 0}
4028         };
4029         dev_t                    last_dev = 0;
4030         char                     short_opts[] = "l:D:a:m:";
4031         struct hsm_user_request *hur, *oldhur;
4032         int                      c, i;
4033         size_t                   len;
4034         int                      nbfile;
4035         char                    *line = NULL;
4036         char                    *filelist = NULL;
4037         char                     fullpath[PATH_MAX];
4038         char                    *opaque = NULL;
4039         int                      opaque_len = 0;
4040         int                      archive_id = 0;
4041         FILE                    *fp;
4042         int                      nbfile_alloc = 0;
4043         char                    *some_file = NULL;
4044         char                    *mntpath = NULL;
4045         int                      rc;
4046
4047         if (argc < 2)
4048                 return CMD_HELP;
4049
4050         while ((c = getopt_long(argc, argv, short_opts,
4051                                 long_opts, NULL)) != -1) {
4052                 switch (c) {
4053                 case 'l':
4054                         filelist = optarg;
4055                         break;
4056                 case 'D':
4057                         opaque = optarg;
4058                         break;
4059                 case 'a':
4060                         if (action != HUA_ARCHIVE &&
4061                             action != HUA_REMOVE) {
4062                                 fprintf(stderr,
4063                                         "error: -a is supported only "
4064                                         "when archiving or removing\n");
4065                                 return CMD_HELP;
4066                         }
4067                         archive_id = atoi(optarg);
4068                         break;
4069                 case 'm':
4070                         if (some_file == NULL) {
4071                                 mntpath = optarg;
4072                                 some_file = strdup(optarg);
4073                         }
4074                         break;
4075                 case '?':
4076                         return CMD_HELP;
4077                 default:
4078                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
4079                                 argv[0], argv[optind - 1]);
4080                         return CMD_HELP;
4081                 }
4082         }
4083
4084         /* All remaining args are files, so we have at least nbfile */
4085         nbfile = argc - optind;
4086
4087         if ((nbfile == 0) && (filelist == NULL))
4088                 return CMD_HELP;
4089
4090         if (opaque != NULL)
4091                 opaque_len = strlen(opaque);
4092
4093         /* Alloc the request structure with enough place to store all files
4094          * from command line. */
4095         hur = llapi_hsm_user_request_alloc(nbfile, opaque_len);
4096         if (hur == NULL) {
4097                 fprintf(stderr, "Cannot create the request: %s\n",
4098                         strerror(errno));
4099                 return errno;
4100         }
4101         nbfile_alloc = nbfile;
4102
4103         hur->hur_request.hr_action = action;
4104         hur->hur_request.hr_archive_id = archive_id;
4105         hur->hur_request.hr_flags = 0;
4106
4107         /* All remaining args are files, add them */
4108         if (nbfile != 0 && some_file == NULL)
4109                 some_file = strdup(argv[optind]);
4110
4111         for (i = 0; i < nbfile; i++) {
4112                 rc = fill_hur_item(hur, i, mntpath, argv[optind + i],
4113                                    &last_dev);
4114                 if (rc)
4115                         goto out_free;
4116         }
4117
4118         /* from here stop using nb_file, use hur->hur_request.hr_itemcount */
4119
4120         /* If a filelist was specified, read the filelist from it. */
4121         if (filelist != NULL) {
4122                 fp = fopen(filelist, "r");
4123                 if (fp == NULL) {
4124                         fprintf(stderr, "Cannot read the file list %s: %s\n",
4125                                 filelist, strerror(errno));
4126                         rc = -errno;
4127                         goto out_free;
4128                 }
4129
4130                 while ((rc = getline(&line, &len, fp)) != -1) {
4131                         /* If allocated buffer was too small, get something
4132                          * larger */
4133                         if (nbfile_alloc <= hur->hur_request.hr_itemcount) {
4134                                 ssize_t size;
4135
4136                                 nbfile_alloc = nbfile_alloc * 2 + 1;
4137                                 oldhur = hur;
4138                                 hur = llapi_hsm_user_request_alloc(nbfile_alloc,
4139                                                                    opaque_len);
4140                                 if (hur == NULL) {
4141                                         fprintf(stderr, "hsm: cannot allocate "
4142                                                 "the request: %s\n",
4143                                                 strerror(errno));
4144                                         hur = oldhur;
4145                                         rc = -errno;
4146                                         fclose(fp);
4147                                         goto out_free;
4148                                 }
4149                                 size = hur_len(oldhur);
4150                                 if (size < 0) {
4151                                         fprintf(stderr, "hsm: cannot allocate "
4152                                                 "%u files + %u bytes data\n",
4153                                             oldhur->hur_request.hr_itemcount,
4154                                             oldhur->hur_request.hr_data_len);
4155                                         free(hur);
4156                                         hur = oldhur;
4157                                         rc = -E2BIG;
4158                                         fclose(fp);
4159                                         goto out_free;
4160                                 }
4161                                 memcpy(hur, oldhur, size);
4162                                 free(oldhur);
4163                         }
4164
4165                         /* Chop CR */
4166                         if (line[strlen(line) - 1] == '\n')
4167                                 line[strlen(line) - 1] = '\0';
4168
4169                         rc = fill_hur_item(hur, hur->hur_request.hr_itemcount,
4170                                            mntpath, line, &last_dev);
4171                         if (rc) {
4172                                 fclose(fp);
4173                                 goto out_free;
4174                         }
4175
4176                         if (some_file == NULL) {
4177                                 some_file = line;
4178                                 line = NULL;
4179                         }
4180                 }
4181
4182                 rc = fclose(fp);
4183                 free(line);
4184         }
4185
4186         /* If a --data was used, add it to the request */
4187         hur->hur_request.hr_data_len = opaque_len;
4188         if (opaque != NULL)
4189                 memcpy(hur_data(hur), opaque, opaque_len);
4190
4191         /* Send the HSM request */
4192         if (realpath(some_file, fullpath) == NULL) {
4193                 fprintf(stderr, "Could not find path '%s': %s\n",
4194                         some_file, strerror(errno));
4195         }
4196         rc = llapi_hsm_request(fullpath, hur);
4197         if (rc) {
4198                 fprintf(stderr, "Cannot send HSM request (use of %s): %s\n",
4199                         some_file, strerror(-rc));
4200                 goto out_free;
4201         }
4202
4203 out_free:
4204         free(some_file);
4205         free(hur);
4206         return rc;
4207 }
4208
4209 static int lfs_hsm_archive(int argc, char **argv)
4210 {
4211         return lfs_hsm_request(argc, argv, HUA_ARCHIVE);
4212 }
4213
4214 static int lfs_hsm_restore(int argc, char **argv)
4215 {
4216         return lfs_hsm_request(argc, argv, HUA_RESTORE);
4217 }
4218
4219 static int lfs_hsm_release(int argc, char **argv)
4220 {
4221         return lfs_hsm_request(argc, argv, HUA_RELEASE);
4222 }
4223
4224 static int lfs_hsm_remove(int argc, char **argv)
4225 {
4226         return lfs_hsm_request(argc, argv, HUA_REMOVE);
4227 }
4228
4229 static int lfs_hsm_cancel(int argc, char **argv)
4230 {
4231         return lfs_hsm_request(argc, argv, HUA_CANCEL);
4232 }
4233
4234 static int lfs_swap_layouts(int argc, char **argv)
4235 {
4236         if (argc != 3)
4237                 return CMD_HELP;
4238
4239         return llapi_swap_layouts(argv[1], argv[2], 0, 0,
4240                                   SWAP_LAYOUTS_KEEP_MTIME |
4241                                   SWAP_LAYOUTS_KEEP_ATIME);
4242 }
4243
4244 static const char *const ladvise_names[] = LU_LADVISE_NAMES;
4245
4246 static enum lu_ladvise_type lfs_get_ladvice(const char *string)
4247 {
4248         enum lu_ladvise_type advice;
4249
4250         for (advice = 0;
4251              advice < ARRAY_SIZE(ladvise_names); advice++) {
4252                 if (ladvise_names[advice] == NULL)
4253                         continue;
4254                 if (strcmp(string, ladvise_names[advice]) == 0)
4255                         return advice;
4256         }
4257
4258         return LU_LADVISE_INVALID;
4259 }
4260
4261 static int lfs_ladvise(int argc, char **argv)
4262 {
4263         struct option            long_opts[] = {
4264                 {"advice",      required_argument,      0, 'a'},
4265                 {"background",  no_argument,            0, 'b'},
4266                 {"end",         required_argument,      0, 'e'},
4267                 {"start",       required_argument,      0, 's'},
4268                 {"length",      required_argument,      0, 'l'},
4269                 {0, 0, 0, 0}
4270         };
4271         char                     short_opts[] = "a:be:l:s:";
4272         int                      c;
4273         int                      rc = 0;
4274         const char              *path;
4275         int                      fd;
4276         struct llapi_lu_ladvise  advice;
4277         enum lu_ladvise_type     advice_type = LU_LADVISE_INVALID;
4278         unsigned long long       start = 0;
4279         unsigned long long       end = LUSTRE_EOF;
4280         unsigned long long       length = 0;
4281         unsigned long long       size_units;
4282         unsigned long long       flags = 0;
4283
4284         optind = 0;
4285         while ((c = getopt_long(argc, argv, short_opts,
4286                                 long_opts, NULL)) != -1) {
4287                 switch (c) {
4288                 case 'a':
4289                         advice_type = lfs_get_ladvice(optarg);
4290                         if (advice_type == LU_LADVISE_INVALID) {
4291                                 fprintf(stderr, "%s: invalid advice type "
4292                                         "'%s'\n", argv[0], optarg);
4293                                 fprintf(stderr, "Valid types:");
4294
4295                                 for (advice_type = 0;
4296                                      advice_type < ARRAY_SIZE(ladvise_names);
4297                                      advice_type++) {
4298                                         if (ladvise_names[advice_type] == NULL)
4299                                                 continue;
4300                                         fprintf(stderr, " %s",
4301                                                 ladvise_names[advice_type]);
4302                                 }
4303                                 fprintf(stderr, "\n");
4304
4305                                 return CMD_HELP;
4306                         }
4307                         break;
4308                 case 'b':
4309                         flags |= LF_ASYNC;
4310                         break;
4311                 case 'e':
4312                         size_units = 1;
4313                         rc = llapi_parse_size(optarg, &end,
4314                                               &size_units, 0);
4315                         if (rc) {
4316                                 fprintf(stderr, "%s: bad end offset '%s'\n",
4317                                         argv[0], optarg);
4318                                 return CMD_HELP;
4319                         }
4320                         break;
4321                 case 's':
4322                         size_units = 1;
4323                         rc = llapi_parse_size(optarg, &start,
4324                                               &size_units, 0);
4325                         if (rc) {
4326                                 fprintf(stderr, "%s: bad start offset "
4327                                         "'%s'\n", argv[0], optarg);
4328                                 return CMD_HELP;
4329                         }
4330                         break;
4331                 case 'l':
4332                         size_units = 1;
4333                         rc = llapi_parse_size(optarg, &length,
4334                                               &size_units, 0);
4335                         if (rc) {
4336                                 fprintf(stderr, "%s: bad length '%s'\n",
4337                                         argv[0], optarg);
4338                                 return CMD_HELP;
4339                         }
4340                         break;
4341                 case '?':
4342                         return CMD_HELP;
4343                 default:
4344                         fprintf(stderr, "%s: option '%s' unrecognized\n",
4345                                 argv[0], argv[optind - 1]);
4346                         return CMD_HELP;
4347                 }
4348         }
4349
4350         if (advice_type == LU_LADVISE_INVALID) {
4351                 fprintf(stderr, "%s: please give an advice type\n", argv[0]);
4352                 fprintf(stderr, "Valid types:");
4353                 for (advice_type = 0; advice_type < ARRAY_SIZE(ladvise_names);
4354                      advice_type++) {
4355                         if (ladvise_names[advice_type] == NULL)
4356                                 continue;
4357                         fprintf(stderr, " %s", ladvise_names[advice_type]);
4358                 }
4359                 fprintf(stderr, "\n");
4360                 return CMD_HELP;
4361         }
4362
4363         if (argc <= optind) {
4364                 fprintf(stderr, "%s: please give one or more file names\n",
4365                         argv[0]);
4366                 return CMD_HELP;
4367         }
4368
4369         if (end != LUSTRE_EOF && length != 0 && end != start + length) {
4370                 fprintf(stderr, "%s: conflicting arguments of -l and -e\n",
4371                         argv[0]);
4372                 return CMD_HELP;
4373         }
4374
4375         if (end == LUSTRE_EOF && length != 0)
4376                 end = start + length;
4377
4378         if (end <= start) {
4379                 fprintf(stderr, "%s: range [%llu, %llu] is invalid\n",
4380                         argv[0], start, end);
4381                 return CMD_HELP;
4382         }
4383
4384         while (optind < argc) {
4385                 int rc2;
4386
4387                 path = argv[optind++];
4388
4389                 fd = open(path, O_RDONLY);
4390                 if (fd < 0) {
4391                         fprintf(stderr, "%s: cannot open file '%s': %s\n",
4392                                 argv[0], path, strerror(errno));
4393                         rc2 = -errno;
4394                         goto next;
4395                 }
4396
4397                 advice.lla_start = start;
4398                 advice.lla_end = end;
4399                 advice.lla_advice = advice_type;
4400                 advice.lla_value1 = 0;
4401                 advice.lla_value2 = 0;
4402                 advice.lla_value3 = 0;
4403                 advice.lla_value4 = 0;
4404                 rc2 = llapi_ladvise(fd, flags, 1, &advice);
4405                 close(fd);
4406                 if (rc2 < 0) {
4407                         fprintf(stderr, "%s: cannot give advice '%s' to file "
4408                                 "'%s': %s\n", argv[0],
4409                                 ladvise_names[advice_type],
4410                                 path, strerror(errno));
4411                 }
4412 next:
4413                 if (rc == 0 && rc2 < 0)
4414                         rc = rc2;
4415         }
4416         return rc;
4417 }
4418
4419 int main(int argc, char **argv)
4420 {
4421         int rc;
4422
4423         /* Ensure that liblustreapi constructor has run */
4424         if (!liblustreapi_initialized)
4425                 fprintf(stderr, "liblustreapi was not properly initialized\n");
4426
4427         setlinebuf(stdout);
4428
4429         Parser_init("lfs > ", cmdlist);
4430
4431         progname = argv[0]; /* Used in error messages */
4432         if (argc > 1) {
4433                 rc = Parser_execarg(argc - 1, argv + 1, cmdlist);
4434         } else {
4435                 rc = Parser_commands();
4436         }
4437
4438         return rc < 0 ? -rc : rc;
4439 }
4440
4441 #ifdef _LUSTRE_IDL_H_
4442 /* Everything we need here should be included by lustreapi.h. */
4443 # error "lfs should not depend on lustre_idl.h"
4444 #endif /* _LUSTRE_IDL_H_ */