From be392f84799b9e0ba2a2bded5d350190994b5a87 Mon Sep 17 00:00:00 2001
From: Justus Winter <justus@gnupg.org>
Date: Thu, 28 Jul 2016 15:18:10 +0200
Subject: add patch series

---
 debian/patches/series                              |    6 +
 .../xattr0001-ext2fs-add-xattr-support.patch       | 1070 ++++++++++++++++++++
 ...fs-using-xattr-to-store-translator-record.patch |  231 +++++
 debian/patches/xattr0003-Small-fixes.patch         |   44 +
 ...r0004-set-translator-flag-on-reading-typo.patch |   42 +
 ...-implement-xattr-support-on-linux-fs-only.patch |   28 +
 .../xattr0006-Fix-removing-the-translator.patch    |   28 +
 7 files changed, 1449 insertions(+)
 create mode 100644 debian/patches/xattr0001-ext2fs-add-xattr-support.patch
 create mode 100644 debian/patches/xattr0002-ext2fs-using-xattr-to-store-translator-record.patch
 create mode 100644 debian/patches/xattr0003-Small-fixes.patch
 create mode 100644 debian/patches/xattr0004-set-translator-flag-on-reading-typo.patch
 create mode 100644 debian/patches/xattr0005-Idea-implement-xattr-support-on-linux-fs-only.patch
 create mode 100644 debian/patches/xattr0006-Fix-removing-the-translator.patch

diff --git a/debian/patches/series b/debian/patches/series
index fcf808ce..547efdd0 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -44,3 +44,9 @@ leaks0001-xxx-print-fail.patch
 
 pager-alloc0001-libpager-provide-pager_create_alloc.patch
 pager-alloc0002-more.patch
