Commit 8603affb authored by Theodore Y. Ts'o's avatar Theodore Y. Ts'o Committed by Linus Torvalds

Port of (bugfixed) 0.8.50 xattr-ext2 to 2.5 (w/ hch cleanups. mbcache API)

This patch adds extended attribute support to the ext2 filesystem.  This
uses the generic extended attribute patch which was developed by Andreas
Gruenbacher and the XFS team.  As a result, the user space utilities
which work for XFS will also work with these patches.
parent f7cfad91
......@@ -946,6 +946,16 @@ config EXT2_FS
be compiled as a module, and so this could be dangerous. Most
everyone wants to say Y here.
config EXT2_FS_XATTR
bool "Ext2 extended attributes"
depends on EXT2_FS
---help---
Extended attributes are name:value pairs associated with inodes by
the kernel or by users (see the attr(5) manual page, or visit
<http://acl.bestbits.at/> for details).
If unsure, say N.
config SYSV_FS
tristate "System V/Xenix/V7/Coherent file system support"
---help---
......
......@@ -7,4 +7,10 @@ obj-$(CONFIG_EXT2_FS) += ext2.o
ext2-objs := balloc.o bitmap.o dir.o file.o fsync.o ialloc.o inode.o \
ioctl.o namei.o super.o symlink.o
export-objs += xattr.o
ifeq ($(CONFIG_EXT2_FS_XATTR),y)
ext2-objs += xattr.o xattr_user.o
endif
include $(TOPDIR)/Rules.make
......@@ -117,6 +117,8 @@ extern struct address_space_operations ext2_aops;
/* namei.c */
extern struct inode_operations ext2_dir_inode_operations;
extern struct inode_operations ext2_special_inode_operations;
/* symlink.c */
extern struct inode_operations ext2_fast_symlink_inode_operations;
extern struct inode_operations ext2_symlink_inode_operations;
......@@ -18,8 +18,9 @@
* (jj@sunsite.ms.mff.cuni.cz)
*/
#include "ext2.h"
#include <linux/time.h>
#include "ext2.h"
#include "xattr.h"
/*
* Called when an inode is released. Note that this is different
......@@ -55,4 +56,8 @@ struct file_operations ext2_file_operations = {
struct inode_operations ext2_file_inode_operations = {
.truncate = ext2_truncate,
.setxattr = ext2_setxattr,
.getxattr = ext2_getxattr,
.listxattr = ext2_listxattr,
.removexattr = ext2_removexattr,
};
......@@ -13,11 +13,12 @@
*/
#include <linux/config.h>
#include "ext2.h"
#include <linux/quotaops.h>
#include <linux/sched.h>
#include <linux/backing-dev.h>
#include <linux/buffer_head.h>
#include "ext2.h"
#include "xattr.h"
/*
* ialloc.c contains the inodes allocation and deallocation routines
......@@ -97,6 +98,7 @@ void ext2_free_inode (struct inode * inode)
*/
if (!is_bad_inode(inode)) {
/* Quota is already initialized in iput() */
ext2_xattr_delete_inode(inode);
DQUOT_FREE_INODE(inode);
DQUOT_DROP(inode);
}
......
......@@ -38,6 +38,18 @@ MODULE_LICENSE("GPL");
static int ext2_update_inode(struct inode * inode, int do_sync);
/*
* Test whether an inode is a fast symlink.
*/
static inline int ext2_inode_is_fast_symlink(struct inode *inode)
{
int ea_blocks = EXT2_I(inode)->i_file_acl ?
(inode->i_sb->s_blocksize >> 9) : 0;
return (S_ISLNK(inode->i_mode) &&
inode->i_blocks - ea_blocks == 0);
}
/*
* Called at each iput()
*/
......@@ -51,9 +63,7 @@ void ext2_put_inode (struct inode * inode)
*/
void ext2_delete_inode (struct inode * inode)
{
if (is_bad_inode(inode) ||
inode->i_ino == EXT2_ACL_IDX_INO ||
inode->i_ino == EXT2_ACL_DATA_INO)
if (is_bad_inode(inode))
goto no_delete;
EXT2_I(inode)->i_dtime = CURRENT_TIME;
mark_inode_dirty(inode);
......@@ -843,6 +853,8 @@ void ext2_truncate (struct inode * inode)
if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
S_ISLNK(inode->i_mode)))
return;
if (ext2_inode_is_fast_symlink(inode))
return;
if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
return;
......@@ -929,8 +941,7 @@ static struct ext2_inode *ext2_get_inode(struct super_block *sb, ino_t ino,
struct ext2_group_desc * gdp;
*p = NULL;
if ((ino != EXT2_ROOT_INO && ino != EXT2_ACL_IDX_INO &&
ino != EXT2_ACL_DATA_INO && ino < EXT2_FIRST_INO(sb)) ||
if ((ino != EXT2_ROOT_INO && ino < EXT2_FIRST_INO(sb)) ||
ino > le32_to_cpu(EXT2_SB(sb)->s_es->s_inodes_count))
goto Einval;
......@@ -1026,9 +1037,7 @@ void ext2_read_inode (struct inode * inode)
for (n = 0; n < EXT2_N_BLOCKS; n++)
ei->i_data[n] = raw_inode->i_block[n];
if (ino == EXT2_ACL_IDX_INO || ino == EXT2_ACL_DATA_INO)
/* Nothing to do */ ;
else if (S_ISREG(inode->i_mode)) {
if (S_ISREG(inode->i_mode)) {
inode->i_op = &ext2_file_inode_operations;
inode->i_fop = &ext2_file_operations;
inode->i_mapping->a_ops = &ext2_aops;
......@@ -1037,15 +1046,17 @@ void ext2_read_inode (struct inode * inode)
inode->i_fop = &ext2_dir_operations;
inode->i_mapping->a_ops = &ext2_aops;
} else if (S_ISLNK(inode->i_mode)) {
if (!inode->i_blocks)
if (ext2_inode_is_fast_symlink(inode))
inode->i_op = &ext2_fast_symlink_inode_operations;
else {
inode->i_op = &page_symlink_inode_operations;
inode->i_op = &ext2_symlink_inode_operations;
inode->i_mapping->a_ops = &ext2_aops;
}
} else
} else {
inode->i_op = &ext2_special_inode_operations;
init_special_inode(inode, inode->i_mode,
le32_to_cpu(raw_inode->i_block[0]));
}
brelse (bh);
if (ei->i_flags & EXT2_SYNC_FL)
inode->i_flags |= S_SYNC;
......@@ -1082,12 +1093,6 @@ static int ext2_update_inode(struct inode * inode, int do_sync)
if (ei->i_state & EXT2_STATE_NEW)
memset(raw_inode, 0, EXT2_SB(sb)->s_inode_size);
if (ino == EXT2_ACL_IDX_INO || ino == EXT2_ACL_DATA_INO) {
ext2_error (sb, "ext2_write_inode", "bad inode number: %lu",
(unsigned long) ino);
brelse(bh);
return -EIO;
}
raw_inode->i_mode = cpu_to_le16(inode->i_mode);
if (!(test_opt(sb, NO_UID32))) {
raw_inode->i_uid_low = cpu_to_le16(low_16_bits(uid));
......
......@@ -29,8 +29,9 @@
* David S. Miller (davem@caip.rutgers.edu), 1995
*/
#include "ext2.h"
#include <linux/pagemap.h>
#include "ext2.h"
#include "xattr.h"
/*
* Couple of helper functions - make the code slightly cleaner.
......@@ -162,7 +163,7 @@ static int ext2_symlink (struct inode * dir, struct dentry * dentry,
if (l > sizeof (EXT2_I(inode)->i_data)) {
/* slow symlink */
inode->i_op = &page_symlink_inode_operations;
inode->i_op = &ext2_symlink_inode_operations;
inode->i_mapping->a_ops = &ext2_aops;
err = page_symlink(inode, symname, l);
if (err)
......@@ -368,4 +369,15 @@ struct inode_operations ext2_dir_inode_operations = {
.rmdir = ext2_rmdir,
.mknod = ext2_mknod,
.rename = ext2_rename,
.setxattr = ext2_setxattr,
.getxattr = ext2_getxattr,
.listxattr = ext2_listxattr,
.removexattr = ext2_removexattr,
};
struct inode_operations ext2_special_inode_operations = {
.setxattr = ext2_setxattr,
.getxattr = ext2_getxattr,
.listxattr = ext2_listxattr,
.removexattr = ext2_removexattr,
};
......@@ -19,7 +19,6 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/string.h>
#include "ext2.h"
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/blkdev.h>
......@@ -27,6 +26,8 @@
#include <linux/buffer_head.h>
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include "ext2.h"
#include "xattr.h"
static void ext2_sync_super(struct super_block *sb,
......@@ -127,6 +128,7 @@ static void ext2_put_super (struct super_block * sb)
int i;
struct ext2_sb_info *sbi = EXT2_SB(sb);
ext2_xattr_put_super(sb);
if (!(sb->s_flags & MS_RDONLY)) {
struct ext2_super_block *es = sbi->s_es;
......@@ -278,6 +280,13 @@ static int parse_options (char * options,
continue;
if ((value = strchr (this_char, '=')) != NULL)
*value++ = 0;
#ifdef CONFIG_EXT2_FS_XATTR
if (!strcmp (this_char, "user_xattr"))
set_opt (sbi->s_mount_opt, XATTR_USER);
else if (!strcmp (this_char, "nouser_xattr"))
clear_opt (sbi->s_mount_opt, XATTR_USER);
else
#endif
if (!strcmp (this_char, "bsddf"))
clear_opt (sbi->s_mount_opt, MINIX_DF);
else if (!strcmp (this_char, "nouid32")) {
......@@ -560,6 +569,8 @@ static int ext2_fill_super(struct super_block *sb, void *data, int silent)
set_opt(sbi->s_mount_opt, GRPID);
if (def_mount_opts & EXT2_DEFM_UID16)
set_opt(sbi->s_mount_opt, NO_UID32);
if (def_mount_opts & EXT2_DEFM_XATTR_USER)
set_opt(sbi->s_mount_opt, XATTR_USER);
if (le16_to_cpu(sbi->s_es->s_errors) == EXT2_ERRORS_PANIC)
set_opt(sbi->s_mount_opt, ERRORS_PANIC);
......@@ -917,7 +928,10 @@ static struct file_system_type ext2_fs_type = {
static int __init init_ext2_fs(void)
{
int err = init_inodecache();
int err = init_ext2_xattr();
if (err)
return err;
err = init_inodecache();
if (err)
goto out1;
err = register_filesystem(&ext2_fs_type);
......@@ -927,6 +941,7 @@ static int __init init_ext2_fs(void)
out:
destroy_inodecache();
out1:
exit_ext2_xattr();
return err;
}
......@@ -934,6 +949,7 @@ static void __exit exit_ext2_fs(void)
{
unregister_filesystem(&ext2_fs_type);
destroy_inodecache();
exit_ext2_xattr();
}
module_init(init_ext2_fs)
......
......@@ -18,6 +18,7 @@
*/
#include "ext2.h"
#include "xattr.h"
static int ext2_readlink(struct dentry *dentry, char *buffer, int buflen)
{
......@@ -31,7 +32,20 @@ static int ext2_follow_link(struct dentry *dentry, struct nameidata *nd)
return vfs_follow_link(nd, (char *)ei->i_data);
}
struct inode_operations ext2_symlink_inode_operations = {
.readlink = page_readlink,
.follow_link = page_follow_link,
.setxattr = ext2_setxattr,
.getxattr = ext2_getxattr,
.listxattr = ext2_listxattr,
.removexattr = ext2_removexattr,
};
struct inode_operations ext2_fast_symlink_inode_operations = {
.readlink = ext2_readlink,
.follow_link = ext2_follow_link,
.setxattr = ext2_setxattr,
.getxattr = ext2_getxattr,
.listxattr = ext2_listxattr,
.removexattr = ext2_removexattr,
};
This diff is collapsed.
/*
File: linux/ext2_xattr.h
On-disk format of extended attributes for the ext2 filesystem.
(C) 2001 Andreas Gruenbacher, <a.gruenbacher@computer.org>
*/
#include <linux/config.h>
#include <linux/init.h>
#include <linux/xattr.h>
/* Magic value in attribute blocks */
#define EXT2_XATTR_MAGIC 0xEA020000
/* Maximum number of references to one attribute block */
#define EXT2_XATTR_REFCOUNT_MAX 1024
/* Name indexes */
#define EXT2_XATTR_INDEX_MAX 10
#define EXT2_XATTR_INDEX_USER 1
#define EXT2_XATTR_INDEX_POSIX_ACL_ACCESS 2
#define EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT 3
struct ext2_xattr_header {
__u32 h_magic; /* magic number for identification */
__u32 h_refcount; /* reference count */
__u32 h_blocks; /* number of disk blocks used */
__u32 h_hash; /* hash value of all attributes */
__u32 h_reserved[4]; /* zero right now */
};
struct ext2_xattr_entry {
__u8 e_name_len; /* length of name */
__u8 e_name_index; /* attribute name index */
__u16 e_value_offs; /* offset in disk block of value */
__u32 e_value_block; /* disk block attribute is stored on (n/i) */
__u32 e_value_size; /* size of attribute value */
__u32 e_hash; /* hash value of name and value */
char e_name[0]; /* attribute name */
};
#define EXT2_XATTR_PAD_BITS 2
#define EXT2_XATTR_PAD (1<<EXT2_XATTR_PAD_BITS)
#define EXT2_XATTR_ROUND (EXT2_XATTR_PAD-1)
#define EXT2_XATTR_LEN(name_len) \
(((name_len) + EXT2_XATTR_ROUND + \
sizeof(struct ext2_xattr_entry)) & ~EXT2_XATTR_ROUND)
#define EXT2_XATTR_NEXT(entry) \
( (struct ext2_xattr_entry *)( \
(char *)(entry) + EXT2_XATTR_LEN((entry)->e_name_len)) )
#define EXT2_XATTR_SIZE(size) \
(((size) + EXT2_XATTR_ROUND) & ~EXT2_XATTR_ROUND)
# ifdef CONFIG_EXT2_FS_XATTR
struct ext2_xattr_handler {
char *prefix;
size_t (*list)(char *list, struct inode *inode, const char *name,
int name_len);
int (*get)(struct inode *inode, const char *name, void *buffer,
size_t size);
int (*set)(struct inode *inode, const char *name, const void *buffer,
size_t size, int flags);
};
extern int ext2_xattr_register(int, struct ext2_xattr_handler *);
extern void ext2_xattr_unregister(int, struct ext2_xattr_handler *);
extern int ext2_setxattr(struct dentry *, const char *, void *, size_t, int);
extern ssize_t ext2_getxattr(struct dentry *, const char *, void *, size_t);
extern ssize_t ext2_listxattr(struct dentry *, char *, size_t);
extern int ext2_removexattr(struct dentry *, const char *);
extern int ext2_xattr_get(struct inode *, int, const char *, void *, size_t);
extern int ext2_xattr_list(struct inode *, char *, size_t);
extern int ext2_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
extern void ext2_xattr_delete_inode(struct inode *);
extern void ext2_xattr_put_super(struct super_block *);
extern int init_ext2_xattr(void);
extern void exit_ext2_xattr(void);
# else /* CONFIG_EXT2_FS_XATTR */
# define ext2_setxattr NULL
# define ext2_getxattr NULL
# define ext2_listxattr NULL
# define ext2_removexattr NULL
static inline int
ext2_xattr_get(struct inode *inode, int name_index,
const char *name, void *buffer, size_t size)
{
return -EOPNOTSUPP;
}
static inline int
ext2_xattr_list(struct inode *inode, char *buffer, size_t size)
{
return -EOPNOTSUPP;
}
static inline int
ext2_xattr_set(struct inode *inode, int name_index, const char *name,
const void *value, size_t size, int flags)
{
return -EOPNOTSUPP;
}
static inline void
ext2_xattr_delete_inode(struct inode *inode)
{
}
static inline void
ext2_xattr_put_super(struct super_block *sb)
{
}
static inline int
init_ext2_xattr(void)
{
return 0;
}
static inline void
exit_ext2_xattr(void)
{
}
# endif /* CONFIG_EXT2_FS_XATTR */
extern struct ext2_xattr_handler ext2_xattr_user_handler;
/*
* linux/fs/ext2/xattr_user.c
* Handler for extended user attributes.
*
* Copyright (C) 2001 by Andreas Gruenbacher, <a.gruenbacher@computer.org>
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/string.h>
#include "ext2.h"
#include "xattr.h"
#ifdef CONFIG_EXT2_FS_POSIX_ACL
# include "acl.h"
#endif
#define XATTR_USER_PREFIX "user."
static size_t
ext2_xattr_user_list(char *list, struct inode *inode,
const char *name, int name_len)
{
const int prefix_len = sizeof(XATTR_USER_PREFIX)-1;
if (!test_opt(inode->i_sb, XATTR_USER))
return 0;
if (list) {
memcpy(list, XATTR_USER_PREFIX, prefix_len);
memcpy(list+prefix_len, name, name_len);
}
return prefix_len + name_len;
}
static int
ext2_xattr_user_get(struct inode *inode, const char *name,
void *buffer, size_t size)
{
int error;
if (strcmp(name, "") == 0)
return -EINVAL;
if (!test_opt(inode->i_sb, XATTR_USER))
return -EOPNOTSUPP;
#ifdef CONFIG_EXT2_FS_POSIX_ACL
error = ext2_permission_locked(inode, MAY_READ);
#else
error = permission(inode, MAY_READ);
#endif
if (error)
return error;
return ext2_xattr_get(inode, EXT2_XATTR_INDEX_USER, name,
buffer, size);
}
static int
ext2_xattr_user_set(struct inode *inode, const char *name,
const void *value, size_t size, int flags)
{
int error;
if (strcmp(name, "") == 0)
return -EINVAL;
if (!test_opt(inode->i_sb, XATTR_USER))
return -EOPNOTSUPP;
if ( !S_ISREG(inode->i_mode) &&
(!S_ISDIR(inode->i_mode) || inode->i_mode & S_ISVTX))
return -EPERM;
#ifdef CONFIG_EXT2_FS_POSIX_ACL
error = ext2_permission_locked(inode, MAY_WRITE);
#else
error = permission(inode, MAY_WRITE);
#endif
if (error)
return error;
return ext2_xattr_set(inode, EXT2_XATTR_INDEX_USER, name,
value, size, flags);
}
struct ext2_xattr_handler ext2_xattr_user_handler = {
prefix: XATTR_USER_PREFIX,
list: ext2_xattr_user_list,
get: ext2_xattr_user_get,
set: ext2_xattr_user_set,
};
int __init
init_ext2_xattr_user(void)
{
return ext2_xattr_register(EXT2_XATTR_INDEX_USER,
&ext2_xattr_user_handler);
}
void
exit_ext2_xattr_user(void)
{
ext2_xattr_unregister(EXT2_XATTR_INDEX_USER,
&ext2_xattr_user_handler);
}
......@@ -58,8 +58,6 @@
*/
#define EXT2_BAD_INO 1 /* Bad blocks inode */
#define EXT2_ROOT_INO 2 /* Root inode */
#define EXT2_ACL_IDX_INO 3 /* ACL inode */
#define EXT2_ACL_DATA_INO 4 /* ACL inode */
#define EXT2_BOOT_LOADER_INO 5 /* Boot loader inode */
#define EXT2_UNDEL_DIR_INO 6 /* Undelete directory inode */
......@@ -99,7 +97,6 @@ static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb)
#else
# define EXT2_BLOCK_SIZE(s) (EXT2_MIN_BLOCK_SIZE << (s)->s_log_block_size)
#endif
#define EXT2_ACLE_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (struct ext2_acl_entry))
#define EXT2_ADDR_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / sizeof (__u32))
#ifdef __KERNEL__
# define EXT2_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits)
......@@ -133,28 +130,6 @@ static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb)
# define EXT2_FRAGS_PER_BLOCK(s) (EXT2_BLOCK_SIZE(s) / EXT2_FRAG_SIZE(s))
#endif
/*
* ACL structures
*/
struct ext2_acl_header /* Header of Access Control Lists */
{
__u32 aclh_size;
__u32 aclh_file_count;
__u32 aclh_acle_count;
__u32 aclh_first_acle;
};
struct ext2_acl_entry /* Access Control List Entry */
{
__u32 acle_size;
__u16 acle_perms; /* Access permissions */
__u16 acle_type; /* Type of entry */
__u16 acle_tag; /* User or group identity */
__u16 acle_pad1;
__u32 acle_next; /* Pointer on next entry for the */
/* same inode or on next free entry */
};
/*
* Structure of a blocks group descriptor
*/
......@@ -332,6 +307,7 @@ struct ext2_inode {
#define EXT2_MOUNT_ERRORS_PANIC 0x0040 /* Panic on errors */
#define EXT2_MOUNT_MINIX_DF 0x0080 /* Mimics the Minix statfs */
#define EXT2_MOUNT_NO_UID32 0x0200 /* Disable 32-bit UIDs */
#define EXT2_MOUNT_XATTR_USER 0x4000 /* Extended user attributes */
#define clear_opt(o, opt) o &= ~EXT2_MOUNT_##opt
#define set_opt(o, opt) o |= EXT2_MOUNT_##opt
......@@ -489,7 +465,7 @@ struct ext2_super_block {
#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010
#define EXT2_FEATURE_INCOMPAT_ANY 0xffffffff
#define EXT2_FEATURE_COMPAT_SUPP 0
#define EXT2_FEATURE_COMPAT_SUPP EXT2_FEATURE_COMPAT_EXT_ATTR
#define EXT2_FEATURE_INCOMPAT_SUPP (EXT2_FEATURE_INCOMPAT_FILETYPE| \
EXT2_FEATURE_INCOMPAT_META_BG)
#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment