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