bug-binutils
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[binutils] ar -xo Archive reports cannot set time: Invalid argument


From: canpool
Subject: [binutils] ar -xo Archive reports cannot set time: Invalid argument
Date: Thu, 4 Aug 2022 22:42:40 +0800 (GMT+08:00)

Product:binutils
Tool:ar
Version:2.37

Here is a simple example on a openEuler-22.03-LTS machine running binutils/2.37:
-------
[root@19fe3bb99663 ~]# rpm -qa | grep binutils
binutils-2.37-6.oe2203.x86_64
[root@19fe3bb99663 ~]# touch 1.txt
[root@19fe3bb99663 ~]# ar -rvU 1.a 1.txt
ar: creating 1.a
a - 1.txt
[root@19fe3bb99663 ~]# rm -f 1.txt
[root@19fe3bb99663 ~]# ar -xo 1.a
ar: 1.txt: cannot set time: Invalid argument
----

gdb message:
Breakpoint 2, set_times (destination=0x555555573680 "1.txt", statbuf=0x7fffffffe900) at rename.c:168
168     {
(gdb)
Continuing.

Breakpoint 3, 0x00007ffff7d76940 in utimensat () from /usr/lib64/libc.so.6
(gdb) l
163     /* Set the times of the file DESTINATION to be the same as those in
164        STATBUF.  */
165
166     void
167     set_times (const char *destination, const struct stat *statbuf)
168     {
169       int result;
170     #if defined HAVE_UTIMENSAT
171       struct timespec times[2];
172       times[0] = get_stat_atime (statbuf);
(gdb)
173       times[1] = get_stat_mtime (statbuf);
174       result = utimensat (AT_FDCWD, destination, times, 0);
175     #elif defined HAVE_UTIMES
176       struct timeval tv[2];
177
178       tv[0].tv_sec = statbuf->st_atime;
179       tv[0].tv_usec = get_stat_atime_ns (statbuf) / 1000;
180       tv[1].tv_sec = statbuf->st_mtime;
181       tv[1].tv_usec = get_stat_mtime_ns (statbuf) / 1000;
182       result = utimes (destination, tv);
(gdb) disassemble
Dump of assembler code for function utimensat:
=> 0x00007ffff7d76940 <+0>:     endbr64
   0x00007ffff7d76944 <+4>:     mov    %ecx,%r10d
   0x00007ffff7d76947 <+7>:     test   %rsi,%rsi
   0x00007ffff7d7694a <+10>:    je     0x7ffff7d76972 <utimensat+50>
   0x00007ffff7d7694c <+12>:    mov    $0x118,%eax
   0x00007ffff7d76951 <+17>:    syscall
   0x00007ffff7d76953 <+19>:    cmp    $0xfffffffffffff000,%rax
   0x00007ffff7d76959 <+25>:    ja     0x7ffff7d76960 <utimensat+32>
   0x00007ffff7d7695b <+27>:    ret
   0x00007ffff7d7695c <+28>:    nopl   0x0(%rax)
   0x00007ffff7d76960 <+32>:    mov    0xeb491(%rip),%rdx        # 0x7ffff7e61df8
   0x00007ffff7d76967 <+39>:    neg    %eax
   0x00007ffff7d76969 <+41>:    mov    %eax,%fs:(%rdx)
   0x00007ffff7d7696c <+44>:    mov    $0xffffffff,%eax
   0x00007ffff7d76971 <+49>:    ret
   0x00007ffff7d76972 <+50>:    mov    0xeb47f(%rip),%rax        # 0x7ffff7e61df8
   0x00007ffff7d76979 <+57>:    movl   $0x16,%fs:(%rax)
   0x00007ffff7d76980 <+64>:    mov    $0xffffffff,%eax
   0x00007ffff7d76985 <+69>:    ret
End of assembler dump.
(gdb) n
Single stepping until exit from function utimensat,
which has no line number information.
set_times (destination=0x555555573680 "1.txt", statbuf=<optimized out>) at rename.c:197
197       if (result != 0)
(gdb) p result
$1 = -1
(gdb) p *(int *)__errno_location()
$2 = 22

The tv_nsec outside range 0 to 999,999,999:
https://man7.org/linux/man-pages/man3/futimens.3.html

   EINVAL Invalid value in one of the tv_nsec fields (value outside
          range 0 to 999,999,999, and not UTIME_NOW or UTIME_OMIT);
          or an invalid value in one of the tv_sec fields.
Breakpoint 2, set_times (destination=0x555555573680 "1.txt", statbuf=0x7fffffffe900) at rename.c:168
168     {
(gdb) n
172       times[0] = get_stat_atime (statbuf);
(gdb)
173       times[1] = get_stat_mtime (statbuf);
(gdb)
174       result = utimensat (AT_FDCWD, destination, times, 0);
(gdb) p times
$3 = {{tv_sec = 1659577707, tv_nsec = 93824992314928}, {tv_sec = 1659577707, tv_nsec = 93824992314592}}
(gdb)


In glibc 2.34, utimes and utimensat call the same interface __utimensat64_helper, so both interfaces are bound by tv_nsec:

void
set_times (const char *destination, const struct stat *statbuf)
{
  int result;
#if defined HAVE_UTIMENSAT
  struct timespec times[2];
  times[0] = get_stat_atime (statbuf);
  times[1] = get_stat_mtime (statbuf);
  result = utimensat (AT_FDCWD, destination, times, 0); 
#elif defined HAVE_UTIMES
  struct timeval tv[2];

  tv[0].tv_sec = statbuf->st_atime;
  tv[0].tv_usec = get_stat_atime_ns (statbuf) / 1000;
  tv[1].tv_sec = statbuf->st_mtime;
  tv[1].tv_usec = get_stat_mtime_ns (statbuf) / 1000;
  result = utimes (destination, tv); 
#elif defined HAVE_GOOD_UTIME_H
  struct utimbuf tb;

  tb.actime = statbuf->st_atime;
  tb.modtime = statbuf->st_mtime;
  result = utime (destination, &tb);
#else
  long tb[2];

  tb[0] = statbuf->st_atime;
  tb[1] = statbuf->st_mtime;
  result = utime (destination, tb);
#endif

  if (result != 0)
    non_fatal (_("%s: cannot set time: %s"), destination, strerror (errno));
}

I provide a patch bound tv_nsec:
diff --git a/binutils/rename.c b/binutils/rename.c
index 8826917c..248952cf 100644
--- a/binutils/rename.c
+++ b/binutils/rename.c
@@ -160,6 +160,24 @@ get_stat_mtime (struct stat const *st)
 }
 /* End FIXME.  */
 
+/* Bound nsec */
+static void
+nsec_bound(long *nsec)
+{
+  long ns = *nsec;
+  if (ns == UTIME_OMIT || ns == UTIME_NOW)
+    return;
+
+  if (ns < 0)
+    {
+      *nsec = 0;
+    }
+  else if (ns > 999999999)
+    {
+      *nsec = 999999999;
+    }
+}
+
 /* Set the times of the file DESTINATION to be the same as those in
    STATBUF.  */
 
@@ -170,7 +188,9 @@ set_times (const char *destination, const struct stat *statbuf)
 #if defined HAVE_UTIMENSAT
   struct timespec times[2];
   times[0] = get_stat_atime (statbuf);
+  nsec_bound(&times[0].tv_nsec);
   times[1] = get_stat_mtime (statbuf);
+  nsec_bound(&times[1].tv_nsec);
   result = utimensat (AT_FDCWD, destination, times, 0);
 #elif defined HAVE_UTIMES
   struct timeval tv[2];

Tested:(tv_nsec was reset to 999999999

[root@19fe3bb99663 ~]# touch 1.txt
[root@19fe3bb99663 ~]# stat 1.txt
  File: 1.txt
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: 0,41    Inode: 1703945     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-08-04 20:00:48.658963408 +0800
Modify: 2022-08-04 20:00:48.658963408 +0800
Change: 2022-08-04 20:00:48.658963408 +0800
 Birth: 2022-08-04 20:00:48.658963408 +0800
[root@19fe3bb99663 ~]# ar -rvU 1.a 1.txt
r - 1.txt
[root@19fe3bb99663 ~]# rm -f 1.txt
[root@19fe3bb99663 ~]# ar -xo 1.a
[root@19fe3bb99663 ~]# stat 1.txt
  File: 1.txt
  Size: 0               Blocks: 0          IO Block: 4096   regular empty file
Device: 0,41    Inode: 1703945     Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-08-04 20:00:48.999999999 +0800
Modify: 2022-08-04 20:00:48.999999999 +0800
Change: 2022-08-04 20:01:13.541931099 +0800
 Birth: 2022-08-04 20:01:13.541931099 +0800
[root@19fe3bb99663 ~]# rpm -qa | grep binutils
binutils-2.37-8.oe2203.x86_64
[root@19fe3bb99663 ~]#


reply via email to

[Prev in Thread] Current Thread [Next in Thread]