Index: fs/Kconfig
===================================================================
RCS file: /home/roland/redhat/bkcvs/linux-2.5/fs/Kconfig,v
retrieving revision 1.38
diff -b -p -u -r1.38 Kconfig
--- fs/Kconfig  19 Feb 2004 04:49:14 -0000      1.38
+++ fs/Kconfig  19 Feb 2004 22:07:15 -0000
@@ -59,6 +59,14 @@ config EXT2_FS_XATTR

          If unsure, say N.

+config EXT2_FS_XATTR_HURD
+       bool "Ext2 GNU/Hurd special attribute support"
+       depends on EXT2_FS_XATTR
+       default y
+       help
+         Enable access to gnu.* extended attribute names on ext2 filesystems
+         created for use with the GNU/Hurd operating system.
+
 config EXT2_FS_POSIX_ACL
        bool "Ext2 POSIX Access Control Lists"
        depends on EXT2_FS_XATTR
Index: fs/ext2/Makefile
===================================================================
RCS file: /home/roland/redhat/bkcvs/linux-2.5/fs/ext2/Makefile,v
retrieving revision 1.10
diff -b -p -u -r1.10 Makefile
--- fs/ext2/Makefile	25 Jul 2003 23:00:05 -0000	1.10
+++ fs/ext2/Makefile	18 Feb 2004 00:52:48 -0000
@@ -10,3 +10,4 @@ ext2-y := balloc.o bitmap.o dir.o file.o
 ext2-$(CONFIG_EXT2_FS_XATTR)	 += xattr.o xattr_user.o xattr_trusted.o
 ext2-$(CONFIG_EXT2_FS_POSIX_ACL) += acl.o
 ext2-$(CONFIG_EXT2_FS_SECURITY)	 += xattr_security.o
+ext2-$(CONFIG_EXT2_FS_XATTR_HURD)+= xattr_hurd.o
Index: fs/ext2/ext2.h
===================================================================
RCS file: /home/roland/redhat/bkcvs/linux-2.5/fs/ext2/ext2.h,v
retrieving revision 1.13
diff -b -p -u -r1.13 ext2.h
--- fs/ext2/ext2.h	7 Jul 2003 02:41:12 -0000	1.13
+++ fs/ext2/ext2.h	18 Feb 2004 01:01:19 -0000
@@ -55,6 +55,10 @@ struct ext2_inode_info {
 	struct posix_acl	*i_acl;
 	struct posix_acl	*i_default_acl;
 #endif
+#ifdef CONFIG_EXT2_FS_XATTR_HURD
+	__u32	i_hurd_translator;
+	__u32	i_hurd_author;
+#endif
 	rwlock_t i_meta_lock;
 	struct inode	vfs_inode;
 };
Index: fs/ext2/ialloc.c
===================================================================
RCS file: /home/roland/redhat/bkcvs/linux-2.5/fs/ext2/ialloc.c,v
retrieving revision 1.39
diff -b -p -u -r1.39 ialloc.c
--- fs/ext2/ialloc.c	20 Jan 2004 05:07:51 -0000	1.39
+++ fs/ext2/ialloc.c	28 Feb 2004 23:10:27 -0000
@@ -590,6 +590,10 @@ got:
 	ei->i_frag_size = 0;
 	ei->i_file_acl = 0;
 	ei->i_dir_acl = 0;
+#ifdef CONFIG_EXT2_FS_XATTR_HURD
+	ei->i_hurd_translator = 0;
+	ei->i_hurd_author = inode->i_uid;
+#endif
 	ei->i_dtime = 0;
 	ei->i_block_group = group;
 	ei->i_next_alloc_block = 0;
