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