Enabled recording and restoring of extattr on FreeBSD.

Gave up unconditional ACL support in favor of configure control.
This commit is contained in:
Thomas Schmitt 2011-08-22 17:10:13 +02:00
parent f885da8087
commit ca63dac7e3
5 changed files with 571 additions and 10 deletions

View File

@ -160,14 +160,19 @@ LIBBURNIA_SET_PKGCONFIG
dnl Add compiler-specific flags
AC_ARG_ENABLE(libacl,
[ --enable-libacl Enable use of libacl by libisofs, default=yes],
[ --enable-libacl Enable use of ACL functions by libisofs, default=yes],
, enable_libacl=yes)
if test "x$enable_libacl" = xyes; then
dnl Check whether there is libacl-devel and libacl-runtime.
dnl If not, erase this macro which would enable use of acl_to_text and others
LIBACL_DEF="-DLibisofs_with_aaip_acL"
dnl The empty yes case obviously causes -lacl to be linked
AC_CHECK_HEADER(sys/acl.h, AC_CHECK_LIB(acl, acl_to_text, , LIBACL_DEF= ), LIBACL_DEF= )
has_acl_h_but_no_func=0
AC_CHECK_HEADER(sys/acl.h, AC_CHECK_LIB(acl, acl_to_text, , has_acl_h_but_no_libacl=1 ), LIBACL_DEF= )
if test "$has_acl_h_but_no_libacl" = 1
then
AC_CHECK_LIB(c, acl_to_text, X= , LIBACL_DEF= )
fi
else
LIBACL_DEF=
fi
@ -183,6 +188,11 @@ dnl Check whether there is the header for Linux xattr.
dnl If not, erase this macro which would enable use of listxattr and others
XATTR_DEF="-DLibisofs_with_aaip_xattR"
AC_CHECK_HEADER(attr/xattr.h, AC_CHECK_LIB(c, listxattr, X= , XATTR_DEF= ), XATTR_DEF= )
if test "x$XATTR_DEF" = x
then
XATTR_DEF="-DLibisofs_with_freebsd_extattR"
AC_CHECK_HEADER(sys/extattr.h, AC_CHECK_LIB(c, extattr_list_file, X=, XATTR_DEF= ), XATTR_DEF= )
fi
else
XATTR_DEF=
fi

View File

