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