diff --git a/configure.ac b/configure.ac index 886ce89..2f2ab0b 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/libisofs/aaip-os-freebsd.c b/libisofs/aaip-os-freebsd.c index 5d374dd..08b3ab6 100644 --- a/libisofs/aaip-os-freebsd.c +++ b/libisofs/aaip-os-freebsd.c @@ -24,15 +24,17 @@ #include #include -#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 #endif +#ifdef Libisofs_with_freebsd_extattR +#include +#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 */ diff --git a/libisofs/fs_local.c b/libisofs/fs_local.c index 7dc370b..483ca39 100644 --- a/libisofs/fs_local.c +++ b/libisofs/fs_local.c @@ -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; diff --git a/libisofs/libisofs.h b/libisofs/libisofs.h index 291b340..18aa685 100644 --- a/libisofs/libisofs.h +++ b/libisofs/libisofs.h @@ -6912,9 +6912,11 @@ int iso_md5_match(char first_md5[16], char second_md5[16]); /** Reserved Rock Ridge leaf name (FAILURE, HIGH, -378) */ #define ISO_RR_NAME_RESERVED 0xE830FE86 -/** Rock Ridge path too long (FAILURE, HIGH, -379) */ +/** 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: diff --git a/libisofs/messages.c b/libisofs/messages.c index d326675..86b19e1 100644 --- a/libisofs/messages.c +++ b/libisofs/messages.c @@ -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"; }