Index: include/apr.h.in
===================================================================
--- include/apr.h.in	(revision 606117)
+++ include/apr.h.in	(working copy)
@@ -103,6 +103,8 @@
 #define APR_HAVE_SYS_UIO_H       @sys_uioh@
 #define APR_HAVE_SYS_UN_H        @sys_unh@
 #define APR_HAVE_SYS_WAIT_H      @sys_waith@
+#define APR_HAVE_SYS_XATTR_H     @sys_xattrh@
+#define APR_HAVE_SYS_EXTATTR_H   @sys_extattrh@
 #define APR_HAVE_TIME_H          @timeh@
 #define APR_HAVE_UNISTD_H        @unistdh@
 #define APR_HAVE_WINDOWS_H       @windowsh@
@@ -238,6 +240,7 @@
 #define APR_HAS_UNICODE_FS        @have_unicode_fs@
 #define APR_HAS_PROC_INVOKED      @have_proc_invoked@
 #define APR_HAS_USER              1
+#define APR_HAS_XATTR             @xattr@
 #define APR_HAS_LARGE_FILES       @aprlfs@
 #define APR_HAS_XTHREAD_FILES     0
 #define APR_HAS_OS_UUID           @osuuid@
Index: configure.in
===================================================================
--- configure.in	(revision 606117)
+++ configure.in	(working copy)
@@ -1135,7 +1135,9 @@
     sys/types.h		\
     sys/uio.h		\
     sys/un.h		\
-    sys/wait.h)
+    sys/wait.h          \
+    sys/xattr.h         \
+    sys/extattr.h)
 
 # IRIX 6.5 has a problem in <netinet/tcp.h> which prevents it from
 # being included by itself.  Check for <netinet/tcp.h> manually,
@@ -1187,6 +1189,8 @@
 AC_SUBST(unistdh)
 AC_SUBST(signalh)
 AC_SUBST(sys_waith)
+AC_SUBST(sys_xattrh)
+AC_SUBST(sys_extattrh)
 AC_SUBST(pthreadh)
 AC_SUBST(semaphoreh)
 AC_SUBST(windowsh)
@@ -2030,6 +2034,87 @@
 APR_CHECK_DIRENT_INODE
 APR_CHECK_DIRENT_TYPE
 
+dnl ----------------------------- Checking for Extended Attributes Support
+echo "${nl}Checking for Extended Attributes Support..."
+
+# Check whether Extended Attributes have explicitly been disabled
+AC_ARG_ENABLE(xattr,
+  [  --disable-xattr         Disable Extended Attributes support in APR.],
+  [ if test "$enableval" = "no"; then
+        user_disabled_xattr=1
+    fi ],
+  [ user_disabled_xattr=0 ] )
+
+xattrdarwin="0"
+xattrlinux="0"
+xattrfreebsd="0"
+xattrsolaris="0"
+if test "$user_disabled_xattr" = 1; then
+    echo "no -- disabled by user"
+else
+    AC_CHECK_FUNCS([fsetxattr fgetxattr flistxattr fremovexattr \
+                    lgetxattr openat unlinkat extattr_set_fd \
+                    extattr_get_fd extattr_list_fd extattr_delete_fd])
+    APR_CHECK_DEFINE(O_XATTR, sys/fcntl.h)
+    APR_CHECK_DEFINE(XATTR_NOFOLLOW, sys/xattr.h)
+    APR_CHECK_DEFINE(XATTR_CREATE, sys/xattr.h)
+    APR_CHECK_DEFINE(XATTR_REPLACE, sys/xattr.h)
+    APR_CHECK_DEFINE(XATTR_NAME_MAX, linux/limits.h)
+    APR_CHECK_DEFINE(XATTR_SIZE_MAX, linux/limits.h)
+    APR_CHECK_DEFINE(XATTR_LIST_MAX, linux/limits.h)
+    APR_CHECK_DEFINE(XATTR_MAXNAMELEN, sys/xattr.h)
+    APR_CHECK_DEFINE(EXTATTR_NAMESPACE_USER, sys/extattr.h)
+    APR_CHECK_DEFINE(EXTATTR_NAMESPACE_SYSTEM, sys/extattr.h)
+    APR_CHECK_DEFINE(EXTATTR_MAXNAMELEN, sys/extattr.h)
+
+    APR_BEGIN_DECISION([xattr attribute system interface])
+    APR_IFALLYES(header:sys/xattr.h dnl
+                 define:XATTR_NOFOLLOW dnl
+                 func:fgetxattr dnl
+                 func:fsetxattr dnl
+                 func:flistxattr dnl
+                 func:fremovexattr,
+                 [xattrdarwin="1"
+                  APR_DECIDE(USE_XATTR_DARWIN, [xattr (Darwin)])])
+
+    APR_IFALLYES(header:sys/xattr.h dnl
+                 func:lgetxattr dnl
+                 func:fgetxattr dnl
+                 func:fsetxattr dnl
+                 func:flistxattr dnl
+                 func:fremovexattr,
+                 [xattrlinux="1"
+                  APR_DECIDE(USE_XATTR_LINUX, [xattr (Linux)])])
+
+    APR_IFALLYES(header:sys/extattr.h dnl
+                 func:extattr_set_fd dnl
+                 func:func:extattr_get_fd dnl
+                 func:extattr_list_fd dnl
+                 func:func:extattr_delete_fd,
+                 [xattrfreebsd="1"
+                  APR_DECIDE(USE_XATTR_FREEBSD, [extattr (FreeBSD)])])
+
+    APR_IFALLYES(define:O_XATTR dnl
+                 func:openat dnl
+                 func:unlinkat,
+                 [xattrsolaris="1"
+                  APR_DECIDE(USE_XATTR_SOLARIS, [attropen (Solaris)])])
+
+    if test "x$ac_decision" = "x"; then
+        ac_decision="USE_XATTR_NONE"
+        ac_decision_msg="no xattr interface available"
+    fi
+    APR_END_DECISION
+    AC_DEFINE_UNQUOTED($ac_decision)
+fi
+
+if test "$xattrdarwin$xattrlinux$xattrfreebsd$xattrsolaris" = "0000"; then
+  xattr="0"
+else
+  xattr="1"
+fi
+AC_SUBST(xattr)
+
 dnl ----------------------------- Checking for UUID Support 
 echo "${nl}Checking for OS UUID Support..."
 
