From 3294dd5e940242090128843779b6d162c9cf3df9 Mon Sep 17 00:00:00 2001 From: Thomas Schmitt Date: Wed, 14 Jan 2009 12:11:47 +0100 Subject: [PATCH] Introduced AAIP code. Now linking with libacl. (Todo: handle system dependy of -lacl in configure.ac) --- Makefile.am | 62 +- doc/susp_aaip_0_2.txt | 333 +++++++ libisofs/aaip-os-freebsd.c | 283 ++++++ libisofs/aaip-os-linux.c | 371 ++++++++ libisofs/aaip_0_2.c | 1693 ++++++++++++++++++++++++++++++++++++ libisofs/aaip_0_2.h | 381 ++++++++ 6 files changed, 3103 insertions(+), 20 deletions(-) create mode 100644 doc/susp_aaip_0_2.txt create mode 100644 libisofs/aaip-os-freebsd.c create mode 100644 libisofs/aaip-os-linux.c create mode 100644 libisofs/aaip_0_2.c create mode 100644 libisofs/aaip_0_2.h diff --git a/Makefile.am b/Makefile.am index eb446f3..a42527b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -9,6 +9,9 @@ lib_LTLIBRARIES = libisofs/libisofs.la libisofs_libisofs_la_LDFLAGS = \ -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) + +# ts A80114 : added aaip_0_2.* + libisofs_libisofs_la_SOURCES = \ libisofs/builder.h \ libisofs/builder.c \ @@ -57,9 +60,12 @@ libisofs_libisofs_la_SOURCES = \ libisofs/make_isohybrid_mbr.c \ libisofs/iso1999.h \ libisofs/iso1999.c \ - libisofs/data_source.c + libisofs/data_source.c \ + libisofs/aaip_0_2.h \ + libisofs/aaip_0_2.c libisofs_libisofs_la_LIBADD= \ - $(THREAD_LIBS) + $(THREAD_LIBS) \ + -lacl libinclude_HEADERS = \ libisofs/libisofs.h @@ -81,51 +87,61 @@ noinst_PROGRAMS = \ demo/isogrow demo_lsl_CPPFLAGS = -Ilibisofs -demo_lsl_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_lsl_LDADD = $(libisofs_libisofs_la_OBJECTS) $(libisofs_libisofs_la_LIBADD) demo_lsl_SOURCES = demo/lsl.c demo_cat_CPPFLAGS = -Ilibisofs -demo_cat_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_cat_LDADD = $(libisofs_libisofs_la_OBJECTS) $(libisofs_libisofs_la_LIBADD) demo_cat_SOURCES = demo/cat.c demo_catbuffer_CPPFLAGS = -Ilibisofs -demo_catbuffer_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_catbuffer_LDADD = $(libisofs_libisofs_la_OBJECTS) \ + $(libisofs_libisofs_la_LIBADD) demo_catbuffer_SOURCES = demo/cat_buffer.c demo_tree_CPPFLAGS = -Ilibisofs -demo_tree_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_tree_LDADD = $(libisofs_libisofs_la_OBJECTS) \ + $(libisofs_libisofs_la_LIBADD) demo_tree_SOURCES = demo/tree.c demo_find_CPPFLAGS = -Ilibisofs -demo_find_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_find_LDADD = $(libisofs_libisofs_la_OBJECTS) \ + $(libisofs_libisofs_la_LIBADD) demo_find_SOURCES = demo/find.c demo_ecma119tree_CPPFLAGS = -Ilibisofs -demo_ecma119tree_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_ecma119tree_LDADD = $(libisofs_libisofs_la_OBJECTS) \ + $(libisofs_libisofs_la_LIBADD) demo_ecma119tree_SOURCES = demo/ecma119_tree.c demo_iso_CPPFLAGS = -Ilibisofs -demo_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_iso_LDADD = $(libisofs_libisofs_la_OBJECTS) $(libisofs_libisofs_la_LIBADD) demo_iso_SOURCES = demo/iso.c demo_isoread_CPPFLAGS = -Ilibisofs -demo_isoread_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_isoread_LDADD = $(libisofs_libisofs_la_OBJECTS) \ + $(libisofs_libisofs_la_LIBADD) demo_isoread_SOURCES = demo/iso_read.c demo_isocat_CPPFLAGS = -Ilibisofs -demo_isocat_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_isocat_LDADD = $(libisofs_libisofs_la_OBJECTS) \ + $(libisofs_libisofs_la_LIBADD) demo_isocat_SOURCES = demo/iso_cat.c demo_isomodify_CPPFLAGS = -Ilibisofs -demo_isomodify_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_isomodify_LDADD = $(libisofs_libisofs_la_OBJECTS) \ + $(libisofs_libisofs_la_LIBADD) demo_isomodify_SOURCES = demo/iso_modify.c demo_isoms_CPPFLAGS = -Ilibisofs -demo_isoms_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) +demo_isoms_LDADD = $(libisofs_libisofs_la_OBJECTS) \ + $(libisofs_libisofs_la_LIBADD) demo_isoms_SOURCES = demo/iso_ms.c demo_isogrow_CPPFLAGS = -Ilibisofs -Ilibburn -demo_isogrow_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lburn +demo_isogrow_LDADD = $(libisofs_libisofs_la_OBJECTS) \ + $(libisofs_libisofs_la_LIBADD) \ + -lburn demo_isogrow_SOURCES = demo/iso_grow.c @@ -135,7 +151,8 @@ check_PROGRAMS = \ test/test test_test_CPPFLAGS = -Ilibisofs -test_test_LDADD = $(libisofs_libisofs_la_OBJECTS) $(THREAD_LIBS) -lcunit +test_test_LDADD = $(libisofs_libisofs_la_OBJECTS) \ + $(libisofs_libisofs_la_LIBADD) -lcunit test_test_LDFLAGS = -L.. -lm test_test_SOURCES = \ @@ -177,6 +194,8 @@ uninstall-local: nodist_pkgconfig_DATA = \ libisofs-1.pc +# ts A80114 : added aaip-os* + EXTRA_DIST = \ libisofs-1.pc.in \ version.h.in \ @@ -186,9 +205,12 @@ EXTRA_DIST = \ AUTHORS \ COPYRIGHT \ COPYING \ - NEWS \ - INSTALL \ - TODO \ - ChangeLog \ - Roadmap + NEWS \ + INSTALL \ + TODO \ + ChangeLog \ + Roadmap \ + doc/susp_aaip_0_2.txt \ + libisofs/aaip-os-linux.c \ + libisofs/aaip-os-freebsd.c diff --git a/doc/susp_aaip_0_2.txt b/doc/susp_aaip_0_2.txt new file mode 100644 index 0000000..ef2704f --- /dev/null +++ b/doc/susp_aaip_0_2.txt @@ -0,0 +1,333 @@ + + + Arbitrary Attribute Interchange Protocol + + Draft version 0.2 + Dec 19 2008 + + Interchange of Persistent File Attributes + + by Thomas Schmitt - mailto:scdbackup@gmx.net + Libburnia project - mailto:libburn-hackers@pykix.org + + +AAIP is intended as companion of the Rock Ridge Interchange Protocol RRIP +which under the general design of System Use Sharing Protocol SUSP extends +ISO 9660 aka ECMA-119 filesystem semantics to match POSIX needs. + +Goal is to have for each file an arbitrary number of attributes which consist +of two components (Name and Value) of arbitrary length and to have a compact +representation of ACLs. + +This document describes a SUSP field with adjustable name (Signature Word). +The name is defined in an ER field of which the content form is described here. +Recommended is to use the name "AA" which collides neither with SUSP 1.12 nor +with RRIP 1.12. +The field has been designed to be as similar to the RRIP field SL as possible. + +Since the size of a SUSP field is limited to 255, multiple fields may be +needed to describe one component. The CE mechanism of SUSP shall be used to +address enough storage if needed. + +This SUSP field and the ER entry of AAIP shall only be present if the ER entry +of RRIP is present. + +------------------------------------------------------------------------------- + +System Entries Provided by this Specification + +* AA (or another name which does not disturb other co-existing SUSP protocols) + +Description of the "AA" System Use Entry + +The field has exactly the same layout as RRIP field SL. One has to expect +more data bytes than with SL, though, and any of the 256 possible byte values. +The reader shall be prepared to detect and handle oversized data. + +One or more AA fields form the Attribute List of a file object with +an even number of components. Each two consequtive components form a pair of +Name and Value. The empty name is reserved for a compact representation of +ALCs. The meaning of any other name is not specified by this document. + +All AA fields except the last one shall have the CONTINUE flag set. An AA +field with CONTINUE set to 0 indicates the end of the Attribute List. + +The format of the "AA" System Use Field is as follows: + + [1] "BP 1 to BP 2 - Signature Word" shall be (41)(41) ("AA") resp. the word + that is defined in the ER field. See below. + + [2] "BP 3 - Length" shall specify as an 8-bit number the length in bytes of + the "AA" entry recorded according to ISO 9660:7.1.1. + + [3] "BP 4 - System Use Entry Version" shall be 1 as in ISO 9660:7.1.1. + + [4] "BP 5 - Flags" shall contain bit field flags numbered 0 to 7 starting + with the least significant bit as follows: + 0 CONTINUE This AA field continues in the next AA field. + All other bits shall be set to 0. + + [5] "BP 6 to Length - Component Area" shall contain Component Records + as described below. + + | 'A' | 'A' | LENGTH | 1 | FLAGS | COMPONENT AREA | + + +Within "AA" fields each component (Name or Value) shall be recorded as one +or more component records. If a component does not fit into the remaining +space of an AA field then it shall be continued in following AA fields. + +All Component Records of a component except the last one shall have the +CONTINUE flag set. A Component Record with CONTINUE set to 0 indicates the end +of the component. An eventually following Component Record starts the next +component. + +------------------------------------------------------------------------------- + +The Component Record format is identical to the one of the SL field. +The complete form of the following summary can be found in RRIP 1.12 "4.1.3.1". +In case of discrepancies, RRIP 1.12 is the decisive specification. + +Component Records shall be recorded contiguously within each Component Area, +starting in the first byte of the Component Area. The last Component Record +in the Component Area of an "AA" System Use Entry may be continued in the +Component Area of the next recorded "AA" System Use Entry in the same +System Use Area. + +Each Component Record shall have the following format: + + [A] "BP 1 - Component Flags" shall contain bit field flags numbered 0 to 7, + starting with the least significant bit, as follows: + 0 CONTINUE This Component Record continues in the next + AA Component Record. + The following bits are defined but may not be set if the Component + Record shall carry payload. (Their use case is unclear yet.) + 1 CURRENT This Component Record refers to the current + directory. + 2 PARENT This Component Record refers to the parent of + the current directory. + 3 ROOT This Component Record refers to root directory. + + all others are RESERVED and shall be 0. + + No more than one of "AA" Component Flag Bits 0-3 shall be set to ONE. + + [B] "BP 2 - Component Length (LEN_CP)" shall specify as an 8-bit number the + number of component bytes in the Component Record. This length shall not + include the first two bytes of the Component Record. + If any of the bit positions 1-3 is set, the value of this field shall be + set to ZERO and no Component Content shall be recorded. + This field shall be recorded according to ISO 9660 Format section 7.1.1. + + [C] "BP 3 to 2 + LEN_CP - Component Content" shall contain the component + bytes in the Component Record. + + | COMPONENT FLAGS | LEN_CP | COMPONENT BYTES | + + +Example: Two pairs of "name"="long...content" and "one"="more" encoded as + two AA fields + + Field 1 contains the Component Record of Name and one Component Record of + Value : + { 'A', 'A', 255, 1, 1, + 0, 4, 'n', 'a', 'm', 'e', + 1, 255, 'l', 'o', 'n', 'g', ... 238 more bytes, 13 go to next AA ... } + Field 2 contains the rest of "long...content" and the complete second pair. + It marks the end of the Attribute List : + { 'A', 'A', 38, 1, 0, + ... 13 remaining bytes of the Component Record in first field ... + 0, 7, 'c', 'o', 'n', 't', 'e', 'n', 't', + 0, 3, 'o', 'n', 'e', + 0, 4, 'm', 'o', 'r', 'e' } + +------------------------------------------------------------------------------- + +Specification of binary ACL representation as special Arbitrary Attribute + +The Name component of a binary ACL shall be of length 0. + +The Value shall be an arbitrary number of ACL Entries: + + [a] "BP 1 - Entry Flags" shall contain bit field flags numbered 0 to 7, + starting with the least significant bit, as follows: + 0 EXEC indicates that this entry grants execute permission + 1 WRITE write permission + 2 READ read permission + 3 QUALIFIER indicates that one or more Qualifier Records follow + 4 - 7 TYPE + shall contain the tag type of the ACL entry as four bit code: + 0 TRANSLATE entry for a global map of name to numeric id + 1 ACL_USER_OBJ permissions of owning user (as of PX field) + 2 ACL_USER of arbitrary user, with name as qualifier + 3 ACL_GROUP_OBJ permissions of owning group (as of PX field) + 4 ACL_GROUP of arbitrary group, with name as qualifier + 5 ACL_MASK restricts 2, 3, and 4 via logical AND + 6 ACL_OTHER permissions of non-listed, non-owning users + 8 SWITCH_MARK switch from "access" ACL to "default" ACL + 10 ACL_USER_N like 2, with numeric user id as qualifier + 12 ACL_GROUP_N like 4, with numeric group id as qualifier + 15 FUTURE_VERSION will indicate that this document + does not apply to the entry. + The other values are reserved. Readers shall ignore them if + they are not aware of updates of this document which would + assign a meaning to them. + +If any of ACL_USER_OBJ, ACL_GROUP_OBJ, ACL_OTHER are missing then the settings +from the PX field shall get into effect. If they exist then they shall override +the PX field. + +A numeric qualifier is a binary number of variable length. The Most Significant +Byte comes first. The number shall be the "POSIX File User ID" resp. +"POSIX File Group ID" as also used in RRIP PX fields. The ids of owning user +and owning group shall be taken from the PX field of the file object. + +Optional TRANSLATE entries may associate user or group names with numeric +ids to allow the reading system to remap the numeric ids. See below. +The writer is not obliged to write them and the reader is not obliged to +interpret them. + +The ACL entries belong to the "access" ACL of a file object. An optional +SWITCH_MARK entry may direct further entries to the "default" ACL which +is defined for directory objects. The switching is controlled by the EXEC bit. + 0 "access" ACL + 1 "default" ACL +The bits for WRITE, READ, QUALIFIER shall be 0 with SWITCH_MARK. + +The eventually needed qualifier is stored in one or more Qualifier Records. + + [b] "BP 2 - Qualifier Record Head" shall be present only if QUALIFIER is set + to 1. It shall give the number of Qualifier Bytes and eventually + indicate that the qualifier continues in a Qualifier Record which comes + imediately after this record. + 0 to 127 Q_LENGTH, the qualifier is complete by this record + 128 to 255 Q_LENGTH+128, the qualifier is continued by next record + So a Qualifier Record can contain at most 127 Qualifier Bytes. + This field shall be recorded according to ISO 9660 Format section 7.1.1. + + [c] "BP 3 to BP 2 + Q_LENGTH - Qualifier Bytes" shall be present only if + QUALIFIER is set to 1 and hold the announced number of bytes of the + user or group name. + + | ENTRY FLAGS [ | QUALIFIER HEAD | QUALIFIER BYTES | ] + + +Example: From man 5 acl: u::rw-,u:lisa:rw-,g::r--,g:toolies:rw-,m::r--,o::r-- + { 'A', 'A', 28, 1, 0, + 0, 0, + 0, 19, 0x16, + 0x2E, 4, 'l', 'i', 's', 'a', + 0x34, + 0x4E, 7, 't', 'o', 'o', 'l', 'i', 'e', 's', + 0x54, + 0x64 } + +Example: An entry with very long qualifier u:His_Excellency_..._the_Boss:r-- + 0x2C, 255, 'H', 'i', 's', '_', 'E', 'x', 'c', 'e', 'l', 'e', + ... 117 more bytes ..., + 8, 't', 'h', 'e', '_', 'B', 'o', 's', 's', + +Example: User number 71 in numerical form gets rwx + 0xAF, 1, 71, + Group number 65534 gets r-x + 0xCD, 2, 255, 254, + + +------------------------------------------------------------------------------- + +About Names and Numeric Identifiers + +It makes an important difference whether qualifiers are represented as names +or as id numbers. By storing names (and usually wasting space) it is possible +to control permissions in a way that is portable between uncoordinated +computers as long as the human readable user/group names are present on both +sides. POSIX File ID numbers make most sense in backups which shall be read +by the same system which wrote it. +Rock Ridge can only record POSIX File Ids but not user or group names. + +The entry flag value 0x08 TRANSLATE is not a ACL entry of the hosting object +but rather a global hint about the relation of roles, names and numeric ids. +If it is recorded at all, then it shall be recorded with the first Directory +Entry of the volume's root directory. According to the description of SUSP +field ER, this has to be "dot" or (00). Other than with ER, a TRANSLATE entry +may not appear in the root of directory sub trees. + +An interested reader shall examine the Arbitrary Attributes of this Directory +Entry in order to collect a translation table. +The advised translation is: PX or AA Id number -> name -> local id number. + +The Qualifier Bytes of a TRANSLATE entry shall have the following format: + + [i] "BP 0 - Role" shall tell whether it is about a user name (role 0) or + a group name (role 1). Other values are not allowed. + + [ii] "BP 1 to BP 8 - Numeric Id" shall hold the 32 bit POSIX Id number of the + entry. This field shall be recorded according to ISO 9660:7.3.3. + +[iii] "BP 9 to End Of Qualifier - Name" shall hold the name bytes of this + entry. + + | ROLE | NUMERIC ID | NAME | + +Example: User id number 1001 gets associated with user name "lisa" + + 0x08, 13, 0, 233,3,0,0, 0,0,3,233, 'l', 'i', 's', 'a', + + +------------------------------------------------------------------------------- + +Specification of the ER System Use Entry Values for AAIP: + +This ER system entry shall only be present if the ER entry of RRIP is present. + +The Extension Version number for this version of AAIP shall be 1. + +The Extension Identifier field shall be "AAIP_0002" with Identifier Length 9. + +The mandatory content form of the Extension Descriptor is +"AA PROVIDES VIA AAIP 0.2 SUPPORT FOR ARBITRARY FILE ATTRIBUTES IN ISO 9660 IMAGES" +with possibly two letters other than "AA" at the start of the string. +The Description Length is 81. + +The reader of AAIP shall take the actual name of the AA field from BP 19 and +BP 20 of the ER field. + +The recommended content of the Extension Source is +"PLEASE CONTACT THE LIBBURNIA PROJECT VIA LIBBURNIA-PROJECT.ORG". +The corresponding Source Length is 62. + + +------------------------------------------------------------------------------- + +Model Relations: + + Attribute List ------------- [1:0..1] ------------- ACL + [1:0..n] [1:0..n] + Arbitrary Attribute ( [1:0..1] ACL ) Entry + [1:2..2n] [1:0..1] + Component ( [1..m:1..n] AA Field ) Qualifier + [1:1..n] << one of >> + Component Record / \ \ + [1..m:1..n] Translation Entry , Name , Numeric Id + AA Field | | | + [1:1..n] [1:1..n] [1:1] + \ | / + Qualifier Record + +------------------------------------------------------------------------------- +Revoked drafts: + +There was a draft AAIP 0.0 with ER signature "AAIP_2008A". It did not resemble +the existing field SL and therefore shall not be used by writers of ISO images. + +------------------------------------------------------------------------------- +References: + +ECMA-119 http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf + +SUSP 1.12 ftp://ftp.ymi.com/pub/rockridge/susp112.ps + +RRIP 1.12 ftp://ftp.ymi.com/pub/rockridge/rrip112.ps +(especially field SL) + + diff --git a/libisofs/aaip-os-freebsd.c b/libisofs/aaip-os-freebsd.c new file mode 100644 index 0000000..caa28da --- /dev/null +++ b/libisofs/aaip-os-freebsd.c @@ -0,0 +1,283 @@ + +/* + + aaip-os-freebsd.c + Arbitrary Attribute Interchange Protocol , system adapter for getting and + setting of ACLs and XFS-style Extended Attributes. + + To be included by aaip_0_2.c +*/ + +#include +#include +#include +#include +#include +#include + +#include + + +/* ------------------------------ 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 + bit15= free text and return 1 + @return > 0 ok + -1 failure of system ACL service (see errno) +*/ +int aaip_get_acl_text(char *path, char **text, int flag) +{ + acl_t acl= NULL; + + if(flag & (1 << 15)) { + if(*text != NULL) + acl_free(text); + *text= NULL; + return(1); + } + *text= NULL; + + /* Note: no ACL_TYPE_DEFAULT in FreeBSD */ + if(flag & 1) + return(0); + + acl= acl_get_file(path, ACL_TYPE_ACCESS); + + if(acl == NULL) + return(-1); + *text= acl_to_text(acl, NULL); + acl_free(acl); + if(*text == NULL) + return(-1); + return(1); +} + + +/* 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 + bit15= free memory of names, value_lengths, values + @return >0 ok + <=0 error +*/ +int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, + size_t **value_lengths, char ***values, int flag) +{ + int ret, retry= 0; + char *list= NULL; + ssize_t list_size= 0, i, num_names, value_ret; + size_t a_acl_len= 0, d_acl_len= 0, acl_len= 0; + unsigned char *a_acl= NULL, *d_acl= NULL, *acl= NULL; + char *acl_text= NULL; + + if(flag & (1 << 15)) { /* Free memory */ + if(*names != NULL) + list= (*names)[0]; + {ret= 1; goto ex;} + } + + *num_attrs= 0; + *names= NULL; + *value_lengths= NULL; + *values= NULL; + + 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= *num_attrs; i < num_names; i++) + (*names)[i]= NULL; + for(i= 0; i < num_names; i++) { + (*values)[i]= NULL; + (*value_lengths)[i]= 0; + } + + if(flag & 1) { /* Obtain ACL */ + /* access-ACL */ + ret= aaip_get_acl_text(path, &acl_text, 0); + if(ret <= 0) + goto ex; + ret= aaip_encode_acl(acl_text, &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 *) acl; + (*value_lengths)[*num_attrs]= acl_len; + (*num_attrs)++; + } + + ret= 1; +ex:; + if(a_acl != NULL) + free(a_acl); + if(d_acl != NULL) + free(d_acl); + if(acl_text != NULL) + aaip_get_acl_text("", &acl_text, 1 << 15); /* free */ + + if(ret <= 0 || (flag & (1 << 15))) { + if(list != NULL) + free(list); + if(*names != NULL) + free(*names); + *names= NULL; + if(*value_lengths != NULL) + free(*value_lengths); + *value_lengths= NULL; + if(*values != NULL) { + for(i= 0; i < *num_attrs; i++) + free((*values)[i]); + free(*values); + } + if(acl != NULL) + free(acl); + *values= NULL; + *num_attrs= 0; + } + return(ret); +} + + +/* ------------------------------ 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 + @return > 0 ok + -1 failure of system ACL service (see errno) +*/ +int aaip_set_acl_text(char *path, char *text, int flag) +{ + int ret; + acl_t acl= NULL; + + 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); +} + + +/* 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 ) + @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 ) + +*/ +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, list_size= 0; + 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 */ + 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); + ret= aaip_set_acl_text(path, acl_text, 0); + if(ret <= 0) + {ret= -3; goto ex;} + 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); + if(ret <= 0) + {ret= -3; goto ex;} + } + } + } + ret= 1; +ex:; + if(acl_text != NULL) + free(acl_text); + if(list != NULL) + free(list); + return(ret); +} + + diff --git a/libisofs/aaip-os-linux.c b/libisofs/aaip-os-linux.c new file mode 100644 index 0000000..c0e36e9 --- /dev/null +++ b/libisofs/aaip-os-linux.c @@ -0,0 +1,371 @@ + +/* + + aaip-os-linux.c + Arbitrary Attribute Interchange Protocol , system adapter for getting and + setting of ACLs and XFS-style Extended Attributes. + + To be included by aaip_0_2.c +*/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define Aaip_acl_attrnamE "system.posix_acl_access" + + +/* ------------------------------ 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 + bit15= free text and return 1 + @return > 0 ok + -1 failure of system ACL service (see errno) +*/ +int aaip_get_acl_text(char *path, char **text, int flag) +{ + acl_t acl= NULL; + + if(flag & (1 << 15)) { + if(*text != NULL) + acl_free(text); + *text= NULL; + return(1); + } + *text= NULL; + acl= acl_get_file(path, (flag & 1) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS); + if(acl == NULL) + return(-1); + *text= acl_to_text(acl, NULL); + acl_free(acl); + if(*text == NULL) + return(-1); + return(1); +} + + +/* 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 local ACL attribute + (e.g. system.posix_acl_access) + bit15= free memory of names, value_lengths, values + @return >0 ok + <=0 error +*/ +int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, + size_t **value_lengths, char ***values, int flag) +{ + int ret, retry= 0; + char *list= NULL; + ssize_t list_size= 0, i, num_names= 0, value_ret; + size_t a_acl_len= 0, d_acl_len= 0, acl_len= 0; + unsigned char *a_acl= NULL, *d_acl= NULL, *acl= NULL; + char *acl_text= NULL; + + if(flag & (1 << 15)) { /* Free memory */ + if(*names != NULL) + list= (*names)[0]; + {ret= 1; goto ex;} + } + + *num_attrs= 0; + *names= NULL; + *value_lengths= NULL; + *values= NULL; + + /* Set up arrays */ + if(!(flag & 4)) { /* Get xattr names */ + list_size= listxattr(path, list, 0); + if(list_size == -1) + {ret= -1; goto ex;} + list= calloc(list_size, 1); + if(list == NULL) + {ret= -1; goto ex;} + list_size= listxattr(path, list, list_size); + if(list_size == -1) + {ret= -1; goto ex;} + for(i= 0; i < list_size; i+= strlen(list + i) + 1) + num_names++; + } + 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;} + + if(!(flag & 4)) + for(i= 0; i < list_size && num_names > *num_attrs; + i+= strlen(list + i) + 1) { + if(!(flag & 8)) + if(strcmp(list + i, Aaip_acl_attrnamE) == 0) + continue; + (*names)[(*num_attrs)++]= list + i; + } + for(i= *num_attrs; i < num_names; i++) + (*names)[i]= NULL; + for(i= 0; i < num_names; i++) { + (*values)[i]= NULL; + (*value_lengths)[i]= 0; + } + + if(!(flag & 4)) { /* Get xattr values */ + for(i= 0; i < *num_attrs; i++) { + if(!(flag & 8)) + if(strcmp((*names)[i], Aaip_acl_attrnamE) == 0) + continue; + value_ret= getxattr(path, (*names)[i], NULL, 0); + if(value_ret == -1) + continue; + (*values)[i]= calloc(value_ret + 1, 1); + if((*values)[i] == NULL) + {ret= -1; goto ex;} + (*value_lengths)[i]= getxattr(path, (*names)[i], (*values)[i], 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; + } + } + + if(flag & 1) { /* Obtain ACL */ + /* access-ACL */ + ret= aaip_get_acl_text(path, &acl_text, 0); + if(ret <= 0) + goto ex; + ret= aaip_encode_acl(acl_text, &a_acl_len, &a_acl, flag & 2); + if(ret <= 0) + goto ex; + aaip_get_acl_text("", &acl_text, 1 << 15); /* free */ + + /* eventually default-ACL */ + ret= aaip_get_acl_text(path, &acl_text, 1); + if(ret > 0) { + /* encode and append to a_acl */; + ret= aaip_encode_acl(acl_text, &d_acl_len, &d_acl, (flag & 2) | 4); + if(ret <= 0) + goto ex; + acl= calloc(a_acl_len + d_acl_len + 1, 1); + if(acl == NULL) + {ret= -1; goto ex;} + if(a_acl_len) + memcpy(acl, a_acl, a_acl_len); + if(d_acl_len) + memcpy(acl + a_acl_len, d_acl, d_acl_len); + acl_len= a_acl_len + d_acl_len; + } else { + acl= a_acl; + a_acl= NULL; + acl_len= a_acl_len; + } + + /* Set as attribute with empty name */; + (*names)[*num_attrs]= strdup(""); + if((*names)[*num_attrs] == NULL) + {ret= -1; goto ex;} + (*values)[*num_attrs]= (char *) acl; + (*value_lengths)[*num_attrs]= acl_len; + (*num_attrs)++; + } + + ret= 1; +ex:; + if(a_acl != NULL) + free(a_acl); + if(d_acl != NULL) + free(d_acl); + if(acl_text != NULL) + aaip_get_acl_text("", &acl_text, 1 << 15); /* free */ + + if(ret <= 0 || (flag & (1 << 15))) { + if(list != NULL) + free(list); + if(*names != NULL) + free(*names); + *names= NULL; + if(*value_lengths != NULL) + free(*value_lengths); + *value_lengths= NULL; + if(*values != NULL) { + for(i= 0; i < *num_attrs; i++) + free((*values)[i]); + free(*values); + } + if(acl != NULL) + free(acl); + *values= NULL; + *num_attrs= 0; + } + return(ret); +} + + +/* ------------------------------ 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 + @return > 0 ok + -1 failure of system ACL service (see errno) +*/ +int aaip_set_acl_text(char *path, char *text, int flag) +{ + int ret; + acl_t acl= NULL; + + acl= acl_from_text(text); + if(acl == NULL) { + ret= -1; goto ex; + } + ret= acl_set_file(path, (flag & 1) ? ACL_TYPE_DEFAULT : ACL_TYPE_ACCESS, acl); + if(ret == -1) + goto ex; + ret= 1; +ex: + if(acl != NULL) + acl_free(acl); + return(ret); +} + + +/* 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 ACL attribute + (e.g. system.posix_acl_access) + @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 + +*/ +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, list_size= 0, acl_idx= 0; + char *acl_text= NULL, *list= NULL; + + if(flag & 2) /* Delete all file attributes */ + list_size= listxattr(path, list, 0); + if(list_size > 0) { /* Delete all file attributes */ + list= calloc(list_size, 1); + if(list == NULL) + {ret= -5; goto ex;} + list_size= listxattr(path, list, list_size); + if(list_size == -1) + {ret= -5; goto ex;} + for(i= 0; i < list_size; i+= strlen(list + i) + 1) { + if(!(flag & 8)) + if(strcmp(list + i, Aaip_acl_attrnamE) == 0) + continue; + ret= removexattr(path, list + i); + if(ret == -1) + {ret= -5; goto ex;} + } + free(list); list= NULL; + } + + 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 & 1) && !(flag & 8)) + if(strcmp(names[i], Aaip_acl_attrnamE) == 0) + continue; + ret= setxattr(path, names[i], values[i], value_lengths[i], 0); + if(ret == -1) + {ret= -4; goto ex;} + } + +/* Decode ACLs */ + if(acl_idx == 0) + {ret= 1; goto ex;} + i= acl_idx - 1; + /* "access" ACL */ + 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); + ret= aaip_set_acl_text(path, acl_text, 0); + if(ret <= 0) + {ret= -3; goto ex;} + /* "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); + if(ret <= 0) + {ret= -3; goto ex;} + } + ret= 1; +ex:; + if(acl_text != NULL) + free(acl_text); + if(list != NULL) + free(list); + return(ret); +} + + diff --git a/libisofs/aaip_0_2.c b/libisofs/aaip_0_2.c new file mode 100644 index 0000000..4f02b77 --- /dev/null +++ b/libisofs/aaip_0_2.c @@ -0,0 +1,1693 @@ + +/* + + Arbitrary Attribute Interchange Protocol , AAIP version 0.2 + Demonstration implementation of encoding and decoding EA and ACL. + + See test/aaip_0_2.h + http://libburnia-project.org/wiki/AAIP + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* <<< */ +#define Aaip_encode_debuG 1 + +#include "aaip_0_2.h" + +#define Aaip_EXEC 1 +#define Aaip_WRITE 2 +#define Aaip_READ 4 + +#define Aaip_TRANSLATE 0 +#define Aaip_ACL_USER_OBJ 1 +#define Aaip_ACL_USER 2 +#define Aaip_ACL_GROUP_OBJ 3 +#define Aaip_ACL_GROUP 4 +#define Aaip_ACL_MASK 5 +#define Aaip_ACL_OTHER 6 +#define Aaip_SWITCH_MARK 8 +#define Aaip_ACL_USER_N 10 +#define Aaip_ACL_GROUP_N 12 +#define Aaip_FUTURE_VERSION 15 + +/* --------------------------------- Encoder ---------------------------- */ + + +static int aaip_encode_pair(char *name, size_t attr_length, char *attr, + unsigned int *num_recs, size_t *comp_size, + unsigned char *result, size_t result_fill, + int flag); + + +/* Convert an array of Arbitrary Attributes into a series of AAIP fields. + @param aa_name The 2 byte SUSP Signature Word of the fields + @param num_attrs Number of attributes + @param names Array of pointers to 0 terminated name strings + @param value_lengths Array of byte lengths for each value + @param values Array of pointers to the value bytes + @param result_len Number of bytes in the resulting SUSP field string + @param result *result will point to the start of the result string. + This is malloc() memory which needs to be freed when + no longer needed + @param flag Bitfield for control purposes + bit0= set CONTINUE bit of last AA field to 1 + @return >0 is the number of SUSP fields generated, + 0 means error +*/ +unsigned int aaip_encode(char aa_name[2], + unsigned int num_attrs, char **names, + size_t *value_lengths, char **values, + size_t *result_len, unsigned char **result, int flag) +{ + size_t mem_size= 0, comp_size; + unsigned int number_of_fields, i, num_recs, total_recs= 0, ret; + + /* Predict memory needs, number of SUSP fields and component records */ + *result_len= 0; + for(i= 0; i < num_attrs; i++) { + ret= aaip_encode_pair(names[i], value_lengths[i], values[i], + &num_recs, &comp_size, NULL, (size_t) 0, 1); + if(ret <= 0) + return(ret); + mem_size+= comp_size; + total_recs= num_recs; + } + number_of_fields= mem_size / 250 + !!(mem_size % 250); + mem_size+= number_of_fields * 5; + +#ifdef Aaip_encode_debuG + *result= (unsigned char *) calloc(1, mem_size + 1024000); + /* generous honeypot for overflows */ +#else + *result= (unsigned char *) calloc(1, mem_size); +#endif + + + /* Encode pairs into result */ + for(i= 0; i < num_attrs; i++) { + ret= aaip_encode_pair(names[i], value_lengths[i], values[i], + &num_recs, &comp_size, *result, *result_len, 0); + if(ret <= 0) + return(ret); + (*result_len)+= comp_size; + } + + /* write the field headers */ + for(i= 0; i < number_of_fields; i++) { + (*result)[i * 255 + 0]= aa_name[0]; + (*result)[i * 255 + 1]= aa_name[1]; + if(i < number_of_fields - 1 || (mem_size % 255) == 0) + (*result)[i * 255 + 2]= 255; + else + (*result)[i * 255 + 2]= mem_size % 255; + (*result)[i * 255 + 3]= 1; + (*result)[i * 255 + 4]= (flag & 1) || (i < number_of_fields - 1); + } + (*result_len)+= number_of_fields * 5; + +#ifdef Aaip_encode_debuG + if(*result_len != mem_size) { + fprintf(stderr, "aaip_encode(): MEMORY MISMATCH BY %d BYTES\n", + (int) (mem_size - *result_len)); + } + ret= 0; + for(i= 0; i < *result_len; i+= ((unsigned char *) (*result))[i + 2]) + ret++; + if(ret != number_of_fields) { + fprintf(stderr, "aaip_encode(): WRONG NUMBER OF FIELDS %d <> %d\n", + number_of_fields, ret); + } +#endif /* Aaip_encode_debuG */ + + return(number_of_fields); +} + + +static void aaip_encode_byte(unsigned char *result, size_t *result_fill, + unsigned char value) +{ + result[(*result_fill / 250) * 255 + 5 + (*result_fill % 250)]= value; + (*result_fill)++; +} + + +static int aaip_encode_comp(unsigned char *result, size_t *result_fill, + char *data, size_t l, int flag) +{ + size_t todo; + char *rpt, *comp_start; + + if(l == 0) { + aaip_encode_byte(result, result_fill, 0); + aaip_encode_byte(result, result_fill, 0); + return(1); + } + for(rpt= data; rpt - data < l;) { + todo= l - (rpt - data); + aaip_encode_byte(result, result_fill, (todo > 255)); + if(todo > 255) + todo= 255; + aaip_encode_byte(result, result_fill, todo); + for(comp_start= rpt; rpt - comp_start < todo; rpt++) + aaip_encode_byte(result, result_fill, *((unsigned char *) rpt)); + } + return(1); +} + + +/* Write the component records for name and attr. Skip the positions of + AA field headers. + @param flag bit0= only count but do not produce result +*/ +static int aaip_encode_pair(char *name, size_t attr_length, char *attr, + unsigned int *num_recs, size_t *comp_size, + unsigned char *result, size_t result_fill, + int flag) +{ + size_t l; + + l= strlen(name); + *num_recs= l / 255 + (!!(l % 255)) + (l == 0) + + attr_length / 255 + (!!(attr_length % 255)) + (attr_length == 0); + *comp_size= l + attr_length + 2 * *num_recs; + + if(flag & 1) + return(1); + + aaip_encode_comp(result, &result_fill, name, l, 0); + aaip_encode_comp(result, &result_fill, attr, attr_length, 0); + return(1); +} + + +/* ----------- Encoder for ACLs ----------- */ + +static ssize_t aaip_encode_acl_text(char *acl_text, + size_t result_size, unsigned char *result, int flag); + + +/* Convert an ACL text as of acl_to_text(3) into the value of an Arbitrary + Attribute. According to AAIP 0.2 this value is to be stored together with + an empty name. + @param acl_text The ACL in long text form + @param result_len Number of bytes in the resulting value + @param result *result will point to the start of the result string. + This is malloc() memory which needs to be freed when + no longer needed + @param flag Bitfield for control purposes + bit0= count only + bit1= use numeric qualifiers rather than names + bit2= this is a default ACL, prepend SWITCH_MARK + @return >0 means ok + 0 means error +*/ +int aaip_encode_acl(char *acl_text, + size_t *result_len, unsigned char **result, int flag) +{ + ssize_t bytes; + + *result= NULL; + *result_len= 0; + bytes= aaip_encode_acl_text(acl_text, (size_t) 0, NULL, 1 | (flag & 6)); + if(bytes < 0) + return(0); + if(flag & 1) { + *result_len= bytes; + return(1); + } + *result= calloc(bytes + 1, 1); + if(*result == NULL) + return(-1); + (*result)[bytes]= 0; + *result_len= bytes; + bytes= aaip_encode_acl_text(acl_text, *result_len, *result, (flag & 6)); + if(bytes != *result_len) { + *result_len= 0; + return(0); + } + return(1); +} + + +static double aaip_numeric_id(char *name, int flag) +{ + double num; + char *cpt; + + for(cpt= name; *cpt != 0; cpt++) + if(*cpt < '0' || *cpt >'9') + break; + if(*cpt != 0) + return(-1); + sscanf(name, "%lf", &num); + return(num); +} + + +/* + @param result_size Number of bytes to store result + @param result Pointer to the start of the result string. + @param flag Bitfield for control purposes + bit0= count only, do not really produce bytes + bit1= use numeric qualifiers + bit2= this is a default ACL, prepend SWITCH_MARK 1 + @return >=0 number of bytes produced resp. counted + <0 means error +*/ +static ssize_t aaip_encode_acl_text(char *acl_text, + size_t result_size, unsigned char *result, int flag) +{ + char *rpt, *npt, *cpt; + int qualifier= 0, perms, type, i, qualifier_len= 0, num_recs; + uid_t uid, huid; + gid_t gid, hgid; + ssize_t count= 0; + struct passwd *pwd; + struct group *grp; + char name[1024]; + double num; + + if(flag & 4) { + /* set SWITCH_MARK to indicate a default ACL */; + if(!(flag & 1)) { + if(count >= result_size) + return(-1); + result[count]= (Aaip_SWITCH_MARK << 4) | Aaip_EXEC; + } + count++; + } + + for(rpt= acl_text; *rpt != 0; rpt= npt) { + npt= strchr(rpt, '\n'); + if(npt == 0) + npt= rpt + strlen(rpt); + else + npt++; + if(*rpt == '#') + continue; + cpt= strchr(rpt, ':'); + if(cpt == NULL) + continue; + cpt= strchr(cpt + 1, ':'); + if(cpt == NULL) + continue; + qualifier= 0; + if(strncmp(rpt, "user:", 5) == 0) { + if(cpt - rpt == 5) + type= Aaip_ACL_USER_OBJ; + else { + if(cpt - (rpt + 5) >= sizeof(name)) + continue; + strncpy(name, rpt + 5, cpt - (rpt + 5)); + name[cpt - (rpt + 5)]= 0; + if(flag & 2) { + type= Aaip_ACL_USER_N; + pwd= getpwnam(name); + if(pwd == NULL) { + num= aaip_numeric_id(name, 0); + if(num <= 0) + goto user_by_name; + uid= huid= num; + } else + uid= huid= pwd->pw_uid; + /* Convert uid into Qualifier Record */ + for(i= 0; huid != 0; i++) + huid= huid >> 8; + qualifier_len= i; + for(i= 0; i < qualifier_len ; i++) + name[i]= uid >> (8 * (qualifier_len - i - 1)); + } else { +user_by_name:; + type= Aaip_ACL_USER; + qualifier_len= strlen(name); + } + qualifier= 1; + } + } else if(strncmp(rpt, "group:", 6) == 0) { + if(cpt - rpt == 6) + type= Aaip_ACL_GROUP_OBJ; + else { + if(cpt - (rpt + 6) >= sizeof(name)) + continue; + strncpy(name, rpt + 6, cpt - (rpt + 6)); + if(flag & 2) { + type= Aaip_ACL_GROUP_N; + grp= getgrnam(name); + if(grp == NULL) { + num= aaip_numeric_id(name, 0); + if(num <= 0) + goto group_by_name; + gid= hgid= num; + } else + gid= hgid= grp->gr_gid; + /* Convert gid into Qualifier Record */ + for(i= 0; hgid != 0; i++) + hgid= hgid >> 8; + qualifier_len= i; + for(i= 0; i < qualifier_len ; i++) + name[i]= gid >> (8 * (qualifier_len - i - 1)); + + } else { +group_by_name:; + type= Aaip_ACL_GROUP; + qualifier_len= strlen(name); + } + qualifier= 1; + } + } else if(strncmp(rpt, "other:", 6) == 0) { + type= Aaip_ACL_OTHER; + } else if(strncmp(rpt, "mask:", 5) == 0) { + type= Aaip_ACL_MASK; + } else + continue; + + if(npt - cpt < 3) + continue; + perms= 0; + if(cpt[1] == 'r') + perms|= Aaip_READ; + if(cpt[2] == 'w') + perms|= Aaip_WRITE; + if(cpt[3] == 'x') + perms|= Aaip_EXEC; + + if(!(flag & 1)) { + if(count >= result_size) + return(-1); + result[count]= perms | ((!!qualifier) << 3) | (type << 4); + } + count++; + + if(qualifier) { + num_recs= (qualifier_len / 127) + !!(qualifier_len % 127); + if(!(flag & 1)) { + if(count + 1 > result_size) + return(-1); + for(i= 0; i < num_recs; i++) { + if(i < num_recs - 1) + result[count++]= 255; + else { + result[count++]= (qualifier_len % 127); + if(result[count - 1] == 0) + result[count - 1]= 127; + } + if(count + (result[count - 1] & 127) > result_size) + return(-1); + memcpy(result + count, name + i * 127, result[count - 1] & 127); + count+= result[count - 1] & 127; + } + } else + count+= qualifier_len + num_recs; + } + } + + return(count); +} + + +/* --------------------------------- Decoder ---------------------------- */ + +/* --- private --- */ + +/* Not less than 2 * 2048 */ +#define Aaip_buffer_sizE 4096 + +/* Enough for one full component record and three empty ones which might get + added in case of unclean end of attribute list. +*/ +#define Aaip_buffer_reservE (257 + 3 * 2) + + +struct aaip_state { + + /* AA field status */ + unsigned char aa_name[2]; + int aa_head_missing; /* number of bytes needed to complete AA field header */ + int aa_missing; /* number of bytes needed to complete current AA field */ + int aa_ends; /* 0= still AA fields expected, 1= last AA being processed, + 2= all AA fields processed, 3= all is delivered */ + + /* Buffer for component records */ + int recs_invalid; /* number of components to skip */ + unsigned char recs[Aaip_buffer_sizE + Aaip_buffer_reservE]; + size_t recs_fill; + unsigned char *recs_start; + int rec_head_missing; /* number of bytes needed to complete rec header */ + int rec_missing; /* number of bytes needed to complete current rec */ + int rec_ends; + + /* Counter for completed data */ + unsigned int num_recs; + size_t ready_bytes; + + /* Counter and meaning for completed components */ + unsigned int num_components; + size_t end_of_components; /* start index of eventual incomplete component */ + int first_is_name; + + /* Last return value of aaip_decode_pair() */ + int pair_status; + unsigned int pairs_skipped; + + /* status of aaip_decode_attrs() */ + size_t list_mem_used; + size_t list_size; + size_t list_num_attrs; + char **list_names; + size_t *list_value_lengths; + char **list_values; + char *name_buf; + size_t name_buf_size; + size_t name_buf_fill; + char *value_buf; + size_t value_buf_size; + size_t value_buf_fill; + int list_pending_pair; +}; + + +/* ------- functions ------ */ + + +size_t aaip_sizeof_aaip_state(void) +{ + return((size_t) sizeof(struct aaip_state)); +} + + +int aaip_init_aaip_state(struct aaip_state *aaip, char aa_name[2], int flag) +{ + aaip->aa_name[0]= aa_name[0]; + aaip->aa_name[1]= aa_name[1]; + aaip->aa_head_missing= 5; + aaip->aa_missing= 0; + + aaip->recs_invalid= 0; + memset(aaip->recs, 0, Aaip_buffer_sizE + Aaip_buffer_reservE); + aaip->recs_fill= 0; + aaip->recs_start= aaip->recs; + aaip->rec_head_missing= 2; + aaip->rec_missing= 0; + aaip->rec_ends= 0; + + aaip->num_recs= 0; + aaip->ready_bytes= 0; + + aaip->num_components= 0; + aaip->end_of_components= 0; + aaip->first_is_name= 1; + + aaip->pair_status= 2; + aaip->pairs_skipped= 0; + + aaip->list_mem_used= 0; + aaip->list_size= 0; + aaip->list_num_attrs= 0; + aaip->list_names= NULL; + aaip->list_value_lengths= NULL; + aaip->list_values= NULL; + aaip->name_buf= NULL; + aaip->name_buf_size= 0; + aaip->name_buf_fill= 0; + aaip->value_buf= NULL; + aaip->value_buf_size= 0; + aaip->value_buf_fill= 0; + aaip->list_pending_pair= 0; + return(1); +} + +/* +*/ +#define Aaip_with_ring_buffeR yes + +#ifdef Aaip_with_ring_buffeR + +/* Compute the one or two byte intervals in the ring buffer which form a + given byte interval in the virtual shift fifo. + @param idx The byte start index in the virtual shift fifo. + @param todo Number of bytes to cover + @param start_pt Will return the start address of the first interval + @param at_start_pt Will return the size of the first interval + @param at_recs Will return the size of the second interval which + always starts at aaip->recs + @param flag Bitfield for control purposes + @return 1= next start_pt is *start_pt + *at_start_pt + 2= next start_pt is aaip->recs + *at_recs +*/ +static int aaip_ring_adr(struct aaip_state *aaip, size_t idx, size_t todo, + unsigned char **start_pt, size_t *at_start_pt, + size_t *at_recs, int flag) +{ + size_t ahead; + + ahead= Aaip_buffer_sizE + Aaip_buffer_reservE + - (aaip->recs_start - aaip->recs); + if(idx < ahead) + *start_pt= (aaip->recs_start + idx); + else + *start_pt= aaip->recs + (idx - ahead); + ahead= Aaip_buffer_sizE + Aaip_buffer_reservE - (*start_pt - aaip->recs); + if(todo >= ahead) { + *at_start_pt= ahead; + *at_recs= todo - ahead; + return(2); + } + *at_start_pt= todo; + *at_recs= 0; + return(1); +} + + +/* + @param flag Bitfield for control purposes + bit0= count as ready_bytes +*/ +static int aaip_push_to_recs(struct aaip_state *aaip, unsigned char *data, + size_t todo, int flag) +{ + unsigned char *start_pt; + size_t at_start_pt, at_recs; + + aaip_ring_adr(aaip, aaip->recs_fill, todo, + &start_pt, &at_start_pt, &at_recs, 0); + if(at_start_pt > 0) + memcpy(start_pt, data, at_start_pt); + if(at_recs > 0) + memcpy(aaip->recs, data + at_start_pt, at_recs); + aaip->recs_fill+= todo; + if(flag & 1) + aaip->ready_bytes+= todo; + return(1); +} + + +static int aaip_read_from_recs(struct aaip_state *aaip, size_t idx, + unsigned char *data, size_t num_data, int flag) +{ + unsigned char *start_pt; + size_t at_start_pt, at_recs; + + aaip_ring_adr(aaip, idx, num_data, + &start_pt, &at_start_pt, &at_recs, 0); + if(at_start_pt > 0) + memcpy(data, start_pt, at_start_pt); + if(at_recs > 0) + memcpy(data + at_start_pt, aaip->recs, at_recs); + return(1); +} + + +static int aaip_set_buffer_byte(struct aaip_state *aaip, size_t idx, + unsigned char data, int flag) +{ + unsigned char *start_pt; + size_t at_start_pt, at_recs; + + aaip_ring_adr(aaip, idx, 1, + &start_pt, &at_start_pt, &at_recs, 0); + *start_pt= data; + return(1); +} + + +static int aaip_get_buffer_byte(struct aaip_state *aaip, size_t idx, int flag) +{ + unsigned char *start_pt; + size_t at_start_pt, at_recs; + + aaip_ring_adr(aaip, idx, 1, + &start_pt, &at_start_pt, &at_recs, 0); + return((int) *start_pt); +} + + +static int aaip_shift_recs(struct aaip_state *aaip, size_t todo, int flag) +{ + int ret; + unsigned char *start_pt; + size_t at_start_pt, at_recs; + + if(todo < aaip->recs_fill) { + ret= aaip_ring_adr(aaip, 0, todo, &start_pt, &at_start_pt, &at_recs, 0); + if(ret == 1) + aaip->recs_start= start_pt + todo; + else + aaip->recs_start= aaip->recs + at_recs; + } else { + aaip->recs_start= aaip->recs; + } + aaip->recs_fill-= todo; + if(aaip->end_of_components >= todo) + aaip->end_of_components-= todo; + else + aaip->end_of_components= 0; + return(1); +} + + +#else /* Aaip_with_ring_buffeR */ + + +/* + @param flag Bitfield for control purposes + bit0= count as ready_bytes +*/ +static int aaip_push_to_recs(struct aaip_state *aaip, unsigned char *data, + size_t todo, int flag) +{ + memcpy(aaip->recs + aaip->recs_fill, data, todo); + aaip->recs_fill+= todo; + if(flag & 1) + aaip->ready_bytes+= todo; + return(1); +} + + +static int aaip_read_from_recs(struct aaip_state *aaip, size_t idx, + unsigned char *data, size_t num_data, int flag) +{ + memcpy(data, aaip->recs + idx, num_data); + return(1); +} + + +static int aaip_set_buffer_byte(struct aaip_state *aaip, size_t idx, + unsigned char data, int flag) +{ + aaip->recs[idx]= data; + return(1); +} + + +static int aaip_get_buffer_byte(struct aaip_state *aaip, size_t idx, int flag) +{ + return((int) aaip->recs[idx]); +} + + +static int aaip_shift_recs(struct aaip_state *aaip, size_t todo, int flag) +{ + if(todo < aaip->recs_fill) + memmove(aaip->recs, aaip->recs + todo, aaip->recs_fill - todo); + aaip->recs_fill-= todo; + + if(aaip->end_of_components >= todo) + aaip->end_of_components-= todo; + else + aaip->end_of_components= 0; + return(1); +} + + +#endif /* ! Aaip_with_ring_buffeR */ + + +static int aaip_consume_rec_head(struct aaip_state *aaip, + unsigned char **data, size_t *num_data, int flag) +{ + size_t todo; + + todo= *num_data; + if(todo > aaip->aa_missing) + todo= aaip->aa_missing; + if(todo >= aaip->rec_head_missing) + todo= aaip->rec_head_missing; + if(!aaip->recs_invalid) + aaip_push_to_recs(aaip, *data, todo, 0); + aaip->rec_head_missing-= todo; + if(aaip->rec_head_missing == 0) { + aaip->rec_missing= aaip_get_buffer_byte(aaip, aaip->recs_fill - 1, 0); + aaip->rec_ends= !(aaip_get_buffer_byte(aaip, aaip->recs_fill - 2, 0) & 1); + } + aaip->aa_missing-= todo; + (*num_data)-= todo; + (*data)+= todo; + return(1); +} + + +static int aaip_consume_rec_data(struct aaip_state *aaip, + unsigned char **data, size_t *num_data, int flag) +{ + size_t todo; + + todo= *num_data; + if(todo > aaip->aa_missing) + todo= aaip->aa_missing; + if(todo > aaip->rec_missing) + todo= aaip->rec_missing; + if(!aaip->recs_invalid) + aaip_push_to_recs(aaip, *data, todo, 1); + aaip->rec_missing-= todo; + aaip->aa_missing-= todo; + (*num_data)-= todo; + (*data)+= todo; + if(aaip->rec_missing <= 0) { + if(aaip->recs_invalid > 0) { + if(aaip->rec_ends) + aaip->recs_invalid--; + } else { + aaip->num_recs++; + if(aaip->rec_ends) { + aaip->num_components++; + aaip->end_of_components= aaip->recs_fill; + } + } + aaip->rec_head_missing= 2; + } + return(0); +} + + +static int aaip_consume_aa_head(struct aaip_state *aaip, + unsigned char **data, size_t *num_data, int flag) +{ + size_t todo; + unsigned char aa_head[5]; + + todo= *num_data; + if(todo >= aaip->aa_head_missing) + todo= aaip->aa_head_missing; + aaip_push_to_recs(aaip, *data, todo, 0); + aaip->aa_head_missing-= todo; + if(aaip->aa_head_missing == 0) { + aaip_read_from_recs(aaip, aaip->recs_fill - 5, aa_head, 5, 0); + if(aa_head[0] != aaip->aa_name[0] || aa_head[1] != aaip->aa_name[1] || + aa_head[3] != 1) + return(-1); + aaip->aa_missing= aa_head[2]; + aaip->aa_ends= !(aa_head[4] & 1); + aaip->recs_fill-= 5; /* AA heads do not get delivered */ + if(aaip->aa_missing >= 5) + aaip->aa_missing-= 5; + else + aaip->aa_missing= 0; + } + (*num_data)-= todo; + (*data)+= todo; + return(1); +} + + +static int aaip_consume_aa_data(struct aaip_state *aaip, + unsigned char **data, size_t *num_data, int flag) +{ + size_t i; + static unsigned char zero_char[2]= {0, 0}; + + while(*num_data > 0 && aaip->aa_missing > 0) { + if(aaip->rec_head_missing > 0) { + aaip_consume_rec_head(aaip, data, num_data, 0); + if(*num_data == 0 || aaip->aa_missing <= 0) + return(1); + } + aaip_consume_rec_data(aaip, data, num_data, 0); + } + if(aaip->aa_missing <= 0) { + if(aaip->aa_ends) { + /* Check for incomplete pair and eventually make emergency closure */ + if(aaip->rec_head_missing != 2) { /* incomplete record detected */ + if(aaip->rec_head_missing) { + /* fake 0 length record */ + aaip_set_buffer_byte(aaip, aaip->recs_fill - 1, (unsigned char) 0, 0); + aaip_push_to_recs(aaip, zero_char, 1, 0); + } else { + /* fill in missing btes */ + for(i= 0; i < aaip->rec_missing; i++) + aaip_push_to_recs(aaip, zero_char, 1, 1); + } + aaip->rec_head_missing= 2; + aaip->rec_missing= 0; + aaip->num_recs++; + if(aaip->rec_ends) { + aaip->num_components++; + aaip->end_of_components= aaip->recs_fill; + } + } + if(aaip->end_of_components != aaip->recs_fill && + aaip->end_of_components != 0) { + /* incomplete component detected */ + /* add empty end record */ + aaip_push_to_recs(aaip, zero_char, 2, 0); + aaip->num_recs++; + aaip->num_components++; + aaip->end_of_components= aaip->recs_fill; + } + if(!(aaip->first_is_name ^ (aaip->num_components % 2))) { + /* value component is missing */ + /* add dummy component */ + aaip_push_to_recs(aaip, zero_char, 2, 0); + aaip->num_recs++; + aaip->num_components++; + aaip->end_of_components= aaip->recs_fill; + } + aaip->aa_ends= 2; + } else + aaip->aa_head_missing= 5; + } + return(0); +} + + +/* Submit small data chunk for decoding. + The return value will tell whether data are pending for being fetched. + @param aaip The AAIP decoder context + @param data Not more than 2048 bytes input for the decoder + @parm num_data Number of bytes in data + 0 inquires the buffer status avoiding replies <= 0 + @param ready_bytes Number of decoded bytes ready for delivery + @param flag Bitfield for control purposes + @return -1= non-AA field detected + *ready_bytes gives number of consumed bytes in data + 0= cannot accept data because buffer full + 1= no component record complete, submit more data + 2= component record complete, may be delivered + 3= component complete, may be delivered + 4= no component available, no more data expected, done +*/ +int aaip_submit_data(struct aaip_state *aaip, + unsigned char *data, size_t num_data, + size_t *ready_bytes, int flag) +{ + int ret; + unsigned char *in_data; + + if(aaip->aa_ends == 3) + return(4); + in_data= data; + if(num_data == 0) + goto ex; + if(aaip->recs_fill + num_data > Aaip_buffer_sizE) + return(0); + + while(num_data > 0) { + if(aaip->aa_head_missing > 0) { + ret= aaip_consume_aa_head(aaip, &data, &num_data, 0); + if(ret < 0) { + *ready_bytes= data - in_data; + return(-1); + } + if(num_data == 0 || aaip->aa_missing <= 0) + goto ex; + } + aaip_consume_aa_data(aaip, &data, &num_data, 0); + if(aaip->aa_missing) + break; + } +ex:; + *ready_bytes= aaip->ready_bytes; + if(aaip->num_components > 0) + return(3); + if(aaip->num_recs > 0) + return(2); + if(aaip->aa_ends && aaip->aa_head_missing == 0 && aaip->aa_missing == 0) + aaip->aa_ends= 2; + if(aaip->aa_ends == 2 && aaip->num_recs == 0) + aaip->aa_ends= 3; + if(aaip->aa_ends == 3) + return(4); + return(1); +} + + +/* Fetch the available part of current component. + The return value will tell whether it belongs to name or to value and + whether that name or value is completed now. + @param aaip The AAIP decoder context + @param result Has to point to storage for the component data + @param result_size Gives the amount of provided result storage + @param num_result Will tell the number of fetched result bytes + @param flag Bitfield for control purposes + bit0= discard data rather than copying to result + @return -2 = insufficient result_size + -1 = no data ready for delivery + 0 = result holds the final part of a name + 1 = result holds an intermediate part of a name + 2 = result holds the final part of a value + 3 = result holds an intermediate part of a value +*/ +int aaip_fetch_data(struct aaip_state *aaip, + char *result, size_t result_size, size_t *num_result, + int flag) +{ + int ret= -1, complete= 0, payload; + unsigned int i, num_bytes= 0, h; + + if(aaip->num_recs == 0) + return(-1); + + /* Copy data until end of buffer or end of component */ + h= 0; + for(i= 0; i < aaip->num_recs && !complete; i++) { + payload= aaip_get_buffer_byte(aaip, h + 1, 0); + if(!(flag & 1)) { + if(num_bytes + payload > result_size) + return(-2); + aaip_read_from_recs(aaip, h + 2, (unsigned char *) (result + num_bytes), + payload, 0); + *num_result= num_bytes + payload; + } + num_bytes+= payload; + if(!(aaip_get_buffer_byte(aaip, h, 0) & 1)) + complete= 1; + h+= payload + 2; + } + aaip->ready_bytes-= num_bytes; + aaip->num_recs-= i; + + /* Shift buffer */ + aaip_shift_recs(aaip, h, 0); + + /* Compute reply */ + ret= 2 * !aaip->first_is_name; + if(complete) { + aaip->first_is_name= !aaip->first_is_name; + if(aaip->num_components > 0) + aaip->num_components--; + } else + ret|= 1; + + return(ret); +} + + +/* Skip the current component and eventually the following value component. + This has to be called if fetching of a component shall be aborted + but the next component resp. pair shall be fetchable again. + aaip_submit_data() will not indicate readiness for fetching until all + bytes of the skipped components are submitted. Those bytes get discarded. + @param aaip The AAIP decoder context + @param flag Bitfield for control purposes + bit0= do not skip value if current component is name + @return <=0 error , 1= now in skip state, 2= not in skip state +*/ +int aaip_skip_component(struct aaip_state *aaip, int flag) +{ + int to_skip= 1; + + if(aaip->first_is_name && !(flag & 1)) + to_skip= 2; + if(aaip->recs_invalid) { + aaip->recs_invalid+= to_skip; + return(1); + } + + if(aaip->num_components) { + /* null-fetch */ + aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); + to_skip--; + } + if(aaip->num_components && to_skip) { + /* null-fetch */ + aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); + to_skip--; + } + if(to_skip) { + aaip->recs_fill= 0; + aaip->num_recs= 0; + aaip->ready_bytes= 0; + } + aaip->recs_invalid= to_skip; + if(aaip->aa_ends == 2 && aaip->num_recs == 0) + aaip->aa_ends= 3; + return(1 + (aaip->num_recs > 0)); +} + + +/* ------------------------- Pair Level Interface ------------------------ */ + +/* + @param flag Bitfield for control purposes + bit0= do not skip oversized component but return -2 + @return see aaip_decode_pair +*/ +static int aaip_advance_pair(struct aaip_state *aaip, + char *name, size_t name_size, size_t *name_fill, + char *value, size_t value_size, size_t *value_fill, + int flag) +{ + int ret; + char *wpt; + size_t size, num; + +retry:; + if(aaip->first_is_name) { + wpt= name + *name_fill; + size= name_size - *name_fill; + } else { + wpt= value + *value_fill; + size= value_size - *value_fill; + } + ret= aaip_fetch_data(aaip, wpt, size, &num, 0); + if(ret == -2) { /* insufficient result size */ + if(flag & 1) + return(-2); + ret= aaip_skip_component(aaip, 0); + *name_fill= *value_fill= 0; + aaip->pairs_skipped++; + if(ret == 2) /* Skip performed, valid data pending */ + goto retry; + } else if(ret == -1) { /* No data ready for delivery : may not happen */ + return(-1); + } else if(ret == 0) { /* result holds the final part of a name */ + (*name_fill)+= num; + /* peek for value data */ + ret= aaip_submit_data(aaip, NULL, (size_t) 0, &num, 0); + if(ret == 2 || ret == 3) { + /* fetch value data */; + ret= aaip_advance_pair(aaip, name, name_size, name_fill, + value, value_size, value_fill, flag); + return ret; + } else if(ret == 4) + return(5); + } else if(ret == 1) { /* result holds an intermediate part of a name */ + (*name_fill)+= num; + } else if(ret == 2) { /* result holds the final part of a value */ + (*value_fill)+= num; + if(aaip->num_components >= 2) + return(3); + if(aaip->aa_ends == 2 && aaip->num_recs == 0) + aaip->aa_ends= 3; + if(aaip->aa_ends == 3) + return(4); + return(2); + } else if(ret == 3) { + /* result holds an intermediate part of a value */; + (*value_fill)+= num; + } else { + return(-1); /* unknown reply from aaip_fetch_data() */ + } + return(1); +} + + +/* Accept raw input data and collect a pair of name and value. + The return value will indicate whether the pair is complete, whether more + pairs are complete or whether more data are desired. No input data will be + accepted as long as complete pairs are pending. The end of the attribute + list will be indicated. + @param aaip The AAIP decoder context + @param data The raw data to decode + @param num_data Number of data bytes provided + @param consumed Returns the number of consumed data bytes + @param name Buffer to build the name string + @param name_size Maximum number of bytes in name + @param name_fill Holds the current buffer fill of name + @param value Buffer to build the value string + @param value_size Maximum number of bytes in value + @param value_fill Holds the current buffer fill of value + @param flag Bitfield for control purposes + bit0= do not skip oversized pair but return -2 + @return <0 error + -3 buffer full (program error) + -2 insufficient result_size (only with flag bit0) + -1 non-AA field detected + 0 data not accepted, first fetch pending pairs with num_data == 0 + 1 name and value are not valid yet, submit more data + 2 name and value are valid, submit more data + 3 name and value are valid, pairs pending, fetch with num_data == 0 + 4 name and value are valid, no more data expected + 5 name and value are not valid, no more data expected +*/ +int aaip_decode_pair(struct aaip_state *aaip, + unsigned char *data, size_t num_data, size_t *consumed, + char *name, size_t name_size, size_t *name_fill, + char *value, size_t value_size, size_t *value_fill, + int flag) +{ + int ret; + size_t ready_bytes; + + *consumed= 0; + if((aaip->pair_status < 0 && aaip->pair_status != -2) || + aaip->pair_status == 4 || + aaip->pair_status == 5) { /* dead ends */ + ret= aaip->pair_status; + goto ex; + } else if(aaip->pair_status == 2 || aaip->pair_status == 3) { + if(aaip->pair_status == 3 && num_data > 0) + {ret= 0; goto ex;} + /* Start a new pair */ + if(!aaip->first_is_name) /* Eventually skip orphaned value */ + aaip_fetch_data(aaip, NULL, (size_t) 0, NULL, 1); + *name_fill= *value_fill= 0; + } + + if(num_data > 0) { + ret= aaip_submit_data(aaip, data, num_data, &ready_bytes, 0); + } else { + ret= 1; + if(aaip->num_components) + ret= 3; + else if(aaip->num_recs) + ret= 2; + } + if(ret < 0) { /* non-AA field detected */ + *consumed= ready_bytes; + {ret= -1; goto ex;} + } else if(ret == 0) { /* buffer overflow */; + /* should not happen with correct usage */ + {ret= -3; goto ex;} + } else if(ret == 1) { /* no component record complete */ + goto ex; + } else if(ret == 2) { /* component record complete, may be delivered */ + ; + } else if(ret == 3) { /* component complete, may be delivered */ + ; + } else if(ret == 4) { /* no component available, no more data expected */ + {ret= 5; goto ex;} + } else + {ret= -1; goto ex;} /* unknown reply from aaip_submit_data() */ + + *consumed= num_data; + ret= aaip_advance_pair(aaip, name, name_size, name_fill, + value, value_size, value_fill, flag & 1); + if(aaip->aa_ends == 3) { + if(ret >= 2 && ret <= 4) + ret= 4; + else + ret= 5; + } +ex:; + aaip->pair_status= ret; + return(ret); +} + + +unsigned int aaip_get_pairs_skipped(struct aaip_state *aaip, int flag) +{ + return(aaip->pairs_skipped); +} + + +/* ------------------------- List Level Interface ------------------------ */ + + +#define Aaip_initial_name_leN 256 +#define Aaip_initial_value_leN 256 +#define Aaip_initial_list_sizE 2 +#define Aaip_list_enlargeR 1.5 + + +/* + @param flag Bitfield for control purposes + bit0= do not update *buf_size +*/ +static int aaip_enlarge_buf(struct aaip_state *aaip, size_t memory_limit, + size_t item_size, char **buf, size_t *buf_size, int flag) +{ + size_t new_size; + char *new_buf; + + new_size= *buf_size * Aaip_list_enlargeR; + if(aaip->list_mem_used + (new_size - *buf_size) * item_size >= memory_limit) + return(3); + aaip->list_mem_used+= (new_size - *buf_size) * item_size; + new_buf= realloc(*buf, new_size * item_size); + if(new_buf == NULL) + return(-1); + *buf= new_buf; + if(!(flag & 1)) + *buf_size= new_size; + return(1); +} + + +/* Accept raw input data and collect arrays of name pointers, value lengths + and value pointers. A handle object will emerge which finally has to be + be freed by a call with bit 15. + @param handle The decoding context. + It will be created by this call with flag bit 0 or if + *handle == NULL. This handle has to be the same as long + as decoding goes on and finally has to be freed by a + call with bit15. + @param aa_name The Signature Word (advised is "AA") + @param memory_limit Maximum number of bytes to allocate + @param num_attr_limit Maximum number of name-value pairs to allocate + @param data The raw data to decode + @param num_data Number of data bytes provided + @param consumed Returns the number of consumed data bytes + @param flag Bitfield for control purposes + bit0= this is the first call with the given handle + (also in effect if *handle is NULL) + bit15= end decoding : + Free handle and its intermediate list memory. + @return <=0 error + -3 program error, unexpected reply from lower layers + -2 non-AA-field detected, arrays are complete, + call aaip_get_decoded_attrs() + -1 out of memory + 1 not complete yet, submit more data + 2 arrays are complete, call aaip_get_decoded_attrs() + 3 limit exceeded, not complete yet, + enlarge memory_limit or call with bit15 and give up + 4 limit exceeded, call aaip_get_decoded_attrs() and try again +*/ +int aaip_decode_attrs(struct aaip_state **handle, char aa_name[2], + size_t memory_limit, size_t num_attr_limit, + unsigned char *data, size_t num_data, size_t *consumed, + int flag) +{ + int ret, was_non_aa= 0; + struct aaip_state *aaip; + size_t h_num, *h_lengths, i, new_mem, pair_consumed= 0; + char **h_names, **h_values, *hpt; + + *consumed= 0; + if(flag & (1 << 15)) { + if(*handle == NULL) + return(0); + ret= aaip_get_decoded_attrs(handle, &h_num, &h_names, &h_lengths, &h_values, + 0); + if(ret > 0) + aaip_get_decoded_attrs(handle, &h_num, &h_names, &h_lengths, &h_values, + 1 << 15); + if((*handle)->name_buf != NULL) + free((*handle)->name_buf); + if((*handle)->value_buf != NULL) + free((*handle)->value_buf); + free((char *) *handle); + *handle= NULL; + return(1); + } + + aaip= *handle; + if(aaip == NULL || (flag & 1)) { + aaip= *handle= calloc(1, sizeof(struct aaip_state)); + if(*handle == NULL) + return(-1); + aaip_init_aaip_state(*handle, aa_name, 0); + } + if(aaip->list_names == NULL || aaip->list_values == NULL || + aaip->list_value_lengths == NULL) { + /* Initialize arrays */ + aaip->list_size= Aaip_initial_list_sizE; + if(num_attr_limit > 0 && num_attr_limit < aaip->list_size) + aaip->list_size= num_attr_limit; + new_mem= aaip->list_size * (2*sizeof(char *) + sizeof(size_t)) + + Aaip_initial_name_leN + Aaip_initial_value_leN; + if(aaip->list_mem_used + new_mem >= memory_limit) + return(3); + aaip->list_mem_used+= new_mem; + aaip->list_names= calloc(sizeof(char *), aaip->list_size); + aaip->list_value_lengths= calloc(sizeof(size_t), aaip->list_size); + aaip->list_values= calloc(sizeof(char *), aaip->list_size); + if(aaip->list_names == NULL || aaip->list_value_lengths == NULL || + aaip->list_values == NULL) + return(-1); + for(i= 0; i < aaip->list_size; i++) { + aaip->list_names[i]= NULL; + aaip->list_value_lengths[i]= 0; + aaip->list_values[i]= NULL; + } + } + if(aaip->name_buf == NULL || aaip->value_buf == NULL) { + new_mem= Aaip_initial_name_leN + Aaip_initial_value_leN; + if(aaip->list_mem_used >= memory_limit) + return(3); + aaip->list_mem_used+= new_mem; + aaip->name_buf= calloc(sizeof(char *), Aaip_initial_name_leN); + aaip->value_buf= calloc(sizeof(char *), Aaip_initial_value_leN); + if(aaip->name_buf == NULL || aaip->value_buf == NULL) + return(-1); + aaip->name_buf_size= Aaip_initial_name_leN; + aaip->value_buf_size= Aaip_initial_name_leN; + } + + while(1) { + if(aaip->list_pending_pair > 0) { + /* the buffer holds a complete pair from a previous memory limit refusal */ + ret= aaip->list_pending_pair; + aaip->list_pending_pair= 0; + } else { + ret= aaip_decode_pair(aaip, data, num_data, &pair_consumed, + aaip->name_buf, aaip->name_buf_size, &aaip->name_buf_fill, + aaip->value_buf, aaip->value_buf_size, &aaip->value_buf_fill, + 1); + *consumed+= pair_consumed; + } + if(ret == -2) { /* insufficient result_size */ + if(aaip->first_is_name) + ret= aaip_enlarge_buf(aaip, memory_limit, (size_t) 1, &(aaip->name_buf), + &(aaip->name_buf_size), 0); + else + ret= aaip_enlarge_buf(aaip, memory_limit, (size_t) 1, + &(aaip->value_buf), &(aaip->value_buf_size), 0); + if(ret != 1) + return(ret); + + } else if(ret == -1) { /* non-AA field detected */ + was_non_aa= 1; + + } else if(ret < 0) { /* other error */ + return(-3); + + } else if(ret == 0) { /* first fetch pending pairs with num_data == 0 */ + /* should not happen, fetch more pairs */; + + } else if(ret == 1) { + /* name and value are not valid yet, submit more data */ + return(1); + + } else if(ret == 2 || ret == 3 || ret == 4) { + /* name and value are valid, submit more data */ + /* name and value are valid, pairs pending, fetch with num_data == 0 */ + /* name and value are valid, no more data expected */ + aaip->list_pending_pair= ret; + + if(aaip->list_num_attrs >= aaip->list_size) { + hpt= (char *) aaip->list_names; + ret= aaip_enlarge_buf(aaip, memory_limit, sizeof(char *), + &hpt, &(aaip->list_size), 1); + if(ret != 1) + return(ret); + aaip->list_names= (char **) hpt; + hpt= (char *) aaip->list_values; + ret= aaip_enlarge_buf(aaip, memory_limit, sizeof(char *), + &hpt, &(aaip->list_size), 1); + if(ret != 1) + return(ret); + aaip->list_values= (char **) hpt; + hpt= (char *) aaip->list_value_lengths; + ret= aaip_enlarge_buf(aaip, memory_limit, sizeof(size_t), + &hpt, &(aaip->list_size), 0); + if(ret != 1) + return(ret); + aaip->list_value_lengths= (size_t *) hpt; + } + + /* Allocate name and value in list */; + if(aaip->list_mem_used + aaip->name_buf_fill + aaip->value_buf_fill + 2 + > memory_limit) { + return(3); + } + aaip->list_mem_used+= aaip->name_buf_fill + aaip->value_buf_fill + 2; + i= aaip->list_num_attrs; + aaip->list_names[i]= calloc(aaip->name_buf_fill + 1, 1); + aaip->list_values[i]= calloc(aaip->value_buf_fill + 1, 1); + memcpy(aaip->list_names[i], aaip->name_buf, aaip->name_buf_fill); + aaip->list_names[i][aaip->name_buf_fill]= 0; + memcpy(aaip->list_values[i], aaip->value_buf, aaip->value_buf_fill); + aaip->list_values[i][aaip->value_buf_fill]= 0; + aaip->list_value_lengths[i]= aaip->value_buf_fill; + aaip->list_num_attrs++; + aaip->name_buf_fill= aaip->value_buf_fill= 0; + + ret= aaip->list_pending_pair; + aaip->list_pending_pair= 0; + + if(ret == 2) + return(1); + if(ret == 4) + break; + + } else if(ret == 5) + break; + else + return(-2); + + num_data= 0; /* consume pending pairs */ + } + aaip->list_pending_pair= 5; + return(2); +} + + +/* Obtain the resulting attributes when aaip_decode_attrs() indicates to + be done or to have the maximum possible amount of result ready. + The returned data objects finally have to be freed by a call with flag + bit 15. + @param handle The decoding context created by aaip_decode_attrs() + @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 + bit15= free memory of names, value_lengths, values + @return <0 error + 0 no attribute list ready + 1 ok +*/ +int aaip_get_decoded_attrs(struct aaip_state **handle, size_t *num_attrs, + char ***names, size_t **value_lengths, char ***values, + int flag) +{ + size_t i; + struct aaip_state *aaip; + + aaip= *((struct aaip_state **) handle); + if(flag & (1 << 15)) { + if(*names != NULL) { + for(i= 0; i < *num_attrs; i++) { + if((*names)[i] != NULL) + free((*names)[i]); + (*names)[i]= NULL; + } + free(*names); + *names= NULL; + } + if(*values != NULL) { + for(i= 0; i < *num_attrs; i++) { + if((*values)[i] != NULL) + free((*values)[i]); + (*values)[i]= NULL; + } + free(*values); + *values= NULL; + } + if(*value_lengths != NULL) + free(*value_lengths); + *value_lengths= NULL; + *num_attrs= 0; + return(1); + } + + /* Check whether decoding is finished yet */ + if(aaip->list_pending_pair != 5) + return(0); + + *num_attrs= aaip->list_num_attrs; + *names= aaip->list_names; + *value_lengths= aaip->list_value_lengths; + *values= aaip->list_values; + + /* Now the memory is owned by the caller */ + aaip->list_num_attrs= 0; + aaip->list_names= NULL; + aaip->list_value_lengths= NULL; + aaip->list_values= NULL; + aaip->list_size= 0; + aaip->list_pending_pair= 0; + return(1); +} + + +/* ------ Decoder for ACLs ------ */ + + +static int aaip_write_acl_line(char **result, size_t *result_size, + char *tag_type, char *qualifier, + char *permissions, int flag) +{ + size_t needed, tag_len, perm_len, qualifier_len; + + tag_len= strlen(tag_type); + qualifier_len= strlen(qualifier); + perm_len= strlen(permissions); + needed= tag_len + qualifier_len + perm_len + 3; + if((flag & 1)) { + (*result_size)+= needed; + return(1); + } + if(needed + 1 > *result_size) /* +1 : want to append a trailing 0 */ + return(-1); + memcpy((*result), tag_type, tag_len); + (*result)[tag_len]= ':'; + memcpy((*result) + tag_len + 1, qualifier, qualifier_len); + (*result)[tag_len + 1 + qualifier_len]= ':'; + memcpy((*result) + tag_len + 1 + qualifier_len + 1, permissions, perm_len); + (*result)[tag_len + 1 + qualifier_len + 1 + perm_len]= '\n'; + (*result)[tag_len + 1 + qualifier_len + 1 + perm_len + 1] = 0; + (*result)+= needed; + (*result_size)-= needed; + return(1); +} + + +static int aaip_read_qualifier(unsigned char *data, size_t num_data, + char *name, size_t name_size, size_t *name_fill, + int flag) +{ + int is_done= 0, rec_len= 0; + unsigned char *rpt; + + *name_fill= 0; + for(rpt= data; !is_done; rpt+= rec_len) { + rec_len= (*rpt) & 127; + is_done= !((*rpt) & 128); + if(*name_fill + rec_len >= name_size || rpt + 1 + rec_len - data > num_data) + return(-1); + memcpy(name + *name_fill, rpt + 1, rec_len); + rpt+= 1 + rec_len; + (*name_fill)+= rec_len; + name[*name_fill]= 0; + } + return(1); +} + + +/* Convert an AAIP ACL attribute value into the long text form of ACL. + @param data The raw data to decode + @param num_data Number of data bytes provided + @param consumed Returns the number of consumed data bytes + @param acl_text Will be filled with ACL long text form + @param acl_text_size Maximum number of bytes to be written to acl_text + @param acl_text_fill Will return the number of bytes in acl_text + @param flag Bitfield for control purposes + bit0= count only, do not really produce bytes: + acl_text will not be touched, + acl_text_size will be ignored, + *acl_text_fill will return the counted number + plus 1 for a trailing zero. + bit1= expected is a default ACL (see return value 2) + @return 1 success + 2 success, begin of default/access ACL encountered, + submit data + *consumed for access/default ACL + -1 error with reading of qualifier + -2 error with writing of ACL text line + -3 version mismatch + -4 unknown tag type encountered +*/ +int aaip_decode_acl(unsigned char *data, size_t num_data, size_t *consumed, + char *acl_text, size_t acl_text_size, + size_t *acl_text_fill, int flag) +{ + unsigned char *rpt; + char perm_text[4], *wpt, name[1024]; + int type, qualifier= 0, perm, ret, i, cnt; + size_t w_size, name_fill= 0; + uid_t uid; + gid_t gid; + struct passwd *pwd; + struct group *grp; + + cnt= flag & 1; + *consumed= 0; + wpt= acl_text; + w_size= acl_text_size; + *acl_text_fill= 0; + for(rpt= data; rpt - data < num_data; ) { + perm= *rpt; + strcpy(perm_text, "---"); + if(perm & Aaip_READ) + perm_text[0]= 'r'; + if(perm & Aaip_WRITE) + perm_text[1]= 'w'; + if(perm & Aaip_EXEC) + perm_text[2]= 'x'; + + type= (*rpt) >> 4; + if(type == Aaip_FUTURE_VERSION) /* indicate to caller: version mismatch */ + return(-3); + + qualifier= !!((*rpt) & 8); + if(qualifier) { + ret= aaip_read_qualifier(rpt + 1, num_data - (rpt + 1 - data), + name, sizeof(name), &name_fill, 0); + if(ret <= 0) + return(-1); + } + + /* Advance read pointer */ + (*consumed)+= 1 + (qualifier ? name_fill + 1 : 0); + rpt+= 1 + (qualifier ? name_fill + 1 : 0); + + ret= 1; + if(type == Aaip_TRANSLATE) { + /* rightfully ignored yet */; + continue; + } else if(type == Aaip_ACL_USER_OBJ) { + /* user::rwx */ + ret= aaip_write_acl_line(&wpt, &w_size, "user", "", perm_text, cnt); + } else if(type == Aaip_ACL_USER) { + /* user::rwx */; + ret= aaip_write_acl_line(&wpt, &w_size, "user", name, perm_text, cnt); + } else if(type == Aaip_ACL_GROUP_OBJ) { + /* user::rwx */ + ret= aaip_write_acl_line(&wpt, &w_size, "group", "", perm_text, cnt); + } else if(type == Aaip_ACL_GROUP) { + /* group::rwx */; + ret= aaip_write_acl_line(&wpt, &w_size, "group", name, perm_text, cnt); + } else if(type == Aaip_ACL_MASK) { + /* mask::rwx */ + ret= aaip_write_acl_line(&wpt, &w_size, "mask", "", perm_text, cnt); + } else if(type == Aaip_ACL_OTHER) { + /* other::rwx */ + ret= aaip_write_acl_line(&wpt, &w_size, "other", "", perm_text, cnt); + } else if(type == Aaip_SWITCH_MARK) { + /* Indicate to caller: end of desired ACL type access/default */ + if((!(perm & Aaip_EXEC)) ^ (!!(flag & 2))) + return(2); + } else if(type == Aaip_ACL_USER_N) { + /* determine username from uid */ + uid= 0; + for(i= 0; i < name_fill; i++) + uid= (uid << 8) | ((unsigned char *) name)[i]; + pwd= getpwuid(uid); + if(pwd == NULL) + sprintf(name, "%.f", (double) uid); + else if(strlen(pwd->pw_name) >= sizeof(name)) + sprintf(name, "%.f", (double) uid); + else + strcpy(name, pwd->pw_name); + /* user::rwx */; + ret= aaip_write_acl_line(&wpt, &w_size, "user", name, perm_text, cnt); + } else if(type == Aaip_ACL_GROUP_N) { + /* determine username from gid */; + gid= 0; + for(i= 0; i < name_fill; i++) + gid= (gid << 8) | ((unsigned char *) name)[i]; + grp= getgrgid(gid); + if(grp == NULL) + sprintf(name, "%.f", (double) gid); + else if(strlen(grp->gr_name) >= sizeof(name)) + sprintf(name, "%.f", (double) gid); + else + strcpy(name, grp->gr_name); + /* user::rwx */; + ret= aaip_write_acl_line(&wpt, &w_size, "group", name, perm_text, cnt); + } else { + /* indicate to caller: unknown type */ + return(-4); + } + if(ret <= 0) + return(-2); + } + if(flag & 1) + *acl_text_fill= w_size + 1; + return(1); +} + + +/* ----------------------- Adapter for operating systems ----------------- */ + + +#ifdef __FreeBSD__ + +#include "aaip-os-freebsd.c" + +#else + +#include "aaip-os-linux.c" + +#endif + diff --git a/libisofs/aaip_0_2.h b/libisofs/aaip_0_2.h new file mode 100644 index 0000000..6a3fa7f --- /dev/null +++ b/libisofs/aaip_0_2.h @@ -0,0 +1,381 @@ + +/* + + Arbitrary Attribute Interchange Protocol , AAIP version 0.2 + Demonstration program for encoding and decoding EA and ACL. + + See http://libburnia-project.org/wiki/AAIP + or doc/susp_aaip_0_2.txt + + test/aaip_0.2.h - Public declarations + +*/ + +#ifndef Aaip_h_is_includeD +#define Aaip_h_is_includeD yes + + +/* --------------------------------- Encoder ---------------------------- */ + +/* Convert an array of Arbitrary Attributes into a series of AAIP fields. + @param aa_name The 2 byte SUSP Signature Word of the fields + @param num_attrs Number of attributes + @param names Array of pointers to 0 terminated name strings + @param value_lengths Array of byte lengths for each value + @param values Array of pointers to the value bytes + @param result_len Number of bytes in the resulting SUSP field string + @param result *result will point to the start of the result string. + This is malloc() memory which needs to be freed when + no longer needed + @param flag Bitfield for control purposes + bit0= set CONTINUE bit of last AA field to 1 + @return >0 is the number of SUSP fields generated, + 0 means error +*/ +unsigned int aaip_encode(char aa_name[2], + unsigned int num_attrs, char **names, + size_t *value_lengths, char **values, + size_t *result_len, unsigned char **result, int flag); + + +/* ------ ACL representation ------ */ + +/* Convert an ACL from long text form into the value of an Arbitrary + Attribute. According to AAIP 0.2 this value is to be stored together with + an empty name. + @param acl_text The ACL in long text form + @param result_len Number of bytes in the resulting value + @param result *result will point to the start of the result string. + This is malloc() memory which needs to be freed when + no longer needed + @param flag Bitfield for control purposes + bit0= count only + bit1= use numeric qualifiers rather than names + @return >0 means ok + 0 means error +*/ +int aaip_encode_acl(char *acl_text, + size_t *result_len, unsigned char **result, int flag); + + +/* ------ OS interface ------ */ + +/* 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 + bit15= free text and return 1 + @return > 0 ok + -1 failure of system ACL service (see errno) +*/ +int aaip_get_acl_text(char *path, char **text, int flag); + + +/* Obtain the Extended Attributes and/or the ACLs of the given file in a form + that is ready for aaip_encode(). The returned data objects finally have + to be freed by a call with flag bit 15. + @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 ACLs (access and eventually default) via + system ACL API and encode + bit1= use numeric ACL qualifiers rather than names + bit2= do not obtain attributes other than ACLs + bit3= do not ignore eventual ACL attribute + (e.g. system.posix_acl_access) + bit15= free memory of names, value_lengths, values + @return >0 ok + <=0 error +*/ +int aaip_get_attr_list(char *path, size_t *num_attrs, char ***names, + size_t **value_lengths, char ***values, int flag); + + +/* --------------------------------- Decoder ---------------------------- */ + +/* + The AAIP decoder offers several levels of abstraction of which the + lower two avoid the use of dynamic memory. It provides a stateful decoding + context with a small buffer which delivers results to caller provided + memory locations. + + The lowest level is the stream-like Component Level Interface. It allows + to decode very many very long attributes. + + Next is the Pair Level Interface which delivers to fixly sized storage for + name and value. It allows to decode very many attributes. + + The List Level Interface uses dynamic memory allocation to provide arrays + of names, values and value lengths. It is intended for moderately sized + attribute lists but may also be used as alternative to Pair Level. +*/ + + +/* The AAIP decoder context. +*/ +struct aaip_state; + + +/* Obtain the size in bytes of an aaip_state object. +*/ +size_t aaip_sizeof_aaip_state(void); + + +/* Initialize a AAIP decoder context. + This has to be done before the first AA field of a node is processed. + The caller has to provide the storage of the struct aaip_state. + @param aaip The AAIP decoder context to be initialized + @param aa_name The Signature Word (advised is "AA") + @param flag Bitfield for control purposes + submit 0 + @return <=0 error , >0 ok +*/ +int aaip_init_aaip_state(struct aaip_state *aaip, char aa_name[2], int flag); + + +/* ------------------------- Component Level Interface ------------------- */ +/* + Provides support for unlimited component size but demands the caller + to have a growing storage facility resp. to do own oversize handling. + + This interface expects moderatly sized input pieces and will hand out + moderately sized result pieces. The number of transactions is virtually + unlimited. +*/ + +/* Submit small data chunk for decoding. + The return value will tell whether data are pending for being fetched. + @param aaip The AAIP decoder context + @param data Not more than 2048 bytes input for the decoder + @param num_data Number of bytes in data + 0 inquires the buffer status avoiding replies <= 0 + @param ready_bytes Number of decoded bytes ready for delivery + @param flag Bitfield for control purposes + @return -1= non-AA field detected + *ready_bytes gives number of consumed bytes in data + 0= cannot accept data because buffer full + 1= no component record complete, submit more data + 2= component record complete, may be delivered + 3= component complete, may be delivered + 4= no component available, no more data expected, done +*/ +int aaip_submit_data(struct aaip_state *aaip, + unsigned char *data, size_t num_data, + size_t *ready_bytes, int flag); + + +/* Fetch the available part of current component. + The return value will tell whether it belongs to name or to value and + whether that name or value is completed now. + @param aaip The AAIP decoder context + @param result Has to point to storage for the component data + @param result_size Gives the amount of provided result storage + @param num_result Will tell the number of fetched result bytes + @param flag Bitfield for control purposes + bit0= discard data rather than copying to result + @return -2 = insufficient result_size + -1 = no data ready for delivery + 0 = result holds the final part of a name + 1 = result holds an intermediate part of a name + 2 = result holds the final part of a value + 3 = result holds an intermediate part of a value +*/ +int aaip_fetch_data(struct aaip_state *aaip, + char *result, size_t result_size, size_t *num_result, + int flag); + + +/* Skip the current component and eventually the following value component. + This has to be called if fetching of a component shall be aborted + but the next component resp. pair shall be fetchable again. + aaip_submit_data() will not indicate readiness for fetching until all + bytes of the skipped components are submitted. Those bytes get discarded. + @param aaip The AAIP decoder context + @param flag Bitfield for control purposes + bit0= do not skip value if current component is name + @return <=0 error , 1= now in skip state, 2= not in skip state +*/ +int aaip_skip_component(struct aaip_state *aaip, int flag); + + +/* ------------------------- Pair Level Interface ------------------------ */ +/* + Provides support for names and values of limited size. The limits are + given by the caller who has to provide the storage for name and value. + + This interface expects moderatly sized input pieces. + The number of input transcations is virtually unlimited. + The number of pair transactions after aaip_init() should be limited + to 4 billion. +*/ + + +/* Accept raw input data and collect a pair of name and value. + The return value will indicate whether the pair is complete, whether more + pairs are complete or whether more data are desired. No input data will be + accepted as long as complete pairs are pending. The end of the attribute + list will be indicated. + @param aaip The AAIP decoder context + @param data The raw data to decode + @param num_data Number of data bytes provided + @param consumed Returns the number of consumed data bytes + @param name Buffer to build the name string + @param name_size Maximum number of bytes in name + @param name_fill Holds the current buffer fill of name + @param value Buffer to build the value string + @param value_size Maximum number of bytes in value + @param value_fill Holds the current buffer fill of value + @param flag Bitfield for control purposes - submit 0 for now + @return <0 error + 0 data not accepted, first fetch pending pairs with num_data == 0 + 1 name and value are not valid yet, submit more data + 2 name and value are valid, submit more data + 3 name and value are valid, pairs pending, fetch with num_data == 0 + 4 name and value are valid, no more data expected + 5 name and value are not valid, no more data expected + +*/ +int aaip_decode_pair(struct aaip_state *aaip, + unsigned char *data, size_t num_data, size_t *consumed, + char *name, size_t name_size, size_t *name_fill, + char *value, size_t value_size, size_t *value_fill, + int flag); + + +/* Inquire the number of pairs which were skipped because being oversized. + @param aaip The AAIP decoder context + @param flag Bitfield for control purposes - submit 0 for now + @return The number of pairs skipped since aaip_init() +*/ +unsigned int aaip_get_pairs_skipped(struct aaip_state *aaip, int flag); + + +/* ------------------------- List Level Interface ------------------------ */ +/* + Provides support for names and values of limited size. The limits are + given for total memory consumption and for number of attributes. + + Iterated decoding is supported as long as no single attribute exceeds + the memory limit. +*/ + +/* Accept raw input data and collect arrays of name pointers, value lengths + and value pointers. A handle object will emerge which finally has to be + be freed by a call with bit 15. + @param handle The decoding context. + It will be created by this call with flag bit 0 or if + *handle == NULL. This handle has to be the same as long + as decoding goes on and finally has to be freed by a + call with bit15. + @param aa_name The Signature Word (advised is "AA") + @param memory_limit Maximum number of bytes to allocate + @param num_attr_limit Maximum number of name-value pairs to allocate + @param data The raw data to decode + @param num_data Number of data bytes provided + @param consumed Returns the number of consumed data bytes + @param flag Bitfield for control purposes + bit0= this is the first call for a file object + bit15= end decoding : + Free handle and its intermediate list memory. + @return <=0 error + 1 not complete yet, submit more data + 2 arrays are complete, call aaip_get_decoded_attrs() + 3 limit exceeded, not complete yet, call with bit15 and give up + 4 limit exceeded, call aaip_get_decoded_attrs() and try again +*/ +int aaip_decode_attrs(struct aaip_state **handle, char aa_name[2], + size_t memory_limit, size_t num_attr_limit, + unsigned char *data, size_t num_data, size_t *consumed, + int flag); + + +/* Obtain the resulting attributes when aaip_decode_attrs() indicates to + be done or to have the maximum possible amount of result ready. + The returned data objects get detached from handle making it ready for + the next round of decoding with possibly a different input source. The + returned data objects finally have to be freed by a call with flag bit 15. + @param handle The decoding context created by aaip_decode_attrs() + @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 + bit15= free memory of names, value_lengths, values +*/ +int aaip_get_decoded_attrs(struct aaip_state **handle, size_t *num_attrs, + char ***names, size_t **value_lengths, char ***values, + int flag); + + +/* ------ ACL representation ------ */ + +/* Convert an AAIP 0.2 ACL attribute value into the long text form of ACL. + @param data The raw data to decode + @param num_data Number of data bytes provided + @param consumed Returns the number of consumed data bytes + @param acl_text Will be filled with ACL long text form + @param acl_text_size Maximum number of bytes to be written to acl_text + @param acl_text_fill Will return the number of bytes in acl_text + @param flag Bitfield for control purposes + bit0= count only, do not really produce bytes: + acl_text will not be touched, + acl_text_size will be ignored, + *acl_text_fill will return the counted number + bit1= expected is a default ACL (see return value 2) + @return 1 success + 2 success, begin of default/access ACL encountered, + submit data + *consumed for access/default ACL + -1 error with reading of qualifier + -2 error with writing of ACL text line + -3 version mismatch + -4 unknown tag type encountered +*/ +int aaip_decode_acl(unsigned char *data, size_t num_data, size_t *consumed, + char *acl_text, size_t acl_text_size, + size_t *acl_text_fill, int flag); + + +/* ------ OS interface ------ */ + +/* 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 + @return > 0 ok + -1 failure of system ACL service (see errno) +*/ +int aaip_set_acl_text(char *path, char *text, int flag); + + +/* Bring the given attributes and/or ACLs into effect with the given file. + @param path Path to the file + @param num_attrs Number of attributes + @param names Array of pointers to 0 terminated name strings + @param value_lengths Array of byte lengths for each attribute payload + @param values Array of pointers to the attribute payload bytes + @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 ACL attribute + (e.g. system.posix_acl_access) + @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 + +*/ +int aaip_set_attr_list(char *path, size_t num_attrs, char **names, + size_t *value_lengths, char **values, int flag); + +#endif /* ! Aaip_h_is_includeD */ +