
Author: rmh (with borrowed code from mono)
Status: NOT SENT.  Needs review and testing.  Also needs to get rid of glib
  dependency.  Needs up-to-date <sys/syscall.h> (see kfreebsd-5 BTS).

diff -Nur attr-2.4.21.old/getfattr/getfattr.c attr-2.4.21/getfattr/getfattr.c
--- attr-2.4.21.old/getfattr/getfattr.c	2005-02-01 02:24:03.000000000 +0100
+++ attr-2.4.21/getfattr/getfattr.c	2005-08-14 16:25:08.000000000 +0200
@@ -94,8 +94,10 @@
 
 const char *strerror_ea(int err)
 {
+#ifdef ENODATA
 	if (err == ENODATA)
 		return _("No such attribute");
+#endif
 	return strerror(err);
 }
 
diff -Nur attr-2.4.21.old/libattr/Makefile attr-2.4.21/libattr/Makefile
--- attr-2.4.21.old/libattr/Makefile	2003-02-26 07:27:13.000000000 +0100
+++ attr-2.4.21/libattr/Makefile	2005-08-14 16:37:33.000000000 +0200
@@ -33,7 +33,7 @@
 
 TOPDIR = ..
 
-LTLDFLAGS = -Wl,--version-script,$(TOPDIR)/exports
+LTLDFLAGS = -Wl,--version-script,$(TOPDIR)/exports `pkg-config --libs glib-2.0`
 include $(TOPDIR)/include/builddefs
 
 LTLIBRARY = libattr.la
@@ -44,7 +44,7 @@
 CFILES = libattr.c syscalls.c attr_copy_fd.c attr_copy_file.c attr_copy_check.c
 HFILES = libattr.h
 
-LCFLAGS = -include libattr.h
+LCFLAGS = -include libattr.h `pkg-config --cflags glib-2.0`
 
 default: $(LTLIBRARY)
 
