4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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
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
27 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
28 * Use is subject to license terms.
30 * Copyright (c) 2011, 2015, Intel Corporation.
33 * This file is part of Lustre, http://www.lustre.org/
34 * Lustre is a trademark of Sun Microsystems, Inc.
38 * Author: Peter J. Braam <braam@clusterfs.com>
39 * Author: Phil Schwan <phil@clusterfs.com>
40 * Author: Robert Read <rread@clusterfs.com>
58 #include <sys/ioctl.h>
59 #include <sys/quota.h>
61 #include <sys/types.h>
67 #ifdef HAVE_SYS_QUOTA_H
68 # include <sys/quota.h>
71 #include <libcfs/util/string.h>
72 #include <libcfs/util/ioctl.h>
73 #include <libcfs/util/parser.h>
74 #include <lustre/lustreapi.h>
75 #include <lustre_ver.h>
76 #include <lustre_param.h>
79 static int lfs_setstripe(int argc, char **argv);
80 static int lfs_find(int argc, char **argv);
81 static int lfs_getstripe(int argc, char **argv);
82 static int lfs_getdirstripe(int argc, char **argv);
83 static int lfs_setdirstripe(int argc, char **argv);
84 static int lfs_rmentry(int argc, char **argv);
85 static int lfs_osts(int argc, char **argv);
86 static int lfs_mdts(int argc, char **argv);
87 static int lfs_df(int argc, char **argv);
88 static int lfs_getname(int argc, char **argv);
89 static int lfs_check(int argc, char **argv);
90 #ifdef HAVE_SYS_QUOTA_H
91 static int lfs_setquota(int argc, char **argv);
92 static int lfs_quota(int argc, char **argv);
94 static int lfs_flushctx(int argc, char **argv);
95 static int lfs_join(int argc, char **argv);
96 static int lfs_lsetfacl(int argc, char **argv);
97 static int lfs_lgetfacl(int argc, char **argv);
98 static int lfs_rsetfacl(int argc, char **argv);
99 static int lfs_rgetfacl(int argc, char **argv);
100 static int lfs_cp(int argc, char **argv);
101 static int lfs_ls(int argc, char **argv);
102 static int lfs_poollist(int argc, char **argv);
103 static int lfs_changelog(int argc, char **argv);
104 static int lfs_changelog_clear(int argc, char **argv);
105 static int lfs_fid2path(int argc, char **argv);
106 static int lfs_path2fid(int argc, char **argv);
107 static int lfs_data_version(int argc, char **argv);
108 static int lfs_hsm_state(int argc, char **argv);
109 static int lfs_hsm_set(int argc, char **argv);
110 static int lfs_hsm_clear(int argc, char **argv);
111 static int lfs_hsm_action(int argc, char **argv);
112 static int lfs_hsm_archive(int argc, char **argv);
113 static int lfs_hsm_restore(int argc, char **argv);
114 static int lfs_hsm_release(int argc, char **argv);
115 static int lfs_hsm_remove(int argc, char **argv);
116 static int lfs_hsm_cancel(int argc, char **argv);
117 static int lfs_swap_layouts(int argc, char **argv);
118 static int lfs_mv(int argc, char **argv);
120 /* Setstripe and migrate share mostly the same parameters */
121 #define SSM_CMD_COMMON(cmd) \
122 "usage: "cmd" [--stripe-count|-c <stripe_count>]\n" \
123 " [--stripe-index|-i <start_ost_idx>]\n" \
124 " [--stripe-size|-S <stripe_size>]\n" \
125 " [--pool|-p <pool_name>]\n" \
126 " [--ost-list|-o <ost_indices>]\n"
128 #define SSM_HELP_COMMON \
129 "\tstripe_size: Number of bytes on each OST (0 filesystem default)\n" \
130 "\t Can be specified with k, m or g (in KB, MB and GB\n" \
131 "\t respectively)\n" \
132 "\tstart_ost_idx: OST index of first stripe (-1 default)\n" \
133 "\tstripe_count: Number of OSTs to stripe over (0 default, -1 all)\n" \
134 "\tpool_name: Name of OST pool to use (default none)\n" \
135 "\tost_indices: List of OST indices, can be repeated multiple times\n"\
136 "\t Indices be specified in a format of:\n" \
137 "\t -o <ost_1>,<ost_i>-<ost_j>,<ost_n>\n" \
139 "\t -o <ost_1> -o <ost_i>-<ost_j> -o <ost_n>\n" \
140 "\t If --pool is set with --ost-list, then the OSTs\n" \
141 "\t must be the members of the pool."
143 #define SETSTRIPE_USAGE \
144 SSM_CMD_COMMON("setstripe") \
145 " <directory|filename>\n" \
148 #define MIGRATE_USAGE \
149 SSM_CMD_COMMON("migrate ") \
151 " [--non-block|-n]\n" \
155 "\tblock: Block file access during data migration (default)\n" \
156 "\tnon-block: Abort migrations if concurrent access is detected\n" \
158 static const char *progname;
159 static bool file_lease_supported = true;
161 /* all available commands */
162 command_t cmdlist[] = {
163 {"setstripe", lfs_setstripe, 0,
164 "Create a new file with a specific striping pattern or\n"
165 "set the default striping pattern on an existing directory or\n"
166 "delete the default striping pattern from an existing directory\n"
167 "usage: setstripe -d <directory> (to delete default striping)\n"\
170 {"getstripe", lfs_getstripe, 0,
171 "To list the striping info for a given file or files in a\n"
172 "directory or recursively for all files in a directory tree.\n"
173 "usage: getstripe [--ost|-O <uuid>] [--quiet | -q] [--verbose | -v]\n"
174 " [--stripe-count|-c] [--stripe-index|-i]\n"
175 " [--pool|-p] [--stripe-size|-S] [--directory|-d]\n"
176 " [--mdt-index|-M] [--recursive|-r] [--raw|-R]\n"
178 " <directory|filename> ..."},
179 {"setdirstripe", lfs_setdirstripe, 0,
180 "To create a striped directory on a specified MDT. This can only\n"
181 "be done on MDT0 with the right of administrator.\n"
182 "usage: setdirstripe <--count|-c stripe_count>\n"
183 " [--index|-i mdt_index] [--hash-type|-t hash_type]\n"
184 " [--default_stripe|-D ] [--mode|-m mode] <dir>\n"
185 "\tstripe_count: stripe count of the striped directory\n"
186 "\tmdt_index: MDT index of first stripe\n"
187 "\thash_type: hash type of the striped directory. Hash types:\n"
188 " fnv_1a_64 FNV-1a hash algorithm (default)\n"
189 " all_char sum of characters % MDT_COUNT (not recommended)\n"
190 "\tdefault_stripe: set default dirstripe of the directory\n"
191 "\tmode: the mode of the directory\n"},
192 {"getdirstripe", lfs_getdirstripe, 0,
193 "To list the striping info for a given directory\n"
194 "or recursively for all directories in a directory tree.\n"
195 "usage: getdirstripe [--obd|-O <uuid>] [--quiet|-q] [--verbose|-v]\n"
196 " [--count|-c ] [--index|-i ] [--raw|-R]\n"
197 " [--recursive | -r] [ --default_stripe | -D ] <dir> "},
198 {"mkdir", lfs_setdirstripe, 0,
199 "To create a striped directory on a specified MDT. This can only\n"
200 "be done on MDT0 with the right of administrator.\n"
201 "usage: mkdir <--count|-c stripe_count>\n"
202 " [--index|-i mdt_index] [--hash-type|-t hash_type]\n"
203 " [--default_stripe|-D ] [--mode|-m mode] <dir>\n"
204 "\tstripe_count: stripe count of the striped directory\n"
205 "\tmdt_index: MDT index of first stripe\n"
206 "\thash_type: hash type of the striped directory. Hash types:\n"
207 " fnv_1a_64 FNV-1a hash algorithm (default)\n"
208 " all_char sum of characters % MDT_COUNT (not recommended)\n"
209 "\tdefault_stripe: set default dirstripe of the directory\n"
210 "\tmode: the mode of the directory\n"},
211 {"rm_entry", lfs_rmentry, 0,
212 "To remove the name entry of the remote directory. Note: This\n"
213 "command will only delete the name entry, i.e. the remote directory\n"
214 "will become inaccessable after this command. This can only be done\n"
215 "by the administrator\n"
216 "usage: rm_entry <dir>\n"},
217 {"pool_list", lfs_poollist, 0,
218 "List pools or pool OSTs\n"
219 "usage: pool_list <fsname>[.<pool>] | <pathname>\n"},
220 {"find", lfs_find, 0,
221 "find files matching given attributes recursively in directory tree.\n"
222 "usage: find <directory|filename> ...\n"
223 " [[!] --atime|-A [+-]N] [[!] --ctime|-C [+-]N]\n"
224 " [[!] --mtime|-M [+-]N] [[!] --mdt|-m <uuid|index,...>]\n"
225 " [--maxdepth|-D N] [[!] --name|-n <pattern>]\n"
226 " [[!] --ost|-O <uuid|index,...>] [--print|-p] [--print0|-P]\n"
227 " [[!] --size|-s [+-]N[bkMGTPE]]\n"
228 " [[!] --stripe-count|-c [+-]<stripes>]\n"
229 " [[!] --stripe-index|-i <index,...>]\n"
230 " [[!] --stripe-size|-S [+-]N[kMGT]] [[!] --type|-t <filetype>]\n"
231 " [[!] --gid|-g|--group|-G <gid>|<gname>]\n"
232 " [[!] --uid|-u|--user|-U <uid>|<uname>] [[!] --pool <pool>]\n"
233 " [[!] --layout|-L released,raid0]\n"
234 "\t !: used before an option indicates 'NOT' requested attribute\n"
235 "\t -: used before a value indicates 'AT MOST' requested value\n"
236 "\t +: used before a value indicates 'AT LEAST' requested value\n"},
237 {"check", lfs_check, 0,
238 "Display the status of MDS or OSTs (as specified in the command)\n"
239 "or all the servers (MDS and OSTs).\n"
240 "usage: check <osts|mds|servers>"},
241 {"join", lfs_join, 0,
242 "join two lustre files into one.\n"
243 "obsolete, HEAD does not support it anymore.\n"},
244 {"osts", lfs_osts, 0, "list OSTs connected to client "
245 "[for specified path only]\n" "usage: osts [path]"},
246 {"mdts", lfs_mdts, 0, "list MDTs connected to client "
247 "[for specified path only]\n" "usage: mdts [path]"},
249 "report filesystem disk space usage or inodes usage"
250 "of each MDS and all OSDs or a batch belonging to a specific pool .\n"
251 "Usage: df [-i] [-h] [--lazy|-l] [--pool|-p <fsname>[.<pool>] [path]"},
252 {"getname", lfs_getname, 0, "list instances and specified mount points "
253 "[for specified path only]\n"
254 "Usage: getname [-h]|[path ...] "},
255 #ifdef HAVE_SYS_QUOTA_H
256 {"setquota", lfs_setquota, 0, "Set filesystem quotas.\n"
257 "usage: setquota <-u|-g> <uname>|<uid>|<gname>|<gid>\n"
258 " -b <block-softlimit> -B <block-hardlimit>\n"
259 " -i <inode-softlimit> -I <inode-hardlimit> <filesystem>\n"
260 " setquota <-u|--user|-g|--group> <uname>|<uid>|<gname>|<gid>\n"
261 " [--block-softlimit <block-softlimit>]\n"
262 " [--block-hardlimit <block-hardlimit>]\n"
263 " [--inode-softlimit <inode-softlimit>]\n"
264 " [--inode-hardlimit <inode-hardlimit>] <filesystem>\n"
265 " setquota [-t] <-u|--user|-g|--group>\n"
266 " [--block-grace <block-grace>]\n"
267 " [--inode-grace <inode-grace>] <filesystem>\n"
268 " -b can be used instead of --block-softlimit/--block-grace\n"
269 " -B can be used instead of --block-hardlimit\n"
270 " -i can be used instead of --inode-softlimit/--inode-grace\n"
271 " -I can be used instead of --inode-hardlimit\n\n"
272 "Note: The total quota space will be split into many qunits and\n"
273 " balanced over all server targets, the minimal qunit size is\n"
274 " 1M bytes for block space and 1K inodes for inode space.\n\n"
275 " Quota space rebalancing process will stop when this mininum\n"
276 " value is reached. As a result, quota exceeded can be returned\n"
277 " while many targets still have 1MB or 1K inodes of spare\n"
279 {"quota", lfs_quota, 0, "Display disk usage and limits.\n"
280 "usage: quota [-q] [-v] [-h] [-o <obd_uuid>|-i <mdt_idx>|-I "
282 " [<-u|-g> <uname>|<uid>|<gname>|<gid>] <filesystem>\n"
283 " quota [-o <obd_uuid>|-i <mdt_idx>|-I <ost_idx>] -t <-u|-g> <filesystem>"},
285 {"flushctx", lfs_flushctx, 0, "Flush security context for current user.\n"
286 "usage: flushctx [-k] [mountpoint...]"},
287 {"lsetfacl", lfs_lsetfacl, 0,
288 "Remote user setfacl for user/group on the same remote client.\n"
289 "usage: lsetfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ..."},
290 {"lgetfacl", lfs_lgetfacl, 0,
291 "Remote user getfacl for user/group on the same remote client.\n"
292 "usage: lgetfacl [-dRLPvh] file ..."},
293 {"rsetfacl", lfs_rsetfacl, 0,
294 "Remote user setfacl for user/group on other clients.\n"
295 "usage: rsetfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ..."},
296 {"rgetfacl", lfs_rgetfacl, 0,
297 "Remote user getfacl for user/group on other clients.\n"
298 "usage: rgetfacl [-dRLPvh] file ..."},
300 "Remote user copy files and directories.\n"
301 "usage: cp [OPTION]... [-T] SOURCE DEST\n\tcp [OPTION]... SOURCE... DIRECTORY\n\tcp [OPTION]... -t DIRECTORY SOURCE..."},
303 "Remote user list directory contents.\n"
304 "usage: ls [OPTION]... [FILE]..."},
305 {"changelog", lfs_changelog, 0,
306 "Show the metadata changes on an MDT."
307 "\nusage: changelog <mdtname> [startrec [endrec]]"},
308 {"changelog_clear", lfs_changelog_clear, 0,
309 "Indicate that old changelog records up to <endrec> are no longer of "
310 "interest to consumer <id>, allowing the system to free up space.\n"
311 "An <endrec> of 0 means all records.\n"
312 "usage: changelog_clear <mdtname> <id> <endrec>"},
313 {"fid2path", lfs_fid2path, 0,
314 "Resolve the full path(s) for given FID(s). For a specific hardlink "
315 "specify link number <linkno>.\n"
316 /* "For a historical link name, specify changelog record <recno>.\n" */
317 "usage: fid2path [--link <linkno>] <fsname|rootpath> <fid> ..."
318 /* [ --rec <recno> ] */ },
319 {"path2fid", lfs_path2fid, 0, "Display the fid(s) for a given path(s).\n"
320 "usage: path2fid [--parents] <path> ..."},
321 {"data_version", lfs_data_version, 0, "Display file data version for "
322 "a given path.\n" "usage: data_version -[n|r|w] <path>"},
323 {"hsm_state", lfs_hsm_state, 0, "Display the HSM information (states, "
324 "undergoing actions) for given files.\n usage: hsm_state <file> ..."},
325 {"hsm_set", lfs_hsm_set, 0, "Set HSM user flag on specified files.\n"
326 "usage: hsm_set [--norelease] [--noarchive] [--dirty] [--exists] "
327 "[--archived] [--lost] <file> ..."},
328 {"hsm_clear", lfs_hsm_clear, 0, "Clear HSM user flag on specified "
330 "usage: hsm_clear [--norelease] [--noarchive] [--dirty] [--exists] "
331 "[--archived] [--lost] <file> ..."},
332 {"hsm_action", lfs_hsm_action, 0, "Display current HSM request for "
333 "given files.\n" "usage: hsm_action <file> ..."},
334 {"hsm_archive", lfs_hsm_archive, 0,
335 "Archive file to external storage.\n"
336 "usage: hsm_archive [--filelist FILELIST] [--data DATA] [--archive NUM] "
338 {"hsm_restore", lfs_hsm_restore, 0,
339 "Restore file from external storage.\n"
340 "usage: hsm_restore [--filelist FILELIST] [--data DATA] <file> ..."},
341 {"hsm_release", lfs_hsm_release, 0,
342 "Release files from Lustre.\n"
343 "usage: hsm_release [--filelist FILELIST] [--data DATA] <file> ..."},
344 {"hsm_remove", lfs_hsm_remove, 0,
345 "Remove file copy from external storage.\n"
346 "usage: hsm_remove [--filelist FILELIST] [--data DATA]\n"
347 " [--mntpath MOUNTPATH] [--archive NUM] <file|FID> ...\n"
349 "Note: To remove files from the archive that have been deleted on\n"
350 "Lustre, set mntpath and optionally archive. In that case, all the\n"
351 "positional arguments and entries in the file list must be FIDs."
353 {"hsm_cancel", lfs_hsm_cancel, 0,
354 "Cancel requests related to specified files.\n"
355 "usage: hsm_cancel [--filelist FILELIST] [--data DATA] <file> ..."},
356 {"swap_layouts", lfs_swap_layouts, 0, "Swap layouts between 2 files.\n"
357 "usage: swap_layouts <path1> <path2>"},
358 {"migrate", lfs_setstripe, 0,
359 "migrate a directory between MDTs.\n"
360 "usage: migrate --mdt-index <mdt_idx> [--verbose|-v] "
362 "\tmdt_idx: index of the destination MDT\n"},
363 {"migrate", lfs_setstripe, 0,
364 "migrate file objects from one OST "
365 "layout\nto another (may be not safe with concurent writes).\n"
367 "[--stripe-count|-c] <stripe_count>\n"
368 "[--stripe-index|-i] <start_ost_index>\n"
369 "[--stripe-size|-S] <stripe_size>\n"
370 "[--pool|-p] <pool_name>\n"
371 "[--ost-list|-o] <ost_indices>\n"
375 "\tstripe_count: number of OSTs to stripe a file over\n"
376 "\tstripe_ost_index: index of the first OST to stripe a file over\n"
377 "\tstripe_size: number of bytes to store before moving to the next OST\n"
378 "\tpool_name: name of the predefined pool of OSTs\n"
379 "\tost_indices: OSTs to stripe over, in order\n"
380 "\tblock: wait for the operation to return before continuing\n"
381 "\tnon-block: do not wait for the operation to return.\n"},
383 "To move directories between MDTs. This command is deprecated, "
384 "use \"migrate\" instead.\n"
385 "usage: mv <directory|filename> [--mdt-index|-M] <mdt_index> "
387 {"help", Parser_help, 0, "help"},
388 {"exit", Parser_quit, 0, "quit"},
389 {"quit", Parser_quit, 0, "quit"},
390 {"--version", Parser_version, 0,
391 "output build version of the utility and exit"},
396 #define MIGRATION_NONBLOCK 1
399 * Internal helper for migrate_copy_data(). Check lease and report error if
402 * \param[in] fd File descriptor on which to check the lease.
403 * \param[out] lease_broken Set to true if the lease was broken.
404 * \param[in] group_locked Whether a group lock was taken or not.
405 * \param[in] path Name of the file being processed, for error
408 * \retval 0 Migration can keep on going.
409 * \retval -errno Error occurred, abort migration.
411 static int check_lease(int fd, bool *lease_broken, bool group_locked,
416 if (!file_lease_supported)
419 rc = llapi_lease_check(fd);
421 return 0; /* llapi_check_lease returns > 0 on success. */
424 fprintf(stderr, "%s: cannot migrate '%s': file busy\n",
426 rc = rc ? rc : -EAGAIN;
428 fprintf(stderr, "%s: external attempt to access file '%s' "
429 "blocked until migration ends.\n", progname, path);
432 *lease_broken = true;
436 static int migrate_copy_data(int fd_src, int fd_dst, size_t buf_size,
437 bool group_locked, const char *fname)
446 bool lease_broken = false;
448 /* Use a page-aligned buffer for direct I/O */
449 rc = posix_memalign(&buf, getpagesize(), buf_size);
454 /* read new data only if we have written all
455 * previously read data */
458 rc = check_lease(fd_src, &lease_broken,
459 group_locked, fname);
463 rsize = read(fd_src, buf, buf_size);
466 fprintf(stderr, "%s: %s: read failed: %s\n",
467 progname, fname, strerror(-rc));
477 wsize = write(fd_dst, buf + bufoff, rpos - wpos);
481 "%s: %s: write failed on volatile: %s\n",
482 progname, fname, strerror(-rc));
492 fprintf(stderr, "%s: %s: fsync failed: %s\n",
493 progname, fname, strerror(-rc));
501 static int migrate_copy_timestamps(int fdv, const struct stat *st)
503 struct timeval tv[2] = {
504 {.tv_sec = st->st_atime},
505 {.tv_sec = st->st_mtime}
508 return futimes(fdv, tv);
511 static int migrate_block(int fd, int fdv, const struct stat *st,
512 size_t buf_size, const char *name)
519 rc = llapi_get_data_version(fd, &dv1, LL_DV_RD_FLUSH);
521 fprintf(stderr, "%s: %s: cannot get dataversion: %s\n",
522 progname, name, strerror(-rc));
530 /* The grouplock blocks all concurrent accesses to the file.
531 * It has to be taken after llapi_get_data_version as it would
533 rc = llapi_group_lock(fd, gid);
535 fprintf(stderr, "%s: %s: cannot get group lock: %s\n",
536 progname, name, strerror(-rc));
540 rc = migrate_copy_data(fd, fdv, buf_size, true, name);
542 fprintf(stderr, "%s: %s: data copy failed\n", progname, name);
546 /* Make sure we keep original atime/mtime values */
547 rc = migrate_copy_timestamps(fdv, st);
549 fprintf(stderr, "%s: %s: timestamp copy failed\n",
555 * for a migration we need to check data version on file did
558 * Pass in gid=0 since we already own grouplock. */
559 rc = llapi_fswap_layouts_grouplock(fd, fdv, dv1, 0, 0,
560 SWAP_LAYOUTS_CHECK_DV1);
562 fprintf(stderr, "%s: %s: dataversion changed during copy, "
563 "migration aborted\n", progname, name);
566 fprintf(stderr, "%s: %s: cannot swap layouts: %s\n", progname,
567 name, strerror(-rc));
572 rc2 = llapi_group_unlock(fd, gid);
573 if (rc2 < 0 && rc == 0) {
574 fprintf(stderr, "%s: %s: putting group lock failed: %s\n",
575 progname, name, strerror(-rc2));
582 static int migrate_nonblock(int fd, int fdv, const struct stat *st,
583 size_t buf_size, const char *name)
589 rc = llapi_get_data_version(fd, &dv1, LL_DV_RD_FLUSH);
591 fprintf(stderr, "%s: %s: cannot get data version: %s\n",
592 progname, name, strerror(-rc));
596 rc = migrate_copy_data(fd, fdv, buf_size, false, name);
598 fprintf(stderr, "%s: %s: data copy failed\n", progname, name);
602 rc = llapi_get_data_version(fd, &dv2, LL_DV_RD_FLUSH);
604 fprintf(stderr, "%s: %s: cannot get data version: %s\n",
605 progname, name, strerror(-rc));
611 fprintf(stderr, "%s: %s: data version changed during "
617 /* Make sure we keep original atime/mtime values */
618 rc = migrate_copy_timestamps(fdv, st);
620 fprintf(stderr, "%s: %s: timestamp copy failed\n",
625 /* Atomically put lease, swap layouts and close.
626 * for a migration we need to check data version on file did
628 rc = llapi_fswap_layouts(fd, fdv, 0, 0, SWAP_LAYOUTS_CLOSE);
630 fprintf(stderr, "%s: %s: cannot swap layouts: %s\n",
631 progname, name, strerror(-rc));
638 static int lfs_migrate(char *name, __u64 migration_flags,
639 struct llapi_stripe_param *param)
643 char parent[PATH_MAX];
646 char volatile_file[sizeof(parent) +
647 LUSTRE_VOLATILE_HDR_LEN +
648 2 * sizeof(mdt_index) +
649 2 * sizeof(random_value) + 4];
652 struct lov_user_md *lum = NULL;
655 bool have_lease_rdlck = false;
659 /* find the right size for the IO and allocate the buffer */
660 lum_size = lov_user_md_size(LOV_MAX_STRIPE_COUNT, LOV_USER_MAGIC_V3);
661 lum = malloc(lum_size);
667 rc = llapi_file_get_stripe(name, lum);
668 /* failure can happen for many reasons and some may be not real errors
670 * in case of a real error, a later call will fail with better
671 * error management */
673 buf_size = 1024 * 1024;
675 buf_size = lum->lmm_stripe_size;
677 /* open file, direct io */
678 /* even if the file is only read, WR mode is nedeed to allow
679 * layout swap on fd */
680 fd = open(name, O_RDWR | O_DIRECT);
683 fprintf(stderr, "%s: %s: cannot open: %s\n", progname, name,
688 if (file_lease_supported) {
689 rc = llapi_lease_get(fd, LL_LEASE_RDLCK);
690 if (rc == -EOPNOTSUPP) {
691 /* Older servers do not support file lease.
692 * Disable related checks. This opens race conditions
693 * as explained in LU-4840 */
694 file_lease_supported = false;
696 fprintf(stderr, "%s: %s: cannot get open lease: %s\n",
697 progname, name, strerror(-rc));
700 have_lease_rdlck = true;
704 /* search for file directory pathname */
705 if (strlen(name) > sizeof(parent)-1) {
709 strncpy(parent, name, sizeof(parent));
710 ptr = strrchr(parent, '/');
712 if (getcwd(parent, sizeof(parent)) == NULL) {
723 rc = llapi_file_fget_mdtidx(fd, &mdt_index);
725 fprintf(stderr, "%s: %s: cannot get MDT index: %s\n",
726 progname, name, strerror(-rc));
731 random_value = random();
732 rc = snprintf(volatile_file, sizeof(volatile_file),
733 "%s/%s:%.4X:%.4X", parent, LUSTRE_VOLATILE_HDR,
734 mdt_index, random_value);
735 if (rc >= sizeof(volatile_file)) {
740 /* create, open a volatile file, use caching (ie no directio) */
741 fdv = llapi_file_open_param(volatile_file,
742 O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW,
743 S_IRUSR | S_IWUSR, param);
744 } while (fdv == -EEXIST);
748 fprintf(stderr, "%s: %s: cannot create volatile file in"
750 progname, parent, strerror(-rc));
754 /* Not-owner (root?) special case.
755 * Need to set owner/group of volatile file like original.
756 * This will allow to pass related check during layout_swap.
761 fprintf(stderr, "%s: %s: cannot stat: %s\n", progname, name,
765 rc = fstat(fdv, &stv);
768 fprintf(stderr, "%s: %s: cannot stat: %s\n", progname,
769 volatile_file, strerror(errno));
772 if (st.st_uid != stv.st_uid || st.st_gid != stv.st_gid) {
773 rc = fchown(fdv, st.st_uid, st.st_gid);
776 fprintf(stderr, "%s: %s: cannot chown: %s\n", progname,
777 name, strerror(errno));
782 if (migration_flags & MIGRATION_NONBLOCK && file_lease_supported) {
783 rc = migrate_nonblock(fd, fdv, &st, buf_size, name);
785 have_lease_rdlck = false;
786 fdv = -1; /* The volatile file is closed as we put the
787 * lease in non-blocking mode. */
790 /* Blocking mode (forced if servers do not support file lease).
791 * It is also the default mode, since we cannot distinguish
792 * between a broken lease and a server that does not support
793 * atomic swap/close (LU-6785) */
794 rc = migrate_block(fd, fdv, &st, buf_size, name);
798 if (have_lease_rdlck)
815 * Parse a string containing an OST index list into an array of integers.
817 * The input string contains a comma delimited list of individual
818 * indices and ranges, for example "1,2-4,7". Add the indices into the
819 * \a osts array and remove duplicates.
821 * \param[out] osts array to store indices in
822 * \param[in] size size of \a osts array
823 * \param[in] offset starting index in \a osts
824 * \param[in] arg string containing OST index list
826 * \retval positive number of indices in \a osts
827 * \retval -EINVAL unable to parse \a arg
829 static int parse_targets(__u32 *osts, int size, int offset, char *arg)
833 int slots = size - offset;
841 while (!end_of_loop) {
849 ptr = strchrnul(arg, ',');
851 end_of_loop = *ptr == '\0';
854 start_index = strtol(arg, &endptr, 0);
855 if (endptr == arg) /* no data at all */
857 if (*endptr != '-' && *endptr != '\0') /* has invalid data */
862 end_index = start_index;
863 if (*endptr == '-') {
864 end_index = strtol(endptr + 1, &endptr, 0);
867 if (end_index < start_index)
871 for (i = start_index; i <= end_index && slots > 0; i++) {
874 /* remove duplicate */
875 for (j = 0; j < offset; j++) {
879 if (j == offset) { /* no duplicate */
884 if (slots == 0 && i < end_index)
892 if (!end_of_loop && ptr != NULL)
895 return rc < 0 ? rc : nr;
899 static int lfs_setstripe(int argc, char **argv)
901 struct llapi_stripe_param *param = NULL;
902 struct find_param migrate_mdt_param = {
909 unsigned long long st_size;
910 int st_offset, st_count;
914 char *stripe_size_arg = NULL;
915 char *stripe_off_arg = NULL;
916 char *stripe_count_arg = NULL;
917 char *pool_name_arg = NULL;
918 char *mdt_idx_arg = NULL;
919 unsigned long long size_units = 1;
920 bool migrate_mode = false;
921 bool migration_block = false;
922 __u64 migration_flags = 0;
923 __u32 osts[LOV_MAX_STRIPE_COUNT] = { 0 };
926 struct option long_opts[] = {
927 /* --block is only valid in migrate mode */
928 {"block", no_argument, 0, 'b'},
929 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
930 /* This formerly implied "stripe-count", but was explicitly
931 * made "stripe-count" for consistency with other options,
932 * and to separate it from "mdt-count" when DNE arrives. */
933 {"count", required_argument, 0, 'c'},
935 {"stripe-count", required_argument, 0, 'c'},
936 {"stripe_count", required_argument, 0, 'c'},
937 {"delete", no_argument, 0, 'd'},
938 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
939 /* This formerly implied "stripe-index", but was explicitly
940 * made "stripe-index" for consistency with other options,
941 * and to separate it from "mdt-index" when DNE arrives. */
942 {"index", required_argument, 0, 'i'},
944 {"stripe-index", required_argument, 0, 'i'},
945 {"stripe_index", required_argument, 0, 'i'},
946 {"mdt-index", required_argument, 0, 'm'},
947 {"mdt_index", required_argument, 0, 'm'},
948 /* --non-block is only valid in migrate mode */
949 {"non-block", no_argument, 0, 'n'},
950 {"ost-list", required_argument, 0, 'o'},
951 {"ost_list", required_argument, 0, 'o'},
952 {"pool", required_argument, 0, 'p'},
953 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
954 /* This formerly implied "--stripe-size", but was confusing
955 * with "lfs find --size|-s", which means "file size", so use
956 * the consistent "--stripe-size|-S" for all commands. */
957 {"size", required_argument, 0, 's'},
959 {"stripe-size", required_argument, 0, 'S'},
960 {"stripe_size", required_argument, 0, 'S'},
961 /* --verbose is only valid in migrate mode */
962 {"verbose", no_argument, 0, 'v'},
970 if (strcmp(argv[0], "migrate") == 0)
973 while ((c = getopt_long(argc, argv, "bc:di:m:no:p:s:S:v",
974 long_opts, NULL)) >= 0) {
981 fprintf(stderr, "--block is valid only for"
985 migration_block = true;
988 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
989 if (strcmp(argv[optind - 1], "--count") == 0)
990 fprintf(stderr, "warning: '--count' deprecated"
991 ", use '--stripe-count' instead\n");
993 stripe_count_arg = optarg;
996 /* delete the default striping pattern */
1000 nr_osts = parse_targets(osts,
1001 sizeof(osts) / sizeof(__u32),
1005 "error: %s: bad OST indices '%s'\n",
1010 if (st_offset == -1) /* first in the command line */
1011 st_offset = osts[0];
1014 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1015 if (strcmp(argv[optind - 1], "--index") == 0)
1016 fprintf(stderr, "warning: '--index' deprecated"
1017 ", use '--stripe-index' instead\n");
1019 stripe_off_arg = optarg;
1022 if (!migrate_mode) {
1023 fprintf(stderr, "--mdt-index is valid only for"
1027 mdt_idx_arg = optarg;
1030 if (!migrate_mode) {
1031 fprintf(stderr, "--non-block is valid only for"
1035 migration_flags |= MIGRATION_NONBLOCK;
1037 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1039 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1040 fprintf(stderr, "warning: '--size|-s' deprecated, "
1041 "use '--stripe-size|-S' instead\n");
1043 #endif /* LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0) */
1045 stripe_size_arg = optarg;
1048 pool_name_arg = optarg;
1051 if (!migrate_mode) {
1052 fprintf(stderr, "--verbose is valid only for"
1056 migrate_mdt_param.fp_verbose = VERBOSE_DETAIL;
1063 fname = argv[optind];
1066 (stripe_size_arg != NULL || stripe_off_arg != NULL ||
1067 stripe_count_arg != NULL || pool_name_arg != NULL)) {
1068 fprintf(stderr, "error: %s: cannot specify -d with "
1069 "-s, -c, -o, or -p options\n",
1074 if (optind == argc) {
1075 fprintf(stderr, "error: %s: missing filename|dirname\n",
1080 if (mdt_idx_arg != NULL && optind > 3) {
1081 fprintf(stderr, "error: %s: cannot specify -m with other "
1082 "options\n", argv[0]);
1086 if ((migration_flags & MIGRATION_NONBLOCK) && migration_block) {
1088 "error: %s: cannot specify --non-block and --block\n",
1093 if (pool_name_arg != NULL) {
1097 ptr = strchr(pool_name_arg, '.');
1099 ptr = pool_name_arg;
1101 if ((ptr - pool_name_arg) == 0) {
1102 fprintf(stderr, "error: %s: fsname is empty "
1103 "in pool name '%s'\n",
1104 argv[0], pool_name_arg);
1111 rc = lustre_is_poolname_valid(ptr, 1, LOV_MAXPOOLNAME);
1113 fprintf(stderr, "error: %s: poolname '%s' is "
1115 argv[0], pool_name_arg);
1117 } else if (rc == -2) {
1118 fprintf(stderr, "error: %s: pool name '%s' is too long "
1119 "(max is %d characters)\n",
1120 argv[0], pool_name_arg, LOV_MAXPOOLNAME);
1122 } else if (rc > 0) {
1123 fprintf(stderr, "error: %s: char '%c' not allowed in "
1125 argv[0], rc, pool_name_arg);
1130 /* get the stripe size */
1131 if (stripe_size_arg != NULL) {
1132 result = llapi_parse_size(stripe_size_arg, &st_size,
1135 fprintf(stderr, "error: %s: bad stripe size '%s'\n",
1136 argv[0], stripe_size_arg);
1140 /* get the stripe offset */
1141 if (stripe_off_arg != NULL) {
1142 st_offset = strtol(stripe_off_arg, &end, 0);
1144 fprintf(stderr, "error: %s: bad stripe offset '%s'\n",
1145 argv[0], stripe_off_arg);
1149 /* get the stripe count */
1150 if (stripe_count_arg != NULL) {
1151 st_count = strtoul(stripe_count_arg, &end, 0);
1153 fprintf(stderr, "error: %s: bad stripe count '%s'\n",
1154 argv[0], stripe_count_arg);
1159 if (mdt_idx_arg != NULL) {
1160 /* initialize migrate mdt parameters */
1161 migrate_mdt_param.fp_mdt_index = strtoul(mdt_idx_arg, &end, 0);
1163 fprintf(stderr, "error: %s: bad MDT index '%s'\n",
1164 argv[0], mdt_idx_arg);
1167 migrate_mdt_param.fp_migrate = 1;
1169 /* initialize stripe parameters */
1170 param = calloc(1, offsetof(typeof(*param), lsp_osts[nr_osts]));
1171 if (param == NULL) {
1172 fprintf(stderr, "error: %s: run out of memory\n",
1177 param->lsp_stripe_size = st_size;
1178 param->lsp_stripe_offset = st_offset;
1179 param->lsp_stripe_count = st_count;
1180 param->lsp_stripe_pattern = 0;
1181 param->lsp_pool = pool_name_arg;
1182 param->lsp_is_specific = false;
1184 if (st_count > 0 && nr_osts != st_count) {
1185 fprintf(stderr, "error: %s: stripe count '%d' "
1186 "doesn't match the number of OSTs: %d\n"
1187 , argv[0], st_count, nr_osts);
1192 param->lsp_is_specific = true;
1193 param->lsp_stripe_count = nr_osts;
1194 memcpy(param->lsp_osts, osts, sizeof(*osts) * nr_osts);
1198 for (fname = argv[optind]; fname != NULL; fname = argv[++optind]) {
1199 if (!migrate_mode) {
1200 result = llapi_file_open_param(fname,
1207 } else if (mdt_idx_arg != NULL) {
1208 result = llapi_migrate_mdt(fname, &migrate_mdt_param);
1210 result = lfs_migrate(fname, migration_flags, param);
1213 /* Save the first error encountered. */
1217 "error: %s: %s file '%s' failed\n",
1218 argv[0], migrate_mode ? "migrate" : "create",
1228 static int lfs_poollist(int argc, char **argv)
1233 return llapi_poollist(argv[1]);
1236 static int set_time(time_t *time, time_t *set, char *str)
1243 else if (str[0] == '-')
1249 t = strtol(str, NULL, 0);
1250 if (*time < t * 24 * 60 * 60) {
1253 fprintf(stderr, "Wrong time '%s' is specified.\n", str);
1257 *set = *time - t * 24 * 60 * 60;
1264 static int name2id(unsigned int *id, char *name, int type)
1267 struct passwd *entry;
1269 if (!(entry = getpwnam(name))) {
1275 *id = entry->pw_uid;
1277 struct group *entry;
1279 if (!(entry = getgrnam(name))) {
1285 *id = entry->gr_gid;
1291 static int id2name(char **name, unsigned int id, int type)
1294 struct passwd *entry;
1296 if (!(entry = getpwuid(id))) {
1302 *name = entry->pw_name;
1304 struct group *entry;
1306 if (!(entry = getgrgid(id))) {
1312 *name = entry->gr_name;
1318 static int name2layout(__u32 *layout, char *name)
1323 for (ptr = name; ; ptr = NULL) {
1324 lyt = strtok(ptr, ",");
1327 if (strcmp(lyt, "released") == 0)
1328 *layout |= LOV_PATTERN_F_RELEASED;
1329 else if (strcmp(lyt, "raid0") == 0)
1330 *layout |= LOV_PATTERN_RAID0;
1337 #define FIND_POOL_OPT 3
1338 static int lfs_find(int argc, char **argv)
1343 struct find_param param = {
1347 struct option long_opts[] = {
1348 {"atime", required_argument, 0, 'A'},
1349 {"stripe-count", required_argument, 0, 'c'},
1350 {"stripe_count", required_argument, 0, 'c'},
1351 {"ctime", required_argument, 0, 'C'},
1352 {"maxdepth", required_argument, 0, 'D'},
1353 {"gid", required_argument, 0, 'g'},
1354 {"group", required_argument, 0, 'G'},
1355 {"stripe-index", required_argument, 0, 'i'},
1356 {"stripe_index", required_argument, 0, 'i'},
1357 {"layout", required_argument, 0, 'L'},
1358 {"mdt", required_argument, 0, 'm'},
1359 {"mtime", required_argument, 0, 'M'},
1360 {"name", required_argument, 0, 'n'},
1361 /* reserve {"or", no_argument, , 0, 'o'}, to match find(1) */
1362 {"obd", required_argument, 0, 'O'},
1363 {"ost", required_argument, 0, 'O'},
1364 /* no short option for pool, p/P already used */
1365 {"pool", required_argument, 0, FIND_POOL_OPT},
1366 {"print0", no_argument, 0, 'p'},
1367 {"print", no_argument, 0, 'P'},
1368 {"size", required_argument, 0, 's'},
1369 {"stripe-size", required_argument, 0, 'S'},
1370 {"stripe_size", required_argument, 0, 'S'},
1371 {"type", required_argument, 0, 't'},
1372 {"uid", required_argument, 0, 'u'},
1373 {"user", required_argument, 0, 'U'},
1386 /* when getopt_long_only() hits '!' it returns 1, puts "!" in optarg */
1387 while ((c = getopt_long_only(argc, argv,
1388 "-A:c:C:D:g:G:i:L:m:M:n:O:Ppqrs:S:t:u:U:v",
1389 long_opts, NULL)) >= 0) {
1394 /* '!' is part of option */
1395 /* when getopt_long_only() finds a string which is not
1396 * an option nor a known option argument it returns 1
1397 * in that case if we already have found pathstart and pathend
1398 * (i.e. we have the list of pathnames),
1399 * the only supported value is "!"
1401 isoption = (c != 1) || (strcmp(optarg, "!") == 0);
1402 if (!isoption && pathend != -1) {
1403 fprintf(stderr, "err: %s: filename|dirname must either "
1404 "precede options or follow options\n",
1409 if (!isoption && pathstart == -1)
1410 pathstart = optind - 1;
1411 if (isoption && pathstart != -1 && pathend == -1)
1412 pathend = optind - 2;
1418 /* unknown; opt is "!" or path component,
1419 * checking done above.
1421 if (strcmp(optarg, "!") == 0)
1425 xtime = ¶m.fp_atime;
1426 xsign = ¶m.fp_asign;
1427 param.fp_exclude_atime = !!neg_opt;
1428 /* no break, this falls through to 'C' for ctime */
1431 xtime = ¶m.fp_ctime;
1432 xsign = ¶m.fp_csign;
1433 param.fp_exclude_ctime = !!neg_opt;
1435 /* no break, this falls through to 'M' for mtime */
1438 xtime = ¶m.fp_mtime;
1439 xsign = ¶m.fp_msign;
1440 param.fp_exclude_mtime = !!neg_opt;
1442 rc = set_time(&t, xtime, optarg);
1443 if (rc == INT_MAX) {
1451 if (optarg[0] == '+') {
1452 param.fp_stripe_count_sign = -1;
1454 } else if (optarg[0] == '-') {
1455 param.fp_stripe_count_sign = 1;
1459 param.fp_stripe_count = strtoul(optarg, &endptr, 0);
1460 if (*endptr != '\0') {
1461 fprintf(stderr,"error: bad stripe_count '%s'\n",
1466 param.fp_check_stripe_count = 1;
1467 param.fp_exclude_stripe_count = !!neg_opt;
1470 param.fp_max_depth = strtol(optarg, 0, 0);
1474 rc = name2id(¶m.fp_gid, optarg, GROUP);
1476 param.fp_gid = strtoul(optarg, &endptr, 10);
1477 if (*endptr != '\0') {
1478 fprintf(stderr, "Group/GID: %s cannot "
1479 "be found.\n", optarg);
1484 param.fp_exclude_gid = !!neg_opt;
1485 param.fp_check_gid = 1;
1488 ret = name2layout(¶m.fp_layout, optarg);
1491 param.fp_exclude_layout = !!neg_opt;
1492 param.fp_check_layout = 1;
1496 rc = name2id(¶m.fp_uid, optarg, USER);
1498 param.fp_uid = strtoul(optarg, &endptr, 10);
1499 if (*endptr != '\0') {
1500 fprintf(stderr, "User/UID: %s cannot "
1501 "be found.\n", optarg);
1506 param.fp_exclude_uid = !!neg_opt;
1507 param.fp_check_uid = 1;
1510 if (strlen(optarg) > LOV_MAXPOOLNAME) {
1512 "Pool name %s is too long"
1513 " (max is %d)\n", optarg,
1518 /* we do check for empty pool because empty pool
1519 * is used to find V1 lov attributes */
1520 strncpy(param.fp_poolname, optarg, LOV_MAXPOOLNAME);
1521 param.fp_poolname[LOV_MAXPOOLNAME] = '\0';
1522 param.fp_exclude_pool = !!neg_opt;
1523 param.fp_check_pool = 1;
1526 param.fp_pattern = (char *)optarg;
1527 param.fp_exclude_pattern = !!neg_opt;
1532 char *buf, *token, *next, *p;
1536 buf = strdup(optarg);
1542 param.fp_exclude_obd = !!neg_opt;
1545 while (token && *token) {
1546 token = strchr(token, ',');
1553 param.fp_exclude_mdt = !!neg_opt;
1554 param.fp_num_alloc_mdts += len;
1555 tmp = realloc(param.fp_mdt_uuid,
1556 param.fp_num_alloc_mdts *
1557 sizeof(*param.fp_mdt_uuid));
1563 param.fp_mdt_uuid = tmp;
1565 param.fp_exclude_obd = !!neg_opt;
1566 param.fp_num_alloc_obds += len;
1567 tmp = realloc(param.fp_obd_uuid,
1568 param.fp_num_alloc_obds *
1569 sizeof(*param.fp_obd_uuid));
1575 param.fp_obd_uuid = tmp;
1577 for (token = buf; token && *token; token = next) {
1578 struct obd_uuid *puuid;
1581 ¶m.fp_mdt_uuid[param.fp_num_mdts++];
1584 ¶m.fp_obd_uuid[param.fp_num_obds++];
1586 p = strchr(token, ',');
1593 if (strlen(token) > sizeof(puuid->uuid) - 1) {
1598 strncpy(puuid->uuid, token,
1599 sizeof(puuid->uuid));
1607 param.fp_zero_end = 1;
1612 if (optarg[0] == '+') {
1613 param.fp_size_sign = -1;
1615 } else if (optarg[0] == '-') {
1616 param.fp_size_sign = 1;
1620 ret = llapi_parse_size(optarg, ¶m.fp_size,
1621 ¶m.fp_size_units, 0);
1623 fprintf(stderr, "error: bad file size '%s'\n",
1627 param.fp_check_size = 1;
1628 param.fp_exclude_size = !!neg_opt;
1631 if (optarg[0] == '+') {
1632 param.fp_stripe_size_sign = -1;
1634 } else if (optarg[0] == '-') {
1635 param.fp_stripe_size_sign = 1;
1639 ret = llapi_parse_size(optarg, ¶m.fp_stripe_size,
1640 ¶m.fp_stripe_size_units, 0);
1642 fprintf(stderr, "error: bad stripe_size '%s'\n",
1646 param.fp_check_stripe_size = 1;
1647 param.fp_exclude_stripe_size = !!neg_opt;
1650 param.fp_exclude_type = !!neg_opt;
1651 switch (optarg[0]) {
1653 param.fp_type = S_IFBLK;
1656 param.fp_type = S_IFCHR;
1659 param.fp_type = S_IFDIR;
1662 param.fp_type = S_IFREG;
1665 param.fp_type = S_IFLNK;
1668 param.fp_type = S_IFIFO;
1671 param.fp_type = S_IFSOCK;
1674 fprintf(stderr, "error: %s: bad type '%s'\n",
1686 if (pathstart == -1) {
1687 fprintf(stderr, "error: %s: no filename|pathname\n",
1691 } else if (pathend == -1) {
1697 rc = llapi_find(argv[pathstart], ¶m);
1698 if (rc != 0 && ret == 0)
1700 } while (++pathstart < pathend);
1703 fprintf(stderr, "error: %s failed for %s.\n",
1704 argv[0], argv[optind - 1]);
1706 if (param.fp_obd_uuid && param.fp_num_alloc_obds)
1707 free(param.fp_obd_uuid);
1709 if (param.fp_mdt_uuid && param.fp_num_alloc_mdts)
1710 free(param.fp_mdt_uuid);
1715 static int lfs_getstripe_internal(int argc, char **argv,
1716 struct find_param *param)
1718 struct option long_opts[] = {
1719 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1720 /* This formerly implied "stripe-count", but was explicitly
1721 * made "stripe-count" for consistency with other options,
1722 * and to separate it from "mdt-count" when DNE arrives. */
1723 {"count", no_argument, 0, 'c'},
1725 {"stripe-count", no_argument, 0, 'c'},
1726 {"stripe_count", no_argument, 0, 'c'},
1727 {"directory", no_argument, 0, 'd'},
1728 {"default", no_argument, 0, 'D'},
1729 {"generation", no_argument, 0, 'g'},
1730 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1731 /* This formerly implied "stripe-index", but was explicitly
1732 * made "stripe-index" for consistency with other options,
1733 * and to separate it from "mdt-index" when DNE arrives. */
1734 {"index", no_argument, 0, 'i'},
1736 {"stripe-index", no_argument, 0, 'i'},
1737 {"stripe_index", no_argument, 0, 'i'},
1738 {"layout", no_argument, 0, 'L'},
1739 {"mdt-index", no_argument, 0, 'M'},
1740 {"mdt_index", no_argument, 0, 'M'},
1741 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1742 /* This formerly implied "stripe-index", but was confusing
1743 * with "file offset" (which will eventually be needed for
1744 * with different layouts by offset), so deprecate it. */
1745 {"offset", no_argument, 0, 'o'},
1747 {"obd", required_argument, 0, 'O'},
1748 {"ost", required_argument, 0, 'O'},
1749 {"pool", no_argument, 0, 'p'},
1750 {"quiet", no_argument, 0, 'q'},
1751 {"recursive", no_argument, 0, 'r'},
1752 {"raw", no_argument, 0, 'R'},
1753 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1754 /* This formerly implied "--stripe-size", but was confusing
1755 * with "lfs find --size|-s", which means "file size", so use
1756 * the consistent "--stripe-size|-S" for all commands. */
1757 {"size", no_argument, 0, 's'},
1759 {"stripe-size", no_argument, 0, 'S'},
1760 {"stripe_size", no_argument, 0, 'S'},
1761 {"verbose", no_argument, 0, 'v'},
1766 param->fp_max_depth = 1;
1767 while ((c = getopt_long(argc, argv, "cdDghiLMoO:pqrRsSv",
1768 long_opts, NULL)) != -1) {
1771 if (param->fp_obd_uuid) {
1773 "error: %s: only one obduuid allowed",
1777 param->fp_obd_uuid = (struct obd_uuid *)optarg;
1783 param->fp_max_depth = 0;
1786 param->fp_get_default_lmv = 1;
1789 param->fp_recursive = 1;
1792 param->fp_verbose = VERBOSE_ALL | VERBOSE_DETAIL;
1795 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1796 if (strcmp(argv[optind - 1], "--count") == 0)
1797 fprintf(stderr, "warning: '--count' deprecated,"
1798 " use '--stripe-count' instead\n");
1800 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1801 param->fp_verbose |= VERBOSE_COUNT;
1802 param->fp_max_depth = 0;
1805 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1807 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1808 fprintf(stderr, "warning: '--size|-s' deprecated, "
1809 "use '--stripe-size|-S' instead\n");
1811 #endif /* LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0) */
1813 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1814 param->fp_verbose |= VERBOSE_SIZE;
1815 param->fp_max_depth = 0;
1818 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1820 fprintf(stderr, "warning: '--offset|-o' deprecated, "
1821 "use '--stripe-index|-i' instead\n");
1824 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1825 if (strcmp(argv[optind - 1], "--index") == 0)
1826 fprintf(stderr, "warning: '--index' deprecated"
1827 ", use '--stripe-index' instead\n");
1829 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1830 param->fp_verbose |= VERBOSE_OFFSET;
1831 param->fp_max_depth = 0;
1835 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1836 param->fp_verbose |= VERBOSE_POOL;
1837 param->fp_max_depth = 0;
1841 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1842 param->fp_verbose |= VERBOSE_GENERATION;
1843 param->fp_max_depth = 0;
1847 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1848 param->fp_verbose |= VERBOSE_LAYOUT;
1849 param->fp_max_depth = 0;
1853 if (!(param->fp_verbose & VERBOSE_DETAIL))
1854 param->fp_max_depth = 0;
1855 param->fp_verbose |= VERBOSE_MDTINDEX;
1868 if (param->fp_recursive)
1869 param->fp_max_depth = -1;
1871 if (!param->fp_verbose)
1872 param->fp_verbose = VERBOSE_ALL;
1873 if (param->fp_quiet)
1874 param->fp_verbose = VERBOSE_OBJID;
1877 rc = llapi_getstripe(argv[optind], param);
1878 } while (++optind < argc && !rc);
1881 fprintf(stderr, "error: %s failed for %s.\n",
1882 argv[0], argv[optind - 1]);
1886 static int lfs_tgts(int argc, char **argv)
1888 char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
1889 struct find_param param;
1890 int index = 0, rc=0;
1895 if (argc == 2 && !realpath(argv[1], path)) {
1897 fprintf(stderr, "error: invalid path '%s': %s\n",
1898 argv[1], strerror(-rc));
1902 while (!llapi_search_mounts(path, index++, mntdir, NULL)) {
1903 /* Check if we have a mount point */
1904 if (mntdir[0] == '\0')
1907 memset(¶m, 0, sizeof(param));
1908 if (!strcmp(argv[0], "mdts"))
1909 param.fp_get_lmv = 1;
1911 rc = llapi_ostlist(mntdir, ¶m);
1913 fprintf(stderr, "error: %s: failed on %s\n",
1916 if (path[0] != '\0')
1918 memset(mntdir, 0, PATH_MAX);
1924 static int lfs_getstripe(int argc, char **argv)
1926 struct find_param param = { 0 };
1927 return lfs_getstripe_internal(argc, argv, ¶m);
1931 static int lfs_getdirstripe(int argc, char **argv)
1933 struct find_param param = { 0 };
1935 param.fp_get_lmv = 1;
1936 return lfs_getstripe_internal(argc, argv, ¶m);
1940 static int lfs_setdirstripe(int argc, char **argv)
1944 unsigned int stripe_offset = -1;
1945 unsigned int stripe_count = 1;
1946 enum lmv_hash_type hash_type;
1949 char *stripe_offset_opt = NULL;
1950 char *stripe_count_opt = NULL;
1951 char *stripe_hash_opt = NULL;
1952 char *mode_opt = NULL;
1953 bool default_stripe = false;
1954 mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
1955 mode_t previous_mode = 0;
1956 bool delete = false;
1958 struct option long_opts[] = {
1959 {"count", required_argument, 0, 'c'},
1960 {"delete", no_argument, 0, 'd'},
1961 {"index", required_argument, 0, 'i'},
1962 {"mode", required_argument, 0, 'm'},
1963 {"hash-type", required_argument, 0, 't'},
1964 {"default_stripe", no_argument, 0, 'D'},
1968 while ((c = getopt_long(argc, argv, "c:dDi:m:t:", long_opts,
1975 stripe_count_opt = optarg;
1979 default_stripe = true;
1982 default_stripe = true;
1985 stripe_offset_opt = optarg;
1991 stripe_hash_opt = optarg;
1994 fprintf(stderr, "error: %s: option '%s' "
1996 argv[0], argv[optind - 1]);
2001 if (optind == argc) {
2002 fprintf(stderr, "error: %s: missing dirname\n",
2007 if (!delete && stripe_offset_opt == NULL && stripe_count_opt == NULL) {
2008 fprintf(stderr, "error: %s: missing stripe offset and count.\n",
2013 if (stripe_offset_opt != NULL) {
2014 /* get the stripe offset */
2015 stripe_offset = strtoul(stripe_offset_opt, &end, 0);
2017 fprintf(stderr, "error: %s: bad stripe offset '%s'\n",
2018 argv[0], stripe_offset_opt);
2024 if (stripe_offset_opt != NULL || stripe_count_opt != NULL) {
2025 fprintf(stderr, "error: %s: cannot specify -d with -s,"
2026 " or -i options.\n", argv[0]);
2034 if (mode_opt != NULL) {
2035 mode = strtoul(mode_opt, &end, 8);
2037 fprintf(stderr, "error: %s: bad mode '%s'\n",
2041 previous_mode = umask(0);
2044 if (stripe_hash_opt == NULL ||
2045 strcmp(stripe_hash_opt, LMV_HASH_NAME_FNV_1A_64) == 0) {
2046 hash_type = LMV_HASH_TYPE_FNV_1A_64;
2047 } else if (strcmp(stripe_hash_opt, LMV_HASH_NAME_ALL_CHARS) == 0) {
2048 hash_type = LMV_HASH_TYPE_ALL_CHARS;
2050 fprintf(stderr, "error: %s: bad stripe hash type '%s'\n",
2051 argv[0], stripe_hash_opt);
2055 /* get the stripe count */
2056 if (stripe_count_opt != NULL) {
2057 stripe_count = strtoul(stripe_count_opt, &end, 0);
2059 fprintf(stderr, "error: %s: bad stripe count '%s'\n",
2060 argv[0], stripe_count_opt);
2065 dname = argv[optind];
2067 if (default_stripe) {
2068 result = llapi_dir_set_default_lmv_stripe(dname,
2069 stripe_offset, stripe_count,
2072 result = llapi_dir_create_pool(dname, mode,
2074 stripe_count, hash_type,
2079 fprintf(stderr, "error: %s: create stripe dir '%s' "
2080 "failed\n", argv[0], dname);
2083 dname = argv[++optind];
2084 } while (dname != NULL);
2086 if (mode_opt != NULL)
2087 umask(previous_mode);
2093 static int lfs_rmentry(int argc, char **argv)
2100 fprintf(stderr, "error: %s: missing dirname\n",
2106 dname = argv[index];
2107 while (dname != NULL) {
2108 result = llapi_direntry_remove(dname);
2110 fprintf(stderr, "error: %s: remove dir entry '%s' "
2111 "failed\n", argv[0], dname);
2114 dname = argv[++index];
2119 static int lfs_mv(int argc, char **argv)
2121 struct find_param param = {
2128 struct option long_opts[] = {
2129 {"mdt-index", required_argument, 0, 'M'},
2130 {"verbose", no_argument, 0, 'v'},
2134 while ((c = getopt_long(argc, argv, "M:v", long_opts, NULL)) != -1) {
2137 param.fp_mdt_index = strtoul(optarg, &end, 0);
2139 fprintf(stderr, "%s: invalid MDT index'%s'\n",
2146 param.fp_verbose = VERBOSE_DETAIL;
2150 fprintf(stderr, "error: %s: unrecognized option '%s'\n",
2151 argv[0], argv[optind - 1]);
2156 if (param.fp_mdt_index == -1) {
2157 fprintf(stderr, "%s: MDT index must be specified\n", argv[0]);
2161 if (optind >= argc) {
2162 fprintf(stderr, "%s: missing operand path\n", argv[0]);
2166 param.fp_migrate = 1;
2167 rc = llapi_migrate_mdt(argv[optind], ¶m);
2169 fprintf(stderr, "%s: cannot migrate '%s' to MDT%04x: %s\n",
2170 argv[0], argv[optind], param.fp_mdt_index,
2175 static int lfs_osts(int argc, char **argv)
2177 return lfs_tgts(argc, argv);
2180 static int lfs_mdts(int argc, char **argv)
2182 return lfs_tgts(argc, argv);
2185 #define COOK(value) \
2188 while (value > 1024) { \
2196 #define CDF "%11llu"
2197 #define HDF "%8.1f%c"
2201 static int showdf(char *mntdir, struct obd_statfs *stat,
2202 char *uuid, int ishow, int cooked,
2203 char *type, int index, int rc)
2205 long long avail, used, total;
2207 char *suffix = "KMGTPEZY";
2208 /* Note if we have >2^64 bytes/fs these buffers will need to be grown */
2209 char tbuf[3 * sizeof(__u64)];
2210 char ubuf[3 * sizeof(__u64)];
2211 char abuf[3 * sizeof(__u64)];
2212 char rbuf[3 * sizeof(__u64)];
2220 avail = stat->os_ffree;
2221 used = stat->os_files - stat->os_ffree;
2222 total = stat->os_files;
2224 int shift = cooked ? 0 : 10;
2226 avail = (stat->os_bavail * stat->os_bsize) >> shift;
2227 used = ((stat->os_blocks - stat->os_bfree) *
2228 stat->os_bsize) >> shift;
2229 total = (stat->os_blocks * stat->os_bsize) >> shift;
2232 if ((used + avail) > 0)
2233 ratio = (double)used / (double)(used + avail);
2239 cook_val = (double)total;
2242 sprintf(tbuf, HDF, cook_val, suffix[i - 1]);
2244 sprintf(tbuf, CDF, total);
2246 cook_val = (double)used;
2249 sprintf(ubuf, HDF, cook_val, suffix[i - 1]);
2251 sprintf(ubuf, CDF, used);
2253 cook_val = (double)avail;
2256 sprintf(abuf, HDF, cook_val, suffix[i - 1]);
2258 sprintf(abuf, CDF, avail);
2260 sprintf(tbuf, CDF, total);
2261 sprintf(ubuf, CDF, used);
2262 sprintf(abuf, CDF, avail);
2265 sprintf(rbuf, RDF, (int)(ratio * 100 + 0.5));
2266 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s",
2267 uuid, tbuf, ubuf, abuf, rbuf, mntdir);
2269 printf("[%s:%d]\n", type, index);
2275 printf(UUF": inactive device\n", uuid);
2278 printf(UUF": %s\n", uuid, strerror(-rc));
2285 struct ll_stat_type {
2290 static int mntdf(char *mntdir, char *fsname, char *pool, int ishow,
2291 int cooked, int lazy)
2293 struct obd_statfs stat_buf, sum = { .os_bsize = 1 };
2294 struct obd_uuid uuid_buf;
2295 char *poolname = NULL;
2296 struct ll_stat_type types[] = { { LL_STATFS_LMV, "MDT" },
2297 { LL_STATFS_LOV, "OST" },
2299 struct ll_stat_type *tp;
2300 __u64 ost_ffree = 0;
2306 poolname = strchr(pool, '.');
2307 if (poolname != NULL) {
2308 if (strncmp(fsname, pool, strlen(fsname))) {
2309 fprintf(stderr, "filesystem name incorrect\n");
2318 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2319 "UUID", "Inodes", "IUsed", "IFree",
2320 "IUse%", "Mounted on");
2322 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2323 "UUID", cooked ? "bytes" : "1K-blocks",
2324 "Used", "Available", "Use%", "Mounted on");
2326 for (tp = types; tp->st_name != NULL; tp++) {
2327 for (index = 0; ; index++) {
2328 memset(&stat_buf, 0, sizeof(struct obd_statfs));
2329 memset(&uuid_buf, 0, sizeof(struct obd_uuid));
2330 type = lazy ? tp->st_op | LL_STATFS_NODELAY : tp->st_op;
2331 rc = llapi_obd_statfs(mntdir, type, index,
2332 &stat_buf, &uuid_buf);
2339 if (poolname && tp->st_op == LL_STATFS_LOV &&
2340 llapi_search_ost(fsname, poolname,
2341 obd_uuid2str(&uuid_buf)) != 1)
2344 /* the llapi_obd_statfs() call may have returned with
2345 * an error, but if it filled in uuid_buf we will at
2346 * lease use that to print out a message for that OBD.
2347 * If we didn't get anything in the uuid_buf, then fill
2348 * it in so that we can print an error message. */
2349 if (uuid_buf.uuid[0] == '\0')
2350 sprintf(uuid_buf.uuid, "%s%04x",
2351 tp->st_name, index);
2352 showdf(mntdir, &stat_buf, obd_uuid2str(&uuid_buf),
2353 ishow, cooked, tp->st_name, index, rc);
2356 if (tp->st_op == LL_STATFS_LMV) {
2357 sum.os_ffree += stat_buf.os_ffree;
2358 sum.os_files += stat_buf.os_files;
2359 } else /* if (tp->st_op == LL_STATFS_LOV) */ {
2360 sum.os_blocks += stat_buf.os_blocks *
2362 sum.os_bfree += stat_buf.os_bfree *
2364 sum.os_bavail += stat_buf.os_bavail *
2366 ost_ffree += stat_buf.os_ffree;
2368 } else if (rc == -EINVAL || rc == -EFAULT) {
2374 /* If we don't have as many objects free on the OST as inodes
2375 * on the MDS, we reduce the total number of inodes to
2376 * compensate, so that the "inodes in use" number is correct.
2377 * Matches ll_statfs_internal() so the results are consistent. */
2378 if (ost_ffree < sum.os_ffree) {
2379 sum.os_files = (sum.os_files - sum.os_ffree) + ost_ffree;
2380 sum.os_ffree = ost_ffree;
2383 showdf(mntdir, &sum, "filesystem summary:", ishow, cooked, NULL, 0, 0);
2388 static int lfs_df(int argc, char **argv)
2390 char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
2391 int ishow = 0, cooked = 0;
2393 int c, rc = 0, index = 0;
2394 char fsname[PATH_MAX] = "", *pool_name = NULL;
2395 struct option long_opts[] = {
2396 {"pool", required_argument, 0, 'p'},
2397 {"lazy", 0, 0, 'l'},
2401 while ((c = getopt_long(argc, argv, "hilp:", long_opts, NULL)) != -1) {
2419 if (optind < argc && !realpath(argv[optind], path)) {
2421 fprintf(stderr, "error: invalid path '%s': %s\n",
2422 argv[optind], strerror(-rc));
2426 while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2427 /* Check if we have a mount point */
2428 if (mntdir[0] == '\0')
2431 rc = mntdf(mntdir, fsname, pool_name, ishow, cooked, lazy);
2432 if (rc || path[0] != '\0')
2434 fsname[0] = '\0'; /* avoid matching in next loop */
2435 mntdir[0] = '\0'; /* avoid matching in next loop */
2441 static int lfs_getname(int argc, char **argv)
2443 char mntdir[PATH_MAX] = "", path[PATH_MAX] = "", fsname[PATH_MAX] = "";
2444 int rc = 0, index = 0, c;
2445 char buf[sizeof(struct obd_uuid)];
2447 while ((c = getopt(argc, argv, "h")) != -1)
2450 if (optind == argc) { /* no paths specified, get all paths. */
2451 while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2452 rc = llapi_getname(mntdir, buf, sizeof(buf));
2455 "cannot get name for `%s': %s\n",
2456 mntdir, strerror(-rc));
2460 printf("%s %s\n", buf, mntdir);
2462 path[0] = fsname[0] = mntdir[0] = 0;
2464 } else { /* paths specified, only attempt to search these. */
2465 for (; optind < argc; optind++) {
2466 rc = llapi_getname(argv[optind], buf, sizeof(buf));
2469 "cannot get name for `%s': %s\n",
2470 argv[optind], strerror(-rc));
2474 printf("%s %s\n", buf, argv[optind]);
2480 static int lfs_check(int argc, char **argv)
2483 char mntdir[PATH_MAX] = {'\0'};
2492 obd_types[0] = obd_type1;
2493 obd_types[1] = obd_type2;
2495 if (strcmp(argv[1], "osts") == 0) {
2496 strcpy(obd_types[0], "osc");
2497 } else if (strcmp(argv[1], "mds") == 0) {
2498 strcpy(obd_types[0], "mdc");
2499 } else if (strcmp(argv[1], "servers") == 0) {
2501 strcpy(obd_types[0], "osc");
2502 strcpy(obd_types[1], "mdc");
2504 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
2509 rc = llapi_search_mounts(NULL, 0, mntdir, NULL);
2510 if (rc < 0 || mntdir[0] == '\0') {
2511 fprintf(stderr, "No suitable Lustre mount found\n");
2515 rc = llapi_target_check(num_types, obd_types, mntdir);
2517 fprintf(stderr, "error: %s: %s status failed\n",
2524 static int lfs_join(int argc, char **argv)
2526 fprintf(stderr, "join two lustre files into one.\n"
2527 "obsolete, HEAD does not support it anymore.\n");
2531 #ifdef HAVE_SYS_QUOTA_H
2532 #define ARG2INT(nr, str, msg) \
2535 nr = strtol(str, &endp, 0); \
2537 fprintf(stderr, "error: bad %s: %s\n", msg, str); \
2542 #define ADD_OVERFLOW(a,b) ((a + b) < a) ? (a = ULONG_MAX) : (a = a + b)
2544 /* Convert format time string "XXwXXdXXhXXmXXs" into seconds value
2545 * returns the value or ULONG_MAX on integer overflow or incorrect format
2547 * 1. the order of specifiers is arbitrary (may be: 5w3s or 3s5w)
2548 * 2. specifiers may be encountered multiple times (2s3s is 5 seconds)
2549 * 3. empty integer value is interpreted as 0
2551 static unsigned long str2sec(const char* timestr)
2553 const char spec[] = "smhdw";
2554 const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2555 unsigned long val = 0;
2558 if (strpbrk(timestr, spec) == NULL) {
2559 /* no specifiers inside the time string,
2560 should treat it as an integer value */
2561 val = strtoul(timestr, &tail, 10);
2562 return *tail ? ULONG_MAX : val;
2565 /* format string is XXwXXdXXhXXmXXs */
2571 v = strtoul(timestr, &tail, 10);
2572 if (v == ULONG_MAX || *tail == '\0')
2573 /* value too large (ULONG_MAX or more)
2574 or missing specifier */
2577 ptr = strchr(spec, *tail);
2579 /* unknown specifier */
2584 /* check if product will overflow the type */
2585 if (!(v < ULONG_MAX / mult[ind]))
2588 ADD_OVERFLOW(val, mult[ind] * v);
2589 if (val == ULONG_MAX)
2601 #define ARG2ULL(nr, str, def_units) \
2603 unsigned long long limit, units = def_units; \
2606 rc = llapi_parse_size(str, &limit, &units, 1); \
2608 fprintf(stderr, "error: bad limit value %s\n", str); \
2614 static inline int has_times_option(int argc, char **argv)
2618 for (i = 1; i < argc; i++)
2619 if (!strcmp(argv[i], "-t"))
2625 int lfs_setquota_times(int argc, char **argv)
2628 struct if_quotactl qctl;
2629 char *mnt, *obd_type = (char *)qctl.obd_type;
2630 struct obd_dqblk *dqb = &qctl.qc_dqblk;
2631 struct obd_dqinfo *dqi = &qctl.qc_dqinfo;
2632 struct option long_opts[] = {
2633 {"block-grace", required_argument, 0, 'b'},
2634 {"group", no_argument, 0, 'g'},
2635 {"inode-grace", required_argument, 0, 'i'},
2636 {"times", no_argument, 0, 't'},
2637 {"user", no_argument, 0, 'u'},
2641 memset(&qctl, 0, sizeof(qctl));
2642 qctl.qc_cmd = LUSTRE_Q_SETINFO;
2643 qctl.qc_type = UGQUOTA;
2645 while ((c = getopt_long(argc, argv, "b:gi:tu", long_opts, NULL)) != -1) {
2649 if (qctl.qc_type != UGQUOTA) {
2650 fprintf(stderr, "error: -u and -g can't be used "
2651 "more than once\n");
2654 qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2657 if ((dqi->dqi_bgrace = str2sec(optarg)) == ULONG_MAX) {
2658 fprintf(stderr, "error: bad block-grace: %s\n",
2662 dqb->dqb_valid |= QIF_BTIME;
2665 if ((dqi->dqi_igrace = str2sec(optarg)) == ULONG_MAX) {
2666 fprintf(stderr, "error: bad inode-grace: %s\n",
2670 dqb->dqb_valid |= QIF_ITIME;
2672 case 't': /* Yes, of course! */
2674 default: /* getopt prints error message for us when opterr != 0 */
2679 if (qctl.qc_type == UGQUOTA) {
2680 fprintf(stderr, "error: neither -u nor -g specified\n");
2684 if (optind != argc - 1) {
2685 fprintf(stderr, "error: unexpected parameters encountered\n");
2690 rc = llapi_quotactl(mnt, &qctl);
2693 fprintf(stderr, "%s %s ", obd_type,
2694 obd_uuid2str(&qctl.obd_uuid));
2695 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2702 #define BSLIMIT (1 << 0)
2703 #define BHLIMIT (1 << 1)
2704 #define ISLIMIT (1 << 2)
2705 #define IHLIMIT (1 << 3)
2707 int lfs_setquota(int argc, char **argv)
2710 struct if_quotactl qctl;
2711 char *mnt, *obd_type = (char *)qctl.obd_type;
2712 struct obd_dqblk *dqb = &qctl.qc_dqblk;
2713 struct option long_opts[] = {
2714 {"block-softlimit", required_argument, 0, 'b'},
2715 {"block-hardlimit", required_argument, 0, 'B'},
2716 {"group", required_argument, 0, 'g'},
2717 {"inode-softlimit", required_argument, 0, 'i'},
2718 {"inode-hardlimit", required_argument, 0, 'I'},
2719 {"user", required_argument, 0, 'u'},
2722 unsigned limit_mask = 0;
2725 if (has_times_option(argc, argv))
2726 return lfs_setquota_times(argc, argv);
2728 memset(&qctl, 0, sizeof(qctl));
2729 qctl.qc_cmd = LUSTRE_Q_SETQUOTA;
2730 qctl.qc_type = UGQUOTA; /* UGQUOTA makes no sense for setquota,
2731 * so it can be used as a marker that qc_type
2732 * isn't reinitialized from command line */
2734 while ((c = getopt_long(argc, argv, "b:B:g:i:I:u:", long_opts, NULL)) != -1) {
2738 if (qctl.qc_type != UGQUOTA) {
2739 fprintf(stderr, "error: -u and -g can't be used"
2740 " more than once\n");
2743 qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2744 rc = name2id(&qctl.qc_id, optarg,
2745 (qctl.qc_type == USRQUOTA) ? USER : GROUP);
2747 qctl.qc_id = strtoul(optarg, &endptr, 10);
2748 if (*endptr != '\0') {
2749 fprintf(stderr, "error: can't find id "
2750 "for name %s\n", optarg);
2756 ARG2ULL(dqb->dqb_bsoftlimit, optarg, 1024);
2757 dqb->dqb_bsoftlimit >>= 10;
2758 limit_mask |= BSLIMIT;
2759 if (dqb->dqb_bsoftlimit &&
2760 dqb->dqb_bsoftlimit <= 1024) /* <= 1M? */
2761 fprintf(stderr, "warning: block softlimit is "
2762 "smaller than the miminal qunit size, "
2763 "please see the help of setquota or "
2764 "Lustre manual for details.\n");
2767 ARG2ULL(dqb->dqb_bhardlimit, optarg, 1024);
2768 dqb->dqb_bhardlimit >>= 10;
2769 limit_mask |= BHLIMIT;
2770 if (dqb->dqb_bhardlimit &&
2771 dqb->dqb_bhardlimit <= 1024) /* <= 1M? */
2772 fprintf(stderr, "warning: block hardlimit is "
2773 "smaller than the miminal qunit size, "
2774 "please see the help of setquota or "
2775 "Lustre manual for details.\n");
2778 ARG2ULL(dqb->dqb_isoftlimit, optarg, 1);
2779 limit_mask |= ISLIMIT;
2780 if (dqb->dqb_isoftlimit &&
2781 dqb->dqb_isoftlimit <= 1024) /* <= 1K inodes? */
2782 fprintf(stderr, "warning: inode softlimit is "
2783 "smaller than the miminal qunit size, "
2784 "please see the help of setquota or "
2785 "Lustre manual for details.\n");
2788 ARG2ULL(dqb->dqb_ihardlimit, optarg, 1);
2789 limit_mask |= IHLIMIT;
2790 if (dqb->dqb_ihardlimit &&
2791 dqb->dqb_ihardlimit <= 1024) /* <= 1K inodes? */
2792 fprintf(stderr, "warning: inode hardlimit is "
2793 "smaller than the miminal qunit size, "
2794 "please see the help of setquota or "
2795 "Lustre manual for details.\n");
2797 default: /* getopt prints error message for us when opterr != 0 */
2802 if (qctl.qc_type == UGQUOTA) {
2803 fprintf(stderr, "error: neither -u nor -g was specified\n");
2807 if (limit_mask == 0) {
2808 fprintf(stderr, "error: at least one limit must be specified\n");
2812 if (optind != argc - 1) {
2813 fprintf(stderr, "error: unexpected parameters encountered\n");
2819 if ((!(limit_mask & BHLIMIT) ^ !(limit_mask & BSLIMIT)) ||
2820 (!(limit_mask & IHLIMIT) ^ !(limit_mask & ISLIMIT))) {
2821 /* sigh, we can't just set blimits/ilimits */
2822 struct if_quotactl tmp_qctl = {.qc_cmd = LUSTRE_Q_GETQUOTA,
2823 .qc_type = qctl.qc_type,
2824 .qc_id = qctl.qc_id};
2826 rc = llapi_quotactl(mnt, &tmp_qctl);
2828 fprintf(stderr, "error: setquota failed while retrieving"
2829 " current quota settings (%s)\n",
2834 if (!(limit_mask & BHLIMIT))
2835 dqb->dqb_bhardlimit = tmp_qctl.qc_dqblk.dqb_bhardlimit;
2836 if (!(limit_mask & BSLIMIT))
2837 dqb->dqb_bsoftlimit = tmp_qctl.qc_dqblk.dqb_bsoftlimit;
2838 if (!(limit_mask & IHLIMIT))
2839 dqb->dqb_ihardlimit = tmp_qctl.qc_dqblk.dqb_ihardlimit;
2840 if (!(limit_mask & ISLIMIT))
2841 dqb->dqb_isoftlimit = tmp_qctl.qc_dqblk.dqb_isoftlimit;
2843 /* Keep grace times if we have got no softlimit arguments */
2844 if ((limit_mask & BHLIMIT) && !(limit_mask & BSLIMIT)) {
2845 dqb->dqb_valid |= QIF_BTIME;
2846 dqb->dqb_btime = tmp_qctl.qc_dqblk.dqb_btime;
2849 if ((limit_mask & IHLIMIT) && !(limit_mask & ISLIMIT)) {
2850 dqb->dqb_valid |= QIF_ITIME;
2851 dqb->dqb_itime = tmp_qctl.qc_dqblk.dqb_itime;
2855 dqb->dqb_valid |= (limit_mask & (BHLIMIT | BSLIMIT)) ? QIF_BLIMITS : 0;
2856 dqb->dqb_valid |= (limit_mask & (IHLIMIT | ISLIMIT)) ? QIF_ILIMITS : 0;
2858 rc = llapi_quotactl(mnt, &qctl);
2861 fprintf(stderr, "%s %s ", obd_type,
2862 obd_uuid2str(&qctl.obd_uuid));
2863 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2870 static inline char *type2name(int check_type)
2872 if (check_type == USRQUOTA)
2874 else if (check_type == GRPQUOTA)
2880 /* Converts seconds value into format string
2881 * result is returned in buf
2883 * 1. result is in descenting order: 1w2d3h4m5s
2884 * 2. zero fields are not filled (except for p. 3): 5d1s
2885 * 3. zero seconds value is presented as "0s"
2887 static char * __sec2str(time_t seconds, char *buf)
2889 const char spec[] = "smhdw";
2890 const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2895 for (i = sizeof(mult) / sizeof(mult[0]) - 1 ; i >= 0; i--) {
2896 c = seconds / mult[i];
2898 if (c > 0 || (i == 0 && buf == tail))
2899 tail += snprintf(tail, 40-(tail-buf), "%lu%c", c, spec[i]);
2907 static void sec2str(time_t seconds, char *buf, int rc)
2914 tail = __sec2str(seconds, tail);
2916 if (rc && tail - buf < 39) {
2922 static void diff2str(time_t seconds, char *buf, time_t now)
2928 if (seconds <= now) {
2929 strcpy(buf, "none");
2932 __sec2str(seconds - now, buf);
2935 static void print_quota_title(char *name, struct if_quotactl *qctl,
2936 bool human_readable)
2938 printf("Disk quotas for %s %s (%cid %u):\n",
2939 type2name(qctl->qc_type), name,
2940 *type2name(qctl->qc_type), qctl->qc_id);
2941 printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n",
2942 "Filesystem", human_readable ? "used" : "kbytes",
2943 "quota", "limit", "grace",
2944 "files", "quota", "limit", "grace");
2947 static void kbytes2str(__u64 num, char *buf, int buflen, bool h)
2950 snprintf(buf, buflen, "%ju", (uintmax_t)num);
2953 snprintf(buf, buflen, "%5.4gP",
2954 (double)num / ((__u64)1 << 40));
2956 snprintf(buf, buflen, "%5.4gT",
2957 (double)num / (1 << 30));
2959 snprintf(buf, buflen, "%5.4gG",
2960 (double)num / (1 << 20));
2962 snprintf(buf, buflen, "%5.4gM",
2963 (double)num / (1 << 10));
2965 snprintf(buf, buflen, "%ju%s", (uintmax_t)num, "k");
2969 #define STRBUF_LEN 32
2970 static void print_quota(char *mnt, struct if_quotactl *qctl, int type,
2977 if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == Q_GETOQUOTA) {
2978 int bover = 0, iover = 0;
2979 struct obd_dqblk *dqb = &qctl->qc_dqblk;
2980 char numbuf[3][STRBUF_LEN];
2982 char strbuf[STRBUF_LEN];
2984 if (dqb->dqb_bhardlimit &&
2985 lustre_stoqb(dqb->dqb_curspace) >= dqb->dqb_bhardlimit) {
2987 } else if (dqb->dqb_bsoftlimit && dqb->dqb_btime) {
2988 if (dqb->dqb_btime > now) {
2995 if (dqb->dqb_ihardlimit &&
2996 dqb->dqb_curinodes >= dqb->dqb_ihardlimit) {
2998 } else if (dqb->dqb_isoftlimit && dqb->dqb_itime) {
2999 if (dqb->dqb_itime > now) {
3007 if (strlen(mnt) > 15)
3008 printf("%s\n%15s", mnt, "");
3010 printf("%15s", mnt);
3013 diff2str(dqb->dqb_btime, timebuf, now);
3015 kbytes2str(lustre_stoqb(dqb->dqb_curspace),
3016 strbuf, sizeof(strbuf), h);
3017 if (rc == -EREMOTEIO)
3018 sprintf(numbuf[0], "%s*", strbuf);
3020 sprintf(numbuf[0], (dqb->dqb_valid & QIF_SPACE) ?
3021 "%s" : "[%s]", strbuf);
3023 kbytes2str(dqb->dqb_bsoftlimit, strbuf, sizeof(strbuf), h);
3024 if (type == QC_GENERAL)
3025 sprintf(numbuf[1], (dqb->dqb_valid & QIF_BLIMITS) ?
3026 "%s" : "[%s]", strbuf);
3028 sprintf(numbuf[1], "%s", "-");
3030 kbytes2str(dqb->dqb_bhardlimit, strbuf, sizeof(strbuf), h);
3031 sprintf(numbuf[2], (dqb->dqb_valid & QIF_BLIMITS) ?
3032 "%s" : "[%s]", strbuf);
3034 printf(" %7s%c %6s %7s %7s",
3035 numbuf[0], bover ? '*' : ' ', numbuf[1],
3036 numbuf[2], bover > 1 ? timebuf : "-");
3039 diff2str(dqb->dqb_itime, timebuf, now);
3041 sprintf(numbuf[0], (dqb->dqb_valid & QIF_INODES) ?
3042 "%ju" : "[%ju]", (uintmax_t)dqb->dqb_curinodes);
3044 if (type == QC_GENERAL)
3045 sprintf(numbuf[1], (dqb->dqb_valid & QIF_ILIMITS) ?
3047 (uintmax_t)dqb->dqb_isoftlimit);
3049 sprintf(numbuf[1], "%s", "-");
3051 sprintf(numbuf[2], (dqb->dqb_valid & QIF_ILIMITS) ?
3052 "%ju" : "[%ju]", (uintmax_t)dqb->dqb_ihardlimit);
3054 if (type != QC_OSTIDX)
3055 printf(" %7s%c %6s %7s %7s",
3056 numbuf[0], iover ? '*' : ' ', numbuf[1],
3057 numbuf[2], iover > 1 ? timebuf : "-");
3059 printf(" %7s %7s %7s %7s", "-", "-", "-", "-");
3062 } else if (qctl->qc_cmd == LUSTRE_Q_GETINFO ||
3063 qctl->qc_cmd == Q_GETOINFO) {
3067 sec2str(qctl->qc_dqinfo.dqi_bgrace, bgtimebuf, rc);
3068 sec2str(qctl->qc_dqinfo.dqi_igrace, igtimebuf, rc);
3069 printf("Block grace time: %s; Inode grace time: %s\n",
3070 bgtimebuf, igtimebuf);
3074 static int print_obd_quota(char *mnt, struct if_quotactl *qctl, int is_mdt,
3075 bool h, __u64 *total)
3077 int rc = 0, rc1 = 0, count = 0;
3078 __u32 valid = qctl->qc_valid;
3080 rc = llapi_get_obd_count(mnt, &count, is_mdt);
3082 fprintf(stderr, "can not get %s count: %s\n",
3083 is_mdt ? "mdt": "ost", strerror(-rc));
3087 for (qctl->qc_idx = 0; qctl->qc_idx < count; qctl->qc_idx++) {
3088 qctl->qc_valid = is_mdt ? QC_MDTIDX : QC_OSTIDX;
3089 rc = llapi_quotactl(mnt, qctl);
3091 /* It is remote client case. */
3092 if (-rc == EOPNOTSUPP) {
3099 fprintf(stderr, "quotactl %s%d failed.\n",
3100 is_mdt ? "mdt": "ost", qctl->qc_idx);
3104 print_quota(obd_uuid2str(&qctl->obd_uuid), qctl,
3105 qctl->qc_valid, 0, h);
3106 *total += is_mdt ? qctl->qc_dqblk.dqb_ihardlimit :
3107 qctl->qc_dqblk.dqb_bhardlimit;
3110 qctl->qc_valid = valid;
3114 static int lfs_quota(int argc, char **argv)
3117 char *mnt, *name = NULL;
3118 struct if_quotactl qctl = { .qc_cmd = LUSTRE_Q_GETQUOTA,
3119 .qc_type = UGQUOTA };
3120 char *obd_type = (char *)qctl.obd_type;
3121 char *obd_uuid = (char *)qctl.obd_uuid.uuid;
3122 int rc, rc1 = 0, rc2 = 0, rc3 = 0,
3123 verbose = 0, pass = 0, quiet = 0, inacc;
3125 __u32 valid = QC_GENERAL, idx = 0;
3126 __u64 total_ialloc = 0, total_balloc = 0;
3127 bool human_readable = false;
3129 while ((c = getopt(argc, argv, "gi:I:o:qtuvh")) != -1) {
3132 if (qctl.qc_type != UGQUOTA) {
3133 fprintf(stderr, "error: use either -u or -g\n");
3136 qctl.qc_type = USRQUOTA;
3139 if (qctl.qc_type != UGQUOTA) {
3140 fprintf(stderr, "error: use either -u or -g\n");
3143 qctl.qc_type = GRPQUOTA;
3146 qctl.qc_cmd = LUSTRE_Q_GETINFO;
3149 valid = qctl.qc_valid = QC_UUID;
3150 strlcpy(obd_uuid, optarg, sizeof(qctl.obd_uuid));
3153 valid = qctl.qc_valid = QC_MDTIDX;
3154 idx = qctl.qc_idx = atoi(optarg);
3157 valid = qctl.qc_valid = QC_OSTIDX;
3158 idx = qctl.qc_idx = atoi(optarg);
3167 human_readable = true;
3170 fprintf(stderr, "error: %s: option '-%c' "
3171 "unrecognized\n", argv[0], c);
3176 /* current uid/gid info for "lfs quota /path/to/lustre/mount" */
3177 if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && qctl.qc_type == UGQUOTA &&
3178 optind == argc - 1) {
3180 memset(&qctl, 0, sizeof(qctl)); /* spoiled by print_*_quota */
3181 qctl.qc_cmd = LUSTRE_Q_GETQUOTA;
3182 qctl.qc_valid = valid;
3185 qctl.qc_type = USRQUOTA;
3186 qctl.qc_id = geteuid();
3188 qctl.qc_type = GRPQUOTA;
3189 qctl.qc_id = getegid();
3191 rc = id2name(&name, qctl.qc_id,
3192 (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3195 /* lfs quota -u username /path/to/lustre/mount */
3196 } else if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) {
3197 /* options should be followed by u/g-name and mntpoint */
3198 if (optind + 2 != argc || qctl.qc_type == UGQUOTA) {
3199 fprintf(stderr, "error: missing quota argument(s)\n");
3203 name = argv[optind++];
3204 rc = name2id(&qctl.qc_id, name,
3205 (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3207 qctl.qc_id = strtoul(name, &endptr, 10);
3208 if (*endptr != '\0') {
3209 fprintf(stderr, "error: can't find id for name "
3214 } else if (optind + 1 != argc || qctl.qc_type == UGQUOTA) {
3215 fprintf(stderr, "error: missing quota info argument(s)\n");
3221 rc1 = llapi_quotactl(mnt, &qctl);
3225 fprintf(stderr, "%s quotas are not enabled.\n",
3226 qctl.qc_type == USRQUOTA ? "user" : "group");
3229 fprintf(stderr, "Permission denied.\n");
3231 /* We already got a "No such file..." message. */
3234 fprintf(stderr, "Unexpected quotactl error: %s\n",
3239 if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && !quiet)
3240 print_quota_title(name, &qctl, human_readable);
3242 if (rc1 && *obd_type)
3243 fprintf(stderr, "%s %s ", obd_type, obd_uuid);
3245 if (qctl.qc_valid != QC_GENERAL)
3248 inacc = (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) &&
3249 ((qctl.qc_dqblk.dqb_valid & (QIF_LIMITS|QIF_USAGE)) !=
3250 (QIF_LIMITS|QIF_USAGE));
3252 print_quota(mnt, &qctl, QC_GENERAL, rc1, human_readable);
3254 if (qctl.qc_valid == QC_GENERAL && qctl.qc_cmd != LUSTRE_Q_GETINFO &&
3256 char strbuf[STRBUF_LEN];
3258 rc2 = print_obd_quota(mnt, &qctl, 1, human_readable,
3260 rc3 = print_obd_quota(mnt, &qctl, 0, human_readable,
3262 kbytes2str(total_balloc, strbuf, sizeof(strbuf),
3264 printf("Total allocated inode limit: %ju, total "
3265 "allocated block limit: %s\n", (uintmax_t)total_ialloc,
3269 if (rc1 || rc2 || rc3 || inacc)
3270 printf("Some errors happened when getting quota info. "
3271 "Some devices may be not working or deactivated. "
3272 "The data in \"[]\" is inaccurate.\n");
3280 #endif /* HAVE_SYS_QUOTA_H! */
3282 static int flushctx_ioctl(char *mp)
3286 fd = open(mp, O_RDONLY);
3288 fprintf(stderr, "flushctx: error open %s: %s\n",
3289 mp, strerror(errno));
3293 rc = ioctl(fd, LL_IOC_FLUSHCTX);
3295 fprintf(stderr, "flushctx: error ioctl %s: %s\n",
3296 mp, strerror(errno));
3302 static int lfs_flushctx(int argc, char **argv)
3304 int kdestroy = 0, c;
3305 char mntdir[PATH_MAX] = {'\0'};
3309 while ((c = getopt(argc, argv, "k")) != -1) {
3315 fprintf(stderr, "error: %s: option '-%c' "
3316 "unrecognized\n", argv[0], c);
3322 if ((rc = system("kdestroy > /dev/null")) != 0) {
3323 rc = WEXITSTATUS(rc);
3324 fprintf(stderr, "error destroying tickets: %d, continuing\n", rc);
3328 if (optind >= argc) {
3329 /* flush for all mounted lustre fs. */
3330 while (!llapi_search_mounts(NULL, index++, mntdir, NULL)) {
3331 /* Check if we have a mount point */
3332 if (mntdir[0] == '\0')
3335 if (flushctx_ioctl(mntdir))
3338 mntdir[0] = '\0'; /* avoid matching in next loop */
3341 /* flush fs as specified */
3342 while (optind < argc) {
3343 if (flushctx_ioctl(argv[optind++]))
3350 static int lfs_lsetfacl(int argc, char **argv)
3353 return(llapi_lsetfacl(argc, argv));
3356 static int lfs_lgetfacl(int argc, char **argv)
3359 return(llapi_lgetfacl(argc, argv));
3362 static int lfs_rsetfacl(int argc, char **argv)
3365 return(llapi_rsetfacl(argc, argv));
3368 static int lfs_rgetfacl(int argc, char **argv)
3371 return(llapi_rgetfacl(argc, argv));
3374 static int lfs_cp(int argc, char **argv)
3376 return(llapi_cp(argc, argv));
3379 static int lfs_ls(int argc, char **argv)
3381 return(llapi_ls(argc, argv));
3384 static int lfs_changelog(int argc, char **argv)
3386 void *changelog_priv;
3387 struct changelog_rec *rec;
3388 long long startrec = 0, endrec = 0;
3390 struct option long_opts[] = {
3391 {"follow", no_argument, 0, 'f'},
3394 char short_opts[] = "f";
3397 while ((rc = getopt_long(argc, argv, short_opts,
3398 long_opts, NULL)) != -1) {
3406 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3407 argv[0], argv[optind - 1]);
3414 mdd = argv[optind++];
3416 startrec = strtoll(argv[optind++], NULL, 10);
3418 endrec = strtoll(argv[optind++], NULL, 10);
3420 rc = llapi_changelog_start(&changelog_priv,
3421 CHANGELOG_FLAG_BLOCK |
3422 CHANGELOG_FLAG_JOBID |
3423 (follow ? CHANGELOG_FLAG_FOLLOW : 0),
3426 fprintf(stderr, "Can't start changelog: %s\n",
3427 strerror(errno = -rc));
3431 while ((rc = llapi_changelog_recv(changelog_priv, &rec)) == 0) {
3435 if (endrec && rec->cr_index > endrec) {
3436 llapi_changelog_free(&rec);
3439 if (rec->cr_index < startrec) {
3440 llapi_changelog_free(&rec);
3444 secs = rec->cr_time >> 30;
3445 gmtime_r(&secs, &ts);
3446 printf("%ju %02d%-5s %02d:%02d:%02d.%06d %04d.%02d.%02d "
3447 "0x%x t="DFID, (uintmax_t) rec->cr_index, rec->cr_type,
3448 changelog_type2str(rec->cr_type),
3449 ts.tm_hour, ts.tm_min, ts.tm_sec,
3450 (int)(rec->cr_time & ((1<<30) - 1)),
3451 ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday,
3452 rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
3454 if (rec->cr_flags & CLF_JOBID) {
3455 struct changelog_ext_jobid *jid =
3456 changelog_rec_jobid(rec);
3458 if (jid->cr_jobid[0] != '\0')
3459 printf(" j=%s", jid->cr_jobid);
3462 if (rec->cr_namelen)
3463 printf(" p="DFID" %.*s", PFID(&rec->cr_pfid),
3464 rec->cr_namelen, changelog_rec_name(rec));
3466 if (rec->cr_flags & CLF_RENAME) {
3467 struct changelog_ext_rename *rnm =
3468 changelog_rec_rename(rec);
3470 if (!fid_is_zero(&rnm->cr_sfid))
3471 printf(" s="DFID" sp="DFID" %.*s",
3472 PFID(&rnm->cr_sfid),
3473 PFID(&rnm->cr_spfid),
3474 (int)changelog_rec_snamelen(rec),
3475 changelog_rec_sname(rec));
3479 llapi_changelog_free(&rec);
3482 llapi_changelog_fini(&changelog_priv);
3485 fprintf(stderr, "Changelog: %s\n", strerror(errno = -rc));
3487 return (rc == 1 ? 0 : rc);
3490 static int lfs_changelog_clear(int argc, char **argv)
3498 endrec = strtoll(argv[3], NULL, 10);
3500 rc = llapi_changelog_clear(argv[1], argv[2], endrec);
3502 fprintf(stderr, "%s error: %s\n", argv[0],
3503 strerror(errno = -rc));
3507 static int lfs_fid2path(int argc, char **argv)
3509 struct option long_opts[] = {
3510 {"cur", no_argument, 0, 'c'},
3511 {"link", required_argument, 0, 'l'},
3512 {"rec", required_argument, 0, 'r'},
3515 char short_opts[] = "cl:r:";
3516 char *device, *fid, *path;
3517 long long recno = -1;
3523 while ((rc = getopt_long(argc, argv, short_opts,
3524 long_opts, NULL)) != -1) {
3530 linkno = strtol(optarg, NULL, 10);
3533 recno = strtoll(optarg, NULL, 10);
3538 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3539 argv[0], argv[optind - 1]);
3547 device = argv[optind++];
3548 path = calloc(1, PATH_MAX);
3550 fprintf(stderr, "error: Not enough memory\n");
3555 while (optind < argc) {
3556 fid = argv[optind++];
3558 lnktmp = (linkno >= 0) ? linkno : 0;
3560 int oldtmp = lnktmp;
3561 long long rectmp = recno;
3563 rc2 = llapi_fid2path(device, fid, path, PATH_MAX,
3566 fprintf(stderr, "%s: error on FID %s: %s\n",
3567 argv[0], fid, strerror(errno = -rc2));
3574 fprintf(stdout, "%lld ", rectmp);
3575 if (device[0] == '/') {
3576 fprintf(stdout, "%s", device);
3577 if (device[strlen(device) - 1] != '/')
3578 fprintf(stdout, "/");
3579 } else if (path[0] == '\0') {
3580 fprintf(stdout, "/");
3582 fprintf(stdout, "%s\n", path);
3585 /* specified linkno */
3587 if (oldtmp == lnktmp)
3597 static int lfs_path2fid(int argc, char **argv)
3599 struct option long_opts[] = {
3600 {"parents", no_argument, 0, 'p'},
3604 const char short_opts[] = "p";
3605 const char *sep = "";
3608 bool show_parents = false;
3610 while ((rc = getopt_long(argc, argv, short_opts,
3611 long_opts, NULL)) != -1) {
3614 show_parents = true;
3617 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3618 argv[0], argv[optind - 1]);
3623 if (optind > argc - 1)
3625 else if (optind < argc - 1)
3629 for (path = argv + optind; *path != NULL; path++) {
3631 if (!show_parents) {
3632 err = llapi_path2fid(*path, &fid);
3634 printf("%s%s"DFID"\n",
3635 *sep != '\0' ? *path : "", sep,
3638 char name[NAME_MAX + 1];
3639 unsigned int linkno = 0;
3641 while ((err = llapi_path2parent(*path, linkno, &fid,
3642 name, sizeof(name))) == 0) {
3643 if (*sep != '\0' && linkno == 0)
3644 printf("%s%s", *path, sep);
3646 printf("%s"DFID"/%s", linkno != 0 ? "\t" : "",
3651 /* err == -ENODATA is end-of-loop */
3652 if (linkno > 0 && err == -ENODATA) {
3659 fprintf(stderr, "%s: can't get %sfid for %s: %s\n",
3660 argv[0], show_parents ? "parent " : "", *path,
3672 static int lfs_data_version(int argc, char **argv)
3679 int data_version_flags = LL_DV_RD_FLUSH; /* Read by default */
3684 while ((c = getopt(argc, argv, "nrw")) != -1) {
3687 data_version_flags = 0;
3690 data_version_flags |= LL_DV_RD_FLUSH;
3693 data_version_flags |= LL_DV_WR_FLUSH;
3702 path = argv[optind];
3703 fd = open(path, O_RDONLY);
3705 err(errno, "cannot open file %s", path);
3707 rc = llapi_get_data_version(fd, &data_version, data_version_flags);
3709 err(errno, "cannot get version for %s", path);
3711 printf("%ju" "\n", (uintmax_t)data_version);
3717 static int lfs_hsm_state(int argc, char **argv)
3722 struct hsm_user_state hus;
3730 rc = llapi_hsm_state_get(path, &hus);
3732 fprintf(stderr, "can't get hsm state for %s: %s\n",
3733 path, strerror(errno = -rc));
3737 /* Display path name and status flags */
3738 printf("%s: (0x%08x)", path, hus.hus_states);
3740 if (hus.hus_states & HS_RELEASED)
3741 printf(" released");
3742 if (hus.hus_states & HS_EXISTS)
3744 if (hus.hus_states & HS_DIRTY)
3746 if (hus.hus_states & HS_ARCHIVED)
3747 printf(" archived");
3748 /* Display user-settable flags */
3749 if (hus.hus_states & HS_NORELEASE)
3750 printf(" never_release");
3751 if (hus.hus_states & HS_NOARCHIVE)
3752 printf(" never_archive");
3753 if (hus.hus_states & HS_LOST)
3754 printf(" lost_from_hsm");
3756 if (hus.hus_archive_id != 0)
3757 printf(", archive_id:%d", hus.hus_archive_id);
3760 } while (++i < argc);
3765 #define LFS_HSM_SET 0
3766 #define LFS_HSM_CLEAR 1
3769 * Generic function to set or clear HSM flags.
3770 * Used by hsm_set and hsm_clear.
3772 * @mode if LFS_HSM_SET, set the flags, if LFS_HSM_CLEAR, clear the flags.
3774 static int lfs_hsm_change_flags(int argc, char **argv, int mode)
3776 struct option long_opts[] = {
3777 {"lost", 0, 0, 'l'},
3778 {"norelease", 0, 0, 'r'},
3779 {"noarchive", 0, 0, 'a'},
3780 {"archived", 0, 0, 'A'},
3781 {"dirty", 0, 0, 'd'},
3782 {"exists", 0, 0, 'e'},
3785 char short_opts[] = "lraAde";
3793 while ((c = getopt_long(argc, argv, short_opts,
3794 long_opts, NULL)) != -1) {
3800 mask |= HS_NOARCHIVE;
3803 mask |= HS_ARCHIVED;
3806 mask |= HS_NORELEASE;
3817 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3818 argv[0], argv[optind - 1]);
3823 /* User should have specified a flag */
3827 while (optind < argc) {
3829 path = argv[optind];
3831 /* If mode == 0, this means we apply the mask. */
3832 if (mode == LFS_HSM_SET)
3833 rc = llapi_hsm_state_set(path, mask, 0, 0);
3835 rc = llapi_hsm_state_set(path, 0, mask, 0);
3838 fprintf(stderr, "Can't change hsm flags for %s: %s\n",
3839 path, strerror(errno = -rc));
3848 static int lfs_hsm_action(int argc, char **argv)
3853 struct hsm_current_action hca;
3854 struct hsm_extent he;
3855 enum hsm_user_action hua;
3856 enum hsm_progress_states hps;
3864 rc = llapi_hsm_current_action(path, &hca);
3866 fprintf(stderr, "can't get hsm action for %s: %s\n",
3867 path, strerror(errno = -rc));
3870 he = hca.hca_location;
3871 hua = hca.hca_action;
3872 hps = hca.hca_state;
3874 printf("%s: %s", path, hsm_user_action2name(hua));
3876 /* Skip file without action */
3877 if (hca.hca_action == HUA_NONE) {
3882 printf(" %s ", hsm_progress_state2name(hps));
3884 if ((hps == HPS_RUNNING) &&
3885 (hua == HUA_ARCHIVE || hua == HUA_RESTORE))
3886 printf("(%llu bytes moved)\n",
3887 (unsigned long long)he.length);
3888 else if ((he.offset + he.length) == LUSTRE_EOF)
3889 printf("(from %llu to EOF)\n",
3890 (unsigned long long)he.offset);
3892 printf("(from %llu to %llu)\n",
3893 (unsigned long long)he.offset,
3894 (unsigned long long)(he.offset + he.length));
3896 } while (++i < argc);
3901 static int lfs_hsm_set(int argc, char **argv)
3903 return lfs_hsm_change_flags(argc, argv, LFS_HSM_SET);
3906 static int lfs_hsm_clear(int argc, char **argv)
3908 return lfs_hsm_change_flags(argc, argv, LFS_HSM_CLEAR);
3912 * Check file state and return its fid, to be used by lfs_hsm_request().
3914 * \param[in] file Path to file to check
3915 * \param[in,out] fid Pointer to allocated lu_fid struct.
3916 * \param[in,out] last_dev Pointer to last device id used.
3918 * \return 0 on success.
3920 static int lfs_hsm_prepare_file(const char *file, struct lu_fid *fid,
3926 rc = lstat(file, &st);
3928 fprintf(stderr, "Cannot stat %s: %s\n", file, strerror(errno));
3931 /* Checking for regular file as archiving as posix copytool
3932 * rejects archiving files other than regular files
3934 if (!S_ISREG(st.st_mode)) {
3935 fprintf(stderr, "error: \"%s\" is not a regular file\n", file);
3938 /* A request should be ... */
3939 if (*last_dev != st.st_dev && *last_dev != 0) {
3940 fprintf(stderr, "All files should be "
3941 "on the same filesystem: %s\n", file);
3944 *last_dev = st.st_dev;
3946 rc = llapi_path2fid(file, fid);
3948 fprintf(stderr, "Cannot read FID of %s: %s\n",
3949 file, strerror(-rc));
3955 /* Fill an HSM HUR item with a given file name.
3957 * If mntpath is set, then the filename is actually a FID, and no
3958 * lookup on the filesystem will be performed.
3960 * \param[in] hur the user request to fill
3961 * \param[in] idx index of the item inside the HUR to fill
3962 * \param[in] mntpath mountpoint of Lustre
3963 * \param[in] fname filename (if mtnpath is NULL)
3964 * or FID (if mntpath is set)
3965 * \param[in] last_dev pointer to last device id used
3967 * \retval 0 on success
3968 * \retval CMD_HELP or a negative errno on error
3970 static int fill_hur_item(struct hsm_user_request *hur, unsigned int idx,
3971 const char *mntpath, const char *fname,
3974 struct hsm_user_item *hui = &hur->hur_user_item[idx];
3977 hui->hui_extent.length = -1;
3979 if (mntpath != NULL) {
3982 rc = sscanf(fname, SFID, RFID(&hui->hui_fid));
3986 fprintf(stderr, "hsm: '%s' is not a valid FID\n",
3991 rc = lfs_hsm_prepare_file(fname, &hui->hui_fid, last_dev);
3995 hur->hur_request.hr_itemcount++;
4000 static int lfs_hsm_request(int argc, char **argv, int action)
4002 struct option long_opts[] = {
4003 {"filelist", 1, 0, 'l'},
4004 {"data", 1, 0, 'D'},
4005 {"archive", 1, 0, 'a'},
4006 {"mntpath", 1, 0, 'm'},
4010 char short_opts[] = "l:D:a:m:";
4011 struct hsm_user_request *hur, *oldhur;
4016 char *filelist = NULL;
4017 char fullpath[PATH_MAX];
4018 char *opaque = NULL;
4022 int nbfile_alloc = 0;
4023 char *some_file = NULL;
4024 char *mntpath = NULL;
4030 while ((c = getopt_long(argc, argv, short_opts,
4031 long_opts, NULL)) != -1) {
4040 if (action != HUA_ARCHIVE &&
4041 action != HUA_REMOVE) {
4043 "error: -a is supported only "
4044 "when archiving or removing\n");
4047 archive_id = atoi(optarg);
4050 if (some_file == NULL) {
4052 some_file = strdup(optarg);
4058 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
4059 argv[0], argv[optind - 1]);
4064 /* All remaining args are files, so we have at least nbfile */
4065 nbfile = argc - optind;
4067 if ((nbfile == 0) && (filelist == NULL))
4071 opaque_len = strlen(opaque);
4073 /* Alloc the request structure with enough place to store all files
4074 * from command line. */
4075 hur = llapi_hsm_user_request_alloc(nbfile, opaque_len);
4077 fprintf(stderr, "Cannot create the request: %s\n",
4081 nbfile_alloc = nbfile;
4083 hur->hur_request.hr_action = action;
4084 hur->hur_request.hr_archive_id = archive_id;
4085 hur->hur_request.hr_flags = 0;
4087 /* All remaining args are files, add them */
4088 if (nbfile != 0 && some_file == NULL)
4089 some_file = strdup(argv[optind]);
4091 for (i = 0; i < nbfile; i++) {
4092 rc = fill_hur_item(hur, i, mntpath, argv[optind + i],
4098 /* from here stop using nb_file, use hur->hur_request.hr_itemcount */
4100 /* If a filelist was specified, read the filelist from it. */
4101 if (filelist != NULL) {
4102 fp = fopen(filelist, "r");
4104 fprintf(stderr, "Cannot read the file list %s: %s\n",
4105 filelist, strerror(errno));
4110 while ((rc = getline(&line, &len, fp)) != -1) {
4111 /* If allocated buffer was too small, get something
4113 if (nbfile_alloc <= hur->hur_request.hr_itemcount) {
4116 nbfile_alloc = nbfile_alloc * 2 + 1;
4118 hur = llapi_hsm_user_request_alloc(nbfile_alloc,
4121 fprintf(stderr, "hsm: cannot allocate "
4122 "the request: %s\n",
4129 size = hur_len(oldhur);
4131 fprintf(stderr, "hsm: cannot allocate "
4132 "%u files + %u bytes data\n",
4133 oldhur->hur_request.hr_itemcount,
4134 oldhur->hur_request.hr_data_len);
4141 memcpy(hur, oldhur, size);
4146 if (line[strlen(line) - 1] == '\n')
4147 line[strlen(line) - 1] = '\0';
4149 rc = fill_hur_item(hur, hur->hur_request.hr_itemcount,
4150 mntpath, line, &last_dev);
4156 if (some_file == NULL) {
4166 /* If a --data was used, add it to the request */
4167 hur->hur_request.hr_data_len = opaque_len;
4169 memcpy(hur_data(hur), opaque, opaque_len);
4171 /* Send the HSM request */
4172 if (realpath(some_file, fullpath) == NULL) {
4173 fprintf(stderr, "Could not find path '%s': %s\n",
4174 some_file, strerror(errno));
4176 rc = llapi_hsm_request(fullpath, hur);
4178 fprintf(stderr, "Cannot send HSM request (use of %s): %s\n",
4179 some_file, strerror(-rc));
4189 static int lfs_hsm_archive(int argc, char **argv)
4191 return lfs_hsm_request(argc, argv, HUA_ARCHIVE);
4194 static int lfs_hsm_restore(int argc, char **argv)
4196 return lfs_hsm_request(argc, argv, HUA_RESTORE);
4199 static int lfs_hsm_release(int argc, char **argv)
4201 return lfs_hsm_request(argc, argv, HUA_RELEASE);
4204 static int lfs_hsm_remove(int argc, char **argv)
4206 return lfs_hsm_request(argc, argv, HUA_REMOVE);
4209 static int lfs_hsm_cancel(int argc, char **argv)
4211 return lfs_hsm_request(argc, argv, HUA_CANCEL);
4214 static int lfs_swap_layouts(int argc, char **argv)
4219 return llapi_swap_layouts(argv[1], argv[2], 0, 0,
4220 SWAP_LAYOUTS_KEEP_MTIME |
4221 SWAP_LAYOUTS_KEEP_ATIME);
4224 int main(int argc, char **argv)
4228 /* Ensure that liblustreapi constructor has run */
4229 if (!liblustreapi_initialized)
4230 fprintf(stderr, "liblustreapi was not properly initialized\n");
4234 Parser_init("lfs > ", cmdlist);
4236 progname = argv[0]; /* Used in error messages */
4238 rc = Parser_execarg(argc - 1, argv + 1, cmdlist);
4240 rc = Parser_commands();
4243 return rc < 0 ? -rc : rc;
4246 #ifdef _LUSTRE_IDL_H_
4247 /* Everything we need here should be included by lustreapi.h. */
4248 # error "lfs should not depend on lustre_idl.h"
4249 #endif /* _LUSTRE_IDL_H_ */