Index: include/apr_file_xattr.h
===================================================================
--- include/apr_file_xattr.h	(revision 0)
+++ include/apr_file_xattr.h	(revision 0)
@@ -0,0 +1,118 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef APR_FILE_XATTR_H
+#define APR_FILE_XATTR_H
+
+/**
+ * @file apr_file_xattr.h
+ * @brief APR File Extended Attributes
+ */
+
+#include "apr.h"
+#include "apr_pools.h"
+#include "apr_tables.h"
+#include "apr_file_io.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+/**
+ * @defgroup apr_file_xattr File Extended Attribute Functions
+ * @ingroup APR 
+ * @{
+ */
+
+/** When setting values, fail if the attribute already exists */
+#define APR_XATTR_CREATE      1
+
+/** When setting values, fail if the attribute does not already exist */
+#define APR_XATTR_REPLACE     2
+
+
+/**
+ * Set an extended attribute on a file
+ * @param file the file to set the attribute on
+ * @param name the attribute name to set
+ * @param value the attribute value
+ * @param size the size in bytes of the attribute value
+ * @param flags to control how the attribute is set
+ * <PRE>
+ *         APR_XATTR_CREATE      return an error if the attribute name
+ *                               already exists.
+ *         APR_XATTR_REPLACE     return an error if the attribute name
+ *                               does not already exist.
+ * </PRE>
+ * @param p the pool to allocate any memory from if required
+ * @remark if neither flag APR_XATTR_CREATE or APR_XATTR_REPLACE are
+ * given then the attribute will either be created if it does not
+ * already exist or replaced if it does exist.
+ */                        
+APR_DECLARE(apr_status_t) apr_file_xattr_set(apr_file_t *file,
+                                             const char *name,
+                                             const void *value,
+                                             apr_size_t size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p);
+
+/**
+ * Get an extended attribute from a file
+ * @param file the file to get the attribute from
+ * @param name the name of the attribute to get
+ * @param value the returned attribute value allocated from the pool
+ * @param size the returned size of the attribute value
+ * @param flags to control how the attribute is got (reserved for future use)
+ * @param p the pool to allocate any memory from if required
+ */                        
+APR_DECLARE(apr_status_t) apr_file_xattr_get(apr_file_t *file,
+                                             const char *name,
+                                             void **value,
+                                             apr_size_t *size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p);
+
+/**
+ * List the extended attributes for a file
+ * @param file the file to list the attributes of
+ * @param list the returned array of attributes names
+ * @param flags to control how the file is listed (reserved for future use)
+ * @param p the pool to allocate any memory from if required
+ * @remark list is an array containing simple null terminated strings.
+ */                        
+APR_DECLARE(apr_status_t) apr_file_xattr_list(apr_file_t *file,
+                                              apr_array_header_t **list,
+                                              apr_uint32_t flags,
+                                              apr_pool_t *p);
+
+/**
+ * Remove an extended attribute from a file
+ * @param file tje file to remove the attribute from
+ * @param name the attribute name to remove
+ * @param flags to control how the attribute is removed (reserved for future use)
+ * @param p the pool to allocate any memory from if required
+ */                        
+APR_DECLARE(apr_status_t) apr_file_xattr_remove(apr_file_t *file,
+                                                const char *name,
+                                                apr_uint32_t flags,
+                                                apr_pool_t *p);
+/** @} */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  /* ! APR_FILE_XATTR_H */
Index: file_io/unix/xattr/darwin.c
===================================================================
--- file_io/unix/xattr/darwin.c	(revision 0)
+++ file_io/unix/xattr/darwin.c	(revision 0)
@@ -0,0 +1,131 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_strings.h"
+#include "apr_tables.h"
+#include "apr_file_xattr.h"
+#include "apr_arch_file_io.h"
+
+#if APR_HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
+#if APR_HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+
+APR_DECLARE(apr_status_t) apr_file_xattr_set(apr_file_t *file,
+                                             const char *name,
+                                             const void *value,
+                                             apr_size_t size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    int ret;
+    int osflags = 0;
+
+    if (flags & APR_XATTR_CREATE) {
+        osflags |= XATTR_CREATE;
+    }
+    if (flags & APR_XATTR_REPLACE) {
+        osflags |= XATTR_REPLACE;
+    }
+
+    ret = fsetxattr(file->filedes, name, value, size, 0, osflags);
+
+    if (ret < 0) {
+        return errno;
+    }
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_get(apr_file_t *file,
+                                             const char *name,
+                                             void **value,
+                                             apr_size_t *size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    apr_ssize_t ret;
+
+    ret = fgetxattr(file->filedes, name, NULL, 0, 0, 0);
+
+    if (ret < 0) {
+        return errno;
+    }
+    if (!value) {
+        *size = ret;
+        return APR_SUCCESS;
+    }
+    *value = apr_palloc(p, ret);
+
+    ret = fgetxattr(file->filedes, name, *value, ret, 0, 0);
+
+    if (ret < 0) {
+        return errno;
+    }
+    *size = ret;
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_list(apr_file_t *file,
+                                              apr_array_header_t **list,
+                                              apr_uint32_t flags,
+                                              apr_pool_t *p)
+{
+    apr_ssize_t lsize;
+    char *listbuf, *listp;
+
+    lsize = flistxattr(file->filedes, NULL, 0, 0);
+
+    if (lsize < 0) {
+        return errno;
+    }
+    if (lsize == 0) {
+        *list = apr_array_make(p, 0, sizeof(char*));
+        return APR_SUCCESS;
+    }
+    listbuf = apr_palloc(p, lsize);
+
+    lsize = flistxattr(file->filedes, listbuf, lsize, 0);
+
+    if (lsize < 0) {
+        return errno;
+    }
+    *list = apr_array_make(p, 1, sizeof(char*));
+    listp = listbuf;
+    while (listp < listbuf + lsize) {
+        int attrlen = strlen(listp) + 1;
+        *(char**)apr_array_push(*list) = listp;
+        listp += attrlen;
+    }
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_remove(apr_file_t *file,
+                                                const char *name,
+                                                apr_uint32_t flags,
+                                                apr_pool_t *p)
+{
+    int ret;
+
+    ret = fremovexattr(file->filedes, name, 0);
+
+    if (ret < 0) {
+        return errno;
+    }
+    return APR_SUCCESS;
+}
Index: file_io/unix/xattr/freebsd.c
===================================================================
--- file_io/unix/xattr/freebsd.c	(revision 0)
+++ file_io/unix/xattr/freebsd.c	(revision 0)
@@ -0,0 +1,154 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_strings.h"
+#include "apr_tables.h"
+#include "apr_file_xattr.h"
+#include "apr_arch_file_io.h"
+
+#if APR_HAVE_SYS_EXTATTR_H
+#include <sys/extattr.h>
+#endif
+#if APR_HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+
+APR_DECLARE(apr_status_t) apr_file_xattr_set(apr_file_t *file,
+                                             const char *name,
+                                             const void *value,
+                                             apr_size_t size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    int ret;
+
+    if ((flags & APR_XATTR_CREATE) && (flags & APR_XATTR_REPLACE)) {
+        return APR_EINVAL;
+    }
+
+    if ((flags & APR_XATTR_CREATE) || (flags & APR_XATTR_REPLACE)) {
+
+        ret = extattr_get_fd(file->filedes, EXTATTR_NAMESPACE_USER,
+			     name, NULL, 0);
+
+        if (ret >= 0 && (flags & APR_XATTR_CREATE)) {
+            return APR_EEXIST;
+        }
+	else if (ret == -1 && errno == ENOATTR) {
+	    if (flags & APR_XATTR_REPLACE) {
+		return APR_ENOENT;
+	    }
+        }
+	else if (ret == -1) {
+	    return errno;
+	}
+    }
+
+    ret = extattr_set_fd(file->filedes, EXTATTR_NAMESPACE_USER,
+			 name, value, size);
+
+    if (ret < 0) {
+        return errno;
+    }
+
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_get(apr_file_t *file,
+                                             const char *name,
+                                             void **value,
+                                             apr_size_t *size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    int ret;
+
+    ret = extattr_get_fd(file->filedes, EXTATTR_NAMESPACE_USER,
+			 name, NULL, 0);
+
+    if (ret < 0) {
+        return errno;
+    }
+    if (!value) {
+        *size = ret;
+        return APR_SUCCESS;
+    }
+    *value = apr_palloc(p, ret);
+
+    ret = extattr_get_fd(file->filedes, EXTATTR_NAMESPACE_USER,
+			 name, *value, ret);
+
+    if (ret < 0) {
+        return errno;
+    }
+    *size = ret;
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_list(apr_file_t *file,
+                                              apr_array_header_t **list,
+                                              apr_uint32_t flags,
+                                              apr_pool_t *p)
+{
+    int lsize;
+    char *listbuf, *listp;
+
+    lsize = extattr_list_fd(file->filedes, EXTATTR_NAMESPACE_USER,
+			    NULL, 0);
+
+    if (lsize < 0) {
+        return errno;
+    }
+    if (lsize == 0) {
+        *list = apr_array_make(p, 0, sizeof(char*));
+        return APR_SUCCESS;
+    }
+    listbuf = apr_palloc(p, lsize);
+
+    lsize = extattr_list_fd(file->filedes, EXTATTR_NAMESPACE_USER,
+			    listbuf, lsize);
+
+    if (lsize < 0) {
+        return errno;
+    }
+
+    *list = apr_array_make(p, 1, sizeof(char*));
+    listp = listbuf;
+    while(listp < listbuf + lsize) {
+        /* first byte is the length of the attribute name and the
+           strings are not null terminated, so we copy them */
+        int attrlen = (int)*listp;
+        *(char**)apr_array_push(*list) = apr_pstrndup(p, listp+1, attrlen);
+        listp += (attrlen + 1);
+    }
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_remove(apr_file_t *file,
+                                                const char *name,
+                                                apr_uint32_t flags,
+                                                apr_pool_t *p)
+{
+    int ret;
+
+    ret = extattr_delete_fd(file->filedes, EXTATTR_NAMESPACE_USER, name);
+    if (ret < 0) {
+        return errno;
+    }
+
+    return APR_SUCCESS;
+}
Index: file_io/unix/xattr/linux.c
===================================================================
--- file_io/unix/xattr/linux.c	(revision 0)
+++ file_io/unix/xattr/linux.c	(revision 0)
@@ -0,0 +1,143 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_strings.h"
+#include "apr_tables.h"
+#include "apr_file_xattr.h"
+#include "apr_arch_file_io.h"
+
+#if APR_HAVE_SYS_XATTR_H
+#include <sys/xattr.h>
+#endif
+#if APR_HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+
+static const char* user_ns_prefix = "user.";
+
+APR_DECLARE(apr_status_t) apr_file_xattr_set(apr_file_t *file,
+                                             const char *name,
+                                             const void *value,
+                                             apr_size_t size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    int ret;
+    int osflags = 0;
+
+    char *tmpname = apr_pstrcat(p, user_ns_prefix, name, NULL);
+
+    if (flags & APR_XATTR_CREATE) {
+        osflags |= XATTR_CREATE;
+    }
+    if (flags & APR_XATTR_REPLACE) {
+        osflags |= XATTR_REPLACE;
+    }
+
+    ret = fsetxattr(file->filedes, tmpname, value, size, osflags);
+
+    if (ret < 0) {
+        return errno;
+    }
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_get(apr_file_t *file,
+                                             const char *name,
+                                             void **value,
+                                             apr_size_t *size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    apr_ssize_t ret;
+    char *tmpname = apr_pstrcat(p, user_ns_prefix, name, NULL);
+
+    ret = fgetxattr(file->filedes, tmpname, NULL, 0);
+
+    if (ret < 0) {
+        return errno;
+    }
+    if (!value) {
+        *size = ret;
+        return APR_SUCCESS;
+    }
+    *value = apr_palloc(p, ret);
+
+    ret = fgetxattr(file->filedes, tmpname, *value, ret);
+
+    if (ret < 0) {
+        return errno;
+    }
+    *size = ret;
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_list(apr_file_t *file,
+                                              apr_array_header_t **list,
+                                              apr_uint32_t flags,
+                                              apr_pool_t *p)
+{
+    apr_ssize_t lsize;
+    char *listbuf, *listp;
+
+    lsize = flistxattr(file->filedes, NULL, 0);
+
+    if (lsize < 0) {
+        return errno;
+    }
+    if (lsize == 0) {
+        *list = apr_array_make(p, 0, sizeof(char*));
+        return APR_SUCCESS;
+    }
+    listbuf = apr_palloc(p, lsize);
+
+    lsize = flistxattr(file->filedes, listbuf, lsize);
+
+    if (lsize < 0) {
+        return errno;
+    }
+    *list = apr_array_make(p, 1, sizeof(char*));
+    listp = listbuf;
+    while (listp < listbuf + lsize) {
+        int attrlen = strlen(listp) + 1;
+        int user_ns_prefix_len = strlen(user_ns_prefix);
+        if (strncmp(listp, user_ns_prefix, user_ns_prefix_len) != 0) {
+            continue;
+        }
+        listp += user_ns_prefix_len;
+        attrlen -= user_ns_prefix_len;
+        *(char**)apr_array_push(*list) = listp;
+        listp += attrlen;
+    }
+    return APR_SUCCESS;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_remove(apr_file_t *file,
+                                                const char *name,
+                                                apr_uint32_t flags,
+                                                apr_pool_t *p)
+{
+    int ret;
+    char *tmpname = apr_pstrcat(p, user_ns_prefix, name, NULL);
+
+    ret = fremovexattr(file->filedes, tmpname);
+
+    if (ret < 0) {
+        return errno;
+    }
+    return APR_SUCCESS;
+}
Index: file_io/unix/xattr/solaris.c
===================================================================
--- file_io/unix/xattr/solaris.c	(revision 0)
+++ file_io/unix/xattr/solaris.c	(revision 0)
@@ -0,0 +1,268 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_arch_file_io.h"
+#include "apr_strings.h"
+#include "apr_tables.h"
+#include "apr_file_xattr.h"
+
+#if APR_HAVE_CTYPE_H
+#include "ctype.h"
+#endif
+
+
+/* The following code is for escaping and unescaping '/' chars
+   (and '%' as it is used as an escape character) in attribute names.
+
+   This is because '/' can't be used in Solaris subfile names and doing
+   so could present a security risk as files could be opened elsewhere.
+*/
+
+static char nibble_to_hex(register char c)
+{
+    if (c >= 0 && c <= 9) return '0' + c;
+    if (c >= 10 && c <= 15) return 'a' + c - 10;
+    else return 0;
+}
+
+static char hex_to_nibble(register char c)
+{
+    if (c >= '0' && c <= '9') return c - '0';
+    if (c >= 'a' && c <= 'f') return c - 'a' + 10;
+    else if(c >= 'A' && c <= 'F') return c - 'A' + 10;
+    else return 0;
+}
+
+static const char* escape_attr_name(const char *name, apr_pool_t *p)
+{
+    const char *namep;
+    int hasspecial = 0;
+    apr_array_header_t *arr;
+
+    /* fastpath if atttrname contains no characters that need escaping */
+    if(strchr(name, '/') == NULL && strchr(name, '%') == NULL) {
+	return name;
+    }
+
+    /* start with a conservative amount of additional space */
+    arr = apr_array_make(p, strlen(name) + 16, sizeof(char));
+    namep = name;
+    while (*namep) {
+        if(*namep == '/' || *namep == '%') {
+            *(char*)apr_array_push(arr) = '%';
+            *(char*)apr_array_push(arr) = nibble_to_hex(*namep >> 4);
+            *(char*)apr_array_push(arr) = nibble_to_hex(*namep & 0x0f);
+        } else {
+            *(char*)apr_array_push(arr) = *namep;
+        }
+        namep++;
+    }
+    *(char*)apr_array_push(arr) = '\0';
+
+    return arr->elts;
+}
+
+static const char* unescape_attr_name(const char *name, apr_pool_t *p)
+{
+    const char *namep;
+    int hasspecial = 0;
+    apr_array_header_t *arr;
+
+    /* fastpath if atttrname contains no escaped characters */
+    if(strchr(name, '%') == NULL) {
+	return name;
+    }
+
+    /* start with a conservative amount of additional space */
+    arr = apr_array_make(p, strlen(name) + 16, sizeof(char));
+    namep = name;
+    while (*namep) {
+        if(*namep == '%' && *(namep+1) != 0 && *(namep+2) != 0) {
+            namep++;            
+            *(char*)apr_array_push(arr) =
+                hex_to_nibble(*namep++) << 4 | hex_to_nibble(*namep++);
+        } else {
+            *(char*)apr_array_push(arr) = *namep++;
+        }
+    }
+    *(char*)apr_array_push(arr) = '\0';
+
+    return arr->elts;
+}
+
+
+APR_DECLARE(apr_status_t) apr_file_xattr_set(apr_file_t *file,
+                                             const char *name,
+                                             const void *value,
+                                             apr_size_t size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    int ret, fd, osflags;
+
+    if (strncmp(name, "SUNW", 4) == 0) {
+        /* don't clash with sun namespace */
+        return EINVAL;
+    } else if (flags & APR_XATTR_CREATE && flags & APR_XATTR_REPLACE) {
+        return EINVAL;
+    } else if (flags & APR_XATTR_CREATE) {
+        osflags = O_CREAT | O_EXCL;
+    } else if (flags & APR_XATTR_REPLACE) {
+        osflags = 0;
+    } else {
+        osflags = O_CREAT;
+    }
+
+    fd = openat(file->filedes, escape_attr_name(name, p),
+                osflags | O_XATTR | O_TRUNC | O_WRONLY, 0666);
+
+    if (fd < 0) {
+        return errno;
+    }
+
+    ret = write(fd, value, size);
+
+    if (ret < 0) {
+        ret = errno;
+    }
+    else if (ret != size) {
+        ret = APR_EGENERAL;
+    }
+    else {
+        ret = APR_SUCCESS;
+    }
+    close(fd);
+    return ret;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_get(apr_file_t *file,
+                                             const char *name,
+                                             void **value,
+                                             apr_size_t *size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    int ret, fd;
+    ssize_t rsize;
+    struct_stat statbuf;
+
+    fd = openat(file->filedes, escape_attr_name(name, p), O_XATTR | O_RDONLY);
+    if (fd < 0) {
+        return errno;
+    }
+
+    ret = fstat(fd, &statbuf);
+
+    if (ret < 0) {
+        ret = errno;
+        close(fd);
+        return ret;
+    }
+    if (!value) {
+        close(fd);
+        *size = statbuf.st_size;
+        return APR_SUCCESS;
+    }
+    *value = apr_palloc(p, statbuf.st_size);
+
+    rsize = read(fd, *value, statbuf.st_size);
+
+    if (rsize < 0) {
+        ret = errno;
+    }
+    else if (rsize != statbuf.st_size) {
+        ret = APR_EGENERAL;
+    }
+    else {
+        ret = APR_SUCCESS;
+        *size = rsize;
+    }
+    close(fd);
+    return ret;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_list(apr_file_t *file,
+                                              apr_array_header_t **list,
+                                              apr_uint32_t flags,
+                                              apr_pool_t *p)
+{
+    int ret, fd;
+    DIR *dirp;
+    struct dirent *ent, *retent;
+    apr_size_t dirent_size = 
+        sizeof(*ent) + (sizeof(ent->d_name) > 1 ? 0 : 255);
+
+    fd = openat(file->filedes, ".", O_XATTR | O_RDONLY);
+
+    if (fd < 0) {
+        return errno;
+    }
+    dirp = fdopendir(fd);
+    if (dirp == NULL) {
+        ret = errno;
+        close(fd);
+        return ret;
+    }
+
+    *list = apr_array_make(p, 1, sizeof(char*));
+    ent = apr_palloc(p, dirent_size);
+    while ((ret = readdir_r(dirp, ent, &retent)) == 0 && retent) {
+        const char *name;
+        if (strncmp(ent->d_name, "SUNW", 4) == 0 || /* private */
+           strcmp(ent->d_name, ".") == 0 ||
+           strcmp(ent->d_name, "..") == 0) continue;
+
+        name = unescape_attr_name(ent->d_name, p);
+	/* we don't need to copy if the name has been unescaped as it
+	   is pool allocated memory already */
+	*(const char**)apr_array_push(*list) =
+	    (name != ent->d_name) ? name : apr_pstrdup(p, ent->d_name);
+    }
+
+    if (ret < 0) {
+        ret = errno;
+    }
+    else {
+        ret = APR_SUCCESS;
+    }
+    closedir(dirp);
+    return ret;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_remove(apr_file_t *file,
+                                                const char *name,
+                                                apr_uint32_t flags,
+                                                apr_pool_t *p)
+{
+    int ret, fd;
+
+    fd = openat(file->filedes, ".", O_XATTR | O_RDONLY);
+
+    if (fd < 0) {
+        return errno;
+    }
+
+    ret = unlinkat(fd, escape_attr_name(name, p), 0);
+
+    if (ret < 0) {
+        ret = errno;
+    }
+    else {
+        ret = APR_SUCCESS;
+    }
+    close(fd);
+    return ret;
+}
Index: file_io/unix/xattr.c
===================================================================
--- file_io/unix/xattr.c	(revision 0)
+++ file_io/unix/xattr.c	(revision 0)
@@ -0,0 +1,75 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_private.h"
+
+#if USE_XATTR_DARWIN
+#include "xattr/darwin.c"
+#endif
+#if USE_XATTR_FREEBSD
+#include "xattr/freebsd.c"
+#endif
+#if USE_XATTR_LINUX
+#include "xattr/linux.c"
+#endif
+#if USE_XATTR_SOLARIS
+#include "xattr/solaris.c"
+#endif
+
+#if !USE_XATTR_DARWIN && !USE_XATTR_FREEBSD && !USE_XATTR_LINUX && \
+    !USE_XATTR_SOLARIS
+
+#include "apr_tables.h"
+#include "apr_file_xattr.h"
+#include "apr_arch_file_io.h"
+
+
+APR_DECLARE(apr_status_t) apr_file_xattr_set(apr_file_t *file,
+                                             const char *name,
+                                             const void *value,
+                                             apr_size_t size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_get(apr_file_t *file,
+                                             const char *name,
+                                             void **value,
+                                             apr_size_t *size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_list(apr_file_t *file,
+                                              apr_array_header_t **list,
+                                              apr_uint32_t flags,
+                                              apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_remove(apr_file_t *file,
+                                                const char *name,
+                                                apr_uint32_t flags,
+                                                apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+#endif
Index: file_io/netware/xattr.c
===================================================================
--- file_io/netware/xattr.c	(revision 0)
+++ file_io/netware/xattr.c	(revision 0)
@@ -0,0 +1,56 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_tables.h"
+#include "apr_file_xattr.h"
+#include "apr_arch_file_io.h"
+
+
+APR_DECLARE(apr_status_t) apr_file_xattr_set(apr_file_t *file,
+                                             const char *name,
+                                             const void *value,
+                                             apr_size_t size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_get(apr_file_t *file,
+                                             const char *name,
+                                             void **value,
+                                             apr_size_t *size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_list(apr_file_t *file,
+                                              apr_array_header_t **list,
+                                              apr_uint32_t flags,
+                                              apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_remove(apr_file_t *file,
+                                                const char *name,
+                                                apr_uint32_t flags,
+                                                apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
Index: file_io/os2/xattr.c
===================================================================
--- file_io/os2/xattr.c	(revision 0)
+++ file_io/os2/xattr.c	(revision 0)
@@ -0,0 +1,56 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_tables.h"
+#include "apr_file_xattr.h"
+#include "apr_arch_file_io.h"
+
+
+APR_DECLARE(apr_status_t) apr_file_xattr_set(apr_file_t *file,
+                                             const char *name,
+                                             const void *value,
+                                             apr_size_t size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_get(apr_file_t *file,
+                                             const char *name,
+                                             void **value,
+                                             apr_size_t *size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_list(apr_file_t *file,
+                                              apr_array_header_t **list,
+                                              apr_uint32_t flags,
+                                              apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_remove(apr_file_t *file,
+                                                const char *name,
+                                                apr_uint32_t flags,
+                                                apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
Index: file_io/win32/xattr.c
===================================================================
--- file_io/win32/xattr.c	(revision 0)
+++ file_io/win32/xattr.c	(revision 0)
@@ -0,0 +1,56 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_tables.h"
+#include "apr_file_xattr.h"
+#include "apr_arch_file_io.h"
+
+
+APR_DECLARE(apr_status_t) apr_file_xattr_set(apr_file_t *file,
+                                             const char *name,
+                                             const void *value,
+                                             apr_size_t size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_get(apr_file_t *file,
+                                             const char *name,
+                                             void **value,
+                                             apr_size_t *size,
+                                             apr_uint32_t flags,
+                                             apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_list(apr_file_t *file,
+                                              apr_array_header_t **list,
+                                              apr_uint32_t flags,
+                                              apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
+
+APR_DECLARE(apr_status_t) apr_file_xattr_remove(apr_file_t *file,
+                                                const char *name,
+                                                apr_uint32_t flags,
+                                                apr_pool_t *p)
+{
+    return APR_ENOTIMPL;
+}
Index: test/Makefile.in
===================================================================
--- test/Makefile.in	(revision 606117)
+++ test/Makefile.in	(working copy)
@@ -28,7 +28,7 @@
 	testhash.lo testargs.lo testnames.lo testuser.lo testpath.lo \
 	testenv.lo testprocmutex.lo testfnmatch.lo testatomic.lo testflock.lo \
 	testsock.lo testglobalmutex.lo teststrnatcmp.lo testfilecopy.lo \
-	testtemp.lo testlfs.lo testcond.lo
+	testtemp.lo testlfs.lo testcond.lo testxattr.lo
 
 OTHER_PROGRAMS = \
 	sendfile@EXEEXT@ \
Index: test/testxattr.c
===================================================================
--- test/testxattr.c	(revision 0)
+++ test/testxattr.c	(revision 0)
@@ -0,0 +1,243 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "apr_file_io.h"
+#include "apr_file_xattr.h"
+#include "apr_errno.h"
+#include "apr_general.h"
+#include "testutil.h"
+
+#if APR_HAS_XATTR
+
+#define DIRNAME "data"
+#define FNAME DIRNAME "/file_xattrfile.txt"
+
+#define TESTKEY1 "testkey1"
+#define TESTVAL1 "testval1"
+#define TESTKEY2 "testkey2"
+#define TESTVAL2 "testval2"
+
+/* test solaris subfilename escaping */
+#define TESTKEYSPECIAL1 "/testkey2"
+#define TESTKEYSPECIAL2 "%testkey2"
+
+static void test_xattr_set(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *filetest = NULL;
+
+    rv = apr_file_open(&filetest, FNAME, 
+                       APR_WRITE | APR_CREATE | APR_DELONCLOSE, 
+                       APR_UREAD | APR_UWRITE | APR_GREAD, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_set(filetest, TESTKEY1, TESTVAL1,
+			    strlen(TESTVAL1), 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    apr_file_close(filetest);
+}
+
+static void test_xattr_set_create(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *filetest = NULL;
+
+    rv = apr_file_open(&filetest, FNAME, 
+                       APR_WRITE | APR_CREATE | APR_DELONCLOSE, 
+                       APR_UREAD | APR_UWRITE | APR_GREAD, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_set(filetest, TESTKEY1, TESTVAL1, strlen(TESTVAL1),
+			    APR_XATTR_CREATE, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_set(filetest, TESTKEY1, TESTVAL1, strlen(TESTVAL1),
+			    APR_XATTR_CREATE, p);
+    ABTS_INT_NEQUAL(tc, APR_SUCCESS, rv);
+
+    apr_file_close(filetest);
+}
+
+static void test_xattr_set_replace(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *filetest = NULL;
+
+    rv = apr_file_open(&filetest, FNAME, 
+                       APR_WRITE | APR_CREATE | APR_DELONCLOSE, 
+                       APR_UREAD | APR_UWRITE | APR_GREAD, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_set(filetest, TESTKEY1, TESTVAL1, strlen(TESTVAL1),
+			    APR_XATTR_REPLACE, p);
+    ABTS_INT_NEQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_set(filetest, TESTKEY1, TESTVAL1, strlen(TESTVAL1),
+			    APR_XATTR_CREATE, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_set(filetest, TESTKEY1, TESTVAL1, strlen(TESTVAL1),
+			    APR_XATTR_REPLACE, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    apr_file_close(filetest);
+}
+
+static void test_xattr_get(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *filetest = NULL;
+    apr_size_t size;
+    char *val = NULL;
+
+    rv = apr_file_open(&filetest, FNAME, 
+                       APR_WRITE | APR_CREATE | APR_DELONCLOSE, 
+                       APR_UREAD | APR_UWRITE | APR_GREAD, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_set(filetest, TESTKEY1, TESTVAL1,
+			    strlen(TESTVAL1), 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_get(filetest, TESTKEY1, (void**)&val, &size, 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+    ABTS_INT_EQUAL(tc, strlen(TESTVAL1), size);
+    ABTS_PTR_NOTNULL(tc, val);
+    ABTS_TRUE(tc, strncmp(TESTVAL1, val, strlen(TESTVAL1)) == 0);
+
+    apr_file_close(filetest);
+}
+
+static void test_xattr_list(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *filetest = NULL;
+    apr_array_header_t *list = NULL;
+        int cmp1, cmp2;
+
+    rv = apr_file_open(&filetest, FNAME, 
+                       APR_WRITE | APR_CREATE | APR_DELONCLOSE, 
+                       APR_UREAD | APR_UWRITE | APR_GREAD, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_set(filetest, TESTKEY1, TESTVAL1,
+			    strlen(TESTVAL1), 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+    rv = apr_file_xattr_set(filetest, TESTKEY2, TESTVAL2,
+			    strlen(TESTVAL2), 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_list(filetest, &list, 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+    ABTS_PTR_NOTNULL(tc, list);
+    ABTS_INT_EQUAL(tc, 2, list->nelts);
+    cmp1 = (strcmp(APR_ARRAY_IDX(list, 0, char*), TESTKEY1) == 0) &&
+           (strcmp(APR_ARRAY_IDX(list, 1, char*), TESTKEY2) == 0);
+    cmp2 = (strcmp(APR_ARRAY_IDX(list, 0, char*), TESTKEY2) == 0) &&
+           (strcmp(APR_ARRAY_IDX(list, 1, char*), TESTKEY1) == 0);
+    ABTS_TRUE(tc, cmp1 || cmp2 );
+
+    apr_file_close(filetest);
+}
+
+static void test_xattr_remove(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *filetest = NULL;
+    apr_size_t size;
+    char *val = NULL;
+
+    rv = apr_file_open(&filetest, FNAME, 
+                       APR_WRITE | APR_CREATE | APR_DELONCLOSE, 
+                       APR_UREAD | APR_UWRITE | APR_GREAD, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_set(filetest, TESTKEY1, TESTVAL1,
+			    strlen(TESTVAL1), 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_remove(filetest, TESTKEY1, 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_get(filetest, TESTKEY1, (void**)&val, &size, 0, p);
+    ABTS_INT_NEQUAL(tc, APR_SUCCESS, rv);
+
+    apr_file_close(filetest);
+}
+
+static void test_xattr_special_chars(abts_case *tc, void *data)
+{
+    apr_status_t rv;
+    apr_file_t *filetest = NULL;
+    apr_size_t size;
+    const char *name = (const char*)data;
+    char *val = NULL;
+    apr_array_header_t *list = NULL;
+    int cmp1, cmp2;
+
+    rv = apr_file_open(&filetest, FNAME, 
+                       APR_WRITE | APR_CREATE | APR_DELONCLOSE, 
+                       APR_UREAD | APR_UWRITE | APR_GREAD, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_set(filetest, name, TESTVAL1,
+			    strlen(TESTVAL1), 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+
+    rv = apr_file_xattr_get(filetest, name, (void**)&val, &size, 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+    ABTS_INT_EQUAL(tc, strlen(TESTVAL1), size);
+    ABTS_PTR_NOTNULL(tc, val);
+    ABTS_TRUE(tc, strncmp(TESTVAL1, val, strlen(TESTVAL1)) == 0);
+
+    rv = apr_file_xattr_list(filetest, &list, 0, p);
+    ABTS_INT_EQUAL(tc, APR_SUCCESS, rv);
+    ABTS_PTR_NOTNULL(tc, list);
+    ABTS_INT_EQUAL(tc, 1, list->nelts);
+    ABTS_TRUE(tc, strcmp(name, APR_ARRAY_IDX(list, 0, char*)) == 0);
+
+    apr_file_close(filetest);
+}
+
+#else
+
+static void test_xattr_not_impl(abts_case *tc, void *data)
+{
+    ABTS_NOT_IMPL(tc, "xattr not implemented on this platform");
+}
+
+#endif /* APR_HAS_XATTR */
+
+abts_suite *testxattr(abts_suite *suite)
+{
+    suite = ADD_SUITE(suite)
+
+#if !APR_HAS_XATTR
+    abts_run_test(suite, test_xattr_not_impl, NULL);
+#else
+    abts_run_test(suite, test_xattr_set, NULL);
+    abts_run_test(suite, test_xattr_set_create, NULL);
+    abts_run_test(suite, test_xattr_set_replace, NULL);
+    abts_run_test(suite, test_xattr_get, NULL);
+    abts_run_test(suite, test_xattr_list, NULL);
+    abts_run_test(suite, test_xattr_remove, NULL);
+    abts_run_test(suite, test_xattr_special_chars, TESTKEYSPECIAL1);
+    abts_run_test(suite, test_xattr_special_chars, TESTKEYSPECIAL2);
+#endif
+
+    return suite;
+}
Index: test/abts_tests.h
===================================================================
--- test/abts_tests.h	(revision 606117)
+++ test/abts_tests.h	(working copy)
@@ -66,7 +66,8 @@
     {testtime},
     {testud},
     {testuser},
-    {testvsn}
+    {testvsn},
+    {testxattr}
 };
 
 #endif /* APR_TEST_INCLUDES */
Index: test/testutil.h
===================================================================
--- test/testutil.h	(revision 606117)
+++ test/testutil.h	(working copy)
@@ -102,5 +102,6 @@
 abts_suite *testud(abts_suite *suite);
 abts_suite *testuser(abts_suite *suite);
 abts_suite *testvsn(abts_suite *suite);
+abts_suite *testxattr(abts_suite *suite);
 
 #endif /* APR_TEST_INCLUDES */

