svn commit: r201159 - head/lib/libarchive

Tim Kientzle kientzle at FreeBSD.org
Tue Dec 29 05:35:40 UTC 2009


Author: kientzle
Date: Tue Dec 29 05:35:40 2009
New Revision: 201159
URL: http://svn.freebsd.org/changeset/base/201159

Log:
  Various fixes when creating objects on disk:
   * Write xattrs last instead of first (required on platforms that use
     system xattrs for security attributes)
   * Better handling of chdir() failures
   * Don't bother trying to shorten files via seek()/write()
   * Fix build on systems that lack link()/symlink()/mknod()
   * Prefer futimens()/utimensat() when they're present

Modified:
  head/lib/libarchive/archive_write_disk.c

Modified: head/lib/libarchive/archive_write_disk.c
==============================================================================
--- head/lib/libarchive/archive_write_disk.c	Tue Dec 29 05:35:25 2009	(r201158)
+++ head/lib/libarchive/archive_write_disk.c	Tue Dec 29 05:35:40 2009	(r201159)
@@ -442,20 +442,23 @@ _archive_write_header(struct archive *_a
 	ret = restore_entry(a);
 
 	/*
-	 * On the GNU tar mailing list, some people working with new
-	 * Linux filesystems observed that system xattrs used as
-	 * layout hints need to be restored before the file contents
-	 * are written, so this can't be done at file close.
+	 * TODO: There are rumours that some extended attributes must
+	 * be restored before file data is written.  If this is true,
+	 * then we either need to write all extended attributes both
+	 * before and after restoring the data, or find some rule for
+	 * determining which must go first and which last.  Due to the
+	 * many ways people are using xattrs, this may prove to be an
+	 * intractable problem.
 	 */
-	if (a->todo & TODO_XATTR) {
-		int r2 = set_xattrs(a);
-		if (r2 < ret) ret = r2;
-	}
 
 #ifdef HAVE_FCHDIR
 	/* If we changed directory above, restore it here. */
 	if (a->restore_pwd >= 0) {
-		fchdir(a->restore_pwd);
+		r = fchdir(a->restore_pwd);
+		if (r != 0) {
+			archive_set_error(&a->archive, errno, "chdir() failure");
+			ret = ARCHIVE_FATAL;
+		}
 		close(a->restore_pwd);
 		a->restore_pwd = -1;
 	}
@@ -692,15 +695,18 @@ _archive_write_finish_entry(struct archi
 		}
 #endif
 		/*
-		 * Explicitly stat the file as some platforms might not
-		 * implement the XSI option to extend files via ftruncate.
+		 * Not all platforms implement the XSI option to
+		 * extend files via ftruncate.  Stat() the file again
+		 * to see what happened.
 		 */
 		a->pst = NULL;
 		if ((ret = _archive_write_disk_lazy_stat(a)) != ARCHIVE_OK)
 			return (ret);
-		if (a->st.st_size != a->filesize) {
+		/* We can use lseek()/write() to extend the file if
+		 * ftruncate didn't work or isn't available. */
+		if (a->st.st_size < a->filesize) {
 			const char nul = '\0';
-			if (lseek(a->fd, a->st.st_size - 1, SEEK_SET) < 0) {
+			if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) {
 				archive_set_error(&a->archive, errno,
 				    "Seek failed");
 				return (ARCHIVE_FATAL);
@@ -747,6 +753,17 @@ _archive_write_finish_entry(struct archi
 		int r2 = set_acls(a);
 		if (r2 < ret) ret = r2;
 	}
+
+	/*
+	 * Security-related extended attributes (such as
+	 * security.capability on Linux) have to be restored last,
+	 * since they're implicitly removed by other file changes.
+	 */
+	if (a->todo & TODO_XATTR) {
+		int r2 = set_xattrs(a);
+		if (r2 < ret) ret = r2;
+	}
+
 	/*
 	 * Some flags prevent file modification; they must be restored after
 	 * file contents are written.
@@ -1057,7 +1074,7 @@ restore_entry(struct archive_write_disk 
  * the failed system call.   Note:  This function should only ever perform
  * a single system call.
  */
-int
+static int
 create_filesystem_object(struct archive_write_disk *a)
 {
 	/* Create the entry. */
@@ -1069,6 +1086,9 @@ create_filesystem_object(struct archive_
 	/* Since link(2) and symlink(2) don't handle modes, we're done here. */
 	linkname = archive_entry_hardlink(a->entry);
 	if (linkname != NULL) {
+#if !HAVE_LINK
+		return (EPERM);
+#else
 		r = link(linkname, a->name) ? errno : 0;
 		/*
 		 * New cpio and pax formats allow hardlink entries
@@ -1091,10 +1111,16 @@ create_filesystem_object(struct archive_
 				r = errno;
 		}
 		return (r);
+#endif
 	}
 	linkname = archive_entry_symlink(a->entry);
-	if (linkname != NULL)
+	if (linkname != NULL) {
+#if HAVE_SYMLINK
 		return symlink(linkname, a->name) ? errno : 0;
+#else
+		return (EPERM);
+#endif
+	}
 
 	/*
 	 * The remaining system calls all set permissions, so let's
@@ -1126,22 +1152,22 @@ create_filesystem_object(struct archive_
 		 * S_IFCHR for the mknod() call.  This is correct.  */
 		r = mknod(a->name, mode | S_IFCHR,
 		    archive_entry_rdev(a->entry));
+		break;
 #else
 		/* TODO: Find a better way to warn about our inability
 		 * to restore a char device node. */
 		return (EINVAL);
 #endif /* HAVE_MKNOD */
-		break;
 	case AE_IFBLK:
 #ifdef HAVE_MKNOD
 		r = mknod(a->name, mode | S_IFBLK,
 		    archive_entry_rdev(a->entry));
+		break;
 #else
 		/* TODO: Find a better way to warn about our inability
 		 * to restore a block device node. */
 		return (EINVAL);
 #endif /* HAVE_MKNOD */
-		break;
 	case AE_IFDIR:
 		mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
 		r = mkdir(a->name, mode);
@@ -1161,12 +1187,12 @@ create_filesystem_object(struct archive_
 	case AE_IFIFO:
 #ifdef HAVE_MKFIFO
 		r = mkfifo(a->name, mode);
+		break;
 #else
 		/* TODO: Find a better way to warn about our inability
 		 * to restore a fifo. */
 		return (EINVAL);
 #endif /* HAVE_MKFIFO */
-		break;
 	}
 
 	/* All the system calls above set errno on failure. */
@@ -1394,9 +1420,15 @@ current_fixup(struct archive_write_disk 
  * scan the path and both can be optimized by comparing against other
  * recent paths.
  */
+/* TODO: Extend this to support symlinks on Windows Vista and later. */
 static int
 check_symlinks(struct archive_write_disk *a)
 {
+#if !defined(HAVE_LSTAT)
+	/* Platform doesn't have lstat, so we can't look for symlinks. */
+	(void)a; /* UNUSED */
+	return (ARCHIVE_OK);
+#else
 	char *pn, *p;
 	char c;
 	int r;
@@ -1477,6 +1509,7 @@ check_symlinks(struct archive_write_disk
 	/* We've checked and/or cleaned the whole path, so remember it. */
 	archive_strcpy(&a->path_safe, a->name);
 	return (ARCHIVE_OK);
+#endif
 }
 
 #if defined(_WIN32) || defined(__CYGWIN__)
@@ -1661,8 +1694,6 @@ create_dir(struct archive_write_disk *a,
 	mode_t mode_final, mode;
 	int r;
 
-	r = ARCHIVE_OK;
-
 	/* Check for special names and just skip them. */
 	slash = strrchr(path, '/');
 	if (slash == NULL)
@@ -1806,11 +1837,31 @@ set_ownership(struct archive_write_disk 
 	return (ARCHIVE_WARN);
 }
 
-#ifdef HAVE_UTIMES
+
+#if defined(HAVE_UTIMENSAT) && defined(HAVE_FUTIMENS)
+/* 
+ * utimensat() and futimens() are defined in POSIX.1-2008. They provide ns
+ * resolution and setting times on fd and on symlinks, too.
+ */
+static int
+set_time(int fd, int mode, const char *name,
+    time_t atime, long atime_nsec,
+    time_t mtime, long mtime_nsec)
+{
+	struct timespec ts[2];
+	ts[0].tv_sec = atime;
+	ts[0].tv_nsec = atime_nsec;
+	ts[1].tv_sec = mtime;
+	ts[1].tv_nsec = mtime_nsec;
+	if (fd >= 0)
+		return futimens(fd, ts);
+	return utimensat(AT_FDCWD, name, ts, AT_SYMLINK_NOFOLLOW);
+}
+#elif HAVE_UTIMES
 /*
- * The utimes()-family functions provide high resolution and
+ * The utimes()-family functions provide µs-resolution and
  * a way to set time on an fd or a symlink.  We prefer them
- * when they're available.
+ * when they're available and utimensat/futimens aren't there.
  */
 static int
 set_time(int fd, int mode, const char *name,


More information about the svn-src-head mailing list