diff --git a/lib/libarchive/Makefile b/lib/libarchive/Makefile index 676b1e34241a..6a72f017b71c 100644 --- a/lib/libarchive/Makefile +++ b/lib/libarchive/Makefile @@ -51,13 +51,23 @@ MAN= archive_entry.3 \ MLINKS+= archive_entry.3 archive_entry_clear.3 MLINKS+= archive_entry.3 archive_entry_clone.3 +MLINKS+= archive_entry.3 archive_entry_copy_fflags_text_w.3 +MLINKS+= archive_entry.3 archive_entry_copy_gname_w.3 +MLINKS+= archive_entry.3 archive_entry_copy_hardlink_w.3 +MLINKS+= archive_entry.3 archive_entry_copy_pathname_w.3 MLINKS+= archive_entry.3 archive_entry_copy_stat.3 -MLINKS+= archive_entry.3 archive_entry_dup.3 +MLINKS+= archive_entry.3 archive_entry_copy_symlink_w.3 +MLINKS+= archive_entry.3 archive_entry_copy_uname_w.3 +MLINKS+= archive_entry.3 archive_entry_fflags.3 +MLINKS+= archive_entry.3 archive_entry_fflags_text.3 MLINKS+= archive_entry.3 archive_entry_free.3 MLINKS+= archive_entry.3 archive_entry_gname.3 +MLINKS+= archive_entry.3 archive_entry_gname_w.3 MLINKS+= archive_entry.3 archive_entry_hardlink.3 +MLINKS+= archive_entry.3 archive_entry_hardlink_w.3 MLINKS+= archive_entry.3 archive_entry_new.3 MLINKS+= archive_entry.3 archive_entry_pathname.3 +MLINKS+= archive_entry.3 archive_entry_pathname_w.3 MLINKS+= archive_entry.3 archive_entry_set_devmajor.3 MLINKS+= archive_entry.3 archive_entry_set_devminor.3 MLINKS+= archive_entry.3 archive_entry_set_gid.3 @@ -72,7 +82,9 @@ MLINKS+= archive_entry.3 archive_entry_set_uname.3 MLINKS+= archive_entry.3 archive_entry_size.3 MLINKS+= archive_entry.3 archive_entry_stat.3 MLINKS+= archive_entry.3 archive_entry_symlink.3 +MLINKS+= archive_entry.3 archive_entry_symlink_w.3 MLINKS+= archive_entry.3 archive_entry_uname.3 +MLINKS+= archive_entry.3 archive_entry_uname_w.3 MLINKS+= archive_read.3 archive_read_data.3 MLINKS+= archive_read.3 archive_read_data_into_buffer.3 MLINKS+= archive_read.3 archive_read_data_into_file.3 diff --git a/lib/libarchive/Makefile.freebsd b/lib/libarchive/Makefile.freebsd index 676b1e34241a..6a72f017b71c 100644 --- a/lib/libarchive/Makefile.freebsd +++ b/lib/libarchive/Makefile.freebsd @@ -51,13 +51,23 @@ MAN= archive_entry.3 \ MLINKS+= archive_entry.3 archive_entry_clear.3 MLINKS+= archive_entry.3 archive_entry_clone.3 +MLINKS+= archive_entry.3 archive_entry_copy_fflags_text_w.3 +MLINKS+= archive_entry.3 archive_entry_copy_gname_w.3 +MLINKS+= archive_entry.3 archive_entry_copy_hardlink_w.3 +MLINKS+= archive_entry.3 archive_entry_copy_pathname_w.3 MLINKS+= archive_entry.3 archive_entry_copy_stat.3 -MLINKS+= archive_entry.3 archive_entry_dup.3 +MLINKS+= archive_entry.3 archive_entry_copy_symlink_w.3 +MLINKS+= archive_entry.3 archive_entry_copy_uname_w.3 +MLINKS+= archive_entry.3 archive_entry_fflags.3 +MLINKS+= archive_entry.3 archive_entry_fflags_text.3 MLINKS+= archive_entry.3 archive_entry_free.3 MLINKS+= archive_entry.3 archive_entry_gname.3 +MLINKS+= archive_entry.3 archive_entry_gname_w.3 MLINKS+= archive_entry.3 archive_entry_hardlink.3 +MLINKS+= archive_entry.3 archive_entry_hardlink_w.3 MLINKS+= archive_entry.3 archive_entry_new.3 MLINKS+= archive_entry.3 archive_entry_pathname.3 +MLINKS+= archive_entry.3 archive_entry_pathname_w.3 MLINKS+= archive_entry.3 archive_entry_set_devmajor.3 MLINKS+= archive_entry.3 archive_entry_set_devminor.3 MLINKS+= archive_entry.3 archive_entry_set_gid.3 @@ -72,7 +82,9 @@ MLINKS+= archive_entry.3 archive_entry_set_uname.3 MLINKS+= archive_entry.3 archive_entry_size.3 MLINKS+= archive_entry.3 archive_entry_stat.3 MLINKS+= archive_entry.3 archive_entry_symlink.3 +MLINKS+= archive_entry.3 archive_entry_symlink_w.3 MLINKS+= archive_entry.3 archive_entry_uname.3 +MLINKS+= archive_entry.3 archive_entry_uname_w.3 MLINKS+= archive_read.3 archive_read_data.3 MLINKS+= archive_read.3 archive_read_data_into_buffer.3 MLINKS+= archive_read.3 archive_read_data_into_file.3 diff --git a/lib/libarchive/archive_entry.3 b/lib/libarchive/archive_entry.3 index 94ed5ea637c0..e128b6cf441d 100644 --- a/lib/libarchive/archive_entry.3 +++ b/lib/libarchive/archive_entry.3 @@ -30,12 +30,15 @@ .Sh NAME .Nm archive_entry_clear .Nm archive_entry_clone +.Nm archive_entry_copy_fflags_text_w .Nm archive_entry_copy_gname_w .Nm archive_entry_copy_hardlink_w .Nm archive_entry_copy_pathname_w .Nm archive_entry_copy_stat .Nm archive_entry_copy_symlink_w .Nm archive_entry_copy_uname_w +.Nm archive_entry_fflags +.Nm archive_entry_fflags_text .Nm archive_entry_free .Nm archive_entry_gname .Nm archive_entry_gname_w @@ -46,9 +49,11 @@ .Nm archive_entry_pathname_w .Nm archive_entry_set_devmajor .Nm archive_entry_set_devminor +.Nm archive_entry_set_fflags .Nm archive_entry_set_gid .Nm archive_entry_set_gname .Nm archive_entry_set_hardlink +.Nm archive_entry_set_link .Nm archive_entry_set_mode .Nm archive_entry_set_pathname .Nm archive_entry_set_symlink @@ -67,6 +72,8 @@ .Fn archive_entry_clear "struct archive_entry *" .Ft struct archive_entry * .Fn archive_entry_clone "struct archive_entry *" +.Ft const wchar_t * +.Fn archive_entry_copy_fflags_text_w "struct archive_entry *" "const wchar_t *" .Ft void .Fn archive_entry_copy_gname_w "struct archive_entry *" "const wchar_t *" .Ft void @@ -80,6 +87,10 @@ .Ft void .Fn archive_entry_copy_uname_w "struct archive_entry *" "const wchar_t *" .Ft void +.Fn archive_entry_fflags "struct archive_entry *" "unsigned long *set" "unsigned long *clear" +.Ft const char * +.Fn archive_entry_fflags_text "struct archive_entry *" +.Ft void .Fn archive_entry_free "struct archive_entry *" .Ft const char * .Fn archive_entry_gname "struct archive_entry *" @@ -100,6 +111,8 @@ .Ft void .Fn archive_entry_set_devminor "struct archive_entry *" "dev_t" .Ft void +.Fn archive_entry_set_fflags "struct archive_entry *" "unsigned long set" "unsigned long clear" +.Ft void .Fn archive_entry_set_gid "struct archive_entry *" "gid_t" .Ft void .Fn archive_entry_set_gname "struct archive_entry *" "const char *" @@ -141,42 +154,10 @@ These objects are used by .Xr libarchive 3 to represent the metadata associated with a particular entry in an archive. -.Pp -Most of the functions here set or read entries -in an object. Such functions have one of the -following forms: -.Bl -tag -compact -width indent -.It Fn archive_entry_set_XXXX -Stores the provided data in the object. -In particular, for strings, the pointer is stored, -not the referenced string. -.It Fn archive_entry_copy_XXXX -As above, except that the referenced data is copied -into the object. -.It Fn archive_entry_XXXX -Returns the specified data. -In the case of strings, a const-qualified pointer to -the string is returned. -.El -The string data can be accessed as wide character strings -(which are suffixed with -.Cm _w ) -or normal -.Va char -strings. -Note that these are different representations of the same -data: -For example, if you store a narrow string and read the corresponding -wide string, the object will transparently convert formats -using the current locale. -Similarly, if you store a wide string and then store a -narrow string for the same data, the previously-set wide string will -be discarded in favor of the new data. -.Pp -The remaining functions allocate, destroy, clear, and copy +.Ss Create and Destroy +There are functions to allocate, destroy, clear, and copy .Va archive_entry -objects. -These functions are described below: +objects: .Bl -tag -compact -width indent .It Fn archive_entry_clear Erases the object, resetting all internal fields to the @@ -194,6 +175,97 @@ Allocate and return a blank .Tn struct archive_entry object. .El +.Ss Set and Get Functions +Most of the functions here set or read entries in an object. +Such functions have one of the following forms: +.Bl -tag -compact -width indent +.It Fn archive_entry_set_XXXX +Stores the provided data in the object. +In particular, for strings, the pointer is stored, +not the referenced string. +.It Fn archive_entry_copy_XXXX +As above, except that the referenced data is copied +into the object. +.It Fn archive_entry_XXXX +Returns the specified data. +In the case of strings, a const-qualified pointer to +the string is returned. +.El +String data can be set or accessed as wide character strings +or normal +.Va char +strings. +The funtions that use wide character strings are suffixed with +.Cm _w . +Note that these are different representations of the same data: +For example, if you store a narrow string and read the corresponding +wide string, the object will transparently convert formats +using the current locale. +Similarly, if you store a wide string and then store a +narrow string for the same data, the previously-set wide string will +be discarded in favor of the new data. +.Pp +There are a few set/get functions that merit additional description: +.Bl -tag -compact -width indent +.It Fn archive_entry_set_link +This function sets the symlink field if it is already set. +Otherwise, it sets the hardlink field. +.El +.Ss File Flags +File flags are transparently converted between a bitmap +representation and a textual format. +For example, if you set the bitmap and ask for text, the library +will build a canonical text format. +However, if you set a text format and request a text format, +you will get back the same text, even if it is ill-formed. +If you need to canonicalize a textual flags string, you should first set the +text form, then request the bitmap form, then use that to set the bitmap form. +Setting the bitmap format will clear the internal text representation +and force it to be reconstructed when you next request the text form. +.Pp +The bitmap format consists of two integers, one containing bits +that should be set, the other specifying bits that should be +cleared. +Bits not mentioned in either bitmap will be ignored. +Usually, the bitmap of bits to be cleared will be set to zero. +In unusual circumstances, you can force a fully-specified set +of file flags by setting the bitmap of flags to clear to the complement +of the bitmap of flags to set. +(This differs from +.Xr fflagstostr 3 , +which only includes names for set bits.) +Converting a bitmap to a textual string is a platform-specific +operation; bits that are not meaningful on the current platform +will be ignored. +.Pp +The canonical text format is a comma-separated list of flag names. +The +.Fn archive_entry_copy_fflags_text_w +function parses the provided text and sets the internal bitmap values. +This is a platform-specific operation; names that are not meaningful +on the current platform will be ignored. +The function returns a pointer to the start of the first name that was not +recognized, or NULL if every name was recognized. +Note that every name--including names that follow an unrecognized name--will +be evaluated, and the bitmaps will be set to reflect every name that is +recognized. +(In particular, this differs from +.Xr strtofflags 3 , +which stops parsing at the first unrecognized name.) +.Ss ACL Handling +XXX This needs serious help. XXX +.Pp +An +.Dq Access Control List +(ACL) is a list of permissions that grant access to particular users or +groups beyond what would normally be provided by standard POSIX mode bits. +The ACL handling here addresses some deficiencies in the POSIX.1e draft 17 ACL +specification. +In particular, POSIX.1e draft 17 specifies several different formats, but +none of those formats include both textual user/group names and numeric +UIDs/GIDs. +.Pp +XXX explain ACL stuff XXX .\" .Sh EXAMPLE .\" .Sh RETURN VALUES .\" .Sh ERRORS diff --git a/lib/libarchive/archive_entry.c b/lib/libarchive/archive_entry.c index 1f85e5900a89..0aedaeba469b 100644 --- a/lib/libarchive/archive_entry.c +++ b/lib/libarchive/archive_entry.c @@ -37,7 +37,6 @@ __FBSDID("$FreeBSD$"); #include "archive.h" #include "archive_entry.h" -#include "archive_private.h" #undef max #define max(a, b) ((a)>(b)?(a):(b)) @@ -73,6 +72,9 @@ 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 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 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); @@ -110,16 +112,20 @@ static int prefix_w(const wchar_t *start, const wchar_t *end, struct archive_entry { /* * Note that ae_stat.st_mode & S_IFMT can be 0! - * This occurs when the actual file type of the underlying object is - * not in the archive. For example, 'tar' archives store hardlinks - * without marking the type of the underlying object. + * + * This occurs when the actual file type of the object is not + * in the archive. For example, 'tar' archives store + * hardlinks without marking the type of the underlying + * object. */ struct stat ae_stat; /* * Use aes here so that we get transparent mbs<->wcs conversions. */ - struct aes ae_fflags; /* Text fflags per fflagstostr(3) */ + struct aes ae_fflags_text; /* Text fflags per fflagstostr(3) */ + unsigned long ae_fflags_set; /* Bitmap fflags */ + unsigned long ae_fflags_clear; struct aes ae_gname; /* Name of owning group */ struct aes ae_hardlink; /* Name of target for hardlink */ struct aes ae_pathname; /* Name of entry */ @@ -132,7 +138,7 @@ struct archive_entry { wchar_t *acl_text_w; }; -void +static void aes_clean(struct aes *aes) { if (aes->aes_mbs_alloc) { @@ -146,7 +152,7 @@ aes_clean(struct aes *aes) memset(aes, 0, sizeof(*aes)); } -void +static void aes_copy(struct aes *dest, struct aes *src) { *dest = *src; @@ -163,7 +169,7 @@ aes_copy(struct aes *dest, struct aes *src) } } -const char * +static const char * aes_get_mbs(struct aes *aes) { if (aes->aes_mbs == NULL && aes->aes_wcs != NULL) { @@ -182,7 +188,7 @@ aes_get_mbs(struct aes *aes) return (aes->aes_mbs); } -const wchar_t * +static const wchar_t * aes_get_wcs(struct aes *aes) { if (aes->aes_wcs == NULL && aes->aes_mbs != NULL) { @@ -200,7 +206,7 @@ aes_get_wcs(struct aes *aes) return (aes->aes_wcs); } -void +static void aes_set_mbs(struct aes *aes, const char *mbs) { if (aes->aes_mbs_alloc) { @@ -215,7 +221,7 @@ aes_set_mbs(struct aes *aes, const char *mbs) aes->aes_wcs = NULL; } -void +static void aes_copy_mbs(struct aes *aes, const char *mbs) { if (aes->aes_mbs_alloc) { @@ -233,7 +239,7 @@ aes_copy_mbs(struct aes *aes, const char *mbs) } #if 0 -void +static void aes_set_wcs(struct aes *aes, const wchar_t *wcs) { if (aes->aes_mbs_alloc) { @@ -249,7 +255,7 @@ aes_set_wcs(struct aes *aes, const wchar_t *wcs) } #endif -void +static void aes_copy_wcs(struct aes *aes, const wchar_t *wcs) { if (aes->aes_mbs_alloc) { @@ -269,7 +275,7 @@ aes_copy_wcs(struct aes *aes, const wchar_t *wcs) struct archive_entry * archive_entry_clear(struct archive_entry *entry) { - aes_clean(&entry->ae_fflags); + aes_clean(&entry->ae_fflags_text); aes_clean(&entry->ae_gname); aes_clean(&entry->ae_hardlink); aes_clean(&entry->ae_pathname); @@ -291,14 +297,17 @@ archive_entry_clone(struct archive_entry *entry) 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, &entry->ae_fflags); + 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_symlink, &entry->ae_symlink); aes_copy(&entry2->ae_uname, &entry->ae_uname); + /* XXX TODO: Copy ACL data over as well. XXX */ return (entry2); } @@ -338,10 +347,44 @@ archive_entry_devminor(struct archive_entry *entry) return (minor(entry->ae_stat.st_rdev)); } -const char * -archive_entry_fflags(struct archive_entry *entry) +void +archive_entry_fflags(struct archive_entry *entry, + unsigned long *set, unsigned long *clear) { - return (aes_get_mbs(&entry->ae_fflags)); + *set = entry->ae_fflags_set; + *clear = entry->ae_fflags_clear; +} + +/* + * Note: if text was provided, this just returns that text. If you + * really need the text to be rebuilt in a canonical form, set the + * text, ask for the bitmaps, then set the bitmaps. (Setting the + * bitmaps clears any stored text.) This design is deliberate: if + * we're editing archives, we don't want to discard flags just because + * they aren't supported on the current system. The bitmap<->text + * conversions are platform-specific (see below). + */ +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); } const char * @@ -444,15 +487,21 @@ archive_entry_set_devminor(struct archive_entry *entry, dev_t m) } void -archive_entry_set_fflags(struct archive_entry *entry, const char *flags) +archive_entry_set_fflags(struct archive_entry *entry, + unsigned long set, unsigned long clear) { - aes_set_mbs(&entry->ae_fflags, flags); + aes_clean(&entry->ae_fflags_text); + entry->ae_fflags_set = set; + entry->ae_fflags_clear = clear; } -void -archive_entry_copy_fflags_w(struct archive_entry *entry, const wchar_t *flags) +const wchar_t * +archive_entry_copy_fflags_text_w(struct archive_entry *entry, + const wchar_t *flags) { - aes_copy_wcs(&entry->ae_fflags, flags); + aes_copy_wcs(&entry->ae_fflags_text, flags); + return (ae_wcstofflags(flags, + &entry->ae_fflags_set, &entry->ae_fflags_clear)); } void @@ -1187,17 +1236,237 @@ prefix_w(const wchar_t *start, const wchar_t *end, const wchar_t *test) } -#if TEST +/* + * Following code is modified from UC Berkeley sources, and + * is subject to the following copyright notice. + */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. 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. + * 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. + */ + +static struct flag { + const char *name; + const wchar_t *wname; + unsigned long set; + unsigned long clear; +} flags[] = { + /* Preferred (shorter) names per flag first, all prefixed by "no" */ +#ifdef SF_APPEND + { "nosappnd", L"nosappnd", SF_APPEND, 0 }, + { "nosappend", L"nosappend", SF_APPEND, 0 }, +#endif +#ifdef EXT2_APPEND_FL /* 'a' */ + { "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 /* 'i' */ + { "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 /* 'd' */ + { "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_COMPR_FL /* 'c' */ + { "nocompress", L"nocompress", EXT2_COMPR_FL, 0 }, +#endif + +#ifdef EXT2_NOATIME_FL /* 'A' */ + { "noatime", L"noatime", 0, EXT2_NOATIME_FL}, +#endif + { NULL, NULL, 0, 0 } +}; +#define longestflaglen 12 +#define nmappings (sizeof(mapping) / sizeof(mapping[0])) + +/* + * fflagstostr -- + * Convert file flags to a comma-separated string. If no flags + * are set, return the empty string. + */ +char * +ae_fflagstostr(unsigned long bitset, unsigned long bitclear) +{ + char *string, *dp; + const char *sp; + unsigned long bits; + struct flag *flag; + int 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); + } + + string = 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); +} + +/* + * wcstofflags -- + * Take string of arguments and return file flags. This + * version works a little differently than strtofflags(3). + * In particular, it always tests every token, skipping any + * unrecognized tokens. It returns a pointer to the first + * unrecognized token, or NULL if every token was recognized. + * This version is also const-correct and does not modify the + * provided string. + */ +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; + /* Find start of first token. */ + while (*start == L'\t' || *start == L' ' || *start == L',') + start++; + while (*start != L'\0') { + /* Locate end of token. */ + 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) { + /* Matched "noXXXX", so reverse the sense. */ + clear |= flag->set; + set |= flag->clear; + break; + } else if (wmemcmp(start, flag->wname + 2, end - start) + == 0) { + /* Matched "XXXX", so don't reverse. */ + set |= flag->set; + clear |= flag->clear; + break; + } + } + /* Ignore unknown flag names. */ + if (flag->wname == NULL && failed == NULL) + failed = start; + + /* Find start of next token. */ + start = end; + while (*start == L'\t' || *start == L' ' || *start == L',') + start++; + + } + + if (setp) + *setp = set; + if (clrp) + *clrp = clear; + + /* Return location of first failure. */ + return (failed); +} + + +#ifdef TEST +#include int main(int argc, char **argv) { - struct aes aes; + struct archive_entry *entry = archive_entry_new(); + unsigned long set, clear; + const wchar_t *remainder; - memset(&aes, 0, sizeof(aes)); - aes_clean(&aes); - aes_set_mbs(&aes, "ÈÈÈabc"); - wprintf("%S\n", L"abcdef"); - wprintf("%S\n",aes_get_wcs(&aes)); + 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 diff --git a/lib/libarchive/archive_entry.h b/lib/libarchive/archive_entry.h index 733a60dd5470..42c1fcae9df1 100644 --- a/lib/libarchive/archive_entry.h +++ b/lib/libarchive/archive_entry.h @@ -68,7 +68,9 @@ struct archive_entry *archive_entry_new(void); 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_fflags_text(struct archive_entry *); +void archive_entry_fflags(struct archive_entry *, + unsigned long *set, unsigned long *clear); const char *archive_entry_gname(struct archive_entry *); const char *archive_entry_hardlink(struct archive_entry *); mode_t archive_entry_mode(struct archive_entry *); @@ -85,14 +87,18 @@ const char *archive_entry_uname(struct archive_entry *); * Set fields in an archive_entry. * * Note that string 'set' functions do not copy the string, only the pointer. - * In contrast, 'copy_stat' does copy the full structure. + * In contrast, 'copy' functions do copy the object pointed to. */ void archive_entry_copy_stat(struct archive_entry *, const struct stat *); -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); void archive_entry_set_devminor(struct archive_entry *, dev_t); +void archive_entry_set_fflags(struct archive_entry *, + unsigned long set, unsigned long clear); +/* Returns pointer to start of first invalid token, or NULL if none. */ +/* Note that all recognized tokens are processed, regardless. */ +const wchar_t *archive_entry_copy_fflags_text_w(struct archive_entry *, + const wchar_t *); void archive_entry_set_gid(struct archive_entry *, gid_t); void archive_entry_set_gname(struct archive_entry *, const char *); void archive_entry_copy_gname_w(struct archive_entry *, const wchar_t *); diff --git a/lib/libarchive/archive_read_extract.c b/lib/libarchive/archive_read_extract.c index 6155c5285c24..77d6bb07c391 100644 --- a/lib/libarchive/archive_read_extract.c +++ b/lib/libarchive/archive_read_extract.c @@ -44,6 +44,10 @@ __FBSDID("$FreeBSD$"); #include #include #include +#ifdef LINUX +#include +#include +#endif #include "archive.h" #include "archive_string.h" @@ -440,7 +444,11 @@ archive_read_extract_dir_create(struct archive *a, const char *name, int mode, return (ARCHIVE_OK); /* Unlink failed. It's okay if it failed because it's already a dir. */ - if (errno != EPERM) { + /* + * BSD returns EPERM for unlink on an dir, + * Linux returns EISDIR + */ + if (errno != EPERM && errno != EISDIR) { archive_set_error(a, errno, "Couldn't create dir"); return (ARCHIVE_WARN); } @@ -782,29 +790,31 @@ set_extended_perm(struct archive *a, struct archive_entry *entry, int flags) static int set_fflags(struct archive *a, struct archive_entry *entry) { - char *fflags; - const char *fflagsc; - char *fflags_p; const char *name; int ret; unsigned long set, clear; struct stat st; +#ifdef LINUX + struct stat *stp; + int fd; + int err; + unsigned long newflags, oldflags; +#endif name = archive_entry_pathname(entry); - ret = ARCHIVE_OK; - fflagsc = archive_entry_fflags(entry); - if (fflagsc == NULL) - return (ARCHIVE_OK); - - fflags = strdup(fflagsc); - if (fflags == NULL) - return (ARCHIVE_WARN); + archive_entry_fflags(entry, &set, &clear); + if (set == 0 && clear == 0) + return (ret); #ifdef HAVE_CHFLAGS - fflags_p = fflags; - if (strtofflags(&fflags_p, &set, &clear) == 0 && - stat(name, &st) == 0) { + /* + * XXX Is the stat here really necessary? Or can I just use + * the 'set' flags directly? In particular, I'm not sure + * about the correct approach if we're overwriting an existing + * file that already has flags on it. XXX + */ + if (stat(name, &st) == 0) { st.st_flags &= ~clear; st.st_flags |= set; if (chflags(name, st.st_flags) != 0) { @@ -814,8 +824,45 @@ set_fflags(struct archive *a, struct archive_entry *entry) } } #endif + /* Linux has flags too, but no chflags syscall */ +#ifdef LINUX + /* + * Linux has no define for the flags that are only settable + * by the root user... + */ +#define SF_MASK (EXT2_IMMUTABLE_FL|EXT2_APPEND_FL) + + /* + * XXX As above, this would be way simpler if we didn't have + * to read the current flags from disk. XXX + */ + stp = archive_entry_stat(entry); + if ((S_ISREG(stp->st_mode) || S_ISDIR(stp->st_mode)) && + ((fd = open(name, O_RDONLY|O_NONBLOCK)) >= 0)) { + err = 1; + if (fd >= 0 && (ioctl(fd, EXT2_IOC_GETFLAGS, &oldflags) >= 0)) { + newflags = (oldflags & ~clear) | set; + if (ioctl(fd, EXT2_IOC_SETFLAGS, &newflags) >= 0) { + err = 0; + } else if (errno == EPERM) { + if (ioctl(fd, EXT2_IOC_GETFLAGS, &oldflags) >= 0) { + newflags &= ~SF_MASK; + oldflags &= SF_MASK; + newflags |= oldflags; + if (ioctl(fd, EXT2_IOC_SETFLAGS, &newflags) >= 0) + err = 0; + } + } + } + close(fd); + if (err) { + archive_set_error(a, errno, + "Failed to set file flags"); + ret = ARCHIVE_WARN; + } + } +#endif - free(fflags); return (ret); } diff --git a/lib/libarchive/archive_read_support_format_tar.c b/lib/libarchive/archive_read_support_format_tar.c index e2f62424aa60..f41e37d515a5 100644 --- a/lib/libarchive/archive_read_support_format_tar.c +++ b/lib/libarchive/archive_read_support_format_tar.c @@ -911,7 +911,7 @@ pax_attribute(struct archive_entry *entry, struct stat *st, st->st_rdev = makedev(major(st->st_dev), tar_atol10(value, wcslen(value))); else if (wcscmp(key, L"SCHILY.fflags")==0) - archive_entry_copy_fflags_w(entry, value); + archive_entry_copy_fflags_text_w(entry, value); else if (wcscmp(key, L"SCHILY.nlink")==0) st->st_nlink = tar_atol10(value, wcslen(value)); break; diff --git a/lib/libarchive/archive_write_set_format_pax.c b/lib/libarchive/archive_write_set_format_pax.c index 19317df0380d..950cd4d2d27a 100644 --- a/lib/libarchive/archive_write_set_format_pax.c +++ b/lib/libarchive/archive_write_set_format_pax.c @@ -493,6 +493,12 @@ archive_write_pax_header(struct archive *a, ((st_main->st_mtime < 0) || (st_main->st_mtime >= 0x7fffffff))) need_extension = 1; + /* I use a star-compatible file flag attribute. */ + p = archive_entry_fflags_text(entry_main); + if (!need_extension && p != NULL && *p != '\0') + need_extension = 1; + + /* If there are non-trivial ACL entries, we need an extension. */ if (!need_extension && archive_entry_acl_count(entry_original, ARCHIVE_ENTRY_ACL_TYPE_ACCESS) > 0) @@ -533,7 +539,7 @@ archive_write_pax_header(struct archive *a, ARCHIVE_STAT_ATIME_NANOS(st_main)); /* I use a star-compatible file flag attribute. */ - p = archive_entry_fflags(entry_main); + p = archive_entry_fflags_text(entry_main); if (p != NULL && *p != '\0') add_pax_attr(&(pax->pax_header), "SCHILY.fflags", p); diff --git a/lib/libarchive/archive_write_set_format_shar.c b/lib/libarchive/archive_write_set_format_shar.c index f9082c6a0d0b..5944e0fe7f31 100644 --- a/lib/libarchive/archive_write_set_format_shar.c +++ b/lib/libarchive/archive_write_set_format_shar.c @@ -423,7 +423,7 @@ archive_write_shar_finish_entry(struct archive *a) archive_entry_pathname(shar->entry)); } - if ((p = archive_entry_fflags(shar->entry)) != NULL) { + if ((p = archive_entry_fflags_text(shar->entry)) != NULL) { shar_printf(a, "chflags %s %s\n", p, archive_entry_pathname(shar->entry)); }