
/*

 aaip-os-freebsd.c
 Arbitrary Attribute Interchange Protocol , system adapter for getting and
 setting of ACLs and xattr.

 To be included by aaip_0_2.c for FreeBSD, NetBSD, and OpenBSD

 Copyright (c) 2009 - 2016 Thomas Schmitt, libburnia project, GPLv2+

*/

#ifdef HAVE_CONFIG_H
#include "../config.h"
#endif

#include <ctype.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/stat.h>
#include <errno.h>

#ifdef Libisofs_with_aaip_acL
#include <sys/acl.h>
#endif

#ifdef Libisofs_with_freebsd_extattR
#include <sys/extattr.h>
#endif

#include <sys/statvfs.h>

/* <<< Use old ACL adapter code that is unable to deal with extattr */
/* # define Libisofs_old_freebsd_acl_adapteR */


/* ------------------------------ Inquiry --------------------------------- */

/* See also API iso_local_attr_support().
   @param flag
        Bitfield for control purposes
             bit0= inquire availability of ACL
             bit1= inquire availability of xattr
             bit2 - bit7= Reserved for future types.
                          It is permissibile to set them to 1 already now.
             bit8 and higher: reserved, submit 0
   @return
        Bitfield corresponding to flag.
             bit0= ACL adapter is enabled
             bit1= xattr adapter is enabled
             bit2 - bit7= Reserved for future types.
             bit8 and higher: reserved, do not interpret these
*/
int aaip_local_attr_support(int flag)
{
 int ret= 0;

#ifdef Libisofs_with_aaip_acL
 if(flag & 1)
   ret|= 1;
#endif
#ifdef Libisofs_with_freebsd_extattR
 if(flag & 2)
   ret|= 2;
#endif

 return(ret);
}


#ifdef Libisofs_with_freebsd_extattR

static int aaip_extattr_path_supp(char *path, int flag)
{

#ifdef MNT_EXTATTR

 int ret;
 struct statvfs statvfs_buf;

 ret = statvfs(path, &statvfs_buf);
 if(ret == -1)
   return(1);
 return(!!(statvfs_buf.f_flag & MNT_EXTATTR));

#else /* MNT_EXTATTR */

 return(1);

#endif /* ! MNT_EXTATTR */

}

#endif /* Libisofs_with_freebsd_extattR */


/* ------------------------------ Getters --------------------------------- */

/* Obtain the ACL of the given file in long text form.
   @param path          Path to the file
   @param text          Will hold the result. This is a managed object which
                        finally has to be freed by a call to this function
                        with bit15 of flag.
   @param flag          Bitfield for control purposes
                        (bit0=  obtain default ACL rather than access ACL)
                        bit4=  set *text = NULL and return 2
                               if the ACL matches st_mode permissions.
                        bit5=  in case of symbolic link: inquire link target
                        bit15= free text and return 1
   @return              > 0 ok
                          0 ACL support not enabled at compile time
                            or filesystem does not support ACL
                         -1 failure of system ACL service (see errno)
                         -2 attempt to inquire ACL of a symbolic
                            link without bit4 or bit5
*/
int aaip_get_acl_text(char *path, char **text, int flag)
{
#ifdef Libisofs_with_aaip_acL
 acl_t acl= NULL;
#endif
 struct stat stbuf;
 int ret;

 if(flag & (1 << 15)) {
   if(*text != NULL)
#ifdef Libisofs_with_aaip_acL
     acl_free(*text);
#else
     free(*text);
#endif
   *text= NULL;
   return(1);
 }

 *text= NULL;
 if(flag & 32)
   ret= stat(path, &stbuf);
 else
   ret= lstat(path, &stbuf);
 if(ret == -1)
   return(-1);
 if((stbuf.st_mode & S_IFMT) == S_IFLNK) {
   if(flag & 16)
     return(2);
   return(-2);
 }

 /* Note: no ACL_TYPE_DEFAULT in FreeBSD  */
 if(flag & 1)
   return(0);

#ifdef Libisofs_with_aaip_acL

 acl= acl_get_file(path, ACL_TYPE_ACCESS);

 if(acl == NULL) {
   if(errno == EOPNOTSUPP) {
     /* filesystem does not support ACL */
     if(flag & 16)
       return(2);

     /* >>> ??? fake ACL from POSIX permissions ? */;

     return(0);
   }
   return(-1);
 }
 *text= acl_to_text(acl, NULL);
 acl_free(acl);

#else /* Libisofs_with_aaip_acL */

 /* ??? >>> Fake ACL */;

 return(0);

#endif /* ! Libisofs_with_aaip_acL */

 if(*text == NULL)
   return(-1);
 if(flag & 16) {
   ret = aaip_cleanout_st_mode(*text, &(stbuf.st_mode), 2);
   if(!(ret & (7 | 64)))
     (*text)[0]= 0;
   if((*text)[0] == 0 || strcmp(*text, "\n") == 0) {
#ifdef Libisofs_with_aaip_acL
     acl_free(*text);
#else
     free(*text);
#endif
     *text= NULL;
     return(2);
   }
 }
 return(1);
}


