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