From 7fec7f3357fbe6bce33a161b6b982feccf88d556 Mon Sep 17 00:00:00 2001 From: Shengyu Zhang 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 #include #include @@ -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 + and Shengyu Zhang + + 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 +#include +#include + +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 + and Shengyu Zhang + + 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