#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) {
   if(! aaip_extattr_path_supp(path, 0)) {
     *list_size = 0;
     return(2);
   }
   if(errno == EPERM && attrnamespace == EXTATTR_NAMESPACE_SYSTEM) {
     *list_size = 0;
     return(3);
   }
   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);
}


static int get_single_attr(char *path, char *name, size_t *value_length,
                           char **value_bytes, int flag)
{
 char *namept;
 int attrnamespace;
 ssize_t value_ret;
 
 *value_bytes= NULL;
 *value_length= 0;
 if(strncmp(name, "user.", 5) == 0) {
   attrnamespace= EXTATTR_NAMESPACE_USER;
   namept= name + 5;
 } else {
   if(!(flag & 8))
     return(0);
   attrnamespace= EXTATTR_NAMESPACE_SYSTEM;
   namept= name + 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)
   return(0);

 *value_bytes= calloc(value_ret + 1, 1);
 if(*value_bytes == NULL)
   return(-1);

 /* Obtain value */
 if(flag & 32) /* follow link */
   value_ret= extattr_get_file(path, attrnamespace, namept,
                               *value_bytes, (size_t) value_ret);
 else
   value_ret= extattr_get_link(path, attrnamespace, namept,
                               *value_bytes, (size_t) value_ret);
 if(value_ret == -1) {
   free(*value_bytes);
   *value_bytes= NULL;
   *value_length= 0;
   return(0);
 }
 *value_length= value_ret;
 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              1   ok
                        2   ok, no permission to inspect non-user namespaces
                        <=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;

#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;
 ssize_t value_ret, list_size= 0, user_list_size= 0;
 ssize_t sys_list_size= 0;
 int acl_names= 0;
#endif
 int no_perm_for_system= 0;

 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;}
     if(ret == 3)
       no_perm_for_system= 1;
   }

   /* 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++;

#ifdef Libisofs_with_freebsd_extattR
   acl_names= 1;
#endif

 }
#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++) {
     value_ret= get_single_attr(path, (*names)[i], *value_lengths + i,
                                *values + i, flag & (8 | 32));
     if(value_ret <= 0)
       {ret= -1; goto ex;}
   }
 }

#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) {
     /* empty ACL / only st_mode info was found in ACL */
     ret= 1 + no_perm_for_system;
     goto ex;
   }
   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 + no_perm_for_system;
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().

   Note: There are no Extended Attributes in FreeBSD. So only ACL will be
         obtained.

   @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 encode attributes other than ACL
                        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
                        <=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;

