ls: support --time=creation to show/sort birth time

* src/ls.c (usage): Reorganize help for --time,
and add description for --time=birth.
(do_statx): Store btime in mtime if available.
(get_stat_btime): A new function to read the creation time
from the appropriate stat structure member.
(cmp_btime): A new function to compare birth time.
(print_long_format): Output '?' when birth time unavailable.
* doc/coreutils.texi: Document --time={birth,creation}.
* tests/local.mk: Reference the new test.
* tests/ls/birthtime.sh: Add a new test.
* NEWS: Mention the new feature.
This commit is contained in:
Pádraig Brady 2020-01-02 16:20:13 +00:00
parent dda53d75a8
commit 2cecc3cc99
5 changed files with 116 additions and 14 deletions

3
NEWS
View File

@ -62,6 +62,9 @@ GNU coreutils NEWS -*- outline -*-
** New Features
ls now supports the --time=birth option to display and sort by
file creation time, where available.
od --skip-bytes now can use lseek even if the input is not a regular
file, greatly improving performance in some cases.

View File

@ -7877,7 +7877,8 @@ Sort by file size, largest first.
@opindex -t
@opindex --sort
@opindex modification timestamp@r{, sorting files by}
Sort by modification timestamp (mtime), newest first.
Sort by modification timestamp (mtime) by default, newest first.
The timestamp to order by can be changed with the @option{--time} option.
@xref{File timestamps}.
@item -u
@ -7895,6 +7896,17 @@ When explicitly sorting by time (@option{--sort=time} or @option{-t})
or when not using a long listing format, sort according to the atime.
@xref{File timestamps}.
@item --time=birth
@itemx --time=creation
@opindex --time
@opindex birth time@r{, printing or sorting files by}
@opindex creation timestamp@r{, printing or sorting files by}
If the long listing format (e.g., @option{--format=long}) is being used,
print the file creation timestamp if available.
When explicitly sorting by time (@option{--sort=time} or @option{-t})
or when not using a long listing format, sort according to the birth time.
@xref{File timestamps}.
@item -U
@itemx --sort=none
@opindex -U

View File

