Re: [Bug-tar] Preserve timestamp of symlinks when extracting (if utimensat available)

2009-08-05 Thread Sergey Poznyakoff
Carl Worth cwo...@cworth.org ha escrit:

 I've attached a patch that fixes the bug when utimensat is available,
 and should have no effect when the function is not available,

Thanks a lot. I'll need some time to test it.

Regards,
Sergey




[Bug-tar] Preserve timestamp of symlinks when extracting (if utimensat available)

2009-08-04 Thread Carl Worth
On Sun, 12 Jun 2005 14:22:53 -0400, D Goel wrote:
 Let's try the tar-trick to copy the contents of dir1 to dir2: 
 (cd dir1  tar -clpsf- *) | (cd dir2  tar -xpsf -)
 
 This copies the contents, including correct timestamps and ownership,
 except that if there is a symlink (named, say s) in dir1.  Then the
 copied file dir2/s fails to get the same timestamp as dir1/s. 

Hi DG,

Thank you for the bug report. I've confirmed that this bug still exists
in the latest version of tar packaged in Debian (1.22-1.1). And I almost
replied saying that Linux doesn't provide the necessary functionality to
do what you want, (I noticed that cp -a also exhibits the same
bug---see bug #26766).

But rsync does not have the bug, so I looked a little closer and
found... the necessary magic is in the flags argument to the POSIX
utimensat function call:

   The flags field is a bit mask that may be 0, or include  the  following
   constant, defined in fcntl.h:

   AT_SYMLINK_NOFOLLOW
  If  pathname  specifies  a  symbolic link, then update the time‐
  stamps of the link, rather than the file to which it refers.

The tar program already prefers to call utimensat if available, but
currently always passes 0 for the flags argument. It also avoids ever
calling this function for symlinks, (which would update the timestamp of
the target to the archived time stamp of the link which is certainly not
desired).

I've attached a patch that fixes the bug when utimensat is available,
and should have no effect when the function is not available, (though I
didn't test on any such system). I also didn't write a new test case for
this behavior, (I'm not sure how to make a test case that would not be
expected to pass on some systems.)

Any feedback would be appreciated,

-Carl



From 2fe8827ad1874abf3295a5aa96eba18372b98c12 Mon Sep 17 00:00:00 2001
From: Carl Worth cwo...@cworth.org
Date: Tue, 4 Aug 2009 17:00:35 -0700
Subject: [PATCH] Preserve timestamp of symlinks when extracting (if utimensat available)

If the utimensat function is not available, then do nothing with
symlink time stamps, (which is the same as the current code).
---
 lib/utimens.c |   26 +++
 lib/utimens.h |4 +-
 src/extract.c |   61 +++-
 src/misc.c|2 +-
 4 files changed, 53 insertions(+), 40 deletions(-)

diff --git a/lib/utimens.c b/lib/utimens.c
index 708de10..ae8b0a6 100644
--- a/lib/utimens.c
+++ b/lib/utimens.c
@@ -72,11 +72,16 @@ struct utimbuf
use just futimes (or equivalent) instead of utimes (or equivalent),
and fail if on an old system without futimes (or equivalent).
If TIMESPEC is null, set the time stamps to the current time.
+   If the file is a symlink and IS_SYMLINK is set, then the
+   time stamps of the symlink itself will be updated if
+   possible, (but if not supported by the operating system
+   then no change will occur).
Return 0 on success, -1 (setting errno) on failure.  */
 
 int
 gl_futimens (int fd ATTRIBUTE_UNUSED,
-	 char const *file, struct timespec const timespec[2])
+	 char const *file, struct timespec const timespec[2],
+	 int is_symlink)
 {
   /* Some Linux-based NFS clients are buggy, and mishandle time stamps
  of files in NFS file systems in some cases.  We have no
@@ -102,7 +107,8 @@ gl_futimens (int fd ATTRIBUTE_UNUSED,
 #if HAVE_UTIMENSAT
   if (fd  0)
 {
-  int result = utimensat (AT_FDCWD, file, timespec, 0);
+  int flags = is_symlink ? AT_SYMLINK_NOFOLLOW : 0;
+  int result = utimensat (AT_FDCWD, file, timespec, flags);
 # ifdef __linux__
   /* Work around what might be a kernel bug:
  http://bugzilla.redhat.com/442352
@@ -119,6 +125,12 @@ gl_futimens (int fd ATTRIBUTE_UNUSED,
 return result;
 }
 #endif
+
+  /* Without utimensat we have no way to update a symlink rather than
+   * the target, so just return immediately. */
+  if (is_symlink)
+  return 0;
+
 #if HAVE_FUTIMENS
   {
 int result = futimens (fd, timespec);
@@ -219,9 +231,13 @@ gl_futimens (int fd ATTRIBUTE_UNUSED,
 }
 
 /* Set the access and modification time stamps of FILE to be
-   TIMESPEC[0] and TIMESPEC[1], respectively.  */
+   TIMESPEC[0] and TIMESPEC[1], respectively.
+   If the file is a symlink and is_symlink is set, then the
+   time stamps of the symlink itself will be updated if
+   possible, (but if not supported by the operating system
+   then no change will occur). */
 int
-utimens (char const *file, struct timespec const timespec[2])
+utimens (char const *file, struct timespec const timespec[2], int is_symlink)
 {
-  return gl_futimens (-1, file, timespec);
+  return gl_futimens (-1, file, timespec, is_symlink);
 }
diff --git a/lib/utimens.h b/lib/utimens.h
index 169521d..625785c 100644
--- a/lib/utimens.h
+++ b/lib/utimens.h
@@ -1,3 +1,3 @@
 #include time.h
-int gl_futimens (int, char const *, struct timespec const [2]);
-int utimens (char const *,