[RFC] io: Return UNSUPPORTED if filesystem do not support 64 bit timestamps

Message ID 20210311123557.3486677-1-adhemerval.zanella@linaro.org
State Superseded
Headers
Series [RFC] io: Return UNSUPPORTED if filesystem do not support 64 bit timestamps |

Commit Message

Adhemerval Zanella Netto March 11, 2021, 12:35 p.m. UTC
  Some Linux filesystems might not fully support 64 bit timestamps [1],
which make some Linux specific tests to fail when they check for the
functionality.

This patch make them return as 'unsupported' is the target file is
created on a buggy filesystem:

  $ dd if=/dev/zero of=loopbackfile.img bs=100M count=1
  1+0 records in
  1+0 records out
  104857600 bytes (105 MB, 100 MiB) copied, 0,0589568 s, 1,8 GB/s
  $ sudo losetup -fP loopbackfile.img
  $ mkfs.xfs loopbackfile.img
  meta-data=loopbackfile.img       isize=512    agcount=4, agsize=6400 blks
           =                       sectsz=512   attr=2, projid32bit=1
           =                       crc=1        finobt=1, sparse=1, rmapbt=0
           =                       reflink=1
  data     =                       bsize=4096   blocks=25600, imaxpct=25
           =                       sunit=0      swidth=0 blks
  naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
  log      =internal log           bsize=4096   blocks=1368, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
  realtime =none                   extsz=4096   blocks=0, rtextents=0
  $ mkdir loopfs
  $ sudo mount -o loop /dev/loop0 loopfs/
  $ sudo chown -R azanella:azanella loopfs
  $ TMPDIR=loopfs/ ./testrun.sh misc/tst-utimes
  error: ../sysdeps/unix/sysv/linux/tst-utimes.c:55: File loopfs//utimesfECsK1 does not support 64-bit timestamps

[1] https://bugzilla.redhat.com/show_bug.cgi?id=1795576
---
 support/Makefile                       |  1 +
 support/support.h                      |  5 ++++
 support/support_path_support_time64.c  | 38 ++++++++++++++++++++++++++
 sysdeps/unix/sysv/linux/tst-futimens.c |  8 +++++-
 sysdeps/unix/sysv/linux/tst-utime.c    |  7 ++++-
 sysdeps/unix/sysv/linux/tst-utimes.c   |  7 ++++-
 6 files changed, 63 insertions(+), 3 deletions(-)
 create mode 100644 support/support_path_support_time64.c
  

Comments

