lfs-find \- Lustre client utility to list files with specific attributes
.SH SYNOPSIS
.B lfs find \fR<\fIdirectory\fR|\fIfilename \fR...>
- [[\fB!\fR] \fB--atime\fR|\fB-A\fR [\fB-+\fR]\fIn\fR]
+ [[\fB!\fR] \fB--atime\fR|\fB-A\fR [\fB-+\fR]\fIn[smhdwy]\fR]
[[\fB!\fR] \fB--blocks\fR|\fB-b\fR [\fB+-\fR]\fIn\fR]
- [[\fB!\fR] \fB--ctime\fR|\fB-C\fR [\fB+-\fR]\fIn\fR]
+ [[\fB!\fR] \fB--ctime\fR|\fB-C\fR [\fB+-\fR]\fIn[smhdwy]\fR]
[[\fB!\fR] \fB--component-count|\fB--comp-count\fR [\fB+-\fR]\fIn\fR]
[[\fB!\fR] \fB--component-end|\fB--comp-end\fR|\fB-E\fR [\fB+-\fR]\fIn\fR[\fBKMGTPE\fR]]
[[\fB!\fR] \fB--component-flags|\fB--comp-flags\fR <[^]\fIflag\fB,\fR...>]
[[\fB!\fR] \fB--component-start|\fB--comp-start\fR [\fB+-\fR]\fIn\fR[\fBKMGTPE\fR]]
- [[\fB!\fR] \fB--mirror-count|\fB-N\fR [\fB+-\fR]\fIn\fR]
- [[\fB!\fR] \fB--mirror-state\fR <[^]\fIstate\fR>]
[[\fB!\fR] \fB--gid\fR|\fB-g\fR|\fB--group\fR|\fB-G\fR <\fIgname\fR>|<\fIgid\fR>]
[[\fB!\fR] \fB--layout\fR|\fB-L mdt\fR,\fBraid0\fR,\fBreleased\fR]
[\fB--maxdepth\fR|\fB-D\fI n\fR]
[[\fB!\fR] \fB--mdt\fR|\fB--mdt-index\fR|\fB-m\fR <\fIuuid\fR|\fIindex\fR,...>]
[[\fB!\fR] \fB--mdt-count\fR|\fB-T\fR [\fB+-\fR]\fIn\fR]
[[\fB!\fR] \fB--mdt-hash\fR|\fB-H <\fIhashtype\fR>]
- [[\fB!\fR] \fB--mtime\fR|\fB-M\fR [\fB-+\fR]\fIn\fR]
+ [[\fB!\fR] \fB--mirror-count|\fB-N\fR [\fB+-\fR]\fIn\fR]
+[[\fB!\fR] \fB--mirror-state\fR <[^]\fIstate\fR>]
+ [[\fB!\fR] \fB--mtime\fR|\fB-M\fR [\fB-+\fR]\fIn[smhdwy]\fR]
[[\fB!\fR] \fB--name\fR|\fB-n <\fIpattern\fR>]
- [[\fB!\fR] \fB--ost\fR|\fB-O\fR <\fIuuid\fR|\fIindex\fR,...>]
+ [[\fB!\fR] \fB--ost\fR|\fB-O\fR <\fIindex\fR,...>]
[[\fB!\fR] \fB--pool\fR <\fIpool\fR>]
[\fB--print\fR|\fB-P\fR]
[\fB--print0\fR|\fB-0\fR]
.SH OPTIONS
.TP
.BR --atime | -A
-File was last accessed \fIn\fR*24 hours ago.
+File was last accessed \fIn\fR*24 hours ago (if no units are given),
+or \fIn\fR*\fBs\fReconds, \fBm\fRinutes, \fBh\fRours, \fBd\fRays,
+\fBw\fReeks, or \fBy\fRears ago within a margin of error of 24h,
+or smaller if a unit is specified. Multiple units can be specified,
+for example \fB8h20m\fR is equivalent to \fB500m\fR. If multipe units
+are specified, the margin of error is based on the smallest unit used.
.TP
.BR --blocks | -b
-Blocks allocated by the file is \fIn\fR kibibytes (if no units are given),
+Blocks allocated by the file is \fIn\fR Kibibytes (if no units are given),
\fIn\fR 512-byte \fBb\fRlocks, or \fBK\fRibi-, \fBM\fRebi-, \fBG\fRibi-,
\fBT\fRebi-, \fBP\fRebi-, or \fBE\fRbi-bytes if that suffix is given.
.TP
.BR --ctime | -C
-File's status was last changed \fIn\fR*24 hours ago.
+File's status was last changed \fIn\fR*24 hours ago, see
+--atime
+for full details and options.
.TP
.BR --component-count | --comp-count
The file has \fIn\fR components in its layout.
.BR --component-start | --comp-start
The file has component start offset \fIn\fR (in bytes) for any component.
.TP
-.BR --mirror-count | -N
-The file has \fIn\fR mirrors in its layout.
-.TP
-.BR --mirror-state
-The file has a state of
-.I state.
-If
-.BI ^ state
-is used, print only files not matching
-.IR state.
-Only one state can be specified. Valid state name is:
-.RS 1.2i
-.TP
-.B ro
-The mirrored file is in read-only state. All of the mirrors contain
-the up-to-date data.
-.TP
-.B wp
-The mirrored file is in a state of being written.
-.TP
-.B sp
-The mirrored file is in a state of being resynchronized.
-.RE
-.TP
.BR --gid | -g
File has specified numeric group ID.
.TP
.BR --mdt-count | -T
The DNE striped directory has the given number of MDT shards.
.TP
+.BR --mirror-count | -N
+The file has \fIn\fR mirrors in its layout.
+.TP
+.BR --mirror-state
+The file has a state of
+.I state.
+If
+.BI ^ state
+is used, print only files not matching
+.IR state.
+Only one state can be specified. Valid state name is:
+.RS 1.2i
+.TP
+.B ro
+The mirrored file is in read-only state. All of the mirrors contain
+the up-to-date data.
+.TP
+.B wp
+The mirrored file is in a state of being written.
+.TP
+.B sp
+The mirrored file is in a state of being resynchronized.
+.RE
+.TP
.BR --mtime | -M
-File's data was last modified \fIn\fR*24 hours ago.
+File's data was last modified \fIn\fR*24 hours ago, see
+--atime
+for full details and options.
.TP
.BR --name | -n
Filename matches the given filename, or regular expression using
using the whole OST target name, or just the OST index number. If multiple
OSTs are given in a comma-separated list, the file may have an object on
any of the given OSTs. Specifying multiple OSTs allows scanning the
-filesystem only once when migrating objects off the OSTs for evacuation
+filesystem only once when migrating objects off multiple OSTs for evacuation
and replacement using
.BR lfs-migrate (1).
.TP
fp_obds_printed:1;
unsigned int fp_depth;
unsigned int fp_hash_type;
+ unsigned int fp_time_margin;
};
int llapi_ostlist(char *path, struct find_param *param);
}
run_test 56o "check lfs find -mtime for old files"
+test_56ob() {
+ local dir=$DIR/$tdir
+ local expected=1
+ local count=0
+
+ # just to make sure there is something that won't be found
+ test_mkdir $dir
+ touch $dir/$tfile.now
+
+ for age in year week day hour min; do
+ count=$((count + 1))
+
+ touch $dir/$tfile-a.$age $dir/$tfile-m.$age
+ touch --date="$count $age ago" -a $dir/$tfile-a.$age
+ touch --date="$count $age ago" -m $dir/$tfile-m.$age
+
+ local cmd="$LFS find $dir -mtime $count${age:0:1}"
+ local nums=$($cmd | wc -l)
+ [ $nums -eq $expected ] ||
+ error "'$cmd' wrong: found $nums, expected $expected"
+
+ cmd="$LFS find $dir -atime $count${age:0:1}"
+ nums=$($cmd | wc -l)
+ [ $nums -eq $expected ] ||
+ error "'$cmd' wrong: found $nums, expected $expected"
+ done
+
+ sleep 2
+ cmd="$LFS find $dir -ctime +1s -type f"
+ nums=$($cmd | wc -l)
+ (( $nums == $count * 2 + 1)) ||
+ error "'$cmd' wrong: found $nums, expected $((expected*2+1))"
+}
+run_test 56ob "check lfs find -atime -mtime -ctime with units"
+
test_56p() {
[ $RUNAS_ID -eq $UID ] &&
skip_env "RUNAS_ID = UID = $UID -- skipping"
{"find", lfs_find, 0,
"find files matching given attributes recursively in directory tree.\n"
"usage: find <directory|filename> ...\n"
- " [[!] --atime|-A [+-]N] [[!] --ctime|-C [+-]N]\n"
- " [[!] --mtime|-M [+-]N] [--maxdepth|-D N] [[!] --blocks|-b N]\n"
+ " [[!] --atime|-A [+-]N[smhdwy]] [[!] --ctime|-C [+-]N[smhdwy]]\n"
+ " [[!] --mtime|-M [+-]N[smhdwy]] [[!] --blocks|-b N]\n"
+ " [--maxdepth|-D N] [[!] --mdt-index|--mdt|-m <uuid|index,...>]\n"
" [[!] --name|-n <pattern>] [[!] --ost|-O <uuid|index,...>]\n"
" [--print|-P] [--print0|-0] [[!] --size|-s [+-]N[bkMGTPE]]\n"
" [[!] --stripe-count|-c [+-]<stripes>]\n"
return llapi_poollist(argv[1]);
}
-static int set_time(time_t *time, time_t *set, char *str)
+static time_t set_time(struct find_param *param, time_t *time, time_t *set,
+ char *str)
{
- time_t t;
+ long long t = 0;
int res = 0;
- char *endptr;
+ char *endptr = "AD";
+ char *timebuf;
if (str[0] == '+')
res = 1;
if (res)
str++;
- t = strtol(str, &endptr, 0);
- if (*endptr != '\0') {
- fprintf(stderr,
- "%s find: bad time '%s': %s\n",
- progname, str, strerror(EINVAL));
- return INT_MAX;
+ for (timebuf = str; *endptr && *(endptr + 1); timebuf = endptr + 1) {
+ long long val = strtoll(timebuf, &endptr, 0);
+ int unit = 1;
+
+ switch (*endptr) {
+ case 'y':
+ unit *= 52; /* 52 weeks + 1 day below */
+ case 'w': /* fallthrough */
+ unit *= 7;
+ case '\0': /* days are default unit if none used */
+ case 'd': /* fallthrough */
+ unit = (unit + (*endptr == 'y')) * 24;
+ case 'h': /* fallthrough */
+ unit *= 60;
+ case 'm': /* fallthrough */
+ unit *= 60;
+ case 's': /* fallthrough */
+ break;
+ /* don't need to multiply by 1 for seconds */
+ default:
+ fprintf(stderr,
+ "%s find: bad time string '%s': %s\n",
+ progname, timebuf, strerror(EINVAL));
+ return LONG_MAX;
+ }
+ if (*endptr && unit < 24 * 60 * 60)
+ param->fp_time_margin = unit;
+
+ t += val * unit;
}
- if (*time < t * 24 * 60 * 60) {
+ if (*time < t) {
if (res != 0)
str--;
- fprintf(stderr,
- "%s find: bad time '%s': too large\n",
+ fprintf(stderr, "%s find: bad time '%s': too large\n",
progname, str);
- return INT_MAX;
+ return LONG_MAX;
}
- *set = *time - t * 24 * 60 * 60;
+ *set = *time - t;
return res;
}
struct find_param param = {
.fp_max_depth = -1,
.fp_quiet = 1,
+ .fp_time_margin = 24 * 60 * 60,
};
struct option long_opts[] = {
{ .val = 'A', .name = "atime", .has_arg = required_argument },
xsign = ¶m.fp_msign;
param.fp_exclude_mtime = !!neg_opt;
}
- rc = set_time(&t, xtime, optarg);
- if (rc == INT_MAX) {
+ rc = set_time(¶m, &t, xtime, optarg);
+ if (rc == LONG_MAX) {
ret = -1;
goto err;
}
if (param->fp_atime) {
rc2 = find_value_cmp(st->st_atime, param->fp_atime,
param->fp_asign, param->fp_exclude_atime,
- 24 * 60 * 60, mds);
+ param->fp_time_margin, mds);
if (rc2 < 0)
return rc2;
rc = rc2;
if (param->fp_mtime) {
rc2 = find_value_cmp(st->st_mtime, param->fp_mtime,
param->fp_msign, param->fp_exclude_mtime,
- 24 * 60 * 60, mds);
+ param->fp_time_margin, mds);
if (rc2 < 0)
return rc2;
if (param->fp_ctime) {
rc2 = find_value_cmp(st->st_ctime, param->fp_ctime,
param->fp_csign, param->fp_exclude_ctime,
- 24 * 60 * 60, mds);
+ param->fp_time_margin, mds);
if (rc2 < 0)
return rc2;