Fedora 11 New Extended File Attributes Namespace

I am experimenting with XAM (eXtensible Access Method), which is a storage standard developed by SNIA (Storage Networking Industry Association and have developed a first prototype of a VIM (Vendor Interface Module) for the ext4 file system based on adding another namespace to the current extended file attributes support.  Since other Linux developers might be interested in how to add an extended attributes namespace to a file system, I decided to publish this post as a guide.

Extended file attributes (EA) are extensions to the normal attributes which are associated with inodes in a file system.  They are simply name:value pairs associated with files and directories and whose purpose is to provide additional functionality which is either defined by the operating system or a user application.  An EA may be defined or undefined.  If an EA is defined, its value may be empty or non-empty.  Most of the initial work to support EAs in Linux was done by Andreas Gruenbacher in the 2001 to 2004 timeframe and is based on work done in the SGI XFS file system.

EAs are stored directly in inodes and on additional disk blocks if necessary.  Currently all EAs associated with a file must fit in an inode and one additional block.  Blocks that contain the identical set of EAs may be shared among several inodes.  EAs in inodes and on blocks have a different header followed by multiple entry descriptors.  Entry descriptors are sorted in disk blocks but are left unsorted in inodes.

EA names are zero-terminated strings and are always specified using a fully qualified namespace.attribute e.g. system.posix_acl_access or security.selinuxNamespaces are used to define different classes of EAs.  Different EA classes are required because the permissions and capabilities required for manipulating EAs in one namespace may differ from one to another.  Currently Linux supports EA namespaces for SELinux (security.), system (system.), trusted (trusted.) and user (user.).

Fedora 11 comes with EA support in the Linux kernel and hence it is the kernel which must be modified in order to add another EA namespace.  In my case I wanted to add an EA namespace called snia in order to support XAM XSet metadata (see below).

Here is the patch file for the Fedora 11 2.6.29.4-167.fc11 kernel which implements the snia namespace.  It should work for both 32-bit and 64-bit kernels but I have only tested it on a 64-bit kernel.
diff -uNrp kernel-2.6.29.orig/fs/ext4/Makefile kernel-2.6.29.new/fs/ext4/Makefile
--- kernel-2.6.29.orig/fs/ext4/Makefile 2009-06-14 22:07:12.191464615 -0400
+++ kernel-2.6.29.new/fs/ext4/Makefile 2009-06-14 22:06:20.009399924 -0400
@@ -8,6 +8,6 @@ ext4-y := balloc.o bitmap.o dir.o file.o
ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
ext4_jbd2.o migrate.o mballoc.o

-ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o
+ext4-$(CONFIG_EXT4_FS_XATTR) += xattr.o xattr_user.o xattr_trusted.o xattr_snia.o
ext4-$(CONFIG_EXT4_FS_POSIX_ACL) += acl.o
ext4-$(CONFIG_EXT4_FS_SECURITY) += xattr_security.o
diff -uNrp kernel-2.6.29.orig/fs/ext4/xattr.c kernel-2.6.29.new/fs/ext4/xattr.c
--- kernel-2.6.29.orig/fs/ext4/xattr.c 2009-06-14 22:11:20.699475221 -0400
+++ kernel-2.6.29.new/fs/ext4/xattr.c 2009-06-14 22:09:48.242839367 -0400
@@ -107,6 +107,7 @@ static struct xattr_handler *ext4_xattr_
#ifdef CONFIG_EXT4_FS_SECURITY
[EXT4_XATTR_INDEX_SECURITY] = &ext4_xattr_security_handler,
#endif
+ [EXT4_XATTR_INDEX_SNIA] = &ext4_xattr_snia_handler,
};

struct xattr_handler *ext4_xattr_handlers[] = {
@@ -119,6 +120,7 @@ struct xattr_handler *ext4_xattr_handler
#ifdef CONFIG_EXT4_FS_SECURITY
&ext4_xattr_security_handler,
#endif
+ &ext4_xattr_snia_handler,
NULL
};
diff -uNrp kernel-2.6.29.orig/fs/ext4/xattr.h kernel-2.6.29.new/fs/ext4/xattr.h
--- kernel-2.6.29.orig/fs/ext4/xattr.h 2009-06-14 22:09:27.016464602 -0400
+++ kernel-2.6.29.new/fs/ext4/xattr.h 2009-06-14 22:07:44.887839381 -0400
@@ -21,6 +21,7 @@
#define EXT4_XATTR_INDEX_TRUSTED 4
#define EXT4_XATTR_INDEX_LUSTRE 5
#define EXT4_XATTR_INDEX_SECURITY 6
+#define EXT4_XATTR_INDEX_SNIA 7

