aboutsummaryrefslogtreecommitdiff
path: root/lib/libarchive
diff options
context:
space:
mode:
authorTim Kientzle <kientzle@FreeBSD.org>2009-12-29 05:35:40 +0000
committerTim Kientzle <kientzle@FreeBSD.org>2009-12-29 05:35:40 +0000
commitbb8a41af518b6fd8b01d32168825b2088104a0b6 (patch)
tree9dd8c561cf37e49e0110352658b09ef2dc7733e9 /lib/libarchive
parentdee0ce0cbdf56b773580eb7de0a1eb061bc56f7e (diff)
downloadsrc-bb8a41af518b6fd8b01d32168825b2088104a0b6.tar.gz
src-bb8a41af518b6fd8b01d32168825b2088104a0b6.zip
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
Notes
Notes: svn path=/head/; revision=201159
Diffstat (limited to 'lib/libarchive')
-rw-r--r--lib/libarchive/archive_write_disk.c97
1 files changed, 74 insertions, 23 deletions
diff --git a/lib/libarchive/archive_write_disk.c b/lib/libarchive/archive_write_disk.c
index 80b1b6a69e66..6abd195dd21d 100644
--- a/lib/libarchive/archive_write_disk.c
+++ b/lib/libarchive/archive_write_disk.c
@@ -442,20 +442,23 @@ _archive_write_header(struct archive *_a, struct archive_entry *entry)
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 archive *_a)
}
#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 archive *_a)
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 *a)
* 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_write_disk *a)
/* 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_write_disk *a)
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_write_disk *a)
* 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_write_disk *a)
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 *a, const char *pathname)
* 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 *a)
/* 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, char *path)
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 *a)
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,