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