@ -460,6 +460,7 @@ enum time_type
time_mtime, /* default */
time_ctime, /* -c */
time_atime, /* -u */
time_btime, /* birth time */
time_numtypes /* the number of elements of this enum */
};
@ -912,11 +913,16 @@ ARGMATCH_VERIFY (sort_args, sort_types);
static char const *const time_args[] =
{
"atime", "access", "use", "ctime", "status", NULL
"atime", "access", "use",
"ctime", "status",
"birth", "creation",
NULL
};
static enum time_type const time_types[] =
{
time_atime, time_atime, time_atime, time_ctime, time_ctime
time_atime, time_atime, time_atime,
time_ctime, time_ctime,
time_btime, time_btime,
};
ARGMATCH_VERIFY (time_args, time_types);
@ -1065,6 +1071,24 @@ dired_dump_obstack (const char *prefix, struct obstack *os)
}
}
/* Return the platform birthtime member of the stat structure,
or fallback to the mtime member, which we have populated
from the statx structure or reset to an invalid timestamp
where birth time is not supported. */
static struct timespec
get_stat_btime (struct stat const *st)
{
struct timespec btimespec;
#if HAVE_STATX && defined STATX_INO
btimespec = get_stat_mtime (st);
#else
btimespec = get_stat_birthtime (st);
#endif
return btimespec;
}
#if HAVE_STATX && defined STATX_INO
static unsigned int _GL_ATTRIBUTE_PURE
time_type_to_statx (void)
@ -1077,6 +1101,8 @@ time_type_to_statx (void)
return STATX_MTIME;
case time_atime:
return STATX_ATIME;
case time_btime:
return STATX_BTIME;
default:
abort ();
}
@ -1127,9 +1153,22 @@ do_statx (int fd, const char *name, struct stat *st, int flags,
unsigned int mask)
{
struct statx stx;
bool want_btime = mask & STATX_BTIME;
int ret = statx (fd, name, flags, mask, &stx);
if (ret >= 0)
statx_to_stat (&stx, st);
{
statx_to_stat (&stx, st);
/* Since we only need one timestamp type,
store birth time in st_mtim. */
if (want_btime)
{
if (stx.stx_mask & STATX_BTIME)
st->st_mtim = statx_timestamp_to_timespec (stx.stx_btime);
else
st->st_mtim.tv_sec = st->st_mtim.tv_nsec = -1;
}
}
return ret;
}
@ -2298,7 +2337,8 @@ decode_switches (int argc, char **argv)
-lu means show atime and sort by name, -lut means show atime and sort
by atime. */
if ((time_type == time_ctime || time_type == time_atime)
if ((time_type == time_ctime || time_type == time_atime
|| time_type == time_btime)
&& !sort_type_specified && format != long_format)
{
sort_type = sort_time;
@ -3785,6 +3825,15 @@ cmp_atime (struct fileinfo const *a, struct fileinfo const *b,
return diff ? diff : cmp (a->name, b->name);
}
static inline int
cmp_btime (struct fileinfo const *a, struct fileinfo const *b,
int (*cmp) (char const *, char const *))
{
int diff = timespec_cmp (get_stat_btime (&b->stat),
get_stat_btime (&a->stat));
return diff ? diff : cmp (a->name, b->name);
}
static inline int
cmp_size (struct fileinfo const *a, struct fileinfo const *b,
int (*cmp) (char const *, char const *))
@ -3816,6 +3865,7 @@ cmp_extension (struct fileinfo const *a, struct fileinfo const *b,
DEFINE_SORT_FUNCTIONS (ctime, cmp_ctime)
DEFINE_SORT_FUNCTIONS (mtime, cmp_mtime)
DEFINE_SORT_FUNCTIONS (atime, cmp_atime)
DEFINE_SORT_FUNCTIONS (btime, cmp_btime)
DEFINE_SORT_FUNCTIONS (size, cmp_size)
DEFINE_SORT_FUNCTIONS (name, cmp_name)
DEFINE_SORT_FUNCTIONS (extension, cmp_extension)
@ -3892,7 +3942,8 @@ static qsortFunc const sort_functions[][2][2][2] =
/* last are time sort functions */
LIST_SORTFUNCTION_VARIANTS (mtime),
LIST_SORTFUNCTION_VARIANTS (ctime),
LIST_SORTFUNCTION_VARIANTS (atime)
LIST_SORTFUNCTION_VARIANTS (atime),
LIST_SORTFUNCTION_VARIANTS (btime)
};
/* The number of sort keys is calculated as the sum of
@ -4162,6 +4213,7 @@ print_long_format (const struct fileinfo *f)
char *p;
struct timespec when_timespec;
struct tm when_local;
bool btime_ok = true;
/* Compute the mode string, except remove the trailing space if no
file in this directory has an ACL or security context. */
@ -4191,6 +4243,11 @@ print_long_format (const struct fileinfo *f)
case time_atime:
when_timespec = get_stat_atime (&f->stat);
break;
case time_btime:
when_timespec = get_stat_btime (&f->stat);
if (when_timespec.tv_sec == -1 && when_timespec.tv_nsec == -1)
btime_ok = false;
break;
default:
abort ();
}
@ -4291,7 +4348,8 @@ print_long_format (const struct fileinfo *f)
s = 0;
*p = '\1';
if (f->stat_ok && localtime_rz (localtz, &when_timespec.tv_sec, &when_local))
if (f->stat_ok && btime_ok
&& localtime_rz (localtz, &when_timespec.tv_sec, &when_local))
{
struct timespec six_months_ago;
bool recent;
@ -4332,7 +4390,7 @@ print_long_format (const struct fileinfo *f)
print it as a huge integer number of seconds. */
char hbuf[INT_BUFSIZE_BOUND (intmax_t)];
sprintf (p, "%*s ", long_time_expected_width (),
(! f->stat_ok
(! f->stat_ok || ! btime_ok
? "?"
: timetostr (when_timespec.tv_sec, hbuf)));
/* FIXME: (maybe) We discarded when_timespec.tv_nsec. */
@ -5380,17 +5438,18 @@ Sort entries alphabetically if none of -cftuvSUX nor --sort is specified.\n\
--sort=WORD sort by WORD instead of name: none (-U), size (-S)\
,\n\
time (-t), version (-v), extension (-X)\n\
--time=WORD with -l, show time as WORD instead of default\n\
modification time: atime or access or use (-u);\
\n\
ctime or status (-c); also use specified time\n\
as sort key if --sort=time (newest first)\n\
--time=WORD change the default of using modification times;\n\
access time (-u): atime, access, use;\n\
change time (-c): ctime, status;\n\
birth time: birth, creation;\n\
with -l, WORD determines which time to show;\n\
with --sort=time, sort by WORD (newest first)\n\
"), stdout);
fputs (_("\
--time-style=TIME_STYLE time/date format with -l; see TIME_STYLE below\n\
"), stdout);
fputs (_("\
-t sort by modification time, newest first\n\
-t sort by time, newest first; see --time\n\
-T, --tabsize=COLS assume tab stops at each COLS instead of 8\n\
"), stdout);
fputs (_("\

View File

@ -589,6 +589,7 @@ all_tests = \
tests/ln/target-1.sh \
tests/ls/a-option.sh \
tests/ls/abmon-align.sh \
tests/ls/birthtime.sh \
tests/ls/block-size.sh \
tests/ls/color-clear-to-eol.sh \
tests/ls/color-dtype-dir.sh \

27
tests/ls/birthtime.sh Executable file
View File

@ -0,0 +1,27 @@
#!/bin/sh
# ensure that ls attempts birthtime access
# Copyright (C) 2020 Free Software Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
print_ver_ ls
# ls should not fail, even if birth time not available
touch a || framework_failure_
ls --time=birth -l a || fail=1
ls --time=creation -t a || fail=1
Exit $fail