struct ext4_xattr_header {
__le32 h_magic; /* magic number for identification */
@@ -70,6 +71,7 @@ extern struct xattr_handler ext4_xattr_t
extern struct xattr_handler ext4_xattr_acl_access_handler;
extern struct xattr_handler ext4_xattr_acl_default_handler;
extern struct xattr_handler ext4_xattr_security_handler;
+extern struct xattr_handler ext4_xattr_snia_handler;

extern ssize_t ext4_listxattr(struct dentry *, char *, size_t);
diff -uNrp kernel-2.6.29.orig/fs/ext4/xattr_snia.c kernel-2.6.29.new/fs/ext4/xattr_snia.c
--- kernel-2.6.29.orig/fs/ext4/xattr_snia.c 1969-12-31 19:00:00.000000000 -0500
+++ kernel-2.6.29.new/fs/ext4/xattr_snia.c 2009-06-14 13:37:02.551476954 -0400
@@ -0,0 +1,68 @@
+/*
+ * linux/fs/ext4/xattr_snia.c
+ * Handler for extended user attributes.
+ *
+ * Copyright (C) 2001 by Andreas Gruenbacher,
+ * Copyright (C) 2009 by Finnbarr P. Murphy,
+ */
+
+#include
+#include
+#include
+#include "ext4_jbd2.h"
+#include "ext4.h"
+#include "xattr.h"
+
+static size_t
+ext4_xattr_snia_list(struct inode *inode, char *list, size_t list_size,
+ const char *name, size_t name_len)
+{
+ const size_t prefix_len = XATTR_SNIA_PREFIX_LEN;
+ const size_t total_len = prefix_len + name_len + 1;
+
+#if FPM
+ if (!test_opt(inode->i_sb, XATTR_USER))
+ return 0;
+#endif
+
+ if (list && total_len <= list_size) {
+ memcpy(list, XATTR_SNIA_PREFIX, prefix_len);
+ memcpy(list+prefix_len, name, name_len);
+ list[prefix_len + name_len] = '\0';
+ }
+ return total_len;
+}
+
+static int
+ext4_xattr_snia_get(struct inode *inode, const char *name,
+ void *buffer, size_t size)
+{
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+#if FPM
+ if (!test_opt(inode->i_sb, XATTR_USER))
+ return -EOPNOTSUPP;
+#endif
+ return ext4_xattr_get(inode, EXT4_XATTR_INDEX_SNIA, name, buffer, size);
+}
+
+static int
+ext4_xattr_snia_set(struct inode *inode, const char *name,
+ const void *value, size_t size, int flags)
+{
+ if (strcmp(name, "") == 0)
+ return -EINVAL;
+#if FPM
+ if (!test_opt(inode->i_sb, XATTR_USER))
+ return -EOPNOTSUPP;
+#endif
+ return ext4_xattr_set(inode, EXT4_XATTR_INDEX_SNIA, name,
+ value, size, flags);
+}
+
+struct xattr_handler ext4_xattr_snia_handler = {
+ .prefix = XATTR_SNIA_PREFIX,
+ .list = ext4_xattr_snia_list,
+ .get = ext4_xattr_snia_get,
+ .set = ext4_xattr_snia_set,
+};
diff -uNrp kernel-2.6.29.orig/fs/xattr.c kernel-2.6.29.new/fs/xattr.c
--- kernel-2.6.29.orig/fs/xattr.c 2009-06-14 22:04:37.472464234 -0400
+++ kernel-2.6.29.new/fs/xattr.c 2009-06-14 22:05:56.759530990 -0400
@@ -42,6 +42,7 @@ xattr_permission(struct inode *inode, co
* on these is left to the underlying filesystem / security module.
*/
if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN) ||
+ !strncmp(name, XATTR_SNIA_PREFIX, XATTR_SNIA_PREFIX_LEN) ||
!strncmp(name, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
return 0;

diff -uNrp kernel-2.6.29.orig/include/linux/xattr.h kernel-2.6.29.new/include/linux/xattr.h
--- kernel-2.6.29.orig/include/linux/xattr.h 2009-06-14 14:53:02.350464255 -0400
+++ kernel-2.6.29.new/include/linux/xattr.h 2009-06-14 14:51:07.585839446 -0400
@@ -33,6 +33,9 @@
#define XATTR_USER_PREFIX "user."
#define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)

+#define XATTR_SNIA_PREFIX "snia."
+#define XATTR_SNIA_PREFIX_LEN (sizeof (XATTR_SNIA_PREFIX) - 1)
+
struct inode;
struct dentry;
I am going to assume that if you are still reading this post you know how to build, package and install a Linux kernel in Fedora.  If not, here is a good tutorial.  The only caveat I would make is to ignore the comment that "most text editors know how to break the hardlink correctly to avoid problems."  That is just plain wrong.  You need to have separate standalone copies of any source file that you intend modifying in both the original and new kernel source trees - not a single hard linked copy - before you start making changes in the new kernel source tree.

Once you are running on the new kernel, you can set and get EAs in the snia namespace as shown in this simple example.
$ touch testfile
$ setfattr -n snia.test -v "hello world" testfile
$ getfattr -n snia.test testfile
# file: testfile
snia.test="hello world"

$
In the next example I set and get the metadata (called fields in XAM terminology) for a sample XAM XSet.  A field has a name and a value, and the following standard attributes: type, length, binding and readonly.  The attributes and values roughly correspond to the fields outputted as an XML document to a persisted XSet by the ReferenceXSetClient example included with the XAM SDK which is available on the SNIA website.  For an introduction to the concepts of an XSystem and XSets a good starting place is Mark Carlson's blog.
$ cat xamtest
# file: testfile
snia.xset.hold.type="application/vnd.snia.xam.boolean"
snia.xset.hold.binding="false"
snia.xset.hold.readOnly="true"
snia.xset.hold.length="1"
snia.xset.hold="0"
snia.xset.management.policy.type="application/vnd.snia.xam.string"
snia.xset.management.policy.binding="true"
snia.xset.management.policy.readOnly="true"
snia.xset.management.policy.length="36"
snia.xset.management.policy="org.snia.refvim.default.mgmt.policy"
snia.xset.retention.base.enabled.type="application/vnd.snia.xam.boolean"
snia.xset.retention.base.enabled.binding="true"
snia.xset.retention.base.enabled.readOnly="true"
snia.xset.retention.base.enabled.length="1"
snia.xset.retention.base="1"
snia.xset.retention.base.starttime.type="application/vnd.snia.xam.datetime"
snia.xset.retention.base.starttime.binding="true"
snia.xset.retention.base.starttime.readOnly="true"
snia.xset.retention.base.starttime.length="29"
snia.xset.retention.base.starttime="2009-06-12T22:34:35.350-05:00"
snia.xset.retention.list.base.type="application/vnd.snia.xam.string"
snia.xset.retention.list.base.binding="true"
snia.xset.retention.list.base.readOnly="true"
snia.xset.retention.list.base.length="4"
snia.xset.retention.list.base="base"
snia.xset.retention.list.event.type="application/vnd.snia.xam.string"
snia.xset.retention.list.event.binding="true"
snia.xset.retention.list.event.readOnly="true"
snia.xset.retention.list.event.length="5"
snia.xset.retention.list.event="event"
snia.xset.time.access.type="application/vnd.snia.xam.datetime"
snia.xset.time.access.binding="false"
snia.xset.time.access.readOnly="true"
snia.xset.time.access.length="29"
snia.xset.time.access="2009-06-12T22:34:35.350-05:00"
snia.xset.time.commit.type="application/vnd.snia.xam.datetime"
snia.xset.time.commit.binding="false"
snia.xset.time.commit.readOnly="true"
snia.xset.time.commit.length="29"
snia.xset.time.commit="2009-06-12T22:34:35.350-05:00"
snia.xset.time.access.type="application/vnd.snia.xam.datetime"
snia.xset.time.access.binding="false"
snia.xset.time.access.readOnly="true"
snia.xset.time.access.length="29"
snia.xset.time.access="2009-06-12T22:34:35.350-05:00"
snia.xset.time.commit.type="application/vnd.snia.xam.datetime"
snia.xset.time.commit.binding="false"
snia.xset.time.commit.readOnly="true"
snia.xset.time.commit.length="29"
snia.xset.time.commit="2009-06-12T22:34:35.350-05:00"
snia.xset.time.creation.type="application/vnd.snia.xam.datetime"
snia.xset.time.creation.binding="true"
snia.xset.time.creation.readOnly="true"
snia.xset.time.creation.length="29"
snia.xset.time.creation="2009-06-12T22:34:35.331-05:00"
snia.xset.time.xuid.type="application/vnd.snia.xam.datetime"
snia.xset.time.xuid.binding="false"
snia.xset.time.xuid.readOnly="true"
snia.xset.time.xuid.length="29"
snia.xset.time.xuid="2009-06-12T22:34:35.331-05:00"
snia.xset.xuid.type="application/vnd.snia.xam.xuid"
snia.xset.xuid.binding="true"
snia.xset.xuid.readOnly="true"
snia.xset.xuid.length="30"
snia.xset.xuid="AAA6AwAeVG4xMjQ0ODY0MDc1MzU4AQWDedILNL7Q"

$ setfattr --restore=xamtest

$ getfattr -m snia. -d testfile
# file: testfile
snia.test="hello world"
snia.xset.hold="0"
snia.xset.hold.binding="false"
snia.xset.hold.length="1"
snia.xset.hold.readOnly="true"
snia.xset.hold.type="\"application/vnd.snia.xam.boolean\" "
snia.xset.management.policy="org.snia.refvim.default.mgmt.policy"
snia.xset.management.policy.binding="true"
snia.xset.management.policy.length="36"
snia.xset.management.policy.readOnly="true"
snia.xset.management.policy.type="application/vnd.snia.xam.string"
snia.xset.retention.base="1"
snia.xset.retention.base.enabled.binding="\"true\" "
snia.xset.retention.base.enabled.length="1"
snia.xset.retention.base.enabled.readOnly="true"
snia.xset.retention.base.enabled.type="application/vnd.snia.xam.boolean"
snia.xset.retention.base.starttime="2009-06-12T22:34:35.350-05:00"
snia.xset.retention.base.starttime.binding="true"
snia.xset.retention.base.starttime.length="29"
snia.xset.retention.base.starttime.readOnly="true"
snia.xset.retention.base.starttime.type="\"application/vnd.snia.xam.datetime\" "
snia.xset.retention.list.base="base"
snia.xset.retention.list.base.binding="true"
snia.xset.retention.list.base.length="4"
snia.xset.retention.list.base.readOnly="true"
snia.xset.retention.list.base.type="application/vnd.snia.xam.string"
snia.xset.retention.list.event="event"
snia.xset.retention.list.event.binding="true"
snia.xset.retention.list.event.length="5"
snia.xset.retention.list.event.readOnly="true"
snia.xset.retention.list.event.type="application/vnd.snia.xam.string"
snia.xset.time.access="2009-06-12T22:34:35.350-05:00"
snia.xset.time.access.binding="false"
snia.xset.time.access.length="29"
snia.xset.time.access.readOnly="true"
snia.xset.time.access.type="application/vnd.snia.xam.datetime"
snia.xset.time.commit="2009-06-12T22:34:35.350-05:00"
snia.xset.time.commit.binding="false"
snia.xset.time.commit.length="29"
snia.xset.time.commit.readOnly="true"
snia.xset.time.commit.type="application/vnd.snia.xam.datetime"
snia.xset.time.creation="2009-06-12T22:34:35.331-05:00"
snia.xset.time.creation.binding="true"
snia.xset.time.creation.length="29"
snia.xset.time.creation.readOnly="true"
snia.xset.time.creation.type="application/vnd.snia.xam.datetime"
snia.xset.time.xuid="2009-06-12T22:34:35.331-05:00"
snia.xset.time.xuid.binding="false"
snia.xset.time.xuid.length="29"
snia.xset.time.xuid.readOnly="true"
snia.xset.time.xuid.type="application/vnd.snia.xam.datetime"
snia.xset.xuid="AAA6AwAeVG4xMjQ0ODY0MDc1MzU4AQWDedILNL7Q"
snia.xset.xuid.binding="true"
snia.xset.xuid.length="30"
snia.xset.xuid.readOnly="true"
snia.xset.xuid.type="application/vnd.snia.xam.xuid"
$
Obviously this work is a proof-of-concept only.  The actual XAM namespace is org.snia.xam.* and not the shortened version, i.e. snia.*, which I used in the proof-of-concept.  I have not tested it sufficiently to discover boundary conditions or size limitations yet.  I suspect that it might be better to store the snia namespace attributes in their own block(s) rather than sharing an inode or block with the other four namespaces and this is what I intend to prototype next.

If anybody else is interested in working with me in this space I would welcome their involvement.
 

0 comments:

Post a Comment