aboutsummaryrefslogtreecommitdiff
path: root/lib/libarchive
diff options
context:
space:
mode:
authorTim Kientzle <kientzle@FreeBSD.org>2008-02-19 05:39:35 +0000
committerTim Kientzle <kientzle@FreeBSD.org>2008-02-19 05:39:35 +0000
commit75018fc59208ea411fa3193f1cfdeb3c03d4316d (patch)
tree5f3d72a5d65eeea06261065c65b2890ace5b46f2 /lib/libarchive
parent2372ae680fbfc74377ef81008ef5913db220cadb (diff)
downloadsrc-75018fc59208ea411fa3193f1cfdeb3c03d4316d.tar.gz
src-75018fc59208ea411fa3193f1cfdeb3c03d4316d.zip
Portability improvements:
* If the platform can't restore char nodes, block nodes, or fifos, don't try and just return error. * Include O_BINARY in most open() calls (define O_BINARY to 0 if the platform doesn't provide a definition already) * Refactor the ownership restore to more cleanly support platforms that don't have any form of {l,f,}chown() call. * Comment a lingering issue with older Unix-like systems that allow root to hose the filesystem. I don't (yet) have a good solution for this, but I expect it will require adding more redundant stat() calls. <sigh> MFC after: 14 days
Notes
Notes: svn path=/head/; revision=176396
Diffstat (limited to 'lib/libarchive')
-rw-r--r--lib/libarchive/archive_write_disk.c89
1 files changed, 66 insertions, 23 deletions
diff --git a/lib/libarchive/archive_write_disk.c b/lib/libarchive/archive_write_disk.c
index a941ab666e84..2336b09149e1 100644
--- a/lib/libarchive/archive_write_disk.c
+++ b/lib/libarchive/archive_write_disk.c
@@ -45,6 +45,9 @@ __FBSDID("$FreeBSD$");
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
+#ifdef HAVE_SYS_UTIME_H
+#include <sys/utime.h>
+#endif
#ifdef HAVE_EXT2FS_EXT2_FS_H
#include <ext2fs/ext2_fs.h> /* for Linux file flags */
@@ -89,6 +92,10 @@ __FBSDID("$FreeBSD$");
#include "archive_entry.h"
#include "archive_private.h"
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
struct fixup_entry {
struct fixup_entry *next;
mode_t mode;
@@ -636,7 +643,9 @@ archive_write_disk_new(void)
a->archive.vtable = archive_write_disk_vtable();
a->lookup_uid = trivial_lookup_uid;
a->lookup_gid = trivial_lookup_gid;
+#ifdef HAVE_GETEUID
a->user_uid = geteuid();
+#endif /* HAVE_GETEUID */
if (archive_string_ensure(&a->path_safe, 512) == NULL) {
free(a);
return (NULL);
@@ -667,7 +676,7 @@ edit_deep_directories(struct archive_write_disk *a)
return;
/* Try to record our starting dir. */
- a->restore_pwd = open(".", O_RDONLY);
+ a->restore_pwd = open(".", O_RDONLY | O_BINARY);
if (a->restore_pwd < 0)
return;
@@ -705,6 +714,14 @@ restore_entry(struct archive_write_disk *a)
int ret = ARCHIVE_OK, en;
if (a->flags & ARCHIVE_EXTRACT_UNLINK && !S_ISDIR(a->mode)) {
+ /*
+ * TODO: Fix this. Apparently, there are platforms
+ * that still allow root to hose the entire filesystem
+ * by unlinking a dir. The S_ISDIR() test above
+ * prevents us from using unlink() here if the new
+ * object is a dir, but that doesn't mean the old
+ * object isn't a dir.
+ */
if (unlink(a->name) == 0) {
/* We removed it, we're done. */
} else if (errno == ENOENT) {
@@ -851,7 +868,7 @@ create_filesystem_object(struct archive_write_disk *a)
* for hardlink entries.
*/
if (r == 0 && a->filesize > 0) {
- a->fd = open(a->name, O_WRONLY | O_TRUNC);
+ a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY);
if (a->fd < 0)
r = errno;
}
@@ -876,24 +893,38 @@ create_filesystem_object(struct archive_write_disk *a)
*/
mode = final_mode & 0777;
- switch (a->mode & S_IFMT) {
+ switch (a->mode & AE_IFMT) {
default:
/* POSIX requires that we fall through here. */
/* FALLTHROUGH */
- case S_IFREG:
+ case AE_IFREG:
a->fd = open(a->name,
- O_WRONLY | O_CREAT | O_EXCL, mode);
+ O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode);
r = (a->fd < 0);
break;
- case S_IFCHR:
+ case AE_IFCHR:
+#ifdef HAVE_MKNOD
+ /* Note: we use AE_IFCHR for the case label, and
+ * S_IFCHR for the mknod() call. This is correct. */
r = mknod(a->name, mode | S_IFCHR,
archive_entry_rdev(a->entry));
+#else
+ /* TODO: Find a better way to warn about our inability
+ * to restore a char device node. */
+ return (EINVAL);
+#endif /* HAVE_MKNOD */
break;
- case S_IFBLK:
+ case AE_IFBLK:
+#ifdef HAVE_MKNOD
r = mknod(a->name, mode | S_IFBLK,
archive_entry_rdev(a->entry));
+#else
+ /* TODO: Find a better way to warn about our inability
+ * to restore a block device node. */
+ return (EINVAL);
+#endif /* HAVE_MKNOD */
break;
- case S_IFDIR:
+ case AE_IFDIR:
mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE;
r = mkdir(a->name, mode);
if (r == 0) {
@@ -906,8 +937,14 @@ create_filesystem_object(struct archive_write_disk *a)
a->todo &= ~TODO_MODE;
}
break;
- case S_IFIFO:
+ case AE_IFIFO:
+#ifdef HAVE_MKFIFO
r = mkfifo(a->name, mode);
+#else
+ /* TODO: Find a better way to warn about our inability
+ * to restore a fifo. */
+ return (EINVAL);
+#endif /* HAVE_MKFIFO */
break;
}
@@ -1453,28 +1490,34 @@ set_ownership(struct archive_write_disk *a)
}
#ifdef HAVE_FCHOWN
- if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0)
- goto success;
+ /* If we have an fd, we can avoid a race. */
+ if (a->fd >= 0 && fchown(a->fd, a->uid, a->gid) == 0) {
+ /* We've set owner and know uid/gid are correct. */
+ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+ return (ARCHIVE_OK);
+ }
#endif
+ /* We prefer lchown() but will use chown() if that's all we have. */
+ /* Of course, if we have neither, this will always fail. */
#ifdef HAVE_LCHOWN
- if (lchown(a->name, a->uid, a->gid) == 0)
- goto success;
-#else
- if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0)
- goto success;
+ if (lchown(a->name, a->uid, a->gid) == 0) {
+ /* We've set owner and know uid/gid are correct. */
+ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+ return (ARCHIVE_OK);
+ }
+#elif HAVE_CHOWN
+ if (!S_ISLNK(a->mode) && chown(a->name, a->uid, a->gid) == 0) {
+ /* We've set owner and know uid/gid are correct. */
+ a->todo &= ~(TODO_OWNER | TODO_SGID_CHECK | TODO_SUID_CHECK);
+ return (ARCHIVE_OK);
+ }
#endif
archive_set_error(&a->archive, errno,
"Can't set user=%d/group=%d for %s", a->uid, a->gid,
a->name);
return (ARCHIVE_WARN);
-success:
- a->todo &= ~TODO_OWNER;
- /* We know the user/group are correct now. */
- a->todo &= ~TODO_SGID_CHECK;
- a->todo &= ~TODO_SUID_CHECK;
- return (ARCHIVE_OK);
}
#ifdef HAVE_UTIMES
@@ -1812,7 +1855,7 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
/* If we weren't given an fd, open it ourselves. */
if (myfd < 0)
- myfd = open(name, O_RDONLY|O_NONBLOCK);
+ myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY);
if (myfd < 0)
return (ARCHIVE_OK);