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 # define ARRAY_SIZE(a) ((sizeof(a)) / (sizeof((a)[0])))
80 #endif /* !ARRAY_SIZE */
83 static int lfs_setstripe(int argc, char **argv);
84 static int lfs_find(int argc, char **argv);
85 static int lfs_getstripe(int argc, char **argv);
86 static int lfs_getdirstripe(int argc, char **argv);
87 static int lfs_setdirstripe(int argc, char **argv);
88 static int lfs_rmentry(int argc, char **argv);
89 static int lfs_osts(int argc, char **argv);
90 static int lfs_mdts(int argc, char **argv);
91 static int lfs_df(int argc, char **argv);
92 static int lfs_getname(int argc, char **argv);
93 static int lfs_check(int argc, char **argv);
94 #ifdef HAVE_SYS_QUOTA_H
95 static int lfs_setquota(int argc, char **argv);
96 static int lfs_quota(int argc, char **argv);
98 static int lfs_flushctx(int argc, char **argv);
99 static int lfs_join(int argc, char **argv);
100 static int lfs_lsetfacl(int argc, char **argv);
101 static int lfs_lgetfacl(int argc, char **argv);
102 static int lfs_rsetfacl(int argc, char **argv);
103 static int lfs_rgetfacl(int argc, char **argv);
104 static int lfs_cp(int argc, char **argv);
105 static int lfs_ls(int argc, char **argv);
106 static int lfs_poollist(int argc, char **argv);
107 static int lfs_changelog(int argc, char **argv);
108 static int lfs_changelog_clear(int argc, char **argv);
109 static int lfs_fid2path(int argc, char **argv);
110 static int lfs_path2fid(int argc, char **argv);
111 static int lfs_data_version(int argc, char **argv);
112 static int lfs_hsm_state(int argc, char **argv);
113 static int lfs_hsm_set(int argc, char **argv);
114 static int lfs_hsm_clear(int argc, char **argv);
115 static int lfs_hsm_action(int argc, char **argv);
116 static int lfs_hsm_archive(int argc, char **argv);
117 static int lfs_hsm_restore(int argc, char **argv);
118 static int lfs_hsm_release(int argc, char **argv);
119 static int lfs_hsm_remove(int argc, char **argv);
120 static int lfs_hsm_cancel(int argc, char **argv);
121 static int lfs_swap_layouts(int argc, char **argv);
122 static int lfs_mv(int argc, char **argv);
123 static int lfs_ladvise(int argc, char **argv);
125 /* Setstripe and migrate share mostly the same parameters */
126 #define SSM_CMD_COMMON(cmd) \
127 "usage: "cmd" [--stripe-count|-c <stripe_count>]\n" \
128 " [--stripe-index|-i <start_ost_idx>]\n" \
129 " [--stripe-size|-S <stripe_size>]\n" \
130 " [--pool|-p <pool_name>]\n" \
131 " [--ost-list|-o <ost_indices>]\n"
133 #define SSM_HELP_COMMON \
134 "\tstripe_size: Number of bytes on each OST (0 filesystem default)\n" \
135 "\t Can be specified with k, m or g (in KB, MB and GB\n" \
136 "\t respectively)\n" \
137 "\tstart_ost_idx: OST index of first stripe (-1 default)\n" \
138 "\tstripe_count: Number of OSTs to stripe over (0 default, -1 all)\n" \
139 "\tpool_name: Name of OST pool to use (default none)\n" \
140 "\tost_indices: List of OST indices, can be repeated multiple times\n"\
141 "\t Indices be specified in a format of:\n" \
142 "\t -o <ost_1>,<ost_i>-<ost_j>,<ost_n>\n" \
144 "\t -o <ost_1> -o <ost_i>-<ost_j> -o <ost_n>\n" \
145 "\t If --pool is set with --ost-list, then the OSTs\n" \
146 "\t must be the members of the pool."
148 #define SETSTRIPE_USAGE \
149 SSM_CMD_COMMON("setstripe") \
150 " <directory|filename>\n" \
153 #define MIGRATE_USAGE \
154 SSM_CMD_COMMON("migrate ") \
156 " [--non-block|-n]\n" \
160 "\tblock: Block file access during data migration (default)\n" \
161 "\tnon-block: Abort migrations if concurrent access is detected\n" \
163 static const char *progname;
164 static bool file_lease_supported = true;
166 /* all available commands */
167 command_t cmdlist[] = {
168 {"setstripe", lfs_setstripe, 0,
169 "Create a new file with a specific striping pattern or\n"
170 "set the default striping pattern on an existing directory or\n"
171 "delete the default striping pattern from an existing directory\n"
172 "usage: setstripe -d <directory> (to delete default striping)\n"\
175 {"getstripe", lfs_getstripe, 0,
176 "To list the striping info for a given file or files in a\n"
177 "directory or recursively for all files in a directory tree.\n"
178 "usage: getstripe [--ost|-O <uuid>] [--quiet | -q] [--verbose | -v]\n"
179 " [--stripe-count|-c] [--stripe-index|-i]\n"
180 " [--pool|-p] [--stripe-size|-S] [--directory|-d]\n"
181 " [--mdt-index|-M] [--recursive|-r] [--raw|-R]\n"
183 " <directory|filename> ..."},
184 {"setdirstripe", lfs_setdirstripe, 0,
185 "To create a striped directory on a specified MDT. This can only\n"
186 "be done on MDT0 with the right of administrator.\n"
187 "usage: setdirstripe <--count|-c stripe_count>\n"
188 " [--index|-i mdt_index] [--hash-type|-t hash_type]\n"
189 " [--default_stripe|-D ] [--mode|-m mode] <dir>\n"
190 "\tstripe_count: stripe count of the striped directory\n"
191 "\tmdt_index: MDT index of first stripe\n"
192 "\thash_type: hash type of the striped directory. Hash types:\n"
193 " fnv_1a_64 FNV-1a hash algorithm (default)\n"
194 " all_char sum of characters % MDT_COUNT (not recommended)\n"
195 "\tdefault_stripe: set default dirstripe of the directory\n"
196 "\tmode: the mode of the directory\n"},
197 {"getdirstripe", lfs_getdirstripe, 0,
198 "To list the striping info for a given directory\n"
199 "or recursively for all directories in a directory tree.\n"
200 "usage: getdirstripe [--obd|-O <uuid>] [--quiet|-q] [--verbose|-v]\n"
201 " [--count|-c ] [--index|-i ] [--raw|-R]\n"
202 " [--recursive | -r] [ --default_stripe | -D ] <dir> "},
203 {"mkdir", lfs_setdirstripe, 0,
204 "To create a striped directory on a specified MDT. This can only\n"
205 "be done on MDT0 with the right of administrator.\n"
206 "usage: mkdir <--count|-c stripe_count>\n"
207 " [--index|-i mdt_index] [--hash-type|-t hash_type]\n"
208 " [--default_stripe|-D ] [--mode|-m mode] <dir>\n"
209 "\tstripe_count: stripe count of the striped directory\n"
210 "\tmdt_index: MDT index of first stripe\n"
211 "\thash_type: hash type of the striped directory. Hash types:\n"
212 " fnv_1a_64 FNV-1a hash algorithm (default)\n"
213 " all_char sum of characters % MDT_COUNT (not recommended)\n"
214 "\tdefault_stripe: set default dirstripe of the directory\n"
215 "\tmode: the mode of the directory\n"},
216 {"rm_entry", lfs_rmentry, 0,
217 "To remove the name entry of the remote directory. Note: This\n"
218 "command will only delete the name entry, i.e. the remote directory\n"
219 "will become inaccessable after this command. This can only be done\n"
220 "by the administrator\n"
221 "usage: rm_entry <dir>\n"},
222 {"pool_list", lfs_poollist, 0,
223 "List pools or pool OSTs\n"
224 "usage: pool_list <fsname>[.<pool>] | <pathname>\n"},
225 {"find", lfs_find, 0,
226 "find files matching given attributes recursively in directory tree.\n"
227 "usage: find <directory|filename> ...\n"
228 " [[!] --atime|-A [+-]N] [[!] --ctime|-C [+-]N]\n"
229 " [[!] --mtime|-M [+-]N] [[!] --mdt|-m <uuid|index,...>]\n"
230 " [--maxdepth|-D N] [[!] --name|-n <pattern>]\n"
231 " [[!] --ost|-O <uuid|index,...>] [--print|-p] [--print0|-P]\n"
232 " [[!] --size|-s [+-]N[bkMGTPE]]\n"
233 " [[!] --stripe-count|-c [+-]<stripes>]\n"
234 " [[!] --stripe-index|-i <index,...>]\n"
235 " [[!] --stripe-size|-S [+-]N[kMGT]] [[!] --type|-t <filetype>]\n"
236 " [[!] --gid|-g|--group|-G <gid>|<gname>]\n"
237 " [[!] --uid|-u|--user|-U <uid>|<uname>] [[!] --pool <pool>]\n"
238 " [[!] --layout|-L released,raid0]\n"
239 "\t !: used before an option indicates 'NOT' requested attribute\n"
240 "\t -: used before a value indicates 'AT MOST' requested value\n"
241 "\t +: used before a value indicates 'AT LEAST' requested value\n"},
242 {"check", lfs_check, 0,
243 "Display the status of MDS or OSTs (as specified in the command)\n"
244 "or all the servers (MDS and OSTs).\n"
245 "usage: check <osts|mds|servers>"},
246 {"join", lfs_join, 0,
247 "join two lustre files into one.\n"
248 "obsolete, HEAD does not support it anymore.\n"},
249 {"osts", lfs_osts, 0, "list OSTs connected to client "
250 "[for specified path only]\n" "usage: osts [path]"},
251 {"mdts", lfs_mdts, 0, "list MDTs connected to client "
252 "[for specified path only]\n" "usage: mdts [path]"},
254 "report filesystem disk space usage or inodes usage"
255 "of each MDS and all OSDs or a batch belonging to a specific pool .\n"
256 "Usage: df [-i] [-h] [--lazy|-l] [--pool|-p <fsname>[.<pool>] [path]"},
257 {"getname", lfs_getname, 0, "list instances and specified mount points "
258 "[for specified path only]\n"
259 "Usage: getname [-h]|[path ...] "},
260 #ifdef HAVE_SYS_QUOTA_H
261 {"setquota", lfs_setquota, 0, "Set filesystem quotas.\n"
262 "usage: setquota <-u|-g> <uname>|<uid>|<gname>|<gid>\n"
263 " -b <block-softlimit> -B <block-hardlimit>\n"
264 " -i <inode-softlimit> -I <inode-hardlimit> <filesystem>\n"
265 " setquota <-u|--user|-g|--group> <uname>|<uid>|<gname>|<gid>\n"
266 " [--block-softlimit <block-softlimit>]\n"
267 " [--block-hardlimit <block-hardlimit>]\n"
268 " [--inode-softlimit <inode-softlimit>]\n"
269 " [--inode-hardlimit <inode-hardlimit>] <filesystem>\n"
270 " setquota [-t] <-u|--user|-g|--group>\n"
271 " [--block-grace <block-grace>]\n"
272 " [--inode-grace <inode-grace>] <filesystem>\n"
273 " -b can be used instead of --block-softlimit/--block-grace\n"
274 " -B can be used instead of --block-hardlimit\n"
275 " -i can be used instead of --inode-softlimit/--inode-grace\n"
276 " -I can be used instead of --inode-hardlimit\n\n"
277 "Note: The total quota space will be split into many qunits and\n"
278 " balanced over all server targets, the minimal qunit size is\n"
279 " 1M bytes for block space and 1K inodes for inode space.\n\n"
280 " Quota space rebalancing process will stop when this mininum\n"
281 " value is reached. As a result, quota exceeded can be returned\n"
282 " while many targets still have 1MB or 1K inodes of spare\n"
284 {"quota", lfs_quota, 0, "Display disk usage and limits.\n"
285 "usage: quota [-q] [-v] [-h] [-o <obd_uuid>|-i <mdt_idx>|-I "
287 " [<-u|-g> <uname>|<uid>|<gname>|<gid>] <filesystem>\n"
288 " quota [-o <obd_uuid>|-i <mdt_idx>|-I <ost_idx>] -t <-u|-g> <filesystem>"},
290 {"flushctx", lfs_flushctx, 0, "Flush security context for current user.\n"
291 "usage: flushctx [-k] [mountpoint...]"},
292 {"lsetfacl", lfs_lsetfacl, 0,
293 "Remote user setfacl for user/group on the same remote client.\n"
294 "usage: lsetfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ..."},
295 {"lgetfacl", lfs_lgetfacl, 0,
296 "Remote user getfacl for user/group on the same remote client.\n"
297 "usage: lgetfacl [-dRLPvh] file ..."},
298 {"rsetfacl", lfs_rsetfacl, 0,
299 "Remote user setfacl for user/group on other clients.\n"
300 "usage: rsetfacl [-bkndRLPvh] [{-m|-x} acl_spec] [{-M|-X} acl_file] file ..."},
301 {"rgetfacl", lfs_rgetfacl, 0,
302 "Remote user getfacl for user/group on other clients.\n"
303 "usage: rgetfacl [-dRLPvh] file ..."},
305 "Remote user copy files and directories.\n"
306 "usage: cp [OPTION]... [-T] SOURCE DEST\n\tcp [OPTION]... SOURCE... DIRECTORY\n\tcp [OPTION]... -t DIRECTORY SOURCE..."},
308 "Remote user list directory contents.\n"
309 "usage: ls [OPTION]... [FILE]..."},
310 {"changelog", lfs_changelog, 0,
311 "Show the metadata changes on an MDT."
312 "\nusage: changelog <mdtname> [startrec [endrec]]"},
313 {"changelog_clear", lfs_changelog_clear, 0,
314 "Indicate that old changelog records up to <endrec> are no longer of "
315 "interest to consumer <id>, allowing the system to free up space.\n"
316 "An <endrec> of 0 means all records.\n"
317 "usage: changelog_clear <mdtname> <id> <endrec>"},
318 {"fid2path", lfs_fid2path, 0,
319 "Resolve the full path(s) for given FID(s). For a specific hardlink "
320 "specify link number <linkno>.\n"
321 /* "For a historical link name, specify changelog record <recno>.\n" */
322 "usage: fid2path [--link <linkno>] <fsname|rootpath> <fid> ..."
323 /* [ --rec <recno> ] */ },
324 {"path2fid", lfs_path2fid, 0, "Display the fid(s) for a given path(s).\n"
325 "usage: path2fid [--parents] <path> ..."},
326 {"data_version", lfs_data_version, 0, "Display file data version for "
327 "a given path.\n" "usage: data_version -[n|r|w] <path>"},
328 {"hsm_state", lfs_hsm_state, 0, "Display the HSM information (states, "
329 "undergoing actions) for given files.\n usage: hsm_state <file> ..."},
330 {"hsm_set", lfs_hsm_set, 0, "Set HSM user flag on specified files.\n"
331 "usage: hsm_set [--norelease] [--noarchive] [--dirty] [--exists] "
332 "[--archived] [--lost] <file> ..."},
333 {"hsm_clear", lfs_hsm_clear, 0, "Clear HSM user flag on specified "
335 "usage: hsm_clear [--norelease] [--noarchive] [--dirty] [--exists] "
336 "[--archived] [--lost] <file> ..."},
337 {"hsm_action", lfs_hsm_action, 0, "Display current HSM request for "
338 "given files.\n" "usage: hsm_action <file> ..."},
339 {"hsm_archive", lfs_hsm_archive, 0,
340 "Archive file to external storage.\n"
341 "usage: hsm_archive [--filelist FILELIST] [--data DATA] [--archive NUM] "
343 {"hsm_restore", lfs_hsm_restore, 0,
344 "Restore file from external storage.\n"
345 "usage: hsm_restore [--filelist FILELIST] [--data DATA] <file> ..."},
346 {"hsm_release", lfs_hsm_release, 0,
347 "Release files from Lustre.\n"
348 "usage: hsm_release [--filelist FILELIST] [--data DATA] <file> ..."},
349 {"hsm_remove", lfs_hsm_remove, 0,
350 "Remove file copy from external storage.\n"
351 "usage: hsm_remove [--filelist FILELIST] [--data DATA]\n"
352 " [--mntpath MOUNTPATH] [--archive NUM] <file|FID> ...\n"
354 "Note: To remove files from the archive that have been deleted on\n"
355 "Lustre, set mntpath and optionally archive. In that case, all the\n"
356 "positional arguments and entries in the file list must be FIDs."
358 {"hsm_cancel", lfs_hsm_cancel, 0,
359 "Cancel requests related to specified files.\n"
360 "usage: hsm_cancel [--filelist FILELIST] [--data DATA] <file> ..."},
361 {"swap_layouts", lfs_swap_layouts, 0, "Swap layouts between 2 files.\n"
362 "usage: swap_layouts <path1> <path2>"},
363 {"migrate", lfs_setstripe, 0,
364 "migrate a directory between MDTs.\n"
365 "usage: migrate --mdt-index <mdt_idx> [--verbose|-v] "
367 "\tmdt_idx: index of the destination MDT\n"
369 "migrate file objects from one OST "
370 "layout\nto another (may be not safe with concurent writes).\n"
372 "[--stripe-count|-c] <stripe_count>\n"
373 " [--stripe-index|-i] <start_ost_index>\n"
374 " [--stripe-size|-S] <stripe_size>\n"
375 " [--pool|-p] <pool_name>\n"
376 " [--ost-list|-o] <ost_indices>\n"
378 " [--non-block|-n]\n"
379 " <file|directory>\n"
380 "\tstripe_count: number of OSTs to stripe a file over\n"
381 "\tstripe_ost_index: index of the first OST to stripe a file over\n"
382 "\tstripe_size: number of bytes to store before moving to the next OST\n"
383 "\tpool_name: name of the predefined pool of OSTs\n"
384 "\tost_indices: OSTs to stripe over, in order\n"
385 "\tblock: wait for the operation to return before continuing\n"
386 "\tnon-block: do not wait for the operation to return.\n"},
388 "To move directories between MDTs. This command is deprecated, "
389 "use \"migrate\" instead.\n"
390 "usage: mv <directory|filename> [--mdt-index|-M] <mdt_index> "
392 {"ladvise", lfs_ladvise, 0,
393 "Provide servers with advice about access patterns for a file.\n"
394 "usage: ladvise [--advice|-a ADVICE] [--start|-s START[kMGT]]\n"
395 " [--background|-b]\n"
396 " {[--end|-e END[kMGT]] | [--length|-l LENGTH[kMGT]]}\n"
398 {"help", Parser_help, 0, "help"},
399 {"exit", Parser_quit, 0, "quit"},
400 {"quit", Parser_quit, 0, "quit"},
401 {"--version", Parser_version, 0,
402 "output build version of the utility and exit"},
407 #define MIGRATION_NONBLOCK 1
410 * Internal helper for migrate_copy_data(). Check lease and report error if
413 * \param[in] fd File descriptor on which to check the lease.
414 * \param[out] lease_broken Set to true if the lease was broken.
415 * \param[in] group_locked Whether a group lock was taken or not.
416 * \param[in] path Name of the file being processed, for error
419 * \retval 0 Migration can keep on going.
420 * \retval -errno Error occurred, abort migration.
422 static int check_lease(int fd, bool *lease_broken, bool group_locked,
427 if (!file_lease_supported)
430 rc = llapi_lease_check(fd);
432 return 0; /* llapi_check_lease returns > 0 on success. */
435 fprintf(stderr, "%s: cannot migrate '%s': file busy\n",
437 rc = rc ? rc : -EAGAIN;
439 fprintf(stderr, "%s: external attempt to access file '%s' "
440 "blocked until migration ends.\n", progname, path);
443 *lease_broken = true;
447 static int migrate_copy_data(int fd_src, int fd_dst, size_t buf_size,
448 bool group_locked, const char *fname)
457 bool lease_broken = false;
459 /* Use a page-aligned buffer for direct I/O */
460 rc = posix_memalign(&buf, getpagesize(), buf_size);
465 /* read new data only if we have written all
466 * previously read data */
469 rc = check_lease(fd_src, &lease_broken,
470 group_locked, fname);
474 rsize = read(fd_src, buf, buf_size);
477 fprintf(stderr, "%s: %s: read failed: %s\n",
478 progname, fname, strerror(-rc));
488 wsize = write(fd_dst, buf + bufoff, rpos - wpos);
492 "%s: %s: write failed on volatile: %s\n",
493 progname, fname, strerror(-rc));
503 fprintf(stderr, "%s: %s: fsync failed: %s\n",
504 progname, fname, strerror(-rc));
512 static int migrate_copy_timestamps(int fdv, const struct stat *st)
514 struct timeval tv[2] = {
515 {.tv_sec = st->st_atime},
516 {.tv_sec = st->st_mtime}
519 return futimes(fdv, tv);
522 static int migrate_block(int fd, int fdv, const struct stat *st,
523 size_t buf_size, const char *name)
530 rc = llapi_get_data_version(fd, &dv1, LL_DV_RD_FLUSH);
532 fprintf(stderr, "%s: %s: cannot get dataversion: %s\n",
533 progname, name, strerror(-rc));
541 /* The grouplock blocks all concurrent accesses to the file.
542 * It has to be taken after llapi_get_data_version as it would
544 rc = llapi_group_lock(fd, gid);
546 fprintf(stderr, "%s: %s: cannot get group lock: %s\n",
547 progname, name, strerror(-rc));
551 rc = migrate_copy_data(fd, fdv, buf_size, true, name);
553 fprintf(stderr, "%s: %s: data copy failed\n", progname, name);
557 /* Make sure we keep original atime/mtime values */
558 rc = migrate_copy_timestamps(fdv, st);
560 fprintf(stderr, "%s: %s: timestamp copy failed\n",
566 * for a migration we need to check data version on file did
569 * Pass in gid=0 since we already own grouplock. */
570 rc = llapi_fswap_layouts_grouplock(fd, fdv, dv1, 0, 0,
571 SWAP_LAYOUTS_CHECK_DV1);
573 fprintf(stderr, "%s: %s: dataversion changed during copy, "
574 "migration aborted\n", progname, name);
577 fprintf(stderr, "%s: %s: cannot swap layouts: %s\n", progname,
578 name, strerror(-rc));
583 rc2 = llapi_group_unlock(fd, gid);
584 if (rc2 < 0 && rc == 0) {
585 fprintf(stderr, "%s: %s: putting group lock failed: %s\n",
586 progname, name, strerror(-rc2));
593 static int migrate_nonblock(int fd, int fdv, const struct stat *st,
594 size_t buf_size, const char *name)
600 rc = llapi_get_data_version(fd, &dv1, LL_DV_RD_FLUSH);
602 fprintf(stderr, "%s: %s: cannot get data version: %s\n",
603 progname, name, strerror(-rc));
607 rc = migrate_copy_data(fd, fdv, buf_size, false, name);
609 fprintf(stderr, "%s: %s: data copy failed\n", progname, name);
613 rc = llapi_get_data_version(fd, &dv2, LL_DV_RD_FLUSH);
615 fprintf(stderr, "%s: %s: cannot get data version: %s\n",
616 progname, name, strerror(-rc));
622 fprintf(stderr, "%s: %s: data version changed during "
628 /* Make sure we keep original atime/mtime values */
629 rc = migrate_copy_timestamps(fdv, st);
631 fprintf(stderr, "%s: %s: timestamp copy failed\n",
636 /* Atomically put lease, swap layouts and close.
637 * for a migration we need to check data version on file did
639 rc = llapi_fswap_layouts(fd, fdv, 0, 0, SWAP_LAYOUTS_CLOSE);
641 fprintf(stderr, "%s: %s: cannot swap layouts: %s\n",
642 progname, name, strerror(-rc));
649 static int lfs_migrate(char *name, __u64 migration_flags,
650 struct llapi_stripe_param *param)
654 char parent[PATH_MAX];
657 char volatile_file[sizeof(parent) +
658 LUSTRE_VOLATILE_HDR_LEN +
659 2 * sizeof(mdt_index) +
660 2 * sizeof(random_value) + 4];
663 struct lov_user_md *lum = NULL;
666 bool have_lease_rdlck = false;
670 /* find the right size for the IO and allocate the buffer */
671 lum_size = lov_user_md_size(LOV_MAX_STRIPE_COUNT, LOV_USER_MAGIC_V3);
672 lum = malloc(lum_size);
678 rc = llapi_file_get_stripe(name, lum);
679 /* failure can happen for many reasons and some may be not real errors
681 * in case of a real error, a later call will fail with better
682 * error management */
684 buf_size = 1024 * 1024;
686 buf_size = lum->lmm_stripe_size;
688 /* open file, direct io */
689 /* even if the file is only read, WR mode is nedeed to allow
690 * layout swap on fd */
691 fd = open(name, O_RDWR | O_DIRECT);
694 fprintf(stderr, "%s: %s: cannot open: %s\n", progname, name,
699 if (file_lease_supported) {
700 rc = llapi_lease_get(fd, LL_LEASE_RDLCK);
701 if (rc == -EOPNOTSUPP) {
702 /* Older servers do not support file lease.
703 * Disable related checks. This opens race conditions
704 * as explained in LU-4840 */
705 file_lease_supported = false;
707 fprintf(stderr, "%s: %s: cannot get open lease: %s\n",
708 progname, name, strerror(-rc));
711 have_lease_rdlck = true;
715 /* search for file directory pathname */
716 if (strlen(name) > sizeof(parent)-1) {
720 strncpy(parent, name, sizeof(parent));
721 ptr = strrchr(parent, '/');
723 if (getcwd(parent, sizeof(parent)) == NULL) {
734 rc = llapi_file_fget_mdtidx(fd, &mdt_index);
736 fprintf(stderr, "%s: %s: cannot get MDT index: %s\n",
737 progname, name, strerror(-rc));
742 random_value = random();
743 rc = snprintf(volatile_file, sizeof(volatile_file),
744 "%s/%s:%.4X:%.4X", parent, LUSTRE_VOLATILE_HDR,
745 mdt_index, random_value);
746 if (rc >= sizeof(volatile_file)) {
751 /* create, open a volatile file, use caching (ie no directio) */
752 fdv = llapi_file_open_param(volatile_file,
753 O_WRONLY | O_CREAT | O_EXCL | O_NOFOLLOW,
754 S_IRUSR | S_IWUSR, param);
755 } while (fdv == -EEXIST);
759 fprintf(stderr, "%s: %s: cannot create volatile file in"
761 progname, parent, strerror(-rc));
765 /* Not-owner (root?) special case.
766 * Need to set owner/group of volatile file like original.
767 * This will allow to pass related check during layout_swap.
772 fprintf(stderr, "%s: %s: cannot stat: %s\n", progname, name,
776 rc = fstat(fdv, &stv);
779 fprintf(stderr, "%s: %s: cannot stat: %s\n", progname,
780 volatile_file, strerror(errno));
783 if (st.st_uid != stv.st_uid || st.st_gid != stv.st_gid) {
784 rc = fchown(fdv, st.st_uid, st.st_gid);
787 fprintf(stderr, "%s: %s: cannot chown: %s\n", progname,
788 name, strerror(errno));
793 if (migration_flags & MIGRATION_NONBLOCK && file_lease_supported) {
794 rc = migrate_nonblock(fd, fdv, &st, buf_size, name);
796 have_lease_rdlck = false;
797 fdv = -1; /* The volatile file is closed as we put the
798 * lease in non-blocking mode. */
801 /* Blocking mode (forced if servers do not support file lease).
802 * It is also the default mode, since we cannot distinguish
803 * between a broken lease and a server that does not support
804 * atomic swap/close (LU-6785) */
805 rc = migrate_block(fd, fdv, &st, buf_size, name);
809 if (have_lease_rdlck)
826 * Parse a string containing an OST index list into an array of integers.
828 * The input string contains a comma delimited list of individual
829 * indices and ranges, for example "1,2-4,7". Add the indices into the
830 * \a osts array and remove duplicates.
832 * \param[out] osts array to store indices in
833 * \param[in] size size of \a osts array
834 * \param[in] offset starting index in \a osts
835 * \param[in] arg string containing OST index list
837 * \retval positive number of indices in \a osts
838 * \retval -EINVAL unable to parse \a arg
840 static int parse_targets(__u32 *osts, int size, int offset, char *arg)
844 int slots = size - offset;
852 while (!end_of_loop) {
860 ptr = strchrnul(arg, ',');
862 end_of_loop = *ptr == '\0';
865 start_index = strtol(arg, &endptr, 0);
866 if (endptr == arg) /* no data at all */
868 if (*endptr != '-' && *endptr != '\0') /* has invalid data */
873 end_index = start_index;
874 if (*endptr == '-') {
875 end_index = strtol(endptr + 1, &endptr, 0);
878 if (end_index < start_index)
882 for (i = start_index; i <= end_index && slots > 0; i++) {
885 /* remove duplicate */
886 for (j = 0; j < offset; j++) {
890 if (j == offset) { /* no duplicate */
895 if (slots == 0 && i < end_index)
903 if (!end_of_loop && ptr != NULL)
906 return rc < 0 ? rc : nr;
910 static int lfs_setstripe(int argc, char **argv)
912 struct llapi_stripe_param *param = NULL;
913 struct find_param migrate_mdt_param = {
920 unsigned long long st_size;
921 int st_offset, st_count;
925 char *stripe_size_arg = NULL;
926 char *stripe_off_arg = NULL;
927 char *stripe_count_arg = NULL;
928 char *pool_name_arg = NULL;
929 char *mdt_idx_arg = NULL;
930 unsigned long long size_units = 1;
931 bool migrate_mode = false;
932 bool migration_block = false;
933 __u64 migration_flags = 0;
934 __u32 osts[LOV_MAX_STRIPE_COUNT] = { 0 };
937 struct option long_opts[] = {
938 /* --block is only valid in migrate mode */
939 {"block", no_argument, 0, 'b'},
940 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
941 /* This formerly implied "stripe-count", but was explicitly
942 * made "stripe-count" for consistency with other options,
943 * and to separate it from "mdt-count" when DNE arrives. */
944 {"count", required_argument, 0, 'c'},
946 {"stripe-count", required_argument, 0, 'c'},
947 {"stripe_count", required_argument, 0, 'c'},
948 {"delete", no_argument, 0, 'd'},
949 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
950 /* This formerly implied "stripe-index", but was explicitly
951 * made "stripe-index" for consistency with other options,
952 * and to separate it from "mdt-index" when DNE arrives. */
953 {"index", required_argument, 0, 'i'},
955 {"stripe-index", required_argument, 0, 'i'},
956 {"stripe_index", required_argument, 0, 'i'},
957 {"mdt-index", required_argument, 0, 'm'},
958 {"mdt_index", required_argument, 0, 'm'},
959 /* --non-block is only valid in migrate mode */
960 {"non-block", no_argument, 0, 'n'},
961 {"ost-list", required_argument, 0, 'o'},
962 {"ost_list", required_argument, 0, 'o'},
963 {"pool", required_argument, 0, 'p'},
964 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
965 /* This formerly implied "--stripe-size", but was confusing
966 * with "lfs find --size|-s", which means "file size", so use
967 * the consistent "--stripe-size|-S" for all commands. */
968 {"size", required_argument, 0, 's'},
970 {"stripe-size", required_argument, 0, 'S'},
971 {"stripe_size", required_argument, 0, 'S'},
972 /* --verbose is only valid in migrate mode */
973 {"verbose", no_argument, 0, 'v'},
981 if (strcmp(argv[0], "migrate") == 0)
984 while ((c = getopt_long(argc, argv, "bc:di:m:no:p:s:S:v",
985 long_opts, NULL)) >= 0) {
992 fprintf(stderr, "--block is valid only for"
996 migration_block = true;
999 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1000 if (strcmp(argv[optind - 1], "--count") == 0)
1001 fprintf(stderr, "warning: '--count' deprecated"
1002 ", use '--stripe-count' instead\n");
1004 stripe_count_arg = optarg;
1007 /* delete the default striping pattern */
1011 nr_osts = parse_targets(osts,
1012 sizeof(osts) / sizeof(__u32),
1016 "error: %s: bad OST indices '%s'\n",
1021 if (st_offset == -1) /* first in the command line */
1022 st_offset = osts[0];
1025 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1026 if (strcmp(argv[optind - 1], "--index") == 0)
1027 fprintf(stderr, "warning: '--index' deprecated"
1028 ", use '--stripe-index' instead\n");
1030 stripe_off_arg = optarg;
1033 if (!migrate_mode) {
1034 fprintf(stderr, "--mdt-index is valid only for"
1038 mdt_idx_arg = optarg;
1041 if (!migrate_mode) {
1042 fprintf(stderr, "--non-block is valid only for"
1046 migration_flags |= MIGRATION_NONBLOCK;
1048 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1050 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1051 fprintf(stderr, "warning: '--size|-s' deprecated, "
1052 "use '--stripe-size|-S' instead\n");
1054 #endif /* LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0) */
1056 stripe_size_arg = optarg;
1059 pool_name_arg = optarg;
1062 if (!migrate_mode) {
1063 fprintf(stderr, "--verbose is valid only for"
1067 migrate_mdt_param.fp_verbose = VERBOSE_DETAIL;
1074 fname = argv[optind];
1077 (stripe_size_arg != NULL || stripe_off_arg != NULL ||
1078 stripe_count_arg != NULL || pool_name_arg != NULL)) {
1079 fprintf(stderr, "error: %s: cannot specify -d with "
1080 "-s, -c, -o, or -p options\n",
1085 if (optind == argc) {
1086 fprintf(stderr, "error: %s: missing filename|dirname\n",
1091 if (mdt_idx_arg != NULL && optind > 3) {
1092 fprintf(stderr, "error: %s: cannot specify -m with other "
1093 "options\n", argv[0]);
1097 if ((migration_flags & MIGRATION_NONBLOCK) && migration_block) {
1099 "error: %s: cannot specify --non-block and --block\n",
1104 if (pool_name_arg != NULL) {
1108 ptr = strchr(pool_name_arg, '.');
1110 ptr = pool_name_arg;
1112 if ((ptr - pool_name_arg) == 0) {
1113 fprintf(stderr, "error: %s: fsname is empty "
1114 "in pool name '%s'\n",
1115 argv[0], pool_name_arg);
1122 rc = lustre_is_poolname_valid(ptr, 1, LOV_MAXPOOLNAME);
1124 fprintf(stderr, "error: %s: poolname '%s' is "
1126 argv[0], pool_name_arg);
1128 } else if (rc == -2) {
1129 fprintf(stderr, "error: %s: pool name '%s' is too long "
1130 "(max is %d characters)\n",
1131 argv[0], pool_name_arg, LOV_MAXPOOLNAME);
1133 } else if (rc > 0) {
1134 fprintf(stderr, "error: %s: char '%c' not allowed in "
1136 argv[0], rc, pool_name_arg);
1141 /* get the stripe size */
1142 if (stripe_size_arg != NULL) {
1143 result = llapi_parse_size(stripe_size_arg, &st_size,
1146 fprintf(stderr, "error: %s: bad stripe size '%s'\n",
1147 argv[0], stripe_size_arg);
1151 /* get the stripe offset */
1152 if (stripe_off_arg != NULL) {
1153 st_offset = strtol(stripe_off_arg, &end, 0);
1155 fprintf(stderr, "error: %s: bad stripe offset '%s'\n",
1156 argv[0], stripe_off_arg);
1160 /* get the stripe count */
1161 if (stripe_count_arg != NULL) {
1162 st_count = strtoul(stripe_count_arg, &end, 0);
1164 fprintf(stderr, "error: %s: bad stripe count '%s'\n",
1165 argv[0], stripe_count_arg);
1170 if (mdt_idx_arg != NULL) {
1171 /* initialize migrate mdt parameters */
1172 migrate_mdt_param.fp_mdt_index = strtoul(mdt_idx_arg, &end, 0);
1174 fprintf(stderr, "error: %s: bad MDT index '%s'\n",
1175 argv[0], mdt_idx_arg);
1178 migrate_mdt_param.fp_migrate = 1;
1180 /* initialize stripe parameters */
1181 param = calloc(1, offsetof(typeof(*param), lsp_osts[nr_osts]));
1182 if (param == NULL) {
1183 fprintf(stderr, "error: %s: run out of memory\n",
1188 param->lsp_stripe_size = st_size;
1189 param->lsp_stripe_offset = st_offset;
1190 param->lsp_stripe_count = st_count;
1191 param->lsp_stripe_pattern = 0;
1192 param->lsp_pool = pool_name_arg;
1193 param->lsp_is_specific = false;
1195 if (st_count > 0 && nr_osts != st_count) {
1196 fprintf(stderr, "error: %s: stripe count '%d' "
1197 "doesn't match the number of OSTs: %d\n"
1198 , argv[0], st_count, nr_osts);
1203 param->lsp_is_specific = true;
1204 param->lsp_stripe_count = nr_osts;
1205 memcpy(param->lsp_osts, osts, sizeof(*osts) * nr_osts);
1209 for (fname = argv[optind]; fname != NULL; fname = argv[++optind]) {
1210 if (!migrate_mode) {
1211 result = llapi_file_open_param(fname,
1218 } else if (mdt_idx_arg != NULL) {
1219 result = llapi_migrate_mdt(fname, &migrate_mdt_param);
1221 result = lfs_migrate(fname, migration_flags, param);
1224 /* Save the first error encountered. */
1227 fprintf(stderr, "error: %s: %s file '%s' failed: %s\n",
1228 argv[0], migrate_mode ? "migrate" : "create",
1230 pool_name_arg != NULL && result == EINVAL ?
1231 "OST not in pool?" : strerror(errno));
1240 static int lfs_poollist(int argc, char **argv)
1245 return llapi_poollist(argv[1]);
1248 static int set_time(time_t *time, time_t *set, char *str)
1255 else if (str[0] == '-')
1261 t = strtol(str, NULL, 0);
1262 if (*time < t * 24 * 60 * 60) {
1265 fprintf(stderr, "Wrong time '%s' is specified.\n", str);
1269 *set = *time - t * 24 * 60 * 60;
1276 static int name2id(unsigned int *id, char *name, int type)
1279 struct passwd *entry;
1281 if (!(entry = getpwnam(name))) {
1287 *id = entry->pw_uid;
1289 struct group *entry;
1291 if (!(entry = getgrnam(name))) {
1297 *id = entry->gr_gid;
1303 static int id2name(char **name, unsigned int id, int type)
1306 struct passwd *entry;
1308 if (!(entry = getpwuid(id))) {
1314 *name = entry->pw_name;
1316 struct group *entry;
1318 if (!(entry = getgrgid(id))) {
1324 *name = entry->gr_name;
1330 static int name2layout(__u32 *layout, char *name)
1335 for (ptr = name; ; ptr = NULL) {
1336 lyt = strtok(ptr, ",");
1339 if (strcmp(lyt, "released") == 0)
1340 *layout |= LOV_PATTERN_F_RELEASED;
1341 else if (strcmp(lyt, "raid0") == 0)
1342 *layout |= LOV_PATTERN_RAID0;
1349 #define FIND_POOL_OPT 3
1350 static int lfs_find(int argc, char **argv)
1355 struct find_param param = {
1359 struct option long_opts[] = {
1360 {"atime", required_argument, 0, 'A'},
1361 {"stripe-count", required_argument, 0, 'c'},
1362 {"stripe_count", required_argument, 0, 'c'},
1363 {"ctime", required_argument, 0, 'C'},
1364 {"maxdepth", required_argument, 0, 'D'},
1365 {"gid", required_argument, 0, 'g'},
1366 {"group", required_argument, 0, 'G'},
1367 {"stripe-index", required_argument, 0, 'i'},
1368 {"stripe_index", required_argument, 0, 'i'},
1369 {"layout", required_argument, 0, 'L'},
1370 {"mdt", required_argument, 0, 'm'},
1371 {"mtime", required_argument, 0, 'M'},
1372 {"name", required_argument, 0, 'n'},
1373 /* reserve {"or", no_argument, , 0, 'o'}, to match find(1) */
1374 {"obd", required_argument, 0, 'O'},
1375 {"ost", required_argument, 0, 'O'},
1376 /* no short option for pool, p/P already used */
1377 {"pool", required_argument, 0, FIND_POOL_OPT},
1378 {"print0", no_argument, 0, 'p'},
1379 {"print", no_argument, 0, 'P'},
1380 {"size", required_argument, 0, 's'},
1381 {"stripe-size", required_argument, 0, 'S'},
1382 {"stripe_size", required_argument, 0, 'S'},
1383 {"type", required_argument, 0, 't'},
1384 {"uid", required_argument, 0, 'u'},
1385 {"user", required_argument, 0, 'U'},
1398 /* when getopt_long_only() hits '!' it returns 1, puts "!" in optarg */
1399 while ((c = getopt_long_only(argc, argv,
1400 "-A:c:C:D:g:G:i:L:m:M:n:O:Ppqrs:S:t:u:U:v",
1401 long_opts, NULL)) >= 0) {
1406 /* '!' is part of option */
1407 /* when getopt_long_only() finds a string which is not
1408 * an option nor a known option argument it returns 1
1409 * in that case if we already have found pathstart and pathend
1410 * (i.e. we have the list of pathnames),
1411 * the only supported value is "!"
1413 isoption = (c != 1) || (strcmp(optarg, "!") == 0);
1414 if (!isoption && pathend != -1) {
1415 fprintf(stderr, "err: %s: filename|dirname must either "
1416 "precede options or follow options\n",
1421 if (!isoption && pathstart == -1)
1422 pathstart = optind - 1;
1423 if (isoption && pathstart != -1 && pathend == -1)
1424 pathend = optind - 2;
1430 /* unknown; opt is "!" or path component,
1431 * checking done above.
1433 if (strcmp(optarg, "!") == 0)
1437 xtime = ¶m.fp_atime;
1438 xsign = ¶m.fp_asign;
1439 param.fp_exclude_atime = !!neg_opt;
1440 /* no break, this falls through to 'C' for ctime */
1443 xtime = ¶m.fp_ctime;
1444 xsign = ¶m.fp_csign;
1445 param.fp_exclude_ctime = !!neg_opt;
1447 /* no break, this falls through to 'M' for mtime */
1450 xtime = ¶m.fp_mtime;
1451 xsign = ¶m.fp_msign;
1452 param.fp_exclude_mtime = !!neg_opt;
1454 rc = set_time(&t, xtime, optarg);
1455 if (rc == INT_MAX) {
1463 if (optarg[0] == '+') {
1464 param.fp_stripe_count_sign = -1;
1466 } else if (optarg[0] == '-') {
1467 param.fp_stripe_count_sign = 1;
1471 param.fp_stripe_count = strtoul(optarg, &endptr, 0);
1472 if (*endptr != '\0') {
1473 fprintf(stderr,"error: bad stripe_count '%s'\n",
1478 param.fp_check_stripe_count = 1;
1479 param.fp_exclude_stripe_count = !!neg_opt;
1482 param.fp_max_depth = strtol(optarg, 0, 0);
1486 rc = name2id(¶m.fp_gid, optarg, GROUP);
1488 param.fp_gid = strtoul(optarg, &endptr, 10);
1489 if (*endptr != '\0') {
1490 fprintf(stderr, "Group/GID: %s cannot "
1491 "be found.\n", optarg);
1496 param.fp_exclude_gid = !!neg_opt;
1497 param.fp_check_gid = 1;
1500 ret = name2layout(¶m.fp_layout, optarg);
1503 param.fp_exclude_layout = !!neg_opt;
1504 param.fp_check_layout = 1;
1508 rc = name2id(¶m.fp_uid, optarg, USER);
1510 param.fp_uid = strtoul(optarg, &endptr, 10);
1511 if (*endptr != '\0') {
1512 fprintf(stderr, "User/UID: %s cannot "
1513 "be found.\n", optarg);
1518 param.fp_exclude_uid = !!neg_opt;
1519 param.fp_check_uid = 1;
1522 if (strlen(optarg) > LOV_MAXPOOLNAME) {
1524 "Pool name %s is too long"
1525 " (max is %d)\n", optarg,
1530 /* we do check for empty pool because empty pool
1531 * is used to find V1 lov attributes */
1532 strncpy(param.fp_poolname, optarg, LOV_MAXPOOLNAME);
1533 param.fp_poolname[LOV_MAXPOOLNAME] = '\0';
1534 param.fp_exclude_pool = !!neg_opt;
1535 param.fp_check_pool = 1;
1538 param.fp_pattern = (char *)optarg;
1539 param.fp_exclude_pattern = !!neg_opt;
1544 char *buf, *token, *next, *p;
1548 buf = strdup(optarg);
1554 param.fp_exclude_obd = !!neg_opt;
1557 while (token && *token) {
1558 token = strchr(token, ',');
1565 param.fp_exclude_mdt = !!neg_opt;
1566 param.fp_num_alloc_mdts += len;
1567 tmp = realloc(param.fp_mdt_uuid,
1568 param.fp_num_alloc_mdts *
1569 sizeof(*param.fp_mdt_uuid));
1575 param.fp_mdt_uuid = tmp;
1577 param.fp_exclude_obd = !!neg_opt;
1578 param.fp_num_alloc_obds += len;
1579 tmp = realloc(param.fp_obd_uuid,
1580 param.fp_num_alloc_obds *
1581 sizeof(*param.fp_obd_uuid));
1587 param.fp_obd_uuid = tmp;
1589 for (token = buf; token && *token; token = next) {
1590 struct obd_uuid *puuid;
1593 ¶m.fp_mdt_uuid[param.fp_num_mdts++];
1596 ¶m.fp_obd_uuid[param.fp_num_obds++];
1598 p = strchr(token, ',');
1605 if (strlen(token) > sizeof(puuid->uuid) - 1) {
1610 strncpy(puuid->uuid, token,
1611 sizeof(puuid->uuid));
1619 param.fp_zero_end = 1;
1624 if (optarg[0] == '+') {
1625 param.fp_size_sign = -1;
1627 } else if (optarg[0] == '-') {
1628 param.fp_size_sign = 1;
1632 ret = llapi_parse_size(optarg, ¶m.fp_size,
1633 ¶m.fp_size_units, 0);
1635 fprintf(stderr, "error: bad file size '%s'\n",
1639 param.fp_check_size = 1;
1640 param.fp_exclude_size = !!neg_opt;
1643 if (optarg[0] == '+') {
1644 param.fp_stripe_size_sign = -1;
1646 } else if (optarg[0] == '-') {
1647 param.fp_stripe_size_sign = 1;
1651 ret = llapi_parse_size(optarg, ¶m.fp_stripe_size,
1652 ¶m.fp_stripe_size_units, 0);
1654 fprintf(stderr, "error: bad stripe_size '%s'\n",
1658 param.fp_check_stripe_size = 1;
1659 param.fp_exclude_stripe_size = !!neg_opt;
1662 param.fp_exclude_type = !!neg_opt;
1663 switch (optarg[0]) {
1665 param.fp_type = S_IFBLK;
1668 param.fp_type = S_IFCHR;
1671 param.fp_type = S_IFDIR;
1674 param.fp_type = S_IFREG;
1677 param.fp_type = S_IFLNK;
1680 param.fp_type = S_IFIFO;
1683 param.fp_type = S_IFSOCK;
1686 fprintf(stderr, "error: %s: bad type '%s'\n",
1698 if (pathstart == -1) {
1699 fprintf(stderr, "error: %s: no filename|pathname\n",
1703 } else if (pathend == -1) {
1709 rc = llapi_find(argv[pathstart], ¶m);
1710 if (rc != 0 && ret == 0)
1712 } while (++pathstart < pathend);
1715 fprintf(stderr, "error: %s failed for %s.\n",
1716 argv[0], argv[optind - 1]);
1718 if (param.fp_obd_uuid && param.fp_num_alloc_obds)
1719 free(param.fp_obd_uuid);
1721 if (param.fp_mdt_uuid && param.fp_num_alloc_mdts)
1722 free(param.fp_mdt_uuid);
1727 static int lfs_getstripe_internal(int argc, char **argv,
1728 struct find_param *param)
1730 struct option long_opts[] = {
1731 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1732 /* This formerly implied "stripe-count", but was explicitly
1733 * made "stripe-count" for consistency with other options,
1734 * and to separate it from "mdt-count" when DNE arrives. */
1735 {"count", no_argument, 0, 'c'},
1737 {"stripe-count", no_argument, 0, 'c'},
1738 {"stripe_count", no_argument, 0, 'c'},
1739 {"directory", no_argument, 0, 'd'},
1740 {"default", no_argument, 0, 'D'},
1741 {"generation", no_argument, 0, 'g'},
1742 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1743 /* This formerly implied "stripe-index", but was explicitly
1744 * made "stripe-index" for consistency with other options,
1745 * and to separate it from "mdt-index" when DNE arrives. */
1746 {"index", no_argument, 0, 'i'},
1748 {"stripe-index", no_argument, 0, 'i'},
1749 {"stripe_index", no_argument, 0, 'i'},
1750 {"layout", no_argument, 0, 'L'},
1751 {"mdt-index", no_argument, 0, 'M'},
1752 {"mdt_index", no_argument, 0, 'M'},
1753 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1754 /* This formerly implied "stripe-index", but was confusing
1755 * with "file offset" (which will eventually be needed for
1756 * with different layouts by offset), so deprecate it. */
1757 {"offset", no_argument, 0, 'o'},
1759 {"obd", required_argument, 0, 'O'},
1760 {"ost", required_argument, 0, 'O'},
1761 {"pool", no_argument, 0, 'p'},
1762 {"quiet", no_argument, 0, 'q'},
1763 {"recursive", no_argument, 0, 'r'},
1764 {"raw", no_argument, 0, 'R'},
1765 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1766 /* This formerly implied "--stripe-size", but was confusing
1767 * with "lfs find --size|-s", which means "file size", so use
1768 * the consistent "--stripe-size|-S" for all commands. */
1769 {"size", no_argument, 0, 's'},
1771 {"stripe-size", no_argument, 0, 'S'},
1772 {"stripe_size", no_argument, 0, 'S'},
1773 {"verbose", no_argument, 0, 'v'},
1778 while ((c = getopt_long(argc, argv, "cdDghiLMoO:pqrRsSv",
1779 long_opts, NULL)) != -1) {
1782 if (param->fp_obd_uuid) {
1784 "error: %s: only one obduuid allowed",
1788 param->fp_obd_uuid = (struct obd_uuid *)optarg;
1794 param->fp_max_depth = 0;
1797 param->fp_get_default_lmv = 1;
1800 param->fp_recursive = 1;
1803 param->fp_verbose = VERBOSE_ALL | VERBOSE_DETAIL;
1806 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1807 if (strcmp(argv[optind - 1], "--count") == 0)
1808 fprintf(stderr, "warning: '--count' deprecated,"
1809 " use '--stripe-count' instead\n");
1811 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1812 param->fp_verbose |= VERBOSE_COUNT;
1813 param->fp_max_depth = 0;
1816 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1818 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1819 fprintf(stderr, "warning: '--size|-s' deprecated, "
1820 "use '--stripe-size|-S' instead\n");
1822 #endif /* LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0) */
1824 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1825 param->fp_verbose |= VERBOSE_SIZE;
1826 param->fp_max_depth = 0;
1829 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1831 fprintf(stderr, "warning: '--offset|-o' deprecated, "
1832 "use '--stripe-index|-i' instead\n");
1835 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1836 if (strcmp(argv[optind - 1], "--index") == 0)
1837 fprintf(stderr, "warning: '--index' deprecated"
1838 ", use '--stripe-index' instead\n");
1840 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1841 param->fp_verbose |= VERBOSE_OFFSET;
1842 param->fp_max_depth = 0;
1846 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1847 param->fp_verbose |= VERBOSE_POOL;
1848 param->fp_max_depth = 0;
1852 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1853 param->fp_verbose |= VERBOSE_GENERATION;
1854 param->fp_max_depth = 0;
1858 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1859 param->fp_verbose |= VERBOSE_LAYOUT;
1860 param->fp_max_depth = 0;
1864 if (!(param->fp_verbose & VERBOSE_DETAIL))
1865 param->fp_max_depth = 0;
1866 param->fp_verbose |= VERBOSE_MDTINDEX;
1879 if (param->fp_recursive)
1880 param->fp_max_depth = -1;
1882 if (!param->fp_verbose)
1883 param->fp_verbose = VERBOSE_ALL;
1884 if (param->fp_quiet)
1885 param->fp_verbose = VERBOSE_OBJID;
1888 rc = llapi_getstripe(argv[optind], param);
1889 } while (++optind < argc && !rc);
1892 fprintf(stderr, "error: %s failed for %s.\n",
1893 argv[0], argv[optind - 1]);
1897 static int lfs_tgts(int argc, char **argv)
1899 char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
1900 struct find_param param;
1901 int index = 0, rc=0;
1906 if (argc == 2 && !realpath(argv[1], path)) {
1908 fprintf(stderr, "error: invalid path '%s': %s\n",
1909 argv[1], strerror(-rc));
1913 while (!llapi_search_mounts(path, index++, mntdir, NULL)) {
1914 /* Check if we have a mount point */
1915 if (mntdir[0] == '\0')
1918 memset(¶m, 0, sizeof(param));
1919 if (!strcmp(argv[0], "mdts"))
1920 param.fp_get_lmv = 1;
1922 rc = llapi_ostlist(mntdir, ¶m);
1924 fprintf(stderr, "error: %s: failed on %s\n",
1927 if (path[0] != '\0')
1929 memset(mntdir, 0, PATH_MAX);
1935 static int lfs_getstripe(int argc, char **argv)
1937 struct find_param param = { 0 };
1939 param.fp_max_depth = 1;
1940 return lfs_getstripe_internal(argc, argv, ¶m);
1944 static int lfs_getdirstripe(int argc, char **argv)
1946 struct find_param param = { 0 };
1948 param.fp_get_lmv = 1;
1949 return lfs_getstripe_internal(argc, argv, ¶m);
1953 static int lfs_setdirstripe(int argc, char **argv)
1957 unsigned int stripe_offset = -1;
1958 unsigned int stripe_count = 1;
1959 enum lmv_hash_type hash_type;
1962 char *stripe_offset_opt = NULL;
1963 char *stripe_count_opt = NULL;
1964 char *stripe_hash_opt = NULL;
1965 char *mode_opt = NULL;
1966 bool default_stripe = false;
1967 mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
1968 mode_t previous_mode = 0;
1969 bool delete = false;
1971 struct option long_opts[] = {
1972 {"count", required_argument, 0, 'c'},
1973 {"delete", no_argument, 0, 'd'},
1974 {"index", required_argument, 0, 'i'},
1975 {"mode", required_argument, 0, 'm'},
1976 {"hash-type", required_argument, 0, 't'},
1977 {"default_stripe", no_argument, 0, 'D'},
1981 while ((c = getopt_long(argc, argv, "c:dDi:m:t:", long_opts,
1988 stripe_count_opt = optarg;
1992 default_stripe = true;
1995 default_stripe = true;
1998 stripe_offset_opt = optarg;
2004 stripe_hash_opt = optarg;
2007 fprintf(stderr, "error: %s: option '%s' "
2009 argv[0], argv[optind - 1]);
2014 if (optind == argc) {
2015 fprintf(stderr, "error: %s: missing dirname\n",
2020 if (!delete && stripe_offset_opt == NULL && stripe_count_opt == NULL) {
2021 fprintf(stderr, "error: %s: missing stripe offset and count.\n",
2026 if (stripe_offset_opt != NULL) {
2027 /* get the stripe offset */
2028 stripe_offset = strtoul(stripe_offset_opt, &end, 0);
2030 fprintf(stderr, "error: %s: bad stripe offset '%s'\n",
2031 argv[0], stripe_offset_opt);
2037 if (stripe_offset_opt != NULL || stripe_count_opt != NULL) {
2038 fprintf(stderr, "error: %s: cannot specify -d with -s,"
2039 " or -i options.\n", argv[0]);
2047 if (mode_opt != NULL) {
2048 mode = strtoul(mode_opt, &end, 8);
2050 fprintf(stderr, "error: %s: bad mode '%s'\n",
2054 previous_mode = umask(0);
2057 if (stripe_hash_opt == NULL ||
2058 strcmp(stripe_hash_opt, LMV_HASH_NAME_FNV_1A_64) == 0) {
2059 hash_type = LMV_HASH_TYPE_FNV_1A_64;
2060 } else if (strcmp(stripe_hash_opt, LMV_HASH_NAME_ALL_CHARS) == 0) {
2061 hash_type = LMV_HASH_TYPE_ALL_CHARS;
2063 fprintf(stderr, "error: %s: bad stripe hash type '%s'\n",
2064 argv[0], stripe_hash_opt);
2068 /* get the stripe count */
2069 if (stripe_count_opt != NULL) {
2070 stripe_count = strtoul(stripe_count_opt, &end, 0);
2072 fprintf(stderr, "error: %s: bad stripe count '%s'\n",
2073 argv[0], stripe_count_opt);
2078 dname = argv[optind];
2080 if (default_stripe) {
2081 result = llapi_dir_set_default_lmv_stripe(dname,
2082 stripe_offset, stripe_count,
2085 result = llapi_dir_create_pool(dname, mode,
2087 stripe_count, hash_type,
2092 fprintf(stderr, "error: %s: create stripe dir '%s' "
2093 "failed\n", argv[0], dname);
2096 dname = argv[++optind];
2097 } while (dname != NULL);
2099 if (mode_opt != NULL)
2100 umask(previous_mode);
2106 static int lfs_rmentry(int argc, char **argv)
2113 fprintf(stderr, "error: %s: missing dirname\n",
2119 dname = argv[index];
2120 while (dname != NULL) {
2121 result = llapi_direntry_remove(dname);
2123 fprintf(stderr, "error: %s: remove dir entry '%s' "
2124 "failed\n", argv[0], dname);
2127 dname = argv[++index];
2132 static int lfs_mv(int argc, char **argv)
2134 struct find_param param = {
2141 struct option long_opts[] = {
2142 {"mdt-index", required_argument, 0, 'M'},
2143 {"verbose", no_argument, 0, 'v'},
2147 while ((c = getopt_long(argc, argv, "M:v", long_opts, NULL)) != -1) {
2150 param.fp_mdt_index = strtoul(optarg, &end, 0);
2152 fprintf(stderr, "%s: invalid MDT index'%s'\n",
2159 param.fp_verbose = VERBOSE_DETAIL;
2163 fprintf(stderr, "error: %s: unrecognized option '%s'\n",
2164 argv[0], argv[optind - 1]);
2169 if (param.fp_mdt_index == -1) {
2170 fprintf(stderr, "%s: MDT index must be specified\n", argv[0]);
2174 if (optind >= argc) {
2175 fprintf(stderr, "%s: missing operand path\n", argv[0]);
2179 param.fp_migrate = 1;
2180 rc = llapi_migrate_mdt(argv[optind], ¶m);
2182 fprintf(stderr, "%s: cannot migrate '%s' to MDT%04x: %s\n",
2183 argv[0], argv[optind], param.fp_mdt_index,
2188 static int lfs_osts(int argc, char **argv)
2190 return lfs_tgts(argc, argv);
2193 static int lfs_mdts(int argc, char **argv)
2195 return lfs_tgts(argc, argv);
2198 #define COOK(value) \
2201 while (value > 1024) { \
2209 #define CDF "%11llu"
2210 #define HDF "%8.1f%c"
2214 static int showdf(char *mntdir, struct obd_statfs *stat,
2215 char *uuid, int ishow, int cooked,
2216 char *type, int index, int rc)
2218 long long avail, used, total;
2220 char *suffix = "KMGTPEZY";
2221 /* Note if we have >2^64 bytes/fs these buffers will need to be grown */
2222 char tbuf[3 * sizeof(__u64)];
2223 char ubuf[3 * sizeof(__u64)];
2224 char abuf[3 * sizeof(__u64)];
2225 char rbuf[3 * sizeof(__u64)];
2233 avail = stat->os_ffree;
2234 used = stat->os_files - stat->os_ffree;
2235 total = stat->os_files;
2237 int shift = cooked ? 0 : 10;
2239 avail = (stat->os_bavail * stat->os_bsize) >> shift;
2240 used = ((stat->os_blocks - stat->os_bfree) *
2241 stat->os_bsize) >> shift;
2242 total = (stat->os_blocks * stat->os_bsize) >> shift;
2245 if ((used + avail) > 0)
2246 ratio = (double)used / (double)(used + avail);
2252 cook_val = (double)total;
2255 sprintf(tbuf, HDF, cook_val, suffix[i - 1]);
2257 sprintf(tbuf, CDF, total);
2259 cook_val = (double)used;
2262 sprintf(ubuf, HDF, cook_val, suffix[i - 1]);
2264 sprintf(ubuf, CDF, used);
2266 cook_val = (double)avail;
2269 sprintf(abuf, HDF, cook_val, suffix[i - 1]);
2271 sprintf(abuf, CDF, avail);
2273 sprintf(tbuf, CDF, total);
2274 sprintf(ubuf, CDF, used);
2275 sprintf(abuf, CDF, avail);
2278 sprintf(rbuf, RDF, (int)(ratio * 100 + 0.5));
2279 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s",
2280 uuid, tbuf, ubuf, abuf, rbuf, mntdir);
2282 printf("[%s:%d]\n", type, index);
2288 printf(UUF": inactive device\n", uuid);
2291 printf(UUF": %s\n", uuid, strerror(-rc));
2298 struct ll_stat_type {
2303 static int mntdf(char *mntdir, char *fsname, char *pool, int ishow,
2304 int cooked, int lazy)
2306 struct obd_statfs stat_buf, sum = { .os_bsize = 1 };
2307 struct obd_uuid uuid_buf;
2308 char *poolname = NULL;
2309 struct ll_stat_type types[] = { { LL_STATFS_LMV, "MDT" },
2310 { LL_STATFS_LOV, "OST" },
2312 struct ll_stat_type *tp;
2313 __u64 ost_ffree = 0;
2319 poolname = strchr(pool, '.');
2320 if (poolname != NULL) {
2321 if (strncmp(fsname, pool, strlen(fsname))) {
2322 fprintf(stderr, "filesystem name incorrect\n");
2331 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2332 "UUID", "Inodes", "IUsed", "IFree",
2333 "IUse%", "Mounted on");
2335 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2336 "UUID", cooked ? "bytes" : "1K-blocks",
2337 "Used", "Available", "Use%", "Mounted on");
2339 for (tp = types; tp->st_name != NULL; tp++) {
2340 for (index = 0; ; index++) {
2341 memset(&stat_buf, 0, sizeof(struct obd_statfs));
2342 memset(&uuid_buf, 0, sizeof(struct obd_uuid));
2343 type = lazy ? tp->st_op | LL_STATFS_NODELAY : tp->st_op;
2344 rc = llapi_obd_statfs(mntdir, type, index,
2345 &stat_buf, &uuid_buf);
2352 if (poolname && tp->st_op == LL_STATFS_LOV &&
2353 llapi_search_ost(fsname, poolname,
2354 obd_uuid2str(&uuid_buf)) != 1)
2357 /* the llapi_obd_statfs() call may have returned with
2358 * an error, but if it filled in uuid_buf we will at
2359 * lease use that to print out a message for that OBD.
2360 * If we didn't get anything in the uuid_buf, then fill
2361 * it in so that we can print an error message. */
2362 if (uuid_buf.uuid[0] == '\0')
2363 sprintf(uuid_buf.uuid, "%s%04x",
2364 tp->st_name, index);
2365 showdf(mntdir, &stat_buf, obd_uuid2str(&uuid_buf),
2366 ishow, cooked, tp->st_name, index, rc);
2369 if (tp->st_op == LL_STATFS_LMV) {
2370 sum.os_ffree += stat_buf.os_ffree;
2371 sum.os_files += stat_buf.os_files;
2372 } else /* if (tp->st_op == LL_STATFS_LOV) */ {
2373 sum.os_blocks += stat_buf.os_blocks *
2375 sum.os_bfree += stat_buf.os_bfree *
2377 sum.os_bavail += stat_buf.os_bavail *
2379 ost_ffree += stat_buf.os_ffree;
2381 } else if (rc == -EINVAL || rc == -EFAULT) {
2387 /* If we don't have as many objects free on the OST as inodes
2388 * on the MDS, we reduce the total number of inodes to
2389 * compensate, so that the "inodes in use" number is correct.
2390 * Matches ll_statfs_internal() so the results are consistent. */
2391 if (ost_ffree < sum.os_ffree) {
2392 sum.os_files = (sum.os_files - sum.os_ffree) + ost_ffree;
2393 sum.os_ffree = ost_ffree;
2396 showdf(mntdir, &sum, "filesystem summary:", ishow, cooked, NULL, 0, 0);
2401 static int lfs_df(int argc, char **argv)
2403 char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
2404 int ishow = 0, cooked = 0;
2406 int c, rc = 0, index = 0;
2407 char fsname[PATH_MAX] = "", *pool_name = NULL;
2408 struct option long_opts[] = {
2409 {"pool", required_argument, 0, 'p'},
2410 {"lazy", 0, 0, 'l'},
2414 while ((c = getopt_long(argc, argv, "hilp:", long_opts, NULL)) != -1) {
2432 if (optind < argc && !realpath(argv[optind], path)) {
2434 fprintf(stderr, "error: invalid path '%s': %s\n",
2435 argv[optind], strerror(-rc));
2439 while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2440 /* Check if we have a mount point */
2441 if (mntdir[0] == '\0')
2444 rc = mntdf(mntdir, fsname, pool_name, ishow, cooked, lazy);
2445 if (rc || path[0] != '\0')
2447 fsname[0] = '\0'; /* avoid matching in next loop */
2448 mntdir[0] = '\0'; /* avoid matching in next loop */
2454 static int lfs_getname(int argc, char **argv)
2456 char mntdir[PATH_MAX] = "", path[PATH_MAX] = "", fsname[PATH_MAX] = "";
2457 int rc = 0, index = 0, c;
2458 char buf[sizeof(struct obd_uuid)];
2460 while ((c = getopt(argc, argv, "h")) != -1)
2463 if (optind == argc) { /* no paths specified, get all paths. */
2464 while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2465 rc = llapi_getname(mntdir, buf, sizeof(buf));
2468 "cannot get name for `%s': %s\n",
2469 mntdir, strerror(-rc));
2473 printf("%s %s\n", buf, mntdir);
2475 path[0] = fsname[0] = mntdir[0] = 0;
2477 } else { /* paths specified, only attempt to search these. */
2478 for (; optind < argc; optind++) {
2479 rc = llapi_getname(argv[optind], buf, sizeof(buf));
2482 "cannot get name for `%s': %s\n",
2483 argv[optind], strerror(-rc));
2487 printf("%s %s\n", buf, argv[optind]);
2493 static int lfs_check(int argc, char **argv)
2496 char mntdir[PATH_MAX] = {'\0'};
2505 obd_types[0] = obd_type1;
2506 obd_types[1] = obd_type2;
2508 if (strcmp(argv[1], "osts") == 0) {
2509 strcpy(obd_types[0], "osc");
2510 } else if (strcmp(argv[1], "mds") == 0) {
2511 strcpy(obd_types[0], "mdc");
2512 } else if (strcmp(argv[1], "servers") == 0) {
2514 strcpy(obd_types[0], "osc");
2515 strcpy(obd_types[1], "mdc");
2517 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
2522 rc = llapi_search_mounts(NULL, 0, mntdir, NULL);
2523 if (rc < 0 || mntdir[0] == '\0') {
2524 fprintf(stderr, "No suitable Lustre mount found\n");
2528 rc = llapi_target_check(num_types, obd_types, mntdir);
2530 fprintf(stderr, "error: %s: %s status failed\n",
2537 static int lfs_join(int argc, char **argv)
2539 fprintf(stderr, "join two lustre files into one.\n"
2540 "obsolete, HEAD does not support it anymore.\n");
2544 #ifdef HAVE_SYS_QUOTA_H
2545 #define ARG2INT(nr, str, msg) \
2548 nr = strtol(str, &endp, 0); \
2550 fprintf(stderr, "error: bad %s: %s\n", msg, str); \
2555 #define ADD_OVERFLOW(a,b) ((a + b) < a) ? (a = ULONG_MAX) : (a = a + b)
2557 /* Convert format time string "XXwXXdXXhXXmXXs" into seconds value
2558 * returns the value or ULONG_MAX on integer overflow or incorrect format
2560 * 1. the order of specifiers is arbitrary (may be: 5w3s or 3s5w)
2561 * 2. specifiers may be encountered multiple times (2s3s is 5 seconds)
2562 * 3. empty integer value is interpreted as 0
2564 static unsigned long str2sec(const char* timestr)
2566 const char spec[] = "smhdw";
2567 const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2568 unsigned long val = 0;
2571 if (strpbrk(timestr, spec) == NULL) {
2572 /* no specifiers inside the time string,
2573 should treat it as an integer value */
2574 val = strtoul(timestr, &tail, 10);
2575 return *tail ? ULONG_MAX : val;
2578 /* format string is XXwXXdXXhXXmXXs */
2584 v = strtoul(timestr, &tail, 10);
2585 if (v == ULONG_MAX || *tail == '\0')
2586 /* value too large (ULONG_MAX or more)
2587 or missing specifier */
2590 ptr = strchr(spec, *tail);
2592 /* unknown specifier */
2597 /* check if product will overflow the type */
2598 if (!(v < ULONG_MAX / mult[ind]))
2601 ADD_OVERFLOW(val, mult[ind] * v);
2602 if (val == ULONG_MAX)
2614 #define ARG2ULL(nr, str, def_units) \
2616 unsigned long long limit, units = def_units; \
2619 rc = llapi_parse_size(str, &limit, &units, 1); \
2621 fprintf(stderr, "error: bad limit value %s\n", str); \
2627 static inline int has_times_option(int argc, char **argv)
2631 for (i = 1; i < argc; i++)
2632 if (!strcmp(argv[i], "-t"))
2638 int lfs_setquota_times(int argc, char **argv)
2641 struct if_quotactl qctl;
2642 char *mnt, *obd_type = (char *)qctl.obd_type;
2643 struct obd_dqblk *dqb = &qctl.qc_dqblk;
2644 struct obd_dqinfo *dqi = &qctl.qc_dqinfo;
2645 struct option long_opts[] = {
2646 {"block-grace", required_argument, 0, 'b'},
2647 {"group", no_argument, 0, 'g'},
2648 {"inode-grace", required_argument, 0, 'i'},
2649 {"times", no_argument, 0, 't'},
2650 {"user", no_argument, 0, 'u'},
2654 memset(&qctl, 0, sizeof(qctl));
2655 qctl.qc_cmd = LUSTRE_Q_SETINFO;
2656 qctl.qc_type = UGQUOTA;
2658 while ((c = getopt_long(argc, argv, "b:gi:tu", long_opts, NULL)) != -1) {
2662 if (qctl.qc_type != UGQUOTA) {
2663 fprintf(stderr, "error: -u and -g can't be used "
2664 "more than once\n");
2667 qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2670 if ((dqi->dqi_bgrace = str2sec(optarg)) == ULONG_MAX) {
2671 fprintf(stderr, "error: bad block-grace: %s\n",
2675 dqb->dqb_valid |= QIF_BTIME;
2678 if ((dqi->dqi_igrace = str2sec(optarg)) == ULONG_MAX) {
2679 fprintf(stderr, "error: bad inode-grace: %s\n",
2683 dqb->dqb_valid |= QIF_ITIME;
2685 case 't': /* Yes, of course! */
2687 default: /* getopt prints error message for us when opterr != 0 */
2692 if (qctl.qc_type == UGQUOTA) {
2693 fprintf(stderr, "error: neither -u nor -g specified\n");
2697 if (optind != argc - 1) {
2698 fprintf(stderr, "error: unexpected parameters encountered\n");
2703 rc = llapi_quotactl(mnt, &qctl);
2706 fprintf(stderr, "%s %s ", obd_type,
2707 obd_uuid2str(&qctl.obd_uuid));
2708 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2715 #define BSLIMIT (1 << 0)
2716 #define BHLIMIT (1 << 1)
2717 #define ISLIMIT (1 << 2)
2718 #define IHLIMIT (1 << 3)
2720 int lfs_setquota(int argc, char **argv)
2723 struct if_quotactl qctl;
2724 char *mnt, *obd_type = (char *)qctl.obd_type;
2725 struct obd_dqblk *dqb = &qctl.qc_dqblk;
2726 struct option long_opts[] = {
2727 {"block-softlimit", required_argument, 0, 'b'},
2728 {"block-hardlimit", required_argument, 0, 'B'},
2729 {"group", required_argument, 0, 'g'},
2730 {"inode-softlimit", required_argument, 0, 'i'},
2731 {"inode-hardlimit", required_argument, 0, 'I'},
2732 {"user", required_argument, 0, 'u'},
2735 unsigned limit_mask = 0;
2738 if (has_times_option(argc, argv))
2739 return lfs_setquota_times(argc, argv);
2741 memset(&qctl, 0, sizeof(qctl));
2742 qctl.qc_cmd = LUSTRE_Q_SETQUOTA;
2743 qctl.qc_type = UGQUOTA; /* UGQUOTA makes no sense for setquota,
2744 * so it can be used as a marker that qc_type
2745 * isn't reinitialized from command line */
2747 while ((c = getopt_long(argc, argv, "b:B:g:i:I:u:", long_opts, NULL)) != -1) {
2751 if (qctl.qc_type != UGQUOTA) {
2752 fprintf(stderr, "error: -u and -g can't be used"
2753 " more than once\n");
2756 qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2757 rc = name2id(&qctl.qc_id, optarg,
2758 (qctl.qc_type == USRQUOTA) ? USER : GROUP);
2760 qctl.qc_id = strtoul(optarg, &endptr, 10);
2761 if (*endptr != '\0') {
2762 fprintf(stderr, "error: can't find id "
2763 "for name %s\n", optarg);
2769 ARG2ULL(dqb->dqb_bsoftlimit, optarg, 1024);
2770 dqb->dqb_bsoftlimit >>= 10;
2771 limit_mask |= BSLIMIT;
2772 if (dqb->dqb_bsoftlimit &&
2773 dqb->dqb_bsoftlimit <= 1024) /* <= 1M? */
2774 fprintf(stderr, "warning: block softlimit is "
2775 "smaller than the miminal qunit size, "
2776 "please see the help of setquota or "
2777 "Lustre manual for details.\n");
2780 ARG2ULL(dqb->dqb_bhardlimit, optarg, 1024);
2781 dqb->dqb_bhardlimit >>= 10;
2782 limit_mask |= BHLIMIT;
2783 if (dqb->dqb_bhardlimit &&
2784 dqb->dqb_bhardlimit <= 1024) /* <= 1M? */
2785 fprintf(stderr, "warning: block hardlimit is "
2786 "smaller than the miminal qunit size, "
2787 "please see the help of setquota or "
2788 "Lustre manual for details.\n");
2791 ARG2ULL(dqb->dqb_isoftlimit, optarg, 1);
2792 limit_mask |= ISLIMIT;
2793 if (dqb->dqb_isoftlimit &&
2794 dqb->dqb_isoftlimit <= 1024) /* <= 1K inodes? */
2795 fprintf(stderr, "warning: inode softlimit is "
2796 "smaller than the miminal qunit size, "
2797 "please see the help of setquota or "
2798 "Lustre manual for details.\n");
2801 ARG2ULL(dqb->dqb_ihardlimit, optarg, 1);
2802 limit_mask |= IHLIMIT;
2803 if (dqb->dqb_ihardlimit &&
2804 dqb->dqb_ihardlimit <= 1024) /* <= 1K inodes? */
2805 fprintf(stderr, "warning: inode hardlimit is "
2806 "smaller than the miminal qunit size, "
2807 "please see the help of setquota or "
2808 "Lustre manual for details.\n");
2810 default: /* getopt prints error message for us when opterr != 0 */
2815 if (qctl.qc_type == UGQUOTA) {
2816 fprintf(stderr, "error: neither -u nor -g was specified\n");
2820 if (limit_mask == 0) {
2821 fprintf(stderr, "error: at least one limit must be specified\n");
2825 if (optind != argc - 1) {
2826 fprintf(stderr, "error: unexpected parameters encountered\n");
2832 if ((!(limit_mask & BHLIMIT) ^ !(limit_mask & BSLIMIT)) ||
2833 (!(limit_mask & IHLIMIT) ^ !(limit_mask & ISLIMIT))) {
2834 /* sigh, we can't just set blimits/ilimits */
2835 struct if_quotactl tmp_qctl = {.qc_cmd = LUSTRE_Q_GETQUOTA,
2836 .qc_type = qctl.qc_type,
2837 .qc_id = qctl.qc_id};
2839 rc = llapi_quotactl(mnt, &tmp_qctl);
2841 fprintf(stderr, "error: setquota failed while retrieving"
2842 " current quota settings (%s)\n",
2847 if (!(limit_mask & BHLIMIT))
2848 dqb->dqb_bhardlimit = tmp_qctl.qc_dqblk.dqb_bhardlimit;
2849 if (!(limit_mask & BSLIMIT))
2850 dqb->dqb_bsoftlimit = tmp_qctl.qc_dqblk.dqb_bsoftlimit;
2851 if (!(limit_mask & IHLIMIT))
2852 dqb->dqb_ihardlimit = tmp_qctl.qc_dqblk.dqb_ihardlimit;
2853 if (!(limit_mask & ISLIMIT))
2854 dqb->dqb_isoftlimit = tmp_qctl.qc_dqblk.dqb_isoftlimit;
2856 /* Keep grace times if we have got no softlimit arguments */
2857 if ((limit_mask & BHLIMIT) && !(limit_mask & BSLIMIT)) {
2858 dqb->dqb_valid |= QIF_BTIME;
2859 dqb->dqb_btime = tmp_qctl.qc_dqblk.dqb_btime;
2862 if ((limit_mask & IHLIMIT) && !(limit_mask & ISLIMIT)) {
2863 dqb->dqb_valid |= QIF_ITIME;
2864 dqb->dqb_itime = tmp_qctl.qc_dqblk.dqb_itime;
2868 dqb->dqb_valid |= (limit_mask & (BHLIMIT | BSLIMIT)) ? QIF_BLIMITS : 0;
2869 dqb->dqb_valid |= (limit_mask & (IHLIMIT | ISLIMIT)) ? QIF_ILIMITS : 0;
2871 rc = llapi_quotactl(mnt, &qctl);
2874 fprintf(stderr, "%s %s ", obd_type,
2875 obd_uuid2str(&qctl.obd_uuid));
2876 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2883 static inline char *type2name(int check_type)
2885 if (check_type == USRQUOTA)
2887 else if (check_type == GRPQUOTA)
2893 /* Converts seconds value into format string
2894 * result is returned in buf
2896 * 1. result is in descenting order: 1w2d3h4m5s
2897 * 2. zero fields are not filled (except for p. 3): 5d1s
2898 * 3. zero seconds value is presented as "0s"
2900 static char * __sec2str(time_t seconds, char *buf)
2902 const char spec[] = "smhdw";
2903 const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2908 for (i = sizeof(mult) / sizeof(mult[0]) - 1 ; i >= 0; i--) {
2909 c = seconds / mult[i];
2911 if (c > 0 || (i == 0 && buf == tail))
2912 tail += snprintf(tail, 40-(tail-buf), "%lu%c", c, spec[i]);
2920 static void sec2str(time_t seconds, char *buf, int rc)
2927 tail = __sec2str(seconds, tail);
2929 if (rc && tail - buf < 39) {
2935 static void diff2str(time_t seconds, char *buf, time_t now)
2941 if (seconds <= now) {
2942 strcpy(buf, "none");
2945 __sec2str(seconds - now, buf);
2948 static void print_quota_title(char *name, struct if_quotactl *qctl,
2949 bool human_readable)
2951 printf("Disk quotas for %s %s (%cid %u):\n",
2952 type2name(qctl->qc_type), name,
2953 *type2name(qctl->qc_type), qctl->qc_id);
2954 printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n",
2955 "Filesystem", human_readable ? "used" : "kbytes",
2956 "quota", "limit", "grace",
2957 "files", "quota", "limit", "grace");
2960 static void kbytes2str(__u64 num, char *buf, int buflen, bool h)
2963 snprintf(buf, buflen, "%ju", (uintmax_t)num);
2966 snprintf(buf, buflen, "%5.4gP",
2967 (double)num / ((__u64)1 << 40));
2969 snprintf(buf, buflen, "%5.4gT",
2970 (double)num / (1 << 30));
2972 snprintf(buf, buflen, "%5.4gG",
2973 (double)num / (1 << 20));
2975 snprintf(buf, buflen, "%5.4gM",
2976 (double)num / (1 << 10));
2978 snprintf(buf, buflen, "%ju%s", (uintmax_t)num, "k");
2982 #define STRBUF_LEN 32
2983 static void print_quota(char *mnt, struct if_quotactl *qctl, int type,
2990 if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == Q_GETOQUOTA) {
2991 int bover = 0, iover = 0;
2992 struct obd_dqblk *dqb = &qctl->qc_dqblk;
2993 char numbuf[3][STRBUF_LEN];
2995 char strbuf[STRBUF_LEN];
2997 if (dqb->dqb_bhardlimit &&
2998 lustre_stoqb(dqb->dqb_curspace) >= dqb->dqb_bhardlimit) {
3000 } else if (dqb->dqb_bsoftlimit && dqb->dqb_btime) {
3001 if (dqb->dqb_btime > now) {
3008 if (dqb->dqb_ihardlimit &&
3009 dqb->dqb_curinodes >= dqb->dqb_ihardlimit) {
3011 } else if (dqb->dqb_isoftlimit && dqb->dqb_itime) {
3012 if (dqb->dqb_itime > now) {
3020 if (strlen(mnt) > 15)
3021 printf("%s\n%15s", mnt, "");
3023 printf("%15s", mnt);
3026 diff2str(dqb->dqb_btime, timebuf, now);
3028 kbytes2str(lustre_stoqb(dqb->dqb_curspace),
3029 strbuf, sizeof(strbuf), h);
3030 if (rc == -EREMOTEIO)
3031 sprintf(numbuf[0], "%s*", strbuf);
3033 sprintf(numbuf[0], (dqb->dqb_valid & QIF_SPACE) ?
3034 "%s" : "[%s]", strbuf);
3036 kbytes2str(dqb->dqb_bsoftlimit, strbuf, sizeof(strbuf), h);
3037 if (type == QC_GENERAL)
3038 sprintf(numbuf[1], (dqb->dqb_valid & QIF_BLIMITS) ?
3039 "%s" : "[%s]", strbuf);
3041 sprintf(numbuf[1], "%s", "-");
3043 kbytes2str(dqb->dqb_bhardlimit, strbuf, sizeof(strbuf), h);
3044 sprintf(numbuf[2], (dqb->dqb_valid & QIF_BLIMITS) ?
3045 "%s" : "[%s]", strbuf);
3047 printf(" %7s%c %6s %7s %7s",
3048 numbuf[0], bover ? '*' : ' ', numbuf[1],
3049 numbuf[2], bover > 1 ? timebuf : "-");
3052 diff2str(dqb->dqb_itime, timebuf, now);
3054 sprintf(numbuf[0], (dqb->dqb_valid & QIF_INODES) ?
3055 "%ju" : "[%ju]", (uintmax_t)dqb->dqb_curinodes);
3057 if (type == QC_GENERAL)
3058 sprintf(numbuf[1], (dqb->dqb_valid & QIF_ILIMITS) ?
3060 (uintmax_t)dqb->dqb_isoftlimit);
3062 sprintf(numbuf[1], "%s", "-");
3064 sprintf(numbuf[2], (dqb->dqb_valid & QIF_ILIMITS) ?
3065 "%ju" : "[%ju]", (uintmax_t)dqb->dqb_ihardlimit);
3067 if (type != QC_OSTIDX)
3068 printf(" %7s%c %6s %7s %7s",
3069 numbuf[0], iover ? '*' : ' ', numbuf[1],
3070 numbuf[2], iover > 1 ? timebuf : "-");
3072 printf(" %7s %7s %7s %7s", "-", "-", "-", "-");
3075 } else if (qctl->qc_cmd == LUSTRE_Q_GETINFO ||
3076 qctl->qc_cmd == Q_GETOINFO) {
3080 sec2str(qctl->qc_dqinfo.dqi_bgrace, bgtimebuf, rc);
3081 sec2str(qctl->qc_dqinfo.dqi_igrace, igtimebuf, rc);
3082 printf("Block grace time: %s; Inode grace time: %s\n",
3083 bgtimebuf, igtimebuf);
3087 static int print_obd_quota(char *mnt, struct if_quotactl *qctl, int is_mdt,
3088 bool h, __u64 *total)
3090 int rc = 0, rc1 = 0, count = 0;
3091 __u32 valid = qctl->qc_valid;
3093 rc = llapi_get_obd_count(mnt, &count, is_mdt);
3095 fprintf(stderr, "can not get %s count: %s\n",
3096 is_mdt ? "mdt": "ost", strerror(-rc));
3100 for (qctl->qc_idx = 0; qctl->qc_idx < count; qctl->qc_idx++) {
3101 qctl->qc_valid = is_mdt ? QC_MDTIDX : QC_OSTIDX;
3102 rc = llapi_quotactl(mnt, qctl);
3104 /* It is remote client case. */
3105 if (-rc == EOPNOTSUPP) {
3112 fprintf(stderr, "quotactl %s%d failed.\n",
3113 is_mdt ? "mdt": "ost", qctl->qc_idx);
3117 print_quota(obd_uuid2str(&qctl->obd_uuid), qctl,
3118 qctl->qc_valid, 0, h);
3119 *total += is_mdt ? qctl->qc_dqblk.dqb_ihardlimit :
3120 qctl->qc_dqblk.dqb_bhardlimit;
3123 qctl->qc_valid = valid;
3127 static int lfs_quota(int argc, char **argv)
3130 char *mnt, *name = NULL;
3131 struct if_quotactl qctl = { .qc_cmd = LUSTRE_Q_GETQUOTA,
3132 .qc_type = UGQUOTA };
3133 char *obd_type = (char *)qctl.obd_type;
3134 char *obd_uuid = (char *)qctl.obd_uuid.uuid;
3135 int rc, rc1 = 0, rc2 = 0, rc3 = 0,
3136 verbose = 0, pass = 0, quiet = 0, inacc;
3138 __u32 valid = QC_GENERAL, idx = 0;
3139 __u64 total_ialloc = 0, total_balloc = 0;
3140 bool human_readable = false;
3142 while ((c = getopt(argc, argv, "gi:I:o:qtuvh")) != -1) {
3145 if (qctl.qc_type != UGQUOTA) {
3146 fprintf(stderr, "error: use either -u or -g\n");
3149 qctl.qc_type = USRQUOTA;
3152 if (qctl.qc_type != UGQUOTA) {
3153 fprintf(stderr, "error: use either -u or -g\n");
3156 qctl.qc_type = GRPQUOTA;
3159 qctl.qc_cmd = LUSTRE_Q_GETINFO;
3162 valid = qctl.qc_valid = QC_UUID;
3163 strlcpy(obd_uuid, optarg, sizeof(qctl.obd_uuid));
3166 valid = qctl.qc_valid = QC_MDTIDX;
3167 idx = qctl.qc_idx = atoi(optarg);
3170 valid = qctl.qc_valid = QC_OSTIDX;
3171 idx = qctl.qc_idx = atoi(optarg);
3180 human_readable = true;
3183 fprintf(stderr, "error: %s: option '-%c' "
3184 "unrecognized\n", argv[0], c);
3189 /* current uid/gid info for "lfs quota /path/to/lustre/mount" */
3190 if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && qctl.qc_type == UGQUOTA &&
3191 optind == argc - 1) {
3193 memset(&qctl, 0, sizeof(qctl)); /* spoiled by print_*_quota */
3194 qctl.qc_cmd = LUSTRE_Q_GETQUOTA;
3195 qctl.qc_valid = valid;
3198 qctl.qc_type = USRQUOTA;
3199 qctl.qc_id = geteuid();
3201 qctl.qc_type = GRPQUOTA;
3202 qctl.qc_id = getegid();
3204 rc = id2name(&name, qctl.qc_id,
3205 (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3208 /* lfs quota -u username /path/to/lustre/mount */
3209 } else if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) {
3210 /* options should be followed by u/g-name and mntpoint */
3211 if (optind + 2 != argc || qctl.qc_type == UGQUOTA) {
3212 fprintf(stderr, "error: missing quota argument(s)\n");
3216 name = argv[optind++];
3217 rc = name2id(&qctl.qc_id, name,
3218 (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3220 qctl.qc_id = strtoul(name, &endptr, 10);
3221 if (*endptr != '\0') {
3222 fprintf(stderr, "error: can't find id for name "
3227 } else if (optind + 1 != argc || qctl.qc_type == UGQUOTA) {
3228 fprintf(stderr, "error: missing quota info argument(s)\n");
3234 rc1 = llapi_quotactl(mnt, &qctl);
3238 fprintf(stderr, "%s quotas are not enabled.\n",
3239 qctl.qc_type == USRQUOTA ? "user" : "group");
3242 fprintf(stderr, "Permission denied.\n");
3244 /* We already got a "No such file..." message. */
3247 fprintf(stderr, "Unexpected quotactl error: %s\n",
3252 if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && !quiet)
3253 print_quota_title(name, &qctl, human_readable);
3255 if (rc1 && *obd_type)
3256 fprintf(stderr, "%s %s ", obd_type, obd_uuid);
3258 if (qctl.qc_valid != QC_GENERAL)
3261 inacc = (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) &&
3262 ((qctl.qc_dqblk.dqb_valid & (QIF_LIMITS|QIF_USAGE)) !=
3263 (QIF_LIMITS|QIF_USAGE));
3265 print_quota(mnt, &qctl, QC_GENERAL, rc1, human_readable);
3267 if (qctl.qc_valid == QC_GENERAL && qctl.qc_cmd != LUSTRE_Q_GETINFO &&
3269 char strbuf[STRBUF_LEN];
3271 rc2 = print_obd_quota(mnt, &qctl, 1, human_readable,
3273 rc3 = print_obd_quota(mnt, &qctl, 0, human_readable,
3275 kbytes2str(total_balloc, strbuf, sizeof(strbuf),
3277 printf("Total allocated inode limit: %ju, total "
3278 "allocated block limit: %s\n", (uintmax_t)total_ialloc,
3282 if (rc1 || rc2 || rc3 || inacc)
3283 printf("Some errors happened when getting quota info. "
3284 "Some devices may be not working or deactivated. "
3285 "The data in \"[]\" is inaccurate.\n");
3293 #endif /* HAVE_SYS_QUOTA_H! */
3295 static int flushctx_ioctl(char *mp)
3299 fd = open(mp, O_RDONLY);
3301 fprintf(stderr, "flushctx: error open %s: %s\n",
3302 mp, strerror(errno));
3306 rc = ioctl(fd, LL_IOC_FLUSHCTX);
3308 fprintf(stderr, "flushctx: error ioctl %s: %s\n",
3309 mp, strerror(errno));
3315 static int lfs_flushctx(int argc, char **argv)
3317 int kdestroy = 0, c;
3318 char mntdir[PATH_MAX] = {'\0'};
3322 while ((c = getopt(argc, argv, "k")) != -1) {
3328 fprintf(stderr, "error: %s: option '-%c' "
3329 "unrecognized\n", argv[0], c);
3335 if ((rc = system("kdestroy > /dev/null")) != 0) {
3336 rc = WEXITSTATUS(rc);
3337 fprintf(stderr, "error destroying tickets: %d, continuing\n", rc);
3341 if (optind >= argc) {
3342 /* flush for all mounted lustre fs. */
3343 while (!llapi_search_mounts(NULL, index++, mntdir, NULL)) {
3344 /* Check if we have a mount point */
3345 if (mntdir[0] == '\0')
3348 if (flushctx_ioctl(mntdir))
3351 mntdir[0] = '\0'; /* avoid matching in next loop */
3354 /* flush fs as specified */
3355 while (optind < argc) {
3356 if (flushctx_ioctl(argv[optind++]))
3363 static int lfs_lsetfacl(int argc, char **argv)
3365 fprintf(stderr, "local client sets facl for remote client.\n"
3366 "obsolete, does not support it anymore.\n");
3370 static int lfs_lgetfacl(int argc, char **argv)
3372 fprintf(stderr, "local client gets facl for remote client.\n"
3373 "obsolete, does not support it anymore.\n");
3377 static int lfs_rsetfacl(int argc, char **argv)
3379 fprintf(stderr, "remote client sets facl for remote client.\n"
3380 "obsolete, does not support it anymore.\n");
3384 static int lfs_rgetfacl(int argc, char **argv)
3386 fprintf(stderr, "remote client gets facl for remote client.\n"
3387 "obsolete, does not support it anymore.\n");
3391 static int lfs_cp(int argc, char **argv)
3393 fprintf(stderr, "remote client copy file(s).\n"
3394 "obsolete, does not support it anymore.\n");
3398 static int lfs_ls(int argc, char **argv)
3400 fprintf(stderr, "remote client lists directory contents.\n"
3401 "obsolete, does not support it anymore.\n");
3405 static int lfs_changelog(int argc, char **argv)
3407 void *changelog_priv;
3408 struct changelog_rec *rec;
3409 long long startrec = 0, endrec = 0;
3411 struct option long_opts[] = {
3412 {"follow", no_argument, 0, 'f'},
3415 char short_opts[] = "f";
3418 while ((rc = getopt_long(argc, argv, short_opts,
3419 long_opts, NULL)) != -1) {
3427 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3428 argv[0], argv[optind - 1]);
3435 mdd = argv[optind++];
3437 startrec = strtoll(argv[optind++], NULL, 10);
3439 endrec = strtoll(argv[optind++], NULL, 10);
3441 rc = llapi_changelog_start(&changelog_priv,
3442 CHANGELOG_FLAG_BLOCK |
3443 CHANGELOG_FLAG_JOBID |
3444 (follow ? CHANGELOG_FLAG_FOLLOW : 0),
3447 fprintf(stderr, "Can't start changelog: %s\n",
3448 strerror(errno = -rc));
3452 while ((rc = llapi_changelog_recv(changelog_priv, &rec)) == 0) {
3456 if (endrec && rec->cr_index > endrec) {
3457 llapi_changelog_free(&rec);
3460 if (rec->cr_index < startrec) {
3461 llapi_changelog_free(&rec);
3465 secs = rec->cr_time >> 30;
3466 gmtime_r(&secs, &ts);
3467 printf("%ju %02d%-5s %02d:%02d:%02d.%06d %04d.%02d.%02d "
3468 "0x%x t="DFID, (uintmax_t) rec->cr_index, rec->cr_type,
3469 changelog_type2str(rec->cr_type),
3470 ts.tm_hour, ts.tm_min, ts.tm_sec,
3471 (int)(rec->cr_time & ((1<<30) - 1)),
3472 ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday,
3473 rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
3475 if (rec->cr_flags & CLF_JOBID) {
3476 struct changelog_ext_jobid *jid =
3477 changelog_rec_jobid(rec);
3479 if (jid->cr_jobid[0] != '\0')
3480 printf(" j=%s", jid->cr_jobid);
3483 if (rec->cr_namelen)
3484 printf(" p="DFID" %.*s", PFID(&rec->cr_pfid),
3485 rec->cr_namelen, changelog_rec_name(rec));
3487 if (rec->cr_flags & CLF_RENAME) {
3488 struct changelog_ext_rename *rnm =
3489 changelog_rec_rename(rec);
3491 if (!fid_is_zero(&rnm->cr_sfid))
3492 printf(" s="DFID" sp="DFID" %.*s",
3493 PFID(&rnm->cr_sfid),
3494 PFID(&rnm->cr_spfid),
3495 (int)changelog_rec_snamelen(rec),
3496 changelog_rec_sname(rec));
3500 llapi_changelog_free(&rec);
3503 llapi_changelog_fini(&changelog_priv);
3506 fprintf(stderr, "Changelog: %s\n", strerror(errno = -rc));
3508 return (rc == 1 ? 0 : rc);
3511 static int lfs_changelog_clear(int argc, char **argv)
3519 endrec = strtoll(argv[3], NULL, 10);
3521 rc = llapi_changelog_clear(argv[1], argv[2], endrec);
3523 fprintf(stderr, "%s error: %s\n", argv[0],
3524 strerror(errno = -rc));
3528 static int lfs_fid2path(int argc, char **argv)
3530 struct option long_opts[] = {
3531 {"cur", no_argument, 0, 'c'},
3532 {"link", required_argument, 0, 'l'},
3533 {"rec", required_argument, 0, 'r'},
3536 char short_opts[] = "cl:r:";
3537 char *device, *fid, *path;
3538 long long recno = -1;
3544 while ((rc = getopt_long(argc, argv, short_opts,
3545 long_opts, NULL)) != -1) {
3551 linkno = strtol(optarg, NULL, 10);
3554 recno = strtoll(optarg, NULL, 10);
3559 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3560 argv[0], argv[optind - 1]);
3568 device = argv[optind++];
3569 path = calloc(1, PATH_MAX);
3571 fprintf(stderr, "error: Not enough memory\n");
3576 while (optind < argc) {
3577 fid = argv[optind++];
3579 lnktmp = (linkno >= 0) ? linkno : 0;
3581 int oldtmp = lnktmp;
3582 long long rectmp = recno;
3584 rc2 = llapi_fid2path(device, fid, path, PATH_MAX,
3587 fprintf(stderr, "%s: error on FID %s: %s\n",
3588 argv[0], fid, strerror(errno = -rc2));
3595 fprintf(stdout, "%lld ", rectmp);
3596 if (device[0] == '/') {
3597 fprintf(stdout, "%s", device);
3598 if (device[strlen(device) - 1] != '/')
3599 fprintf(stdout, "/");
3600 } else if (path[0] == '\0') {
3601 fprintf(stdout, "/");
3603 fprintf(stdout, "%s\n", path);
3606 /* specified linkno */
3608 if (oldtmp == lnktmp)
3618 static int lfs_path2fid(int argc, char **argv)
3620 struct option long_opts[] = {
3621 {"parents", no_argument, 0, 'p'},
3625 const char short_opts[] = "p";
3626 const char *sep = "";
3629 bool show_parents = false;
3631 while ((rc = getopt_long(argc, argv, short_opts,
3632 long_opts, NULL)) != -1) {
3635 show_parents = true;
3638 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3639 argv[0], argv[optind - 1]);
3644 if (optind > argc - 1)
3646 else if (optind < argc - 1)
3650 for (path = argv + optind; *path != NULL; path++) {
3652 if (!show_parents) {
3653 err = llapi_path2fid(*path, &fid);
3655 printf("%s%s"DFID"\n",
3656 *sep != '\0' ? *path : "", sep,
3659 char name[NAME_MAX + 1];
3660 unsigned int linkno = 0;
3662 while ((err = llapi_path2parent(*path, linkno, &fid,
3663 name, sizeof(name))) == 0) {
3664 if (*sep != '\0' && linkno == 0)
3665 printf("%s%s", *path, sep);
3667 printf("%s"DFID"/%s", linkno != 0 ? "\t" : "",
3672 /* err == -ENODATA is end-of-loop */
3673 if (linkno > 0 && err == -ENODATA) {
3680 fprintf(stderr, "%s: can't get %sfid for %s: %s\n",
3681 argv[0], show_parents ? "parent " : "", *path,
3693 static int lfs_data_version(int argc, char **argv)
3700 int data_version_flags = LL_DV_RD_FLUSH; /* Read by default */
3705 while ((c = getopt(argc, argv, "nrw")) != -1) {
3708 data_version_flags = 0;
3711 data_version_flags |= LL_DV_RD_FLUSH;
3714 data_version_flags |= LL_DV_WR_FLUSH;
3723 path = argv[optind];
3724 fd = open(path, O_RDONLY);
3726 err(errno, "cannot open file %s", path);
3728 rc = llapi_get_data_version(fd, &data_version, data_version_flags);
3730 err(errno, "cannot get version for %s", path);
3732 printf("%ju" "\n", (uintmax_t)data_version);
3738 static int lfs_hsm_state(int argc, char **argv)
3743 struct hsm_user_state hus;
3751 rc = llapi_hsm_state_get(path, &hus);
3753 fprintf(stderr, "can't get hsm state for %s: %s\n",
3754 path, strerror(errno = -rc));
3758 /* Display path name and status flags */
3759 printf("%s: (0x%08x)", path, hus.hus_states);
3761 if (hus.hus_states & HS_RELEASED)
3762 printf(" released");
3763 if (hus.hus_states & HS_EXISTS)
3765 if (hus.hus_states & HS_DIRTY)
3767 if (hus.hus_states & HS_ARCHIVED)
3768 printf(" archived");
3769 /* Display user-settable flags */
3770 if (hus.hus_states & HS_NORELEASE)
3771 printf(" never_release");
3772 if (hus.hus_states & HS_NOARCHIVE)
3773 printf(" never_archive");
3774 if (hus.hus_states & HS_LOST)
3775 printf(" lost_from_hsm");
3777 if (hus.hus_archive_id != 0)
3778 printf(", archive_id:%d", hus.hus_archive_id);
3781 } while (++i < argc);
3786 #define LFS_HSM_SET 0
3787 #define LFS_HSM_CLEAR 1
3790 * Generic function to set or clear HSM flags.
3791 * Used by hsm_set and hsm_clear.
3793 * @mode if LFS_HSM_SET, set the flags, if LFS_HSM_CLEAR, clear the flags.
3795 static int lfs_hsm_change_flags(int argc, char **argv, int mode)
3797 struct option long_opts[] = {
3798 {"lost", 0, 0, 'l'},
3799 {"norelease", 0, 0, 'r'},
3800 {"noarchive", 0, 0, 'a'},
3801 {"archived", 0, 0, 'A'},
3802 {"dirty", 0, 0, 'd'},
3803 {"exists", 0, 0, 'e'},
3806 char short_opts[] = "lraAde";
3814 while ((c = getopt_long(argc, argv, short_opts,
3815 long_opts, NULL)) != -1) {
3821 mask |= HS_NOARCHIVE;
3824 mask |= HS_ARCHIVED;
3827 mask |= HS_NORELEASE;
3838 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3839 argv[0], argv[optind - 1]);
3844 /* User should have specified a flag */
3848 while (optind < argc) {
3850 path = argv[optind];
3852 /* If mode == 0, this means we apply the mask. */
3853 if (mode == LFS_HSM_SET)
3854 rc = llapi_hsm_state_set(path, mask, 0, 0);
3856 rc = llapi_hsm_state_set(path, 0, mask, 0);
3859 fprintf(stderr, "Can't change hsm flags for %s: %s\n",
3860 path, strerror(errno = -rc));
3869 static int lfs_hsm_action(int argc, char **argv)
3874 struct hsm_current_action hca;
3875 struct hsm_extent he;
3876 enum hsm_user_action hua;
3877 enum hsm_progress_states hps;
3885 rc = llapi_hsm_current_action(path, &hca);
3887 fprintf(stderr, "can't get hsm action for %s: %s\n",
3888 path, strerror(errno = -rc));
3891 he = hca.hca_location;
3892 hua = hca.hca_action;
3893 hps = hca.hca_state;
3895 printf("%s: %s", path, hsm_user_action2name(hua));
3897 /* Skip file without action */
3898 if (hca.hca_action == HUA_NONE) {
3903 printf(" %s ", hsm_progress_state2name(hps));
3905 if ((hps == HPS_RUNNING) &&
3906 (hua == HUA_ARCHIVE || hua == HUA_RESTORE))
3907 printf("(%llu bytes moved)\n",
3908 (unsigned long long)he.length);
3909 else if ((he.offset + he.length) == LUSTRE_EOF)
3910 printf("(from %llu to EOF)\n",
3911 (unsigned long long)he.offset);
3913 printf("(from %llu to %llu)\n",
3914 (unsigned long long)he.offset,
3915 (unsigned long long)(he.offset + he.length));
3917 } while (++i < argc);
3922 static int lfs_hsm_set(int argc, char **argv)
3924 return lfs_hsm_change_flags(argc, argv, LFS_HSM_SET);
3927 static int lfs_hsm_clear(int argc, char **argv)
3929 return lfs_hsm_change_flags(argc, argv, LFS_HSM_CLEAR);
3933 * Check file state and return its fid, to be used by lfs_hsm_request().
3935 * \param[in] file Path to file to check
3936 * \param[in,out] fid Pointer to allocated lu_fid struct.
3937 * \param[in,out] last_dev Pointer to last device id used.
3939 * \return 0 on success.
3941 static int lfs_hsm_prepare_file(const char *file, struct lu_fid *fid,
3947 rc = lstat(file, &st);
3949 fprintf(stderr, "Cannot stat %s: %s\n", file, strerror(errno));
3952 /* Checking for regular file as archiving as posix copytool
3953 * rejects archiving files other than regular files
3955 if (!S_ISREG(st.st_mode)) {
3956 fprintf(stderr, "error: \"%s\" is not a regular file\n", file);
3959 /* A request should be ... */
3960 if (*last_dev != st.st_dev && *last_dev != 0) {
3961 fprintf(stderr, "All files should be "
3962 "on the same filesystem: %s\n", file);
3965 *last_dev = st.st_dev;
3967 rc = llapi_path2fid(file, fid);
3969 fprintf(stderr, "Cannot read FID of %s: %s\n",
3970 file, strerror(-rc));
3976 /* Fill an HSM HUR item with a given file name.
3978 * If mntpath is set, then the filename is actually a FID, and no
3979 * lookup on the filesystem will be performed.
3981 * \param[in] hur the user request to fill
3982 * \param[in] idx index of the item inside the HUR to fill
3983 * \param[in] mntpath mountpoint of Lustre
3984 * \param[in] fname filename (if mtnpath is NULL)
3985 * or FID (if mntpath is set)
3986 * \param[in] last_dev pointer to last device id used
3988 * \retval 0 on success
3989 * \retval CMD_HELP or a negative errno on error
3991 static int fill_hur_item(struct hsm_user_request *hur, unsigned int idx,
3992 const char *mntpath, const char *fname,
3995 struct hsm_user_item *hui = &hur->hur_user_item[idx];
3998 hui->hui_extent.length = -1;
4000 if (mntpath != NULL) {
4003 rc = sscanf(fname, SFID, RFID(&hui->hui_fid));
4007 fprintf(stderr, "hsm: '%s' is not a valid FID\n",
4012 rc = lfs_hsm_prepare_file(fname, &hui->hui_fid, last_dev);
4016 hur->hur_request.hr_itemcount++;
4021 static int lfs_hsm_request(int argc, char **argv, int action)
4023 struct option long_opts[] = {
4024 {"filelist", 1, 0, 'l'},
4025 {"data", 1, 0, 'D'},
4026 {"archive", 1, 0, 'a'},
4027 {"mntpath", 1, 0, 'm'},
4031 char short_opts[] = "l:D:a:m:";
4032 struct hsm_user_request *hur, *oldhur;
4037 char *filelist = NULL;
4038 char fullpath[PATH_MAX];
4039 char *opaque = NULL;
4043 int nbfile_alloc = 0;
4044 char *some_file = NULL;
4045 char *mntpath = NULL;
4051 while ((c = getopt_long(argc, argv, short_opts,
4052 long_opts, NULL)) != -1) {
4061 if (action != HUA_ARCHIVE &&
4062 action != HUA_REMOVE) {
4064 "error: -a is supported only "
4065 "when archiving or removing\n");
4068 archive_id = atoi(optarg);
4071 if (some_file == NULL) {
4073 some_file = strdup(optarg);
4079 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
4080 argv[0], argv[optind - 1]);
4085 /* All remaining args are files, so we have at least nbfile */
4086 nbfile = argc - optind;
4088 if ((nbfile == 0) && (filelist == NULL))
4092 opaque_len = strlen(opaque);
4094 /* Alloc the request structure with enough place to store all files
4095 * from command line. */
4096 hur = llapi_hsm_user_request_alloc(nbfile, opaque_len);
4098 fprintf(stderr, "Cannot create the request: %s\n",
4102 nbfile_alloc = nbfile;
4104 hur->hur_request.hr_action = action;
4105 hur->hur_request.hr_archive_id = archive_id;
4106 hur->hur_request.hr_flags = 0;
4108 /* All remaining args are files, add them */
4109 if (nbfile != 0 && some_file == NULL)
4110 some_file = strdup(argv[optind]);
4112 for (i = 0; i < nbfile; i++) {
4113 rc = fill_hur_item(hur, i, mntpath, argv[optind + i],
4119 /* from here stop using nb_file, use hur->hur_request.hr_itemcount */
4121 /* If a filelist was specified, read the filelist from it. */
4122 if (filelist != NULL) {
4123 fp = fopen(filelist, "r");
4125 fprintf(stderr, "Cannot read the file list %s: %s\n",
4126 filelist, strerror(errno));
4131 while ((rc = getline(&line, &len, fp)) != -1) {
4132 /* If allocated buffer was too small, get something
4134 if (nbfile_alloc <= hur->hur_request.hr_itemcount) {
4137 nbfile_alloc = nbfile_alloc * 2 + 1;
4139 hur = llapi_hsm_user_request_alloc(nbfile_alloc,
4142 fprintf(stderr, "hsm: cannot allocate "
4143 "the request: %s\n",
4150 size = hur_len(oldhur);
4152 fprintf(stderr, "hsm: cannot allocate "
4153 "%u files + %u bytes data\n",
4154 oldhur->hur_request.hr_itemcount,
4155 oldhur->hur_request.hr_data_len);
4162 memcpy(hur, oldhur, size);
4167 if (line[strlen(line) - 1] == '\n')
4168 line[strlen(line) - 1] = '\0';
4170 rc = fill_hur_item(hur, hur->hur_request.hr_itemcount,
4171 mntpath, line, &last_dev);
4177 if (some_file == NULL) {
4187 /* If a --data was used, add it to the request */
4188 hur->hur_request.hr_data_len = opaque_len;
4190 memcpy(hur_data(hur), opaque, opaque_len);
4192 /* Send the HSM request */
4193 if (realpath(some_file, fullpath) == NULL) {
4194 fprintf(stderr, "Could not find path '%s': %s\n",
4195 some_file, strerror(errno));
4197 rc = llapi_hsm_request(fullpath, hur);
4199 fprintf(stderr, "Cannot send HSM request (use of %s): %s\n",
4200 some_file, strerror(-rc));
4210 static int lfs_hsm_archive(int argc, char **argv)
4212 return lfs_hsm_request(argc, argv, HUA_ARCHIVE);
4215 static int lfs_hsm_restore(int argc, char **argv)
4217 return lfs_hsm_request(argc, argv, HUA_RESTORE);
4220 static int lfs_hsm_release(int argc, char **argv)
4222 return lfs_hsm_request(argc, argv, HUA_RELEASE);
4225 static int lfs_hsm_remove(int argc, char **argv)
4227 return lfs_hsm_request(argc, argv, HUA_REMOVE);
4230 static int lfs_hsm_cancel(int argc, char **argv)
4232 return lfs_hsm_request(argc, argv, HUA_CANCEL);
4235 static int lfs_swap_layouts(int argc, char **argv)
4240 return llapi_swap_layouts(argv[1], argv[2], 0, 0,
4241 SWAP_LAYOUTS_KEEP_MTIME |
4242 SWAP_LAYOUTS_KEEP_ATIME);
4245 static const char *const ladvise_names[] = LU_LADVISE_NAMES;
4247 static enum lu_ladvise_type lfs_get_ladvice(const char *string)
4249 enum lu_ladvise_type advice;
4252 advice < ARRAY_SIZE(ladvise_names); advice++) {
4253 if (ladvise_names[advice] == NULL)
4255 if (strcmp(string, ladvise_names[advice]) == 0)
4259 return LU_LADVISE_INVALID;
4262 static int lfs_ladvise(int argc, char **argv)
4264 struct option long_opts[] = {
4265 {"advice", required_argument, 0, 'a'},
4266 {"background", no_argument, 0, 'b'},
4267 {"end", required_argument, 0, 'e'},
4268 {"start", required_argument, 0, 's'},
4269 {"length", required_argument, 0, 'l'},
4272 char short_opts[] = "a:be:l:s:";
4277 struct llapi_lu_ladvise advice;
4278 enum lu_ladvise_type advice_type = LU_LADVISE_INVALID;
4279 unsigned long long start = 0;
4280 unsigned long long end = LUSTRE_EOF;
4281 unsigned long long length = 0;
4282 unsigned long long size_units;
4283 unsigned long long flags = 0;
4286 while ((c = getopt_long(argc, argv, short_opts,
4287 long_opts, NULL)) != -1) {
4290 advice_type = lfs_get_ladvice(optarg);
4291 if (advice_type == LU_LADVISE_INVALID) {
4292 fprintf(stderr, "%s: invalid advice type "
4293 "'%s'\n", argv[0], optarg);
4294 fprintf(stderr, "Valid types:");
4296 for (advice_type = 0;
4297 advice_type < ARRAY_SIZE(ladvise_names);
4299 if (ladvise_names[advice_type] == NULL)
4301 fprintf(stderr, " %s",
4302 ladvise_names[advice_type]);
4304 fprintf(stderr, "\n");
4314 rc = llapi_parse_size(optarg, &end,
4317 fprintf(stderr, "%s: bad end offset '%s'\n",
4324 rc = llapi_parse_size(optarg, &start,
4327 fprintf(stderr, "%s: bad start offset "
4328 "'%s'\n", argv[0], optarg);
4334 rc = llapi_parse_size(optarg, &length,
4337 fprintf(stderr, "%s: bad length '%s'\n",
4345 fprintf(stderr, "%s: option '%s' unrecognized\n",
4346 argv[0], argv[optind - 1]);
4351 if (advice_type == LU_LADVISE_INVALID) {
4352 fprintf(stderr, "%s: please give an advice type\n", argv[0]);
4353 fprintf(stderr, "Valid types:");
4354 for (advice_type = 0; advice_type < ARRAY_SIZE(ladvise_names);
4356 if (ladvise_names[advice_type] == NULL)
4358 fprintf(stderr, " %s", ladvise_names[advice_type]);
4360 fprintf(stderr, "\n");
4364 if (argc <= optind) {
4365 fprintf(stderr, "%s: please give one or more file names\n",
4370 if (end != LUSTRE_EOF && length != 0 && end != start + length) {
4371 fprintf(stderr, "%s: conflicting arguments of -l and -e\n",
4376 if (end == LUSTRE_EOF && length != 0)
4377 end = start + length;
4380 fprintf(stderr, "%s: range [%llu, %llu] is invalid\n",
4381 argv[0], start, end);
4385 while (optind < argc) {
4388 path = argv[optind++];
4390 fd = open(path, O_RDONLY);
4392 fprintf(stderr, "%s: cannot open file '%s': %s\n",
4393 argv[0], path, strerror(errno));
4398 advice.lla_start = start;
4399 advice.lla_end = end;
4400 advice.lla_advice = advice_type;
4401 advice.lla_value1 = 0;
4402 advice.lla_value2 = 0;
4403 advice.lla_value3 = 0;
4404 advice.lla_value4 = 0;
4405 rc2 = llapi_ladvise(fd, flags, 1, &advice);
4408 fprintf(stderr, "%s: cannot give advice '%s' to file "
4409 "'%s': %s\n", argv[0],
4410 ladvise_names[advice_type],
4411 path, strerror(errno));
4414 if (rc == 0 && rc2 < 0)
4420 int main(int argc, char **argv)
4424 /* Ensure that liblustreapi constructor has run */
4425 if (!liblustreapi_initialized)
4426 fprintf(stderr, "liblustreapi was not properly initialized\n");
4430 Parser_init("lfs > ", cmdlist);
4432 progname = argv[0]; /* Used in error messages */
4434 rc = Parser_execarg(argc - 1, argv + 1, cmdlist);
4436 rc = Parser_commands();
4439 return rc < 0 ? -rc : rc;
4442 #ifdef _LUSTRE_IDL_H_
4443 /* Everything we need here should be included by lustreapi.h. */
4444 # error "lfs should not depend on lustre_idl.h"
4445 #endif /* _LUSTRE_IDL_H_ */