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