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
This commit is contained in:
parent
2372ae680f
commit
75018fc592
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user