commit-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[hurd] 01/21: ext2fs: Add support for xattr


From: Samuel Thibault
Subject: [hurd] 01/21: ext2fs: Add support for xattr
Date: Sat, 04 Mar 2017 21:10:57 +0000

This is an automated email from the git hooks/post-receive script.

sthibault pushed a commit to branch upstream
in repository hurd.

commit 6ebebc80de3dfc7ada3a69d609f00088c2143be3
Author: Shengyu Zhang <address@hidden>
Date:   Fri May 13 09:18:59 2016 +0800

    ext2fs: Add support for xattr
    
    * ext2fs/Makefile (SRCS): Add xattr.c.
    * ext2fs/ext2_fs.h: Define EXT2_FEATURE_COMPAT_EXT_ATTR.
    * ext2fs/ext2fs.h: Add xattr functions.
    * ext2fs/ialloc.c (diskfs_free_node): Free xattr block.
    * ext2fs/xattr.c: xattr implement.
    * 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(-)

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 a3d22b2..76adf63 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>
@@ -575,3 +578,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..1e298dcb
--- /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 <address@hidden>
+   and Shengyu Zhang <address@hidden>
+
+   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..245f896
--- /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 <address@hidden>
+   and Shengyu Zhang <address@hidden>
+
+   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

-- 
Alioth's /usr/local/bin/git-commit-notice on 
/srv/git.debian.org/git/pkg-hurd/hurd.git



reply via email to

[Prev in Thread] Current Thread [Next in Thread]