Whamcloud - gitweb
LU-12027 utils: add units to "lfs find -amctime" 67/34367/5
authorAndreas Dilger <adilger@whamcloud.com>
Wed, 27 Feb 2019 09:25:23 +0000 (02:25 -0700)
committerOleg Drokin <green@whamcloud.com>
Sat, 13 Apr 2019 04:50:04 +0000 (04:50 +0000)
The normal find command can only specify time arguments in terms
of whole days. The MacOS find(1) man page reports the use of unit
suffixes to give better control over the time range, such as "1d4h".

Possible time units are as follows:

    s       second
    m       minute (60 seconds)
    h       hour (60 minutes)
    d       day (24 hours)
    w       week (7 days)
    y       year (365 days)

There is no "month" specifier here or in find(1), since the length
of a month is not fixed, and it would conflict with the 'm' minutes
unit.  Units may be combined in one argument, e.g. "-atime -1h30m".

For matches that are "equal" to the specified time, they must match
within the smallest unit specified.  For example, "-mtime 24h" would
match anything within 1h of 24h ago, similar to "-size 100M" will
match anything within 1MB of 100MB.

Test-Parameters: trivial fstype=zfs
Signed-off-by: Andreas Dilger <adilger@whamcloud.com>
Change-Id: Ib1eb4e626b712bb75f13b075849f959f003ebbe5
Reviewed-on: https://review.whamcloud.com/34367
Reviewed-by: Wang Shilong <wshilong@ddn.com>
Reviewed-by: Li Xi <lixi@ddn.com>
Tested-by: Jenkins
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
lustre/doc/lfs-find.1
lustre/include/lustre/lustreapi.h
lustre/tests/sanity.sh
lustre/utils/lfs.c
lustre/utils/liblustreapi.c

index e842178..29fb105 100644 (file)
@@ -3,24 +3,24 @@
 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]
@@ -45,15 +45,22 @@ OST and MDT location, and composite layout attributes.
 .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.
@@ -90,30 +97,6 @@ local on the network to clients.
 .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
@@ -156,8 +139,34 @@ sequential filenames.
 .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
@@ -170,7 +179,7 @@ File has an object on the specified OST(s).  The OST names can be specified
 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
index ba1e603..f8466e3 100644 (file)
@@ -320,6 +320,7 @@ struct find_param {
                                 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);
index b6fec4f..920e089 100755 (executable)
@@ -5080,6 +5080,41 @@ test_56o() {
 }
 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"
index e4a26f2..193eb5d 100644 (file)
@@ -386,8 +386,9 @@ command_t cmdlist[] = {
        {"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"
@@ -3406,11 +3407,13 @@ static int lfs_poollist(int argc, char **argv)
         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;
@@ -3420,23 +3423,45 @@ static int set_time(time_t *time, time_t *set, char *str)
        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;
 }
 
@@ -3522,6 +3547,7 @@ static int lfs_find(int argc, char **argv)
        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 },
@@ -3654,8 +3680,8 @@ static int lfs_find(int argc, char **argv)
                                xsign = &param.fp_msign;
                                param.fp_exclude_mtime = !!neg_opt;
                        }
-                       rc = set_time(&t, xtime, optarg);
-                       if (rc == INT_MAX) {
+                       rc = set_time(&param, &t, xtime, optarg);
+                       if (rc == LONG_MAX) {
                                ret = -1;
                                goto err;
                        }
index 67eb90b..f14d288 100644 (file)
@@ -3646,7 +3646,7 @@ static int find_time_check(lstat_t *st, struct find_param *param, int mds)
        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;
@@ -3655,7 +3655,7 @@ static int find_time_check(lstat_t *st, struct find_param *param, int mds)
        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;
 
@@ -3668,7 +3668,7 @@ static int find_time_check(lstat_t *st, struct find_param *param, int mds)
        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;