diff mbox

Ext4: Make file creation time, i_version and i_generation available by xattrs

Message ID 20100628162626.6026.26679.stgit@warthog.procyon.org.uk (mailing list archive)
State New, archived
Headers show

Commit Message

David Howells June 28, 2010, 4:26 p.m. UTC
None
diff mbox

Patch

diff --git a/fs/ext4/Makefile b/fs/ext4/Makefile
index 8867b2a..034dd27 100644
--- a/fs/ext4/Makefile
+++ b/fs/ext4/Makefile
@@ -8,6 +8,8 @@  ext4-y	:= balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
 		ioctl.o namei.o super.o symlink.o hash.o resize.o extents.o \
 		ext4_jbd2.o migrate.o mballoc.o block_validity.o move_extent.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_file.o
+
 ext4-$(CONFIG_EXT4_FS_POSIX_ACL)	+= acl.o
 ext4-$(CONFIG_EXT4_FS_SECURITY)		+= xattr_security.o
diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c
index 0433800..1360f7c 100644
--- a/fs/ext4/xattr.c
+++ b/fs/ext4/xattr.c
@@ -111,6 +111,7 @@  static const struct xattr_handler *ext4_xattr_handler_map[] = {
 
 const struct xattr_handler *ext4_xattr_handlers[] = {
 	&ext4_xattr_user_handler,
+	&ext4_xattr_file_handler,
 	&ext4_xattr_trusted_handler,
 #ifdef CONFIG_EXT4_FS_POSIX_ACL
 	&ext4_xattr_acl_access_handler,
@@ -427,23 +428,35 @@  cleanup:
 static int
 ext4_xattr_list(struct dentry *dentry, char *buffer, size_t buffer_size)
 {
-	int i_error, b_error;
+	int ret, result = 0;
 
 	down_read(&EXT4_I(dentry->d_inode)->xattr_sem);
-	i_error = ext4_xattr_ibody_list(dentry, buffer, buffer_size);
-	if (i_error < 0) {
-		b_error = 0;
-	} else {
-		if (buffer) {
-			buffer += i_error;
-			buffer_size -= i_error;
-		}
-		b_error = ext4_xattr_block_list(dentry, buffer, buffer_size);
-		if (b_error < 0)
-			i_error = 0;
+	ret = ext4_xattr_ibody_list(dentry, buffer, buffer_size);
+	if (ret < 0)
+		goto error;
+	result += ret;
+	if (buffer) {
+		buffer += ret;
+		buffer_size -= ret;
+	}
+
+	ret = ext4_xattr_block_list(dentry, buffer, buffer_size);
+	if (ret < 0)
+		goto error;
+	result += ret;
+	if (buffer) {
+		buffer += ret;
+		buffer_size -= ret;
 	}
+
+	ret = ext4_xattr_file_list(dentry, buffer, buffer_size);
+	if (ret < 0)
+		goto error;
+	result += ret;
+
+error:
 	up_read(&EXT4_I(dentry->d_inode)->xattr_sem);
-	return i_error + b_error;
+	return ret < 0 ? ret : result;
 }
 
 /*
diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h
index 518e96e..f0e3aaf 100644
--- a/fs/ext4/xattr.h
+++ b/fs/ext4/xattr.h
@@ -66,12 +66,14 @@  struct ext4_xattr_entry {
 # ifdef CONFIG_EXT4_FS_XATTR
 
 extern const struct xattr_handler ext4_xattr_user_handler;
+extern const struct xattr_handler ext4_xattr_file_handler;
 extern const struct xattr_handler ext4_xattr_trusted_handler;
 extern const struct xattr_handler ext4_xattr_acl_access_handler;
 extern const struct xattr_handler ext4_xattr_acl_default_handler;
 extern const struct xattr_handler ext4_xattr_security_handler;
 
 extern ssize_t ext4_listxattr(struct dentry *, char *, size_t);
+extern int ext4_xattr_file_list(struct dentry *, char *, size_t);
 
 extern int ext4_xattr_get(struct inode *, int, const char *, void *, size_t);
 extern int ext4_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
diff --git a/fs/ext4/xattr_file.c b/fs/ext4/xattr_file.c
new file mode 100644
index 0000000..81044c5
--- /dev/null
+++ b/fs/ext4/xattr_file.c
@@ -0,0 +1,108 @@ 
+/* File-specific xattrs
+ *
+ * Copyright (C) 2010 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/capability.h>
+#include <linux/fs.h>
+#include "ext4_jbd2.h"
+#include "ext4.h"
+#include "xattr.h"
+
+static const char *ext4_file_xattrs[] = {
+	"crtime",
+	"i_generation"
+};
+
+static const char *ext4_dir_xattrs[] = {
+	"i_version",
+};
+
+int
+ext4_xattr_file_list(struct dentry *dentry, char *list, size_t list_size)
+{
+	struct ext4_inode_info *ei = EXT4_I(dentry->d_inode);
+	const size_t prefix_len = XATTR_FILE_PREFIX_LEN;
+	int total_len = 0;
+	int loop;
+
+	for (loop = 0; loop < ARRAY_SIZE(ext4_file_xattrs); loop++) {
+		const char *fxname = ext4_file_xattrs[loop];
+		int fxnlen = strlen(fxname);
+
+		total_len += prefix_len + fxnlen + 1;
+		if (list && total_len <= list_size) {
+			memcpy(list, XATTR_FILE_PREFIX, prefix_len);
+			list += prefix_len;
+			memcpy(list, fxname, fxnlen + 1);
+			list += fxnlen + 1;
+		}
+	}
+
+	if (!S_ISDIR(ei->vfs_inode.i_mode))
+		goto out;
+
+	/* Ext4 only supports i_version on directories */
+	for (loop = 0; loop < ARRAY_SIZE(ext4_dir_xattrs); loop++) {
+		const char *fxname = ext4_dir_xattrs[loop];
+		int fxnlen = strlen(fxname);
+
+		total_len += prefix_len + fxnlen + 1;
+		if (list && total_len <= list_size) {
+			memcpy(list, XATTR_FILE_PREFIX, prefix_len);
+			list += prefix_len;
+			memcpy(list, fxname, fxnlen + 1);
+			list += fxnlen + 1;
+		}
+	}
+
+out:
+	return total_len;
+}
+
+static int
+ext4_xattr_file_get(struct dentry *dentry, const char *name, void *buffer,
+		    size_t size, int type)
+{
+	struct ext4_inode_info *ei = EXT4_I(dentry->d_inode);
+	size_t result_size;
+	union {
+		struct timespec ts;
+		u64 val;
+	} result;
+
+	if (strcmp(name, "crtime") == 0) {
+		result_size = sizeof(struct timespec);
+		result.ts = ei->i_crtime;
+	} else if (strcmp(name, "i_version") == 0) {
+		if (!S_ISDIR(ei->vfs_inode.i_mode))
+			return -ENOTDIR;
+		result_size = sizeof(u64);
+		result.val = ei->vfs_inode.i_version;
+	} else if (strcmp(name, "i_generation") == 0) {
+		result_size = sizeof(u64);
+		result.val = ei->vfs_inode.i_generation;
+	} else {
+		return -EINVAL;
+	}
+
+	if (size == 0)
+		return result_size;
+	if (size < result_size)
+		return -E2BIG;
+	memcpy(buffer, &result, result_size);
+	return result_size;
+}
+
+const struct xattr_handler ext4_xattr_file_handler = {
+	.prefix	= XATTR_FILE_PREFIX,
+	.get	= ext4_xattr_file_get,
+};
diff --git a/include/linux/xattr.h b/include/linux/xattr.h
index 0cfa1e9..e52a8ce 100644
--- a/include/linux/xattr.h
+++ b/include/linux/xattr.h
@@ -33,6 +33,9 @@ 
 #define XATTR_USER_PREFIX "user."
 #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1)
 
+#define XATTR_FILE_PREFIX "file."
+#define XATTR_FILE_PREFIX_LEN (sizeof (XATTR_FILE_PREFIX) - 1)
+
 struct inode;
 struct dentry;