#include "archive_platform.h"
__FBSDID("$FreeBSD: head/lib/libarchive/archive_entry.c 201096 2009-12-28 02:41:27Z kientzle $");
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#if MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#define HAVE_MAJOR
#elif MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#define HAVE_MAJOR
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_LINUX_FS_H
#include <linux/fs.h>
#endif
#ifdef HAVE_LINUX_EXT2_FS_H
#include <linux/ext2_fs.h>
#endif
#if defined(HAVE_EXT2FS_EXT2_FS_H) && !defined(__CYGWIN__)
#include <ext2fs/ext2_fs.h>
#endif
#include <stddef.h>
#include <stdio.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_WCHAR_H
#include <wchar.h>
#endif
#include "archive.h"
#include "archive_entry.h"
#include "archive_private.h"
#include "archive_entry_private.h"
#undef max
#define max(a, b) ((a)>(b)?(a):(b))
#if !defined(HAVE_MAJOR) && !defined(major)
#define major(x) ((int)(0x00ff & ((x) >> 8)))
#define minor(x) ((int)(0xffff00ff & (x)))
#define makedev(maj,min) ((0xff00 & ((maj)<<8)) | (0xffff00ff & (min)))
#endif
#ifdef __QNXNTO__
#include <sys/netmgr.h>
#define ae_makedev(maj, min) makedev(ND_LOCAL_NODE, (maj), (min))
#elif defined makedev
#define ae_makedev(maj, min) makedev((maj), (min))
#elif defined mkdev || ((defined _WIN32 || defined __WIN32__) && !defined(__CYGWIN__))
#define ae_makedev(maj, min) mkdev((maj), (min))
#else
#define ae_makedev(maj, min) makedev((maj), (min))
#endif
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 int aes_set_mbs(struct aes *, const char *mbs);
static int aes_copy_mbs(struct aes *, const char *mbs);
static int aes_copy_wcs(struct aes *, const wchar_t *wcs);
static int aes_copy_wcs_len(struct aes *, const wchar_t *wcs, size_t);
static char * ae_fflagstostr(unsigned long bitset, unsigned long bitclear);
static const wchar_t *ae_wcstofflags(const wchar_t *stringp,
unsigned long *setp, unsigned long *clrp);
static const char *ae_strtofflags(const char *stringp,
unsigned long *setp, unsigned long *clrp);
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 int isint_w(const wchar_t *start, const wchar_t *end, int *result);
static int ismode_w(const wchar_t *start, const wchar_t *end, int *result);
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);
static void
archive_entry_acl_add_entry_w_len(struct archive_entry *entry, int type,
int permset, int tag, int id, const wchar_t *name, size_t);
#ifndef HAVE_WCSCPY
static wchar_t * wcscpy(wchar_t *s1, const wchar_t *s2)
{
wchar_t *dest = s1;
while ((*s1 = *s2) != L'\0')
++s1, ++s2;
return dest;
}
#endif
#ifndef HAVE_WCSLEN
static size_t wcslen(const wchar_t *s)
{
const wchar_t *p = s;
while (*p != L'\0')
++p;
return p - s;
}
#endif
#ifndef HAVE_WMEMCMP
#define wmemcmp(a,b,i) memcmp((a), (b), (i) * sizeof(wchar_t))
#endif
#ifndef HAVE_WMEMCPY
#define wmemcpy(a,b,i) (wchar_t *)memcpy((a), (b), (i) * sizeof(wchar_t))
#endif
static void
aes_clean(struct aes *aes)
{
if (aes->aes_wcs) {
free((wchar_t *)(uintptr_t)aes->aes_wcs);
aes->aes_wcs = NULL;
}
archive_string_free(&(aes->aes_mbs));
archive_string_free(&(aes->aes_utf8));
aes->aes_set = 0;
}
static void
aes_copy(struct aes *dest, struct aes *src)
{
wchar_t *wp;
dest->aes_set = src->aes_set;
archive_string_copy(&(dest->aes_mbs), &(src->aes_mbs));
archive_string_copy(&(dest->aes_utf8), &(src->aes_utf8));
if (src->aes_wcs != NULL) {
wp = (wchar_t *)malloc((wcslen(src->aes_wcs) + 1)
* sizeof(wchar_t));
if (wp == NULL)
__archive_errx(1, "No memory for aes_copy()");
wcscpy(wp, src->aes_wcs);
dest->aes_wcs = wp;
}
}
static const char *
aes_get_utf8(struct aes *aes)
{
if (aes->aes_set & AES_SET_UTF8)
return (aes->aes_utf8.s);
if ((aes->aes_set & AES_SET_WCS)
&& archive_strappend_w_utf8(&(aes->aes_utf8), aes->aes_wcs) != NULL) {
aes->aes_set |= AES_SET_UTF8;
return (aes->aes_utf8.s);
}
return (NULL);
}
static const char *
aes_get_mbs(struct aes *aes)
{
if (aes->aes_set & AES_SET_MBS)
return (aes->aes_mbs.s);
if ((aes->aes_set & AES_SET_WCS)
&& archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) != NULL) {
aes->aes_set |= AES_SET_MBS;
return (aes->aes_mbs.s);
}
return (aes_get_utf8(aes));
}
static const wchar_t *
aes_get_wcs(struct aes *aes)
{
wchar_t *w;
size_t r;
if (aes->aes_set & AES_SET_WCS)
return (aes->aes_wcs);
if (aes->aes_set & AES_SET_MBS) {
size_t wcs_length = aes->aes_mbs.length;
w = (wchar_t *)malloc((wcs_length + 1) * sizeof(wchar_t));
if (w == NULL)
__archive_errx(1, "No memory for aes_get_wcs()");
r = mbstowcs(w, aes->aes_mbs.s, wcs_length);
if (r != (size_t)-1 && r != 0) {
w[r] = 0;
aes->aes_set |= AES_SET_WCS;
return (aes->aes_wcs = w);
}
free(w);
}
if (aes->aes_set & AES_SET_UTF8) {
aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8));
if (aes->aes_wcs != NULL)
aes->aes_set |= AES_SET_WCS;
return (aes->aes_wcs);
}
return (NULL);
}
static int
aes_set_mbs(struct aes *aes, const char *mbs)
{
return (aes_copy_mbs(aes, mbs));
}
static int
aes_copy_mbs(struct aes *aes, const char *mbs)
{
if (mbs == NULL) {
aes->aes_set = 0;
return (0);
}
aes->aes_set = AES_SET_MBS;
archive_strcpy(&(aes->aes_mbs), mbs);
archive_string_empty(&(aes->aes_utf8));
if (aes->aes_wcs) {
free((wchar_t *)(uintptr_t)aes->aes_wcs);
aes->aes_wcs = NULL;
}
return (0);
}
static int
aes_update_utf8(struct aes *aes, const char *utf8)
{
if (utf8 == NULL) {
aes->aes_set = 0;
return (1);
}
archive_strcpy(&(aes->aes_utf8), utf8);
archive_string_empty(&(aes->aes_mbs));
if (aes->aes_wcs) {
free((wchar_t *)(uintptr_t)aes->aes_wcs);
aes->aes_wcs = NULL;
}
aes->aes_set = AES_SET_UTF8;
aes->aes_wcs = __archive_string_utf8_w(&(aes->aes_utf8));
if (aes->aes_wcs == NULL)
return (0);
aes->aes_set = AES_SET_UTF8 | AES_SET_WCS;
if (archive_strappend_w_mbs(&(aes->aes_mbs), aes->aes_wcs) == NULL)
return (0);
aes->aes_set = AES_SET_UTF8 | AES_SET_WCS | AES_SET_MBS;
return (1);
}
static int
aes_copy_wcs(struct aes *aes, const wchar_t *wcs)
{
return aes_copy_wcs_len(aes, wcs, wcs == NULL ? 0 : wcslen(wcs));
}
static int
aes_copy_wcs_len(struct aes *aes, const wchar_t *wcs, size_t len)
{
wchar_t *w;
if (wcs == NULL) {
aes->aes_set = 0;
return (0);
}
aes->aes_set = AES_SET_WCS;
archive_string_empty(&(aes->aes_mbs));
archive_string_empty(&(aes->aes_utf8));
if (aes->aes_wcs) {
free((wchar_t *)(uintptr_t)aes->aes_wcs);
aes->aes_wcs = NULL;
}
w = (wchar_t *)malloc((len + 1) * sizeof(wchar_t));
if (w == NULL)
__archive_errx(1, "No memory for aes_copy_wcs()");
wmemcpy(w, wcs, len);
w[len] = L'\0';
aes->aes_wcs = w;
return (0);
}
struct archive_entry *
archive_entry_clear(struct archive_entry *entry)
{
if (entry == NULL)
return (NULL);
aes_clean(&entry->ae_fflags_text);
aes_clean(&entry->ae_gname);
aes_clean(&entry->ae_hardlink);
aes_clean(&entry->ae_pathname);
aes_clean(&entry->ae_sourcepath);
aes_clean(&entry->ae_symlink);
aes_clean(&entry->ae_uname);
archive_entry_acl_clear(entry);
archive_entry_xattr_clear(entry);
free(entry->stat);
memset(entry, 0, sizeof(*entry));
return entry;
}
struct archive_entry *
archive_entry_clone(struct archive_entry *entry)
{
struct archive_entry *entry2;
struct ae_acl *ap, *ap2;
struct ae_xattr *xp;
entry2 = (struct archive_entry *)malloc(sizeof(*entry2));
if (entry2 == NULL)
return (NULL);
memset(entry2, 0, sizeof(*entry2));
entry2->ae_stat = entry->ae_stat;
entry2->ae_fflags_set = entry->ae_fflags_set;
entry2->ae_fflags_clear = entry->ae_fflags_clear;
aes_copy(&entry2->ae_fflags_text, &entry->ae_fflags_text);
aes_copy(&entry2->ae_gname, &entry->ae_gname);
aes_copy(&entry2->ae_hardlink, &entry->ae_hardlink);
aes_copy(&entry2->ae_pathname, &entry->ae_pathname);
aes_copy(&entry2->ae_sourcepath, &entry->ae_sourcepath);
aes_copy(&entry2->ae_symlink, &entry->ae_symlink);
entry2->ae_set = entry->ae_set;
aes_copy(&entry2->ae_uname, &entry->ae_uname);
ap = entry->acl_head;
while (ap != NULL) {
ap2 = acl_new_entry(entry2,
ap->type, ap->permset, ap->tag, ap->id);
if (ap2 != NULL)
aes_copy(&ap2->name, &ap->name);
ap = ap->next;
}
xp = entry->xattr_head;
while (xp != NULL) {
archive_entry_xattr_add_entry(entry2,
xp->name, xp->value, xp->size);
xp = xp->next;
}
return (entry2);
}
void
archive_entry_free(struct archive_entry *entry)
{
archive_entry_clear(entry);
free(entry);
}
struct archive_entry *
archive_entry_new(void)
{
struct archive_entry *entry;
entry = (struct archive_entry *)malloc(sizeof(*entry));
if (entry == NULL)
return (NULL);
memset(entry, 0, sizeof(*entry));
return (entry);
}
time_t
archive_entry_atime(struct archive_entry *entry)
{
return (entry->ae_stat.aest_atime);
}
long
archive_entry_atime_nsec(struct archive_entry *entry)
{
return (entry->ae_stat.aest_atime_nsec);
}
int
archive_entry_atime_is_set(struct archive_entry *entry)
{
return (entry->ae_set & AE_SET_ATIME);
}
time_t
archive_entry_birthtime(struct archive_entry *entry)
{
return (entry->ae_stat.aest_birthtime);
}
long
archive_entry_birthtime_nsec(struct archive_entry *entry)
{
return (entry->ae_stat.aest_birthtime_nsec);
}
int
archive_entry_birthtime_is_set(struct archive_entry *entry)
{
return (entry->ae_set & AE_SET_BIRTHTIME);
}
time_t
archive_entry_ctime(struct archive_entry *entry)
{
return (entry->ae_stat.aest_ctime);
}
int
archive_entry_ctime_is_set(struct archive_entry *entry)
{
return (entry->ae_set & AE_SET_CTIME);
}
long
archive_entry_ctime_nsec(struct archive_entry *entry)
{
return (entry->ae_stat.aest_ctime_nsec);
}
dev_t
archive_entry_dev(struct archive_entry *entry)
{
if (entry->ae_stat.aest_dev_is_broken_down)
return ae_makedev(entry->ae_stat.aest_devmajor,
entry->ae_stat.aest_devminor);
else
return (entry->ae_stat.aest_dev);
}
dev_t
archive_entry_devmajor(struct archive_entry *entry)
{
if (entry->ae_stat.aest_dev_is_broken_down)
return (entry->ae_stat.aest_devmajor);
else
return major(entry->ae_stat.aest_dev);
}
dev_t
archive_entry_devminor(struct archive_entry *entry)
{
if (entry->ae_stat.aest_dev_is_broken_down)
return (entry->ae_stat.aest_devminor);
else
return minor(entry->ae_stat.aest_dev);
}
mode_t
archive_entry_filetype(struct archive_entry *entry)
{
return (AE_IFMT & entry->ae_stat.aest_mode);
}
void
archive_entry_fflags(struct archive_entry *entry,
unsigned long *set, unsigned long *clear)
{
*set = entry->ae_fflags_set;
*clear = entry->ae_fflags_clear;
}
const char *
archive_entry_fflags_text(struct archive_entry *entry)
{
const char *f;
char *p;
f = aes_get_mbs(&entry->ae_fflags_text);
if (f != NULL)
return (f);
if (entry->ae_fflags_set == 0 && entry->ae_fflags_clear == 0)
return (NULL);
p = ae_fflagstostr(entry->ae_fflags_set, entry->ae_fflags_clear);
if (p == NULL)
return (NULL);
aes_copy_mbs(&entry->ae_fflags_text, p);
free(p);
f = aes_get_mbs(&entry->ae_fflags_text);
return (f);
}
gid_t
archive_entry_gid(struct archive_entry *entry)
{
return (entry->ae_stat.aest_gid);
}
const char *
archive_entry_gname(struct archive_entry *entry)
{
return (aes_get_mbs(&entry->ae_gname));
}
const wchar_t *
archive_entry_gname_w(struct archive_entry *entry)
{
return (aes_get_wcs(&entry->ae_gname));
}
const char *
archive_entry_hardlink(struct archive_entry *entry)
{
if (entry->ae_set & AE_SET_HARDLINK)
return (aes_get_mbs(&entry->ae_hardlink));
return (NULL);
}
const wchar_t *
archive_entry_hardlink_w(struct archive_entry *entry)
{
if (entry->ae_set & AE_SET_HARDLINK)
return (aes_get_wcs(&entry->ae_hardlink));
return (NULL);
}
ino_t
archive_entry_ino(struct archive_entry *entry)
{
return (entry->ae_stat.aest_ino);
}
int64_t
archive_entry_ino64(struct archive_entry *entry)
{
return (entry->ae_stat.aest_ino);
}
mode_t
archive_entry_mode(struct archive_entry *entry)
{
return (entry->ae_stat.aest_mode);
}
time_t
archive_entry_mtime(struct archive_entry *entry)
{
return (entry->ae_stat.aest_mtime);
}
long
archive_entry_mtime_nsec(struct archive_entry *entry)
{
return (entry->ae_stat.aest_mtime_nsec);
}
int
archive_entry_mtime_is_set(struct archive_entry *entry)
{
return (entry->ae_set & AE_SET_MTIME);
}
unsigned int
archive_entry_nlink(struct archive_entry *entry)
{
return (entry->ae_stat.aest_nlink);
}
const char *
archive_entry_pathname(struct archive_entry *entry)
{
return (aes_get_mbs(&entry->ae_pathname));
}
const wchar_t *
archive_entry_pathname_w(struct archive_entry *entry)
{
return (aes_get_wcs(&entry->ae_pathname));
}
dev_t
archive_entry_rdev(struct archive_entry *entry)
{
if (entry->ae_stat.aest_rdev_is_broken_down)
return ae_makedev(entry->ae_stat.aest_rdevmajor,
entry->ae_stat.aest_rdevminor);
else
return (entry->ae_stat.aest_rdev);
}
dev_t
archive_entry_rdevmajor(struct archive_entry *entry)
{
if (entry->ae_stat.aest_rdev_is_broken_down)
return (entry->ae_stat.aest_rdevmajor);
else
return major(entry->ae_stat.aest_rdev);
}
dev_t
archive_entry_rdevminor(struct archive_entry *entry)
{
if (entry->ae_stat.aest_rdev_is_broken_down)
return (entry->ae_stat.aest_rdevminor);
else
return minor(entry->ae_stat.aest_rdev);
}
int64_t
archive_entry_size(struct archive_entry *entry)
{
return (entry->ae_stat.aest_size);
}
int
archive_entry_size_is_set(struct archive_entry *entry)
{
return (entry->ae_set & AE_SET_SIZE);
}
const char *
archive_entry_sourcepath(struct archive_entry *entry)
{
return (aes_get_mbs(&entry->ae_sourcepath));
}
const char *
archive_entry_symlink(struct archive_entry *entry)
{
if (entry->ae_set & AE_SET_SYMLINK)
return (aes_get_mbs(&entry->ae_symlink));
return (NULL);
}
const wchar_t *
archive_entry_symlink_w(struct archive_entry *entry)
{
if (entry->ae_set & AE_SET_SYMLINK)
return (aes_get_wcs(&entry->ae_symlink));
return (NULL);
}
uid_t
archive_entry_uid(struct archive_entry *entry)
{
return (entry->ae_stat.aest_uid);
}
const char *
archive_entry_uname(struct archive_entry *entry)
{
return (aes_get_mbs(&entry->ae_uname));
}
const wchar_t *
archive_entry_uname_w(struct archive_entry *entry)
{
return (aes_get_wcs(&entry->ae_uname));
}
void
archive_entry_set_filetype(struct archive_entry *entry, unsigned int type)
{
entry->stat_valid = 0;
entry->ae_stat.aest_mode &= ~AE_IFMT;
entry->ae_stat.aest_mode |= AE_IFMT & type;
}
void
archive_entry_set_fflags(struct archive_entry *entry,
unsigned long set, unsigned long clear)
{
aes_clean(&entry->ae_fflags_text);
entry->ae_fflags_set = set;
entry->ae_fflags_clear = clear;
}
const char *
archive_entry_copy_fflags_text(struct archive_entry *entry,
const char *flags)
{
aes_copy_mbs(&entry->ae_fflags_text, flags);
return (ae_strtofflags(flags,
&entry->ae_fflags_set, &entry->ae_fflags_clear));
}
const wchar_t *
archive_entry_copy_fflags_text_w(struct archive_entry *entry,
const wchar_t *flags)
{
aes_copy_wcs(&entry->ae_fflags_text, flags);
return (ae_wcstofflags(flags,
&entry->ae_fflags_set, &entry->ae_fflags_clear));
}
void
archive_entry_set_gid(struct archive_entry *entry, gid_t g)
{
entry->stat_valid = 0;
entry->ae_stat.aest_gid = g;
}
void
archive_entry_set_gname(struct archive_entry *entry, const char *name)
{
aes_set_mbs(&entry->ae_gname, name);
}
void
archive_entry_copy_gname(struct archive_entry *entry, const char *name)
{
aes_copy_mbs(&entry->ae_gname, name);
}
void
archive_entry_copy_gname_w(struct archive_entry *entry, const wchar_t *name)
{
aes_copy_wcs(&entry->ae_gname, name);
}
int
archive_entry_update_gname_utf8(struct archive_entry *entry, const char *name)
{
return (aes_update_utf8(&entry->ae_gname, name));
}
void
archive_entry_set_ino(struct archive_entry *entry, unsigned long ino)
{
entry->stat_valid = 0;
entry->ae_stat.aest_ino = ino;
}
void
archive_entry_set_ino64(struct archive_entry *entry, int64_t ino)
{
entry->stat_valid = 0;
entry->ae_stat.aest_ino = ino;
}
void
archive_entry_set_hardlink(struct archive_entry *entry, const char *target)
{
aes_set_mbs(&entry->ae_hardlink, target);
if (target != NULL)
entry->ae_set |= AE_SET_HARDLINK;
else
entry->ae_set &= ~AE_SET_HARDLINK;
}
void
archive_entry_copy_hardlink(struct archive_entry *entry, const char *target)
{
aes_copy_mbs(&entry->ae_hardlink, target);
if (target != NULL)
entry->ae_set |= AE_SET_HARDLINK;
else
entry->ae_set &= ~AE_SET_HARDLINK;
}
void
archive_entry_copy_hardlink_w(struct archive_entry *entry, const wchar_t *target)
{
aes_copy_wcs(&entry->ae_hardlink, target);
if (target != NULL)
entry->ae_set |= AE_SET_HARDLINK;
else
entry->ae_set &= ~AE_SET_HARDLINK;
}
int
archive_entry_update_hardlink_utf8(struct archive_entry *entry, const char *target)
{
if (target != NULL)
entry->ae_set |= AE_SET_HARDLINK;
else
entry->ae_set &= ~AE_SET_HARDLINK;
return (aes_update_utf8(&entry->ae_hardlink, target));
}
void
archive_entry_set_atime(struct archive_entry *entry, time_t t, long ns)
{
entry->stat_valid = 0;
entry->ae_set |= AE_SET_ATIME;
entry->ae_stat.aest_atime = t;
entry->ae_stat.aest_atime_nsec = ns;
}
void
archive_entry_unset_atime(struct archive_entry *entry)
{
archive_entry_set_atime(entry, 0, 0);
entry->ae_set &= ~AE_SET_ATIME;
}
void
archive_entry_set_birthtime(struct archive_entry *entry, time_t m, long ns)
{
entry->stat_valid = 0;
entry->ae_set |= AE_SET_BIRTHTIME;
entry->ae_stat.aest_birthtime = m;
entry->ae_stat.aest_birthtime_nsec = ns;
}
void
archive_entry_unset_birthtime(struct archive_entry *entry)
{
archive_entry_set_birthtime(entry, 0, 0);
entry->ae_set &= ~AE_SET_BIRTHTIME;
}
void
archive_entry_set_ctime(struct archive_entry *entry, time_t t, long ns)
{
entry->stat_valid = 0;
entry->ae_set |= AE_SET_CTIME;
entry->ae_stat.aest_ctime = t;
entry->ae_stat.aest_ctime_nsec = ns;
}
void
archive_entry_unset_ctime(struct archive_entry *entry)
{
archive_entry_set_ctime(entry, 0, 0);
entry->ae_set &= ~AE_SET_CTIME;
}
void
archive_entry_set_dev(struct archive_entry *entry, dev_t d)
{
entry->stat_valid = 0;
entry->ae_stat.aest_dev_is_broken_down = 0;
entry->ae_stat.aest_dev = d;
}
void
archive_entry_set_devmajor(struct archive_entry *entry, dev_t m)
{
entry->stat_valid = 0;
entry->ae_stat.aest_dev_is_broken_down = 1;
entry->ae_stat.aest_devmajor = m;
}
void
archive_entry_set_devminor(struct archive_entry *entry, dev_t m)
{
entry->stat_valid = 0;
entry->ae_stat.aest_dev_is_broken_down = 1;
entry->ae_stat.aest_devminor = m;
}
void
archive_entry_set_link(struct archive_entry *entry, const char *target)
{
if (entry->ae_set & AE_SET_SYMLINK)
aes_set_mbs(&entry->ae_symlink, target);
else
aes_set_mbs(&entry->ae_hardlink, target);
}
void
archive_entry_copy_link(struct archive_entry *entry, const char *target)
{
if (entry->ae_set & AE_SET_SYMLINK)
aes_copy_mbs(&entry->ae_symlink, target);
else
aes_copy_mbs(&entry->ae_hardlink, target);
}
void
archive_entry_copy_link_w(struct archive_entry *entry, const wchar_t *target)
{
if (entry->ae_set & AE_SET_SYMLINK)
aes_copy_wcs(&entry->ae_symlink, target);
else
aes_copy_wcs(&entry->ae_hardlink, target);
}
int
archive_entry_update_link_utf8(struct archive_entry *entry, const char *target)
{
if (entry->ae_set & AE_SET_SYMLINK)
return (aes_update_utf8(&entry->ae_symlink, target));
else
return (aes_update_utf8(&entry->ae_hardlink, target));
}
void
archive_entry_set_mode(struct archive_entry *entry, mode_t m)
{
entry->stat_valid = 0;
entry->ae_stat.aest_mode = m;
}
void
archive_entry_set_mtime(struct archive_entry *entry, time_t m, long ns)
{
entry->stat_valid = 0;
entry->ae_set |= AE_SET_MTIME;
entry->ae_stat.aest_mtime = m;
entry->ae_stat.aest_mtime_nsec = ns;
}
void
archive_entry_unset_mtime(struct archive_entry *entry)
{
archive_entry_set_mtime(entry, 0, 0);
entry->ae_set &= ~AE_SET_MTIME;
}
void
archive_entry_set_nlink(struct archive_entry *entry, unsigned int nlink)
{
entry->stat_valid = 0;
entry->ae_stat.aest_nlink = nlink;
}
void
archive_entry_set_pathname(struct archive_entry *entry, const char *name)
{
aes_set_mbs(&entry->ae_pathname, name);
}
void
archive_entry_copy_pathname(struct archive_entry *entry, const char *name)
{
aes_copy_mbs(&entry->ae_pathname, name);
}
void
archive_entry_copy_pathname_w(struct archive_entry *entry, const wchar_t *name)
{
aes_copy_wcs(&entry->ae_pathname, name);
}
int
archive_entry_update_pathname_utf8(struct archive_entry *entry, const char *name)
{
return (aes_update_utf8(&entry->ae_pathname, name));
}
void
archive_entry_set_perm(struct archive_entry *entry, mode_t p)
{
entry->stat_valid = 0;
entry->ae_stat.aest_mode &= AE_IFMT;
entry->ae_stat.aest_mode |= ~AE_IFMT & p;
}
void
archive_entry_set_rdev(struct archive_entry *entry, dev_t m)
{
entry->stat_valid = 0;
entry->ae_stat.aest_rdev = m;
entry->ae_stat.aest_rdev_is_broken_down = 0;
}
void
archive_entry_set_rdevmajor(struct archive_entry *entry, dev_t m)
{
entry->stat_valid = 0;
entry->ae_stat.aest_rdev_is_broken_down = 1;
entry->ae_stat.aest_rdevmajor = m;
}
void
archive_entry_set_rdevminor(struct archive_entry *entry, dev_t m)
{
entry->stat_valid = 0;
entry->ae_stat.aest_rdev_is_broken_down = 1;
entry->ae_stat.aest_rdevminor = m;
}
void
archive_entry_set_size(struct archive_entry *entry, int64_t s)
{
entry->stat_valid = 0;
entry->ae_stat.aest_size = s;
entry->ae_set |= AE_SET_SIZE;
}
void
archive_entry_unset_size(struct archive_entry *entry)
{
archive_entry_set_size(entry, 0);
entry->ae_set &= ~AE_SET_SIZE;
}
void
archive_entry_copy_sourcepath(struct archive_entry *entry, const char *path)
{
aes_set_mbs(&entry->ae_sourcepath, path);
}
void
archive_entry_set_symlink(struct archive_entry *entry, const char *linkname)
{
aes_set_mbs(&entry->ae_symlink, linkname);
if (linkname != NULL)
entry->ae_set |= AE_SET_SYMLINK;
else
entry->ae_set &= ~AE_SET_SYMLINK;
}
void
archive_entry_copy_symlink(struct archive_entry *entry, const char *linkname)
{
aes_copy_mbs(&entry->ae_symlink, linkname);
if (linkname != NULL)
entry->ae_set |= AE_SET_SYMLINK;
else
entry->ae_set &= ~AE_SET_SYMLINK;
}
void
archive_entry_copy_symlink_w(struct archive_entry *entry, const wchar_t *linkname)
{
aes_copy_wcs(&entry->ae_symlink, linkname);
if (linkname != NULL)
entry->ae_set |= AE_SET_SYMLINK;
else
entry->ae_set &= ~AE_SET_SYMLINK;
}
int
archive_entry_update_symlink_utf8(struct archive_entry *entry, const char *linkname)
{
if (linkname != NULL)
entry->ae_set |= AE_SET_SYMLINK;
else
entry->ae_set &= ~AE_SET_SYMLINK;
return (aes_update_utf8(&entry->ae_symlink, linkname));
}
void
archive_entry_set_uid(struct archive_entry *entry, uid_t u)
{
entry->stat_valid = 0;
entry->ae_stat.aest_uid = u;
}
void
archive_entry_set_uname(struct archive_entry *entry, const char *name)
{
aes_set_mbs(&entry->ae_uname, name);
}
void
archive_entry_copy_uname(struct archive_entry *entry, const char *name)
{
aes_copy_mbs(&entry->ae_uname, name);
}
void
archive_entry_copy_uname_w(struct archive_entry *entry, const wchar_t *name)
{
aes_copy_wcs(&entry->ae_uname, name);
}
int
archive_entry_update_uname_utf8(struct archive_entry *entry, const char *name)
{
return (aes_update_utf8(&entry->ae_uname, name));
}
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;
}
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) {
return;
}
if (name != NULL && *name != '\0')
aes_copy_mbs(&ap->name, name);
else
aes_clean(&ap->name);
}
void
archive_entry_acl_add_entry_w(struct archive_entry *entry,
int type, int permset, int tag, int id, const wchar_t *name)
{
archive_entry_acl_add_entry_w_len(entry, type, permset, tag, id, name, wcslen(name));
}
static void
archive_entry_acl_add_entry_w_len(struct archive_entry *entry,
int type, int permset, int tag, int id, const wchar_t *name, size_t len)
{
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) {
return;
}
if (name != NULL && *name != L'\0' && len > 0)
aes_copy_wcs_len(&ap->name, name, len);
else
aes_clean(&ap->name);
}
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.aest_mode &= ~0700;
entry->ae_stat.aest_mode |= (permset & 7) << 6;
return (0);
case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
entry->ae_stat.aest_mode &= ~0070;
entry->ae_stat.aest_mode |= (permset & 7) << 3;
return (0);
case ARCHIVE_ENTRY_ACL_OTHER:
entry->ae_stat.aest_mode &= ~0007;
entry->ae_stat.aest_mode |= permset & 7;
return (0);
}
}
return (1);
}
static struct ae_acl *
acl_new_entry(struct archive_entry *entry,
int type, int permset, int tag, int id)
{
struct ae_acl *ap, *aq;
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;
}
ap = entry->acl_head;
aq = NULL;
while (ap != NULL) {
if (ap->type == type && ap->tag == tag && ap->id == id) {
ap->permset = permset;
return (ap);
}
aq = ap;
ap = ap->next;
}
ap = (struct ae_acl *)malloc(sizeof(*ap));
if (ap == NULL)
return (NULL);
memset(ap, 0, sizeof(*ap));
if (aq == NULL)
entry->acl_head = ap;
else
aq->next = ap;
ap->type = type;
ap->tag = tag;
ap->id = id;
ap->permset = permset;
return (ap);
}
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);
}
int
archive_entry_acl_reset(struct archive_entry *entry, int want_type)
{
int count, cutoff;
count = archive_entry_acl_count(entry, want_type);
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 = entry->acl_head;
return (count);
}
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;
if (entry->acl_state == 0)
return (ARCHIVE_WARN);
if ((want_type & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
switch (entry->acl_state) {
case ARCHIVE_ENTRY_ACL_USER_OBJ:
*permset = (entry->ae_stat.aest_mode >> 6) & 7;
*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
*tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
entry->acl_state = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
return (ARCHIVE_OK);
case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
*permset = (entry->ae_stat.aest_mode >> 3) & 7;
*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
*tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
entry->acl_state = ARCHIVE_ENTRY_ACL_OTHER;
return (ARCHIVE_OK);
case ARCHIVE_ENTRY_ACL_OTHER:
*permset = entry->ae_stat.aest_mode & 7;
*type = ARCHIVE_ENTRY_ACL_TYPE_ACCESS;
*tag = ARCHIVE_ENTRY_ACL_OTHER;
entry->acl_state = -1;
entry->acl_p = entry->acl_head;
return (ARCHIVE_OK);
default:
break;
}
}
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;
*type = 0;
*permset = 0;
*tag = 0;
*id = -1;
*name = NULL;
return (ARCHIVE_EOF);
}
*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);
}
const wchar_t *
archive_entry_acl_text_w(struct archive_entry *entry, int flags)
{
int count;
size_t length;
const wchar_t *wname;
const wchar_t *prefix;
wchar_t separator;
struct ae_acl *ap;
int id;
wchar_t *wp;
if (entry->acl_text_w != NULL) {
free (entry->acl_text_w);
entry->acl_text_w = NULL;
}
separator = L',';
count = 0;
length = 0;
ap = entry->acl_head;
while (ap != NULL) {
if ((ap->type & flags) != 0) {
count++;
if ((flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT) &&
(ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT))
length += 8;
length += 5;
length += 1;
wname = aes_get_wcs(&ap->name);
if (wname != NULL)
length += wcslen(wname);
else
length += sizeof(uid_t) * 3 + 1;
length ++;
length += 3;
length += 1;
length += max(sizeof(uid_t), sizeof(gid_t)) * 3 + 1;
length ++;
}
ap = ap->next;
}
if (count > 0 && ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0)) {
length += 10;
length += 11;
length += 11;
}
if (count == 0)
return (NULL);
wp = entry->acl_text_w = (wchar_t *)malloc(length * sizeof(wchar_t));
if (wp == NULL)
__archive_errx(1, "No memory to generate the text version of the ACL");
count = 0;
if ((flags & ARCHIVE_ENTRY_ACL_TYPE_ACCESS) != 0) {
append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_USER_OBJ, NULL,
entry->ae_stat.aest_mode & 0700, -1);
*wp++ = ',';
append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_GROUP_OBJ, NULL,
entry->ae_stat.aest_mode & 0070, -1);
*wp++ = ',';
append_entry_w(&wp, NULL, ARCHIVE_ENTRY_ACL_OTHER, NULL,
entry->ae_stat.aest_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++ = separator;
if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
id = ap->id;
else
id = -1;
append_entry_w(&wp, NULL, ap->tag, wname,
ap->permset, id);
count++;
}
ap = ap->next;
}
}
if ((flags & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
if (flags & ARCHIVE_ENTRY_ACL_STYLE_MARK_DEFAULT)
prefix = L"default:";
else
prefix = NULL;
ap = entry->acl_head;
count = 0;
while (ap != NULL) {
if ((ap->type & ARCHIVE_ENTRY_ACL_TYPE_DEFAULT) != 0) {
wname = aes_get_wcs(&ap->name);
if (count > 0)
*wp++ = separator;
if (flags & ARCHIVE_ENTRY_ACL_STYLE_EXTRA_ID)
id = ap->id;
else
id = -1;
append_entry_w(&wp, prefix, ap->tag,
wname, ap->permset, id);
count ++;
}
ap = ap->next;
}
}
return (entry->acl_text_w);
}
static void
append_id_w(wchar_t **wp, int id)
{
if (id < 0)
id = 0;
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;
case ARCHIVE_ENTRY_ACL_USER:
wcscpy(*wp, L"user");
break;
case ARCHIVE_ENTRY_ACL_GROUP_OBJ:
wname = NULL;
id = -1;
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);
} else if (tag == ARCHIVE_ENTRY_ACL_USER
|| tag == ARCHIVE_ENTRY_ACL_GROUP) {
append_id_w(wp, id);
id = -1;
}
*(*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';
}
int
__archive_entry_acl_parse_w(struct archive_entry *entry,
const wchar_t *text, int default_type)
{
struct {
const wchar_t *start;
const wchar_t *end;
} field[4], name;
int fields, n;
int type, tag, permset, id;
wchar_t sep;
while (text != NULL && *text != L'\0') {
fields = 0;
do {
const wchar_t *start, *end;
next_field_w(&text, &start, &end, &sep);
if (fields < 4) {
field[fields].start = start;
field[fields].end = end;
}
++fields;
} while (sep == L':');
for (n = fields; n < 4; ++n)
field[n].start = field[n].end = NULL;
id = -1;
isint_w(field[1].start, field[1].end, &id);
if (id == -1 && fields > 3)
isint_w(field[3].start, field[3].end, &id);
if (field[0].end - field[0].start > 7
&& wmemcmp(field[0].start, L"default", 7) == 0) {
type = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT;
field[0].start += 7;
} else
type = default_type;
name.start = name.end = NULL;
if (prefix_w(field[0].start, field[0].end, L"user")) {
if (!ismode_w(field[2].start, field[2].end, &permset))
return (ARCHIVE_WARN);
if (id != -1 || field[1].start < field[1].end) {
tag = ARCHIVE_ENTRY_ACL_USER;
name = field[1];
} else
tag = ARCHIVE_ENTRY_ACL_USER_OBJ;
} else if (prefix_w(field[0].start, field[0].end, L"group")) {
if (!ismode_w(field[2].start, field[2].end, &permset))
return (ARCHIVE_WARN);
if (id != -1 || field[1].start < field[1].end) {
tag = ARCHIVE_ENTRY_ACL_GROUP;
name = field[1];
} else
tag = ARCHIVE_ENTRY_ACL_GROUP_OBJ;
} else if (prefix_w(field[0].start, field[0].end, L"other")) {
if (fields == 2
&& field[1].start < field[1].end
&& ismode_w(field[1].start, field[1].end, &permset)) {
} else if (fields == 3
&& field[1].start == field[1].end
&& field[2].start < field[2].end
&& ismode_w(field[2].start, field[2].end, &permset)) {
} else
return (ARCHIVE_WARN);
tag = ARCHIVE_ENTRY_ACL_OTHER;
} else if (prefix_w(field[0].start, field[0].end, L"mask")) {
if (fields == 2
&& field[1].start < field[1].end
&& ismode_w(field[1].start, field[1].end, &permset)) {
} else if (fields == 3
&& field[1].start == field[1].end
&& field[2].start < field[2].end
&& ismode_w(field[2].start, field[2].end, &permset)) {
} else
return (ARCHIVE_WARN);
tag = ARCHIVE_ENTRY_ACL_MASK;
} else
return (ARCHIVE_WARN);
archive_entry_acl_add_entry_w_len(entry, type, permset,
tag, id, name.start, name.end - name.start);
}
return (ARCHIVE_OK);
}
static int
isint_w(const wchar_t *start, const wchar_t *end, int *result)
{
int n = 0;
if (start >= end)
return (0);
while (start < end) {
if (*start < '0' || *start > '9')
return (0);
if (n > (INT_MAX / 10))
n = INT_MAX;
else {
n *= 10;
n += *start - '0';
}
start++;
}
*result = n;
return (1);
}
static int
ismode_w(const wchar_t *start, const wchar_t *end, int *permset)
{
const wchar_t *p;
if (start >= end)
return (0);
p = start;
*permset = 0;
while (p < end) {
switch (*p++) {
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:
return (0);
}
}
return (1);
}
static void
next_field_w(const wchar_t **wp, const wchar_t **start,
const wchar_t **end, wchar_t *sep)
{
while (**wp == L' ' || **wp == L'\t' || **wp == L'\n') {
(*wp)++;
}
*start = *wp;
while (**wp != L'\0' && **wp != L',' && **wp != L':' &&
**wp != L'\n') {
(*wp)++;
}
*sep = **wp;
*end = *wp - 1;
while (**end == L' ' || **end == L'\t' || **end == L'\n') {
(*end)--;
}
(*end)++;
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);
}
static struct flag {
const char *name;
const wchar_t *wname;
unsigned long set;
unsigned long clear;
} flags[] = {
#ifdef SF_APPEND
{ "nosappnd", L"nosappnd", SF_APPEND, 0 },
{ "nosappend", L"nosappend", SF_APPEND, 0 },
#endif
#ifdef EXT2_APPEND_FL
{ "nosappnd", L"nosappnd", EXT2_APPEND_FL, 0 },
{ "nosappend", L"nosappend", EXT2_APPEND_FL, 0 },
#endif
#ifdef SF_ARCHIVED
{ "noarch", L"noarch", SF_ARCHIVED, 0 },
{ "noarchived", L"noarchived", SF_ARCHIVED, 0 },
#endif
#ifdef SF_IMMUTABLE
{ "noschg", L"noschg", SF_IMMUTABLE, 0 },
{ "noschange", L"noschange", SF_IMMUTABLE, 0 },
{ "nosimmutable", L"nosimmutable", SF_IMMUTABLE, 0 },
#endif
#ifdef EXT2_IMMUTABLE_FL
{ "noschg", L"noschg", EXT2_IMMUTABLE_FL, 0 },
{ "noschange", L"noschange", EXT2_IMMUTABLE_FL, 0 },
{ "nosimmutable", L"nosimmutable", EXT2_IMMUTABLE_FL, 0 },
#endif
#ifdef SF_NOUNLINK
{ "nosunlnk", L"nosunlnk", SF_NOUNLINK, 0 },
{ "nosunlink", L"nosunlink", SF_NOUNLINK, 0 },
#endif
#ifdef SF_SNAPSHOT
{ "nosnapshot", L"nosnapshot", SF_SNAPSHOT, 0 },
#endif
#ifdef UF_APPEND
{ "nouappnd", L"nouappnd", UF_APPEND, 0 },
{ "nouappend", L"nouappend", UF_APPEND, 0 },
#endif
#ifdef UF_IMMUTABLE
{ "nouchg", L"nouchg", UF_IMMUTABLE, 0 },
{ "nouchange", L"nouchange", UF_IMMUTABLE, 0 },
{ "nouimmutable", L"nouimmutable", UF_IMMUTABLE, 0 },
#endif
#ifdef UF_NODUMP
{ "nodump", L"nodump", 0, UF_NODUMP},
#endif
#ifdef EXT2_NODUMP_FL
{ "nodump", L"nodump", 0, EXT2_NODUMP_FL},
#endif
#ifdef UF_OPAQUE
{ "noopaque", L"noopaque", UF_OPAQUE, 0 },
#endif
#ifdef UF_NOUNLINK
{ "nouunlnk", L"nouunlnk", UF_NOUNLINK, 0 },
{ "nouunlink", L"nouunlink", UF_NOUNLINK, 0 },
#endif
#ifdef EXT2_UNRM_FL
{ "nouunlink", L"nouunlink", EXT2_UNRM_FL, 0},
#endif
#ifdef EXT2_BTREE_FL
{ "nobtree", L"nobtree", EXT2_BTREE_FL, 0 },
#endif
#ifdef EXT2_ECOMPR_FL
{ "nocomperr", L"nocomperr", EXT2_ECOMPR_FL, 0 },
#endif
#ifdef EXT2_COMPR_FL
{ "nocompress", L"nocompress", EXT2_COMPR_FL, 0 },
#endif
#ifdef EXT2_NOATIME_FL
{ "noatime", L"noatime", 0, EXT2_NOATIME_FL},
#endif
#ifdef EXT2_DIRTY_FL
{ "nocompdirty",L"nocompdirty", EXT2_DIRTY_FL, 0},
#endif
#ifdef EXT2_COMPRBLK_FL
#ifdef EXT2_NOCOMPR_FL
{ "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, EXT2_NOCOMPR_FL},
#else
{ "nocomprblk", L"nocomprblk", EXT2_COMPRBLK_FL, 0},
#endif
#endif
#ifdef EXT2_DIRSYNC_FL
{ "nodirsync", L"nodirsync", EXT2_DIRSYNC_FL, 0},
#endif
#ifdef EXT2_INDEX_FL
{ "nohashidx", L"nohashidx", EXT2_INDEX_FL, 0},
#endif
#ifdef EXT2_IMAGIC_FL
{ "noimagic", L"noimagic", EXT2_IMAGIC_FL, 0},
#endif
#ifdef EXT3_JOURNAL_DATA_FL
{ "nojournal", L"nojournal", EXT3_JOURNAL_DATA_FL, 0},
#endif
#ifdef EXT2_SECRM_FL
{ "nosecuredeletion",L"nosecuredeletion",EXT2_SECRM_FL, 0},
#endif
#ifdef EXT2_SYNC_FL
{ "nosync", L"nosync", EXT2_SYNC_FL, 0},
#endif
#ifdef EXT2_NOTAIL_FL
{ "notail", L"notail", 0, EXT2_NOTAIL_FL},
#endif
#ifdef EXT2_TOPDIR_FL
{ "notopdir", L"notopdir", EXT2_TOPDIR_FL, 0},
#endif
#ifdef EXT2_RESERVED_FL
{ "noreserved", L"noreserved", EXT2_RESERVED_FL, 0},
#endif
{ NULL, NULL, 0, 0 }
};
static char *
ae_fflagstostr(unsigned long bitset, unsigned long bitclear)
{
char *string, *dp;
const char *sp;
unsigned long bits;
struct flag *flag;
size_t length;
bits = bitset | bitclear;
length = 0;
for (flag = flags; flag->name != NULL; flag++)
if (bits & (flag->set | flag->clear)) {
length += strlen(flag->name) + 1;
bits &= ~(flag->set | flag->clear);
}
if (length == 0)
return (NULL);
string = (char *)malloc(length);
if (string == NULL)
return (NULL);
dp = string;
for (flag = flags; flag->name != NULL; flag++) {
if (bitset & flag->set || bitclear & flag->clear) {
sp = flag->name + 2;
} else if (bitset & flag->clear || bitclear & flag->set) {
sp = flag->name;
} else
continue;
bitset &= ~(flag->set | flag->clear);
bitclear &= ~(flag->set | flag->clear);
if (dp > string)
*dp++ = ',';
while ((*dp++ = *sp++) != '\0')
;
dp--;
}
*dp = '\0';
return (string);
}
static const char *
ae_strtofflags(const char *s, unsigned long *setp, unsigned long *clrp)
{
const char *start, *end;
struct flag *flag;
unsigned long set, clear;
const char *failed;
set = clear = 0;
start = s;
failed = NULL;
while (*start == '\t' || *start == ' ' || *start == ',')
start++;
while (*start != '\0') {
end = start;
while (*end != '\0' && *end != '\t' &&
*end != ' ' && *end != ',')
end++;
for (flag = flags; flag->name != NULL; flag++) {
if (memcmp(start, flag->name, end - start) == 0) {
clear |= flag->set;
set |= flag->clear;
break;
} else if (memcmp(start, flag->name + 2, end - start)
== 0) {
set |= flag->set;
clear |= flag->clear;
break;
}
}
if (flag->name == NULL && failed == NULL)
failed = start;
start = end;
while (*start == '\t' || *start == ' ' || *start == ',')
start++;
}
if (setp)
*setp = set;
if (clrp)
*clrp = clear;
return (failed);
}
static const wchar_t *
ae_wcstofflags(const wchar_t *s, unsigned long *setp, unsigned long *clrp)
{
const wchar_t *start, *end;
struct flag *flag;
unsigned long set, clear;
const wchar_t *failed;
set = clear = 0;
start = s;
failed = NULL;
while (*start == L'\t' || *start == L' ' || *start == L',')
start++;
while (*start != L'\0') {
end = start;
while (*end != L'\0' && *end != L'\t' &&
*end != L' ' && *end != L',')
end++;
for (flag = flags; flag->wname != NULL; flag++) {
if (wmemcmp(start, flag->wname, end - start) == 0) {
clear |= flag->set;
set |= flag->clear;
break;
} else if (wmemcmp(start, flag->wname + 2, end - start)
== 0) {
set |= flag->set;
clear |= flag->clear;
break;
}
}
if (flag->wname == NULL && failed == NULL)
failed = start;
start = end;
while (*start == L'\t' || *start == L' ' || *start == L',')
start++;
}
if (setp)
*setp = set;
if (clrp)
*clrp = clear;
return (failed);
}
#ifdef TEST
#include <stdio.h>
int
main(int argc, char **argv)
{
struct archive_entry *entry = archive_entry_new();
unsigned long set, clear;
const wchar_t *remainder;
remainder = archive_entry_copy_fflags_text_w(entry, L"nosappnd dump archive,,,,,,,");
archive_entry_fflags(entry, &set, &clear);
wprintf(L"set=0x%lX clear=0x%lX remainder='%ls'\n", set, clear, remainder);
wprintf(L"new flags='%s'\n", archive_entry_fflags_text(entry));
return (0);
}
#endif