Index: fs/ext2/inode.c
===================================================================
RCS file: /home/roland/redhat/bkcvs/linux-2.5/fs/ext2/inode.c,v
retrieving revision 1.71
diff -b -p -u -r1.71 inode.c
--- fs/ext2/inode.c	19 Jan 2004 18:06:27 -0000	1.71
+++ fs/ext2/inode.c	28 Feb 2004 23:12:16 -0000
@@ -1092,6 +1092,16 @@ void ext2_read_inode (struct inode * ino
 	ei->i_prealloc_count = 0;
 	ei->i_block_group = (ino - 1) / EXT2_INODES_PER_GROUP(inode->i_sb);
 	ei->i_dir_start_lookup = 0;
+#ifdef CONFIG_EXT2_FS_XATTR_HURD
+	if (EXT2_SB(inode->i_sb)->s_es->s_creator_os == cpu_to_le32(EXT2_OS_HURD)) {
+		ei->i_hurd_translator = le32_to_cpu(raw_inode->osd1.hurd1.h_i_translator);
+		ei->i_hurd_author = le32_to_cpu(raw_inode->osd2.hurd2.h_i_author);
+	}
+	else {
+		ei->i_hurd_translator = 0;
+		ei->i_hurd_author = inode->i_uid;
+	}
+#endif
 
 	/*
 	 * NOTE! The in-memory inode i_data array is in little-endian order
@@ -1218,6 +1228,13 @@ static int ext2_update_inode(struct inod
 		}
 	}
 	
+#ifdef CONFIG_EXT2_FS_XATTR_HURD
+	if (EXT2_SB(inode->i_sb)->s_es->s_creator_os == cpu_to_le32(EXT2_OS_HURD)) {
+		raw_inode->osd1.hurd1.h_i_translator = cpu_to_le32(ei->i_hurd_translator);
+		raw_inode->osd2.hurd2.h_i_author = cpu_to_le32(ei->i_hurd_author);
+	}
+#endif
+
 	raw_inode->i_generation = cpu_to_le32(inode->i_generation);
 	if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode)) {
 		if (old_valid_dev(inode->i_rdev)) {
Index: fs/ext2/xattr.c
===================================================================
RCS file: /home/roland/redhat/bkcvs/linux-2.5/fs/ext2/xattr.c,v
retrieving revision 1.20
diff -b -p -u -r1.20 xattr.c
--- fs/ext2/xattr.c	6 Feb 2004 16:50:15 -0000	1.20
+++ fs/ext2/xattr.c	28 Feb 2004 23:22:53 -0000
@@ -434,6 +434,38 @@ bad_block:	ext2_error(inode->i_sb, "ext2
 	error = size;
 
 cleanup:
+#ifdef CONFIG_EXT2_FS_XATTR_HURD
+	if (error >= 0) {
+		struct ext2_inode_info *ei = EXT2_I(inode);
+		buf = buffer + size;
+		if (ei->i_hurd_author != inode->i_uid) {
+			size += sizeof "gnu.author";
+			if (buffer) {
+				if (size > buffer_size)
+					error = -ERANGE;
+				else {
+					memcpy(buf, "gnu.author",
+					       sizeof "gnu.author");
+					buf += sizeof "gnu.author";
+				}
+			}
+		}
+		if (ei->i_hurd_translator != 0) {
+			size += sizeof "gnu.translator";
+			if (buffer) {
+				if (size > buffer_size)
+					error = -ERANGE;
+				else {
+					memcpy(buf, "gnu.translator",
+					       sizeof "gnu.translator");
+					buf += sizeof "gnu.translator";
+				}
+			}
+		}
+		if (error >= 0)
+			error = size;
+	}
+#endif
 	brelse(bh);
 	up_read(&EXT2_I(inode)->xattr_sem);
 
@@ -892,6 +924,14 @@ ext2_xattr_delete_inode(struct inode *in
 
 cleanup:
 	brelse(bh);
+#ifdef CONFIG_EXT2_FS_XATTR_HURD
+	printk("dropping inode %ld translator %u\n",
+	       inode->i_ino, EXT2_I(inode)->i_hurd_translator);
+	if (EXT2_I(inode)->i_hurd_translator != 0) {
+		ext2_free_blocks(inode, EXT2_I(inode)->i_hurd_translator, 1);
+		EXT2_I(inode)->i_hurd_translator = 0;
+	}
+#endif
 	up_write(&EXT2_I(inode)->xattr_sem);
 }
 
@@ -1125,10 +1165,16 @@ init_ext2_xattr(void)
 				  &ext2_xattr_user_handler);
 	if (err)
 		return err;
+#ifdef CONFIG_EXT2_FS_XATTR_HURD
+	err = ext2_xattr_register(EXT2_XATTR_INDEX_HURD,
+				  &ext2_xattr_hurd_handler);
+	if (err)
+		goto out;
+#endif
 	err = ext2_xattr_register(EXT2_XATTR_INDEX_TRUSTED,
 				  &ext2_xattr_trusted_handler);
 	if (err)
-		goto out;
+		goto out0;
 #ifdef CONFIG_EXT2_FS_SECURITY
 	err = ext2_xattr_register(EXT2_XATTR_INDEX_SECURITY,
 				  &ext2_xattr_security_handler);
@@ -1160,7 +1206,12 @@ out1:
 #endif
 	ext2_xattr_unregister(EXT2_XATTR_INDEX_TRUSTED,
 			      &ext2_xattr_trusted_handler);
+out0:
+#ifdef CONFIG_EXT2_FS_XATTR_HURD
+	ext2_xattr_unregister(EXT2_XATTR_INDEX_HURD,
+			      &ext2_xattr_hurd_handler);
 out:
+#endif
 	ext2_xattr_unregister(EXT2_XATTR_INDEX_USER,
 			      &ext2_xattr_user_handler);
 	return err;
Index: fs/ext2/xattr.h
===================================================================
RCS file: /home/roland/redhat/bkcvs/linux-2.5/fs/ext2/xattr.h,v
retrieving revision 1.8
diff -b -p -u -r1.8 xattr.h
--- fs/ext2/xattr.h	13 May 2003 06:12:52 -0000	1.8
+++ fs/ext2/xattr.h	13 Feb 2004 03:22:36 -0000
@@ -24,6 +24,7 @@
 #define EXT2_XATTR_INDEX_TRUSTED		4
 #define	EXT2_XATTR_INDEX_LUSTRE			5
 #define EXT2_XATTR_INDEX_SECURITY	        6
+#define EXT2_XATTR_INDEX_HURD			7
 
 struct ext2_xattr_header {
 	__u32	h_magic;	/* magic number for identification */
@@ -135,6 +136,6 @@ exit_ext2_xattr(void)
 # endif  /* CONFIG_EXT2_FS_XATTR */
 
 extern struct ext2_xattr_handler ext2_xattr_user_handler;
+extern struct ext2_xattr_handler ext2_xattr_hurd_handler;
 extern struct ext2_xattr_handler ext2_xattr_trusted_handler;
 extern struct ext2_xattr_handler ext2_xattr_security_handler;
-
--- /dev/null	2003-06-05 09:19:08.000000000 -0700
+++ fs/ext2/xattr_hurd.c	2004-02-28 15:11:57.000000000 -0800
@@ -0,0 +1,260 @@
+/*
+ * linux/fs/ext2/xattr_hurd.c
+ * Handler for Hurd-specific attributes.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/buffer_head.h>
+#include "ext2.h"
+#include "xattr.h"
+
+#define XATTR_HURD_PREFIX "gnu."
+
+static size_t
+ext2_xattr_hurd_list(char *list, struct inode *inode,
+		     const char *name, int name_len)
+{
+	const int prefix_len = sizeof(XATTR_HURD_PREFIX)-1;
+
+	if (!test_opt(inode->i_sb, XATTR_USER))
+		return 0;
+
+	if (list) {
+		memcpy(list, XATTR_HURD_PREFIX, prefix_len);
+		memcpy(list+prefix_len, name, name_len);
+		list[prefix_len + name_len] = '\0';
+	}
+	return prefix_len + name_len + 1;
+}
+
+static inline int
+IS_HURD_COMPAT(struct super_block *sb)
+{
+	return (EXT2_SB(sb)->s_es->s_creator_os == cpu_to_le32(EXT2_OS_HURD));
+}
+
+
+/*
+ * Report the "gnu.translator" and "gnu.author" attribute values
+ * from an inode in EXT2_OS_HURD format.
+ */
+static int
+hurd_compat_get(struct inode *inode, const char *name,
+		void *buffer, size_t size)
+{
+	int error = -EOPNOTSUPP;
+	struct ext2_inode_info *ei = EXT2_I(inode);
+
+	/*
+	 * Compatibility mode.
+	 */
+	if (!strcmp(name, "translator")) {
+		struct buffer_head *bh;
+		u16 len;
+		if (ei->i_hurd_translator == 0)
+			/* No translator set, empty.  */
+			return 0;
+		error = -EIO;
+		bh = sb_bread(inode->i_sb, ei->i_hurd_translator);
+		if (bh) {
+			len = le16_to_cpup(bh->b_data);
+			if (len > bh->b_size - 2)
+				error = -EFBIG;	/* ? */
+			else if (buffer == NULL)
+				/* Just return the total.  */
+				error = len;
+			else if (len > size)
+				error = -ERANGE;
+			else {
+				memcpy(buffer, bh->b_data + 2, len);
+				error = len;
+			}
+			brelse(bh);
+		}
+	}
+	else if (!strcmp(name, "author")) {
+		if (ei->i_hurd_author == inode->i_uid)
+			error = 0;
+		else {
+			if (buffer && size < sizeof ei->i_hurd_author)
+				error = -ERANGE;
+			else if (buffer)
+				memcpy(buffer, &ei->i_hurd_author,
+				       sizeof ei->i_hurd_author);
+			error = sizeof ei->i_hurd_author;
+		}
+	}
+
+	return error;
+}
+
+/*
+ * Store the "gnu.translator" and "gnu.author" attribute values
+ * in an inode in EXT2_OS_HURD format.
+ */
+static int
+compat_hurd_set(struct inode *inode, const char *name,
+		const void *value, size_t size, int flags)
+{
+	int error;
+	struct ext2_inode_info *ei = EXT2_I(inode);
+
+	/*
+	 * Compatibility mode.  We actually have to re-read the
+	 * raw inode because ext2_read_inode does not cache this field.
+	 */
+	if (!strcmp(name, "translator")) {
+		u32 bno;
+		u16 len;
+		struct buffer_head *bh;
+
+		error = permission(inode, MAY_WRITE, NULL);
+		if (error)
+			return error;
+		if (ei->i_hurd_translator == 0) {
+			/* No existing translator.  */
+			if (flags & XATTR_REPLACE)
+				return -ENODATA;
+			if (size == 0) { /* Nothing really to do.  */
+				inode->i_ctime = CURRENT_TIME;
+				mark_inode_dirty(inode);
+				return 0;
+			}
+		}
+		else if (flags & XATTR_CREATE)
+			return -EEXIST;
+
+		if (size == 0) { /* Removing translator.  */
+			bno = ei->i_hurd_translator;
+			ei->i_hurd_translator = 0;
+			ext2_free_blocks(inode, bno, 1);
+			unmap_underlying_metadata(inode->i_sb->s_bdev,
+						  bno);
+			inode->i_ctime = CURRENT_TIME;
+			mark_inode_dirty(inode);
+			return 0;
+		}
+
+		if (size > inode->i_sb->s_blocksize - 2)
+			return -ERANGE;
+
+		bno = ei->i_hurd_translator;
+		if (bno == 0) {
+			/*
+			 * Need to allocate a new block.
+			 */
+			struct super_block *sb = inode->i_sb;
+			u32 goal = le32_to_cpu(EXT2_SB(sb)->s_es->
+					       s_first_data_block) +
+				EXT2_I(inode)->i_block_group *
+				EXT2_BLOCKS_PER_GROUP(sb);
+			bno = ext2_new_block(inode, goal, 0, 0,
+					     &error);
+			if (error)
+				return error;
+		}
+
+		bh = sb_getblk(inode->i_sb, bno);
+		lock_buffer(bh);
+		len = cpu_to_le16(size);
+		memcpy(bh->b_data, &len, sizeof len);
+		memcpy(bh->b_data + 2, value, size);
+		set_buffer_uptodate(bh);
+		unlock_buffer(bh);
+		mark_buffer_dirty(bh);
+		brelse(bh);
+
+		ei->i_hurd_translator = bno;
+		inode->i_ctime = CURRENT_TIME;
+		mark_inode_dirty(inode);
+		return 0;
+	}
+
+	if (!strcmp(name, "author")) {
+		error = permission(inode, MAY_WRITE, NULL);
+		if (error)
+			return error;
+		switch (size) {
+		default:
+			return -EINVAL;
+		case 0:
+			ei->i_hurd_author = inode->i_uid;
+			break;
+		case sizeof ei->i_hurd_author:
+			memcpy(&ei->i_hurd_author,
+			       value, sizeof ei->i_hurd_author);
+			break;
+		}
+		inode->i_ctime = CURRENT_TIME;
+		mark_inode_dirty(inode);
+		return 0;
+	}
+
+	return -EOPNOTSUPP;
+}
+
+static int
+ext2_xattr_hurd_get(struct inode *inode, const char *name,
+		    void *buffer, size_t size)
+{
+	int error;
+
+	if (IS_HURD_COMPAT(inode->i_sb)) {
+		down_read(&EXT2_I(inode)->xattr_sem);
+		error = hurd_compat_get(inode, name, buffer, size);
+		up_read(&EXT2_I(inode)->xattr_sem);
+		if (error != -EOPNOTSUPP)
+			return error;
+	}
+
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+
+	if (!test_opt(inode->i_sb, XATTR_USER))	/* XXX ? */
+		return -EOPNOTSUPP;
+	error = permission(inode, MAY_READ, NULL);
+	if (error)
+		return error;
+
+	return ext2_xattr_get(inode, EXT2_XATTR_INDEX_HURD, name,
+			      buffer, size);
+}
+
+static int
+ext2_xattr_hurd_set(struct inode *inode, const char *name,
+		    const void *value, size_t size, int flags)
+{
+	int error;
+
+	if (value == NULL)
+		size = 0;
+
+	if (IS_HURD_COMPAT(inode->i_sb)) {
+		down_write(&EXT2_I(inode)->xattr_sem);
+		error = compat_hurd_set(inode, name, value, size, flags);
+		up_write(&EXT2_I(inode)->xattr_sem);
+		if (error != -EOPNOTSUPP)
+			return error;
+	}
+
+	if (strcmp(name, "") == 0)
+		return -EINVAL;
+
+	if (!test_opt(inode->i_sb, XATTR_USER))	/* XXX ? */
+		return -EOPNOTSUPP;
+	error = permission(inode, MAY_WRITE, NULL);
+	if (error)
+		return error;
+
+	return ext2_xattr_set(inode, EXT2_XATTR_INDEX_HURD, name,
+			      value, size, flags);
+}
+
+struct ext2_xattr_handler ext2_xattr_hurd_handler = {
+	.prefix	= XATTR_HURD_PREFIX,
+	.list	= ext2_xattr_hurd_list,
+	.get	= ext2_xattr_hurd_get,
+	.set	= ext2_xattr_hurd_set,
+};