+xattr0001-ext2fs-add-xattr-support.patch
+xattr0002-ext2fs-using-xattr-to-store-translator-record.patch
+xattr0003-Small-fixes.patch
+xattr0004-set-translator-flag-on-reading-typo.patch
+xattr0005-Idea-implement-xattr-support-on-linux-fs-only.patch
+xattr0006-Fix-removing-the-translator.patch
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
+
diff --git a/debian/patches/xattr0002-ext2fs-using-xattr-to-store-translator-record.patch b/debian/patches/xattr0002-ext2fs-using-xattr-to-store-translator-record.patch
new file mode 100644
index 00000000..b67606ba
--- /dev/null
+++ b/debian/patches/xattr0002-ext2fs-using-xattr-to-store-translator-record.patch
@@ -0,0 +1,231 @@
+From 8743e2f7c22c50bcc289c1a5ac093731f9d1638c Mon Sep 17 00:00:00 2001
+From: Shengyu Zhang <lastavengers@outlook.com>
+Date: Fri, 8 Jul 2016 00:21:52 +0200
+Subject: [PATCH hurd 2/6] ext2fs: using xattr to store translator record
+
+The purpose we implement xattr is storing passive translator in a
+more general way. Now passive translator record now can be stored
+using extended attributes. The new diskfs_{get,set}_translator()
+functions are also compatibility with the "legacy" passive translator
+record.
+
+Thanks for antrik's suggestions.
+---
+ ext2fs/inode.c | 158 +++++++++++++++++++++++++++++----------------------------
+ 1 file changed, 81 insertions(+), 77 deletions(-)
+
+diff --git a/ext2fs/inode.c b/ext2fs/inode.c
+index ccc8d69..175bc26 100644
+--- a/ext2fs/inode.c
++++ b/ext2fs/inode.c
+@@ -26,6 +26,7 @@
+ #include <sys/stat.h>
+ #include <sys/statfs.h>
+ #include <sys/statvfs.h>
++#include <sys/xattr.h>
+ 
+ /* these flags aren't actually defined by a header file yet, so temporarily
+    disable them if necessary.  */
+@@ -540,81 +541,62 @@ error_t
+ diskfs_set_translator (struct node *np, const char *name, unsigned namelen,
+ 		       struct protid *cred)
+ {
+-  daddr_t blkno;
++  int len;
+   error_t err;
+-  char buf[block_size];
+-  struct ext2_inode *di;
+ 
+   assert (!diskfs_readonly);
+ 
+-  if (sblock->s_creator_os != EXT2_OS_HURD)
+-    return EOPNOTSUPP;
+-
+-  if (namelen + 2 > block_size)
+-    return ENAMETOOLONG;
+-
+   err = diskfs_catch_exception ();
+   if (err)
+     return err;
+ 
+-  di = dino_ref (np->cache_id);
+-  blkno = di->i_translator;
+-
+-  if (namelen && !blkno)
++  /* If a old translator record found, clear it */
++  if (sblock->s_creator_os == EXT2_OS_HURD)
+     {
+-      /* Allocate block for translator */
+-      blkno =
+-	ext2_new_block ((diskfs_node_disknode (np)->info.i_block_group
+-			 * EXT2_BLOCKS_PER_GROUP (sblock))
+-			+ sblock->s_first_data_block,
+-			0, 0, 0);
+-      if (blkno == 0)
+-	{
+-	  dino_deref (di);
+-	  diskfs_end_catch_exception ();
+-	  return ENOSPC;
+-	}
++      daddr_t blkno;
++      struct ext2_inode *di;
+ 
+-      di->i_translator = blkno;
+-      diskfs_node_disknode (np)->info_i_translator = blkno;
+-      record_global_poke (di);
++      di = dino_ref (np->cache_id);
++      blkno = di->i_translator;
+ 
+-      np->dn_stat.st_blocks += 1 << log2_stat_blocks_per_fs_block;
+-      np->dn_set_ctime = 1;
+-    }
+-  else if (!namelen && blkno)
+-    {
+-      /* Clear block for translator going away. */
+-      di->i_translator = 0;
+-      diskfs_node_disknode (np)->info_i_translator = 0;
+-      record_global_poke (di);
+-      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;
++      if (blkno)
++	{
++          ext2_warning("Old tranlator record found, clear it");
++
++          /* Clear block for translator going away. */
++          di->i_translator = 0;
++          diskfs_node_disknode (np)->info_i_translator = 0;
++          record_global_poke (di);
++          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
++        dino_deref (di);
+     }
+-  else
+-    dino_deref (di);
+ 
+-  if (namelen)
+-    {
+-      void *blkptr;
+-
+-      buf[0] = namelen & 0xFF;
+-      buf[1] = (namelen >> 8) & 0xFF;
+-      memcpy (buf + 2, name, namelen);
++  /* Use xattr to store translator record, with key "gnu.translator" */
++  err = ext2_get_xattr(np, "gnu.translator", NULL, &len);
++  if (err && err != ENODATA)
++    return err;
+ 
+-      blkptr = disk_cache_block_ref (blkno);
+-      memcpy (blkptr, buf, block_size);
+-      record_global_poke (blkptr);
++  if (namelen && err == ENODATA)
++    {
++      err = ext2_set_xattr(np, "gnu.translator", name, namelen, XATTR_CREATE);
+ 
+       np->dn_stat.st_mode |= S_IPTRANS;
+       np->dn_set_ctime = 1;
+     }
++  else if (!namelen && !err)
++    {
++      err = ext2_set_xattr(np, "gnu.translator", NULL, 0, 0);
++    }
+ 
+   diskfs_end_catch_exception ();
+   return err;
++
+ }
+ 
+ /* Implement the diskfs_get_translator callback from the diskfs library.
+@@ -623,37 +605,59 @@ error_t
+ diskfs_get_translator (struct node *np, char **namep, unsigned *namelen)
+ {
+   error_t err = 0;
+-  daddr_t blkno;
+-  unsigned datalen;
+-  void *transloc;
+-  struct ext2_inode *di;
+-
+-  assert (sblock->s_creator_os == EXT2_OS_HURD);
++  int datalen;
+ 
+   err = diskfs_catch_exception ();
+   if (err)
+     return err;
+ 
+-  di = dino_ref (np->cache_id);
+-  blkno = di->i_translator;
+-  dino_deref (di);
+-  assert (blkno);
+-  transloc = disk_cache_block_ref (blkno);
+-
+-  datalen =
+-    ((unsigned char *)transloc)[0] + (((unsigned char *)transloc)[1] << 8);
+-  if (datalen > block_size - 2)
+-    err = EFTYPE;		/* ? */
+-  else
++  /* If a old translator record found, read it firstly */
++  if (sblock->s_creator_os == EXT2_OS_HURD)
+     {
+-      *namep = malloc (datalen);
+-      if (!*namep)
+-	err = ENOMEM;
+-      else
+-	memcpy (*namep, transloc + 2, datalen);
++      daddr_t blkno;
++      void *transloc;
++      struct ext2_inode *di;
++
++      di = dino_ref (np->cache_id);
++      blkno = di->i_translator;
++      dino_deref (di);
++
++      if (blkno)
++        {
++          ext2_warning("This is a old translotor record, please update it");
++
++          transloc = disk_cache_block_ref (blkno);
++          datalen = ((unsigned char *)transloc)[0] +
++            (((unsigned char *)transloc)[1] << 8);
++          if (datalen > block_size - 2)
++            err = EFTYPE;  /* ? */
++          else
++            {
++              *namep = malloc (datalen);
++              if (!*namep)
++                err = ENOMEM;
++              else
++                memcpy (*namep, transloc + 2, datalen);
++            }
++
++          disk_cache_block_deref (transloc);
++          diskfs_end_catch_exception ();
++
++          *namelen = datalen;
++          return err;
++        }
+     }
+ 
+-  disk_cache_block_deref (transloc);
++  err = ext2_get_xattr (np, "gnu.translator", NULL, &datalen);
++  if (err)
++    return err;
++
++  *namep = malloc (datalen);
++  if (!*namep)
++    err = ENOMEM;
++  else
++    err = ext2_get_xattr (np, "gnu.translator", *namep, &datalen);
++
+   diskfs_end_catch_exception ();
+ 
+   *namelen = datalen;
+-- 
+2.8.1
+
diff --git a/debian/patches/xattr0003-Small-fixes.patch b/debian/patches/xattr0003-Small-fixes.patch
new file mode 100644
index 00000000..77a5e433
--- /dev/null
+++ b/debian/patches/xattr0003-Small-fixes.patch
@@ -0,0 +1,44 @@
+From 19f7a0af88ebd92223c89c7134d5ba15b781c8d9 Mon Sep 17 00:00:00 2001
+From: Justus Winter <justus@gnupg.org>
+Date: Thu, 14 Jul 2016 23:53:28 +0200
+Subject: [PATCH hurd 3/6] Small fixes
+
+---
+ ext2fs/inode.c | 15 +++++++--------
+ 1 file changed, 7 insertions(+), 8 deletions(-)
+
+diff --git a/ext2fs/inode.c b/ext2fs/inode.c
+index 175bc26..fd28295 100644
+--- a/ext2fs/inode.c
++++ b/ext2fs/inode.c
+@@ -578,20 +578,19 @@ diskfs_set_translator (struct node *np, const char *name, unsigned namelen,
+     }
+ 
+   /* Use xattr to store translator record, with key "gnu.translator" */
+-  err = ext2_get_xattr(np, "gnu.translator", NULL, &len);
+-  if (err && err != ENODATA)
+-    return err;
+-
+-  if (namelen && err == ENODATA)
++  if (namelen)
+     {
+-      err = ext2_set_xattr(np, "gnu.translator", name, namelen, XATTR_CREATE);
++      err = ext2_set_xattr (np, "gnu.translator", name, namelen, 0);
+ 
+       np->dn_stat.st_mode |= S_IPTRANS;
+       np->dn_set_ctime = 1;
+     }
+-  else if (!namelen && !err)
++  else
+     {
+-      err = ext2_set_xattr(np, "gnu.translator", NULL, 0, 0);
++      err = ext2_set_xattr (np, "gnu.translator", NULL, 0, 0);
++
++      np->dn_stat.st_mode &= ~S_IPTRANS;
++      np->dn_set_ctime = 1;
+     }
+ 
+   diskfs_end_catch_exception ();
+-- 
+2.8.1
+
diff --git a/debian/patches/xattr0004-set-translator-flag-on-reading-typo.patch b/debian/patches/xattr0004-set-translator-flag-on-reading-typo.patch
new file mode 100644
index 00000000..14b6bb44
--- /dev/null
+++ b/debian/patches/xattr0004-set-translator-flag-on-reading-typo.patch
@@ -0,0 +1,42 @@
+From cbc7438ca1f23b16d01c5dade7caf768be0e7c5a Mon Sep 17 00:00:00 2001
+From: Justus Winter <justus@gnupg.org>
+Date: Sun, 24 Jul 2016 19:40:37 +0200
+Subject: [PATCH hurd 4/6] set translator flag on reading; typo
+
+---
+ ext2fs/inode.c | 10 +++++++++-
+ 1 file changed, 9 insertions(+), 1 deletion(-)
+
+diff --git a/ext2fs/inode.c b/ext2fs/inode.c
+index fd28295..f1c3356 100644
+--- a/ext2fs/inode.c
++++ b/ext2fs/inode.c
+@@ -167,8 +167,16 @@ diskfs_user_read_node (struct node *np, struct lookup_context *ctx)
+     {
+       st->st_mode = di->i_mode | (di->i_mode_high << 16);
+       st->st_mode &= ~S_ITRANS;
++
+       if (di->i_translator)
+ 	st->st_mode |= S_IPTRANS;
++      else
++        {
++          size_t datalen;
++          err = ext2_get_xattr (np, "gnu.translator", NULL, &datalen);
++          if (! err && datalen > 0)
++            st->st_mode |= S_IPTRANS;
++        }
+ 
+       st->st_uid = di->i_uid | (di->i_uid_high << 16);
+       st->st_gid = di->i_gid | (di->i_gid_high << 16);
+@@ -623,7 +631,7 @@ diskfs_get_translator (struct node *np, char **namep, unsigned *namelen)
+ 
+       if (blkno)
+         {
+-          ext2_warning("This is a old translotor record, please update it");
++          ext2_warning("This is an old translator record, please update it");
+ 
+           transloc = disk_cache_block_ref (blkno);
+           datalen = ((unsigned char *)transloc)[0] +
+-- 
+2.8.1
+
diff --git a/debian/patches/xattr0005-Idea-implement-xattr-support-on-linux-fs-only.patch b/debian/patches/xattr0005-Idea-implement-xattr-support-on-linux-fs-only.patch
new file mode 100644
index 00000000..9868e888
--- /dev/null
+++ b/debian/patches/xattr0005-Idea-implement-xattr-support-on-linux-fs-only.patch
@@ -0,0 +1,28 @@
+From 3b6a8690093474b98e035d579da7767f88246302 Mon Sep 17 00:00:00 2001
+From: Justus Winter <justus@gnupg.org>
+Date: Tue, 26 Jul 2016 15:26:49 +0200
+Subject: [PATCH hurd 5/6] Idea: implement xattr support on linux fs (only!?)
+
+---
+ ext2fs/inode.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/ext2fs/inode.c b/ext2fs/inode.c
+index f1c3356..f2e2f77 100644
+--- a/ext2fs/inode.c
++++ b/ext2fs/inode.c
+@@ -187,7 +187,11 @@ diskfs_user_read_node (struct node *np, struct lookup_context *ctx)
+     }
+   else
+     {
++      size_t datalen;
+       st->st_mode = di->i_mode & ~S_ITRANS;
++      err = ext2_get_xattr (np, "gnu.translator", NULL, &datalen);
++      if (! err && datalen > 0)
++        st->st_mode |= S_IPTRANS;
+       st->st_uid = di->i_uid;
+       st->st_gid = di->i_gid;
+       st->st_author = st->st_uid;
+-- 
+2.8.1
+
diff --git a/debian/patches/xattr0006-Fix-removing-the-translator.patch b/debian/patches/xattr0006-Fix-removing-the-translator.patch
new file mode 100644
index 00000000..e3b496fc
--- /dev/null
+++ b/debian/patches/xattr0006-Fix-removing-the-translator.patch
@@ -0,0 +1,28 @@
+From 9dca47ea808cdb30922d436cd029f2a826fac6e8 Mon Sep 17 00:00:00 2001
+From: Justus Winter <justus@gnupg.org>
+Date: Tue, 26 Jul 2016 15:27:03 +0200
+Subject: [PATCH hurd 6/6] Fix removing the translator
+
+---
+ ext2fs/inode.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/ext2fs/inode.c b/ext2fs/inode.c
+index f2e2f77..78cba3a 100644
+--- a/ext2fs/inode.c
++++ b/ext2fs/inode.c
+@@ -599,7 +599,11 @@ diskfs_set_translator (struct node *np, const char *name, unsigned namelen,
+     }
+   else
+     {
++      /* Removing the translator.  */
+       err = ext2_set_xattr (np, "gnu.translator", NULL, 0, 0);
++      if (err == ENODATA)
++        /* Happens if the key did not exist in the first place.  */
++        err = 0;
+ 
+       np->dn_stat.st_mode &= ~S_IPTRANS;
+       np->dn_set_ctime = 1;
+-- 
+2.8.1
+
-- 
cgit v1.2.3