diff -Nur attr-2.4.21.old/libattr/syscalls-bsd.c attr-2.4.21/libattr/syscalls-bsd.c
--- attr-2.4.21.old/libattr/syscalls-bsd.c	1970-01-01 01:00:00.000000000 +0100
+++ attr-2.4.21/libattr/syscalls-bsd.c	2005-08-14 16:46:39.000000000 +0200
@@ -0,0 +1,459 @@
+/*
+ * Copyright (C) 2005 Robert Millan
+ *
+ * Heavily based on sys-xattr.c wrapper file from mono:
+ *   Copyright (C) 2005 Daniel Drake <dsd@gentoo.org>
+ * 
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2.1 of the GNU Lesser General Public License
+ * as published by the Free Software Foundation.
+ * 
+ * This program is distributed in the hope that it would be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * 
+ * Further, this software is distributed without any warranty that it is
+ * free of the rightful claim of any third person regarding infringement
+ * or the like.  Any license provided herein, whether implied or
+ * otherwise, applies only to this software file.  Patent licenses, if
+ * any, provided herein do not apply to combinations of this program with
+ * other software, or any other product whatsoever.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307,
+ * USA.
+ * 
+ * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy,
+ * Mountain View, CA  94043, or:
+ * 
+ * http://www.sgi.com 
+ * 
+ * For further information regarding this notice, see: 
+ * 
+ * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
+ */
+
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib/gstrfuncs.h> /* g_strsplit, g_strfreev */
+
+/* From <glib/gmacros.h> */
+#ifndef G_N_ELEMENTS
+#define G_N_ELEMENTS(arr)          (sizeof (arr) / sizeof ((arr)[0]))
+#endif
+
+/* BSD-specific */
+#include <sys/extattr.h>
+#include <sys/uio.h>
+
+/*
+ * Linux provides extended attributes through the <sys/xattr.h> API.
+ * Any file or link can have attributes assigned to it (provided that they are
+ * supported by the backing filesystem). Each attribute has to be placed in a
+ * namespace, of which "user" is the most common. Namespaces are specified as
+ * a prefix to the attribute name, proceeded by a '.' (e.g. user.myattribute)
+ *
+ * FreeBSD provides extended attributes through the <sys/extattr.h> API.
+ * Behaviour is very similar to Linux EA's, but the namespace is specified
+ * through an enum-style parameter rather than as a prefix to an attribute
+ * name. There are also differences in the behaviour of the "list attributes"
+ * system calls.
+ *
+ * This file implements the same API as provided by Linux libattr, but using
+ * BSDish calls instead of Linux EA's.
+ *
+ * There is no limit to the namespaces accepted by the Linux side of this
+ * implementation, but you are obviously limited to the ones available to you
+ * on the system.
+ * FreeBSD namespaces have to be converted from the textual prefix into their
+ * relevant number so that they can be used in the FreeBSD system calls.
+ * This means that the only namespaces available are the ones known by in this
+ * file (see _bsd_extattr_namespaces). However, you can also specify the
+ * numericalnamespace index yourself, by using an attribute name such as
+ * "5.myattr".
+ * (this will obviously fail on Linux, your code will no longer be 'portable')
+ *
+ * Linux {,l,f}setxattr calls have a flags parameter which allow you to control
+ * what should happen if an attribute with the same name does (or doesn't)
+ * already exist. The 'flags' parameter is available here, but because FreeBSD
+ * does not support this kind of refinement, it will fail on FreeBSD if you
+ * specify anything other than XATTR_AUTO (XATTR_AUTO will create the attribute
+ * if it doesn't already exist, and overwrite the existing attribute if it
+ * already set).
+ * 
+ */
+
+//
+// HELPER FUNCTIONS
+//
+
+struct _BsdNamespaceInfo {
+	const char *name;
+	int value;
+};
+
+static struct _BsdNamespaceInfo _bsd_extattr_namespaces[] = {
+	{"user"         , EXTATTR_NAMESPACE_USER},
+	{"system"       , EXTATTR_NAMESPACE_SYSTEM}
+};
+
+static int _bsd_check_flags (int flags)
+{
+	// BSD doesn't support flags, but always provides the same behaviour as
+	// XATTR_AUTO. So we enforce that here.
+
+	/* In mono 1.1.8.2, Mono_Posix_XattrFlags_XATTR_AUTO is defined to 0. */
+#ifndef XATTR_AUTO
+#define XATTR_AUTO 0
+#endif
+	if (flags != XATTR_AUTO) {
+		errno = EINVAL;
+		return -1;
+	}
+	return 0;
+}
+
+// On FreeBSD, we need to convert "user.blah" into namespace 1 and attribute
+// name "blah", or maybe "6.blah" into namespace 6 attribute "blah"
+static int
+_bsd_handle_nsprefix (const char *name, char **_name, int *namespace)
+{
+	int i;
+	char **components = (char **) g_strsplit (name, ".", 2);
+
+	// Find namespace number from textual representation
+	for (i = 0; i < G_N_ELEMENTS(_bsd_extattr_namespaces); i++)
+		if (strcmp (_bsd_extattr_namespaces[i].name, components[0]) == 0) {
+			*namespace = _bsd_extattr_namespaces[i].value;
+			break;
+		}
+
+	if (*namespace == 0) {
+		// Perhaps they specified the namespace number themselves..?
+		char *endptr;
+		*namespace = (int) strtol (components[0], &endptr, 10);
+		if (*endptr != '\0')
+			return -1;
+	}
+
+	*_name = strdup (components[1]);
+	g_strfreev (components);
+	return 0;
+}
+
+static void
+_init_attrlists (char *attrlists[])
+{
+	memset (attrlists, 0, G_N_ELEMENTS(_bsd_extattr_namespaces) * sizeof(char*));
+}
+
+static void
+_free_attrlists (char *attrlists[])
+{
+	int i;
+	for (i = 0; i < G_N_ELEMENTS(_bsd_extattr_namespaces); i++)
+		free (attrlists[i]);
+}
+
+// Counts the number of attributes in the result of a
+// extattr_list_*() call. Note that the format of the data
+// is: \3one\3two\6eleven where the leading charaters represent the length
+// of the following attribute. (the description in the man-page is wrong)
+static unsigned int
+_count_num_attrs (char *attrs, size_t size)
+{
+	size_t i = 0;
+	unsigned int num_attrs = 0;
+
+	if (!attrs || !size)
+		return 0;
+
+	while (i < size) {
+		num_attrs++;
+		i += attrs[i] + 1;
+	}
+
+	return num_attrs;
+}
+
+// Convert a BSD-style list buffer (see the description for _count_num_attrs)
+// into a Linux-style NULL-terminated list including namespace prefix.
+static char
+*_bsd_convert_list (const char *nsprefix, const char *src, size_t size, char *dest)
+{
+	size_t i = 0;
+	if (src == NULL || dest == NULL || size == 0)
+		return NULL;
+
+	while (i < size) {
+		// Read length
+		int attr_len = (int) src[i];
+		int prefix_len = strlen (nsprefix);
+
+		// Add namespace prefix
+		strncpy (dest, nsprefix, prefix_len);
+		dest[prefix_len] = '.';
+		dest += prefix_len + 1;
+
+		// Copy attribute
+		memcpy(dest, src + ++i, attr_len);
+
+		// NULL-terminate
+		i += attr_len;
+		dest[attr_len] = '\0';
+		dest += attr_len + 1;
+	}
+
+	return dest;
+}
+
+// Combine all the lists of attributes that we know about into a single
+// Linux-style buffer
+static ssize_t
+_bsd_combine_lists (char *attrlists[], char *dest, size_t dest_size_needed, size_t dest_size)
+{
+	int i;
+	if (!dest)
+		return dest_size_needed;
+
+	if (dest_size < dest_size_needed) {
+		errno = ERANGE;
+		return -1;
+	}
+
+	for (i = 0; i < G_N_ELEMENTS(_bsd_extattr_namespaces); i++)
+		if (attrlists[i])
+			dest = _bsd_convert_list (_bsd_extattr_namespaces[i].name, attrlists[i], strlen (attrlists[i]), dest);
+
+	return dest_size_needed;
+}
+
+//
+// THE PROVIDED API
+//
+
+int
+setxattr (const char *path, const char *name, void *value, size_t size, int flags)
+{
+		int ret;
+		char *_name;
+		int namespace;
+		if (_bsd_check_flags (flags) == -1)
+			return -1;
+		if (_bsd_handle_nsprefix (name, &_name, &namespace) == -1)
+			return -1;
+		ret = syscall (SYS_extattr_set_file, path, namespace, _name, value, size);
+		free (_name);
+                return ret;
+}
+
+int
+lsetxattr (const char *path, const char *name, void *value, size_t size, int flags)
+{
+		int ret;
+		char *_name;
+		int namespace;
+		if (_bsd_check_flags (flags) == -1)
+			return -1;
+		if (_bsd_handle_nsprefix (name, &_name, &namespace) == -1)
+			return -1;
+		ret = syscall (SYS_extattr_set_link, path, namespace, _name, value, size);
+		free (_name);
+	        return ret;
+}
+
+int
+fsetxattr (int fd, const char *name, void *value, size_t size, int flags)
+{
+		int ret;
+		char *_name;
+		int namespace;
+		if (_bsd_check_flags (flags) == -1)
+			return -1;
+		if (_bsd_handle_nsprefix (name, &_name, &namespace) == -1)
+			return -1;
+		ret = syscall (SYS_extattr_set_fd, fd, namespace, _name, value, size);
+		free (_name);
+                return ret;
+}
+
+ssize_t
+getxattr (const char *path, const char *name, void *value, size_t size)
+{
+		ssize_t ret;
+		char *_name;
+		int namespace;
+		if (_bsd_handle_nsprefix (name, &_name, &namespace) == -1)
+			return -1;
+		ret = syscall (SYS_extattr_get_file, path, namespace, _name, value, size);
+		free (_name);
+                return ret;
+}
+
+ssize_t
+lgetxattr (const char *path, const char *name, void *value, size_t size)
+{
+		ssize_t ret;
+		char *_name;
+		int namespace;
+		if (_bsd_handle_nsprefix (name, &_name, &namespace) == -1)
+			return -1;
+		ret = syscall (SYS_extattr_get_link, path, namespace, _name, value, size);
+		free (_name);
+                return ret;
+}
+
+ssize_t
+fgetxattr (int fd, const char *name, void *value, size_t size)
+{
+		ssize_t ret;
+		char *_name;
+		int namespace;
+		if (_bsd_handle_nsprefix (name, &_name, &namespace) == -1)
+			return -1;
+		ret = syscall (SYS_extattr_get_fd, fd, namespace, _name, value, size);
+		free (_name);
+                return ret;
+}
+
+ssize_t
+listxattr (const char *path, void *list, size_t size)
+{
+	size_t full_size = 0;
+	int i;
+	char *attrlists[G_N_ELEMENTS(_bsd_extattr_namespaces)];
+
+	_init_attrlists (attrlists);
+	for (i = 0; i < G_N_ELEMENTS(_bsd_extattr_namespaces); i++) {
+		size_t buf_size;
+		int num_attrs;
+
+		buf_size = (size_t) syscall (SYS_extattr_list_file, path, i + 1, NULL, 0);
+		if (buf_size == -1)
+			continue;
+
+		attrlists[i] = malloc (buf_size + 1);
+		buf_size = (size_t) syscall (SYS_extattr_list_file, path, i + 1, attrlists[i], buf_size);
+		if (buf_size == -1)
+			continue;
+
+		num_attrs = _count_num_attrs(attrlists[i], buf_size);
+		full_size += buf_size + (num_attrs * (strlen (_bsd_extattr_namespaces[i].name) + 1));
+	}
+
+	full_size = _bsd_combine_lists (attrlists, (char *) list, full_size, size);
+	_free_attrlists (attrlists);
+	return full_size;
+}
+
+ssize_t
+llistxattr (const char *path, void *list, size_t size)
+{
+	size_t full_size = 0;
+	int i;
+	char *attrlists[G_N_ELEMENTS(_bsd_extattr_namespaces)];
+
+	_init_attrlists (attrlists);
+	for (i = 0; i < G_N_ELEMENTS(_bsd_extattr_namespaces); i++) {
+		size_t buf_size;
+		int num_attrs;
+
+		buf_size = (size_t) syscall (SYS_extattr_list_link, path, i + 1, NULL, 0);
+		if (buf_size == -1)
+			continue;
+
+		attrlists[i] = malloc (buf_size + 1);
+		buf_size = (size_t) syscall (SYS_extattr_list_link, path, i + 1, attrlists[i], buf_size);
+		if (buf_size == -1)
+			continue;
+
+		num_attrs = _count_num_attrs(attrlists[i], buf_size);
+		full_size += buf_size + (num_attrs * (strlen (_bsd_extattr_namespaces[i].name) + 1));
+	}
+
+	full_size = _bsd_combine_lists (attrlists, (char *) list, full_size, size);
+	_free_attrlists (attrlists);
+	return full_size;
+}
+
+ssize_t
+flistxattr (int fd, void *list, size_t size)
+{
+	size_t full_size = 0;
+	int i;
+	char *attrlists[G_N_ELEMENTS(_bsd_extattr_namespaces)];
+
+	_init_attrlists (attrlists);
+	for (i = 0; i < G_N_ELEMENTS(_bsd_extattr_namespaces); i++) {
+		size_t buf_size;
+		int num_attrs;
+
+		buf_size = (size_t) syscall (SYS_extattr_list_fd, fd, i + 1, NULL, 0);
+		if (buf_size == -1)
+			continue;
+
+		attrlists[i] = malloc (buf_size + 1);
+		buf_size = (size_t) syscall (SYS_extattr_list_fd, fd, i + 1, attrlists[i], buf_size);
+		if (buf_size == -1)
+			continue;
+
+		num_attrs = _count_num_attrs(attrlists[i], buf_size);
+		full_size += buf_size + (num_attrs * (strlen (_bsd_extattr_namespaces[i].name) + 1));
+	}
+
+	full_size = _bsd_combine_lists (attrlists, (char *) list, full_size, size);
+	_free_attrlists (attrlists);
+	return full_size;
+}
+
+int
+removexattr (const char *path, const char *name)
+{
+	int ret;
+
+		char *_name;
+		int namespace;
+		if (_bsd_handle_nsprefix (name, &_name, &namespace) == -1)
+			return -1;
+		ret = syscall (SYS_extattr_delete_file, path, namespace, _name);
+		free (_name);
+
+	return ret;
+}
+
+int
+lremovexattr (const char *path, const char *name)
+{
+	int ret;
+
+		char *_name;
+		int namespace;
+		if (_bsd_handle_nsprefix (name, &_name, &namespace) == -1)
+			return -1;
+		ret = syscall (SYS_extattr_delete_link, path, namespace, _name);
+		free (_name);
+
+	return ret;
+}
+
+int
+fremovexattr (int fd, const char *name)
+{
+	int ret;
+
+		char *_name;
+		int namespace;
+		if (_bsd_handle_nsprefix (name, &_name, &namespace) == -1)
+			return -1;
+		ret = syscall (SYS_extattr_delete_fd, fd, namespace, _name);
+		free (_name);
+
+	return ret;
+}
diff -Nur attr-2.4.21.old/libattr/syscalls.c attr-2.4.21/libattr/syscalls.c
--- attr-2.4.21.old/libattr/syscalls.c	2002-09-06 05:15:41.000000000 +0200
+++ attr-2.4.21/libattr/syscalls.c	2005-08-14 16:46:01.000000000 +0200
@@ -31,6 +31,10 @@
  * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/
  */
 
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include "syscalls-bsd.c"
+/* Maybe this should be in syscalls-linux.c ? */
+#elif defined(__linux__)
 /*
  * The use of the syscall() function is an additional level of
  * indirection.  This avoids the dependency on kernel sources.
@@ -272,3 +276,6 @@
 {
 	return SYSCALL(__NR_fremovexattr, filedes, name);
 }
+#else /* !__linux__ */
+#error
+#endif
diff -Nur attr-2.4.21.old/setfattr/setfattr.c attr-2.4.21/setfattr/setfattr.c
--- attr-2.4.21.old/setfattr/setfattr.c	2005-02-04 01:36:54.000000000 +0100
+++ attr-2.4.21/setfattr/setfattr.c	2005-08-14 16:25:55.000000000 +0200
@@ -67,8 +67,10 @@
 
 const char *strerror_ea(int err)
 {
+#ifdef ENODATA
 	if (err == ENODATA)
 		return _("No such attribute");
+#endif
 	return strerror(err);
 }
 