#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;}
 }

 *num_attrs= 0;
 *names= NULL;
 *value_lengths= NULL;
 *values= NULL;

 num_names= 0;
 if(flag & 1)
   num_names++;
 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_aaip_acL

 if(flag & 1) { /* Obtain ACL */
   /* access-ACL */
   aaip_get_acl_text(path, &acl_text, flag & (16 | 32));
   if(acl_text == NULL)
     {ret= 1; goto ex;} /* empty ACL / only st_mode info was found in ACL */
   ret= aaip_encode_acl(acl_text, (mode_t) 0, &a_acl_len, &a_acl, flag & 2);
   if(ret <= 0)
     goto ex;
   aaip_get_acl_text("", &acl_text, 1 << 15); /* free */
   
   /* 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(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) {
     for(i= 0; i < (ssize_t) *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; i < (ssize_t) *num_attrs; i++)
       free((*values)[i]);
     free(*values);
   }
   *values= NULL;
   *num_attrs= 0;
 }
 return(ret);
}

#endif /* Libisofs_old_freebsd_acl_adapteR */


/* ------------------------------ Setters --------------------------------- */


/* Set the ACL of the given file to a given list in long text form.
   @param path          Path to the file
   @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
                        bit6= tolerate inappropriate presence or absence of
                              directory default ACL
   @return              > 0 ok
                         0 no suitable ACL manipulation adapter available
                        -1  failure of system ACL service (see errno)
                        -2 attempt to manipulate ACL of a symbolic link
                           without bit5 or with no suitable link target
*/
int aaip_set_acl_text(char *path, char *text, int flag)
{

#ifdef Libisofs_with_aaip_acL

 int ret;
 acl_t acl= NULL;
 struct stat stbuf;

 if(flag & 32)
   ret= stat(path, &stbuf);
 else
   ret= lstat(path, &stbuf);
 if(ret == -1)
   return(-1);
 if((stbuf.st_mode & S_IFMT) == S_IFLNK)
   return(-2);

 acl= acl_from_text(text);
 if(acl == NULL) {
   ret= -1; goto ex;
 }

 /* Note: no ACL_TYPE_DEFAULT in FreeBSD */
 if(flag & 1)
   {ret= 0; goto ex;}

 ret= acl_set_file(path, ACL_TYPE_ACCESS, acl);

 if(ret == -1)
   goto ex;
 ret= 1;
ex:
 if(acl != NULL)
   acl_free(acl);
 return(ret);

#else /* Libisofs_with_aaip_acL */

 return(0);

#endif /* ! Libisofs_with_aaip_acL */

}


#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 */


static void register_errno(int *errnos, int i, int in_errno)
{
 if(in_errno > 0)
   errnos[i]= in_errno;
 else
   errnos[i]= -1;
}


/* 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
                        bit6= tolerate inappropriate presence or absence of
                              directory default ACL
                        bit7= void setting a name value pair if it already
                              exists and has the desired value.
   @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
    ISO_AAIP_ACL_MULT_OBJ multiple entries of user::, group::, other::
*/
int aaip_set_attr_list(char *path, size_t num_attrs, char **names,
                       size_t *value_lengths, char **values,
                       int *errnos,  int flag)
{
 int ret, has_default_acl= 0, end_ret= 1;
 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, *old_value;
 ssize_t user_list_size= 0, sys_list_size= 0, value_ret;
 int attrnamespace;
 size_t old_value_l;
 int skip;
#endif

 for(i= 0; i < num_attrs; i++)
   errnos[i]= 0;

#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 {
     register_errno(errnos, i, (int) EFAULT);
     end_ret= -8;
 continue;
   }
   skip= 0;
   if(flag & 128) {
     value_ret= get_single_attr(path, names[i], &old_value_l,
                                &old_value, flag & (8 | 32));
     if(value_ret > 0 && old_value_l == value_lengths[i]) {
       if(memcmp(old_value, values[i], value_lengths[i]) == 0)
         skip= 1;
     }
     if(old_value != NULL)
       free(old_value);
   }
   if(!skip) {
     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) {
       register_errno(errnos, i, errno);
       if(end_ret != 1)
         end_ret= -4;
 continue;
     }
   }

#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 */
 /* It is important that this happens after restoring xattr which might be
    representations of ACL, too. If isofs ACL are enabled, then they shall
    override the xattr ones.
 */
 if(acl_idx == 0)
   {ret= end_ret; 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 < -3)
   goto ex;
 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 < -3)
   goto ex;
 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 | 64));
 if(ret == -1)
    register_errno(errnos, i, errno);
 if(ret <= 0)
   {ret= -3; goto ex;}
#else
 {ret= -7; goto ex;}
#endif

 if(has_default_acl && !(flag & 64))
   {ret= -3; goto ex;}

 ret= end_ret;
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.

   @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." )
   @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 attribute )
                       -6 support of xattr not enabled at compile time
                       -7 support of ACL not enabled at compile time
*/
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, was_xattr= 0;
 size_t i, consumed, acl_text_fill;
 char *acl_text= NULL, *list= NULL;

 for(i= 0; i < num_attrs; i++) {
   if(names[i] == NULL || values[i] == NULL)
 continue;
   if(names[i][0] == 0) { /* Decode ACLs */
     /* access ACL */
     if(!(flag & 1))
 continue;
     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
                                                            /* "default" ACL */
     if(has_default_acl) {
       free(acl_text);
       acl_text= NULL;
       ret= aaip_decode_acl((unsigned char *) (values[i] + consumed),
                            value_lengths[i] - consumed, &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] + consumed),
                            value_lengths[i] - consumed, &consumed,
                            acl_text, acl_text_fill, &acl_text_fill, 0);
       if(ret <= 0)
         {ret= -2; goto ex;}
       ret= aaip_set_acl_text(path, acl_text, 1 | (flag & 32));
       if(ret <= 0)
         {ret= -3; goto ex;}
     }
   } else {
     if(flag & 4)
 continue;
     if(!(flag & 8))
       if(strncmp(names[i], "user.", 5))
 continue;
     was_xattr= 1;
   }
 }
 ret= 1;
 if(was_xattr)
   ret= -6;
ex:;
 if(acl_text != NULL)
   free(acl_text);
 if(list != NULL)
   free(list);
 return(ret);
}

#endif /* Libisofs_old_freebsd_acl_adapteR */

