summaryrefslogtreecommitdiff
path: root/debian/patches/xattr0001-ext2fs-add-xattr-support.patch
diff options
context:
space:
mode:
Diffstat (limited to 'debian/patches/xattr0001-ext2fs-add-xattr-support.patch')
-rw-r--r--debian/patches/xattr0001-ext2fs-add-xattr-support.patch1070
1 files changed, 1070 insertions, 0 deletions
diff --git a/debian/patches/xattr0001-ext2fs-add-xattr-support.patch b/debian/patches/xattr0001-ext2fs-add-xattr-support.patch
new file mode 100644
index 00000000..1f735650
--- /dev/null
+++ b/debian/patches/xattr0001-ext2fs-add-xattr-support.patch
@@ -0,0 +1,1070 @@
+From 7fec7f3357fbe6bce33a161b6b982feccf88d556 Mon Sep 17 00:00:00 2001
+From: Shengyu Zhang <lastavengers@outlook.com>
+Date: Thu, 7 Jul 2016 23:47:39 +0200
+Subject: [PATCH hurd 1/6] ext2fs: add xattr support
+
+This patch is base on cascardo's previous work[1],
+Only 2 namespaces ("user." and "gnu.") are supported,
+as other namespaces are useless on hurd now.
+
+[1]: https://savannah.gnu.org/patch/?5126
+
+* ext2fs/Makefile: Needs description.
+* ext2fs/ext2_fs.h: Likewise.
+* ext2fs/ext2fs.h: Likewise.
+* ext2fs/ialloc.c: Likewise.
+* ext2fs/xattr.c: New file.
+* ext2fs/xattr.h: Likewise.
+---
+ ext2fs/Makefile | 3 +-
+ ext2fs/ext2_fs.h | 3 +-
+ ext2fs/ext2fs.h | 13 +
+ ext2fs/ialloc.c | 2 +
+ ext2fs/xattr.c | 866 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ ext2fs/xattr.h | 85 ++++++
+ 6 files changed, 970 insertions(+), 2 deletions(-)
+ create mode 100644 ext2fs/xattr.c
+ create mode 100644 ext2fs/xattr.h
+
+diff --git a/ext2fs/Makefile b/ext2fs/Makefile
+index 88f8f46..0c2f4a2 100644
+--- a/ext2fs/Makefile
++++ b/ext2fs/Makefile
+@@ -21,7 +21,8 @@ makemode := server
+
+ target = ext2fs
+ SRCS = balloc.c dir.c ext2fs.c getblk.c hyper.c ialloc.c \
+- inode.c pager.c pokel.c truncate.c storeinfo.c msg.c xinl.c
++ inode.c pager.c pokel.c truncate.c storeinfo.c msg.c xinl.c \
++ xattr.c
+ OBJS = $(SRCS:.c=.o)
+ HURDLIBS = diskfs pager iohelp fshelp store ports ihash shouldbeinlibc
+ LDLIBS = -lpthread $(and $(HAVE_LIBBZ2),-lbz2) $(and $(HAVE_LIBZ),-lz)
+diff --git a/ext2fs/ext2_fs.h b/ext2fs/ext2_fs.h
+index b1caeef..019ba15 100644
+--- a/ext2fs/ext2_fs.h
++++ b/ext2fs/ext2_fs.h
+@@ -462,6 +462,7 @@ struct ext2_super_block {
+ ( EXT2_SB(sb)->s_feature_incompat & (mask) )
+
+ #define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001
++#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008
+
+ #define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
+ #define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
+@@ -470,7 +471,7 @@ struct ext2_super_block {
+ #define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
+ #define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002
+
+-#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
+ #define EXT2_FEATURE_RO_COMPAT_SUPP (EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER| \
+ EXT2_FEATURE_RO_COMPAT_LARGE_FILE| \
+diff --git a/ext2fs/ext2fs.h b/ext2fs/ext2fs.h
+index 070d5bd..bbf7a0c 100644
+--- a/ext2fs/ext2fs.h
++++ b/ext2fs/ext2fs.h
+@@ -17,6 +17,9 @@
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
++#ifndef _EXT2FS_H
++#define _EXT2FS_H
++
+ #include <mach.h>
+ #include <hurd.h>
+ #include <hurd/ports.h>
+@@ -571,3 +574,13 @@ extern void _ext2_panic (const char *, const char *, ...)
+
+ extern void ext2_warning (const char *, ...)
+ __attribute__ ((format (printf, 1, 2)));
++
++/* ---------------------------------------------------------------- */
++/* xattr.c */
++
++error_t ext2_list_xattr (struct node *np, char *buffer, int *len);
++error_t ext2_get_xattr (struct node *np, const char *name, char *value, int *len);
++error_t ext2_set_xattr (struct node *np, const char *name, const char *value, int len, int flags);
++error_t ext2_free_xattr_block (struct node *np);
++
++#endif
+diff --git a/ext2fs/ialloc.c b/ext2fs/ialloc.c
+index 2809371..71bfb8c 100644
+--- a/ext2fs/ialloc.c
++++ b/ext2fs/ialloc.c
+@@ -62,6 +62,8 @@ diskfs_free_node (struct node *np, mode_t old_mode)
+
+ ext2_debug ("freeing inode %u", inum);
+
++ ext2_free_xattr_block (np);
++
+ pthread_spin_lock (&global_lock);
+
+ if (inum < EXT2_FIRST_INO (sblock) || inum > sblock->s_inodes_count)
+diff --git a/ext2fs/xattr.c b/ext2fs/xattr.c
+new file mode 100644
+index 0000000..eea5ef6
+--- /dev/null
++++ b/ext2fs/xattr.c
+@@ -0,0 +1,866 @@
++ /* Ext2 support for extended attributes
++
++ Copyright (C) 2006, 2016 Free Software Foundation, Inc.
++
++ Written by Thadeu Lima de Souza Cascardo <cascardo@dcc.ufmg.br>
++ and Shengyu Zhang <lastavengers@outlook.com>
++
++ This program is free software; you can redistribute it and/or
++ modify it under the terms of the GNU General Public License as
++ published by the Free Software Foundation; either version 2, or (at
++ your option) any later version.
++
++ This program is distributed in the hope that it will be useful, but
++ WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
++
++#include "ext2fs.h"
++#include "xattr.h"
++#include <stdlib.h>
++#include <string.h>
++#include <sys/xattr.h>
++
++struct _xattr_prefix
++{
++ int index;
++ char *prefix;
++ ssize_t size;
++};
++
++/* Prefixes are represented as numbers when stored in ext2 filesystems. */
++struct _xattr_prefix
++xattr_prefixes[] =
++{
++ {
++ 1, "user.", sizeof "user." - 1},
++ {
++ 7, "gnu.", sizeof "gnu." - 1},
++ {
++ 0, NULL, 0}
++};
++
++/*
++ * Given an attribute name in full_name, the ext2 number (index) and
++ * suffix name (name) are given. Returns the index in the array
++ * indicating whether a corresponding prefix was found or not.
++ */
++static int
++xattr_name_prefix (const char *full_name, int *index, const char **name)
++{
++ int i;
++
++ for (i = 0; xattr_prefixes[i].prefix != NULL; i++)
++ {
++ if (!strncmp (xattr_prefixes[i].prefix, full_name,
++ xattr_prefixes[i].size))
++ {
++ *name = full_name + xattr_prefixes[i].size;
++ *index = xattr_prefixes[i].index;
++ break;
++ }
++ }
++ return i;
++}
++
++#define NAME_HASH_SHIFT 5
++#define VALUE_HASH_SHIFT 16
++
++/* Given a xattr block header and a entry, compute the hash of this
++ * entry.
++ */
++static void
++xattr_entry_hash (struct ext2_xattr_header *header,
++ struct ext2_xattr_entry *entry)
++{
++
++ __u32 hash = 0;
++ char *name = entry->e_name;
++ int n;
++
++ for (n = 0; n < entry->e_name_len; n++)
++ {
++ hash = (hash << NAME_HASH_SHIFT)
++ ^ (hash >> (8 * sizeof (hash) - NAME_HASH_SHIFT))
++ ^ *name++;
++ }
++
++ if (entry->e_value_block == 0 && entry->e_value_size != 0)
++ {
++ __u32 *value = (__u32 *) ((char *) header + entry->e_value_offs);
++ for (n = (entry->e_value_size + EXT2_XATTR_ROUND) >>
++ EXT2_XATTR_PAD_BITS; n; n--)
++ {
++ hash = (hash << VALUE_HASH_SHIFT)
++ ^ (hash >> (8 * sizeof (hash) - VALUE_HASH_SHIFT))
++ ^ *value++;
++ }
++ }
++
++ entry->e_hash = hash;
++
++}
++
++#undef NAME_HASH_SHIFT
++#undef VALUE_HASH_SHIFT
++
++#define BLOCK_HASH_SHIFT 16
++
++/* Given a xattr block header and a entry, re-compute the
++ * hash of the entry after it has changed, and computer the hash
++ * of the header.
++ */
++static void
++xattr_entry_rehash (struct ext2_xattr_header *header,
++ struct ext2_xattr_entry *entry)
++{
++
++ __u32 hash = 0;
++ struct ext2_xattr_entry *position;
++
++ xattr_entry_hash (header, entry);
++
++ position = EXT2_XATTR_ENTRY_FIRST (header);
++ while (!EXT2_XATTR_ENTRY_LAST (position))
++ {
++ if (position->e_hash == 0)
++ {
++ /* Block is not shared if an entry's hash value == 0 */
++ hash = 0;
++ break;
++ }
++
++ hash = (hash << BLOCK_HASH_SHIFT)
++ ^ (hash >> (8 * sizeof (hash) - BLOCK_HASH_SHIFT))
++ ^ position->e_hash;
++
++ position = EXT2_XATTR_ENTRY_NEXT (position);
++ }
++
++ header->h_hash = hash;
++
++}
++
++#undef BLOCK_HASH_SHIFT
++
++/*
++ * Given an entry, appends its name to a buffer. The provided buffer
++ * length is reduced by the name size, even if the buffer is NULL (for
++ * computing the list size). Returns EOPNOTSUPP (operation not
++ * supported) if the entry index cannot be found on the array of
++ * supported prefixes. If a buffer is provided (not NULL) and its
++ * length is not enough for name, ERANGE is returned.
++ */
++static error_t
++xattr_entry_list (struct ext2_xattr_entry *entry, char *buffer, int *len)
++{
++
++ int i;
++ int size;
++
++ for (i = 0; xattr_prefixes[i].prefix != NULL; i++)
++ {
++ if (entry->e_name_index == xattr_prefixes[i].index)
++ break;
++ }
++
++ if (xattr_prefixes[i].prefix == NULL)
++ return EOPNOTSUPP;
++
++ size = xattr_prefixes[i].size + entry->e_name_len + 1;
++
++ if (buffer)
++ {
++ if (size <= *len)
++ {
++ memcpy (buffer, xattr_prefixes[i].prefix, xattr_prefixes[i].size);
++ buffer += xattr_prefixes[i].size;
++ memcpy (buffer, entry->e_name, entry->e_name_len);
++ buffer += entry->e_name_len;
++ *buffer++ = 0;
++ }
++ else
++ {
++ return ERANGE;
++ }
++ }
++
++ *len -= size;
++ return 0;
++
++}
++
++/*
++ * Given the xattr block, an entry and a attribute name, retrieves its
++ * value. The value length is also returned through parameter len. In
++ * case the name prefix cannot be found in the prefix array,
++ * EOPNOTSUPP is returned, indicating the prefix is not supported. In
++ * case there is not enough space in the buffer provided, ERANGE is
++ * returned. If the value buffer was NULL, the length is returned
++ * through len parameter and the function is successfull (returns 0).
++ * If the entry does not match the name, ENODATA is returned, and
++ * parameter cmp is set to the comparison value (less than 0 if a
++ * entry with name full_name should be before the current entry,
++ * more than 0 otherwise.
++ */
++static error_t
++xattr_entry_get (char *block, struct ext2_xattr_entry *entry,
++ const char *full_name, char *value, int *len, int *cmp)
++{
++
++ int i;
++ int index;
++ int tmp_cmp;
++ const char *name;
++
++ i = xattr_name_prefix (full_name, &index, &name);
++
++ if (xattr_prefixes[i].prefix == NULL)
++ return EOPNOTSUPP;
++
++ tmp_cmp = index - entry->e_name_index;
++ if (!tmp_cmp)
++ tmp_cmp = strlen (name) - entry->e_name_len;
++ if (!tmp_cmp)
++ tmp_cmp = strncmp (name, entry->e_name, entry->e_name_len);
++
++ if (tmp_cmp)
++ {
++ if (cmp)
++ *cmp = tmp_cmp;
++ return ENODATA;
++ }
++
++ if (value)
++ {
++ if (*len < entry->e_value_size)
++ {
++ return ERANGE;
++ }
++ memcpy (value, block + entry->e_value_offs, entry->e_value_size);
++ }
++
++ *len = entry->e_value_size;
++ return 0;
++
++}
++
++/*
++ * Creates an entry in the xattr block, giving its header, the last
++ * entry, the position where this new one should be inserted, the name
++ * of the attribute, its value and the value length, and, the
++ * remaining space in the block (parameter rest). If no space is
++ * available for the required size of the entry, ERANGE is returned.
++ */
++static error_t
++xattr_entry_create (struct ext2_xattr_header *header,
++ struct ext2_xattr_entry *last,
++ struct ext2_xattr_entry *position,
++ const char *full_name, const char *value,
++ int len, int rest)
++{
++
++ int i;
++ int name_len;
++ off_t start;
++ off_t end;
++ int entry_size;
++ int value_size;
++ int index;
++ const char *name;
++
++ i = xattr_name_prefix (full_name, &index, &name);
++
++ if (xattr_prefixes[i].prefix == NULL)
++ return EOPNOTSUPP;
++
++ name_len = strlen (name);
++ entry_size = EXT2_XATTR_ENTRY_SIZE (name_len);
++ value_size = EXT2_XATTR_ALIGN (len);
++
++ if (entry_size + value_size > rest - 4)
++ {
++ return ERANGE;
++ }
++
++ start = EXT2_XATTR_ENTRY_OFFSET (header, position);
++ end = EXT2_XATTR_ENTRY_OFFSET (header, last);
++
++ /* Leave room for new entry */
++ memmove ((char *) position + entry_size, position, end - start);
++
++ position->e_name_len = name_len;
++ position->e_name_index = index;
++ position->e_value_offs = end + rest - value_size;
++ position->e_value_block = 0;
++ position->e_value_size = len;
++ strncpy (position->e_name, name, name_len);
++
++ memcpy ((char *) header + position->e_value_offs, value, len);
++ memset ((char *) header + position->e_value_offs + len, 0,
++ value_size - len);
++
++ return 0;
++
++}
++
++/*
++ * Removes an entry from the xattr block, giving a pointer to the
++ * block header, the last attribute entry, the position of the entry
++ * to be removed and the remaining space in the block.
++ */
++static error_t
++xattr_entry_remove (struct ext2_xattr_header *header,
++ struct ext2_xattr_entry *last,
++ struct ext2_xattr_entry *position, int rest)
++{
++
++ size_t size;
++ off_t start;
++ off_t end;
++ struct ext2_xattr_entry *entry;
++
++ /* Remove the value */
++ size = EXT2_XATTR_ALIGN (position->e_value_size);
++ start = EXT2_XATTR_ENTRY_OFFSET (header, last) + rest;
++ end = position->e_value_offs;
++
++ memmove ((char *) header + start + size, (char *) header + start,
++ end - start);
++ memset ((char *) header + start, 0, size);
++
++ /* Adjust all value offsets */
++ entry = EXT2_XATTR_ENTRY_FIRST (header);
++ while (!EXT2_XATTR_ENTRY_LAST (entry))
++ {
++ if (entry->e_value_offs < end)
++ entry->e_value_offs += size;
++ entry = EXT2_XATTR_ENTRY_NEXT (entry);
++ }
++
++ /* Remove the name */
++ size = EXT2_XATTR_ENTRY_SIZE (position->e_name_len);
++ start = EXT2_XATTR_ENTRY_OFFSET (header, position);
++ end = EXT2_XATTR_ENTRY_OFFSET (header, last);
++
++ memmove ((char *) header + start , (char *) header + start + size,
++ end - (start + size));
++ memset ((char *) header + end - size, 0, size);
++
++ return 0;
++
++}
++
++/*
++ * Replaces the value of an existing attribute entry, given the block
++ * header, the last entry, the entry whose value should be replaced,
++ * the new value, its length, and the remaining space in the block.
++ * Returns ERANGE if there is not enough space (when the new value is
++ * bigger than the old one).
++ */
++static error_t
++xattr_entry_replace (struct ext2_xattr_header *header,
++ struct ext2_xattr_entry *last,
++ struct ext2_xattr_entry *position,
++ const char *value, int len, int rest)
++{
++
++ ssize_t old_size;
++ ssize_t new_size;
++
++ old_size = EXT2_XATTR_ALIGN (position->e_value_size);
++ new_size = EXT2_XATTR_ALIGN (len);
++
++ if (new_size - old_size > rest - 4)
++ return ERANGE;
++
++ if (new_size != old_size)
++ {
++ off_t start;
++ off_t end;
++ struct ext2_xattr_entry *entry;
++
++ start = EXT2_XATTR_ENTRY_OFFSET (header, last) + rest;
++ end = position->e_value_offs;
++
++ /* Remove the old value */
++ memmove ((char *) header + start + old_size, (char *) header + start,
++ end - start);
++
++ /* Adjust all value offsets */
++ entry = EXT2_XATTR_ENTRY_FIRST (header);
++ while (!EXT2_XATTR_ENTRY_LAST (entry))
++ {
++ if (entry->e_value_offs < end)
++ entry->e_value_offs += old_size;
++ entry = EXT2_XATTR_ENTRY_NEXT (entry);
++ }
++
++ position->e_value_offs = start - (new_size - old_size);
++ }
++
++ position->e_value_size = len;
++
++ /* Write the new value */
++ memcpy ((char *) header + position->e_value_offs, value, len);
++ memset ((char *) header + position->e_value_offs + len, 0, new_size - len);
++
++ return 0;
++
++}
++
++
++/*
++ * Given a node, free extended attributes block associated with
++ * this node.
++ */
++error_t
++ext2_free_xattr_block (struct node *np)
++{
++ error_t err;
++ block_t blkno;
++ void *block;
++ struct ext2_inode *ei;
++ struct ext2_xattr_header *header;
++
++ if (!EXT2_HAS_COMPAT_FEATURE (sblock, EXT2_FEATURE_COMPAT_EXT_ATTR))
++ {
++ ext2_warning ("Filesystem has no support for extended attributes.");
++ return EOPNOTSUPP;
++ }
++
++ err = 0;
++ block = NULL;
++
++ ei = dino_ref (np->cache_id);
++ blkno = ei->i_file_acl;
++
++ if (blkno == 0)
++ {
++ err = 0;
++ goto cleanup;
++ }
++
++ assert (!diskfs_readonly);
++
++ block = disk_cache_block_ref (blkno);
++ header = EXT2_XATTR_HEADER (block);
++
++ if (header->h_magic != EXT2_XATTR_BLOCK_MAGIC || header->h_blocks != 1)
++ {
++ ext2_warning ("Invalid extended attribute block.");
++ err = EIO;
++ goto cleanup;
++ }
++
++ if (header->h_refcount == 1)
++ {
++ ext2_debug("free block %d", blkno);
++
++ disk_cache_block_deref (block);
++ ext2_free_blocks(blkno, 1);
++
++ np->dn_stat.st_blocks -= 1 << log2_stat_blocks_per_fs_block;
++ np->dn_stat.st_mode &= ~S_IPTRANS;
++ np->dn_set_ctime = 1;
++ }
++ else
++ {
++ ext2_debug("h_refcount: %d", header->h_refcount);
++
++ header->h_refcount--;
++ record_global_poke (block);
++ }
++
++
++ ei->i_file_acl = 0;
++ record_global_poke (ei);
++
++ return err;
++
++cleanup:
++ if (block)
++ disk_cache_block_deref (block);
++
++ dino_deref (ei);
++
++ return err;
++
++}
++
++/*
++ * Given a node, return its list of attribute names in a buffer.
++ * The size of used/required buffer will returned through parameter
++ * len, even if the buffer is NULL. Returns EOPNOTSUPP if underlying
++ * filesystem has no extended attributes support. Returns EIO if
++ * xattr block is invalid (has no valid h_magic number).
++ */
++error_t
++ext2_list_xattr (struct node *np, char *buffer, int *len)
++{
++
++ error_t err;
++ block_t blkno;
++ void *block;
++ struct ext2_inode *ei;
++ struct ext2_xattr_header *header;
++ struct ext2_xattr_entry *entry;
++
++ if (!EXT2_HAS_COMPAT_FEATURE (sblock, EXT2_FEATURE_COMPAT_EXT_ATTR))
++ {
++ ext2_warning ("Filesystem has no support for extended attributes.");
++ return EOPNOTSUPP;
++ }
++
++ if (!len)
++ return EINVAL;
++
++ int size = *len;
++
++ ei = dino_ref (np->cache_id);
++ blkno = ei->i_file_acl;
++ dino_deref (ei);
++
++ if (blkno == 0)
++ {
++ *len = 0;
++ return 0;
++ }
++
++ err = EIO;
++ block = disk_cache_block_ref (blkno);
++
++ header = EXT2_XATTR_HEADER (block);
++ if (header->h_magic != EXT2_XATTR_BLOCK_MAGIC || header->h_blocks != 1)
++ {
++ ext2_warning ("Invalid extended attribute block.");
++ err = EIO;
++ goto cleanup;
++ }
++
++ entry = EXT2_XATTR_ENTRY_FIRST (header);
++
++ while (!EXT2_XATTR_ENTRY_LAST (entry))
++ {
++ err = xattr_entry_list (entry, buffer, &size);
++ if (err)
++ goto cleanup;
++ if (buffer)
++ buffer += strlen (buffer) + 1;
++ entry = EXT2_XATTR_ENTRY_NEXT (entry);
++ }
++
++ *len = *len - size;
++
++cleanup:
++ disk_cache_block_deref (block);
++
++ return err;
++
++}
++
++
++/*
++ * Given a node and an attribute name, returns the value and its
++ * length in a buffer. The length is returned through parameter len
++ * even if the value is NULL. May return EOPNOTSUPP if underlying
++ * filesystem does not support extended attributes or the given name
++ * prefix. If there is no sufficient space in value buffer or
++ * attribute name is too long, returns ERANGE. Returns EIO if xattr
++ * block is invalid and ENODATA if there is no such block or no entry
++ * in the block matching the name.
++ */
++error_t
++ext2_get_xattr (struct node *np, const char *name, char *value, int *len)
++{
++
++ int size;
++ int err;
++ void *block;
++ struct ext2_inode *ei;
++ block_t blkno;
++ struct ext2_xattr_header *header;
++ struct ext2_xattr_entry *entry;
++
++ if (!EXT2_HAS_COMPAT_FEATURE (sblock, EXT2_FEATURE_COMPAT_EXT_ATTR))
++ {
++ ext2_warning ("Filesystem has no support for extended attributes.");
++ return EOPNOTSUPP;
++ }
++
++ if (!name || !len)
++ return EINVAL;
++
++ if (strlen(name) > 255)
++ return ERANGE;
++
++ size = *len;
++
++ ei = dino_ref (np->cache_id);
++ blkno = ei->i_file_acl;
++ dino_deref (ei);
++
++ if (blkno == 0)
++ {
++ return ENODATA;
++ }
++
++ block = disk_cache_block_ref (blkno);
++
++ header = EXT2_XATTR_HEADER (block);
++ if (header->h_magic != EXT2_XATTR_BLOCK_MAGIC || header->h_blocks != 1)
++ {
++ ext2_warning ("Invalid extended attribute block.");
++ err = EIO;
++ goto cleanup;
++ }
++
++ err = ENODATA;
++ entry = EXT2_XATTR_ENTRY_FIRST (header);
++
++ while (!EXT2_XATTR_ENTRY_LAST (entry))
++ {
++ err = xattr_entry_get (block, entry, name, value, &size, NULL);
++ if (err!= ENODATA)
++ break;
++ entry = EXT2_XATTR_ENTRY_NEXT (entry);
++ }
++
++ if (!err)
++ *len = size;
++
++cleanup:
++ disk_cache_block_deref (block);
++
++ return err;
++
++}
++
++/*
++ * Set the value of an attribute giving the node, the attribute name,
++ * value, the value length and flags. If name or value is too long,
++ * ERANGE is returned. If flags is XATTR_CREATE, the
++ * attribute is created if no existing matching entry is found.
++ * Otherwise, EEXIST is returned. If flags is XATTR_REPLACE, the
++ * attribute value is replaced if an entry is found and ENODATA is
++ * returned otherwise. If no flags are used, the entry is properly
++ * created or replaced. The entry is removed if value is NULL and no
++ * flags are used. In this case, if any flags are used, EINVAL is
++ * returned. If no matching entry is found, ENODATA is returned.
++ * EOPNOTSUPP is returned in case extended attributes or the name
++ * prefix are not supported. If there is no space available in the
++ * block, ERANGE is returned. If there is no any entry after removing
++ * the specified entry, free the xattr block.
++ */
++error_t
++ext2_set_xattr (struct node *np, const char *name, const char *value, int len,
++ int flags)
++{
++
++ int found;
++ int rest;
++ error_t err;
++ block_t blkno;
++ void *block;
++ struct ext2_inode *ei;
++ struct ext2_xattr_header *header;
++ struct ext2_xattr_entry *entry;
++ struct ext2_xattr_entry *location;
++
++ if (!EXT2_HAS_COMPAT_FEATURE (sblock, EXT2_FEATURE_COMPAT_EXT_ATTR))
++ {
++ ext2_warning ("Filesystem has no support for extended attributes.");
++ return EOPNOTSUPP;
++ }
++
++ if (!name)
++ return EINVAL;
++
++ if (strlen(name) > 255 || len > block_size)
++ return ERANGE;
++
++ ei = dino_ref (np->cache_id);
++ blkno = ei->i_file_acl;
++
++ if (blkno == 0)
++ {
++ /* Allocate and initialize new block */
++ block_t goal;
++
++ assert (!diskfs_readonly);
++
++ goal = sblock->s_first_data_block + np->dn->info.i_block_group *
++ EXT2_BLOCKS_PER_GROUP (sblock);
++ blkno = ext2_new_block (goal, 0, 0, 0);
++ block = disk_cache_block_ref (blkno);
++
++ if (blkno == 0)
++ {
++ err = ENOSPC;
++ goto cleanup;
++ }
++
++ memset (block, 0, block_size);
++
++ header = EXT2_XATTR_HEADER (block);
++ header->h_magic = EXT2_XATTR_BLOCK_MAGIC;
++ header->h_blocks = 1;
++ header->h_refcount = 1;
++ }
++ else
++ {
++ block = disk_cache_block_ref (blkno);
++ header = EXT2_XATTR_HEADER (block);
++ if (header->h_magic != EXT2_XATTR_BLOCK_MAGIC || header->h_blocks != 1)
++ {
++ ext2_warning ("Invalid extended attribute block.");
++ err = EIO;
++ goto cleanup;
++ }
++ }
++
++ entry = EXT2_XATTR_ENTRY_FIRST (header);
++ location = NULL;
++
++ rest = block_size;
++ err = ENODATA;
++ found = FALSE;
++
++ while (!EXT2_XATTR_ENTRY_LAST (entry))
++ {
++ int size;
++ int cmp;
++
++ err = xattr_entry_get (NULL, entry, name, NULL, &size, &cmp);
++ if (err == 0)
++ {
++ location = entry;
++ found = TRUE;
++ }
++ else if (err == ENODATA)
++ {
++ /* The xattr entry are sorted by attribute name */
++ if (cmp < 0 && !found)
++ {
++ location = entry;
++ found = FALSE;
++ }
++ }
++ else
++ {
++ break;
++ }
++
++ rest -= EXT2_XATTR_ALIGN (entry->e_value_size);
++ entry = EXT2_XATTR_ENTRY_NEXT (entry);
++ }
++
++ if (err != 0 && err != ENODATA)
++ {
++ goto cleanup;
++ }
++
++ if (location == NULL)
++ location = entry;
++
++ rest = rest - EXT2_XATTR_ENTRY_OFFSET (header, entry);
++ ext2_debug("space rest: %d", rest);
++
++ /* 4 null bytes after xattr entry */
++ if (rest < 4)
++ {
++ err = EIO;
++ goto cleanup;
++ }
++
++ if (value && flags & XATTR_CREATE)
++ {
++ if (found)
++ {
++ err = EEXIST;
++ goto cleanup;
++ }
++ else
++ err = xattr_entry_create (header, entry, location, name, value, len,
++ rest);
++ }
++ else if (value && flags & XATTR_REPLACE)
++ {
++ if (!found)
++ {
++ err = ENODATA;
++ goto cleanup;
++ }
++ else
++ err = xattr_entry_replace (header, entry, location, value, len, rest);
++ }
++ else if (value)
++ {
++ if (found)
++ err = xattr_entry_replace (header, entry, location, value, len, rest);
++ else
++ err = xattr_entry_create (header, entry, location, name, value, len,
++ rest);
++ }
++ else
++ {
++ if (flags & XATTR_REPLACE || flags & XATTR_CREATE)
++ {
++ err = EINVAL;
++ goto cleanup;
++ }
++ else if (!found)
++ {
++ err = ENODATA;
++ goto cleanup;
++ }
++ else
++ err = xattr_entry_remove (header, entry, location, rest);
++ }
++
++ /* Check if the xattr block is empty */
++ entry = EXT2_XATTR_ENTRY_FIRST (header);
++ int empty = EXT2_XATTR_ENTRY_LAST (entry);
++
++ if (err == 0)
++ {
++ if (empty)
++ {
++ disk_cache_block_deref (block);
++ dino_deref (ei);
++
++ return ext2_free_xattr_block (np);
++ }
++ else
++ {
++ xattr_entry_rehash (header, location);
++
++ record_global_poke (block);
++
++ if (ei->i_file_acl == 0)
++ {
++ np->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block;
++ np->dn_set_ctime = 1;
++
++ ei->i_file_acl = blkno;
++ record_global_poke (ei);
++ }
++ else
++ dino_deref (ei);
++
++ return 0;
++ }
++ }
++
++cleanup:
++ if (block)
++ disk_cache_block_deref (block);
++ dino_deref (ei);
++
++ return err;
++
++}
+diff --git a/ext2fs/xattr.h b/ext2fs/xattr.h
+new file mode 100644
+index 0000000..d9b87e6
+--- /dev/null
++++ b/ext2fs/xattr.h
+@@ -0,0 +1,85 @@
++ /* Ext2 support for extended attributes
++
++ Copyright (C) 2006, 2016 Free Software Foundation, Inc.
++
++ Written by Thadeu Lima de Souza Cascardo <cascardo@dcc.ufmg.br>
++ and Shengyu Zhang <lastavengers@outlook.com>
++
++ This program is free software; you can redistribute it and/or
++ modify it under the terms of the GNU General Public License as
++ published by the Free Software Foundation; either version 2, or (at
++ your option) any later version.
++
++ This program is distributed in the hope that it will be useful, but
++ WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
++
++#ifndef EXT2_XATTR_H
++#define EXT2_XATTR_H
++
++#include "ext2fs.h"
++
++/* Identifies whether a block is a proper xattr block. */
++#define EXT2_XATTR_BLOCK_MAGIC 0xEA020000
++
++/* xattr block header. */
++struct ext2_xattr_header
++{
++ __u32 h_magic; /* h_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 */
++};
++
++/* xattr entry in xattr block. */
++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)
++
++/* Entry alignment in xattr block. */
++#define EXT2_XATTR_ALIGN(x) (((unsigned long) (x) + \
++ EXT2_XATTR_ROUND) & \
++ (~EXT2_XATTR_ROUND))
++
++/* Given a fs block, return the xattr header. */
++#define EXT2_XATTR_HEADER(block) ((struct ext2_xattr_header *) block)
++
++/* Aligned size of entry, including the name length. */
++#define EXT2_XATTR_ENTRY_SIZE(len) EXT2_XATTR_ALIGN ((sizeof \
++ (struct ext2_xattr_entry) + \
++ len))
++
++/* Offset of entry, given the block header. */
++#define EXT2_XATTR_ENTRY_OFFSET(header, entry) ((off_t) ((char *) entry - \
++ (char *) header))
++
++/* First entry of xattr block, given its header. */
++#define EXT2_XATTR_ENTRY_FIRST(header) ((struct ext2_xattr_entry *) (header + 1))
++
++/* Next entry, giving an entry. */
++#define EXT2_XATTR_ENTRY_NEXT(entry) ((struct ext2_xattr_entry *) \
++ ((char *) entry + \
++ EXT2_XATTR_ENTRY_SIZE \
++ (entry->e_name_len)))
++
++/* Checks if this entry is the last (not valid) one. */
++#define EXT2_XATTR_ENTRY_LAST(entry) (*(unsigned long *) entry == 0UL)
++
++#endif
+--
+2.8.1
+