diff options
author | kientzle <kientzle@FreeBSD.org> | 2004-04-26 23:37:54 +0000 |
---|---|---|
committer | kientzle <kientzle@FreeBSD.org> | 2004-04-26 23:37:54 +0000 |
commit | 736725bcabf6c72ac5bfda301f3aa3b447ac4c6b (patch) | |
tree | f0f5a81c35118c56a6a3ac1a5d72fd50e2cdf842 /lib/libarchive | |
parent | f5f7c88a25b2bbf11fbacb20c1194ad48018294b (diff) | |
download | FreeBSD-src-736725bcabf6c72ac5bfda301f3aa3b447ac4c6b.zip FreeBSD-src-736725bcabf6c72ac5bfda301f3aa3b447ac4c6b.tar.gz |
Update file flag handling.
The new fflags support in archive_entry supports Linux and FreeBSD
file flags and is a bit more gracious about unrecognized flag names
than strtofflags(3). This involves some minor API breakage.
The default tar format ("restricted pax") now enables pax extensions
when archiving files that have flags. In particular, copying dir
heirarchies with 'bsdtar cf - -C src . | bsdtar xpf - -C dest' now
preserves file flags. (Note the "p" on extract!)
While I'm here, fill in some additional explanation in the
archive_entry.3 manpage, fill in some missing MLINKS, mark some
overlooked internal functions 'static', and make a few minor style
fixes.
Diffstat (limited to 'lib/libarchive')
-rw-r--r-- | lib/libarchive/Makefile | 14 | ||||
-rw-r--r-- | lib/libarchive/Makefile.freebsd | 14 | ||||
-rw-r--r-- | lib/libarchive/archive_entry.3 | 128 | ||||
-rw-r--r-- | lib/libarchive/archive_entry.c | 327 | ||||
-rw-r--r-- | lib/libarchive/archive_entry.h | 14 | ||||
-rw-r--r-- | lib/libarchive/archive_read_extract.c | 79 | ||||
-rw-r--r-- | lib/libarchive/archive_read_support_format_tar.c | 2 | ||||
-rw-r--r-- | lib/libarchive/archive_write_set_format_pax.c | 8 | ||||
-rw-r--r-- | lib/libarchive/archive_write_set_format_shar.c | 2 |
9 files changed, 506 insertions, 82 deletions
diff --git a/lib/libarchive/Makefile b/lib/libarchive/Makefile index 676b1e3..6a72f01 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 676b1e3..6a72f01 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 94ed5ea..e128b6c 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,10 +154,30 @@ 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: +.Ss Create and Destroy +There are functions to allocate, destroy, clear, and copy +.Va archive_entry +objects: +.Bl -tag -compact -width indent +.It Fn archive_entry_clear +Erases the object, resetting all internal fields to the +same state as a newly-created object. +This is provided to allow you to quickly recycle objects +without thrashing the heap. +.It Fn archive_entry_clone +A deep copy operation; all text fields are duplicated. +.It Fn archive_entry_free +Releases the +.Tn struct archive_entry +object. +.It Fn archive_entry_new +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. @@ -158,14 +191,13 @@ 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 ) +String data can be set or accessed as wide character strings or normal .Va char strings. -Note that these are different representations of the same -data: +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. @@ -173,27 +205,67 @@ 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 -.Va archive_entry -objects. -These functions are described below: +There are a few set/get functions that merit additional description: .Bl -tag -compact -width indent -.It Fn archive_entry_clear -Erases the object, resetting all internal fields to the -same state as a newly-created object. -This is provided to allow you to quickly recycle objects -without thrashing the heap. -.It Fn archive_entry_clone -A deep copy operation; all text fields are duplicated. -.It Fn archive_entry_free -Releases the -.Tn struct archive_entry -object. -.It Fn archive_entry_new -Allocate and return a blank -.Tn struct archive_entry -object. +.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 1f85e59..0aedaeb 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)); } +void +archive_entry_fflags(struct archive_entry *entry, + unsigned long *set, unsigned long *clear) +{ + *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(struct archive_entry *entry) +archive_entry_fflags_text(struct archive_entry *entry) { - return (aes_get_mbs(&entry->ae_fflags)); + 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 <stdio.h> int main(int argc, char **argv) { - struct aes aes; + 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); - 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)); + 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 733a60d..42c1fca 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 6155c52..77d6bb0 100644 --- a/lib/libarchive/archive_read_extract.c +++ b/lib/libarchive/archive_read_extract.c @@ -44,6 +44,10 @@ __FBSDID("$FreeBSD$"); #include <string.h> #include <tar.h> #include <unistd.h> +#ifdef LINUX +#include <ext2fs/ext2_fs.h> +#include <sys/ioctl.h> +#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 e2f6242..f41e37d 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 19317df..950cd4d 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 f9082c6..5944e0f 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)); } |