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(result));
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 param->fp_max_depth = 1;
1779 while ((c = getopt_long(argc, argv, "cdDghiLMoO:pqrRsSv",
1780 long_opts, NULL)) != -1) {
1783 if (param->fp_obd_uuid) {
1785 "error: %s: only one obduuid allowed",
1789 param->fp_obd_uuid = (struct obd_uuid *)optarg;
1795 param->fp_max_depth = 0;
1798 param->fp_get_default_lmv = 1;
1801 param->fp_recursive = 1;
1804 param->fp_verbose = VERBOSE_ALL | VERBOSE_DETAIL;
1807 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1808 if (strcmp(argv[optind - 1], "--count") == 0)
1809 fprintf(stderr, "warning: '--count' deprecated,"
1810 " use '--stripe-count' instead\n");
1812 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1813 param->fp_verbose |= VERBOSE_COUNT;
1814 param->fp_max_depth = 0;
1817 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1819 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1820 fprintf(stderr, "warning: '--size|-s' deprecated, "
1821 "use '--stripe-size|-S' instead\n");
1823 #endif /* LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0) */
1825 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1826 param->fp_verbose |= VERBOSE_SIZE;
1827 param->fp_max_depth = 0;
1830 #if LUSTRE_VERSION_CODE < OBD_OCD_VERSION(2, 9, 53, 0)
1832 fprintf(stderr, "warning: '--offset|-o' deprecated, "
1833 "use '--stripe-index|-i' instead\n");
1836 #if LUSTRE_VERSION_CODE >= OBD_OCD_VERSION(2, 6, 53, 0)
1837 if (strcmp(argv[optind - 1], "--index") == 0)
1838 fprintf(stderr, "warning: '--index' deprecated"
1839 ", use '--stripe-index' instead\n");
1841 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1842 param->fp_verbose |= VERBOSE_OFFSET;
1843 param->fp_max_depth = 0;
1847 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1848 param->fp_verbose |= VERBOSE_POOL;
1849 param->fp_max_depth = 0;
1853 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1854 param->fp_verbose |= VERBOSE_GENERATION;
1855 param->fp_max_depth = 0;
1859 if (!(param->fp_verbose & VERBOSE_DETAIL)) {
1860 param->fp_verbose |= VERBOSE_LAYOUT;
1861 param->fp_max_depth = 0;
1865 if (!(param->fp_verbose & VERBOSE_DETAIL))
1866 param->fp_max_depth = 0;
1867 param->fp_verbose |= VERBOSE_MDTINDEX;
1880 if (param->fp_recursive)
1881 param->fp_max_depth = -1;
1883 if (!param->fp_verbose)
1884 param->fp_verbose = VERBOSE_ALL;
1885 if (param->fp_quiet)
1886 param->fp_verbose = VERBOSE_OBJID;
1889 rc = llapi_getstripe(argv[optind], param);
1890 } while (++optind < argc && !rc);
1893 fprintf(stderr, "error: %s failed for %s.\n",
1894 argv[0], argv[optind - 1]);
1898 static int lfs_tgts(int argc, char **argv)
1900 char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
1901 struct find_param param;
1902 int index = 0, rc=0;
1907 if (argc == 2 && !realpath(argv[1], path)) {
1909 fprintf(stderr, "error: invalid path '%s': %s\n",
1910 argv[1], strerror(-rc));
1914 while (!llapi_search_mounts(path, index++, mntdir, NULL)) {
1915 /* Check if we have a mount point */
1916 if (mntdir[0] == '\0')
1919 memset(¶m, 0, sizeof(param));
1920 if (!strcmp(argv[0], "mdts"))
1921 param.fp_get_lmv = 1;
1923 rc = llapi_ostlist(mntdir, ¶m);
1925 fprintf(stderr, "error: %s: failed on %s\n",
1928 if (path[0] != '\0')
1930 memset(mntdir, 0, PATH_MAX);
1936 static int lfs_getstripe(int argc, char **argv)
1938 struct find_param param = { 0 };
1939 return lfs_getstripe_internal(argc, argv, ¶m);
1943 static int lfs_getdirstripe(int argc, char **argv)
1945 struct find_param param = { 0 };
1947 param.fp_get_lmv = 1;
1948 return lfs_getstripe_internal(argc, argv, ¶m);
1952 static int lfs_setdirstripe(int argc, char **argv)
1956 unsigned int stripe_offset = -1;
1957 unsigned int stripe_count = 1;
1958 enum lmv_hash_type hash_type;
1961 char *stripe_offset_opt = NULL;
1962 char *stripe_count_opt = NULL;
1963 char *stripe_hash_opt = NULL;
1964 char *mode_opt = NULL;
1965 bool default_stripe = false;
1966 mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
1967 mode_t previous_mode = 0;
1968 bool delete = false;
1970 struct option long_opts[] = {
1971 {"count", required_argument, 0, 'c'},
1972 {"delete", no_argument, 0, 'd'},
1973 {"index", required_argument, 0, 'i'},
1974 {"mode", required_argument, 0, 'm'},
1975 {"hash-type", required_argument, 0, 't'},
1976 {"default_stripe", no_argument, 0, 'D'},
1980 while ((c = getopt_long(argc, argv, "c:dDi:m:t:", long_opts,
1987 stripe_count_opt = optarg;
1991 default_stripe = true;
1994 default_stripe = true;
1997 stripe_offset_opt = optarg;
2003 stripe_hash_opt = optarg;
2006 fprintf(stderr, "error: %s: option '%s' "
2008 argv[0], argv[optind - 1]);
2013 if (optind == argc) {
2014 fprintf(stderr, "error: %s: missing dirname\n",
2019 if (!delete && stripe_offset_opt == NULL && stripe_count_opt == NULL) {
2020 fprintf(stderr, "error: %s: missing stripe offset and count.\n",
2025 if (stripe_offset_opt != NULL) {
2026 /* get the stripe offset */
2027 stripe_offset = strtoul(stripe_offset_opt, &end, 0);
2029 fprintf(stderr, "error: %s: bad stripe offset '%s'\n",
2030 argv[0], stripe_offset_opt);
2036 if (stripe_offset_opt != NULL || stripe_count_opt != NULL) {
2037 fprintf(stderr, "error: %s: cannot specify -d with -s,"
2038 " or -i options.\n", argv[0]);
2046 if (mode_opt != NULL) {
2047 mode = strtoul(mode_opt, &end, 8);
2049 fprintf(stderr, "error: %s: bad mode '%s'\n",
2053 previous_mode = umask(0);
2056 if (stripe_hash_opt == NULL ||
2057 strcmp(stripe_hash_opt, LMV_HASH_NAME_FNV_1A_64) == 0) {
2058 hash_type = LMV_HASH_TYPE_FNV_1A_64;
2059 } else if (strcmp(stripe_hash_opt, LMV_HASH_NAME_ALL_CHARS) == 0) {
2060 hash_type = LMV_HASH_TYPE_ALL_CHARS;
2062 fprintf(stderr, "error: %s: bad stripe hash type '%s'\n",
2063 argv[0], stripe_hash_opt);
2067 /* get the stripe count */
2068 if (stripe_count_opt != NULL) {
2069 stripe_count = strtoul(stripe_count_opt, &end, 0);
2071 fprintf(stderr, "error: %s: bad stripe count '%s'\n",
2072 argv[0], stripe_count_opt);
2077 dname = argv[optind];
2079 if (default_stripe) {
2080 result = llapi_dir_set_default_lmv_stripe(dname,
2081 stripe_offset, stripe_count,
2084 result = llapi_dir_create_pool(dname, mode,
2086 stripe_count, hash_type,
2091 fprintf(stderr, "error: %s: create stripe dir '%s' "
2092 "failed\n", argv[0], dname);
2095 dname = argv[++optind];
2096 } while (dname != NULL);
2098 if (mode_opt != NULL)
2099 umask(previous_mode);
2105 static int lfs_rmentry(int argc, char **argv)
2112 fprintf(stderr, "error: %s: missing dirname\n",
2118 dname = argv[index];
2119 while (dname != NULL) {
2120 result = llapi_direntry_remove(dname);
2122 fprintf(stderr, "error: %s: remove dir entry '%s' "
2123 "failed\n", argv[0], dname);
2126 dname = argv[++index];
2131 static int lfs_mv(int argc, char **argv)
2133 struct find_param param = {
2140 struct option long_opts[] = {
2141 {"mdt-index", required_argument, 0, 'M'},
2142 {"verbose", no_argument, 0, 'v'},
2146 while ((c = getopt_long(argc, argv, "M:v", long_opts, NULL)) != -1) {
2149 param.fp_mdt_index = strtoul(optarg, &end, 0);
2151 fprintf(stderr, "%s: invalid MDT index'%s'\n",
2158 param.fp_verbose = VERBOSE_DETAIL;
2162 fprintf(stderr, "error: %s: unrecognized option '%s'\n",
2163 argv[0], argv[optind - 1]);
2168 if (param.fp_mdt_index == -1) {
2169 fprintf(stderr, "%s: MDT index must be specified\n", argv[0]);
2173 if (optind >= argc) {
2174 fprintf(stderr, "%s: missing operand path\n", argv[0]);
2178 param.fp_migrate = 1;
2179 rc = llapi_migrate_mdt(argv[optind], ¶m);
2181 fprintf(stderr, "%s: cannot migrate '%s' to MDT%04x: %s\n",
2182 argv[0], argv[optind], param.fp_mdt_index,
2187 static int lfs_osts(int argc, char **argv)
2189 return lfs_tgts(argc, argv);
2192 static int lfs_mdts(int argc, char **argv)
2194 return lfs_tgts(argc, argv);
2197 #define COOK(value) \
2200 while (value > 1024) { \
2208 #define CDF "%11llu"
2209 #define HDF "%8.1f%c"
2213 static int showdf(char *mntdir, struct obd_statfs *stat,
2214 char *uuid, int ishow, int cooked,
2215 char *type, int index, int rc)
2217 long long avail, used, total;
2219 char *suffix = "KMGTPEZY";
2220 /* Note if we have >2^64 bytes/fs these buffers will need to be grown */
2221 char tbuf[3 * sizeof(__u64)];
2222 char ubuf[3 * sizeof(__u64)];
2223 char abuf[3 * sizeof(__u64)];
2224 char rbuf[3 * sizeof(__u64)];
2232 avail = stat->os_ffree;
2233 used = stat->os_files - stat->os_ffree;
2234 total = stat->os_files;
2236 int shift = cooked ? 0 : 10;
2238 avail = (stat->os_bavail * stat->os_bsize) >> shift;
2239 used = ((stat->os_blocks - stat->os_bfree) *
2240 stat->os_bsize) >> shift;
2241 total = (stat->os_blocks * stat->os_bsize) >> shift;
2244 if ((used + avail) > 0)
2245 ratio = (double)used / (double)(used + avail);
2251 cook_val = (double)total;
2254 sprintf(tbuf, HDF, cook_val, suffix[i - 1]);
2256 sprintf(tbuf, CDF, total);
2258 cook_val = (double)used;
2261 sprintf(ubuf, HDF, cook_val, suffix[i - 1]);
2263 sprintf(ubuf, CDF, used);
2265 cook_val = (double)avail;
2268 sprintf(abuf, HDF, cook_val, suffix[i - 1]);
2270 sprintf(abuf, CDF, avail);
2272 sprintf(tbuf, CDF, total);
2273 sprintf(ubuf, CDF, used);
2274 sprintf(abuf, CDF, avail);
2277 sprintf(rbuf, RDF, (int)(ratio * 100 + 0.5));
2278 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s",
2279 uuid, tbuf, ubuf, abuf, rbuf, mntdir);
2281 printf("[%s:%d]\n", type, index);
2287 printf(UUF": inactive device\n", uuid);
2290 printf(UUF": %s\n", uuid, strerror(-rc));
2297 struct ll_stat_type {
2302 static int mntdf(char *mntdir, char *fsname, char *pool, int ishow,
2303 int cooked, int lazy)
2305 struct obd_statfs stat_buf, sum = { .os_bsize = 1 };
2306 struct obd_uuid uuid_buf;
2307 char *poolname = NULL;
2308 struct ll_stat_type types[] = { { LL_STATFS_LMV, "MDT" },
2309 { LL_STATFS_LOV, "OST" },
2311 struct ll_stat_type *tp;
2312 __u64 ost_ffree = 0;
2318 poolname = strchr(pool, '.');
2319 if (poolname != NULL) {
2320 if (strncmp(fsname, pool, strlen(fsname))) {
2321 fprintf(stderr, "filesystem name incorrect\n");
2330 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2331 "UUID", "Inodes", "IUsed", "IFree",
2332 "IUse%", "Mounted on");
2334 printf(UUF" "CSF" "CSF" "CSF" "RSF" %-s\n",
2335 "UUID", cooked ? "bytes" : "1K-blocks",
2336 "Used", "Available", "Use%", "Mounted on");
2338 for (tp = types; tp->st_name != NULL; tp++) {
2339 for (index = 0; ; index++) {
2340 memset(&stat_buf, 0, sizeof(struct obd_statfs));
2341 memset(&uuid_buf, 0, sizeof(struct obd_uuid));
2342 type = lazy ? tp->st_op | LL_STATFS_NODELAY : tp->st_op;
2343 rc = llapi_obd_statfs(mntdir, type, index,
2344 &stat_buf, &uuid_buf);
2351 if (poolname && tp->st_op == LL_STATFS_LOV &&
2352 llapi_search_ost(fsname, poolname,
2353 obd_uuid2str(&uuid_buf)) != 1)
2356 /* the llapi_obd_statfs() call may have returned with
2357 * an error, but if it filled in uuid_buf we will at
2358 * lease use that to print out a message for that OBD.
2359 * If we didn't get anything in the uuid_buf, then fill
2360 * it in so that we can print an error message. */
2361 if (uuid_buf.uuid[0] == '\0')
2362 sprintf(uuid_buf.uuid, "%s%04x",
2363 tp->st_name, index);
2364 showdf(mntdir, &stat_buf, obd_uuid2str(&uuid_buf),
2365 ishow, cooked, tp->st_name, index, rc);
2368 if (tp->st_op == LL_STATFS_LMV) {
2369 sum.os_ffree += stat_buf.os_ffree;
2370 sum.os_files += stat_buf.os_files;
2371 } else /* if (tp->st_op == LL_STATFS_LOV) */ {
2372 sum.os_blocks += stat_buf.os_blocks *
2374 sum.os_bfree += stat_buf.os_bfree *
2376 sum.os_bavail += stat_buf.os_bavail *
2378 ost_ffree += stat_buf.os_ffree;
2380 } else if (rc == -EINVAL || rc == -EFAULT) {
2386 /* If we don't have as many objects free on the OST as inodes
2387 * on the MDS, we reduce the total number of inodes to
2388 * compensate, so that the "inodes in use" number is correct.
2389 * Matches ll_statfs_internal() so the results are consistent. */
2390 if (ost_ffree < sum.os_ffree) {
2391 sum.os_files = (sum.os_files - sum.os_ffree) + ost_ffree;
2392 sum.os_ffree = ost_ffree;
2395 showdf(mntdir, &sum, "filesystem summary:", ishow, cooked, NULL, 0, 0);
2400 static int lfs_df(int argc, char **argv)
2402 char mntdir[PATH_MAX] = {'\0'}, path[PATH_MAX] = {'\0'};
2403 int ishow = 0, cooked = 0;
2405 int c, rc = 0, index = 0;
2406 char fsname[PATH_MAX] = "", *pool_name = NULL;
2407 struct option long_opts[] = {
2408 {"pool", required_argument, 0, 'p'},
2409 {"lazy", 0, 0, 'l'},
2413 while ((c = getopt_long(argc, argv, "hilp:", long_opts, NULL)) != -1) {
2431 if (optind < argc && !realpath(argv[optind], path)) {
2433 fprintf(stderr, "error: invalid path '%s': %s\n",
2434 argv[optind], strerror(-rc));
2438 while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2439 /* Check if we have a mount point */
2440 if (mntdir[0] == '\0')
2443 rc = mntdf(mntdir, fsname, pool_name, ishow, cooked, lazy);
2444 if (rc || path[0] != '\0')
2446 fsname[0] = '\0'; /* avoid matching in next loop */
2447 mntdir[0] = '\0'; /* avoid matching in next loop */
2453 static int lfs_getname(int argc, char **argv)
2455 char mntdir[PATH_MAX] = "", path[PATH_MAX] = "", fsname[PATH_MAX] = "";
2456 int rc = 0, index = 0, c;
2457 char buf[sizeof(struct obd_uuid)];
2459 while ((c = getopt(argc, argv, "h")) != -1)
2462 if (optind == argc) { /* no paths specified, get all paths. */
2463 while (!llapi_search_mounts(path, index++, mntdir, fsname)) {
2464 rc = llapi_getname(mntdir, buf, sizeof(buf));
2467 "cannot get name for `%s': %s\n",
2468 mntdir, strerror(-rc));
2472 printf("%s %s\n", buf, mntdir);
2474 path[0] = fsname[0] = mntdir[0] = 0;
2476 } else { /* paths specified, only attempt to search these. */
2477 for (; optind < argc; optind++) {
2478 rc = llapi_getname(argv[optind], buf, sizeof(buf));
2481 "cannot get name for `%s': %s\n",
2482 argv[optind], strerror(-rc));
2486 printf("%s %s\n", buf, argv[optind]);
2492 static int lfs_check(int argc, char **argv)
2495 char mntdir[PATH_MAX] = {'\0'};
2504 obd_types[0] = obd_type1;
2505 obd_types[1] = obd_type2;
2507 if (strcmp(argv[1], "osts") == 0) {
2508 strcpy(obd_types[0], "osc");
2509 } else if (strcmp(argv[1], "mds") == 0) {
2510 strcpy(obd_types[0], "mdc");
2511 } else if (strcmp(argv[1], "servers") == 0) {
2513 strcpy(obd_types[0], "osc");
2514 strcpy(obd_types[1], "mdc");
2516 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
2521 rc = llapi_search_mounts(NULL, 0, mntdir, NULL);
2522 if (rc < 0 || mntdir[0] == '\0') {
2523 fprintf(stderr, "No suitable Lustre mount found\n");
2527 rc = llapi_target_check(num_types, obd_types, mntdir);
2529 fprintf(stderr, "error: %s: %s status failed\n",
2536 static int lfs_join(int argc, char **argv)
2538 fprintf(stderr, "join two lustre files into one.\n"
2539 "obsolete, HEAD does not support it anymore.\n");
2543 #ifdef HAVE_SYS_QUOTA_H
2544 #define ARG2INT(nr, str, msg) \
2547 nr = strtol(str, &endp, 0); \
2549 fprintf(stderr, "error: bad %s: %s\n", msg, str); \
2554 #define ADD_OVERFLOW(a,b) ((a + b) < a) ? (a = ULONG_MAX) : (a = a + b)
2556 /* Convert format time string "XXwXXdXXhXXmXXs" into seconds value
2557 * returns the value or ULONG_MAX on integer overflow or incorrect format
2559 * 1. the order of specifiers is arbitrary (may be: 5w3s or 3s5w)
2560 * 2. specifiers may be encountered multiple times (2s3s is 5 seconds)
2561 * 3. empty integer value is interpreted as 0
2563 static unsigned long str2sec(const char* timestr)
2565 const char spec[] = "smhdw";
2566 const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2567 unsigned long val = 0;
2570 if (strpbrk(timestr, spec) == NULL) {
2571 /* no specifiers inside the time string,
2572 should treat it as an integer value */
2573 val = strtoul(timestr, &tail, 10);
2574 return *tail ? ULONG_MAX : val;
2577 /* format string is XXwXXdXXhXXmXXs */
2583 v = strtoul(timestr, &tail, 10);
2584 if (v == ULONG_MAX || *tail == '\0')
2585 /* value too large (ULONG_MAX or more)
2586 or missing specifier */
2589 ptr = strchr(spec, *tail);
2591 /* unknown specifier */
2596 /* check if product will overflow the type */
2597 if (!(v < ULONG_MAX / mult[ind]))
2600 ADD_OVERFLOW(val, mult[ind] * v);
2601 if (val == ULONG_MAX)
2613 #define ARG2ULL(nr, str, def_units) \
2615 unsigned long long limit, units = def_units; \
2618 rc = llapi_parse_size(str, &limit, &units, 1); \
2620 fprintf(stderr, "error: bad limit value %s\n", str); \
2626 static inline int has_times_option(int argc, char **argv)
2630 for (i = 1; i < argc; i++)
2631 if (!strcmp(argv[i], "-t"))
2637 int lfs_setquota_times(int argc, char **argv)
2640 struct if_quotactl qctl;
2641 char *mnt, *obd_type = (char *)qctl.obd_type;
2642 struct obd_dqblk *dqb = &qctl.qc_dqblk;
2643 struct obd_dqinfo *dqi = &qctl.qc_dqinfo;
2644 struct option long_opts[] = {
2645 {"block-grace", required_argument, 0, 'b'},
2646 {"group", no_argument, 0, 'g'},
2647 {"inode-grace", required_argument, 0, 'i'},
2648 {"times", no_argument, 0, 't'},
2649 {"user", no_argument, 0, 'u'},
2653 memset(&qctl, 0, sizeof(qctl));
2654 qctl.qc_cmd = LUSTRE_Q_SETINFO;
2655 qctl.qc_type = UGQUOTA;
2657 while ((c = getopt_long(argc, argv, "b:gi:tu", long_opts, NULL)) != -1) {
2661 if (qctl.qc_type != UGQUOTA) {
2662 fprintf(stderr, "error: -u and -g can't be used "
2663 "more than once\n");
2666 qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2669 if ((dqi->dqi_bgrace = str2sec(optarg)) == ULONG_MAX) {
2670 fprintf(stderr, "error: bad block-grace: %s\n",
2674 dqb->dqb_valid |= QIF_BTIME;
2677 if ((dqi->dqi_igrace = str2sec(optarg)) == ULONG_MAX) {
2678 fprintf(stderr, "error: bad inode-grace: %s\n",
2682 dqb->dqb_valid |= QIF_ITIME;
2684 case 't': /* Yes, of course! */
2686 default: /* getopt prints error message for us when opterr != 0 */
2691 if (qctl.qc_type == UGQUOTA) {
2692 fprintf(stderr, "error: neither -u nor -g specified\n");
2696 if (optind != argc - 1) {
2697 fprintf(stderr, "error: unexpected parameters encountered\n");
2702 rc = llapi_quotactl(mnt, &qctl);
2705 fprintf(stderr, "%s %s ", obd_type,
2706 obd_uuid2str(&qctl.obd_uuid));
2707 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2714 #define BSLIMIT (1 << 0)
2715 #define BHLIMIT (1 << 1)
2716 #define ISLIMIT (1 << 2)
2717 #define IHLIMIT (1 << 3)
2719 int lfs_setquota(int argc, char **argv)
2722 struct if_quotactl qctl;
2723 char *mnt, *obd_type = (char *)qctl.obd_type;
2724 struct obd_dqblk *dqb = &qctl.qc_dqblk;
2725 struct option long_opts[] = {
2726 {"block-softlimit", required_argument, 0, 'b'},
2727 {"block-hardlimit", required_argument, 0, 'B'},
2728 {"group", required_argument, 0, 'g'},
2729 {"inode-softlimit", required_argument, 0, 'i'},
2730 {"inode-hardlimit", required_argument, 0, 'I'},
2731 {"user", required_argument, 0, 'u'},
2734 unsigned limit_mask = 0;
2737 if (has_times_option(argc, argv))
2738 return lfs_setquota_times(argc, argv);
2740 memset(&qctl, 0, sizeof(qctl));
2741 qctl.qc_cmd = LUSTRE_Q_SETQUOTA;
2742 qctl.qc_type = UGQUOTA; /* UGQUOTA makes no sense for setquota,
2743 * so it can be used as a marker that qc_type
2744 * isn't reinitialized from command line */
2746 while ((c = getopt_long(argc, argv, "b:B:g:i:I:u:", long_opts, NULL)) != -1) {
2750 if (qctl.qc_type != UGQUOTA) {
2751 fprintf(stderr, "error: -u and -g can't be used"
2752 " more than once\n");
2755 qctl.qc_type = (c == 'u') ? USRQUOTA : GRPQUOTA;
2756 rc = name2id(&qctl.qc_id, optarg,
2757 (qctl.qc_type == USRQUOTA) ? USER : GROUP);
2759 qctl.qc_id = strtoul(optarg, &endptr, 10);
2760 if (*endptr != '\0') {
2761 fprintf(stderr, "error: can't find id "
2762 "for name %s\n", optarg);
2768 ARG2ULL(dqb->dqb_bsoftlimit, optarg, 1024);
2769 dqb->dqb_bsoftlimit >>= 10;
2770 limit_mask |= BSLIMIT;
2771 if (dqb->dqb_bsoftlimit &&
2772 dqb->dqb_bsoftlimit <= 1024) /* <= 1M? */
2773 fprintf(stderr, "warning: block softlimit is "
2774 "smaller than the miminal qunit size, "
2775 "please see the help of setquota or "
2776 "Lustre manual for details.\n");
2779 ARG2ULL(dqb->dqb_bhardlimit, optarg, 1024);
2780 dqb->dqb_bhardlimit >>= 10;
2781 limit_mask |= BHLIMIT;
2782 if (dqb->dqb_bhardlimit &&
2783 dqb->dqb_bhardlimit <= 1024) /* <= 1M? */
2784 fprintf(stderr, "warning: block hardlimit is "
2785 "smaller than the miminal qunit size, "
2786 "please see the help of setquota or "
2787 "Lustre manual for details.\n");
2790 ARG2ULL(dqb->dqb_isoftlimit, optarg, 1);
2791 limit_mask |= ISLIMIT;
2792 if (dqb->dqb_isoftlimit &&
2793 dqb->dqb_isoftlimit <= 1024) /* <= 1K inodes? */
2794 fprintf(stderr, "warning: inode softlimit is "
2795 "smaller than the miminal qunit size, "
2796 "please see the help of setquota or "
2797 "Lustre manual for details.\n");
2800 ARG2ULL(dqb->dqb_ihardlimit, optarg, 1);
2801 limit_mask |= IHLIMIT;
2802 if (dqb->dqb_ihardlimit &&
2803 dqb->dqb_ihardlimit <= 1024) /* <= 1K inodes? */
2804 fprintf(stderr, "warning: inode hardlimit is "
2805 "smaller than the miminal qunit size, "
2806 "please see the help of setquota or "
2807 "Lustre manual for details.\n");
2809 default: /* getopt prints error message for us when opterr != 0 */
2814 if (qctl.qc_type == UGQUOTA) {
2815 fprintf(stderr, "error: neither -u nor -g was specified\n");
2819 if (limit_mask == 0) {
2820 fprintf(stderr, "error: at least one limit must be specified\n");
2824 if (optind != argc - 1) {
2825 fprintf(stderr, "error: unexpected parameters encountered\n");
2831 if ((!(limit_mask & BHLIMIT) ^ !(limit_mask & BSLIMIT)) ||
2832 (!(limit_mask & IHLIMIT) ^ !(limit_mask & ISLIMIT))) {
2833 /* sigh, we can't just set blimits/ilimits */
2834 struct if_quotactl tmp_qctl = {.qc_cmd = LUSTRE_Q_GETQUOTA,
2835 .qc_type = qctl.qc_type,
2836 .qc_id = qctl.qc_id};
2838 rc = llapi_quotactl(mnt, &tmp_qctl);
2840 fprintf(stderr, "error: setquota failed while retrieving"
2841 " current quota settings (%s)\n",
2846 if (!(limit_mask & BHLIMIT))
2847 dqb->dqb_bhardlimit = tmp_qctl.qc_dqblk.dqb_bhardlimit;
2848 if (!(limit_mask & BSLIMIT))
2849 dqb->dqb_bsoftlimit = tmp_qctl.qc_dqblk.dqb_bsoftlimit;
2850 if (!(limit_mask & IHLIMIT))
2851 dqb->dqb_ihardlimit = tmp_qctl.qc_dqblk.dqb_ihardlimit;
2852 if (!(limit_mask & ISLIMIT))
2853 dqb->dqb_isoftlimit = tmp_qctl.qc_dqblk.dqb_isoftlimit;
2855 /* Keep grace times if we have got no softlimit arguments */
2856 if ((limit_mask & BHLIMIT) && !(limit_mask & BSLIMIT)) {
2857 dqb->dqb_valid |= QIF_BTIME;
2858 dqb->dqb_btime = tmp_qctl.qc_dqblk.dqb_btime;
2861 if ((limit_mask & IHLIMIT) && !(limit_mask & ISLIMIT)) {
2862 dqb->dqb_valid |= QIF_ITIME;
2863 dqb->dqb_itime = tmp_qctl.qc_dqblk.dqb_itime;
2867 dqb->dqb_valid |= (limit_mask & (BHLIMIT | BSLIMIT)) ? QIF_BLIMITS : 0;
2868 dqb->dqb_valid |= (limit_mask & (IHLIMIT | ISLIMIT)) ? QIF_ILIMITS : 0;
2870 rc = llapi_quotactl(mnt, &qctl);
2873 fprintf(stderr, "%s %s ", obd_type,
2874 obd_uuid2str(&qctl.obd_uuid));
2875 fprintf(stderr, "setquota failed: %s\n", strerror(-rc));
2882 static inline char *type2name(int check_type)
2884 if (check_type == USRQUOTA)
2886 else if (check_type == GRPQUOTA)
2892 /* Converts seconds value into format string
2893 * result is returned in buf
2895 * 1. result is in descenting order: 1w2d3h4m5s
2896 * 2. zero fields are not filled (except for p. 3): 5d1s
2897 * 3. zero seconds value is presented as "0s"
2899 static char * __sec2str(time_t seconds, char *buf)
2901 const char spec[] = "smhdw";
2902 const unsigned long mult[] = {1, 60, 60*60, 24*60*60, 7*24*60*60};
2907 for (i = sizeof(mult) / sizeof(mult[0]) - 1 ; i >= 0; i--) {
2908 c = seconds / mult[i];
2910 if (c > 0 || (i == 0 && buf == tail))
2911 tail += snprintf(tail, 40-(tail-buf), "%lu%c", c, spec[i]);
2919 static void sec2str(time_t seconds, char *buf, int rc)
2926 tail = __sec2str(seconds, tail);
2928 if (rc && tail - buf < 39) {
2934 static void diff2str(time_t seconds, char *buf, time_t now)
2940 if (seconds <= now) {
2941 strcpy(buf, "none");
2944 __sec2str(seconds - now, buf);
2947 static void print_quota_title(char *name, struct if_quotactl *qctl,
2948 bool human_readable)
2950 printf("Disk quotas for %s %s (%cid %u):\n",
2951 type2name(qctl->qc_type), name,
2952 *type2name(qctl->qc_type), qctl->qc_id);
2953 printf("%15s%8s %7s%8s%8s%8s %7s%8s%8s\n",
2954 "Filesystem", human_readable ? "used" : "kbytes",
2955 "quota", "limit", "grace",
2956 "files", "quota", "limit", "grace");
2959 static void kbytes2str(__u64 num, char *buf, int buflen, bool h)
2962 snprintf(buf, buflen, "%ju", (uintmax_t)num);
2965 snprintf(buf, buflen, "%5.4gP",
2966 (double)num / ((__u64)1 << 40));
2968 snprintf(buf, buflen, "%5.4gT",
2969 (double)num / (1 << 30));
2971 snprintf(buf, buflen, "%5.4gG",
2972 (double)num / (1 << 20));
2974 snprintf(buf, buflen, "%5.4gM",
2975 (double)num / (1 << 10));
2977 snprintf(buf, buflen, "%ju%s", (uintmax_t)num, "k");
2981 #define STRBUF_LEN 32
2982 static void print_quota(char *mnt, struct if_quotactl *qctl, int type,
2989 if (qctl->qc_cmd == LUSTRE_Q_GETQUOTA || qctl->qc_cmd == Q_GETOQUOTA) {
2990 int bover = 0, iover = 0;
2991 struct obd_dqblk *dqb = &qctl->qc_dqblk;
2992 char numbuf[3][STRBUF_LEN];
2994 char strbuf[STRBUF_LEN];
2996 if (dqb->dqb_bhardlimit &&
2997 lustre_stoqb(dqb->dqb_curspace) >= dqb->dqb_bhardlimit) {
2999 } else if (dqb->dqb_bsoftlimit && dqb->dqb_btime) {
3000 if (dqb->dqb_btime > now) {
3007 if (dqb->dqb_ihardlimit &&
3008 dqb->dqb_curinodes >= dqb->dqb_ihardlimit) {
3010 } else if (dqb->dqb_isoftlimit && dqb->dqb_itime) {
3011 if (dqb->dqb_itime > now) {
3019 if (strlen(mnt) > 15)
3020 printf("%s\n%15s", mnt, "");
3022 printf("%15s", mnt);
3025 diff2str(dqb->dqb_btime, timebuf, now);
3027 kbytes2str(lustre_stoqb(dqb->dqb_curspace),
3028 strbuf, sizeof(strbuf), h);
3029 if (rc == -EREMOTEIO)
3030 sprintf(numbuf[0], "%s*", strbuf);
3032 sprintf(numbuf[0], (dqb->dqb_valid & QIF_SPACE) ?
3033 "%s" : "[%s]", strbuf);
3035 kbytes2str(dqb->dqb_bsoftlimit, strbuf, sizeof(strbuf), h);
3036 if (type == QC_GENERAL)
3037 sprintf(numbuf[1], (dqb->dqb_valid & QIF_BLIMITS) ?
3038 "%s" : "[%s]", strbuf);
3040 sprintf(numbuf[1], "%s", "-");
3042 kbytes2str(dqb->dqb_bhardlimit, strbuf, sizeof(strbuf), h);
3043 sprintf(numbuf[2], (dqb->dqb_valid & QIF_BLIMITS) ?
3044 "%s" : "[%s]", strbuf);
3046 printf(" %7s%c %6s %7s %7s",
3047 numbuf[0], bover ? '*' : ' ', numbuf[1],
3048 numbuf[2], bover > 1 ? timebuf : "-");
3051 diff2str(dqb->dqb_itime, timebuf, now);
3053 sprintf(numbuf[0], (dqb->dqb_valid & QIF_INODES) ?
3054 "%ju" : "[%ju]", (uintmax_t)dqb->dqb_curinodes);
3056 if (type == QC_GENERAL)
3057 sprintf(numbuf[1], (dqb->dqb_valid & QIF_ILIMITS) ?
3059 (uintmax_t)dqb->dqb_isoftlimit);
3061 sprintf(numbuf[1], "%s", "-");
3063 sprintf(numbuf[2], (dqb->dqb_valid & QIF_ILIMITS) ?
3064 "%ju" : "[%ju]", (uintmax_t)dqb->dqb_ihardlimit);
3066 if (type != QC_OSTIDX)
3067 printf(" %7s%c %6s %7s %7s",
3068 numbuf[0], iover ? '*' : ' ', numbuf[1],
3069 numbuf[2], iover > 1 ? timebuf : "-");
3071 printf(" %7s %7s %7s %7s", "-", "-", "-", "-");
3074 } else if (qctl->qc_cmd == LUSTRE_Q_GETINFO ||
3075 qctl->qc_cmd == Q_GETOINFO) {
3079 sec2str(qctl->qc_dqinfo.dqi_bgrace, bgtimebuf, rc);
3080 sec2str(qctl->qc_dqinfo.dqi_igrace, igtimebuf, rc);
3081 printf("Block grace time: %s; Inode grace time: %s\n",
3082 bgtimebuf, igtimebuf);
3086 static int print_obd_quota(char *mnt, struct if_quotactl *qctl, int is_mdt,
3087 bool h, __u64 *total)
3089 int rc = 0, rc1 = 0, count = 0;
3090 __u32 valid = qctl->qc_valid;
3092 rc = llapi_get_obd_count(mnt, &count, is_mdt);
3094 fprintf(stderr, "can not get %s count: %s\n",
3095 is_mdt ? "mdt": "ost", strerror(-rc));
3099 for (qctl->qc_idx = 0; qctl->qc_idx < count; qctl->qc_idx++) {
3100 qctl->qc_valid = is_mdt ? QC_MDTIDX : QC_OSTIDX;
3101 rc = llapi_quotactl(mnt, qctl);
3103 /* It is remote client case. */
3104 if (-rc == EOPNOTSUPP) {
3111 fprintf(stderr, "quotactl %s%d failed.\n",
3112 is_mdt ? "mdt": "ost", qctl->qc_idx);
3116 print_quota(obd_uuid2str(&qctl->obd_uuid), qctl,
3117 qctl->qc_valid, 0, h);
3118 *total += is_mdt ? qctl->qc_dqblk.dqb_ihardlimit :
3119 qctl->qc_dqblk.dqb_bhardlimit;
3122 qctl->qc_valid = valid;
3126 static int lfs_quota(int argc, char **argv)
3129 char *mnt, *name = NULL;
3130 struct if_quotactl qctl = { .qc_cmd = LUSTRE_Q_GETQUOTA,
3131 .qc_type = UGQUOTA };
3132 char *obd_type = (char *)qctl.obd_type;
3133 char *obd_uuid = (char *)qctl.obd_uuid.uuid;
3134 int rc, rc1 = 0, rc2 = 0, rc3 = 0,
3135 verbose = 0, pass = 0, quiet = 0, inacc;
3137 __u32 valid = QC_GENERAL, idx = 0;
3138 __u64 total_ialloc = 0, total_balloc = 0;
3139 bool human_readable = false;
3141 while ((c = getopt(argc, argv, "gi:I:o:qtuvh")) != -1) {
3144 if (qctl.qc_type != UGQUOTA) {
3145 fprintf(stderr, "error: use either -u or -g\n");
3148 qctl.qc_type = USRQUOTA;
3151 if (qctl.qc_type != UGQUOTA) {
3152 fprintf(stderr, "error: use either -u or -g\n");
3155 qctl.qc_type = GRPQUOTA;
3158 qctl.qc_cmd = LUSTRE_Q_GETINFO;
3161 valid = qctl.qc_valid = QC_UUID;
3162 strlcpy(obd_uuid, optarg, sizeof(qctl.obd_uuid));
3165 valid = qctl.qc_valid = QC_MDTIDX;
3166 idx = qctl.qc_idx = atoi(optarg);
3169 valid = qctl.qc_valid = QC_OSTIDX;
3170 idx = qctl.qc_idx = atoi(optarg);
3179 human_readable = true;
3182 fprintf(stderr, "error: %s: option '-%c' "
3183 "unrecognized\n", argv[0], c);
3188 /* current uid/gid info for "lfs quota /path/to/lustre/mount" */
3189 if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && qctl.qc_type == UGQUOTA &&
3190 optind == argc - 1) {
3192 memset(&qctl, 0, sizeof(qctl)); /* spoiled by print_*_quota */
3193 qctl.qc_cmd = LUSTRE_Q_GETQUOTA;
3194 qctl.qc_valid = valid;
3197 qctl.qc_type = USRQUOTA;
3198 qctl.qc_id = geteuid();
3200 qctl.qc_type = GRPQUOTA;
3201 qctl.qc_id = getegid();
3203 rc = id2name(&name, qctl.qc_id,
3204 (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3207 /* lfs quota -u username /path/to/lustre/mount */
3208 } else if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) {
3209 /* options should be followed by u/g-name and mntpoint */
3210 if (optind + 2 != argc || qctl.qc_type == UGQUOTA) {
3211 fprintf(stderr, "error: missing quota argument(s)\n");
3215 name = argv[optind++];
3216 rc = name2id(&qctl.qc_id, name,
3217 (qctl.qc_type == USRQUOTA) ? USER : GROUP);
3219 qctl.qc_id = strtoul(name, &endptr, 10);
3220 if (*endptr != '\0') {
3221 fprintf(stderr, "error: can't find id for name "
3226 } else if (optind + 1 != argc || qctl.qc_type == UGQUOTA) {
3227 fprintf(stderr, "error: missing quota info argument(s)\n");
3233 rc1 = llapi_quotactl(mnt, &qctl);
3237 fprintf(stderr, "%s quotas are not enabled.\n",
3238 qctl.qc_type == USRQUOTA ? "user" : "group");
3241 fprintf(stderr, "Permission denied.\n");
3243 /* We already got a "No such file..." message. */
3246 fprintf(stderr, "Unexpected quotactl error: %s\n",
3251 if (qctl.qc_cmd == LUSTRE_Q_GETQUOTA && !quiet)
3252 print_quota_title(name, &qctl, human_readable);
3254 if (rc1 && *obd_type)
3255 fprintf(stderr, "%s %s ", obd_type, obd_uuid);
3257 if (qctl.qc_valid != QC_GENERAL)
3260 inacc = (qctl.qc_cmd == LUSTRE_Q_GETQUOTA) &&
3261 ((qctl.qc_dqblk.dqb_valid & (QIF_LIMITS|QIF_USAGE)) !=
3262 (QIF_LIMITS|QIF_USAGE));
3264 print_quota(mnt, &qctl, QC_GENERAL, rc1, human_readable);
3266 if (qctl.qc_valid == QC_GENERAL && qctl.qc_cmd != LUSTRE_Q_GETINFO &&
3268 char strbuf[STRBUF_LEN];
3270 rc2 = print_obd_quota(mnt, &qctl, 1, human_readable,
3272 rc3 = print_obd_quota(mnt, &qctl, 0, human_readable,
3274 kbytes2str(total_balloc, strbuf, sizeof(strbuf),
3276 printf("Total allocated inode limit: %ju, total "
3277 "allocated block limit: %s\n", (uintmax_t)total_ialloc,
3281 if (rc1 || rc2 || rc3 || inacc)
3282 printf("Some errors happened when getting quota info. "
3283 "Some devices may be not working or deactivated. "
3284 "The data in \"[]\" is inaccurate.\n");
3292 #endif /* HAVE_SYS_QUOTA_H! */
3294 static int flushctx_ioctl(char *mp)
3298 fd = open(mp, O_RDONLY);
3300 fprintf(stderr, "flushctx: error open %s: %s\n",
3301 mp, strerror(errno));
3305 rc = ioctl(fd, LL_IOC_FLUSHCTX);
3307 fprintf(stderr, "flushctx: error ioctl %s: %s\n",
3308 mp, strerror(errno));
3314 static int lfs_flushctx(int argc, char **argv)
3316 int kdestroy = 0, c;
3317 char mntdir[PATH_MAX] = {'\0'};
3321 while ((c = getopt(argc, argv, "k")) != -1) {
3327 fprintf(stderr, "error: %s: option '-%c' "
3328 "unrecognized\n", argv[0], c);
3334 if ((rc = system("kdestroy > /dev/null")) != 0) {
3335 rc = WEXITSTATUS(rc);
3336 fprintf(stderr, "error destroying tickets: %d, continuing\n", rc);
3340 if (optind >= argc) {
3341 /* flush for all mounted lustre fs. */
3342 while (!llapi_search_mounts(NULL, index++, mntdir, NULL)) {
3343 /* Check if we have a mount point */
3344 if (mntdir[0] == '\0')
3347 if (flushctx_ioctl(mntdir))
3350 mntdir[0] = '\0'; /* avoid matching in next loop */
3353 /* flush fs as specified */
3354 while (optind < argc) {
3355 if (flushctx_ioctl(argv[optind++]))
3362 static int lfs_lsetfacl(int argc, char **argv)
3364 fprintf(stderr, "local client sets facl for remote client.\n"
3365 "obsolete, does not support it anymore.\n");
3369 static int lfs_lgetfacl(int argc, char **argv)
3371 fprintf(stderr, "local client gets facl for remote client.\n"
3372 "obsolete, does not support it anymore.\n");
3376 static int lfs_rsetfacl(int argc, char **argv)
3378 fprintf(stderr, "remote client sets facl for remote client.\n"
3379 "obsolete, does not support it anymore.\n");
3383 static int lfs_rgetfacl(int argc, char **argv)
3385 fprintf(stderr, "remote client gets facl for remote client.\n"
3386 "obsolete, does not support it anymore.\n");
3390 static int lfs_cp(int argc, char **argv)
3392 fprintf(stderr, "remote client copy file(s).\n"
3393 "obsolete, does not support it anymore.\n");
3397 static int lfs_ls(int argc, char **argv)
3399 fprintf(stderr, "remote client lists directory contents.\n"
3400 "obsolete, does not support it anymore.\n");
3404 static int lfs_changelog(int argc, char **argv)
3406 void *changelog_priv;
3407 struct changelog_rec *rec;
3408 long long startrec = 0, endrec = 0;
3410 struct option long_opts[] = {
3411 {"follow", no_argument, 0, 'f'},
3414 char short_opts[] = "f";
3417 while ((rc = getopt_long(argc, argv, short_opts,
3418 long_opts, NULL)) != -1) {
3426 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3427 argv[0], argv[optind - 1]);
3434 mdd = argv[optind++];
3436 startrec = strtoll(argv[optind++], NULL, 10);
3438 endrec = strtoll(argv[optind++], NULL, 10);
3440 rc = llapi_changelog_start(&changelog_priv,
3441 CHANGELOG_FLAG_BLOCK |
3442 CHANGELOG_FLAG_JOBID |
3443 (follow ? CHANGELOG_FLAG_FOLLOW : 0),
3446 fprintf(stderr, "Can't start changelog: %s\n",
3447 strerror(errno = -rc));
3451 while ((rc = llapi_changelog_recv(changelog_priv, &rec)) == 0) {
3455 if (endrec && rec->cr_index > endrec) {
3456 llapi_changelog_free(&rec);
3459 if (rec->cr_index < startrec) {
3460 llapi_changelog_free(&rec);
3464 secs = rec->cr_time >> 30;
3465 gmtime_r(&secs, &ts);
3466 printf("%ju %02d%-5s %02d:%02d:%02d.%06d %04d.%02d.%02d "
3467 "0x%x t="DFID, (uintmax_t) rec->cr_index, rec->cr_type,
3468 changelog_type2str(rec->cr_type),
3469 ts.tm_hour, ts.tm_min, ts.tm_sec,
3470 (int)(rec->cr_time & ((1<<30) - 1)),
3471 ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday,
3472 rec->cr_flags & CLF_FLAGMASK, PFID(&rec->cr_tfid));
3474 if (rec->cr_flags & CLF_JOBID) {
3475 struct changelog_ext_jobid *jid =
3476 changelog_rec_jobid(rec);
3478 if (jid->cr_jobid[0] != '\0')
3479 printf(" j=%s", jid->cr_jobid);
3482 if (rec->cr_namelen)
3483 printf(" p="DFID" %.*s", PFID(&rec->cr_pfid),
3484 rec->cr_namelen, changelog_rec_name(rec));
3486 if (rec->cr_flags & CLF_RENAME) {
3487 struct changelog_ext_rename *rnm =
3488 changelog_rec_rename(rec);
3490 if (!fid_is_zero(&rnm->cr_sfid))
3491 printf(" s="DFID" sp="DFID" %.*s",
3492 PFID(&rnm->cr_sfid),
3493 PFID(&rnm->cr_spfid),
3494 (int)changelog_rec_snamelen(rec),
3495 changelog_rec_sname(rec));
3499 llapi_changelog_free(&rec);
3502 llapi_changelog_fini(&changelog_priv);
3505 fprintf(stderr, "Changelog: %s\n", strerror(errno = -rc));
3507 return (rc == 1 ? 0 : rc);
3510 static int lfs_changelog_clear(int argc, char **argv)
3518 endrec = strtoll(argv[3], NULL, 10);
3520 rc = llapi_changelog_clear(argv[1], argv[2], endrec);
3522 fprintf(stderr, "%s error: %s\n", argv[0],
3523 strerror(errno = -rc));
3527 static int lfs_fid2path(int argc, char **argv)
3529 struct option long_opts[] = {
3530 {"cur", no_argument, 0, 'c'},
3531 {"link", required_argument, 0, 'l'},
3532 {"rec", required_argument, 0, 'r'},
3535 char short_opts[] = "cl:r:";
3536 char *device, *fid, *path;
3537 long long recno = -1;
3543 while ((rc = getopt_long(argc, argv, short_opts,
3544 long_opts, NULL)) != -1) {
3550 linkno = strtol(optarg, NULL, 10);
3553 recno = strtoll(optarg, NULL, 10);
3558 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3559 argv[0], argv[optind - 1]);
3567 device = argv[optind++];
3568 path = calloc(1, PATH_MAX);
3570 fprintf(stderr, "error: Not enough memory\n");
3575 while (optind < argc) {
3576 fid = argv[optind++];
3578 lnktmp = (linkno >= 0) ? linkno : 0;
3580 int oldtmp = lnktmp;
3581 long long rectmp = recno;
3583 rc2 = llapi_fid2path(device, fid, path, PATH_MAX,
3586 fprintf(stderr, "%s: error on FID %s: %s\n",
3587 argv[0], fid, strerror(errno = -rc2));
3594 fprintf(stdout, "%lld ", rectmp);
3595 if (device[0] == '/') {
3596 fprintf(stdout, "%s", device);
3597 if (device[strlen(device) - 1] != '/')
3598 fprintf(stdout, "/");
3599 } else if (path[0] == '\0') {
3600 fprintf(stdout, "/");
3602 fprintf(stdout, "%s\n", path);
3605 /* specified linkno */
3607 if (oldtmp == lnktmp)
3617 static int lfs_path2fid(int argc, char **argv)
3619 struct option long_opts[] = {
3620 {"parents", no_argument, 0, 'p'},
3624 const char short_opts[] = "p";
3625 const char *sep = "";
3628 bool show_parents = false;
3630 while ((rc = getopt_long(argc, argv, short_opts,
3631 long_opts, NULL)) != -1) {
3634 show_parents = true;
3637 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3638 argv[0], argv[optind - 1]);
3643 if (optind > argc - 1)
3645 else if (optind < argc - 1)
3649 for (path = argv + optind; *path != NULL; path++) {
3651 if (!show_parents) {
3652 err = llapi_path2fid(*path, &fid);
3654 printf("%s%s"DFID"\n",
3655 *sep != '\0' ? *path : "", sep,
3658 char name[NAME_MAX + 1];
3659 unsigned int linkno = 0;
3661 while ((err = llapi_path2parent(*path, linkno, &fid,
3662 name, sizeof(name))) == 0) {
3663 if (*sep != '\0' && linkno == 0)
3664 printf("%s%s", *path, sep);
3666 printf("%s"DFID"/%s", linkno != 0 ? "\t" : "",
3671 /* err == -ENODATA is end-of-loop */
3672 if (linkno > 0 && err == -ENODATA) {
3679 fprintf(stderr, "%s: can't get %sfid for %s: %s\n",
3680 argv[0], show_parents ? "parent " : "", *path,
3692 static int lfs_data_version(int argc, char **argv)
3699 int data_version_flags = LL_DV_RD_FLUSH; /* Read by default */
3704 while ((c = getopt(argc, argv, "nrw")) != -1) {
3707 data_version_flags = 0;
3710 data_version_flags |= LL_DV_RD_FLUSH;
3713 data_version_flags |= LL_DV_WR_FLUSH;
3722 path = argv[optind];
3723 fd = open(path, O_RDONLY);
3725 err(errno, "cannot open file %s", path);
3727 rc = llapi_get_data_version(fd, &data_version, data_version_flags);
3729 err(errno, "cannot get version for %s", path);
3731 printf("%ju" "\n", (uintmax_t)data_version);
3737 static int lfs_hsm_state(int argc, char **argv)
3742 struct hsm_user_state hus;
3750 rc = llapi_hsm_state_get(path, &hus);
3752 fprintf(stderr, "can't get hsm state for %s: %s\n",
3753 path, strerror(errno = -rc));
3757 /* Display path name and status flags */
3758 printf("%s: (0x%08x)", path, hus.hus_states);
3760 if (hus.hus_states & HS_RELEASED)
3761 printf(" released");
3762 if (hus.hus_states & HS_EXISTS)
3764 if (hus.hus_states & HS_DIRTY)
3766 if (hus.hus_states & HS_ARCHIVED)
3767 printf(" archived");
3768 /* Display user-settable flags */
3769 if (hus.hus_states & HS_NORELEASE)
3770 printf(" never_release");
3771 if (hus.hus_states & HS_NOARCHIVE)
3772 printf(" never_archive");
3773 if (hus.hus_states & HS_LOST)
3774 printf(" lost_from_hsm");
3776 if (hus.hus_archive_id != 0)
3777 printf(", archive_id:%d", hus.hus_archive_id);
3780 } while (++i < argc);
3785 #define LFS_HSM_SET 0
3786 #define LFS_HSM_CLEAR 1
3789 * Generic function to set or clear HSM flags.
3790 * Used by hsm_set and hsm_clear.
3792 * @mode if LFS_HSM_SET, set the flags, if LFS_HSM_CLEAR, clear the flags.
3794 static int lfs_hsm_change_flags(int argc, char **argv, int mode)
3796 struct option long_opts[] = {
3797 {"lost", 0, 0, 'l'},
3798 {"norelease", 0, 0, 'r'},
3799 {"noarchive", 0, 0, 'a'},
3800 {"archived", 0, 0, 'A'},
3801 {"dirty", 0, 0, 'd'},
3802 {"exists", 0, 0, 'e'},
3805 char short_opts[] = "lraAde";
3813 while ((c = getopt_long(argc, argv, short_opts,
3814 long_opts, NULL)) != -1) {
3820 mask |= HS_NOARCHIVE;
3823 mask |= HS_ARCHIVED;
3826 mask |= HS_NORELEASE;
3837 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
3838 argv[0], argv[optind - 1]);
3843 /* User should have specified a flag */
3847 while (optind < argc) {
3849 path = argv[optind];
3851 /* If mode == 0, this means we apply the mask. */
3852 if (mode == LFS_HSM_SET)
3853 rc = llapi_hsm_state_set(path, mask, 0, 0);
3855 rc = llapi_hsm_state_set(path, 0, mask, 0);
3858 fprintf(stderr, "Can't change hsm flags for %s: %s\n",
3859 path, strerror(errno = -rc));
3868 static int lfs_hsm_action(int argc, char **argv)
3873 struct hsm_current_action hca;
3874 struct hsm_extent he;
3875 enum hsm_user_action hua;
3876 enum hsm_progress_states hps;
3884 rc = llapi_hsm_current_action(path, &hca);
3886 fprintf(stderr, "can't get hsm action for %s: %s\n",
3887 path, strerror(errno = -rc));
3890 he = hca.hca_location;
3891 hua = hca.hca_action;
3892 hps = hca.hca_state;
3894 printf("%s: %s", path, hsm_user_action2name(hua));
3896 /* Skip file without action */
3897 if (hca.hca_action == HUA_NONE) {
3902 printf(" %s ", hsm_progress_state2name(hps));
3904 if ((hps == HPS_RUNNING) &&
3905 (hua == HUA_ARCHIVE || hua == HUA_RESTORE))
3906 printf("(%llu bytes moved)\n",
3907 (unsigned long long)he.length);
3908 else if ((he.offset + he.length) == LUSTRE_EOF)
3909 printf("(from %llu to EOF)\n",
3910 (unsigned long long)he.offset);
3912 printf("(from %llu to %llu)\n",
3913 (unsigned long long)he.offset,
3914 (unsigned long long)(he.offset + he.length));
3916 } while (++i < argc);
3921 static int lfs_hsm_set(int argc, char **argv)
3923 return lfs_hsm_change_flags(argc, argv, LFS_HSM_SET);
3926 static int lfs_hsm_clear(int argc, char **argv)
3928 return lfs_hsm_change_flags(argc, argv, LFS_HSM_CLEAR);
3932 * Check file state and return its fid, to be used by lfs_hsm_request().
3934 * \param[in] file Path to file to check
3935 * \param[in,out] fid Pointer to allocated lu_fid struct.
3936 * \param[in,out] last_dev Pointer to last device id used.
3938 * \return 0 on success.
3940 static int lfs_hsm_prepare_file(const char *file, struct lu_fid *fid,
3946 rc = lstat(file, &st);
3948 fprintf(stderr, "Cannot stat %s: %s\n", file, strerror(errno));
3951 /* Checking for regular file as archiving as posix copytool
3952 * rejects archiving files other than regular files
3954 if (!S_ISREG(st.st_mode)) {
3955 fprintf(stderr, "error: \"%s\" is not a regular file\n", file);
3958 /* A request should be ... */
3959 if (*last_dev != st.st_dev && *last_dev != 0) {
3960 fprintf(stderr, "All files should be "
3961 "on the same filesystem: %s\n", file);
3964 *last_dev = st.st_dev;
3966 rc = llapi_path2fid(file, fid);
3968 fprintf(stderr, "Cannot read FID of %s: %s\n",
3969 file, strerror(-rc));
3975 /* Fill an HSM HUR item with a given file name.
3977 * If mntpath is set, then the filename is actually a FID, and no
3978 * lookup on the filesystem will be performed.
3980 * \param[in] hur the user request to fill
3981 * \param[in] idx index of the item inside the HUR to fill
3982 * \param[in] mntpath mountpoint of Lustre
3983 * \param[in] fname filename (if mtnpath is NULL)
3984 * or FID (if mntpath is set)
3985 * \param[in] last_dev pointer to last device id used
3987 * \retval 0 on success
3988 * \retval CMD_HELP or a negative errno on error
3990 static int fill_hur_item(struct hsm_user_request *hur, unsigned int idx,
3991 const char *mntpath, const char *fname,
3994 struct hsm_user_item *hui = &hur->hur_user_item[idx];
3997 hui->hui_extent.length = -1;
3999 if (mntpath != NULL) {
4002 rc = sscanf(fname, SFID, RFID(&hui->hui_fid));
4006 fprintf(stderr, "hsm: '%s' is not a valid FID\n",
4011 rc = lfs_hsm_prepare_file(fname, &hui->hui_fid, last_dev);
4015 hur->hur_request.hr_itemcount++;
4020 static int lfs_hsm_request(int argc, char **argv, int action)
4022 struct option long_opts[] = {
4023 {"filelist", 1, 0, 'l'},
4024 {"data", 1, 0, 'D'},
4025 {"archive", 1, 0, 'a'},
4026 {"mntpath", 1, 0, 'm'},
4030 char short_opts[] = "l:D:a:m:";
4031 struct hsm_user_request *hur, *oldhur;
4036 char *filelist = NULL;
4037 char fullpath[PATH_MAX];
4038 char *opaque = NULL;
4042 int nbfile_alloc = 0;
4043 char *some_file = NULL;
4044 char *mntpath = NULL;
4050 while ((c = getopt_long(argc, argv, short_opts,
4051 long_opts, NULL)) != -1) {
4060 if (action != HUA_ARCHIVE &&
4061 action != HUA_REMOVE) {
4063 "error: -a is supported only "
4064 "when archiving or removing\n");
4067 archive_id = atoi(optarg);
4070 if (some_file == NULL) {
4072 some_file = strdup(optarg);
4078 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
4079 argv[0], argv[optind - 1]);
4084 /* All remaining args are files, so we have at least nbfile */
4085 nbfile = argc - optind;
4087 if ((nbfile == 0) && (filelist == NULL))
4091 opaque_len = strlen(opaque);
4093 /* Alloc the request structure with enough place to store all files
4094 * from command line. */
4095 hur = llapi_hsm_user_request_alloc(nbfile, opaque_len);
4097 fprintf(stderr, "Cannot create the request: %s\n",
4101 nbfile_alloc = nbfile;
4103 hur->hur_request.hr_action = action;
4104 hur->hur_request.hr_archive_id = archive_id;
4105 hur->hur_request.hr_flags = 0;
4107 /* All remaining args are files, add them */
4108 if (nbfile != 0 && some_file == NULL)
4109 some_file = strdup(argv[optind]);
4111 for (i = 0; i < nbfile; i++) {
4112 rc = fill_hur_item(hur, i, mntpath, argv[optind + i],
4118 /* from here stop using nb_file, use hur->hur_request.hr_itemcount */
4120 /* If a filelist was specified, read the filelist from it. */
4121 if (filelist != NULL) {
4122 fp = fopen(filelist, "r");
4124 fprintf(stderr, "Cannot read the file list %s: %s\n",
4125 filelist, strerror(errno));
4130 while ((rc = getline(&line, &len, fp)) != -1) {
4131 /* If allocated buffer was too small, get something
4133 if (nbfile_alloc <= hur->hur_request.hr_itemcount) {
4136 nbfile_alloc = nbfile_alloc * 2 + 1;
4138 hur = llapi_hsm_user_request_alloc(nbfile_alloc,
4141 fprintf(stderr, "hsm: cannot allocate "
4142 "the request: %s\n",
4149 size = hur_len(oldhur);
4151 fprintf(stderr, "hsm: cannot allocate "
4152 "%u files + %u bytes data\n",
4153 oldhur->hur_request.hr_itemcount,
4154 oldhur->hur_request.hr_data_len);
4161 memcpy(hur, oldhur, size);
4166 if (line[strlen(line) - 1] == '\n')
4167 line[strlen(line) - 1] = '\0';
4169 rc = fill_hur_item(hur, hur->hur_request.hr_itemcount,
4170 mntpath, line, &last_dev);
4176 if (some_file == NULL) {
4186 /* If a --data was used, add it to the request */
4187 hur->hur_request.hr_data_len = opaque_len;
4189 memcpy(hur_data(hur), opaque, opaque_len);
4191 /* Send the HSM request */
4192 if (realpath(some_file, fullpath) == NULL) {
4193 fprintf(stderr, "Could not find path '%s': %s\n",
4194 some_file, strerror(errno));
4196 rc = llapi_hsm_request(fullpath, hur);
4198 fprintf(stderr, "Cannot send HSM request (use of %s): %s\n",
4199 some_file, strerror(-rc));
4209 static int lfs_hsm_archive(int argc, char **argv)
4211 return lfs_hsm_request(argc, argv, HUA_ARCHIVE);
4214 static int lfs_hsm_restore(int argc, char **argv)
4216 return lfs_hsm_request(argc, argv, HUA_RESTORE);
4219 static int lfs_hsm_release(int argc, char **argv)
4221 return lfs_hsm_request(argc, argv, HUA_RELEASE);
4224 static int lfs_hsm_remove(int argc, char **argv)
4226 return lfs_hsm_request(argc, argv, HUA_REMOVE);
4229 static int lfs_hsm_cancel(int argc, char **argv)
4231 return lfs_hsm_request(argc, argv, HUA_CANCEL);
4234 static int lfs_swap_layouts(int argc, char **argv)
4239 return llapi_swap_layouts(argv[1], argv[2], 0, 0,
4240 SWAP_LAYOUTS_KEEP_MTIME |
4241 SWAP_LAYOUTS_KEEP_ATIME);
4244 static const char *const ladvise_names[] = LU_LADVISE_NAMES;
4246 static enum lu_ladvise_type lfs_get_ladvice(const char *string)
4248 enum lu_ladvise_type advice;
4251 advice < ARRAY_SIZE(ladvise_names); advice++) {
4252 if (ladvise_names[advice] == NULL)
4254 if (strcmp(string, ladvise_names[advice]) == 0)
4258 return LU_LADVISE_INVALID;
4261 static int lfs_ladvise(int argc, char **argv)
4263 struct option long_opts[] = {
4264 {"advice", required_argument, 0, 'a'},
4265 {"background", no_argument, 0, 'b'},
4266 {"end", required_argument, 0, 'e'},
4267 {"start", required_argument, 0, 's'},
4268 {"length", required_argument, 0, 'l'},
4271 char short_opts[] = "a:be:l:s:";
4276 struct lu_ladvise advice;
4277 enum lu_ladvise_type advice_type = LU_LADVISE_INVALID;
4278 unsigned long long start = 0;
4279 unsigned long long end = LUSTRE_EOF;
4280 unsigned long long length = 0;
4281 unsigned long long size_units;
4282 unsigned long long flags = 0;
4285 while ((c = getopt_long(argc, argv, short_opts,
4286 long_opts, NULL)) != -1) {
4289 advice_type = lfs_get_ladvice(optarg);
4290 if (advice_type == LU_LADVISE_INVALID) {
4291 fprintf(stderr, "%s: invalid advice type "
4292 "'%s'\n", argv[0], optarg);
4293 fprintf(stderr, "Valid types:");
4295 for (advice_type = 0;
4296 advice_type < ARRAY_SIZE(ladvise_names);
4298 if (ladvise_names[advice_type] == NULL)
4300 fprintf(stderr, " %s",
4301 ladvise_names[advice_type]);
4303 fprintf(stderr, "\n");
4313 rc = llapi_parse_size(optarg, &end,
4316 fprintf(stderr, "%s: bad end offset '%s'\n",
4323 rc = llapi_parse_size(optarg, &start,
4326 fprintf(stderr, "%s: bad start offset "
4327 "'%s'\n", argv[0], optarg);
4333 rc = llapi_parse_size(optarg, &length,
4336 fprintf(stderr, "%s: bad length '%s'\n",
4344 fprintf(stderr, "%s: option '%s' unrecognized\n",
4345 argv[0], argv[optind - 1]);
4350 if (advice_type == LU_LADVISE_INVALID) {
4351 fprintf(stderr, "%s: please give an advice type\n", argv[0]);
4352 fprintf(stderr, "Valid types:");
4353 for (advice_type = 0; advice_type < ARRAY_SIZE(ladvise_names);
4355 if (ladvise_names[advice_type] == NULL)
4357 fprintf(stderr, " %s", ladvise_names[advice_type]);
4359 fprintf(stderr, "\n");
4363 if (argc <= optind) {
4364 fprintf(stderr, "%s: please give one or more file names\n",
4369 if (end != LUSTRE_EOF && length != 0 && end != start + length) {
4370 fprintf(stderr, "%s: conflicting arguments of -l and -e\n",
4375 if (end == LUSTRE_EOF && length != 0)
4376 end = start + length;
4379 fprintf(stderr, "%s: range [%llu, %llu] is invalid\n",
4380 argv[0], start, end);
4384 while (optind < argc) {
4387 path = argv[optind++];
4389 fd = open(path, O_RDONLY);
4391 fprintf(stderr, "%s: cannot open file '%s': %s\n",
4392 argv[0], path, strerror(errno));
4397 advice.lla_start = start;
4398 advice.lla_end = end;
4399 advice.lla_advice = advice_type;
4400 advice.lla_padding = 0;
4401 rc2 = llapi_ladvise(fd, flags, 1, &advice);
4404 fprintf(stderr, "%s: cannot give advice '%s' to file "
4405 "'%s': %s\n", argv[0],
4406 ladvise_names[advice_type],
4407 path, strerror(errno));
4410 if (rc == 0 && rc2 < 0)
4416 int main(int argc, char **argv)
4420 /* Ensure that liblustreapi constructor has run */
4421 if (!liblustreapi_initialized)
4422 fprintf(stderr, "liblustreapi was not properly initialized\n");
4426 Parser_init("lfs > ", cmdlist);
4428 progname = argv[0]; /* Used in error messages */
4430 rc = Parser_execarg(argc - 1, argv + 1, cmdlist);
4432 rc = Parser_commands();
4435 return rc < 0 ? -rc : rc;
4438 #ifdef _LUSTRE_IDL_H_
4439 /* Everything we need here should be included by lustreapi.h. */
4440 # error "lfs should not depend on lustre_idl.h"
4441 #endif /* _LUSTRE_IDL_H_ */