Whamcloud - gitweb
LU-6052 utils: change "lfs mv" to "lfs migrate"
[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;