diff options
Diffstat (limited to 'debian/patches/xattr0001-ext2fs-add-xattr-support.patch')
-rw-r--r-- | debian/patches/xattr0001-ext2fs-add-xattr-support.patch | 1070 |
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 + |