summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustus Winter <justus@gnupg.org>2016-07-28 15:18:10 +0200
committerJustus Winter <justus@gnupg.org>2016-07-28 15:18:10 +0200
commitbe392f84799b9e0ba2a2bded5d350190994b5a87 (patch)
tree413a45ac82d8916df1669528676a27cc08e64fc7
parent6bfe68531d81f5a1353b88586e6470ca36d0e724 (diff)
add patch seriesHEADmaster
-rw-r--r--debian/patches/series6
-rw-r--r--debian/patches/xattr0001-ext2fs-add-xattr-support.patch1070
-rw-r--r--debian/patches/xattr0002-ext2fs-using-xattr-to-store-translator-record.patch231
-rw-r--r--debian/patches/xattr0003-Small-fixes.patch44
-rw-r--r--debian/patches/xattr0004-set-translator-flag-on-reading-typo.patch42
-rw-r--r--debian/patches/xattr0005-Idea-implement-xattr-support-on-linux-fs-only.patch28
-rw-r--r--debian/patches/xattr0006-Fix-removing-the-translator.patch28
7 files changed, 1449 insertions, 0 deletions
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
+