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