Christoph Hellwig March 11, 2021, 3:08 p.m. UTC | #1
On Thu, Mar 11, 2021 at 09:35:57AM -0300, Adhemerval Zanella via Libc-alpha wrote:
> +bool
> +support_path_support_time64 (const char *path)
> +{
> +#ifdef __linux__
> +  struct statfs64 fs;
> +  if (statfs64 (path, &fs) < 0)
> +    FAIL_EXIT1 ("statfs64 (\"%s\"): %m", path);
> +  /* Kernel silent truncate metadata timestamps on some version.  */
> +  return ! (fs.f_type == XFS_SUPER_MAGIC);

Please check for the actual truncation, as XFS does not always do
this based on the exact on-disk format revision.
  
Lukasz Majewski March 11, 2021, 3:32 p.m. UTC | #2
On Thu, 11 Mar 2021 09:35:57 -0300
Adhemerval Zanella <adhemerval.zanella@linaro.org> wrote:

> Some Linux filesystems might not fully support 64 bit timestamps [1],
> which make some Linux specific tests to fail when they check for the
> functionality.
> 
> This patch make them return as 'unsupported' is the target file is
> created on a buggy filesystem:
> 
>   $ dd if=/dev/zero of=loopbackfile.img bs=100M count=1
>   1+0 records in
>   1+0 records out
>   104857600 bytes (105 MB, 100 MiB) copied, 0,0589568 s, 1,8 GB/s
>   $ sudo losetup -fP loopbackfile.img
>   $ mkfs.xfs loopbackfile.img
>   meta-data=loopbackfile.img       isize=512    agcount=4,
> agsize=6400 blks =                       sectsz=512   attr=2,
> projid32bit=1 =                       crc=1        finobt=1,
> sparse=1, rmapbt=0 =                       reflink=1
>   data     =                       bsize=4096   blocks=25600,
> imaxpct=25 =                       sunit=0      swidth=0 blks
>   naming   =version 2              bsize=4096   ascii-ci=0, ftype=1
>   log      =internal log           bsize=4096   blocks=1368, version=2
>          =                       sectsz=512   sunit=0 blks,
> lazy-count=1 realtime =none                   extsz=4096   blocks=0,
> rtextents=0 $ mkdir loopfs
>   $ sudo mount -o loop /dev/loop0 loopfs/
>   $ sudo chown -R azanella:azanella loopfs
>   $ TMPDIR=loopfs/ ./testrun.sh misc/tst-utimes
>   error: ../sysdeps/unix/sysv/linux/tst-utimes.c:55: File
> loopfs//utimesfECsK1 does not support 64-bit timestamps
> 
> [1] https://bugzilla.redhat.com/show_bug.cgi?id=1795576
> ---
>  support/Makefile                       |  1 +
>  support/support.h                      |  5 ++++
>  support/support_path_support_time64.c  | 38
> ++++++++++++++++++++++++++ sysdeps/unix/sysv/linux/tst-futimens.c |
> 8 +++++- sysdeps/unix/sysv/linux/tst-utime.c    |  7 ++++-
>  sysdeps/unix/sysv/linux/tst-utimes.c   |  7 ++++-
>  6 files changed, 63 insertions(+), 3 deletions(-)
>  create mode 100644 support/support_path_support_time64.c
> 
> diff --git a/support/Makefile b/support/Makefile
> index 8d63fbd5da..5904cdd412 100644
> --- a/support/Makefile
> +++ b/support/Makefile
> @@ -59,6 +59,7 @@ libsupport-routines = \
>    support_format_hostent \
>    support_format_netent \
>    support_isolate_in_subprocess \
> +  support_path_support_time64 \
>    support_process_state \
>    support_ptrace \
>    support_openpty \
> diff --git a/support/support.h b/support/support.h
> index 9cbc455720..2e477c9e7c 100644
> --- a/support/support.h
> +++ b/support/support.h
> @@ -23,6 +23,7 @@
>  #ifndef SUPPORT_H
>  #define SUPPORT_H
>  
> +#include <stdbool.h>
>  #include <stddef.h>
>  #include <sys/cdefs.h>
>  /* For mode_t.  */
> @@ -129,6 +130,10 @@ extern void support_copy_file (const char *from,
> const char *to); extern ssize_t support_copy_file_range (int, off64_t
> *, int, off64_t *, size_t, unsigned int);
>  
> +/* Return true is PATH supports 64-bit time_t interfaces for file
> +   operations (such as fstatat or utimensat).  */
> +extern bool support_path_support_time64 (const char *path);
> +
>  __END_DECLS
>  
>  #endif /* SUPPORT_H */
> diff --git a/support/support_path_support_time64.c
> b/support/support_path_support_time64.c new file mode 100644
> index 0000000000..c780dcd4aa
> --- /dev/null
> +++ b/support/support_path_support_time64.c
> @@ -0,0 +1,38 @@
> +/* Check if path supports 64-bit time interfaces.
> +   Copyright (C) 2021 Free Software Foundation, Inc.
> +   This file is part of the GNU C Library.
> +
> +   The GNU C Library is free software; you can redistribute it and/or
> +   modify it under the terms of the GNU Lesser General Public
> +   License as published by the Free Software Foundation; either
> +   version 2.1 of the License, or (at your option) any later version.
> +
> +   The GNU C Library 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
> +   Lesser General Public License for more details.
> +
> +   You should have received a copy of the GNU Lesser General Public
> +   License along with the GNU C Library; if not, see
> +   <https://www.gnu.org/licenses/>.  */
> +
> +#include <support/check.h>
> +#include <support/support.h>
> +#include <sys/vfs.h>
> +#ifdef __linux
> +#include <linux_fsinfo.h>
> +#endif
> +
> +bool
> +support_path_support_time64 (const char *path)
> +{
> +#ifdef __linux__
> +  struct statfs64 fs;
> +  if (statfs64 (path, &fs) < 0)
> +    FAIL_EXIT1 ("statfs64 (\"%s\"): %m", path);
> +  /* Kernel silent truncate metadata timestamps on some version.  */
> +  return ! (fs.f_type == XFS_SUPER_MAGIC);
> +#else
> +  return true;
> +#endif
> +}
> diff --git a/sysdeps/unix/sysv/linux/tst-futimens.c
> b/sysdeps/unix/sysv/linux/tst-futimens.c index 8f48cfee63..785cd87557
> 100644 --- a/sysdeps/unix/sysv/linux/tst-futimens.c
> +++ b/sysdeps/unix/sysv/linux/tst-futimens.c
> @@ -21,10 +21,12 @@
>  #include <stdlib.h>
>  #include <sys/stat.h>
>  #include <support/check.h>
> +#include <support/support.h>
>  #include <support/xunistd.h>
>  #include <support/temp_file.h>
>  
>  static int temp_fd = -1;
> +static char *testfile;
>  
>  /* struct timespec array with Y2038 threshold minus 2 and 1 seconds.
>  */ const struct timespec t1[2] = { { 0x7FFFFFFE, 0 },  { 0x7FFFFFFF,
> 0 } }; @@ -39,13 +41,17 @@ const struct timespec t3[2] = { {
> 0x7FFFFFFE, 0 },  { 0x80000002ULL, 0 } }; static void
>  do_prepare (int argc, char *argv[])
>  {
> -  temp_fd = create_temp_file ("futimensat", NULL);
> +  temp_fd = create_temp_file ("futimensat", &testfile);
>    TEST_VERIFY_EXIT (temp_fd > 0);
>  }
>  
>  static int
>  test_futimens_helper (const struct timespec *ts)
>  {
> +  if (!support_path_support_time64 (testfile))
> +    FAIL_UNSUPPORTED ("File %s does not support 64-bit timestamps",
> +		      testfile);
> +
>    struct stat64 st;
>    int result;
>    time_t t;
> diff --git a/sysdeps/unix/sysv/linux/tst-utime.c
> b/sysdeps/unix/sysv/linux/tst-utime.c index 6735421657..21e4e41dea
> 100644 --- a/sysdeps/unix/sysv/linux/tst-utime.c
> +++ b/sysdeps/unix/sysv/linux/tst-utime.c
> @@ -23,11 +23,12 @@
>  #include <sys/types.h>
>  #include <utime.h>
>  #include <support/check.h>
> +#include <support/support.h>
>  #include <support/xunistd.h>
>  #include <support/temp_file.h>
>  
>  static int temp_fd = -1;
> -char *testfile;
> +static char *testfile;
>  
>  /* struct utimbuf with Y2038 threshold minus 2 and 1 seconds.  */
>  const static struct utimbuf t1 = { 0x7FFFFFFE, 0x7FFFFFFF };
> @@ -49,6 +50,10 @@ do_prepare (int argc, char *argv[])
>  static int
>  test_utime_helper (const struct utimbuf *ut)
>  {
> +  if (!support_path_support_time64 (testfile))
> +    FAIL_UNSUPPORTED ("File %s does not support 64-bit timestamps",
> +		      testfile);
> +
>    struct stat64 st;
>    int result;
>    time_t t;
> diff --git a/sysdeps/unix/sysv/linux/tst-utimes.c
> b/sysdeps/unix/sysv/linux/tst-utimes.c index 8c7b006a22..0f23e44897
> 100644 --- a/sysdeps/unix/sysv/linux/tst-utimes.c
> +++ b/sysdeps/unix/sysv/linux/tst-utimes.c
> @@ -22,11 +22,12 @@
>  #include <sys/stat.h>
>  #include <sys/time.h>
>  #include <support/check.h>
> +#include <support/support.h>
>  #include <support/xunistd.h>
>  #include <support/temp_file.h>
>  
>  static int temp_fd = -1;
> -char *testfile;
> +static char *testfile;
>  
>  /* struct timeval array with Y2038 threshold minus 2 and 1 seconds.
> */ const static struct timeval t1[2] = { { 0x7FFFFFFE, 0 },  {
> 0x7FFFFFFF, 0 } }; @@ -50,6 +51,10 @@ do_prepare (int argc, char
> *argv[]) static int
>  test_utime_helper (const struct timeval *tv)
>  {
> +  if (!support_path_support_time64 (testfile))
> +    FAIL_UNSUPPORTED ("File %s does not support 64-bit timestamps",
> +		      testfile);
> +
>    struct stat64 st;
>    int result;
>    time_t t;

Approach proposed in this patch seems to be logical, as those tests
shall simply be "UNSUPPORTED" when called on FS not supporting 64 bit
time_t.

Reviewed-by: Lukasz Majewski <lukma@denx.de>


Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de
  
Adhemerval Zanella Netto March 11, 2021, 5:20 p.m. UTC | #3
On 11/03/2021 12:08, Christoph Hellwig wrote:
> On Thu, Mar 11, 2021 at 09:35:57AM -0300, Adhemerval Zanella via Libc-alpha wrote:
>> +bool
>> +support_path_support_time64 (const char *path)
>> +{
>> +#ifdef __linux__
>> +  struct statfs64 fs;
>> +  if (statfs64 (path, &fs) < 0)
>> +    FAIL_EXIT1 ("statfs64 (\"%s\"): %m", path);
>> +  /* Kernel silent truncate metadata timestamps on some version.  */
>> +  return ! (fs.f_type == XFS_SUPER_MAGIC);
> 
> Please check for the actual truncation, as XFS does not always do
> this based on the exact on-disk format revision.
> 

It would require to emulate a syscall that sets/reset some timestamp
on the filsystem without using the glibc interfaces (since we are
working on providing y2038 support for old ABI with 32-bit time as
default).

I will check out a better to way to do it, but this ad-hoc solution
is just to be more friendly on system with buggy filesystem.  My
preferred personal solution is let the tests fail and warn the
tests something is wrong.
  

Patch

diff --git a/support/Makefile b/support/Makefile
index 8d63fbd5da..5904cdd412 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -59,6 +59,7 @@  libsupport-routines = \
   support_format_hostent \
   support_format_netent \
   support_isolate_in_subprocess \
+  support_path_support_time64 \
   support_process_state \
   support_ptrace \
   support_openpty \
diff --git a/support/support.h b/support/support.h
index 9cbc455720..2e477c9e7c 100644
--- a/support/support.h
+++ b/support/support.h
@@ -23,6 +23,7 @@ 
 #ifndef SUPPORT_H
 #define SUPPORT_H
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <sys/cdefs.h>
 /* For mode_t.  */
@@ -129,6 +130,10 @@  extern void support_copy_file (const char *from, const char *to);
 extern ssize_t support_copy_file_range (int, off64_t *, int, off64_t *,
 					size_t, unsigned int);
 
+/* Return true is PATH supports 64-bit time_t interfaces for file
+   operations (such as fstatat or utimensat).  */
+extern bool support_path_support_time64 (const char *path);
+
 __END_DECLS
 
 #endif /* SUPPORT_H */
diff --git a/support/support_path_support_time64.c b/support/support_path_support_time64.c
new file mode 100644
index 0000000000..c780dcd4aa
--- /dev/null
+++ b/support/support_path_support_time64.c
@@ -0,0 +1,38 @@ 
+/* Check if path supports 64-bit time interfaces.
+   Copyright (C) 2021 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library 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
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <support/check.h>
+#include <support/support.h>
+#include <sys/vfs.h>
+#ifdef __linux
+#include <linux_fsinfo.h>
+#endif
+
+bool
+support_path_support_time64 (const char *path)
+{
+#ifdef __linux__
+  struct statfs64 fs;
+  if (statfs64 (path, &fs) < 0)
+    FAIL_EXIT1 ("statfs64 (\"%s\"): %m", path);
+  /* Kernel silent truncate metadata timestamps on some version.  */
+  return ! (fs.f_type == XFS_SUPER_MAGIC);
+#else
+  return true;
+#endif
+}
diff --git a/sysdeps/unix/sysv/linux/tst-futimens.c b/sysdeps/unix/sysv/linux/tst-futimens.c
index 8f48cfee63..785cd87557 100644
--- a/sysdeps/unix/sysv/linux/tst-futimens.c
+++ b/sysdeps/unix/sysv/linux/tst-futimens.c
@@ -21,10 +21,12 @@ 
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <support/check.h>
+#include <support/support.h>
 #include <support/xunistd.h>
 #include <support/temp_file.h>
 
 static int temp_fd = -1;
+static char *testfile;
 
 /* struct timespec array with Y2038 threshold minus 2 and 1 seconds.  */
 const struct timespec t1[2] = { { 0x7FFFFFFE, 0 },  { 0x7FFFFFFF, 0 } };
@@ -39,13 +41,17 @@  const struct timespec t3[2] = { { 0x7FFFFFFE, 0 },  { 0x80000002ULL, 0 } };
 static void
 do_prepare (int argc, char *argv[])
 {
-  temp_fd = create_temp_file ("futimensat", NULL);
+  temp_fd = create_temp_file ("futimensat", &testfile);
   TEST_VERIFY_EXIT (temp_fd > 0);
 }
 
 static int
 test_futimens_helper (const struct timespec *ts)
 {
+  if (!support_path_support_time64 (testfile))
+    FAIL_UNSUPPORTED ("File %s does not support 64-bit timestamps",
+		      testfile);
+
   struct stat64 st;
   int result;
   time_t t;
diff --git a/sysdeps/unix/sysv/linux/tst-utime.c b/sysdeps/unix/sysv/linux/tst-utime.c
index 6735421657..21e4e41dea 100644
--- a/sysdeps/unix/sysv/linux/tst-utime.c
+++ b/sysdeps/unix/sysv/linux/tst-utime.c
@@ -23,11 +23,12 @@ 
 #include <sys/types.h>
 #include <utime.h>
 #include <support/check.h>
+#include <support/support.h>
 #include <support/xunistd.h>
 #include <support/temp_file.h>
 
 static int temp_fd = -1;
-char *testfile;
+static char *testfile;
 
 /* struct utimbuf with Y2038 threshold minus 2 and 1 seconds.  */
 const static struct utimbuf t1 = { 0x7FFFFFFE, 0x7FFFFFFF };
@@ -49,6 +50,10 @@  do_prepare (int argc, char *argv[])
 static int
 test_utime_helper (const struct utimbuf *ut)
 {
+  if (!support_path_support_time64 (testfile))
+    FAIL_UNSUPPORTED ("File %s does not support 64-bit timestamps",
+		      testfile);
+
   struct stat64 st;
   int result;
   time_t t;
diff --git a/sysdeps/unix/sysv/linux/tst-utimes.c b/sysdeps/unix/sysv/linux/tst-utimes.c
index 8c7b006a22..0f23e44897 100644
--- a/sysdeps/unix/sysv/linux/tst-utimes.c
+++ b/sysdeps/unix/sysv/linux/tst-utimes.c
@@ -22,11 +22,12 @@ 
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <support/check.h>
+#include <support/support.h>
 #include <support/xunistd.h>
 #include <support/temp_file.h>
 
 static int temp_fd = -1;
-char *testfile;
+static char *testfile;
 
 /* struct timeval array with Y2038 threshold minus 2 and 1 seconds.  */
 const static struct timeval t1[2] = { { 0x7FFFFFFE, 0 },  { 0x7FFFFFFF, 0 } };
@@ -50,6 +51,10 @@  do_prepare (int argc, char *argv[])
 static int
 test_utime_helper (const struct timeval *tv)
 {
+  if (!support_path_support_time64 (testfile))
+    FAIL_UNSUPPORTED ("File %s does not support 64-bit timestamps",
+		      testfile);
+
   struct stat64 st;
   int result;
   time_t t;