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