summaryrefslogtreecommitdiff
path: root/debian
diff options
context:
space:
mode:
authorJustus Winter <justus@gnupg.org>2016-04-17 17:10:35 +0200
committerJustus Winter <justus@gnupg.org>2016-04-17 17:10:35 +0200
commitccd7e7529feeac77f053f9663d5f5256e6487627 (patch)
tree2f96de7fe0706e2d549ca4464dd8356b7065b019 /debian
parentbb48da218883fedb08ed4282747a8fbf7b4dfe8a (diff)
add patch series
Diffstat (limited to 'debian')
-rw-r--r--debian/patches/gpg0001-isofs-make-superblock-detection-more-robust.patch82
-rw-r--r--debian/patches/gpg0002-utils-settrans-get-an-authenticated-root-node-in-chr.patch83
-rw-r--r--debian/patches/gpg0003-utils-settrans-add-option-to-specify-the-underlying-.patch71
-rw-r--r--debian/patches/gpg0004-trans-add-identity-translator.patch941
-rw-r--r--debian/patches/gpg0005-trans-add-transparent-GnuPG-translator.patch890
-rw-r--r--debian/patches/series5
6 files changed, 2072 insertions, 0 deletions
diff --git a/debian/patches/gpg0001-isofs-make-superblock-detection-more-robust.patch b/debian/patches/gpg0001-isofs-make-superblock-detection-more-robust.patch
new file mode 100644
index 00000000..eee344c8
--- /dev/null
+++ b/debian/patches/gpg0001-isofs-make-superblock-detection-more-robust.patch
@@ -0,0 +1,82 @@
+From 21ee0e79e90c3cf63613bc1a00a609cfaf1777b2 Mon Sep 17 00:00:00 2001
+From: Justus Winter <justus@gnupg.org>
+Date: Sun, 17 Apr 2016 16:36:32 +0200
+Subject: [PATCH hurd 1/5] isofs: make superblock detection more robust
+
+* isofs/isofs.h (disk_image_len): New variable.
+* isofs/main.c (read_sblock): Avoid out of bounds access.
+* isofs/pager.c (disk_image_len): New variable.
+(create_disk_pager): Initialize 'disk_image_len'.
+---
+ isofs/isofs.h | 1 +
+ isofs/main.c | 9 ++-------
+ isofs/pager.c | 2 ++
+ 3 files changed, 5 insertions(+), 7 deletions(-)
+
+diff --git a/isofs/isofs.h b/isofs/isofs.h
+index 3f6690b..2ba013c 100644
+--- a/isofs/isofs.h
++++ b/isofs/isofs.h
+@@ -75,6 +75,7 @@ char *mounted_on;
+
+ /* Mapped image of disk */
+ void *disk_image;
++size_t disk_image_len;
+
+ /* Processed sblock info */
+
+diff --git a/isofs/main.c b/isofs/main.c
+index 95c90fe..c07cf3f 100644
+--- a/isofs/main.c
++++ b/isofs/main.c
+@@ -72,17 +72,13 @@ static void
+ read_sblock ()
+ {
+ struct voldesc *vd;
+- error_t err;
+ struct sblock * volatile sb = 0;
+
+- err = diskfs_catch_exception ();
+- if (err)
+- error (4, err, "reading superblock");
+-
+ /* Start at logical sector 16 and keep going until
+ we find a matching superblock */
+ for (vd = disk_image + (logical_sector_size * 16);
+- (void *) vd < disk_image + (logical_sector_size * 500); /* for sanity */
++ (void *) vd < disk_image + (logical_sector_size * 500) /* for sanity */
++ && (void *) vd + logical_sector_size < disk_image + disk_image_len;
+ vd = (void *) vd + logical_sector_size)
+ {
+ if (vd->type == VOLDESC_END)
+@@ -105,7 +101,6 @@ read_sblock ()
+ if (!sblock)
+ error (1, errno, "Could not allocate memory for superblock");
+ memcpy (sblock, sb, sizeof (struct sblock));
+- diskfs_end_catch_exception ();
+
+ /* Parse some important bits of this */
+ logical_block_size = isonum_723 (sblock->blksize);
+diff --git a/isofs/pager.c b/isofs/pager.c
+index 35de37e..b4be4e2 100644
+--- a/isofs/pager.c
++++ b/isofs/pager.c
+@@ -28,6 +28,7 @@ struct port_bucket *pager_bucket;
+
+ /* Mapped image of the disk */
+ void *disk_image;
++size_t disk_image_len;
+
+
+ /* Implement the pager_read_page callback from the pager library. See
+@@ -148,6 +149,7 @@ create_disk_pager (void)
+ upi->np = 0;
+ pager_bucket = ports_create_bucket ();
+ diskfs_start_disk_pager (upi, pager_bucket, 1, 0, store->size, &disk_image);
++ disk_image_len = store->size;
+ upi->p = diskfs_disk_pager;
+ }
+
+--
+2.1.4
+
diff --git a/debian/patches/gpg0002-utils-settrans-get-an-authenticated-root-node-in-chr.patch b/debian/patches/gpg0002-utils-settrans-get-an-authenticated-root-node-in-chr.patch
new file mode 100644
index 00000000..559cab7b
--- /dev/null
+++ b/debian/patches/gpg0002-utils-settrans-get-an-authenticated-root-node-in-chr.patch
@@ -0,0 +1,83 @@
+From f5e6769c33723a1d4b72722eff8bb68245ca3803 Mon Sep 17 00:00:00 2001
+From: Justus Winter <justus@gnupg.org>
+Date: Sat, 16 Apr 2016 16:12:35 +0200
+Subject: [PATCH hurd 2/5] utils/settrans: get an authenticated root node in
+ chroot mode
+
+* utils/settrans.c (get_credentials): New function.
+(main): Get an authenticated root node for the process we are
+chrooting.
+
+Signed-off-by: Justus Winter <justus@gnupg.org>
+---
+ utils/settrans.c | 45 ++++++++++++++++++++++++++++++++++++++++++++-
+ 1 file changed, 44 insertions(+), 1 deletion(-)
+
+diff --git a/utils/settrans.c b/utils/settrans.c
+index 00cc358..399bd12 100644
+--- a/utils/settrans.c
++++ b/utils/settrans.c
+@@ -88,6 +88,44 @@ static char *args_doc = "NODE [TRANSLATOR ARG...]";
+ static char *doc = "Set the passive/active translator on NODE."
+ "\vBy default the passive translator is set.";
+
++/* Authentication of the current process. */
++uid_t *uids;
++gid_t *gids;
++size_t uids_len, gids_len;
++
++/* Initialize and populate the uids and gids vectors. */
++error_t
++get_credentials (void)
++{
++ /* Fetch uids... */
++ uids_len = geteuids (0, 0);
++ if (uids_len < 0)
++ return errno;
++
++ uids = malloc (uids_len * sizeof (uid_t));
++ if (! uids)
++ return ENOMEM;
++
++ uids_len = geteuids (uids_len, uids);
++ if (uids_len < 0)
++ return errno;
++
++ /* ... and gids. */
++ gids_len = getgroups (0, 0);
++ if (gids_len < 0)
++ return errno;
++
++ gids = malloc (gids_len * sizeof (gid_t));
++ if (! uids)
++ return ENOMEM;
++
++ gids_len = getgroups (gids_len, gids);
++ if (gids_len < 0)
++ return errno;
++
++ return 0;
++}
++
+ /* ---------------------------------------------------------------- */
+
+ int
+@@ -340,9 +378,14 @@ main(int argc, char *argv[])
+ mach_port_t root;
+ file_t executable;
+ char *prefixed_name;
++
++ err = get_credentials ();
++ if (err)
++ error (6, err, "getting credentials");
++
+ err = fsys_getroot (active_control,
+ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND,
+- NULL, 0, NULL, 0, 0,
++ uids, uids_len, gids, gids_len, 0,
+ &do_retry, retry_name, &root);
+ mach_port_deallocate (mach_task_self (), active_control);
+ if (err)
+--
+2.1.4
+
diff --git a/debian/patches/gpg0003-utils-settrans-add-option-to-specify-the-underlying-.patch b/debian/patches/gpg0003-utils-settrans-add-option-to-specify-the-underlying-.patch
new file mode 100644
index 00000000..010b7707
--- /dev/null
+++ b/debian/patches/gpg0003-utils-settrans-add-option-to-specify-the-underlying-.patch
@@ -0,0 +1,71 @@
+From 6ff6261bb8b2bcf419689fdae4b68f6114f014a3 Mon Sep 17 00:00:00 2001
+From: Justus Winter <justus@gnupg.org>
+Date: Thu, 24 Mar 2016 20:45:01 +0100
+Subject: [PATCH hurd 3/5] utils/settrans: add option to specify the underlying
+ node
+
+* utils/settrans.c (options): Add '--underlying'.
+(main): Handle new option.
+(open_node): Open the node if given.
+---
+ utils/settrans.c | 23 ++++++++++++++++++++++-
+ 1 file changed, 22 insertions(+), 1 deletion(-)
+
+diff --git a/utils/settrans.c b/utils/settrans.c
+index 399bd12..e01906b 100644
+--- a/utils/settrans.c
++++ b/utils/settrans.c
+@@ -64,6 +64,8 @@ static struct argp_option options[] =
+ {"exclusive", 'x', 0, 0, "Only set the translator if there is not one already"},
+ {"orphan", 'o', 0, 0, "Disconnect old translator from the filesystem "
+ "(do not ask it to go away)"},
++ {"underlying", 'U', "NODE", 0, "Open NODE and hand it to the translator "
++ "as the underlying node"},
+
+ {"chroot", 'C', 0, 0,
+ "Instead of setting the node's translator, take following arguments up to"
+@@ -157,6 +159,7 @@ main(int argc, char *argv[])
+ char *pid_file = NULL;
+ int excl = 0;
+ int timeout = DEFAULT_TIMEOUT * 1000; /* ms */
++ char *underlying_node_name = NULL;
+ char **chroot_command = 0;
+ char *chroot_chdir = "/";
+
+@@ -202,6 +205,11 @@ main(int argc, char *argv[])
+ break;
+
+ case 'o': orphan = 1; break;
++ case 'U':
++ underlying_node_name = strdup (arg);
++ if (underlying_node_name == NULL)
++ error(3, ENOMEM, "Failed to duplicate argument");
++ break;
+
+ case 'C':
+ if (chroot_command)
+@@ -330,7 +338,20 @@ main(int argc, char *argv[])
+ return open_err;
+ }
+
+- *underlying = node;
++ if (underlying_node_name)
++ {
++ *underlying = file_name_lookup (underlying_node_name,
++ flags | lookup_flags, 0666);
++ if (! MACH_PORT_VALID (*underlying))
++ {
++ /* For the error message. */
++ node_name = underlying_node_name;
++ open_err = errno;
++ return open_err;
++ }
++ }
++ else
++ *underlying = node;
+ *underlying_type = MACH_MSG_TYPE_COPY_SEND;
+
+ return 0;
+--
+2.1.4
+
diff --git a/debian/patches/gpg0004-trans-add-identity-translator.patch b/debian/patches/gpg0004-trans-add-identity-translator.patch
new file mode 100644
index 00000000..f72d4643
--- /dev/null
+++ b/debian/patches/gpg0004-trans-add-identity-translator.patch
@@ -0,0 +1,941 @@
+From 7e72e8a337c58e6cb6badff8be4381dc180d8277 Mon Sep 17 00:00:00 2001
+From: Justus Winter <4winter@informatik.uni-hamburg.de>
+Date: Sat, 23 Jan 2016 00:14:56 +0100
+Subject: [PATCH hurd 4/5] trans: add identity translator
+
+The 'identity' translator is the simplest possible translator. It
+computes the identity function of Hurdish RPC translations.
+
+* trans/Makefile: Add new files.
+* trans/chroot.c: New file.
+* trans/chroot.h: Likewise.
+* trans/identity.c: Likewise.
+---
+ trans/Makefile | 7 +-
+ trans/chroot.c | 724 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ trans/chroot.h | 34 +++
+ trans/identity.c | 113 +++++++++
+ 4 files changed, 875 insertions(+), 3 deletions(-)
+ create mode 100644 trans/chroot.c
+ create mode 100644 trans/chroot.h
+ create mode 100644 trans/identity.c
+
+diff --git a/trans/Makefile b/trans/Makefile
+index 65b51d1..e4eba0a 100644
+--- a/trans/Makefile
++++ b/trans/Makefile
+@@ -21,10 +21,10 @@ makemode := servers
+
+ targets = symlink firmlink ifsock magic null fifo new-fifo fwd crash \
+ password hello hello-mt streamio fakeroot proxy-defpager remap \
+- mtab
++ mtab identity
+ SRCS = ifsock.c symlink.c magic.c null.c fifo.c new-fifo.c fwd.c \
+ crash.c firmlink.c password.c hello.c hello-mt.c streamio.c \
+- fakeroot.c proxy-defpager.c remap.c mtab.c
++ fakeroot.c proxy-defpager.c remap.c mtab.c chroot.c identity.c
+ OBJS = $(SRCS:.c=.o) fsysServer.o ifsockServer.o passwordServer.o \
+ crashServer.o crash_replyUser.o msgServer.o \
+ default_pagerServer.o default_pagerUser.o \
+@@ -65,7 +65,8 @@ proxy-defpager: default_pagerServer.o default_pagerUser.o
+ streamio: device_replyServer.o
+ symlink: fsysServer.o
+
+-fakeroot: ../libnetfs/libnetfs.a
++identity: chroot.o
++fakeroot identity: ../libnetfs/libnetfs.a
+ fifo new-fifo: ../libpipe/libpipe.a
+ crash fifo firmlink hello hello-mt ifsock magic mtab new-fifo null password proxy-defpager remap streamio: ../libtrivfs/libtrivfs.a
+ $(targets): ../libfshelp/libfshelp.a \
+diff --git a/trans/chroot.c b/trans/chroot.c
+new file mode 100644
+index 0000000..0055d6c
+--- /dev/null
++++ b/trans/chroot.c
+@@ -0,0 +1,724 @@
++/* chroot.c -- support for writing chrooting translators
++ Copyright (C) 2002, 2003, 2008, 2013, 2016 Free Software Foundation, Inc.
++
++ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++#include <argz.h>
++#include <argp.h>
++#include <dirent.h>
++#include <error.h>
++#include <hurd/netfs.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <fcntl.h>
++#include <sys/mman.h>
++#include <sys/stat.h>
++#include <pthread.h>
++#include <hurd/ihash.h>
++#include <hurd/paths.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <unistd.h>
++
++#include <version.h>
++
++#include "chroot.h"
++
++#include "libnetfs/fs_S.h"
++#include "libnetfs/io_S.h"
++#include "libnetfs/fsys_S.h"
++#include "libports/notify_S.h"
++#include "libports/interrupt_S.h"
++
++/* Make a new virtual node. Always consumes the ports. If
++ successful, NP will be locked. */
++error_t __attribute__ ((weak))
++chroot_new_node (file_t file, size_t size, struct node **np)
++{
++ error_t err;
++ struct netnode *nn;
++
++ *np = netfs_make_node_alloc (sizeof *nn + size);
++ if (*np == NULL)
++ {
++ mach_port_deallocate (mach_task_self (), file);
++ return ENOMEM;
++ }
++ nn = netfs_node_netnode (*np);
++ nn->file = file;
++ return 0;
++}
++
++/* Node NP has no more references; free all its associated storage. */
++void
++netfs_node_norefs (struct node *np)
++{
++ pthread_mutex_unlock (&np->lock);
++
++ chroot_node_norefs (np);
++ mach_port_deallocate (mach_task_self (), netfs_node_netnode (np)->file);
++ free (np);
++}
++
++/* This is the cleanup function we install in netfs_protid_class. If
++ the associated nodes reference count would also drop to zero, and
++ the node has no faked attributes, we destroy it as well. */
++static void
++chroot_netfs_release_protid (void *cookie)
++{
++ netfs_release_protid (cookie);
++
++ int cports = ports_count_class (netfs_control_class);
++ int nports = ports_count_class (netfs_protid_class);
++ ports_enable_class (netfs_control_class);
++ ports_enable_class (netfs_protid_class);
++ if (cports == 0 && nports == 0)
++ {
++ /* The last client is gone. Our job is done. */
++ error_t err = netfs_shutdown (0);
++ if (! err)
++ exit (EXIT_SUCCESS);
++
++ /* If netfs_shutdown returns EBUSY, we lost a race against
++ fsys_goaway. Hence we ignore this error. */
++ if (err != EBUSY)
++ error (1, err, "netfs_shutdown");
++ }
++}
++
++error_t __attribute__ ((weak))
++chroot_init (void)
++{
++ /* Install our own clean routine. */
++ netfs_protid_class->clean_routine = chroot_netfs_release_protid;
++ return 0;
++}
++
++/* This is called by netfs_S_fsys_getroot. */
++error_t __attribute__ ((weak))
++netfs_check_open_permissions (struct iouser *user, struct node *np,
++ int flags, int newnode)
++{
++ return 0; /* XXX */
++}
++
++error_t __attribute__ ((weak))
++netfs_S_dir_lookup (struct protid *diruser,
++ char *filename,
++ int flags,
++ mode_t mode,
++ retry_type *do_retry,
++ char *retry_name,
++ mach_port_t *retry_port,
++ mach_msg_type_name_t *retry_port_type)
++{
++ struct node *dnp, *np;
++ error_t err;
++ struct protid *newpi;
++ struct iouser *user;
++ file_t file;
++ file_t dir;
++
++ if (!diruser)
++ return EOPNOTSUPP;
++
++ dnp = diruser->po->np;
++
++ //error (0, 0, "dir_lookup (#uids: %d, %s, flags %x, ...)", diruser->user->uids->num, filename, flags);
++
++ dir = netfs_node_netnode (dnp)->file;
++ err = dir_lookup (dir, filename, flags, mode, do_retry, retry_name, &file);
++ if (err)
++ return err;
++
++ switch (*do_retry)
++ {
++ case FS_RETRY_NORMAL:
++ break;
++
++ case FS_RETRY_REAUTH:
++ /* Fallthrough. */
++
++ case FS_RETRY_MAGICAL:
++ *retry_port = file;
++ *retry_port_type = MACH_MSG_TYPE_MOVE_SEND;
++ return 0;
++
++ default:
++ /* Invalid response to our dir_lookup request. */
++ if (MACH_PORT_VALID (file))
++ mach_port_deallocate (mach_task_self (), file);
++ *retry_port = MACH_PORT_NULL;
++ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
++ return EOPNOTSUPP;
++ }
++
++ /* We have a new port to an underlying node.
++ Find or make our corresponding virtual node. */
++
++ np = 0;
++
++ err = chroot_new_node (file, 0, &np);
++ if (!err)
++ err = netfs_validate_stat (np, diruser->user);
++ if (err)
++ goto lose;
++
++ assert (*do_retry == FS_RETRY_NORMAL);
++ flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK);
++
++ err = iohelp_dup_iouser (&user, diruser->user);
++ if (!err)
++ {
++ newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po),
++ user);
++ if (! newpi)
++ {
++ iohelp_free_iouser (user);
++ err = errno;
++ }
++ else
++ {
++ *retry_port = ports_get_right (newpi);
++ *retry_port_type = MACH_MSG_TYPE_MAKE_SEND;
++ ports_port_deref (newpi);
++ }
++ }
++
++ lose:
++ if (dir != netfs_node_netnode (dnp)->file)
++ mach_port_deallocate (mach_task_self (), dir);
++ if (np != NULL)
++ netfs_nput (np);
++ return err;
++}
++
++/* The user may define this function. Attempt to set the passive
++ translator record for FILE to ARGZ (of length ARGZLEN) for user
++ CRED. */
++error_t __attribute__ ((weak))
++netfs_set_translator (struct iouser *cred, struct node *np,
++ char *argz, size_t argzlen)
++{
++ return file_set_translator (netfs_node_netnode (np)->file,
++ FS_TRANS_EXCL|FS_TRANS_SET,
++ 0, 0,
++ argz, argzlen,
++ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
++}
++
++/* These callbacks are used only by the standard netfs_S_dir_lookup,
++ which we do not use. But the shared library requires us to define them. */
++error_t __attribute__ ((weak))
++netfs_attempt_lookup (struct iouser *user, struct node *dir,
++ char *name, struct node **np)
++{
++ assert (! "should not be here");
++ return EIEIO;
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_create_file (struct iouser *user, struct node *dir,
++ char *name, mode_t mode, struct node **np)
++{
++ assert (! "should not be here");
++ return EIEIO;
++}
++
++/* Make sure that NP->nn_stat is filled with the most current information.
++ CRED identifies the user responsible for the operation. NP is locked. */
++error_t __attribute__ ((weak))
++netfs_validate_stat (struct node *np, struct iouser *cred)
++{
++ struct stat st;
++ error_t err = io_stat (netfs_node_netnode (np)->file, &st);
++ if (err)
++ return err;
++
++ np->nn_stat = st;
++ /* XXX */
++ np->nn_translated = S_ISLNK (st.st_mode) ? S_IFLNK : 0;
++
++ return 0;
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_chown (struct iouser *cred, struct node *np,
++ uid_t uid, uid_t gid)
++{
++ if (! cred)
++ return EOPNOTSUPP;
++ return file_chown (netfs_node_netnode (np)->file, uid, gid);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_chauthor (struct iouser *cred, struct node *np, uid_t author)
++{
++ if (! cred)
++ return EOPNOTSUPP;
++ return file_chauthor (netfs_node_netnode (np)->file, author);
++}
++
++/* This should attempt a chmod call for the user specified by CRED on
++ locked node NODE, to change the mode to MODE. Unlike the normal Unix
++ and Hurd meaning of chmod, this function is also used to attempt to
++ change files into other types. If such a transition is attempted which
++ is impossible, then return EOPNOTSUPP. */
++error_t __attribute__ ((weak))
++netfs_attempt_chmod (struct iouser *cred, struct node *np, mode_t mode)
++{
++ if (! cred)
++ return EOPNOTSUPP;
++ return file_chmod (netfs_node_netnode (np)->file, mode);
++}
++
++/* The user must define this function. Attempt to turn locked node NP
++ (user CRED) into a symlink with target NAME. */
++error_t __attribute__ ((weak))
++netfs_attempt_mksymlink (struct iouser *cred, struct node *np, char *name)
++{
++ int namelen = strlen (name) + 1;
++ char trans[sizeof _HURD_SYMLINK + namelen];
++ memcpy (trans, _HURD_SYMLINK, sizeof _HURD_SYMLINK);
++ memcpy (&trans[sizeof _HURD_SYMLINK], name, namelen);
++ return file_set_translator (netfs_node_netnode (np)->file,
++ FS_TRANS_EXCL|FS_TRANS_SET,
++ FS_TRANS_EXCL|FS_TRANS_SET, 0,
++ trans, sizeof trans,
++ MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_mkdev (struct iouser *cred, struct node *np,
++ mode_t type, dev_t indexes)
++{
++ char *trans = 0;
++ int translen = asprintf (&trans, "%s%c%d%c%d",
++ S_ISCHR (type) ? _HURD_CHRDEV : _HURD_BLKDEV,
++ '\0', major (indexes), '\0', minor (indexes));
++ if (trans == 0)
++ return ENOMEM;
++ else
++ {
++ error_t err = file_set_translator (netfs_node_netnode (np)->file,
++ FS_TRANS_EXCL|FS_TRANS_SET,
++ FS_TRANS_EXCL|FS_TRANS_SET, 0,
++ trans, translen + 1,
++ MACH_PORT_NULL,
++ MACH_MSG_TYPE_COPY_SEND);
++ free (trans);
++ return err;
++ }
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_chflags (struct iouser *cred, struct node *np, int flags)
++{
++ return file_chflags (netfs_node_netnode (np)->file, flags);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_utimes (struct iouser *cred, struct node *np,
++ struct timespec *atime, struct timespec *mtime)
++{
++ union tv
++ {
++ struct timeval tv;
++ time_value_t tvt;
++ };
++ union tv a, m;
++ if (atime)
++ {
++ TIMESPEC_TO_TIMEVAL (&a.tv, atime);
++ }
++ else
++ a.tv.tv_sec = a.tv.tv_usec = -1;
++ if (mtime)
++ {
++ TIMESPEC_TO_TIMEVAL (&m.tv, mtime);
++ }
++ else
++ m.tv.tv_sec = m.tv.tv_usec = -1;
++
++ return file_utimes (netfs_node_netnode (np)->file, a.tvt, m.tvt);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_set_size (struct iouser *cred, struct node *np, off_t size)
++{
++ return file_set_size (netfs_node_netnode (np)->file, size);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_statfs (struct iouser *cred, struct node *np, struct statfs *st)
++{
++ return file_statfs (netfs_node_netnode (np)->file, st);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
++{
++ return file_sync (netfs_node_netnode (np)->file, wait, 0);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_syncfs (struct iouser *cred, int wait)
++{
++ return 0;
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_mkdir (struct iouser *user, struct node *dir,
++ char *name, mode_t mode)
++{
++ return dir_mkdir (netfs_node_netnode (dir)->file, name, mode | S_IRWXU);
++}
++
++
++/* XXX
++ Removing a node should mark the netnode so that it is GC'd when
++ it has no hard refs.
++ */
++
++error_t __attribute__ ((weak))
++netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name)
++{
++ return dir_unlink (netfs_node_netnode (dir)->file, name);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_rename (struct iouser *user, struct node *fromdir,
++ char *fromname, struct node *todir,
++ char *toname, int excl)
++{
++ return dir_rename (netfs_node_netnode (fromdir)->file, fromname,
++ netfs_node_netnode (todir)->file, toname, excl);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_rmdir (struct iouser *user,
++ struct node *dir, char *name)
++{
++ return dir_rmdir (netfs_node_netnode (dir)->file, name);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_link (struct iouser *user, struct node *dir,
++ struct node *file, char *name, int excl)
++{
++ return dir_link (netfs_node_netnode (dir)->file,
++ netfs_node_netnode (file)->file, name, excl);
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_mkfile (struct iouser *user, struct node *dir,
++ mode_t mode, struct node **np)
++{
++ file_t newfile;
++ error_t err = dir_mkfile (netfs_node_netnode (dir)->file, O_RDWR|O_EXEC,
++ mode, &newfile);
++ pthread_mutex_unlock (&dir->lock);
++ if (err)
++ return err;
++
++ err = chroot_new_node (newfile, 0, np);
++ if (err)
++ return err;
++
++ pthread_mutex_unlock (&(*np)->lock);
++ return err;
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_readlink (struct iouser *user, struct node *np, char *buf)
++{
++ char transbuf[sizeof _HURD_SYMLINK + np->nn_stat.st_size + 1];
++ char *trans = transbuf;
++ size_t translen = sizeof transbuf;
++ error_t err = file_get_translator (netfs_node_netnode (np)->file,
++ &trans, &translen);
++ if (err == 0)
++ {
++ if (translen < sizeof _HURD_SYMLINK
++ || memcmp (trans, _HURD_SYMLINK, sizeof _HURD_SYMLINK) != 0)
++ err = EINVAL;
++ else
++ {
++ assert (translen <= sizeof _HURD_SYMLINK + np->nn_stat.st_size + 1);
++ memcpy (buf, &trans[sizeof _HURD_SYMLINK],
++ translen - sizeof _HURD_SYMLINK);
++ }
++ if (trans != transbuf)
++ munmap (trans, translen);
++ }
++ return err;
++}
++
++error_t __attribute__ ((weak))
++netfs_get_dirents (struct iouser *cred, struct node *dir,
++ int entry, int nentries, char **data,
++ mach_msg_type_number_t *datacnt,
++ vm_size_t bufsize, int *amt)
++{
++ error_t err;
++ err = dir_readdir (netfs_node_netnode (dir)->file, data, datacnt,
++ entry, nentries, bufsize, amt);
++ return err;
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_read (struct iouser *cred, struct node *np,
++ off_t offset, size_t *len, void *data)
++{
++ char *buf = data;
++ error_t err = io_read (netfs_node_netnode (np)->file,
++ &buf, len, offset, *len);
++ if (err == 0 && buf != data)
++ {
++ memcpy (data, buf, *len);
++ munmap (buf, *len);
++ }
++ return err;
++}
++
++error_t __attribute__ ((weak))
++netfs_attempt_write (struct iouser *cred, struct node *np,
++ off_t offset, size_t *len, void *data)
++{
++ return io_write (netfs_node_netnode (np)->file, data, *len, offset, len);
++}
++
++error_t __attribute__ ((weak))
++netfs_report_access (struct iouser *cred, struct node *np, int *types)
++{
++ return file_check_access (netfs_node_netnode (np)->file, types);
++}
++
++error_t __attribute__ ((weak))
++netfs_file_get_storage_info (struct iouser *cred,
++ struct node *np,
++ mach_port_t **ports,
++ mach_msg_type_name_t *ports_type,
++ mach_msg_type_number_t *num_ports,
++ int **ints,
++ mach_msg_type_number_t *num_ints,
++ off_t **offsets,
++ mach_msg_type_number_t *num_offsets,
++ char **data,
++ mach_msg_type_number_t *data_len)
++{
++ *ports_type = MACH_MSG_TYPE_MOVE_SEND;
++ return file_get_storage_info (netfs_node_netnode (np)->file,
++ ports, num_ports,
++ ints, num_ints,
++ offsets, num_offsets,
++ data, data_len);
++}
++
++error_t __attribute__ ((weak))
++netfs_S_file_exec (struct protid *user,
++ task_t task,
++ int flags,
++ char *argv,
++ size_t argvlen,
++ char *envp,
++ size_t envplen,
++ mach_port_t *fds,
++ size_t fdslen,
++ mach_port_t *portarray,
++ size_t portarraylen,
++ int *intarray,
++ size_t intarraylen,
++ mach_port_t *deallocnames,
++ size_t deallocnameslen,
++ mach_port_t *destroynames,
++ size_t destroynameslen)
++{
++ error_t err;
++ file_t file;
++
++ if (!user)
++ return EOPNOTSUPP;
++
++ file = netfs_node_netnode (user->po->np)->file;
++ err = mach_port_mod_refs (mach_task_self (),
++ file, MACH_PORT_RIGHT_SEND, 1);
++
++ if (!err)
++ {
++ /* We cannot use MACH_MSG_TYPE_MOVE_SEND because we might need to
++ retry an interrupted call that would have consumed the rights. */
++ err = file_exec (file, task, flags, argv, argvlen,
++ envp, envplen, fds, MACH_MSG_TYPE_COPY_SEND, fdslen,
++ portarray, MACH_MSG_TYPE_COPY_SEND, portarraylen,
++ intarray, intarraylen, deallocnames, deallocnameslen,
++ destroynames, destroynameslen);
++ mach_port_deallocate (mach_task_self (), file);
++ }
++
++ if (err == 0)
++ {
++ size_t i;
++ mach_port_deallocate (mach_task_self (), task);
++ for (i = 0; i < fdslen; ++i)
++ mach_port_deallocate (mach_task_self (), fds[i]);
++ for (i = 0; i < portarraylen; ++i)
++ mach_port_deallocate (mach_task_self (), portarray[i]);
++ }
++ return err;
++}
++
++error_t __attribute__ ((weak))
++netfs_S_io_map (struct protid *user,
++ mach_port_t *rdobj, mach_msg_type_name_t *rdobjtype,
++ mach_port_t *wrobj, mach_msg_type_name_t *wrobjtype)
++{
++ if (!user)
++ return EOPNOTSUPP;
++ *rdobjtype = *wrobjtype = MACH_MSG_TYPE_MOVE_SEND;
++
++ return io_map (netfs_node_netnode (user->po->np)->file, rdobj, wrobj);
++}
++
++error_t __attribute__ ((weak))
++netfs_S_io_map_cntl (struct protid *user,
++ mach_port_t *obj,
++ mach_msg_type_name_t *objtype)
++{
++ if (!user)
++ return EOPNOTSUPP;
++ *objtype = MACH_MSG_TYPE_MOVE_SEND;
++
++ return io_map_cntl (netfs_node_netnode (user->po->np)->file, obj);
++}
++
++error_t __attribute__ ((weak))
++netfs_S_io_identity (struct protid *user,
++ mach_port_t *id,
++ mach_msg_type_name_t *idtype,
++ mach_port_t *fsys,
++ mach_msg_type_name_t *fsystype,
++ ino_t *fileno)
++{
++ if (!user)
++ return EOPNOTSUPP;
++
++ *idtype = *fsystype = MACH_MSG_TYPE_MOVE_SEND;
++
++ return io_identity (netfs_node_netnode (user->po->np)->file,
++ id, fsys, fileno);
++}
++
++#define NETFS_S_SIMPLE(name) \
++error_t __attribute__ ((weak)) \
++netfs_S_##name (struct protid *user) \
++{ \
++ if (!user) \
++ return EOPNOTSUPP; \
++ \
++ return name (netfs_node_netnode (user->po->np)->file); \
++}
++
++NETFS_S_SIMPLE (io_get_conch)
++NETFS_S_SIMPLE (io_release_conch)
++NETFS_S_SIMPLE (io_eofnotify)
++NETFS_S_SIMPLE (io_readnotify)
++NETFS_S_SIMPLE (io_readsleep)
++NETFS_S_SIMPLE (io_sigio)
++
++error_t __attribute__ ((weak))
++netfs_S_io_prenotify (struct protid *user,
++ vm_offset_t start, vm_offset_t stop)
++{
++ if (!user)
++ return EOPNOTSUPP;
++
++ return io_prenotify (netfs_node_netnode (user->po->np)->file, start, stop);
++}
++
++error_t __attribute__ ((weak))
++netfs_S_io_postnotify (struct protid *user,
++ vm_offset_t start, vm_offset_t stop)
++{
++ if (!user)
++ return EOPNOTSUPP;
++
++ return io_postnotify (netfs_node_netnode (user->po->np)->file, start, stop);
++}
++
++/* This overrides the library's definition. */
++int __attribute__ ((weak))
++netfs_demuxer (mach_msg_header_t *inp,
++ mach_msg_header_t *outp)
++{
++ mig_routine_t routine;
++ if ((routine = netfs_io_server_routine (inp)) ||
++ (routine = netfs_fs_server_routine (inp)) ||
++ (routine = ports_notify_server_routine (inp)) ||
++ (routine = netfs_fsys_server_routine (inp)) ||
++ /* XXX we should intercept interrupt_operation and do
++ the ports_S_interrupt_operation work as well as
++ sending an interrupt_operation to the underlying file.
++ */
++ (routine = ports_interrupt_server_routine (inp)))
++ {
++ (*routine) (inp, outp);
++ return TRUE;
++ }
++ else
++ {
++ /* We didn't recognize the message ID, so pass the message through
++ unchanged to the underlying file. */
++ struct protid *cred;
++ if (MACH_MSGH_BITS_LOCAL (inp->msgh_bits) ==
++ MACH_MSG_TYPE_PROTECTED_PAYLOAD)
++ cred = ports_lookup_payload (netfs_port_bucket,
++ inp->msgh_protected_payload,
++ netfs_protid_class);
++ else
++ cred = ports_lookup_port (netfs_port_bucket,
++ inp->msgh_local_port,
++ netfs_protid_class);
++ if (cred == 0)
++ /* This must be an unknown message on our fsys control port. */
++ return 0;
++ else
++ {
++ error_t err;
++ assert (MACH_MSGH_BITS_LOCAL (inp->msgh_bits)
++ == MACH_MSG_TYPE_MOVE_SEND
++ || MACH_MSGH_BITS_LOCAL (inp->msgh_bits)
++ == MACH_MSG_TYPE_PROTECTED_PAYLOAD);
++ inp->msgh_bits = (inp->msgh_bits & MACH_MSGH_BITS_COMPLEX)
++ | MACH_MSGH_BITS (MACH_MSG_TYPE_COPY_SEND,
++ MACH_MSGH_BITS_REMOTE (inp->msgh_bits));
++ inp->msgh_local_port = inp->msgh_remote_port; /* reply port */
++ inp->msgh_remote_port = netfs_node_netnode (cred->po->np)->file;
++ err = mach_msg (inp, MACH_SEND_MSG, inp->msgh_size, 0,
++ MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE,
++ MACH_PORT_NULL);
++ if (err)
++ ((mig_reply_header_t *) outp)->RetCode = err;
++ else
++ /* We already sent the message, so the server loop
++ shouldn't do it again. */
++ ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
++ ports_port_deref (cred);
++ return 1;
++ }
++ }
++}
+diff --git a/trans/chroot.h b/trans/chroot.h
+new file mode 100644
+index 0000000..057a251
+--- /dev/null
++++ b/trans/chroot.h
+@@ -0,0 +1,34 @@
++#ifndef __HURD_CHROOT_H__
++#define __HURD_CHROOT_H__
++
++struct netnode
++{
++ file_t file; /* port on real file */
++};
++
++struct chroot_node;
++
++error_t chroot_init (void);
++
++/* Make a new virtual node. Always consumes the ports. If
++ successful, NP will be locked. */
++error_t chroot_new_node (file_t file, size_t size, struct node **np);
++
++/* Users must implement this. */
++void chroot_node_norefs (struct node *np);
++
++/* Return the address of the chroot_node for NODE. NODE must have been
++ allocated using chroot_new_node. */
++static inline struct chroot_node *
++chroot_node (struct node *node)
++{
++ return (struct chroot_node *) ((char *) netfs_node_netnode (node)
++ + sizeof (struct netnode));
++}
++
++// XXX
++pthread_mutex_t idport_ihash_lock;
++struct hurd_ihash idport_ihash;
++
++
++#endif /* __HURD_CHROOT_H__ */
+diff --git a/trans/identity.c b/trans/identity.c
+new file mode 100644
+index 0000000..875fc4f
+--- /dev/null
++++ b/trans/identity.c
+@@ -0,0 +1,113 @@
++/* identity -- a translator interposing file RPCs.
++ Copyright (C) 2016 Free Software Foundation, Inc.
++
++ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++#include <argp.h>
++#include <error.h>
++#include <fcntl.h>
++#include <hurd/netfs.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++
++#include <version.h>
++
++#include "chroot.h"
++
++const char *argp_program_version = STANDARD_HURD_VERSION (identity);
++
++char *netfs_server_name = "identity";
++char *netfs_server_version = HURD_VERSION;
++int netfs_maxsymlinks = 16; /* arbitrary */
++
++struct chroot_node
++{
++};
++
++void
++chroot_node_norefs (struct node *np)
++{
++}
++
++
++static struct argp_option options[] =
++{
++ {0}
++};
++
++static char *args_doc = "";
++static char *doc =
++ "The identity translator relays file RPCs without changing them."
++ "\v"
++ "It can be used in conjunction with settrans --chroot to implement "
++ "a hurdish chroot utility.";
++
++static error_t
++parse_opt (int key, char *arg, struct argp_state *state)
++{
++ switch (key)
++ {
++ default:
++ return ARGP_ERR_UNKNOWN;
++ }
++ return 0;
++}
++
++int
++main (int argc, char **argv)
++{
++ error_t err;
++ mach_port_t bootstrap;
++
++ struct argp argp =
++ {
++ .options = options,
++ .parser = parse_opt,
++ .args_doc = args_doc,
++ .doc = doc,
++ };
++
++ /* Parse our command line arguments (all none of them). */
++ argp_parse (&argp, argc, argv, 0, 0, 0);
++
++ task_get_bootstrap_port (mach_task_self (), &bootstrap);
++ if (! MACH_PORT_VALID (bootstrap))
++ error (1, 0, "Must be started as a translator");
++
++ netfs_init ();
++ chroot_init ();
++
++ /* Get our underlying node (we presume it's a directory) and use
++ that to make the root node of the filesystem. */
++ err = chroot_new_node (netfs_startup (bootstrap, O_READ),
++ sizeof (struct chroot_node), &netfs_root_node);
++ if (err)
++ error (5, err, "Cannot create root node");
++
++ err = netfs_validate_stat (netfs_root_node, 0);
++ if (err)
++ error (6, err, "Cannot stat underlying node");
++
++ netfs_root_node->nn_stat.st_mode &= ~(S_IPTRANS | S_IATRANS);
++ netfs_root_node->nn_stat.st_mode |= S_IROOT;
++ pthread_mutex_unlock (&netfs_root_node->lock);
++
++ netfs_server_loop (); /* Never returns. */
++
++ /*NOTREACHED*/
++ return 0;
++}
+--
+2.1.4
+
diff --git a/debian/patches/gpg0005-trans-add-transparent-GnuPG-translator.patch b/debian/patches/gpg0005-trans-add-transparent-GnuPG-translator.patch
new file mode 100644
index 00000000..09b1f317
--- /dev/null
+++ b/debian/patches/gpg0005-trans-add-transparent-GnuPG-translator.patch
@@ -0,0 +1,890 @@
+From f3dde907bc9bae67710eb47cb9d43748bbee278c Mon Sep 17 00:00:00 2001
+From: Justus Winter <justus@gnupg.org>
+Date: Thu, 24 Mar 2016 22:55:54 +0100
+Subject: [PATCH hurd 5/5] trans: add transparent GnuPG translator
+
+* trans/Makefile: Add new file.
+* trans/gpg.c: New file.
+* utils/Makefile: Add new file.
+* utils/gpg-env.sh: New file.
+---
+ trans/Makefile | 9 +-
+ trans/gpg.c | 682 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ utils/Makefile | 6 +-
+ utils/gpg-env.sh | 122 ++++++++++
+ 4 files changed, 812 insertions(+), 7 deletions(-)
+ create mode 100644 trans/gpg.c
+ create mode 100644 utils/gpg-env.sh
+
+diff --git a/trans/Makefile b/trans/Makefile
+index e4eba0a..422d499 100644
+--- a/trans/Makefile
++++ b/trans/Makefile
+@@ -21,10 +21,11 @@ makemode := servers
+
+ targets = symlink firmlink ifsock magic null fifo new-fifo fwd crash \
+ password hello hello-mt streamio fakeroot proxy-defpager remap \
+- mtab identity
++ mtab identity gpg
+ SRCS = ifsock.c symlink.c magic.c null.c fifo.c new-fifo.c fwd.c \
+ crash.c firmlink.c password.c hello.c hello-mt.c streamio.c \
+- fakeroot.c proxy-defpager.c remap.c mtab.c chroot.c identity.c
++ fakeroot.c proxy-defpager.c remap.c mtab.c chroot.c identity.c \
++ gpg.c
+ OBJS = $(SRCS:.c=.o) fsysServer.o ifsockServer.o passwordServer.o \
+ crashServer.o crash_replyUser.o msgServer.o \
+ default_pagerServer.o default_pagerUser.o \
+@@ -65,8 +66,8 @@ proxy-defpager: default_pagerServer.o default_pagerUser.o
+ streamio: device_replyServer.o
+ symlink: fsysServer.o
+
+-identity: chroot.o
+-fakeroot identity: ../libnetfs/libnetfs.a
++identity gpg: chroot.o
++fakeroot identity gpg: ../libnetfs/libnetfs.a
+ fifo new-fifo: ../libpipe/libpipe.a
+ crash fifo firmlink hello hello-mt ifsock magic mtab new-fifo null password proxy-defpager remap streamio: ../libtrivfs/libtrivfs.a
+ $(targets): ../libfshelp/libfshelp.a \
+diff --git a/trans/gpg.c b/trans/gpg.c
+new file mode 100644
+index 0000000..abdea7c
+--- /dev/null
++++ b/trans/gpg.c
+@@ -0,0 +1,682 @@
++/* gpg -- a translator for encrypting, decrypting, and verifying files
++ Copyright (C) 2016 Free Software Foundation, Inc.
++
++ 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
++
++#include <argz.h>
++#include <argp.h>
++#include <dirent.h>
++#include <error.h>
++#include <hurd/netfs.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <fcntl.h>
++#include <sys/mman.h>
++#include <sys/stat.h>
++#include <pthread.h>
++#include <hurd/ihash.h>
++#include <hurd/paths.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++#include <unistd.h>
++
++#include <version.h>
++
++#include "chroot.h"
++
++#include "libnetfs/fs_S.h"
++#include "libnetfs/io_S.h"
++#include "libnetfs/fsys_S.h"
++#include "libports/notify_S.h"
++#include "libports/interrupt_S.h"
++
++const char *argp_program_version = STANDARD_HURD_VERSION (gpg);
++
++char *netfs_server_name = "gpg";
++char *netfs_server_version = HURD_VERSION;
++int netfs_maxsymlinks = 16; /* arbitrary */
++
++struct chroot_node
++{
++ unsigned int encrypt:1;
++ file_t shadow_file;
++};
++
++#define GPG_DEFAULT_FILENAME "/usr/bin/gpg2"
++static char* gpg_filename = GPG_DEFAULT_FILENAME;
++static file_t gpg_file;
++
++int symmetric;
++char *recipients;
++size_t recipients_len;
++
++static error_t
++execute_gpg (mach_port_t in, mach_port_t out, mach_port_t extra,
++ char *argz, size_t argz_len, pid_t *pid)
++{
++ error_t err;
++ task_t task;
++ process_t proc;
++ mach_port_t ports[INIT_PORT_MAX];
++ mach_port_t fds[STDERR_FILENO + 2];
++ int ints[INIT_INT_MAX];
++ int i;
++
++ for (i = 0; i < INIT_PORT_MAX; i++)
++ ports[i] = MACH_PORT_NULL;
++ for (i = 0; i < STDERR_FILENO + 2; i++)
++ fds[i] = MACH_PORT_NULL;
++ memset (ints, 0, INIT_INT_MAX * sizeof(int));
++
++ ports[INIT_PORT_CWDIR] = getcwdir ();
++ ports[INIT_PORT_CRDIR] = getcrdir ();
++ ports[INIT_PORT_AUTH] = getauth ();
++ fds[STDIN_FILENO] = in;
++ fds[STDOUT_FILENO] = out;
++ fds[STDERR_FILENO] = getdport (STDERR_FILENO);
++ fds[STDERR_FILENO+1] = extra;
++
++ err = task_create (mach_task_self (), 0, &task);
++ if (err)
++ goto lose;
++
++ proc = getproc ();
++ proc_child (proc, task);
++
++ /* Try and exec GnuPG in TASK... */
++ err = file_exec (gpg_file, task, EXEC_DEFAULTS,
++ argz, argz_len, 0, 0,
++ fds, MACH_MSG_TYPE_COPY_SEND, STDERR_FILENO + 2,
++ ports, MACH_MSG_TYPE_COPY_SEND, INIT_PORT_MAX,
++ ints, INIT_INT_MAX, 0, 0, 0, 0);
++ if (err)
++ goto lose;
++
++ err = proc_task2pid (proc, task, pid);
++
++ lose:
++ for (i = 0; i < INIT_PORT_MAX; i++)
++ mach_port_deallocate (mach_task_self (), ports[i]);
++ mach_port_deallocate (mach_task_self (), fds[STDERR_FILENO]);
++ return err;
++}
++
++error_t
++clone_file (file_t file, file_t *new_file)
++{
++ error_t err;
++ off_t newp;
++ retry_type do_retry;
++ string_t retry_name;
++ err = dir_lookup (file, "", O_RDONLY, 0, &do_retry, retry_name, new_file);
++ if (err)
++ return err;
++ return io_seek (*new_file, 0, SEEK_SET, &newp);
++}
++
++static int
++gpg_verify (file_t sigfile, file_t orig_file)
++{
++ error_t err;
++ file_t file;
++ pid_t pid;
++ char *argz = NULL;
++ size_t argz_len = 0;
++ err = clone_file (orig_file, &file);
++ if (err)
++ return 0;
++
++ argz_add (&argz, &argz_len, "gpg2");
++ argz_add (&argz, &argz_len, "--verify");
++ argz_add (&argz, &argz_len, "/dev/fd/3");
++ argz_add (&argz, &argz_len, "-");
++ err = execute_gpg (file, getdport (STDERR_FILENO), sigfile, argz, argz_len, &pid);
++ mach_port_deallocate (mach_task_self (), file);
++ if (err)
++ {
++ error (0, err, "execute_gpg");
++ return 0;
++ }
++
++ int status;
++ while (waitpid (pid, &status, 0) == (pid_t)-1 && errno == EINTR)
++ { }
++
++ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
++}
++
++static int
++gpg_decrypt (file_t cipherfile, file_t plainfile)
++{
++ error_t err;
++ pid_t pid;
++ char *argz = NULL;
++ size_t argz_len = 0;
++
++ argz_add (&argz, &argz_len, "gpg2");
++ argz_add (&argz, &argz_len, "--output");
++ argz_add (&argz, &argz_len, "-");
++ err = execute_gpg (cipherfile, plainfile, MACH_PORT_NULL,
++ argz, argz_len, &pid);
++ if (err)
++ {
++ error (0, err, "execute_gpg");
++ return 0;
++ }
++
++ int status;
++ while (waitpid (pid, &status, 0) == (pid_t)-1 && errno == EINTR)
++ { }
++
++ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
++}
++
++static int
++gpg_encrypt (file_t plainfile, file_t cipherfile)
++{
++ error_t err;
++ pid_t pid;
++ char *argz = NULL;
++ size_t argz_len = 0;
++
++ argz_add (&argz, &argz_len, "gpg2");
++ argz_add (&argz, &argz_len, "--output");
++ argz_add (&argz, &argz_len, "-");
++ if (recipients)
++ {
++ char *recipient;
++ argz_add (&argz, &argz_len, "--encrypt");
++ for (recipient = recipients;
++ recipient;
++ recipient = argz_next (recipients, recipients_len, recipient))
++ {
++ argz_add (&argz, &argz_len, "--recipient");
++ argz_add (&argz, &argz_len, recipient);
++ }
++ }
++ if (symmetric)
++ argz_add (&argz, &argz_len, "--symmetric");
++
++ err = execute_gpg (plainfile, cipherfile, MACH_PORT_NULL,
++ argz, argz_len, &pid);
++ if (err)
++ {
++ error (0, err, "execute_gpg");
++ return 0;
++ }
++
++ int status;
++ while (waitpid (pid, &status, 0) == (pid_t)-1 && errno == EINTR)
++ { }
++
++ return WIFEXITED (status) && WEXITSTATUS (status) == 0;
++}
++
++static error_t
++make_tmp_file (file_t *file)
++{
++ char name[] = "/tmp/gpgtmp-XXXXXX";
++ int fd;
++
++ fd = mkstemp (name);
++ if (fd == -1)
++ return errno;
++ if (unlink (name) == -1)
++ return errno;
++ *file = getdport (fd);
++ close (fd);
++ return 0;
++}
++
++error_t
++netfs_S_dir_lookup (struct protid *diruser,
++ char *filename,
++ int flags,
++ mode_t mode,
++ retry_type *do_retry,
++ char *retry_name,
++ mach_port_t *retry_port,
++ mach_msg_type_name_t *retry_port_type)
++{
++ struct node *dnp, *np;
++ error_t err;
++ struct protid *newpi;
++ struct iouser *user;
++ file_t file;
++ file_t dir;
++
++ if (! diruser)
++ return EOPNOTSUPP;
++
++ dnp = diruser->po->np;
++
++ dir = netfs_node_netnode (dnp)->file;
++ err = dir_lookup (dir, filename,
++ flags & (O_NOLINK|O_RDWR|O_EXEC|O_CREAT|O_EXCL|O_NONBLOCK),
++ mode, do_retry, retry_name, &file);
++ if (err == ENOENT)
++ {
++ file_t gpgfile;
++ char *gpgname;
++ asprintf (&gpgname, "%s.gpg", filename);
++ gpgfile = file_name_lookup_under (dir, gpgname, O_RDONLY, 0);
++ if (MACH_PORT_VALID (gpgfile))
++ {
++ file_t tmpfile;
++ int ok;
++ off_t newp;
++
++ err = make_tmp_file (&tmpfile);
++ if (err)
++ return err;
++
++ ok = gpg_decrypt (gpgfile, tmpfile);
++ mach_port_deallocate (mach_task_self (), gpgfile);
++ if (! ok)
++ {
++ mach_port_deallocate (mach_task_self (), tmpfile);
++ return EIO;
++ }
++
++ err = io_seek (tmpfile, 0, SEEK_SET, &newp);
++ if (err)
++ {
++ mach_port_deallocate (mach_task_self (), tmpfile);
++ return err;
++ }
++
++ *do_retry = FS_RETRY_NORMAL;
++ retry_name[0] = 0;
++ *retry_port = tmpfile;
++ *retry_port_type = MACH_MSG_TYPE_MOVE_SEND;
++ return 0;
++ }
++ free (gpgname);
++ }
++
++ if (err)
++ return err;
++
++ switch (*do_retry)
++ {
++ case FS_RETRY_NORMAL:
++ break;
++
++ case FS_RETRY_REAUTH:
++ error (0, 0, "FS_RETRY_REAUTH");
++ /* Fallthrough. */
++
++ case FS_RETRY_MAGICAL:
++ *retry_port = file;
++ *retry_port_type = MACH_MSG_TYPE_MOVE_SEND;
++ return 0;
++
++ default:
++ /* Invalid response to our dir_lookup request. */
++ if (MACH_PORT_VALID (file))
++ mach_port_deallocate (mach_task_self (), file);
++ *retry_port = MACH_PORT_NULL;
++ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
++ return EOPNOTSUPP;
++ }
++
++ /* We have a new port to an underlying node.
++ Find or make our corresponding virtual node. */
++
++ np = 0;
++
++ pthread_mutex_lock (&dnp->lock);
++ err = chroot_new_node (file, sizeof (struct chroot_node), &np);
++ pthread_mutex_unlock (&dnp->lock);
++ if (!err)
++ {
++ chroot_node (np)->encrypt = chroot_node (dnp)->encrypt;
++ chroot_node (np)->shadow_file = MACH_PORT_NULL;
++ err = netfs_validate_stat (np, diruser->user);
++ }
++ if (err)
++ goto lose;
++
++ if (chroot_node (np)->encrypt && flags & O_WRITE
++ && strncmp (filename, "dev/", 4) != 0)
++ {
++ char *ciphname;
++ asprintf (&ciphname, "%s.gpg", filename);
++ if (ciphname == NULL)
++ {
++ err = errno;
++ goto lose;
++ }
++
++ /* Fixup name. */
++ err = dir_link (dir, file, ciphname, 0);
++ free (ciphname);
++ if (err)
++ return err;
++
++ err = dir_unlink (dir, filename);
++ if (err)
++ return err;
++
++ err = make_tmp_file (&chroot_node (np)->shadow_file);
++ if (err)
++ return err;
++ }
++
++ if (retry_name[0] == 0)
++ {
++ file_t sig;
++ char *signame;
++
++ asprintf (&signame, "%s.sig", filename);
++ if (signame == NULL)
++ {
++ err = errno;
++ goto lose;
++ }
++
++ sig = file_name_lookup_under (dir, signame, O_RDONLY, 0);
++ if (MACH_PORT_VALID (sig))
++ {
++ if (! gpg_verify (sig, file))
++ err = EIO;
++ free (signame);
++ signame = NULL;
++ mach_port_deallocate (mach_task_self (), sig);
++ if (err)
++ goto lose;
++ }
++ free (signame);
++ }
++
++ assert (*do_retry == FS_RETRY_NORMAL);
++ flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK);
++
++ err = iohelp_dup_iouser (&user, diruser->user);
++ if (!err)
++ {
++ newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po),
++ user);
++ if (! newpi)
++ {
++ iohelp_free_iouser (user);
++ err = errno;
++ }
++ else
++ {
++ *retry_port = ports_get_right (newpi);
++ *retry_port_type = MACH_MSG_TYPE_MAKE_SEND;
++ ports_port_deref (newpi);
++ }
++ }
++
++ lose:
++ if (dir != netfs_node_netnode (dnp)->file)
++ mach_port_deallocate (mach_task_self (), dir);
++ if (np != NULL)
++ netfs_nput (np);
++ return err;
++}
++
++error_t
++netfs_attempt_mkfile (struct iouser *user, struct node *dir,
++ mode_t mode, struct node **np)
++{
++ file_t newfile;
++ error_t err = dir_mkfile (netfs_node_netnode (dir)->file, O_RDWR|O_EXEC,
++ mode, &newfile);
++ pthread_mutex_unlock (&dir->lock);
++ if (err)
++ return err;
++
++ err = chroot_new_node (newfile, sizeof (struct chroot_node), np);
++ if (err)
++ return err;
++
++ if (chroot_node (dir)->encrypt)
++ {
++ err = make_tmp_file (&chroot_node (*np)->shadow_file);
++ if (err)
++ return err;
++ }
++ else
++ chroot_node (*np)->shadow_file = MACH_PORT_NULL;
++
++ pthread_mutex_unlock (&(*np)->lock);
++ return err;
++}
++
++error_t
++netfs_attempt_read (struct iouser *cred, struct node *np,
++ off_t offset, size_t *len, void *data)
++{
++ struct chroot_node *n = chroot_node (np);
++ char *buf = data;
++ error_t err = io_read (n->shadow_file ?: netfs_node_netnode (np)->file,
++ &buf, len, offset, *len);
++ if (err == 0 && buf != data)
++ {
++ memcpy (data, buf, *len);
++ munmap (buf, *len);
++ }
++ return err;
++}
++
++error_t
++netfs_attempt_write (struct iouser *cred, struct node *np,
++ off_t offset, size_t *len, void *data)
++{
++ struct chroot_node *n = chroot_node (np);
++ return io_write (n->shadow_file ?: netfs_node_netnode (np)->file,
++ data, *len, offset, len);
++}
++
++error_t
++netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
++{
++ error_t err;
++ file_t gpg_file = netfs_node_netnode (np)->file;
++ struct chroot_node *n = chroot_node (np);
++
++ err = file_sync (n->shadow_file ?: gpg_file, wait, 0);
++ if (err)
++ return err;
++
++ if (wait && MACH_PORT_VALID (n->shadow_file))
++ {
++ file_t shadow_file;
++ off_t newp;
++
++ err = clone_file (n->shadow_file, &shadow_file);
++ if (err)
++ return err;
++
++ err = io_seek (gpg_file, 0, SEEK_SET, &newp);
++ if (err)
++ return err;
++
++ err = gpg_encrypt (shadow_file, gpg_file);
++ if (err)
++ return err;
++
++ mach_port_deallocate (mach_task_self (), shadow_file);
++ err = file_sync (gpg_file, wait, 0);
++ }
++
++ return err;
++}
++
++
++error_t
++netfs_attempt_link (struct iouser *user, struct node *dir,
++ struct node *file, char *name, int excl)
++{
++ /* XXX translate name if file is encrypted. */
++ return dir_link (netfs_node_netnode (dir)->file,
++ netfs_node_netnode (file)->file, name, excl);
++}
++
++void
++chroot_node_norefs (struct node *np)
++{
++ error_t err;
++ file_t gpg_file = netfs_node_netnode (np)->file;
++ struct chroot_node *n = chroot_node (np);
++ if (n->encrypt)
++ {
++ off_t newp;
++
++ err = io_seek (n->shadow_file, 0, SEEK_SET, &newp);
++ if (err)
++ return;
++
++ err = io_seek (gpg_file, 0, SEEK_SET, &newp);
++ if (err)
++ return;
++
++ err = gpg_encrypt (n->shadow_file, gpg_file);
++ if (err)
++ return;
++
++ mach_port_deallocate (mach_task_self (), n->shadow_file);
++ }
++}
++
++
++error_t
++netfs_get_dirents (struct iouser *cred, struct node *dir,
++ int entry, int nentries, char **data,
++ mach_msg_type_number_t *datacnt,
++ vm_size_t bufsize, int *amt)
++{
++ error_t err;
++ char *ptr;
++ struct dirent *dent;
++ err = dir_readdir (netfs_node_netnode (dir)->file, data, datacnt,
++ entry, nentries, bufsize, amt);
++ if (err)
++ return err;
++
++ for (ptr = *data, dent = (struct dirent *) ptr;
++ ptr - *data < *datacnt;
++ ptr += dent->d_reclen, dent = (struct dirent *) ptr)
++ {
++ size_t len = strlen (dent->d_name);
++ if (len > 4 && strcmp (&dent->d_name[len - 4], ".gpg") == 0)
++ memset (&dent->d_name[len - 4], 0, 4);
++ }
++
++ return 0;
++}
++
++#define OPT_GPG_PROGRAM -1
++
++static struct argp_option options[] =
++{
++ {NULL, 0, NULL, 0, "How to encrypt:", 1},
++ {"recipient", 'r', "NAME", 0, "Encrypt for user NAME. "
++ "Can be given multiple times.", 1},
++ {"symmetric", 'c', NULL, 0, "Encrypt with a password.", 1},
++ {NULL, 0, NULL, 0, "Advanced options:", 2},
++ {"gpg-program", OPT_GPG_PROGRAM, "FILENAME", 0,
++ "Path to the GNU Privacy Guard executable ["GPG_DEFAULT_FILENAME"].", 2},
++ {0}
++};
++
++static char *args_doc = "";
++static char *doc = "Transparently encrypt, decrypt, and verify signatures"
++ " using GnuPG."
++ "\vFor everyday use, the commands 'encrypt', 'decrypt', and 'verify'"
++ " offer a simple and convenient frontend for this translator.";
++
++static error_t
++parse_opt (int key, char *arg, struct argp_state *state)
++{
++ error_t err;
++ switch (key)
++ {
++ case 'r':
++ err = argz_add (&recipients, &recipients_len, arg);
++ if (err)
++ error (1, err, "argz_add");
++ break;
++
++ case 'c':
++ symmetric = 1;
++ break;
++
++ case OPT_GPG_PROGRAM:
++ gpg_filename = arg;
++ break;
++
++ default:
++ return ARGP_ERR_UNKNOWN;
++ }
++ return 0;
++}
++
++int
++main (int argc, char **argv)
++{
++ error_t err;
++ mach_port_t bootstrap;
++
++ struct argp argp =
++ {
++ .options = options,
++ .parser = parse_opt,
++ .args_doc = args_doc,
++ .doc = doc,
++ };
++
++ /* Parse our command line arguments (all none of them). */
++ argp_parse (&argp, argc, argv, 0, 0, 0);
++
++ task_get_bootstrap_port (mach_task_self (), &bootstrap);
++ if (! MACH_PORT_VALID (bootstrap))
++ error (1, 0, "Must be started as a translator");
++
++ gpg_file = file_name_lookup (gpg_filename, O_EXEC, 0);
++ if (! MACH_PORT_VALID (gpg_file))
++ error (1, errno, "%s", gpg_filename);
++
++ netfs_init ();
++ chroot_init ();
++
++ /* Get our underlying node (we presume it's a directory) and use
++ that to make the root node of the filesystem. */
++ err = chroot_new_node (netfs_startup (bootstrap, O_READ),
++ sizeof (struct chroot_node), &netfs_root_node);
++ if (err)
++ error (5, err, "Cannot create root node");
++
++ err = netfs_validate_stat (netfs_root_node, 0);
++ if (err)
++ error (6, err, "Cannot stat underlying node");
++
++ netfs_root_node->nn_stat.st_mode &= ~(S_IPTRANS | S_IATRANS);
++ netfs_root_node->nn_stat.st_mode |= S_IROOT;
++ pthread_mutex_unlock (&netfs_root_node->lock);
++
++ chroot_node (netfs_root_node)->encrypt = 1;
++ chroot_node (netfs_root_node)->shadow_file = MACH_PORT_NULL;
++
++ netfs_server_loop (); /* Never returns. */
++
++ /*NOTREACHED*/
++ return 0;
++}
+diff --git a/utils/Makefile b/utils/Makefile
+index d2ef9e8..aa6ff34 100644
+--- a/utils/Makefile
++++ b/utils/Makefile
+@@ -22,16 +22,16 @@ targets = shd ps settrans showtrans syncfs fsysopts \
+ storeinfo login w uptime ids loginpr sush vmstat portinfo \
+ devprobe vminfo addauth rmauth unsu setauth ftpcp ftpdir storecat \
+ storeread msgport rpctrace mount gcore fakeauth fakeroot remap \
+- umount nullauth rpcscan vmallocate
++ umount nullauth rpcscan vmallocate gpg-env
+
+-special-targets = loginpr sush uptime fakeroot remap
++special-targets = loginpr sush uptime fakeroot remap gpg-env
+ SRCS = shd.c ps.c settrans.c syncfs.c showtrans.c addauth.c rmauth.c \
+ fsysopts.c storeinfo.c login.c loginpr.sh sush.sh w.c \
+ uptime.sh psout.c ids.c vmstat.c portinfo.c devprobe.c vminfo.c \
+ parse.c frobauth.c frobauth-mod.c setauth.c pids.c nonsugid.c \
+ unsu.c ftpcp.c ftpdir.c storeread.c storecat.c msgport.c \
+ rpctrace.c mount.c gcore.c fakeauth.c fakeroot.sh remap.sh \
+- nullauth.c match-options.c msgids.c rpcscan.c
++ nullauth.c match-options.c msgids.c rpcscan.c gpg-env.sh
+
+ OBJS = $(filter-out %.sh,$(SRCS:.c=.o))
+ HURDLIBS = ps ihash store fshelp ports ftpconn shouldbeinlibc
+diff --git a/utils/gpg-env.sh b/utils/gpg-env.sh
+new file mode 100644
+index 0000000..cd3c9d5
+--- /dev/null
++++ b/utils/gpg-env.sh
+@@ -0,0 +1,122 @@
++#!/bin/sh
++# Execute a command in an environment which encrypts, decrypts, and
++# verifies files on demand.
++#
++# Copyright (C) 2016 Free Software Foundation, Inc.
++#
++# This file is part of the GNU Hurd.
++#
++# The GNU Hurd 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.
++#
++# The GNU Hurd 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.
++#
++
++USAGE="Usage:
++ [gpg-env] encrypt for RECIPIENT [RECIPIENT...] -- [OPTION...] [COMMAND...]
++ [gpg-env] encrypt with password [OPTION...] [COMMAND...]
++ [gpg-env] decrypt [OPTION...] [COMMAND...]
++ [gpg-env] decrypt with password [OPTION...] [COMMAND...]
++ [gpg-env] verify [OPTION...] [COMMAND...]"
++DOC="Execute COMMAND in an environment where files are automatically
++encrypted, decrypted and verified."
++
++help()
++{
++ [ "$1" ] && echo "$1
++"
++ echo "$USAGE"
++ echo ""
++ echo "$DOC"
++ echo ""
++ echo " -?, --help Give this help list"
++ echo " --usage Give a short usage message"
++ echo " -V, --version Print program version"
++ [ "$1" ] && exit 1 || exit 0
++}
++
++if [ "$(basename $0)" = "gpg-env.sh" ] \
++ || [ "$(basename $0)" = "gpg-env" ]; then
++ ACTION="$1"
++ if [ ! "$ACTION" ]; then
++ help "No action given."
++ fi
++ shift
++else
++ ACTION="$(basename $0)"
++fi
++
++case "$ACTION" in
++ "encrypt") ;;
++ "decrypt") ;;
++ "verify") ;;
++ *)
++ help "Invalid action '$ACTION'."
++esac
++
++ENCRYPT=""
++if [ "$ACTION" = "encrypt" ]; then
++ if [ "$1" = "with" ] && [ "$2" = "password" ]; then
++ ENCRYPT="--symmetric"
++ shift 2
++ elif [ "$1" = "for" ]; then
++ shift
++ while [ "$#" -gt 0 ] && [ "x$1" != "x--" ]; do
++ ENCRYPT="$ENCRYPT --recipient $1"
++ shift
++ done
++ if [ "$ENCRYPT" = "" ]; then
++ echo "No recipients given."
++ exit 1
++ fi
++ if [ "x$1" = "x--" ]; then
++ shift
++ elif [ "$#" -eq 0 ]; then
++ # it's ok if there are no more arguments
++ :
++ else
++ echo "Recipient list must be terminated using '--'."
++ exit 1
++ fi
++ fi
++fi
++
++while [ "$#" -gt 0 ]; do
++ case "$1" in
++ --help|"-?")
++ help
++ ;;
++ --usage)
++ echo "$USAGE"
++ echo "Options: [-V?] [--help] [--usage] [--version]"
++ exit 0;;
++ --version|-V)
++ echo "STANDARD_HURD_VERSION_gpg-env_"; exit 0;;
++ --)
++ shift
++ break
++ ;;
++ *)
++ break
++ esac
++done
++
++if [ $# -eq 0 ]; then
++ set -- ${SHELL:-/bin/sh}
++fi
++
++# We exec settrans, which execs the target command in the chroot
++# context provided by /hurd/gpg.
++exec /bin/settrans \
++ --chroot-chdir "$PWD" \
++ --chroot "$@" -- \
++ / /hurd/gpg $ENCRYPT
+--
+2.1.4
+
diff --git a/debian/patches/series b/debian/patches/series
index 4e643312..c178ae4b 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -36,3 +36,8 @@ exec_filename0002-Add-a-file_exec_file_name-RPC.patch
exec_filename0003-Use-the-new-_hurd_exec_file_name-function.patch
exec_filename0004-This-patch-is-an-amendment-of-exec_filename_exec.pat.patch
crash0001-xxx-crash-logging-works.patch
+gpg0001-isofs-make-superblock-detection-more-robust.patch
+gpg0002-utils-settrans-get-an-authenticated-root-node-in-chr.patch
+gpg0003-utils-settrans-add-option-to-specify-the-underlying-.patch
+gpg0004-trans-add-identity-translator.patch
+gpg0005-trans-add-transparent-GnuPG-translator.patch