@@ -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
@@ -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;
}
/*
@@ -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);
new file mode 100644
@@ -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,
+};
@@ -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;