mirror of
https://github.com/opnsense/src.git
synced 2026-06-08 16:22:46 -04:00
Overhauled ACL support. This makes us compatible
with 'star' ACL handling, though there's still a bit more work needed in this area. Added 'write_open_fd' and 'read_open_fd' to simplify, e.g., tar's u and r modes. Eliminated old 'write_open_file_position' as a bad idea. (It required closing/reopening files to do updates, which led to unpleasant implications.) Various other minor fixes, API tweaks, etc.
This commit is contained in:
parent
7f8a436ff2
commit
71b44796d9
21 changed files with 1238 additions and 193 deletions
|
|
@ -1,13 +1,13 @@
|
|||
# Makefile for libarchive.
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
DEBUG_FLAGS=-g
|
||||
LIB= archive
|
||||
SHLIB_MAJOR= 1
|
||||
|
||||
# I'm not yet ready for a shared version of this library, as
|
||||
# there are still a couple of API changes still in the works.
|
||||
NOSHLIBS= 1
|
||||
# there are a couple of API changes still in the works.
|
||||
NOPIC= 1
|
||||
|
||||
SRCS= archive_check_magic.c \
|
||||
archive_entry.c \
|
||||
|
|
@ -15,6 +15,7 @@ SRCS= archive_check_magic.c \
|
|||
archive_read_data_into_buffer.c \
|
||||
archive_read_data_into_fd.c \
|
||||
archive_read_extract.c \
|
||||
archive_read_open_fd.c \
|
||||
archive_read_open_file.c \
|
||||
archive_read_support_compression_all.c \
|
||||
archive_read_support_compression_bzip2.c \
|
||||
|
|
@ -28,6 +29,7 @@ SRCS= archive_check_magic.c \
|
|||
archive_string_sprintf.c \
|
||||
archive_util.c \
|
||||
archive_write.c \
|
||||
archive_write_open_fd.c \
|
||||
archive_write_open_file.c \
|
||||
archive_write_set_compression_bzip2.c \
|
||||
archive_write_set_compression_gzip.c \
|
||||
|
|
@ -80,8 +82,8 @@ MLINKS += archive_read.3 archive_read_finish.3
|
|||
MLINKS += archive_read.3 archive_read_new.3
|
||||
MLINKS += archive_read.3 archive_read_next_header.3
|
||||
MLINKS += archive_read.3 archive_read_open.3
|
||||
MLINKS += archive_read.3 archive_read_open_fd.3
|
||||
MLINKS += archive_read.3 archive_read_open_file.3
|
||||
MLINKS += archive_read.3 archive_read_open_tar.3
|
||||
MLINKS += archive_read.3 archive_read_set_bytes_per_block.3
|
||||
MLINKS += archive_read.3 archive_read_support_compression_all.3
|
||||
MLINKS += archive_read.3 archive_read_support_compression_bzip2.3
|
||||
|
|
@ -102,6 +104,7 @@ MLINKS += archive_write.3 archive_write_finish.3
|
|||
MLINKS += archive_write.3 archive_write_header.3
|
||||
MLINKS += archive_write.3 archive_write_new.3
|
||||
MLINKS += archive_write.3 archive_write_open.3
|
||||
MLINKS += archive_write.3 archive_write_open_fd.3
|
||||
MLINKS += archive_write.3 archive_write_open_file.3
|
||||
MLINKS += archive_write.3 archive_write_prepare.3
|
||||
MLINKS += archive_write.3 archive_write_set_bytes_per_block.3
|
||||
|
|
@ -120,7 +123,7 @@ DEBUG_FLAGS+= -DDEBUG -g
|
|||
CFLAGS+= -DHAVE_DMALLOC -I/usr/local/include
|
||||
LDFLAGS+= -L/usr/local/lib -ldmalloc
|
||||
.endif
|
||||
CFLAGS+= -O3
|
||||
#CFLAGS+= -O3
|
||||
|
||||
WARNS?= 6
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
# Makefile for libarchive.
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
DEBUG_FLAGS=-g
|
||||
LIB= archive
|
||||
SHLIB_MAJOR= 1
|
||||
|
||||
# I'm not yet ready for a shared version of this library, as
|
||||
# there are still a couple of API changes still in the works.
|
||||
NOSHLIBS= 1
|
||||
# there are a couple of API changes still in the works.
|
||||
NOPIC= 1
|
||||
|
||||
SRCS= archive_check_magic.c \
|
||||
archive_entry.c \
|
||||
|
|
@ -15,6 +15,7 @@ SRCS= archive_check_magic.c \
|
|||
archive_read_data_into_buffer.c \
|
||||
archive_read_data_into_fd.c \
|
||||
archive_read_extract.c \
|
||||
archive_read_open_fd.c \
|
||||
archive_read_open_file.c \
|
||||
archive_read_support_compression_all.c \
|
||||
archive_read_support_compression_bzip2.c \
|
||||
|
|
@ -28,6 +29,7 @@ SRCS= archive_check_magic.c \
|
|||
archive_string_sprintf.c \
|
||||
archive_util.c \
|
||||
archive_write.c \
|
||||
archive_write_open_fd.c \
|
||||
archive_write_open_file.c \
|
||||
archive_write_set_compression_bzip2.c \
|
||||
archive_write_set_compression_gzip.c \
|
||||
|
|
@ -80,8 +82,8 @@ MLINKS += archive_read.3 archive_read_finish.3
|
|||
MLINKS += archive_read.3 archive_read_new.3
|
||||
MLINKS += archive_read.3 archive_read_next_header.3
|
||||
MLINKS += archive_read.3 archive_read_open.3
|
||||
MLINKS += archive_read.3 archive_read_open_fd.3
|
||||
MLINKS += archive_read.3 archive_read_open_file.3
|
||||
MLINKS += archive_read.3 archive_read_open_tar.3
|
||||
MLINKS += archive_read.3 archive_read_set_bytes_per_block.3
|
||||
MLINKS += archive_read.3 archive_read_support_compression_all.3
|
||||
MLINKS += archive_read.3 archive_read_support_compression_bzip2.3
|
||||
|
|
@ -102,6 +104,7 @@ MLINKS += archive_write.3 archive_write_finish.3
|
|||
MLINKS += archive_write.3 archive_write_header.3
|
||||
MLINKS += archive_write.3 archive_write_new.3
|
||||
MLINKS += archive_write.3 archive_write_open.3
|
||||
MLINKS += archive_write.3 archive_write_open_fd.3
|
||||
MLINKS += archive_write.3 archive_write_open_file.3
|
||||
MLINKS += archive_write.3 archive_write_prepare.3
|
||||
MLINKS += archive_write.3 archive_write_set_bytes_per_block.3
|
||||
|
|
@ -120,7 +123,7 @@ DEBUG_FLAGS+= -DDEBUG -g
|
|||
CFLAGS+= -DHAVE_DMALLOC -I/usr/local/include
|
||||
LDFLAGS+= -L/usr/local/lib -ldmalloc
|
||||
.endif
|
||||
CFLAGS+= -O3
|
||||
#CFLAGS+= -O3
|
||||
|
||||
WARNS?= 6
|
||||
|
||||
|
|
|
|||
|
|
@ -149,6 +149,8 @@ int archive_read_open(struct archive *, void *_client_data,
|
|||
*/
|
||||
int archive_read_open_file(struct archive *, const char *_file,
|
||||
size_t _block_size);
|
||||
int archive_read_open_fd(struct archive *, int _fd,
|
||||
size_t _block_size);
|
||||
|
||||
/* Parses and returns next entry header. */
|
||||
int archive_read_next_header(struct archive *,
|
||||
|
|
@ -239,10 +241,8 @@ int archive_write_set_format_ustar(struct archive *);
|
|||
int archive_write_open(struct archive *, void *,
|
||||
archive_open_callback *, archive_write_callback *,
|
||||
archive_close_callback *);
|
||||
int archive_write_open_fd(struct archive *, int _fd);
|
||||
int archive_write_open_file(struct archive *, const char *_file);
|
||||
int archive_write_open_file_position(struct archive *,
|
||||
const char *_filename, int64_t offset);
|
||||
int archive_write_open_tar(struct archive *, const char *_file);
|
||||
|
||||
/*
|
||||
* Note that the library will truncate writes beyond the size provided
|
||||
|
|
|
|||
|
|
@ -149,6 +149,8 @@ int archive_read_open(struct archive *, void *_client_data,
|
|||
*/
|
||||
int archive_read_open_file(struct archive *, const char *_file,
|
||||
size_t _block_size);
|
||||
int archive_read_open_fd(struct archive *, int _fd,
|
||||
size_t _block_size);
|
||||
|
||||
/* Parses and returns next entry header. */
|
||||
int archive_read_next_header(struct archive *,
|
||||
|
|
@ -239,10 +241,8 @@ int archive_write_set_format_ustar(struct archive *);
|
|||
int archive_write_open(struct archive *, void *,
|
||||
archive_open_callback *, archive_write_callback *,
|
||||
archive_close_callback *);
|
||||
int archive_write_open_fd(struct archive *, int _fd);
|
||||
int archive_write_open_file(struct archive *, const char *_file);
|
||||
int archive_write_open_file_position(struct archive *,
|
||||
const char *_filename, int64_t offset);
|
||||
int archive_write_open_tar(struct archive *, const char *_file);
|
||||
|
||||
/*
|
||||
* Note that the library will truncate writes beyond the size provided
|
||||
|
|
|
|||
|
|
@ -32,12 +32,18 @@ __FBSDID("$FreeBSD$");
|
|||
#ifdef HAVE_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_entry.h"
|
||||
#include "archive_private.h"
|
||||
|
||||
#undef max
|
||||
#define max(a, b) ((a)>(b)?(a):(b))
|
||||
|
||||
/*
|
||||
* Handle wide character (i.e., Unicode) and non-wide character
|
||||
|
|
@ -52,20 +58,44 @@ struct aes {
|
|||
wchar_t *aes_wcs_alloc;
|
||||
};
|
||||
|
||||
void aes_clean(struct aes *);
|
||||
void aes_copy(struct aes *dest, struct aes *src);
|
||||
const char * aes_get_mbs(struct aes *);
|
||||
const wchar_t * aes_get_wcs(struct aes *);
|
||||
void aes_set_mbs(struct aes *, const char *mbs);
|
||||
void aes_set_wcs(struct aes *, const wchar_t *wcs);
|
||||
void aes_copy_wcs(struct aes *, const wchar_t *wcs);
|
||||
struct ae_acl {
|
||||
struct ae_acl *next;
|
||||
int type; /* E.g., access or default */
|
||||
int tag; /* E.g., user/group/other/mask */
|
||||
int permset; /* r/w/x bits */
|
||||
int id; /* uid/gid for user/group */
|
||||
struct aes name; /* uname/gname */
|
||||
};
|
||||
|
||||
static void aes_clean(struct aes *);
|
||||
static void aes_copy(struct aes *dest, struct aes *src);
|
||||
static const char * aes_get_mbs(struct aes *);
|
||||
static const wchar_t * aes_get_wcs(struct aes *);
|
||||
static void aes_set_mbs(struct aes *, const char *mbs);
|
||||
static void aes_copy_mbs(struct aes *, const char *mbs);
|
||||
/* static void aes_set_wcs(struct aes *, const wchar_t *wcs); */
|
||||
static void aes_copy_wcs(struct aes *, const wchar_t *wcs);
|
||||
|
||||
static void append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
|
||||
const wchar_t *wname, int perm, int id);
|
||||
static void append_id_w(wchar_t **wp, int id);
|
||||
|
||||
static int acl_special(struct archive_entry *entry,
|
||||
int type, int permset, int tag);
|
||||
static struct ae_acl *acl_new_entry(struct archive_entry *entry,
|
||||
int type, int permset, int tag, int id);
|
||||
static void next_field_w(const wchar_t **wp, const wchar_t **start,
|
||||
const wchar_t **end, wchar_t *sep);
|
||||
static int prefix_w(const wchar_t *start, const wchar_t *end,
|
||||
const wchar_t *test);
|
||||
|
||||
|
||||
/*
|
||||
* Description of an archive entry.
|
||||
*
|
||||
* Basically, this is a "struct stat" with a few text fields added in.
|
||||
*
|
||||
* TODO: Add "comment", "charset", "acl", and possibly other entries
|
||||
* TODO: Add "comment", "charset", and possibly other entries
|
||||
* that are supported by "pax interchange" format. However, GNU, ustar,
|
||||
* cpio, and other variants don't support these features, so they're not an
|
||||
* excruciatingly high priority right now.
|
||||
|
|
@ -95,14 +125,17 @@ struct archive_entry {
|
|||
/*
|
||||
* Use aes here so that we get transparent mbs<->wcs conversions.
|
||||
*/
|
||||
struct aes ae_acl; /* ACL text */
|
||||
struct aes ae_acl_default; /* default ACL */
|
||||
struct aes ae_fflags; /* Text fflags per fflagstostr(3) */
|
||||
struct aes ae_gname; /* Name of owning group */
|
||||
struct aes ae_hardlink; /* Name of target for hardlink */
|
||||
struct aes ae_pathname; /* Name of entry */
|
||||
struct aes ae_symlink; /* symlink contents */
|
||||
struct aes ae_uname; /* Name of owner */
|
||||
|
||||
struct ae_acl *acl_head;
|
||||
struct ae_acl *acl_p;
|
||||
int acl_state; /* See acl_next for details. */
|
||||
wchar_t *acl_text_w;
|
||||
};
|
||||
|
||||
void
|
||||
|
|
@ -188,6 +221,24 @@ aes_set_mbs(struct aes *aes, const char *mbs)
|
|||
aes->aes_wcs = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
aes_copy_mbs(struct aes *aes, const char *mbs)
|
||||
{
|
||||
if (aes->aes_mbs_alloc) {
|
||||
free(aes->aes_mbs_alloc);
|
||||
aes->aes_mbs_alloc = NULL;
|
||||
}
|
||||
if (aes->aes_wcs_alloc) {
|
||||
free(aes->aes_wcs_alloc);
|
||||
aes->aes_wcs_alloc = NULL;
|
||||
}
|
||||
aes->aes_mbs_alloc = malloc((strlen(mbs) + 1) * sizeof(char));
|
||||
strcpy(aes->aes_mbs_alloc, mbs);
|
||||
aes->aes_mbs = aes->aes_mbs_alloc;
|
||||
aes->aes_wcs = NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void
|
||||
aes_set_wcs(struct aes *aes, const wchar_t *wcs)
|
||||
{
|
||||
|
|
@ -202,6 +253,7 @@ aes_set_wcs(struct aes *aes, const wchar_t *wcs)
|
|||
aes->aes_mbs = NULL;
|
||||
aes->aes_wcs = wcs;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
|
||||
|
|
@ -223,14 +275,13 @@ aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
|
|||
struct archive_entry *
|
||||
archive_entry_clear(struct archive_entry *entry)
|
||||
{
|
||||
aes_clean(&entry->ae_acl);
|
||||
aes_clean(&entry->ae_acl_default);
|
||||
aes_clean(&entry->ae_fflags);
|
||||
aes_clean(&entry->ae_gname);
|
||||
aes_clean(&entry->ae_hardlink);
|
||||
aes_clean(&entry->ae_pathname);
|
||||
aes_clean(&entry->ae_symlink);
|
||||
aes_clean(&entry->ae_uname);
|
||||
archive_entry_acl_clear(entry);
|
||||
memset(entry, 0, sizeof(*entry));
|
||||
entry->ae_tartype = -1;
|
||||
return entry;
|
||||
|
|
@ -243,11 +294,12 @@ archive_entry_clone(struct archive_entry *entry)
|
|||
|
||||
/* Allocate new structure and copy over all of the fields. */
|
||||
entry2 = malloc(sizeof(*entry2));
|
||||
if(entry2 == NULL)
|
||||
return (NULL);
|
||||
memset(entry2, 0, sizeof(*entry2));
|
||||
entry2->ae_stat = entry->ae_stat;
|
||||
entry2->ae_tartype = entry->ae_tartype;
|
||||
|
||||
aes_copy(&entry2->ae_acl ,&entry->ae_acl);
|
||||
aes_copy(&entry2->ae_acl_default ,&entry->ae_acl_default);
|
||||
aes_copy(&entry2->ae_fflags ,&entry->ae_fflags);
|
||||
aes_copy(&entry2->ae_gname ,&entry->ae_gname);
|
||||
aes_copy(&entry2->ae_hardlink ,&entry->ae_hardlink);
|
||||
|
|
@ -282,19 +334,6 @@ archive_entry_new(void)
|
|||
* Functions for reading fields from an archive_entry.
|
||||
*/
|
||||
|
||||
const char *
|
||||
archive_entry_acl(struct archive_entry *entry)
|
||||
{
|
||||
return (aes_get_mbs(&entry->ae_acl));
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
archive_entry_acl_default(struct archive_entry *entry)
|
||||
{
|
||||
return (aes_get_mbs(&entry->ae_acl_default));
|
||||
}
|
||||
|
||||
dev_t
|
||||
archive_entry_devmajor(struct archive_entry *entry)
|
||||
{
|
||||
|
|
@ -332,6 +371,20 @@ archive_entry_mode(struct archive_entry *entry)
|
|||
return (entry->ae_stat.st_mode);
|
||||
}
|
||||
|
||||
|
||||
time_t
|
||||
archive_entry_mtime(struct archive_entry *entry)
|
||||
{
|
||||
return (entry->ae_stat.st_mtime);
|
||||
}
|
||||
|
||||
|
||||
long
|
||||
archive_entry_mtime_nsec(struct archive_entry *entry)
|
||||
{
|
||||
return (entry->ae_stat.st_mtimespec.tv_nsec);
|
||||
}
|
||||
|
||||
const char *
|
||||
archive_entry_pathname(struct archive_entry *entry)
|
||||
{
|
||||
|
|
@ -388,30 +441,6 @@ archive_entry_copy_stat(struct archive_entry *entry, const struct stat *st)
|
|||
entry->ae_stat = *st;
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_set_acl(struct archive_entry *entry, const char *acl)
|
||||
{
|
||||
aes_set_mbs(&entry->ae_acl, acl);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_copy_acl_w(struct archive_entry *entry, const wchar_t *acl)
|
||||
{
|
||||
aes_copy_wcs(&entry->ae_acl, acl);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_set_acl_default(struct archive_entry *entry, const char *acl)
|
||||
{
|
||||
aes_set_mbs(&entry->ae_acl_default, acl);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_copy_acl_default_w(struct archive_entry *entry, const wchar_t *acl)
|
||||
{
|
||||
aes_copy_wcs(&entry->ae_acl_default, acl);
|
||||
}
|
||||
|
||||
void
|
||||
archive_entry_set_devmajor(struct archive_entry *entry, dev_t m)
|
||||
{
|
||||
|
|
@ -532,6 +561,622 @@ archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
|
|||
aes_copy_wcs(&entry->ae_uname, name);
|
||||
}
|
||||
|
||||
/*
|
||||
* ACL management. The following would, of course, be a lot simpler
|
||||
* if: 1) the last draft of POSIX.1e were a really thorough and
|
||||
* complete standard that addressed the needs of ACL archiving and 2)
|
||||
* everyone followed it faithfully. Alas, neither is true, so the
|
||||
* following is a lot more complex than might seem necessary to the
|
||||
* uninitiated.
|
||||
*/
|
||||
|
||||
void
|
||||
archive_entry_acl_clear(struct archive_entry *entry)
|
||||
{
|
||||
struct ae_acl *ap;
|
||||
|
||||
while (entry->acl_head != NULL) {
|
||||
ap = entry->acl_head->next;
|
||||
aes_clean(&entry->acl_head->name);
|
||||
free(entry->acl_head);
|
||||
entry->acl_head = ap;
|
||||
}
|
||||
if (entry->acl_text_w != NULL) {
|
||||
free(entry->acl_text_w);
|
||||
entry->acl_text_w = NULL;
|
||||
}
|
||||
entry->acl_p = NULL;
|
||||
entry->acl_state = 0; /* Not counting. */
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a single ACL entry to the internal list of ACL data.
|
||||
*/
|
||||
void
|
||||
archive_entry_acl_add_entry(struct archive_entry *entry,
|
||||
int type, int permset, int tag, int id, const char *name)
|
||||
{
|
||||
struct ae_acl *ap;
|
||||
|
||||
if (acl_special(entry, type, permset, tag) == 0)
|
||||
return;
|
||||
ap = acl_new_entry(entry, type, permset, tag, id);
|
||||
if (ap == NULL) {
|
||||
/* XXX Error XXX */
|
||||
return;
|
||||
}
|
||||
if (name != NULL && *name != '\0')
|
||||
aes_copy_mbs(&ap->name, name);
|
||||
else
|
||||
aes_clean(&ap->name);
|
||||
}
|
||||
|
||||
/*
|
||||
* As above, but with a wide-character name.
|
||||
*/
|
||||
void
|
||||
archive_entry_acl_add_entry_w(struct archive_entry *entry,
|
||||
int type, int permset, int tag, int id, const wchar_t *name)
|
||||
{
|
||||
struct ae_acl *ap;
|
||||
|
||||
if (acl_special(entry, type, permset, tag) == 0)
|
||||
return;
|
||||
ap = acl_new_entry(entry, type, permset, tag, id);
|
||||
if (ap == NULL) {
|
||||
/* XXX Error XXX */
|
||||
return;
|
||||
}
|
||||
if (name != NULL && *name != L'\0')
|
||||
aes_copy_wcs(&ap->name, name);
|
||||
else
|
||||
aes_clean(&ap->name);
|
||||
}
|
||||
|
||||
/*
|
||||
* If this ACL entry is part of the standard POSIX permissions set,
|
||||
* store the permissions in the stat structure and return zero.
|
||||
*/
|
||||
static int
|
||||
acl_special(struct archive_entry *entry, int type, int permset, int tag)
|
||||
{
|
||||
if (type == ARCHIVE_ENTRY_ACL_TYPE_ACCESS) {
|
||||
switch (tag) {
|
||||
case ARCHIVE_ENTRY_ACL_USER_OBJ:
|
||||
entry->ae_stat.st_mode &= 0077;
|
||||
entry->ae_stat.st_mode |= (permset & 7) << 6;
|
||||
return (0);
|
||||
case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
|
||||
entry->ae_stat.st_mode &= 0707;
|
||||
entry->ae_stat.st_mode |= (permset & 7) << 3;
|
||||
return (0);
|
||||
case ARCHIVE_ENTRY_ACL_OTHER:
|
||||
entry->ae_stat.st_mode &= 0770;
|
||||
entry->ae_stat.st_mode |= permset & 7;
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate and populate a new ACL entry with everything but the
|
||||
* name.
|
||||
*/
|
||||
static struct ae_acl *
|
||||
acl_new_entry(struct archive_entry *entry,
|
||||
int type, int permset, int tag, int id)
|
||||
{
|
||||
struct ae_acl *ap;
|
||||
|
||||
if (type != ARCHIVE_ENTRY_ACL_TYPE_ACCESS &&
|
||||
type != ARCHIVE_ENTRY_ACL_TYPE_DEFAULT)
|
||||
return (NULL);
|
||||
if (entry->acl_text_w != NULL) {
|
||||
free(entry->acl_text_w);
|
||||
entry->acl_text_w = NULL;
|
||||
}
|
||||
|
||||
/* XXX TODO: More sanity-checks on the arguments XXX */
|
||||
|
||||
/* If there's a matching entry already in the list, overwrite it. */
|
||||
for (ap = entry->acl_head; ap != NULL; ap = ap->next) {
|
||||
if (ap->type == type && ap->tag == tag && ap->id == id) {
|
||||
ap->permset = permset;
|
||||
return (ap);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add a new entry to the list. */
|
||||
ap = malloc(sizeof(*ap));
|
||||
memset(ap, 0, sizeof(*ap));
|
||||
ap->next = entry->acl_head;
|
||||
entry->acl_head = ap;
|
||||
ap->type = type;
|
||||
ap->tag = tag;
|
||||
ap->id = id;
|
||||
ap->permset = permset;
|
||||
return (ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a count of entries matching "want_type".
|
||||
*/
|
||||
int
|
||||
archive_entry_acl_count(struct archive_entry *entry, int want_type)
|
||||
{
|
||||
int count;
|
||||
struct ae_acl *ap;
|
||||
|
||||
count = 0;
|
||||
ap = entry->acl_head;
|
||||
while (ap != NULL) {
|
||||
if ((ap->type & want_type) != 0)
|
||||
count++;
|
||||
ap = ap->next;
|
||||
}
|
||||
|
||||
if (count > 0 && ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0))
|
||||
count += 3;
|
||||
return (count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare for reading entries from the ACL data. Returns a count
|
||||
* of entries matching "want_type", or zero if there are no
|
||||
* non-extended ACL entries of that type.
|
||||
*/
|
||||
int
|
||||
archive_entry_acl_reset(struct archive_entry *entry, int want_type)
|
||||
{
|
||||
int count, cutoff;
|
||||
|
||||
count = archive_entry_acl_count(entry, want_type);
|
||||
|
||||
/*
|
||||
* If the only entries are the three standard ones,
|
||||
* then don't return any ACL data. (In this case,
|
||||
* client can just use chmod(2) to set permissions.)
|
||||
*/
|
||||
if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)
|
||||
cutoff = 3;
|
||||
else
|
||||
cutoff = 0;
|
||||
|
||||
if (count > cutoff)
|
||||
entry->acl_state = ARCHIVE_ENTRY_ACL_USER_OBJ;
|
||||
else
|
||||
entry->acl_state = 0;
|
||||
entry->acl_p = NULL;
|
||||
return (count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the next ACL entry in the list. Fake entries for the
|
||||
* standard permissions and include them in the returned list.
|
||||
*/
|
||||
|
||||
int
|
||||
archive_entry_acl_next(struct archive_entry *entry, int want_type, int *type,
|
||||
int *permset, int *tag, int *id, const char **name)
|
||||
{
|
||||
*name = NULL;
|
||||
*id = -1;
|
||||
|
||||
/*
|
||||
* The acl_state is either zero (no entries available), -1
|
||||
* (reading from list), or an entry type (retrieve that type
|
||||
* from ae_stat.st_mode).
|
||||
*/
|
||||
|
||||
if (entry->acl_state == 0)
|
||||
return (ARCHIVE_WARN);
|
||||
|
||||
if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0 &&
|
||||
entry->acl_state > 0) {
|
||||
*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
|
||||
*tag = entry->acl_state;
|
||||
switch (entry->acl_state) {
|
||||
case ARCHIVE_ENTRY_ACL_USER_OBJ:
|
||||
*permset = (entry->ae_stat.st_mode >> 6) & 7;
|
||||
entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
|
||||
break;
|
||||
case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
|
||||
*permset = (entry->ae_stat.st_mode >> 3) & 7;
|
||||
entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
|
||||
break;
|
||||
case ARCHIVE_ENTRY_ACL_OTHER:
|
||||
*permset = entry->ae_stat.st_mode & 7;
|
||||
entry->acl_state = -1;
|
||||
entry->acl_p = entry->acl_head;
|
||||
break;
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
while (entry->acl_p != NULL && (entry->acl_p->type & want_type) == 0)
|
||||
entry->acl_p = entry->acl_p->next;
|
||||
if (entry->acl_p == NULL) {
|
||||
entry->acl_state = 0;
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
*type = entry->acl_p->type;
|
||||
*permset = entry->acl_p->permset;
|
||||
*tag = entry->acl_p->tag;
|
||||
*id = entry->acl_p->id;
|
||||
*name = aes_get_mbs(&entry->acl_p->name);
|
||||
entry->acl_p = entry->acl_p->next;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a text version of the ACL. The format here varies
|
||||
* from POSIX.1e in a couple of useful ways:
|
||||
*
|
||||
* * An additional colon-delimited field holds the numeric uid
|
||||
* or gid. For proper archiving, it is essential to have both
|
||||
* the uname/gname and the uid/gid.
|
||||
*
|
||||
* * You can request a single text holding both access and default
|
||||
* entries. In this case, each default entry is prefixed with
|
||||
* "default:".
|
||||
*/
|
||||
const wchar_t *
|
||||
__archive_entry_acl_text_w(struct archive_entry *entry, int type)
|
||||
{
|
||||
int count;
|
||||
int length;
|
||||
const wchar_t *wname;
|
||||
struct ae_acl *ap;
|
||||
wchar_t *wp;
|
||||
|
||||
if (entry->acl_text_w != NULL) {
|
||||
free (entry->acl_text_w);
|
||||
entry->acl_text_w = NULL;
|
||||
}
|
||||
|
||||
count = 0;
|
||||
length = 0;
|
||||
ap = entry->acl_head;
|
||||
while (ap != NULL) {
|
||||
if ((ap->type & type) != 0) {
|
||||
count++;
|
||||
if (type & (ARCHIVE_ENTRY_ACL_TYPE_ACCESS |
|
||||
ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
|
||||
length += 8; /* "default:" */
|
||||
length += 5; /* tag name */
|
||||
length += 1; /* colon */
|
||||
wname = aes_get_wcs(&ap->name);
|
||||
if (wname != NULL)
|
||||
length += wcslen(wname);
|
||||
length ++; /* colon */
|
||||
length += 3; /* rwx */
|
||||
length += 1; /* colon */
|
||||
length += max(sizeof(uid_t),sizeof(gid_t)) * 3 + 1;
|
||||
length ++; /* newline */
|
||||
}
|
||||
ap = ap->next;
|
||||
}
|
||||
|
||||
if (count > 0 && ((type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
|
||||
length += 10; /* "user::rwx\n" */
|
||||
length += 11; /* "group::rwx\n" */
|
||||
length += 11; /* "other::rwx\n" */
|
||||
}
|
||||
|
||||
if (count == 0)
|
||||
return (NULL);
|
||||
|
||||
/* Now, allocate the string and actually populate it. */
|
||||
wp = entry->acl_text_w = malloc(length * sizeof(wchar_t));
|
||||
count = 0;
|
||||
if ((type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
|
||||
append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
|
||||
entry->ae_stat.st_mode & 0700, -1);
|
||||
*wp++ = ',';
|
||||
append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
|
||||
entry->ae_stat.st_mode & 0070, -1);
|
||||
*wp++ = ',';
|
||||
append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
|
||||
entry->ae_stat.st_mode & 0007, -1);
|
||||
count += 3;
|
||||
|
||||
ap = entry->acl_head;
|
||||
while (ap != NULL) {
|
||||
if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
|
||||
wname = aes_get_wcs(&ap->name);
|
||||
*wp++ = ',';
|
||||
append_entry_w(&wp, NULL, ap->tag, wname,
|
||||
ap->permset, ap->id);
|
||||
count++;
|
||||
}
|
||||
ap = ap->next;
|
||||
}
|
||||
}
|
||||
|
||||
if ((type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
|
||||
ap = entry->acl_head;
|
||||
while (ap != NULL) {
|
||||
if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
|
||||
wname = aes_get_wcs(&ap->name);
|
||||
if (count > 0)
|
||||
*wp++ = ',';
|
||||
append_entry_w(&wp, L"default:", ap->tag,
|
||||
wname, ap->permset, ap->id);
|
||||
}
|
||||
ap = ap->next;
|
||||
}
|
||||
}
|
||||
|
||||
return (entry->acl_text_w);
|
||||
}
|
||||
|
||||
static void
|
||||
append_id_w(wchar_t **wp, int id)
|
||||
{
|
||||
if (id > 9)
|
||||
append_id_w(wp, id / 10);
|
||||
*(*wp)++ = L"0123456789"[id % 10];
|
||||
}
|
||||
|
||||
static void
|
||||
append_entry_w(wchar_t **wp, const wchar_t *prefix, int tag,
|
||||
const wchar_t *wname, int perm, int id)
|
||||
{
|
||||
if (prefix != NULL) {
|
||||
wcscpy(*wp, prefix);
|
||||
*wp += wcslen(*wp);
|
||||
}
|
||||
switch (tag) {
|
||||
case ARCHIVE_ENTRY_ACL_USER_OBJ:
|
||||
wname = NULL;
|
||||
id = -1;
|
||||
/* FALL THROUGH */
|
||||
case ARCHIVE_ENTRY_ACL_USER:
|
||||
wcscpy(*wp, L"user");
|
||||
break;
|
||||
case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
|
||||
wname = NULL;
|
||||
id = -1;
|
||||
/* FALL THROUGH */
|
||||
case ARCHIVE_ENTRY_ACL_GROUP:
|
||||
wcscpy(*wp, L"group");
|
||||
break;
|
||||
case ARCHIVE_ENTRY_ACL_MASK:
|
||||
wcscpy(*wp, L"mask");
|
||||
wname = NULL;
|
||||
id = -1;
|
||||
break;
|
||||
case ARCHIVE_ENTRY_ACL_OTHER:
|
||||
wcscpy(*wp, L"other");
|
||||
wname = NULL;
|
||||
id = -1;
|
||||
break;
|
||||
}
|
||||
*wp += wcslen(*wp);
|
||||
*(*wp)++ = L':';
|
||||
if (wname != NULL)
|
||||
wcscpy(*wp, wname);
|
||||
*wp += wcslen(*wp);
|
||||
*(*wp)++ = L':';
|
||||
*(*wp)++ = (perm & 0444) ? L'r' : L'-';
|
||||
*(*wp)++ = (perm & 0222) ? L'w' : L'-';
|
||||
*(*wp)++ = (perm & 0111) ? L'x' : L'-';
|
||||
if (id != -1) {
|
||||
*(*wp)++ = L':';
|
||||
append_id_w(wp, id);
|
||||
}
|
||||
**wp = L'\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse a textual ACL. This automatically recognizes and supports
|
||||
* extensions described above. The 'type' argument is used to
|
||||
* indicate the type that should be used for any entries not
|
||||
* explicitly marked as "default:".
|
||||
*/
|
||||
int
|
||||
__archive_entry_acl_parse_w(struct archive_entry *entry,
|
||||
const wchar_t *text, int default_type)
|
||||
{
|
||||
int type, tag, permset, id;
|
||||
const wchar_t *start, *end;
|
||||
const wchar_t *name_start, *name_end;
|
||||
wchar_t sep;
|
||||
wchar_t *namebuff;
|
||||
int namebuff_length;
|
||||
|
||||
name_start = name_end = NULL;
|
||||
namebuff = NULL;
|
||||
namebuff_length = 0;
|
||||
|
||||
archive_entry_acl_clear(entry);
|
||||
|
||||
while (text != NULL && *text != L'\0') {
|
||||
next_field_w(&text, &start, &end, &sep);
|
||||
if (sep != L':')
|
||||
goto fail;
|
||||
|
||||
if (prefix_w(start, end, L"default")) {
|
||||
type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
|
||||
next_field_w(&text, &start, &end, &sep);
|
||||
if (sep != L':')
|
||||
goto fail;
|
||||
} else
|
||||
type = default_type;
|
||||
|
||||
if (prefix_w(start, end, L"user")) {
|
||||
next_field_w(&text, &start, &end, &sep);
|
||||
if (sep != L':')
|
||||
goto fail;
|
||||
if (end > start) {
|
||||
tag = ARCHIVE_ENTRY_ACL_USER;
|
||||
name_start = start;
|
||||
name_end = end;
|
||||
} else
|
||||
tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
|
||||
} else if (prefix_w(start, end, L"group")) {
|
||||
next_field_w(&text, &start, &end, &sep);
|
||||
if (sep != L':')
|
||||
goto fail;
|
||||
if (end > start) {
|
||||
tag = ARCHIVE_ENTRY_ACL_GROUP;
|
||||
name_start = start;
|
||||
name_end = end;
|
||||
} else
|
||||
tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
|
||||
} else if (prefix_w(start, end, L"other")) {
|
||||
next_field_w(&text, &start, &end, &sep);
|
||||
if (sep != L':')
|
||||
goto fail;
|
||||
if (end > start)
|
||||
goto fail;
|
||||
tag = ARCHIVE_ENTRY_ACL_OTHER;
|
||||
} else if (prefix_w(start, end, L"mask")) {
|
||||
next_field_w(&text, &start, &end, &sep);
|
||||
if (sep != L':')
|
||||
goto fail;
|
||||
if (end > start)
|
||||
goto fail;
|
||||
tag = ARCHIVE_ENTRY_ACL_MASK;
|
||||
} else
|
||||
goto fail;
|
||||
|
||||
next_field_w(&text, &start, &end, &sep);
|
||||
permset = 0;
|
||||
while (start < end) {
|
||||
switch (*start++) {
|
||||
case 'r': case 'R':
|
||||
permset |= ARCHIVE_ENTRY_ACL_READ;
|
||||
break;
|
||||
case 'w': case 'W':
|
||||
permset |= ARCHIVE_ENTRY_ACL_WRITE;
|
||||
break;
|
||||
case 'x': case 'X':
|
||||
permset |= ARCHIVE_ENTRY_ACL_EXECUTE;
|
||||
break;
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Support star-compatible numeric UID/GID extension.
|
||||
* This extension adds a ":" followed by the numeric
|
||||
* ID so that "group:groupname:rwx", for example,
|
||||
* becomes "group:groupname:rwx:999", where 999 is the
|
||||
* numeric GID. This extension makes it possible, for
|
||||
* example, to correctly restore ACLs on a system that
|
||||
* might have a damaged passwd file or be disconnected
|
||||
* from a central NIS server. This extension is compatible
|
||||
* with POSIX.1e draft 17.
|
||||
*/
|
||||
if (sep == L':' && (tag == ARCHIVE_ENTRY_ACL_USER ||
|
||||
tag == ARCHIVE_ENTRY_ACL_GROUP)) {
|
||||
next_field_w(&text, &start, &end, &sep);
|
||||
|
||||
id = 0;
|
||||
while (start < end && *start >= '0' && *start <= '9') {
|
||||
if (id > (INT_MAX / 10))
|
||||
id = INT_MAX;
|
||||
else {
|
||||
id *= 10;
|
||||
id += *start - '0';
|
||||
start++;
|
||||
}
|
||||
}
|
||||
} else
|
||||
id = -1; /* No id specified. */
|
||||
|
||||
/* Skip any additional entries. */
|
||||
while (sep == L':') {
|
||||
next_field_w(&text, &start, &end, &sep);
|
||||
}
|
||||
|
||||
/* Add entry to the internal list. */
|
||||
if (name_end == name_start) {
|
||||
archive_entry_acl_add_entry_w(entry, type, permset,
|
||||
tag, id, NULL);
|
||||
} else {
|
||||
if (namebuff_length <= name_end - name_start) {
|
||||
if (namebuff != NULL)
|
||||
free(namebuff);
|
||||
namebuff_length = name_end - name_start + 256;
|
||||
namebuff =
|
||||
malloc(namebuff_length * sizeof(wchar_t));
|
||||
}
|
||||
wmemcpy(namebuff, start, end-start);
|
||||
archive_entry_acl_add_entry_w(entry, type,
|
||||
permset, tag, id, namebuff);
|
||||
}
|
||||
}
|
||||
return (ARCHIVE_OK);
|
||||
|
||||
fail:
|
||||
fprintf(stderr, "ACL error\n");
|
||||
if (namebuff != NULL)
|
||||
free(namebuff);
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
/*
|
||||
* Match "[:whitespace:]*(.*)[:whitespace:]*[:,\n]". *wp is updated
|
||||
* to point to just after the separator. *start points to the first
|
||||
* character of the matched text and *end just after the last
|
||||
* character of the matched identifier. In particular *end - *start
|
||||
* is the length of the field body, not including leading or trailing
|
||||
* whitespace.
|
||||
*/
|
||||
static void
|
||||
next_field_w(const wchar_t **wp, const wchar_t **start,
|
||||
const wchar_t **end, wchar_t *sep)
|
||||
{
|
||||
/* Skip leading whitespace to find start of field. */
|
||||
while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
|
||||
(*wp)++;
|
||||
}
|
||||
*start = *wp;
|
||||
|
||||
/* Scan for the separator. */
|
||||
while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
|
||||
**wp != L'\n') {
|
||||
(*wp)++;
|
||||
}
|
||||
*sep = **wp;
|
||||
|
||||
/* Trim trailing whitespace to locate end of field. */
|
||||
*end = *wp - 1;
|
||||
while (**end == L' ' || **end == L'\t' || **end == L'\n') {
|
||||
(*end)--;
|
||||
}
|
||||
(*end)++;
|
||||
|
||||
/* Adjust scanner location. */
|
||||
if (**wp != L'\0')
|
||||
(*wp)++;
|
||||
}
|
||||
|
||||
static int
|
||||
prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test)
|
||||
{
|
||||
if (start == end)
|
||||
return (0);
|
||||
|
||||
if (*start++ != *test++)
|
||||
return (0);
|
||||
|
||||
while (start < end && *start++ == *test++)
|
||||
;
|
||||
|
||||
if (start < end)
|
||||
return (0);
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
|
||||
#if TEST
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
|
|
|
|||
|
|
@ -66,14 +66,14 @@ struct archive_entry *archive_entry_new(void);
|
|||
* Retrieve fields from an archive_entry.
|
||||
*/
|
||||
|
||||
const char *archive_entry_acl(struct archive_entry *);
|
||||
const char *archive_entry_acl_default(struct archive_entry *);
|
||||
dev_t archive_entry_devmajor(struct archive_entry *);
|
||||
dev_t archive_entry_devminor(struct archive_entry *);
|
||||
const char *archive_entry_fflags(struct archive_entry *);
|
||||
const char *archive_entry_gname(struct archive_entry *);
|
||||
const char *archive_entry_hardlink(struct archive_entry *);
|
||||
mode_t archive_entry_mode(struct archive_entry *);
|
||||
time_t archive_entry_mtime(struct archive_entry *);
|
||||
long archive_entry_mtime_nsec(struct archive_entry *);
|
||||
const char *archive_entry_pathname(struct archive_entry *);
|
||||
const wchar_t *archive_entry_pathname_w(struct archive_entry *);
|
||||
int64_t archive_entry_size(struct archive_entry *);
|
||||
|
|
@ -90,10 +90,6 @@ const char *archive_entry_uname(struct archive_entry *);
|
|||
*/
|
||||
|
||||
void archive_entry_copy_stat(struct archive_entry *, const struct stat *);
|
||||
void archive_entry_set_acl(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_acl_w(struct archive_entry *, const wchar_t *);
|
||||
void archive_entry_set_acl_default(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_acl_default_w(struct archive_entry *, const wchar_t *);
|
||||
void archive_entry_set_fflags(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_fflags_w(struct archive_entry *, const wchar_t *);
|
||||
void archive_entry_set_devmajor(struct archive_entry *, dev_t);
|
||||
|
|
@ -114,4 +110,66 @@ void archive_entry_set_uid(struct archive_entry *, uid_t);
|
|||
void archive_entry_set_uname(struct archive_entry *, const char *);
|
||||
void archive_entry_copy_uname_w(struct archive_entry *, const wchar_t *);
|
||||
|
||||
/*
|
||||
* ACL routines. This used to simply store and return text-format ACL
|
||||
* strings, but that proved insufficient. The intent here is to allow
|
||||
* libarchive internals to fetch/store text-format strings, but
|
||||
* clients use the more involved interface that allows them control
|
||||
* over uid/uname/gid/gname lookups.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Permission bits mimic POSIX.1e. Note that I've not followed POSIX.1e's
|
||||
* "permset"/"perm" abstract type nonsense. A permset is just a simple
|
||||
* bitmap, following long-standing Unix tradition.
|
||||
*/
|
||||
#define ARCHIVE_ENTRY_ACL_EXECUTE 1
|
||||
#define ARCHIVE_ENTRY_ACL_WRITE 2
|
||||
#define ARCHIVE_ENTRY_ACL_READ 4
|
||||
|
||||
/* We need to be able to specify either or both of these. */
|
||||
#define ARCHIVE_ENTRY_ACL_TYPE_ACCESS 256
|
||||
#define ARCHIVE_ENTRY_ACL_TYPE_DEFAULT 512
|
||||
|
||||
/* Tag values mimic POSIX.1e */
|
||||
#define ARCHIVE_ENTRY_ACL_USER 10001 /* Specified user. */
|
||||
#define ARCHIVE_ENTRY_ACL_USER_OBJ 10002 /* User who owns the file. */
|
||||
#define ARCHIVE_ENTRY_ACL_GROUP 10003 /* Specified group. */
|
||||
#define ARCHIVE_ENTRY_ACL_GROUP_OBJ 10004 /* Group who owns the file. */
|
||||
#define ARCHIVE_ENTRY_ACL_MASK 10005 /* Modify group access. */
|
||||
#define ARCHIVE_ENTRY_ACL_OTHER 10006 /* Public. */
|
||||
|
||||
|
||||
/*
|
||||
* Set the ACL by clearing it and adding entries one at a time.
|
||||
* Unlike the POSIX.1e ACL routines, you must specify the type
|
||||
* (access/default) for each entry. Internally, the ACL data is just
|
||||
* a soup of entries. API calls here allow you to retrieve just the
|
||||
* entries of interest. This design (which goes against the spirit of
|
||||
* POSIX.1e) is useful for handling archive formats that combine
|
||||
* default and access information in a single ACL list.
|
||||
*/
|
||||
void archive_entry_acl_clear(struct archive_entry *);
|
||||
void archive_entry_acl_add_entry(struct archive_entry *,
|
||||
int type, int permset, int tag, int qual, const char *name);
|
||||
void archive_entry_acl_add_entry_w(struct archive_entry *,
|
||||
int type, int permset, int tag, int qual, const wchar_t *name);
|
||||
|
||||
/*
|
||||
* To retrieve the ACL, first "reset", then repeatedly ask for the
|
||||
* "next" entry. The want_type parameter allows you to request only
|
||||
* access entries or only default entries.
|
||||
*/
|
||||
int archive_entry_acl_reset(struct archive_entry *, int want_type);
|
||||
int archive_entry_acl_next(struct archive_entry *, int want_type,
|
||||
int *type, int *permset, int *tag, int *qual, const char **name);
|
||||
int archive_entry_acl_next_w(struct archive_entry *, int want_type,
|
||||
int *type, int *permset, int *tag, int *qual,
|
||||
const wchar_t **name);
|
||||
|
||||
/* Return a count of entries matching 'want_type' */
|
||||
int archive_entry_acl_count(struct archive_entry *, int want_type);
|
||||
|
||||
|
||||
|
||||
#endif /* !ARCHIVE_ENTRY_H_INCLUDED */
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@
|
|||
#ifndef ARCHIVE_PRIVATE_H_INCLUDED
|
||||
#define ARCHIVE_PRIVATE_H_INCLUDED
|
||||
|
||||
#include <wchar.h>
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_string.h"
|
||||
|
||||
|
|
@ -229,4 +231,18 @@ int __archive_read_register_compression(struct archive *a,
|
|||
|
||||
#define err_combine(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
|
||||
/*
|
||||
* Private ACL handling: parse and generate ACL strings.
|
||||
* These are private because they handle a lot of very weird formats
|
||||
* that clients should not be messing with. Clients should only
|
||||
* deal with their platform-native formats. Because of the need to
|
||||
* support many formats cleanly, new arguments are likely to get added
|
||||
* on a regular basis. Clients who try to use this interface are
|
||||
* likely to be surprised when it changes.
|
||||
*/
|
||||
int __archive_entry_acl_parse_w(struct archive_entry *,
|
||||
const wchar_t *, int type);
|
||||
const wchar_t *__archive_entry_acl_text_w(struct archive_entry *, int type);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@
|
|||
.Nm archive_read_support_format_cpio ,
|
||||
.Nm archive_read_support_format_all ,
|
||||
.Nm archive_read_open ,
|
||||
.Nm archive_read_open_fd ,
|
||||
.Nm archive_read_open_file ,
|
||||
.Nm archive_read_next_header ,
|
||||
.Nm archive_read_data ,
|
||||
|
|
@ -73,6 +74,8 @@
|
|||
.Ft int
|
||||
.Fn archive_read_open "struct archive *" "void *client_data" "archive_read_archive_callback *" "archive_open_archive_callback *" "archive_close_archive_callback *"
|
||||
.Ft int
|
||||
.Fn archive_read_open_fd "struct archive *" "int fd"
|
||||
.Ft int
|
||||
.Fn archive_read_open_file "struct archive *" "const char *filename"
|
||||
.Ft int
|
||||
.Fn archive_read_next_header "struct archive *" "struct archive_entry **"
|
||||
|
|
@ -133,6 +136,21 @@ This is the most generic version of this call, which accepts
|
|||
three callback functions.
|
||||
The library invokes these client-provided functions to obtain
|
||||
raw bytes from the archive.
|
||||
Note: The API permits a decompression method to fork and invoke the
|
||||
callbacks from another process.
|
||||
Although none of the current decompression methods use this technique,
|
||||
future decompression methods may utilize this technique.
|
||||
If the decompressor forks, it will ensure that the open and close
|
||||
callbacks are invoked within the same process as the read callback.
|
||||
In particular, clients should not attempt to use shared variables to
|
||||
communicate between the open/read/close callbacks and the mainline code.
|
||||
.It Fn archive_read_open_fd
|
||||
Like
|
||||
.Fn archive_read_open ,
|
||||
except that it accepts a file descriptor rather than
|
||||
a trio of function pointers.
|
||||
Note that the file descriptor will not be automatically closed at
|
||||
end-of-archive.
|
||||
.It Fn archive_read_open_file
|
||||
Like
|
||||
.Fn archive_read_open ,
|
||||
|
|
|
|||
|
|
@ -405,6 +405,9 @@ archive_read_data_skip(struct archive *a)
|
|||
void
|
||||
archive_read_finish(struct archive *a)
|
||||
{
|
||||
int i;
|
||||
int slots;
|
||||
|
||||
archive_check_magic(a, ARCHIVE_READ_MAGIC, ARCHIVE_STATE_ANY);
|
||||
a->state = ARCHIVE_STATE_CLOSED;
|
||||
|
||||
|
|
@ -418,23 +421,21 @@ archive_read_finish(struct archive *a)
|
|||
if (a->compression_finish != NULL)
|
||||
(a->compression_finish)(a);
|
||||
|
||||
/*-
|
||||
* Release allocated strings.
|
||||
*
|
||||
* TODO: Add a "cleanup" column to the "formats" array and
|
||||
* use that to cleanup format-specific data. E.g.,
|
||||
*
|
||||
* for (i=0; i< slots; i++) {
|
||||
* if (a->formats[i].cleanup)
|
||||
* (a->formats[i].cleanup)(a);
|
||||
* }
|
||||
*/
|
||||
/* Cleanup format-specific data. */
|
||||
slots = sizeof(a->formats) / sizeof(a->formats[0]);
|
||||
for (i = 0; i < slots; i++) {
|
||||
a->pformat_data = &(a->formats[i].format_data);
|
||||
if (a->formats[i].cleanup)
|
||||
(a->formats[i].cleanup)(a);
|
||||
}
|
||||
|
||||
/* Casting a pointer to int allows us to remove 'const.' */
|
||||
free((void *)(uintptr_t)(const void *)a->nulls);
|
||||
if (a->extract_mkdirpath.s != NULL)
|
||||
free(a->extract_mkdirpath.s);
|
||||
if (a->entry)
|
||||
archive_entry_free(a->entry);
|
||||
a->magic = 0;
|
||||
free(a);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -74,12 +74,14 @@ static int archive_read_extract_regular_open(struct archive *,
|
|||
const char *name, int mode, int flags);
|
||||
static int archive_read_extract_symbolic_link(struct archive *,
|
||||
struct archive_entry *, int);
|
||||
static gid_t lookup_gid(struct archive *, const char *uname, gid_t);
|
||||
static uid_t lookup_uid(struct archive *, const char *uname, uid_t);
|
||||
static int mkdirpath(struct archive *, const char *);
|
||||
static int mkdirpath_recursive(char *path);
|
||||
static int mksubdir(char *path);
|
||||
#ifdef HAVE_POSIX_ACL
|
||||
static int set_acl(struct archive *a, const char *acl_text,
|
||||
acl_type_t type, const char *pathname);
|
||||
static int set_acl(struct archive *, struct archive_entry *,
|
||||
acl_type_t, int archive_entry_acl_type);
|
||||
#endif
|
||||
static int set_acls(struct archive *, struct archive_entry *);
|
||||
static int set_extended_perm(struct archive *, struct archive_entry *,
|
||||
|
|
@ -673,6 +675,9 @@ mksubdir(char *path)
|
|||
static int
|
||||
set_ownership(struct archive *a, struct archive_entry *entry, int flags)
|
||||
{
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
|
||||
/* If UID/GID are already correct, return 0. */
|
||||
/* TODO: Fix this; need to stat() to find on-disk GID <sigh> */
|
||||
if (a->user_uid == archive_entry_stat(entry)->st_uid)
|
||||
|
|
@ -682,21 +687,23 @@ set_ownership(struct archive *a, struct archive_entry *entry, int flags)
|
|||
if ((flags & ARCHIVE_EXTRACT_OWNER) == 0)
|
||||
return (ARCHIVE_WARN);
|
||||
|
||||
uid = lookup_uid(a, archive_entry_uname(entry),
|
||||
archive_entry_stat(entry)->st_uid);
|
||||
gid = lookup_gid(a, archive_entry_gname(entry),
|
||||
archive_entry_stat(entry)->st_gid);
|
||||
|
||||
/*
|
||||
* Root can change owner/group; owner can change group;
|
||||
* otherwise, bail out now.
|
||||
*/
|
||||
if ((a->user_uid != 0)
|
||||
&& (a->user_uid != archive_entry_stat(entry)->st_uid))
|
||||
if (a->user_uid != 0 && a->user_uid != uid) {
|
||||
/* XXXX archive_set_error( XXXX ) ; XXX */
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
||||
if (lchown(archive_entry_pathname(entry),
|
||||
archive_entry_stat(entry)->st_uid,
|
||||
archive_entry_stat(entry)->st_gid)) {
|
||||
if (lchown(archive_entry_pathname(entry), uid, gid)) {
|
||||
archive_set_error(a, errno,
|
||||
"Can't set user=%d/group=%d for %s",
|
||||
archive_entry_stat(entry)->st_uid,
|
||||
archive_entry_stat(entry)->st_gid,
|
||||
"Can't set user=%d/group=%d for %s", uid, gid,
|
||||
archive_entry_pathname(entry));
|
||||
return (ARCHIVE_WARN);
|
||||
}
|
||||
|
|
@ -817,64 +824,130 @@ set_fflags(struct archive *a, struct archive_entry *entry)
|
|||
return (ret);
|
||||
}
|
||||
|
||||
#ifndef HAVE_POSIX_ACL
|
||||
/* Default empty function body to satisfy mainline code. */
|
||||
static int
|
||||
set_acls(struct archive *a, struct archive_entry *entry)
|
||||
{
|
||||
(void)a;
|
||||
(void)entry;
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* XXX TODO: What about ACL types other than ACCESS and DEFAULT?
|
||||
*/
|
||||
static int
|
||||
set_acls(struct archive *a, struct archive_entry *entry)
|
||||
{
|
||||
#ifdef HAVE_POSIX_ACL
|
||||
const char *acl_text;
|
||||
const char *name;
|
||||
int ret;
|
||||
|
||||
ret = ARCHIVE_OK;
|
||||
|
||||
name = archive_entry_pathname(entry);
|
||||
acl_text = archive_entry_acl(entry);
|
||||
ret = set_acl(a, acl_text, ACL_TYPE_ACCESS, name);
|
||||
if (ret == ARCHIVE_OK) {
|
||||
acl_text = archive_entry_acl_default(entry);
|
||||
ret = set_acl(a, acl_text, ACL_TYPE_DEFAULT, name);
|
||||
}
|
||||
|
||||
ret = set_acl(a, entry, ACL_TYPE_ACCESS,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
|
||||
if (ret != ARCHIVE_OK)
|
||||
return (ret);
|
||||
ret = set_acl(a, entry, ACL_TYPE_DEFAULT,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
|
||||
return (ret);
|
||||
#else
|
||||
/* Default empty function body to satisfy mainline code. */
|
||||
(void)a;
|
||||
(void)entry;
|
||||
return (ARCHIVE_OK);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef HAVE_POSIX_ACL
|
||||
|
||||
static int
|
||||
set_acl(struct archive *a, const char *acl_text, acl_type_t type,
|
||||
const char *name)
|
||||
set_acl(struct archive *a, struct archive_entry *entry, acl_type_t acl_type,
|
||||
int ae_requested_type)
|
||||
{
|
||||
acl_t acl;
|
||||
acl_entry_t acl_entry;
|
||||
acl_permset_t acl_permset;
|
||||
int ret;
|
||||
int ae_type, ae_permset, ae_tag, ae_id;
|
||||
uid_t ae_uid;
|
||||
gid_t ae_gid;
|
||||
const char *ae_name;
|
||||
int entries;
|
||||
const char *name;
|
||||
|
||||
ret = ARCHIVE_OK;
|
||||
entries = archive_entry_acl_reset(entry, ae_requested_type);
|
||||
if (entries == 0)
|
||||
return (ARCHIVE_OK);
|
||||
acl = acl_init(entries);
|
||||
while (archive_entry_acl_next(entry, ae_requested_type, &ae_type,
|
||||
&ae_permset, &ae_tag, &ae_id, &ae_name) == ARCHIVE_OK) {
|
||||
acl_create_entry(&acl, &acl_entry);
|
||||
|
||||
if (acl_text == NULL)
|
||||
return (ret);
|
||||
if (ae_tag == ARCHIVE_ENTRY_ACL_USER) {
|
||||
acl_set_tag_type(acl_entry, ACL_USER);
|
||||
ae_uid = lookup_uid(a, ae_name, ae_id);
|
||||
acl_set_qualifier(acl_entry, &ae_uid);
|
||||
} else if (ae_tag == ARCHIVE_ENTRY_ACL_GROUP) {
|
||||
acl_set_tag_type(acl_entry, ACL_GROUP);
|
||||
ae_gid = lookup_gid(a, ae_name, ae_id);
|
||||
acl_set_qualifier(acl_entry, &ae_gid);
|
||||
} else if (ae_tag == ARCHIVE_ENTRY_ACL_USER_OBJ)
|
||||
acl_set_tag_type(acl_entry, ACL_USER_OBJ);
|
||||
else if (ae_tag == ARCHIVE_ENTRY_ACL_GROUP_OBJ)
|
||||
acl_set_tag_type(acl_entry, ACL_GROUP_OBJ);
|
||||
else if (ae_tag == ARCHIVE_ENTRY_ACL_MASK)
|
||||
acl_set_tag_type(acl_entry, ACL_MASK);
|
||||
else if (ae_tag == ARCHIVE_ENTRY_ACL_OTHER)
|
||||
acl_set_tag_type(acl_entry, ACL_OTHER);
|
||||
|
||||
acl = acl_from_text(acl_text);
|
||||
if (acl == NULL) {
|
||||
archive_set_error(a, errno, "Error parsing acl '%s'",
|
||||
acl_text);
|
||||
ret = ARCHIVE_WARN;
|
||||
} else {
|
||||
if (acl_set_file(name, type, acl) != 0) {
|
||||
archive_set_error(a, errno,
|
||||
"Failed to set acl '%s' (type %d)",
|
||||
acl_text, type);
|
||||
ret = ARCHIVE_WARN;
|
||||
}
|
||||
acl_free(acl);
|
||||
acl_get_permset(acl_entry, &acl_permset);
|
||||
acl_clear_perms(acl_permset);
|
||||
if (ae_permset & ARCHIVE_ENTRY_ACL_EXECUTE)
|
||||
acl_add_perm(acl_permset, ACL_EXECUTE);
|
||||
if (ae_permset & ARCHIVE_ENTRY_ACL_WRITE)
|
||||
acl_add_perm(acl_permset, ACL_WRITE);
|
||||
if (ae_permset & ARCHIVE_ENTRY_ACL_READ)
|
||||
acl_add_perm(acl_permset, ACL_READ);
|
||||
}
|
||||
|
||||
name = archive_entry_pathname(entry);
|
||||
if (acl_set_file(name, acl_type, acl) != 0) {
|
||||
archive_set_error(a, errno, "Failed to set acl");
|
||||
ret = ARCHIVE_WARN;
|
||||
}
|
||||
acl_free(acl);
|
||||
return (ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* XXX The following gid/uid lookups can be a performance bottleneck.
|
||||
* Some form of caching would probably be very effective, though
|
||||
* I have concerns about staleness.
|
||||
*/
|
||||
static gid_t
|
||||
lookup_gid(struct archive *a, const char *gname, gid_t gid)
|
||||
{
|
||||
struct group *grent;
|
||||
|
||||
(void)a; /* UNUSED */
|
||||
|
||||
/* Look up gid from gname. */
|
||||
if (gname != NULL && *gname != '\0') {
|
||||
grent = getgrnam(gname);
|
||||
if (grent != NULL)
|
||||
gid = grent->gr_gid;
|
||||
}
|
||||
return (gid);
|
||||
}
|
||||
|
||||
static uid_t
|
||||
lookup_uid(struct archive *a, const char *uname, uid_t uid)
|
||||
{
|
||||
struct passwd *pwent;
|
||||
|
||||
(void)a; /* UNUSED */
|
||||
|
||||
/* Look up uid from uname. */
|
||||
if (uname != NULL && *uname != '\0') {
|
||||
pwent = getpwnam(uname);
|
||||
if (pwent != NULL)
|
||||
uid = pwent->pw_uid;
|
||||
}
|
||||
return (uid);
|
||||
}
|
||||
|
|
|
|||
93
lib/libarchive/archive_read_open_fd.c
Normal file
93
lib/libarchive/archive_read_open_fd.c
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
/*-
|
||||
* Copyright (c) 2003-2004 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "archive_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#ifdef HAVE_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "archive.h"
|
||||
|
||||
struct read_fd_data {
|
||||
int fd;
|
||||
size_t block_size;
|
||||
void *buffer;
|
||||
};
|
||||
|
||||
static int file_close(struct archive *, void *);
|
||||
static int file_open(struct archive *, void *);
|
||||
static ssize_t file_read(struct archive *, void *, const void **buff);
|
||||
|
||||
int
|
||||
archive_read_open_fd(struct archive *a, int fd, size_t block_size)
|
||||
{
|
||||
struct read_fd_data *mine;
|
||||
|
||||
mine = malloc(sizeof(*mine));
|
||||
if (mine == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
mine->block_size = block_size;
|
||||
mine->buffer = malloc(mine->block_size);
|
||||
mine->fd = fd;
|
||||
return (archive_read_open(a, mine, file_open, file_read, file_close));
|
||||
}
|
||||
|
||||
static int
|
||||
file_open(struct archive *a, void *client_data)
|
||||
{
|
||||
(void)client_data; /* UNUSED */
|
||||
(void)a; /* UNUSED */
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
file_read(struct archive *a, void *client_data, const void **buff)
|
||||
{
|
||||
struct read_fd_data *mine = client_data;
|
||||
|
||||
(void)a; /* UNUSED */
|
||||
*buff = mine->buffer;
|
||||
return (read(mine->fd, mine->buffer, mine->block_size));
|
||||
}
|
||||
|
||||
static int
|
||||
file_close(struct archive *a, void *client_data)
|
||||
{
|
||||
struct read_fd_data *mine = client_data;
|
||||
|
||||
(void)a; /* UNUSED */
|
||||
free(mine);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
|
@ -55,12 +55,19 @@ archive_read_open_file(struct archive *a, const char *filename,
|
|||
{
|
||||
struct read_file_data *mine;
|
||||
|
||||
/* XXX detect and report malloc failure XXX */
|
||||
if (filename == NULL) {
|
||||
mine = malloc(sizeof(*mine));
|
||||
if (mine == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
mine->filename[0] = 0;
|
||||
} else {
|
||||
mine = malloc(sizeof(*mine) + strlen(filename));
|
||||
if (mine == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
strcpy(mine->filename, filename);
|
||||
}
|
||||
mine->block_size = block_size;
|
||||
|
|
|
|||
|
|
@ -55,12 +55,19 @@ archive_read_open_file(struct archive *a, const char *filename,
|
|||
{
|
||||
struct read_file_data *mine;
|
||||
|
||||
/* XXX detect and report malloc failure XXX */
|
||||
if (filename == NULL) {
|
||||
mine = malloc(sizeof(*mine));
|
||||
if (mine == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
mine->filename[0] = 0;
|
||||
} else {
|
||||
mine = malloc(sizeof(*mine) + strlen(filename));
|
||||
if (mine == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
strcpy(mine->filename, filename);
|
||||
}
|
||||
mine->block_size = block_size;
|
||||
|
|
|
|||
|
|
@ -241,6 +241,7 @@ archive_decompressor_none_read_consume(struct archive *a, size_t request)
|
|||
state->client_next += request;
|
||||
state->client_avail -= request;
|
||||
}
|
||||
a->file_position += request;
|
||||
return (request);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -831,9 +831,11 @@ pax_attribute(struct archive_entry *entry, struct stat *st,
|
|||
case 'S':
|
||||
/* We support some keys used by the "star" archiver */
|
||||
if (wcscmp(key, L"SCHILY.acl.access")==0)
|
||||
archive_entry_copy_acl_w(entry, value);
|
||||
__archive_entry_acl_parse_w(entry, value,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
|
||||
else if (wcscmp(key, L"SCHILY.acl.default")==0)
|
||||
archive_entry_copy_acl_default_w(entry, value);
|
||||
__archive_entry_acl_parse_w(entry, value,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
|
||||
else if (wcscmp(key, L"SCHILY.devmajor")==0)
|
||||
st->st_rdev = makedev(tar_atol10(value, wcslen(value)),
|
||||
minor(st->st_dev));
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@
|
|||
.Nm archive_write_set_compressor_gzip ,
|
||||
.Nm archive_write_set_compressor_bzip2 ,
|
||||
.Nm archive_write_open ,
|
||||
.Nm archive_write_open_fd ,
|
||||
.Nm archive_write_open_file ,
|
||||
.Nm archive_write_open_tar ,
|
||||
.Nm archive_write_prepare ,
|
||||
.Nm archive_write_header ,
|
||||
.Nm archive_write_data ,
|
||||
|
|
@ -74,9 +74,9 @@
|
|||
.Ft int
|
||||
.Fn archive_write_open "struct archive *" "void *client_data" "archive_write_archive_callback *" "archive_open_archive_callback *" "archive_close_archive_callback *"
|
||||
.Ft int
|
||||
.Fn archive_write_open_file "struct archive *" "const char *filename"
|
||||
.Fn archive_write_open_fd "struct archive *" "int fd"
|
||||
.Ft int
|
||||
.Fn archive_write_open_tar "struct archive *" "const char *archive_name"
|
||||
.Fn archive_write_open_file "struct archive *" "const char *filename"
|
||||
.Ft int
|
||||
.Fn archive_write_header "struct archive *"
|
||||
.Ft int
|
||||
|
|
@ -117,7 +117,7 @@ is applied only after the compression.
|
|||
The uncompressed data is always unpadded.
|
||||
The default is to pad the last block to the full block size (note that
|
||||
.Fn archive_write_open_file
|
||||
affects this).
|
||||
will set this based on the file type).
|
||||
Unlike the other
|
||||
.Dq set
|
||||
functions, this function can be called after the archive is opened.
|
||||
|
|
@ -153,6 +153,16 @@ Freeze the settings, open the archive, and prepare for writing entries.
|
|||
This is the most generic form of this function, which accepts
|
||||
pointers to three callback functions which will be invoked by
|
||||
the library to write the constructed archive.
|
||||
Note: Internally, the callbacks are invoked by the compression layer.
|
||||
In order to support external compression programs, the compression
|
||||
is permitted to fork and invoke the callbacks from a separate process.
|
||||
In particular, clients should not assume that they can communicate
|
||||
between the callbacks and the mainline code using shared variables.
|
||||
Note that the standard gzip, bzip2, and "none" compression methods do not fork.
|
||||
.It Fn archive_write_open_fd
|
||||
A convenience form of
|
||||
.Fn archive_write_open
|
||||
that accepts a file descriptor.
|
||||
.It Fn archive_write_open_file
|
||||
A convenience form of
|
||||
.Fn archive_write_open
|
||||
|
|
@ -172,14 +182,6 @@ You can override this by manually invoking
|
|||
.Fn archive_write_set_bytes_in_last_block
|
||||
either before or after calling
|
||||
.Fn archive_write_open .
|
||||
.It Fn archive_write_open_tar
|
||||
A convenience form of
|
||||
.Fn archive_write_open
|
||||
that accepts an archive name in the same formats accepted by
|
||||
.Xr tar 1 .
|
||||
In particular, a
|
||||
.Pa -
|
||||
argument indicates that the output should be written to standard output.
|
||||
.It Fn archive_write_header
|
||||
Build and write a header using the data in the provided
|
||||
.Tn struct archive_entry
|
||||
|
|
|
|||
|
|
@ -170,6 +170,7 @@ archive_write_finish(struct archive *a)
|
|||
free((void *)(uintptr_t)(const void *)a->nulls);
|
||||
if (a->extract_mkdirpath.s != NULL)
|
||||
free(a->extract_mkdirpath.s);
|
||||
a->magic = 0;
|
||||
free(a);
|
||||
}
|
||||
|
||||
|
|
|
|||
121
lib/libarchive/archive_write_open_fd.c
Normal file
121
lib/libarchive/archive_write_open_fd.c
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/*-
|
||||
* Copyright (c) 2003-2004 Tim Kientzle
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer
|
||||
* in this position and unchanged.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "archive_platform.h"
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/stat.h>
|
||||
#ifdef HAVE_DMALLOC
|
||||
#include <dmalloc.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "archive.h"
|
||||
#include "archive_private.h"
|
||||
|
||||
struct write_fd_data {
|
||||
off_t offset;
|
||||
int fd;
|
||||
};
|
||||
|
||||
static int file_close(struct archive *, void *);
|
||||
static int file_open(struct archive *, void *);
|
||||
static ssize_t file_write(struct archive *, void *, void *buff, size_t);
|
||||
|
||||
int
|
||||
archive_write_open_fd(struct archive *a, int fd)
|
||||
{
|
||||
struct write_fd_data *mine;
|
||||
|
||||
mine = malloc(sizeof(*mine));
|
||||
if (mine == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
mine->fd = fd;
|
||||
return (archive_write_open(a, mine,
|
||||
file_open, file_write, file_close));
|
||||
}
|
||||
|
||||
static int
|
||||
file_open(struct archive *a, void *client_data)
|
||||
{
|
||||
struct write_fd_data *mine;
|
||||
struct stat st;
|
||||
|
||||
mine = client_data;
|
||||
|
||||
/*
|
||||
* If client hasn't explicitly set the last block handling,
|
||||
* then set it here: If the output is a block or character
|
||||
* device, pad the last block, otherwise leave it unpadded.
|
||||
*/
|
||||
if (mine->fd >= 0 && a->bytes_in_last_block < 0) {
|
||||
/* Last block will be fully padded. */
|
||||
fstat(mine->fd, &st);
|
||||
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode) ||
|
||||
S_ISFIFO(st.st_mode))
|
||||
archive_write_set_bytes_in_last_block(a, 0);
|
||||
else
|
||||
archive_write_set_bytes_in_last_block(a, 1);
|
||||
}
|
||||
|
||||
if (mine->fd == 1) {
|
||||
if (a->bytes_in_last_block < 0) /* Still default? */
|
||||
/* Last block will be fully padded. */
|
||||
archive_write_set_bytes_in_last_block(a, 0);
|
||||
}
|
||||
|
||||
if (mine->fd < 0) {
|
||||
archive_set_error(a, errno, "Failed to open");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
file_write(struct archive *a, void *client_data, void *buff, size_t length)
|
||||
{
|
||||
struct write_fd_data *mine;
|
||||
|
||||
(void)a; /* UNUSED */
|
||||
mine = client_data;
|
||||
return (write(mine->fd, buff, length));
|
||||
}
|
||||
|
||||
static int
|
||||
file_close(struct archive *a, void *client_data)
|
||||
{
|
||||
struct write_fd_data *mine = client_data;
|
||||
|
||||
(void)a; /* UNUSED */
|
||||
free(mine);
|
||||
return (ARCHIVE_OK);
|
||||
}
|
||||
|
|
@ -41,7 +41,6 @@ __FBSDID("$FreeBSD$");
|
|||
#include "archive_private.h"
|
||||
|
||||
struct write_file_data {
|
||||
off_t offset;
|
||||
int fd;
|
||||
char filename[1];
|
||||
};
|
||||
|
|
@ -52,24 +51,24 @@ static ssize_t file_write(struct archive *, void *, void *buff, size_t);
|
|||
|
||||
int
|
||||
archive_write_open_file(struct archive *a, const char *filename)
|
||||
{
|
||||
return (archive_write_open_file_position(a, filename, 0));
|
||||
}
|
||||
|
||||
int
|
||||
archive_write_open_file_position(struct archive *a, const char *filename,
|
||||
int64_t offset)
|
||||
{
|
||||
struct write_file_data *mine;
|
||||
|
||||
if (filename == NULL) {
|
||||
mine = malloc(sizeof(*mine));
|
||||
if (mine == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
mine->filename[0] = 0;
|
||||
} else {
|
||||
mine = malloc(sizeof(*mine) + strlen(filename));
|
||||
if (mine == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
strcpy(mine->filename, filename);
|
||||
}
|
||||
mine->offset = offset;
|
||||
mine->fd = -1;
|
||||
return (archive_write_open(a, mine,
|
||||
file_open, file_write, file_close));
|
||||
|
|
@ -83,10 +82,7 @@ file_open(struct archive *a, void *client_data)
|
|||
struct stat st;
|
||||
|
||||
mine = client_data;
|
||||
if (mine->offset == 0)
|
||||
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||
else
|
||||
flags = O_WRONLY;
|
||||
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||
|
||||
if (*mine->filename != 0) {
|
||||
mine->fd = open(mine->filename, flags, 0666);
|
||||
|
|
@ -115,12 +111,7 @@ file_open(struct archive *a, void *client_data)
|
|||
|
||||
if (mine->fd < 0) {
|
||||
archive_set_error(a, errno, "Failed to open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mine->offset > 0) {
|
||||
lseek(mine->fd, mine->offset, SEEK_SET);
|
||||
ftruncate(mine->fd, mine->offset);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ __FBSDID("$FreeBSD$");
|
|||
#include "archive_private.h"
|
||||
|
||||
struct write_file_data {
|
||||
off_t offset;
|
||||
int fd;
|
||||
char filename[1];
|
||||
};
|
||||
|
|
@ -52,24 +51,24 @@ static ssize_t file_write(struct archive *, void *, void *buff, size_t);
|
|||
|
||||
int
|
||||
archive_write_open_file(struct archive *a, const char *filename)
|
||||
{
|
||||
return (archive_write_open_file_position(a, filename, 0));
|
||||
}
|
||||
|
||||
int
|
||||
archive_write_open_file_position(struct archive *a, const char *filename,
|
||||
int64_t offset)
|
||||
{
|
||||
struct write_file_data *mine;
|
||||
|
||||
if (filename == NULL) {
|
||||
mine = malloc(sizeof(*mine));
|
||||
if (mine == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
mine->filename[0] = 0;
|
||||
} else {
|
||||
mine = malloc(sizeof(*mine) + strlen(filename));
|
||||
if (mine == NULL) {
|
||||
archive_set_error(a, ENOMEM, "No memory");
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
strcpy(mine->filename, filename);
|
||||
}
|
||||
mine->offset = offset;
|
||||
mine->fd = -1;
|
||||
return (archive_write_open(a, mine,
|
||||
file_open, file_write, file_close));
|
||||
|
|
@ -83,10 +82,7 @@ file_open(struct archive *a, void *client_data)
|
|||
struct stat st;
|
||||
|
||||
mine = client_data;
|
||||
if (mine->offset == 0)
|
||||
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||
else
|
||||
flags = O_WRONLY;
|
||||
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||
|
||||
if (*mine->filename != 0) {
|
||||
mine->fd = open(mine->filename, flags, 0666);
|
||||
|
|
@ -115,12 +111,7 @@ file_open(struct archive *a, void *client_data)
|
|||
|
||||
if (mine->fd < 0) {
|
||||
archive_set_error(a, errno, "Failed to open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (mine->offset > 0) {
|
||||
lseek(mine->fd, mine->offset, SEEK_SET);
|
||||
ftruncate(mine->fd, mine->offset);
|
||||
return (ARCHIVE_FATAL);
|
||||
}
|
||||
|
||||
return (ARCHIVE_OK);
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ add_pax_attr_w(struct archive_string *as, const char *key, const wchar_t *wval)
|
|||
p += 6;
|
||||
}
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
add_pax_attr(as, key, utf8_value);
|
||||
free(utf8_value);
|
||||
}
|
||||
|
|
@ -488,6 +488,16 @@ archive_write_pax_header(struct archive *a,
|
|||
if ((st_main->st_mtime < 0) || (st_main->st_mtime >= 0x7fffffff))
|
||||
need_extension = 1;
|
||||
|
||||
/* If there are non-trivial ACL entries, we need an extension. */
|
||||
if (archive_entry_acl_count(entry_original,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_ACCESS) > 0)
|
||||
need_extension = 1;
|
||||
|
||||
/* If there are non-trivial ACL entries, we need an extension. */
|
||||
if (archive_entry_acl_count(entry_original,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) > 0)
|
||||
need_extension = 1;
|
||||
|
||||
/*
|
||||
* The following items are handled differently in "pax
|
||||
* restricted" format. In particular, in "pax restricted"
|
||||
|
|
@ -520,13 +530,15 @@ archive_write_pax_header(struct archive *a,
|
|||
add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p);
|
||||
|
||||
/* I use star-compatible ACL attributes. */
|
||||
p = archive_entry_acl(entry_main);
|
||||
if (p != NULL && *p != '\0')
|
||||
add_pax_attr(&(pax->pax_header), "SCHILY.acl.access", p);
|
||||
p = archive_entry_acl_default(entry_main);
|
||||
if (p != NULL && *p != '\0')
|
||||
add_pax_attr(&(pax->pax_header), "SCHILY.acl.default",
|
||||
p);
|
||||
wp = __archive_entry_acl_text_w(entry_original,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_ACCESS);
|
||||
if (wp != NULL && *wp != L'\0')
|
||||
add_pax_attr_w(&(pax->pax_header), "SCHILY.acl.access", wp);
|
||||
wp = __archive_entry_acl_text_w(entry_original,
|
||||
ARCHIVE_ENTRY_ACL_TYPE_DEFAULT);
|
||||
if (wp != NULL && *wp != L'\0')
|
||||
add_pax_attr_w(&(pax->pax_header), "SCHILY.acl.default",
|
||||
wp);
|
||||
|
||||
/* Include star-compatible metadata info. */
|
||||
add_pax_attr_int(&(pax->pax_header), "SCHILY.dev",
|
||||
|
|
|
|||
Loading…
Reference in a new issue