@ -24,15 +24,17 @@
#include <sys/stat.h>
#include <errno.h>
#ifndef Libisofs_with_aaip_acL
/* It seems ACL is fixely integrated in FreeBSD libc. There is no libacl. */
#define Libisofs_with_aaip_acL yes
#endif
#ifdef Libisofs_with_aaip_acL
#include <sys/acl.h>
#endif
#ifdef Libisofs_with_freebsd_extattR
#include <sys/extattr.h>
#endif
/* <<< Use old ACL adapter code that is unable to deal with extattr */
/* # define Libisofs_old_freebsd_acl_adapteR */
/* ------------------------------ Inquiry --------------------------------- */
@ -59,6 +61,10 @@ int aaip_local_attr_support(int flag)
if(flag & 1)
ret|= 1;
#endif
#ifdef Libisofs_with_freebsd_extattR
if(flag & 2)
ret|= 2;
#endif
return(ret);
}
@ -167,6 +173,349 @@ int aaip_get_acl_text(char *path, char **text, int flag)
}
#ifndef Libisofs_old_freebsd_acl_adapteR
#ifdef Libisofs_with_freebsd_extattR
/*
@param flag Bitfield for control purposes
bit5= in case of symbolic link: inquire link target
*/
static int aaip_extattr_make_list(char *path, int attrnamespace,
char **list, ssize_t *list_size, int flag)
{
*list= NULL;
*list_size= 0;
/* man 2 extattr_list_file:
If data is NULL in a call to extattr_get_file() and extattr_list_file()
then the size of defined extended attribute data will be returned,
*/
if(flag & 32) /* follow link */
*list_size= extattr_list_file(path, attrnamespace, NULL, (size_t) 0);
else
*list_size= extattr_list_link(path, attrnamespace, NULL, (size_t) 0);
if(*list_size == -1)
return(0);
if(*list_size == 0)
return(2);
*list= calloc(*list_size, 1);
if(*list == NULL)
return(-1);
if(flag & 32)
*list_size= extattr_list_file(path, attrnamespace, *list,
(size_t) *list_size);
else
*list_size= extattr_list_link(path, attrnamespace, *list,
(size_t) *list_size);
if(*list_size == -1)
return(0);
return(1);
}
/*
@param flag Bitfield for control purposes
bit0= preserve existing namelist content
bit1= ignore names with NUL rather than returning error
*/
static int aaip_extattr_make_namelist(char *path, char *attrnamespace,
char *list, ssize_t list_size,
char **namelist, ssize_t *namelist_size,
ssize_t *num_names, int flag)
{
int i, j, len, new_bytes= 0, space_len;
char *new_list= NULL, *wpt;
if(!(flag & 1)) {
*namelist= NULL;
*namelist_size= 0;
*num_names= 0;
}
if(list_size <= 0)
return(1);
space_len= strlen(attrnamespace);
for(i= 0; i < list_size; i+= len + 1) {
len= *((unsigned char *) (list + i));
if(len == 0)
return ISO_AAIP_BAD_ATTR_NAME; /* empty name is reserved for ACL */
for(j= 0; j < len; j++)
if(list[i + 1 + j] == 0) {
if(flag & 2)
continue;
return ISO_AAIP_BAD_ATTR_NAME; /* names may not contain 0-bytes */
}
new_bytes+= space_len + 1 + len + 1;
}
if((flag & 1) && *namelist_size > 0)
new_bytes+= *namelist_size;
new_list= calloc(new_bytes, 1);
if(new_list == NULL)
return(ISO_OUT_OF_MEM);
wpt= new_list;
if((flag & 1) && *namelist_size > 0) {
memcpy(new_list, *namelist, *namelist_size);
wpt= new_list + *namelist_size;
}
for(i= 0; i < list_size; i+= len + 1) {
len= *((unsigned char *) (list + i));
if(flag & 2) {
for(j= 0; j < len; j++)
if(list[i + j] == 0)
continue;
}
memcpy(wpt, attrnamespace, space_len);
wpt[space_len]= '.';
wpt+= space_len + 1;
memcpy(wpt, list + i + 1, len);
wpt+= len;
*(wpt++)= 0;
(*num_names)++;
}
if((flag & 1) && *namelist != NULL)
free(*namelist);
*namelist= new_list;
*namelist_size= new_bytes;
return(1);
}
#endif /* Libisofs_with_freebsd_extattR */
/* Obtain the Extended Attributes and/or the ACLs of the given file in a form
that is ready for aaip_encode().
@param path Path to the file
@param num_attrs Will return the number of name-value pairs
@param names Will return an array of pointers to 0-terminated names
@param value_lengths Will return an arry with the lenghts of values
@param values Will return an array of pointers to 8-bit values
@param flag Bitfield for control purposes
bit0= obtain ACL (access and eventually default)
bit1= use numeric ACL qualifiers rather than names
bit2= do not obtain attributes other than ACL
bit3= do not ignore eventual non-user attributes
I.e. those with a name which does not begin
by "user."
bit4= do not return trivial ACL that matches st_mode
bit5= in case of symbolic link: inquire link target
bit15= free memory of names, value_lengths, values
@return >0 ok
<=0 error
-1= out of memory
-2= program error with prediction of result size
-3= error with conversion of name to uid or gid
*/
int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names,
size_t **value_lengths, char ***values, int flag)
{
int ret;
ssize_t i, num_names= 0, acl_names= 0;
#ifdef Libisofs_with_aaip_acL
unsigned char *a_acl= NULL;
char *a_acl_text= NULL;
size_t a_acl_len= 0;
#endif
#ifdef Libisofs_with_freebsd_extattR
char *list= NULL, *user_list= NULL, *sys_list= NULL, *namept;
ssize_t value_ret, retry= 0, list_size= 0, user_list_size= 0;
ssize_t sys_list_size= 0;
int attrnamespace;
#endif
if(flag & (1 << 15)) { /* Free memory */
{ret= 1; goto ex;}
}
*num_attrs= 0;
*names= NULL;
*value_lengths= NULL;
*values= NULL;
/* Set up arrays */
#ifdef Libisofs_with_freebsd_extattR
if(!(flag & 4)) { /* Get extattr names */
/* Linux : Names are encoded as name NUL
FreeBSD: Names are encoded as length_byte:chars (no NUL)
AAIP demands names not to contain NUL bytes.
*/
/* Obtain lists of names
Must be done separately for namespaces. See man 9 extattr :
EXTATTR_NAMESPACE_USER , EXTATTR_NAMESPACE_SYSTEM
Must then be marked by "user." and "system." for libisofs use.
*/
ret= aaip_extattr_make_list(path, EXTATTR_NAMESPACE_USER,
&user_list, &user_list_size, flag & 32);
if(ret <= 0)
{ret= -1; goto ex;}
if(flag & 8) {
ret= aaip_extattr_make_list(path, EXTATTR_NAMESPACE_SYSTEM,
&sys_list, &sys_list_size, flag & 32);
if(ret <= 0)
{ret= -1; goto ex;}
}
/* Check for NUL in names, convert into a linuxish list of namespace.name */
ret= aaip_extattr_make_namelist(path, "user", user_list, user_list_size,
&list, &list_size, &num_names, 0);
if(ret <= 0)
goto ex;
ret= aaip_extattr_make_namelist(path, "system", sys_list, sys_list_size,
&list, &list_size, &num_names, 1);
if(ret <= 0)
goto ex;
}
#endif /* Libisofs_with_freebsd_extattR */
#ifdef Libisofs_with_aaip_acL
if(flag & 1) {
num_names++;
acl_names= 1;
}
#endif
if(num_names == 0)
{ret= 1; goto ex;}
(*names)= calloc(num_names, sizeof(char *));
(*value_lengths)= calloc(num_names, sizeof(size_t));
(*values)= calloc(num_names, sizeof(char *));
if(*names == NULL || *value_lengths == NULL || *values == NULL)
{ret= -1; goto ex;}
for(i= 0; i < num_names; i++) {
(*names)[i]= NULL;
(*values)[i]= NULL;
(*value_lengths)[i]= 0;
}
#ifdef Libisofs_with_freebsd_extattR
if(!(flag & 4)) { /* Get xattr values */
for(i= 0; i < list_size && (size_t) num_names - acl_names > *num_attrs;
i+= strlen(list + i) + 1) {
if(!(flag & 8))
if(strncmp(list + i, "user.", 5))
continue;
(*names)[(*num_attrs)++]= strdup(list + i);
if((*names)[(*num_attrs) - 1] == NULL)
{ret= -1; goto ex;}
}
for(i= 0; (size_t) i < *num_attrs; i++) {
if(strncmp((*names)[i], "user.", 5) == 0) {
attrnamespace= EXTATTR_NAMESPACE_USER;
namept= (*names)[i] + 5;
} else {
if(!(flag & 8))
continue;
attrnamespace= EXTATTR_NAMESPACE_SYSTEM;
namept= (*names)[i] + 7;
}
/* Predict length of value */
if(flag & 32) /* follow link */
value_ret= extattr_get_file(path, attrnamespace, namept,
NULL, (size_t) 0);
else
value_ret= extattr_get_link(path, attrnamespace, namept,
NULL, (size_t) 0);
if(value_ret == -1)
continue;
(*values)[i]= calloc(value_ret + 1, 1);
if((*values)[i] == NULL)
{ret= -1; goto ex;}
/* Obtain value */
if(flag & 32) /* follow link */
value_ret= extattr_get_file(path, attrnamespace, namept,
(*values)[i], (size_t) value_ret);
else
value_ret= extattr_get_link(path, attrnamespace, namept,
(*values)[i], (size_t) value_ret);
if(value_ret == -1) { /* there could be a race condition */
if(retry++ > 5)
{ret= -1; goto ex;}
i--;
continue;
}
(*value_lengths)[i]= value_ret;
retry= 0;
}
}
#endif /* Libisofs_with_freebsd_extattR */
#ifdef Libisofs_with_aaip_acL
if(flag & 1) { /* Obtain ACL */
/* access-ACL */
aaip_get_acl_text(path, &a_acl_text, flag & (16 | 32));
if(a_acl_text == NULL)
{ret= 1; goto ex;} /* empty ACL / only st_mode info was found in ACL */
ret= aaip_encode_acl(a_acl_text, (mode_t) 0, &a_acl_len, &a_acl, flag & 2);
if(ret <= 0)
goto ex;
/* Note: There are no default-ACL in FreeBSD */
/* Set as attribute with empty name */;
(*names)[*num_attrs]= strdup("");
if((*names)[*num_attrs] == NULL)
{ret= -1; goto ex;}
(*values)[*num_attrs]= (char *) a_acl;
a_acl= NULL;
(*value_lengths)[*num_attrs]= a_acl_len;
(*num_attrs)++;
}
#endif /* Libisofs_with_aaip_acL */
ret= 1;
ex:;
#ifdef Libisofs_with_aaip_acL
if(a_acl != NULL)
free(a_acl);
if(a_acl_text != NULL)
aaip_get_acl_text("", &a_acl_text, 1 << 15); /* free */
#endif
#ifdef Libisofs_with_freebsd_extattR
if(list != NULL)
free(list);
if(user_list != NULL)
free(user_list);
if(sys_list != NULL)
free(sys_list);
#endif
if(ret <= 0 || (flag & (1 << 15))) {
if(*names != NULL) {
for(i= 0; (size_t) i < *num_attrs; i++)
free((*names)[i]);
free(*names);
}
*names= NULL;
if(*value_lengths != NULL)
free(*value_lengths);
*value_lengths= NULL;
if(*values != NULL) {
for(i= 0; (size_t) i < *num_attrs; i++)
free((*values)[i]);
free(*values);
}
*values= NULL;
*num_attrs= 0;
}
return(ret);
}
#else /* ! Libisofs_old_freebsd_acl_adapteR */
/* Obtain the Extended Attributes and/or the ACLs of the given file in a form
that is ready for aaip_encode().
@ -182,7 +531,9 @@ int aaip_get_acl_text(char *path, char **text, int flag)
bit0= obtain ACL (access and eventually default)
bit1= use numeric ACL qualifiers rather than names
bit2= do not encode attributes other than ACL
bit3= -reserved-
bit3= do not ignore eventual non-user attributes
I.e. those which are not from name space
EXTATTR_NAMESPACE_USER
bit4= do not return trivial ACL that matches st_mode
bit15= free memory of names, value_lengths, values
@return >0 ok
@ -196,9 +547,12 @@ int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names,
{
int ret;
ssize_t i, num_names;
#ifdef Libisofs_with_aaip_acL
size_t a_acl_len= 0;
unsigned char *a_acl= NULL;
char *acl_text= NULL;
#endif
if(flag & (1 << 15)) { /* Free memory */
{ret= 1; goto ex;}
@ -245,18 +599,21 @@ int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names,
if((*names)[*num_attrs] == NULL)
{ret= -1; goto ex;}
(*values)[*num_attrs]= (char *) a_acl;
a_acl= NULL;
(*value_lengths)[*num_attrs]= a_acl_len;
(*num_attrs)++;
}
#endif /* Libisofs_with_aaip_acL */
#endif /* ! Libisofs_with_aaip_acL */
ret= 1;
ex:;
#ifdef Libisofs_with_aaip_acL
if(a_acl != NULL)
free(a_acl);
if(acl_text != NULL)
aaip_get_acl_text("", &acl_text, 1 << 15); /* free */
#endif /* Libisofs_with_aaip_acL */
if(ret <= 0 || (flag & (1 << 15))) {
if(*names != NULL) {
@ -279,6 +636,8 @@ ex:;
return(ret);
}
#endif /* Libisofs_old_freebsd_acl_adapteR */
/* ------------------------------ Setters --------------------------------- */
@ -288,6 +647,7 @@ ex:;
@param text The input text (0 terminated, ACL long text form)
@param flag Bitfield for control purposes
bit0= set default ACL rather than access ACL
bit5= in case of symbolic link: manipulate link target
@return > 0 ok
-1 failure of system ACL service (see errno)
-2 ACL support not enabled at compile time
@ -338,6 +698,190 @@ ex:
}
#ifndef Libisofs_old_freebsd_acl_adapteR
#ifdef Libisofs_with_freebsd_extattR
/*
@param flag Bitfield for control purposes
bit5= in case of symbolic link: manipulate link target
*/
static int aaip_extattr_delete_names(char *path, int attrnamespace,
char *list, ssize_t list_size, int flag)
{
int len;
char name[256];
ssize_t value_ret, i;
for(i= 0; i < list_size; i+= len + 1) {
len= *((unsigned char *) (list + i));
if(len > 0)
strncpy(name, list + i + 1, len);
name[len]= 0;
if(flag & 32)
value_ret= extattr_delete_file(path, attrnamespace, name);
else
value_ret= extattr_delete_file(path, attrnamespace, name);
if(value_ret == -1)
return(0);
}
return(1);
}
#endif /* Libisofs_with_freebsd_extattR */
/* Bring the given attributes and/or ACLs into effect with the given file.
@param flag Bitfield for control purposes
bit0= decode and set ACLs
bit1= first clear all existing attributes of the file
bit2= do not set attributes other than ACLs
bit3= do not ignore eventual non-user attributes.
I.e. those with a name which does not begin
by "user."
bit5= in case of symbolic link: manipulate link target
@return 1 success
-1 error memory allocation
-2 error with decoding of ACL
-3 error with setting ACL
-4 error with setting attribute
-5 error with deleting attributes
-6 support of xattr not enabled at compile time
-7 support of ACL not enabled at compile time
-8 unsupported xattr namespace
*/
int aaip_set_attr_list(char *path, size_t num_attrs, char **names,
size_t *value_lengths, char **values, int flag)
{
int ret, has_default_acl= 0;
size_t i, consumed, acl_text_fill, acl_idx= 0;
char *acl_text= NULL;
#ifdef Libisofs_with_freebsd_extattR
char *user_list= NULL, *sys_list= NULL, *namept;
ssize_t user_list_size= 0, sys_list_size= 0;
int attrnamespace;
#endif
#ifdef Libisofs_with_freebsd_extattR
if(flag & 2) { /* Delete all file attributes */
ret= aaip_extattr_make_list(path, EXTATTR_NAMESPACE_USER,
&user_list, &user_list_size, flag & 32);
if(ret <= 0)
{ret= -1; goto ex;}
ret= aaip_extattr_delete_names(path, EXTATTR_NAMESPACE_USER,
user_list, user_list_size, flag & 32);
if(ret <= 0)
{ret= -5; goto ex;}
if(flag & 8) {
ret= aaip_extattr_make_list(path, EXTATTR_NAMESPACE_SYSTEM,
&sys_list, &sys_list_size, flag & 32);
if(ret <= 0)
{ret= -5; goto ex;}
ret= aaip_extattr_delete_names(path, EXTATTR_NAMESPACE_SYSTEM,
sys_list, sys_list_size, flag & 32);
if(ret <= 0)
{ret= -5; goto ex;}
}
}
#endif /* Libisofs_with_freebsd_extattR */
for(i= 0; i < num_attrs; i++) {
if(names[i] == NULL || values[i] == NULL)
continue;
if(names[i][0] == 0) { /* ACLs */
if(flag & 1)
acl_idx= i + 1;
continue;
}
/* Extended Attribute */
if(flag & 4)
continue;
#ifdef Libisofs_with_freebsd_extattR
if(strncmp(names[i], "user.", 5) == 0) {
attrnamespace= EXTATTR_NAMESPACE_USER;
namept= names[i] + 5;
} else if(strncmp(names[i], "isofs.", 6) == 0 || !(flag & 8)) {
continue;
} else if(strncmp(names[i], "system.", 7) == 0) {
attrnamespace= EXTATTR_NAMESPACE_SYSTEM;
namept= names[i] + 7;
} else {
{ret= -8; goto ex;}
}
if(flag & 32)
ret= extattr_set_file(path, attrnamespace, namept,
values[i], value_lengths[i]);
else
ret= extattr_set_link(path, attrnamespace, namept,
values[i], value_lengths[i]);
if(ret == -1)
{ret= -4; goto ex;}
#else
if(strncmp(names[i], "user.", 5) == 0)
;
else if(strncmp(names[i], "isofs.", 6) == 0 || !(flag & 8))
continue;
{ret= -6; goto ex;}
#endif /* Libisofs_with_freebsd_extattR */
}
/* Decode ACLs */
if(acl_idx == 0)
{ret= 1; goto ex;}
i= acl_idx - 1;
ret= aaip_decode_acl((unsigned char *) values[i], value_lengths[i],
&consumed, NULL, 0, &acl_text_fill, 1);
if(ret <= 0)
{ret= -2; goto ex;}
acl_text= calloc(acl_text_fill, 1);
if(acl_text == NULL)
{ret= -1; goto ex;}
ret= aaip_decode_acl((unsigned char *) values[i], value_lengths[i],
&consumed, acl_text, acl_text_fill, &acl_text_fill, 0);
if(ret <= 0)
{ret= -2; goto ex;}
has_default_acl= (ret == 2);
#ifdef Libisofs_with_aaip_acL
ret= aaip_set_acl_text(path, acl_text, flag & 32);
if(ret <= 0)
{ret= -3; goto ex;}
#else
{ret= -7; goto ex;}
#endif
if(has_default_acl) {
/* >>> EXTATTR : Give opportunity to ignore default ACLs */;
{ret= -3; goto ex;}
}
ret= 1;
ex:;
if(acl_text != NULL)
free(acl_text);
#ifdef Libisofs_with_freebsd_extattR
if(user_list != NULL)
free(user_list);
if(sys_list != NULL)
free(sys_list);
#endif /* Libisofs_with_freebsd_extattR */
return(ret);
}
#else /* ! Libisofs_old_freebsd_acl_adapteR */
/* Bring the given attributes and/or ACLs into effect with the given file.
Note: There are no Extended Attributes in FreeBSD. So only ACL get set.
@ -433,4 +977,5 @@ ex:;
return(ret);
}
#endif /* Libisofs_old_freebsd_acl_adapteR */

View File

@ -876,6 +876,8 @@ int iso_local_set_attrs(char *disk_path, size_t num_attrs, char **names,
return ISO_AAIP_BAD_AASTRING;
if (ret == -6 || ret == -7)
return ISO_AAIP_NOT_ENABLED;
if (ret == -8)
return ISO_AAIP_BAD_ATTR_NAME;
return ISO_AAIP_NO_SET_LOCAL;
}
return 1;

View File

@ -6915,6 +6915,8 @@ int iso_md5_match(char first_md5[16], char second_md5[16]);
/** Rock Ridge path too long (FAILURE, HIGH, -379) */
#define ISO_RR_PATH_TOO_LONG 0xE830FE85
/** Attribute name cannot be represented (FAILURE, HIGH, -380) */
#define ISO_AAIP_BAD_ATTR_NAME 0xE830FE84
/* Internal developer note:

View File

@ -464,6 +464,8 @@ const char *iso_error_to_msg(int errcode)
return "Reserved Rock Ridge leaf name";
case ISO_RR_PATH_TOO_LONG:
return "Rock Ridge path too long";
case ISO_AAIP_BAD_ATTR_NAME:
return "Attribute name cannot be represented";
default:
return "Unknown error";
}