1
0
mirror of https://git.FreeBSD.org/src.git synced 2025-01-16 15:11:52 +00:00

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.
This commit is contained in:
Tim Kientzle 2004-04-26 23:37:54 +00:00
parent e99eef3d4f
commit 61fac2242c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=128669
9 changed files with 514 additions and 90 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 <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;
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

View File

@ -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 *);

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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));
}