--- /dev/null
+Index: linux-2.6.10/mm/truncate.c
+===================================================================
+--- linux-2.6.10.orig/mm/truncate.c 2005-04-06 23:38:35.000000000 +0800
++++ linux-2.6.10/mm/truncate.c 2005-04-07 14:55:03.019979256 +0800
+@@ -42,7 +42,7 @@
+ * its lock, b) when a concurrent invalidate_inode_pages got there first and
+ * c) when tmpfs swizzles a page between a tmpfs inode and swapper_space.
+ */
+-static void
++void
+ truncate_complete_page(struct address_space *mapping, struct page *page)
+ {
+ if (page->mapping != mapping)
+@@ -58,6 +58,8 @@
+ page_cache_release(page); /* pagecache ref */
+ }
+
++EXPORT_SYMBOL(truncate_complete_page);
++
+ /*
+ * This is for invalidate_inode_pages(). That function can be called at
+ * any time, and is not supposed to throw away dirty pages. But pages can
+Index: linux-2.6.10/net/core/sock.c
+===================================================================
+--- linux-2.6.10.orig/net/core/sock.c 2004-12-25 05:35:23.000000000 +0800
++++ linux-2.6.10/net/core/sock.c 2005-04-07 14:55:03.020979104 +0800
+@@ -1359,6 +1359,7 @@
+ EXPORT_SYMBOL(sk_alloc);
+ EXPORT_SYMBOL(sk_free);
+ EXPORT_SYMBOL(sk_send_sigurg);
++EXPORT_SYMBOL(sock_getsockopt);
+ EXPORT_SYMBOL(sock_alloc_send_pskb);
+ EXPORT_SYMBOL(sock_alloc_send_skb);
+ EXPORT_SYMBOL(sock_init_data);
+Index: linux-2.6.10/fs/dcache.c
+===================================================================
+--- linux-2.6.10.orig/fs/dcache.c 2005-04-07 00:35:35.000000000 +0800
++++ linux-2.6.10/fs/dcache.c 2005-04-07 14:55:03.003981688 +0800
+@@ -1737,6 +1737,7 @@
+
+ EXPORT_SYMBOL(d_alloc);
+ EXPORT_SYMBOL(d_alloc_anon);
++EXPORT_SYMBOL(is_subdir);
+ EXPORT_SYMBOL(d_alloc_root);
+ EXPORT_SYMBOL(d_delete);
+ EXPORT_SYMBOL(d_find_alias);
+Index: linux-2.6.10/fs/jbd/journal.c
+===================================================================
+--- linux-2.6.10.orig/fs/jbd/journal.c 2004-12-25 05:34:01.000000000 +0800
++++ linux-2.6.10/fs/jbd/journal.c 2005-04-07 14:55:03.006981232 +0800
+@@ -71,6 +71,7 @@
+ EXPORT_SYMBOL(journal_errno);
+ EXPORT_SYMBOL(journal_ack_err);
+ EXPORT_SYMBOL(journal_clear_err);
++EXPORT_SYMBOL(log_start_commit);
+ EXPORT_SYMBOL(log_wait_commit);
+ EXPORT_SYMBOL(journal_start_commit);
+ EXPORT_SYMBOL(journal_force_commit_nested);
+Index: linux-2.6.10/fs/namespace.c
+===================================================================
+--- linux-2.6.10.orig/fs/namespace.c 2005-04-07 00:35:35.000000000 +0800
++++ linux-2.6.10/fs/namespace.c 2005-04-07 14:55:03.007981080 +0800
+@@ -1233,6 +1233,7 @@
+ mntput(old_pwdmnt);
+ }
+ }
++EXPORT_SYMBOL(set_fs_pwd);
+
+ static void chroot_fs_refs(struct nameidata *old_nd, struct nameidata *new_nd)
+ {
+Index: linux-2.6.10/fs/file_table.c
+===================================================================
+--- linux-2.6.10.orig/fs/file_table.c 2004-12-25 05:33:50.000000000 +0800
++++ linux-2.6.10/fs/file_table.c 2005-04-07 14:55:03.004981536 +0800
+@@ -196,6 +196,7 @@
+ file_free(file);
+ }
+ }
++EXPORT_SYMBOL(put_filp);
+
+ void file_move(struct file *file, struct list_head *list)
+ {
+Index: linux-2.6.10/kernel/sched.c
+===================================================================
+--- linux-2.6.10.orig/kernel/sched.c 2005-04-07 00:35:34.000000000 +0800
++++ linux-2.6.10/kernel/sched.c 2005-04-07 14:55:03.018979408 +0800
+@@ -2942,6 +2942,19 @@
+
+ EXPORT_SYMBOL(sleep_on_timeout);
+
++void fastcall __sched sleep_on(wait_queue_head_t *q)
++{
++ SLEEP_ON_VAR
++
++ current->state = TASK_UNINTERRUPTIBLE;
++
++ SLEEP_ON_HEAD
++ schedule();
++ SLEEP_ON_TAIL
++}
++
++EXPORT_SYMBOL(sleep_on);
++
+ void set_user_nice(task_t *p, long nice)
+ {
+ unsigned long flags;
+Index: linux-2.6.10/kernel/exit.c
+===================================================================
+--- linux-2.6.10.orig/kernel/exit.c 2005-04-06 23:44:29.000000000 +0800
++++ linux-2.6.10/kernel/exit.c 2005-04-07 14:55:03.014980016 +0800
+@@ -246,6 +246,8 @@
+ switch_uid(INIT_USER);
+ }
+
++EXPORT_SYMBOL(reparent_to_init);
++
+ void __set_special_pids(pid_t session, pid_t pgrp)
+ {
+ struct task_struct *curr = current;
+@@ -427,6 +429,7 @@
+ {
+ __exit_files(tsk);
+ }
++EXPORT_SYMBOL(exit_files);
+
+ static inline void __put_fs_struct(struct fs_struct *fs)
+ {
+@@ -512,6 +515,7 @@
+ {
+ __exit_mm(tsk);
+ }
++EXPORT_SYMBOL(exit_mm);
+
+ static inline void choose_new_parent(task_t *p, task_t *reaper, task_t *child_reaper)
+ {
+Index: linux-2.6.10/include/linux/fs.h
+===================================================================
+--- linux-2.6.10.orig/include/linux/fs.h 2005-04-07 14:54:24.454842048 +0800
++++ linux-2.6.10/include/linux/fs.h 2005-04-07 14:55:03.010980624 +0800
+@@ -1183,6 +1183,7 @@
+ extern struct vfsmount *kern_mount(struct file_system_type *);
+ extern int may_umount_tree(struct vfsmount *);
+ extern int may_umount(struct vfsmount *);
++struct vfsmount *do_kern_mount(const char *type, int flags, const char *name, void *data);
+ extern long do_mount(char *, char *, char *, unsigned long, void *);
+
+ extern int vfs_statfs(struct super_block *, struct kstatfs *);
+Index: linux-2.6.10/include/linux/mm.h
+===================================================================
+--- linux-2.6.10.orig/include/linux/mm.h 2005-04-07 00:22:23.000000000 +0800
++++ linux-2.6.10/include/linux/mm.h 2005-04-07 14:55:03.012980320 +0800
+@@ -715,6 +715,9 @@
+ extern unsigned long __do_brk(unsigned long, unsigned long);
+ extern unsigned long do_brk(unsigned long, unsigned long);
+
++/* truncate.c */
++extern void truncate_complete_page(struct address_space *mapping,struct page *);
++
+ /* filemap.c */
+ extern unsigned long page_unuse(struct page *);
+ extern void truncate_inode_pages(struct address_space *, loff_t);
--- /dev/null
+%patch
+Index: linux-2.6.10/fs/ext3/inode.c
+===================================================================
+--- linux-2.6.10.orig/fs/ext3/inode.c 2005-03-31 20:12:32.748192632 +0800
++++ linux-2.6.10/fs/ext3/inode.c 2005-03-31 20:12:35.433784360 +0800
+@@ -2276,7 +2276,7 @@
+ * trying to determine the inode's location on-disk and no read need be
+ * performed.
+ */
+-static int ext3_get_inode_loc(struct inode *inode,
++int ext3_get_inode_loc(struct inode *inode,
+ struct ext3_iloc *iloc, int in_mem)
+ {
+ unsigned long block;
+@@ -2484,6 +2484,11 @@
+ ei->i_data[block] = raw_inode->i_block[block];
+ INIT_LIST_HEAD(&ei->i_orphan);
+
++ if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE)
++ ei->i_extra_isize = le16_to_cpu(raw_inode->i_extra_isize);
++ else
++ ei->i_extra_isize = 0;
++
+ if (S_ISREG(inode->i_mode)) {
+ inode->i_op = &ext3_file_inode_operations;
+ inode->i_fop = &ext3_file_operations;
+@@ -2619,6 +2624,9 @@
+ } else for (block = 0; block < EXT3_N_BLOCKS; block++)
+ raw_inode->i_block[block] = ei->i_data[block];
+
++ if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE)
++ raw_inode->i_extra_isize = cpu_to_le16(ei->i_extra_isize);
++
+ BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata");
+ rc = ext3_journal_dirty_metadata(handle, bh);
+ if (!err)
+Index: linux-2.6.10/fs/ext3/ialloc.c
+===================================================================
+--- linux-2.6.10.orig/fs/ext3/ialloc.c 2005-03-31 20:12:28.734802760 +0800
++++ linux-2.6.10/fs/ext3/ialloc.c 2005-03-31 20:12:35.434784208 +0800
+@@ -627,6 +627,11 @@
+ spin_unlock(&sbi->s_next_gen_lock);
+
+ ei->i_state = EXT3_STATE_NEW;
++ if (EXT3_INODE_SIZE(inode->i_sb) > EXT3_GOOD_OLD_INODE_SIZE) {
++ ei->i_extra_isize = sizeof(__u16) /* i_extra_isize */
++ + sizeof(__u16); /* i_pad1 */
++ } else
++ ei->i_extra_isize = 0;
+
+ ret = inode;
+ if(DQUOT_ALLOC_INODE(inode)) {
+Index: linux-2.6.10/fs/ext3/xattr.c
+===================================================================
+--- linux-2.6.10.orig/fs/ext3/xattr.c 2005-03-31 19:44:53.846384040 +0800
++++ linux-2.6.10/fs/ext3/xattr.c 2005-03-31 20:13:25.376191960 +0800
+@@ -149,17 +149,12 @@
+ }
+
+ /*
+- * ext3_xattr_get()
+- *
+- * Copy an extended attribute into the buffer
+- * provided, or compute the buffer size required.
+- * Buffer is NULL to compute the size of the buffer required.
++ * ext3_xattr_block_get()
+ *
+- * Returns a negative error number on failure, or the number of bytes
+- * used / required on success.
++ * routine looks for attribute in EA block and returns it's value and size
+ */
+ int
+-ext3_xattr_get(struct inode *inode, int name_index, const char *name,
++ext3_xattr_block_get(struct inode *inode, int name_index, const char *name,
+ void *buffer, size_t buffer_size)
+ {
+ struct buffer_head *bh = NULL;
+@@ -173,7 +168,6 @@
+
+ if (name == NULL)
+ return -EINVAL;
+- down_read(&EXT3_I(inode)->xattr_sem);
+ error = -ENODATA;
+ if (!EXT3_I(inode)->i_file_acl)
+ goto cleanup;
+@@ -246,15 +240,87 @@
+
+ cleanup:
+ brelse(bh);
+- up_read(&EXT3_I(inode)->xattr_sem);
+
+ return error;
+ }
+
+ /*
+- * ext3_xattr_list()
++ * ext3_xattr_ibode_get()
+ *
+- * Copy a list of attribute names into the buffer
++ * routine looks for attribute in inode body and returns it's value and size
++ */
++int
++ext3_xattr_ibody_get(struct inode *inode, int name_index, const char *name,
++ void *buffer, size_t buffer_size)
++{
++ int size, name_len = strlen(name), storage_size;
++ struct ext3_xattr_entry *last;
++ struct ext3_inode *raw_inode;
++ struct ext3_iloc iloc;
++ char *start, *end;
++ int ret = -ENOENT;
++
++ if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE)
++ return -ENOENT;
++
++ ret = ext3_get_inode_loc(inode, &iloc, 1);
++ if (ret)
++ return ret;
++ raw_inode = ext3_raw_inode(&iloc);
++
++ storage_size = EXT3_SB(inode->i_sb)->s_inode_size -
++ EXT3_GOOD_OLD_INODE_SIZE -
++ EXT3_I(inode)->i_extra_isize -
++ sizeof(__u32);
++ start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE +
++ EXT3_I(inode)->i_extra_isize;
++ if (le32_to_cpu((*(__u32*) start)) != EXT3_XATTR_MAGIC) {
++ brelse(iloc.bh);
++ return -ENOENT;
++ }
++ start += sizeof(__u32);
++ end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size;
++
++ last = (struct ext3_xattr_entry *) start;
++ while (!IS_LAST_ENTRY(last)) {
++ struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last);
++ if (le32_to_cpu(last->e_value_size) > storage_size ||
++ (char *) next >= end) {
++ ext3_error(inode->i_sb, "ext3_xattr_ibody_get",
++ "inode %ld", inode->i_ino);
++ brelse(iloc.bh);
++ return -EIO;
++ }
++ if (name_index == last->e_name_index &&
++ name_len == last->e_name_len &&
++ !memcmp(name, last->e_name, name_len))
++ goto found;
++ last = next;
++ }
++
++ /* can't find EA */
++ brelse(iloc.bh);
++ return -ENOENT;
++
++found:
++ size = le32_to_cpu(last->e_value_size);
++ if (buffer) {
++ ret = -ERANGE;
++ if (buffer_size >= size) {
++ memcpy(buffer, start + le16_to_cpu(last->e_value_offs),
++ size);
++ ret = size;
++ }
++ } else
++ ret = size;
++ brelse(iloc.bh);
++ return ret;
++}
++
++/*
++ * ext3_xattr_get()
++ *
++ * Copy an extended attribute into the buffer
+ * provided, or compute the buffer size required.
+ * Buffer is NULL to compute the size of the buffer required.
+ *
+@@ -262,7 +328,31 @@
+ * used / required on success.
+ */
+ int
+-ext3_xattr_list(struct inode *inode, char *buffer, size_t buffer_size)
++ext3_xattr_get(struct inode *inode, int name_index, const char *name,
++ void *buffer, size_t buffer_size)
++{
++ int err;
++
++ down_read(&EXT3_I(inode)->xattr_sem);
++
++ /* try to find attribute in inode body */
++ err = ext3_xattr_ibody_get(inode, name_index, name,
++ buffer, buffer_size);
++ if (err < 0)
++ /* search was unsuccessful, try to find EA in dedicated block */
++ err = ext3_xattr_block_get(inode, name_index, name,
++ buffer, buffer_size);
++ up_read(&EXT3_I(inode)->xattr_sem);
++
++ return err;
++}
++
++/* ext3_xattr_ibody_list()
++ *
++ * generate list of attributes stored in EA block
++ */
++int
++ext3_xattr_block_list(struct inode *inode, char *buffer, size_t buffer_size)
+ {
+ struct buffer_head *bh = NULL;
+ struct ext3_xattr_entry *entry;
+@@ -273,7 +363,6 @@
+ ea_idebug(inode, "buffer=%p, buffer_size=%ld",
+ buffer, (long)buffer_size);
+
+- down_read(&EXT3_I(inode)->xattr_sem);
+ error = 0;
+ if (!EXT3_I(inode)->i_file_acl)
+ goto cleanup;
+@@ -283,7 +372,7 @@
+ if (!bh)
+ goto cleanup;
+ ea_bdebug(bh, "b_count=%d, refcount=%d",
+- atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
++ (int) atomic_read(&(bh->b_count)), (int) le32_to_cpu(HDR(bh)->h_refcount));
+ end = bh->b_data + bh->b_size;
+ if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) ||
+ HDR(bh)->h_blocks != cpu_to_le32(1)) {
+@@ -330,11 +419,149 @@
+
+ cleanup:
+ brelse(bh);
+- up_read(&EXT3_I(inode)->xattr_sem);
+
+ return error;
+ }
+
++/* ext3_xattr_ibody_list()
++ *
++ * generate list of attributes stored in inode body
++ */
++int
++ext3_xattr_ibody_list(struct inode *inode, char *buffer, size_t buffer_size)
++{
++ struct ext3_xattr_entry *last;
++ struct ext3_inode *raw_inode;
++ char *start, *end, *buf;
++ struct ext3_iloc iloc;
++ int storage_size;
++ size_t rest = buffer_size;
++ int ret;
++ int size = 0;
++
++ if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE)
++ return 0;
++
++ ret = ext3_get_inode_loc(inode, &iloc, 1);
++ if (ret)
++ return ret;
++ raw_inode = ext3_raw_inode(&iloc);
++
++ storage_size = EXT3_SB(inode->i_sb)->s_inode_size -
++ EXT3_GOOD_OLD_INODE_SIZE -
++ EXT3_I(inode)->i_extra_isize -
++ sizeof(__u32);
++ start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE +
++ EXT3_I(inode)->i_extra_isize;
++ if (le32_to_cpu((*(__u32*) start)) != EXT3_XATTR_MAGIC) {
++ brelse(iloc.bh);
++ return 0;
++ }
++ start += sizeof(__u32);
++ end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size;
++
++ last = (struct ext3_xattr_entry *) start;
++ while (!IS_LAST_ENTRY(last)) {
++ struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last);
++ struct xattr_handler *handler;
++ if (le32_to_cpu(last->e_value_size) > storage_size ||
++ (char *) next >= end) {
++ ext3_error(inode->i_sb, "ext3_xattr_ibody_list",
++ "inode %ld", inode->i_ino);
++ brelse(iloc.bh);
++ return -EIO;
++ }
++ handler = ext3_xattr_handler(last->e_name_index);
++ if (handler)
++ size += handler->list(inode, NULL, 0, last->e_name,
++ last->e_name_len);
++ last = next;
++ }
++
++ if (!buffer) {
++ ret = size;
++ goto cleanup;
++ } else {
++ ret = -ERANGE;
++ if (size > buffer_size)
++ goto cleanup;
++ }
++
++ last = (struct ext3_xattr_entry *) start;
++ buf = buffer;
++ while (!IS_LAST_ENTRY(last)) {
++ struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last);
++ struct xattr_handler *handler;
++ handler = ext3_xattr_handler(last->e_name_index);
++ if (handler) {
++ size_t size = handler->list(inode, buffer, rest,
++ last->e_name,
++ last->e_name_len);
++ if (buffer) {
++ if (size > rest) {
++ ret = -ERANGE;
++ goto cleanup;
++ }
++ buffer += size;
++ }
++ rest -= size;
++ }
++ last = next;
++ }
++ ret = size;
++cleanup:
++ brelse(iloc.bh);
++ return ret;
++}
++
++/*
++ * ext3_xattr_list()
++ *
++ * Copy a list of attribute names into the buffer
++ * provided, or compute the buffer size required.
++ * Buffer is NULL to compute the size of the buffer required.
++ *
++ * Returns a negative error number on failure, or the number of bytes
++ * used / required on success.
++ */
++int
++ext3_xattr_list(struct inode *inode, char *buffer, size_t buffer_size)
++{
++ int error;
++ int size = buffer_size;
++
++ down_read(&EXT3_I(inode)->xattr_sem);
++
++ /* get list of attributes stored in inode body */
++ error = ext3_xattr_ibody_list(inode, buffer, buffer_size);
++ if (error < 0) {
++ /* some error occured while collecting
++ * attributes in inode body */
++ size = 0;
++ goto cleanup;
++ }
++ size = error;
++
++ /* get list of attributes stored in dedicated block */
++ if (buffer) {
++ buffer_size -= error;
++ if (buffer_size <= 0) {
++ buffer = NULL;
++ buffer_size = 0;
++ } else
++ buffer += error;
++ }
++
++ error = ext3_xattr_block_list(inode, buffer, buffer_size);
++ if (error < 0)
++ /* listing was successful, so we return len */
++ size = 0;
++
++cleanup:
++ up_read(&EXT3_I(inode)->xattr_sem);
++ return error + size;
++}
++
+ /*
+ * If the EXT3_FEATURE_COMPAT_EXT_ATTR feature of this file system is
+ * not set, set it.
+@@ -356,6 +583,279 @@
+ }
+
+ /*
++ * ext3_xattr_ibody_find()
++ *
++ * search attribute and calculate free space in inode body
++ * NOTE: free space includes space our attribute hold
++ */
++int
++ext3_xattr_ibody_find(struct inode *inode, int name_index,
++ const char *name, struct ext3_xattr_entry *rentry, int *free)
++{
++ struct ext3_xattr_entry *last;
++ struct ext3_inode *raw_inode;
++ int name_len = strlen(name);
++ int err, storage_size;
++ struct ext3_iloc iloc;
++ char *start, *end;
++ int ret = -ENOENT;
++
++ if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE)
++ return ret;
++
++ err = ext3_get_inode_loc(inode, &iloc, 1);
++ if (err)
++ return -EIO;
++ raw_inode = ext3_raw_inode(&iloc);
++
++ storage_size = EXT3_SB(inode->i_sb)->s_inode_size -
++ EXT3_GOOD_OLD_INODE_SIZE -
++ EXT3_I(inode)->i_extra_isize -
++ sizeof(__u32);
++ *free = storage_size - sizeof(__u32);
++ start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE +
++ EXT3_I(inode)->i_extra_isize;
++ if (le32_to_cpu((*(__u32*) start)) != EXT3_XATTR_MAGIC) {
++ brelse(iloc.bh);
++ return -ENOENT;
++ }
++ start += sizeof(__u32);
++ end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size;
++
++ last = (struct ext3_xattr_entry *) start;
++ while (!IS_LAST_ENTRY(last)) {
++ struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(last);
++ if (le32_to_cpu(last->e_value_size) > storage_size ||
++ (char *) next >= end) {
++ ext3_error(inode->i_sb, "ext3_xattr_ibody_find",
++ "inode %ld", inode->i_ino);
++ brelse(iloc.bh);
++ return -EIO;
++ }
++
++ if (name_index == last->e_name_index &&
++ name_len == last->e_name_len &&
++ !memcmp(name, last->e_name, name_len)) {
++ memcpy(rentry, last, sizeof(struct ext3_xattr_entry));
++ ret = 0;
++ } else {
++ *free -= EXT3_XATTR_LEN(last->e_name_len);
++ *free -= le32_to_cpu(last->e_value_size);
++ }
++ last = next;
++ }
++
++ brelse(iloc.bh);
++ return ret;
++}
++
++/*
++ * ext3_xattr_block_find()
++ *
++ * search attribute and calculate free space in EA block (if it allocated)
++ * NOTE: free space includes space our attribute hold
++ */
++int
++ext3_xattr_block_find(struct inode *inode, int name_index, const char *name,
++ struct ext3_xattr_entry *rentry, int *free)
++{
++ struct buffer_head *bh = NULL;
++ struct ext3_xattr_entry *entry;
++ char *end;
++ int name_len, error = -ENOENT;
++
++ if (!EXT3_I(inode)->i_file_acl) {
++ *free = inode->i_sb->s_blocksize -
++ sizeof(struct ext3_xattr_header) -
++ sizeof(__u32);
++ return -ENOENT;
++ }
++ ea_idebug(inode, "reading block %d", EXT3_I(inode)->i_file_acl);
++ bh = sb_bread(inode->i_sb, EXT3_I(inode)->i_file_acl);
++ if (!bh)
++ return -EIO;
++ ea_bdebug(bh, "b_count=%d, refcount=%d",
++ atomic_read(&(bh->b_count)), le32_to_cpu(HDR(bh)->h_refcount));
++ end = bh->b_data + bh->b_size;
++ if (HDR(bh)->h_magic != cpu_to_le32(EXT3_XATTR_MAGIC) ||
++ HDR(bh)->h_blocks != cpu_to_le32(1)) {
++bad_block: ext3_error(inode->i_sb, "ext3_xattr_get",
++ "inode %ld: bad block %d", inode->i_ino,
++ EXT3_I(inode)->i_file_acl);
++ brelse(bh);
++ return -EIO;
++ }
++ /* find named attribute */
++ name_len = strlen(name);
++ *free = bh->b_size - sizeof(__u32);
++
++ entry = FIRST_ENTRY(bh);
++ while (!IS_LAST_ENTRY(entry)) {
++ struct ext3_xattr_entry *next =
++ EXT3_XATTR_NEXT(entry);
++ if ((char *)next >= end)
++ goto bad_block;
++ if (name_index == entry->e_name_index &&
++ name_len == entry->e_name_len &&
++ memcmp(name, entry->e_name, name_len) == 0) {
++ memcpy(rentry, entry, sizeof(struct ext3_xattr_entry));
++ error = 0;
++ } else {
++ *free -= EXT3_XATTR_LEN(entry->e_name_len);
++ *free -= le32_to_cpu(entry->e_value_size);
++ }
++ entry = next;
++ }
++ brelse(bh);
++
++ return error;
++}
++
++/*
++ * ext3_xattr_inode_set()
++ *
++ * this routine add/remove/replace attribute in inode body
++ */
++int
++ext3_xattr_ibody_set(handle_t *handle, struct inode *inode, int name_index,
++ const char *name, const void *value, size_t value_len,
++ int flags)
++{
++ struct ext3_xattr_entry *last, *next, *here = NULL;
++ struct ext3_inode *raw_inode;
++ int name_len = strlen(name);
++ int esize = EXT3_XATTR_LEN(name_len);
++ struct buffer_head *bh;
++ int err, storage_size;
++ struct ext3_iloc iloc;
++ int free, min_offs;
++ char *start, *end;
++
++ if (EXT3_SB(inode->i_sb)->s_inode_size <= EXT3_GOOD_OLD_INODE_SIZE)
++ return -ENOSPC;
++
++ err = ext3_get_inode_loc(inode, &iloc, 1);
++ if (err)
++ return err;
++ raw_inode = ext3_raw_inode(&iloc);
++ bh = iloc.bh;
++
++ storage_size = EXT3_SB(inode->i_sb)->s_inode_size -
++ EXT3_GOOD_OLD_INODE_SIZE -
++ EXT3_I(inode)->i_extra_isize -
++ sizeof(__u32);
++ start = (char *) raw_inode + EXT3_GOOD_OLD_INODE_SIZE +
++ EXT3_I(inode)->i_extra_isize;
++ if ((*(__u32*) start) != EXT3_XATTR_MAGIC) {
++ /* inode had no attributes before */
++ *((__u32*) start) = cpu_to_le32(EXT3_XATTR_MAGIC);
++ }
++ start += sizeof(__u32);
++ end = (char *) raw_inode + EXT3_SB(inode->i_sb)->s_inode_size;
++ min_offs = storage_size;
++ free = storage_size - sizeof(__u32);
++
++ last = (struct ext3_xattr_entry *) start;
++ while (!IS_LAST_ENTRY(last)) {
++ next = EXT3_XATTR_NEXT(last);
++ if (le32_to_cpu(last->e_value_size) > storage_size ||
++ (char *) next >= end) {
++ ext3_error(inode->i_sb, "ext3_xattr_ibody_set",
++ "inode %ld", inode->i_ino);
++ brelse(bh);
++ return -EIO;
++ }
++
++ if (last->e_value_size) {
++ int offs = le16_to_cpu(last->e_value_offs);
++ if (offs < min_offs)
++ min_offs = offs;
++ }
++ if (name_index == last->e_name_index &&
++ name_len == last->e_name_len &&
++ !memcmp(name, last->e_name, name_len))
++ here = last;
++ else {
++ /* we calculate all but our attribute
++ * because it will be removed before changing */
++ free -= EXT3_XATTR_LEN(last->e_name_len);
++ free -= le32_to_cpu(last->e_value_size);
++ }
++ last = next;
++ }
++
++ if (value && (esize + value_len > free)) {
++ brelse(bh);
++ return -ENOSPC;
++ }
++
++ err = ext3_reserve_inode_write(handle, inode, &iloc);
++ if (err) {
++ brelse(bh);
++ return err;
++ }
++
++ if (here) {
++ /* time to remove old value */
++ struct ext3_xattr_entry *e;
++ int size = le32_to_cpu(here->e_value_size);
++ int border = le16_to_cpu(here->e_value_offs);
++ char *src;
++
++ /* move tail */
++ memmove(start + min_offs + size, start + min_offs,
++ border - min_offs);
++
++ /* recalculate offsets */
++ e = (struct ext3_xattr_entry *) start;
++ while (!IS_LAST_ENTRY(e)) {
++ struct ext3_xattr_entry *next = EXT3_XATTR_NEXT(e);
++ int offs = le16_to_cpu(e->e_value_offs);
++ if (offs < border)
++ e->e_value_offs =
++ cpu_to_le16(offs + size);
++ e = next;
++ }
++ min_offs += size;
++
++ /* remove entry */
++ border = EXT3_XATTR_LEN(here->e_name_len);
++ src = (char *) here + EXT3_XATTR_LEN(here->e_name_len);
++ size = (char *) last - src;
++ if ((char *) here + size > end)
++ printk("ALERT at %s:%d: 0x%p + %d > 0x%p\n",
++ __FILE__, __LINE__, here, size, end);
++ memmove(here, src, size);
++ last = (struct ext3_xattr_entry *) ((char *) last - border);
++ *((__u32 *) last) = 0;
++ }
++
++ if (value) {
++ int offs = min_offs - value_len;
++ /* use last to create new entry */
++ last->e_name_len = strlen(name);
++ last->e_name_index = name_index;
++ last->e_value_offs = cpu_to_le16(offs);
++ last->e_value_size = cpu_to_le32(value_len);
++ last->e_hash = last->e_value_block = 0;
++ memset(last->e_name, 0, esize);
++ memcpy(last->e_name, name, last->e_name_len);
++ if (start + offs + value_len > end)
++ printk("ALERT at %s:%d: 0x%p + %d + %d > 0x%p\n",
++ __FILE__, __LINE__, start, offs,
++ value_len, end);
++ memcpy(start + offs, value, value_len);
++ last = EXT3_XATTR_NEXT(last);
++ *((__u32 *) last) = 0;
++ }
++
++ ext3_mark_iloc_dirty(handle, inode, &iloc);
++ brelse(bh);
++
++ return 0;
++}
++
++/*
+ * ext3_xattr_set_handle()
+ *
+ * Create, replace or remove an extended attribute for this inode. Buffer
+@@ -369,6 +869,104 @@
+ */
+ int
+ ext3_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
++ const char *name, const void *value, size_t value_len,
++ int flags)
++{
++ struct ext3_xattr_entry entry;
++ int err, where = 0, found = 0, total;
++ int free1 = -1, free2 = -1;
++ int name_len;
++
++ ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld",
++ name_index, name, value, (long)value_len);
++
++ if (IS_RDONLY(inode))
++ return -EROFS;
++ if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
++ return -EPERM;
++ if (value == NULL)
++ value_len = 0;
++ if (name == NULL)
++ return -EINVAL;
++ name_len = strlen(name);
++ if (name_len > 255 || value_len > inode->i_sb->s_blocksize)
++ return -ERANGE;
++ down_write(&EXT3_I(inode)->xattr_sem);
++
++ /* try to find attribute in inode body */
++ err = ext3_xattr_ibody_find(inode, name_index, name, &entry, &free1);
++ if (err == 0) {
++ /* found EA in inode */
++ found = 1;
++ where = 0;
++ } else if (err == -ENOENT) {
++ /* there is no such attribute in inode body */
++ /* try to find attribute in dedicated block */
++ err = ext3_xattr_block_find(inode, name_index, name,
++ &entry, &free2);
++ if (err != 0 && err != -ENOENT) {
++ /* not found EA in block */
++ goto finish;
++ } else if (err == 0) {
++ /* found EA in block */
++ where = 1;
++ found = 1;
++ }
++ } else
++ goto finish;
++
++ /* check flags: may replace? may create ? */
++ if (found && (flags & XATTR_CREATE)) {
++ err = -EEXIST;
++ goto finish;
++ } else if (!found && (flags & XATTR_REPLACE)) {
++ err = -ENODATA;
++ goto finish;
++ }
++
++ /* check if we have enough space to store attribute */
++ total = EXT3_XATTR_LEN(strlen(name)) + value_len;
++ if (free1 >= 0 && total > free1 && free2 >= 0 && total > free2) {
++ /* have no enough space */
++ err = -ENOSPC;
++ goto finish;
++ }
++
++ /* time to remove attribute */
++ if (found) {
++ if (where == 0) {
++ /* EA is stored in inode body */
++ ext3_xattr_ibody_set(handle, inode, name_index, name,
++ NULL, 0, flags);
++ } else {
++ /* EA is stored in separated block */
++ ext3_xattr_block_set(handle, inode, name_index, name,
++ NULL, 0, flags);
++ }
++ }
++
++ /* try to store EA in inode body */
++ err = ext3_xattr_ibody_set(handle, inode, name_index, name,
++ value, value_len, flags);
++ if (err) {
++ /* can't store EA in inode body */
++ /* try to store in block */
++ err = ext3_xattr_block_set(handle, inode, name_index,
++ name, value, value_len, flags);
++ }
++
++finish:
++ up_write(&EXT3_I(inode)->xattr_sem);
++ return err;
++}
++
++/*
++ * ext3_xattr_block_set()
++ *
++ * this routine add/remove/replace attribute in EA block
++ */
++int
++ext3_xattr_block_set(handle_t *handle, struct inode *inode, int name_index,
+ const char *name, const void *value, size_t value_len,
+ int flags)
+ {
+@@ -391,22 +989,7 @@
+ * towards the end of the block).
+ * end -- Points right after the block pointed to by header.
+ */
+-
+- ea_idebug(inode, "name=%d.%s, value=%p, value_len=%ld",
+- name_index, name, value, (long)value_len);
+-
+- if (IS_RDONLY(inode))
+- return -EROFS;
+- if (IS_IMMUTABLE(inode) || IS_APPEND(inode))
+- return -EPERM;
+- if (value == NULL)
+- value_len = 0;
+- if (name == NULL)
+- return -EINVAL;
+ name_len = strlen(name);
+- if (name_len > 255 || value_len > sb->s_blocksize)
+- return -ERANGE;
+- down_write(&EXT3_I(inode)->xattr_sem);
+ if (EXT3_I(inode)->i_file_acl) {
+ /* The inode already has an extended attribute block. */
+ bh = sb_bread(sb, EXT3_I(inode)->i_file_acl);
+@@ -637,7 +1220,6 @@
+ brelse(bh);
+ if (!(bh && header == HDR(bh)))
+ kfree(header);
+- up_write(&EXT3_I(inode)->xattr_sem);
+
+ return error;
+ }
+Index: linux-2.6.10/fs/ext3/xattr.h
+===================================================================
+--- linux-2.6.10.orig/fs/ext3/xattr.h 2004-12-25 05:35:28.000000000 +0800
++++ linux-2.6.10/fs/ext3/xattr.h 2005-03-31 20:12:35.438783600 +0800
+@@ -67,7 +67,8 @@
+ extern int ext3_xattr_get(struct inode *, int, const char *, void *, size_t);
+ extern int ext3_xattr_list(struct inode *, char *, size_t);
+ extern int ext3_xattr_set(struct inode *, int, const char *, const void *, size_t, int);
+-extern int ext3_xattr_set_handle(handle_t *, struct inode *, int, const char *, const void *, size_t, int);
++extern int ext3_xattr_set_handle(handle_t *, struct inode *, int, const char *,const void *,size_t,int);
++extern int ext3_xattr_block_set(handle_t *, struct inode *, int, const char *,const void *,size_t,int);
+
+ extern void ext3_xattr_delete_inode(handle_t *, struct inode *);
+ extern void ext3_xattr_put_super(struct super_block *);
+Index: linux-2.6.10/include/linux/ext3_fs_i.h
+===================================================================
+--- linux-2.6.10.orig/include/linux/ext3_fs_i.h 2005-03-31 20:12:27.759950960 +0800
++++ linux-2.6.10/include/linux/ext3_fs_i.h 2005-03-31 20:12:35.439783448 +0800
+@@ -114,6 +114,9 @@
+ */
+ loff_t i_disksize;
+
++ /* on-disk additional length */
++ __u16 i_extra_isize;
++
+ /*
+ * truncate_sem is for serialising ext3_truncate() against
+ * ext3_getblock(). In the 2.4 ext2 design, great chunks of inode's
+Index: linux-2.6.10/include/linux/ext3_fs.h
+===================================================================
+--- linux-2.6.10.orig/include/linux/ext3_fs.h 2005-03-31 20:12:30.546527336 +0800
++++ linux-2.6.10/include/linux/ext3_fs.h 2005-03-31 20:12:35.440783296 +0800
+@@ -294,6 +294,8 @@
+ __u32 m_i_reserved2[2];
+ } masix2;
+ } osd2; /* OS dependent 2 */
++ __u16 i_extra_isize;
++ __u16 i_pad1;
+ };
+
+ #define i_size_high i_dir_acl
+@@ -760,6 +762,7 @@
+ extern struct buffer_head * ext3_getblk (handle_t *, struct inode *, long, int, int *);
+ extern struct buffer_head * ext3_bread (handle_t *, struct inode *, int, int, int *);
+
++extern int ext3_get_inode_loc(struct inode *, struct ext3_iloc *, int);
+ extern void ext3_read_inode (struct inode *);
+ extern int ext3_write_inode (struct inode *, int);
+ extern int ext3_setattr (struct dentry *, struct iattr *);
+
+%diffstat
+ fs/ext3/ialloc.c | 5
+ fs/ext3/inode.c | 10
+ fs/ext3/xattr.c | 646 +++++++++++++++++++++++++++++++++++++++++++---
+ fs/ext3/xattr.h | 3
+ include/linux/ext3_fs.h | 3
+ include/linux/ext3_fs_i.h | 3
+ 6 files changed, 636 insertions(+), 34 deletions(-)
+