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