summaryrefslogtreecommitdiff
path: root/trans
diff options
context:
space:
mode:
Diffstat (limited to 'trans')
-rw-r--r--trans/Makefile67
-rw-r--r--trans/bogus-fifo.c160
-rw-r--r--trans/crash.c800
-rw-r--r--trans/fakeroot.c1041
-rw-r--r--trans/fifo.c628
-rw-r--r--trans/firmlink.c288
-rw-r--r--trans/fwd.c51
-rw-r--r--trans/hello-mt.c332
-rw-r--r--trans/hello.c293
-rw-r--r--trans/ifsock.c152
-rw-r--r--trans/magic.c567
-rw-r--r--trans/mtab.c882
-rw-r--r--trans/new-fifo.c857
-rw-r--r--trans/null.c340
-rw-r--r--trans/password.c230
-rw-r--r--trans/proxy-defpager.c279
-rw-r--r--trans/remap.c152
-rw-r--r--trans/streamio.c1189
-rw-r--r--trans/symlink.c236
19 files changed, 8544 insertions, 0 deletions
diff --git a/trans/Makefile b/trans/Makefile
new file mode 100644
index 00000000..71e64248
--- /dev/null
+++ b/trans/Makefile
@@ -0,0 +1,67 @@
+#
+# Copyright (C) 1994, 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2006, 2007,
+# 2008, 2013, 2014 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := trans
+makemode := servers
+
+targets = symlink firmlink ifsock magic null fifo new-fifo fwd crash \
+ password hello hello-mt streamio fakeroot proxy-defpager remap \
+ mtab
+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
+OBJS = $(SRCS:.c=.o) fsysServer.o ifsockServer.o passwordServer.o \
+ crashServer.o crash_replyUser.o msgServer.o \
+ default_pagerServer.o default_pagerUser.o \
+ device_replyServer.o elfcore.o
+HURDLIBS = ports netfs trivfs iohelp fshelp pipe ihash shouldbeinlibc
+LDLIBS += -lpthread
+password-LDLIBS = -lcrypt
+password-MIGSFLAGS=\
+ "-DIO_INTRAN=trivfs_protid_t trivfs_begin_using_protid (io_t)" \
+ "-DIO_DESTRUCTOR=trivfs_end_using_protid (trivfs_protid_t)" \
+ "-DPASSWORD_IMPORTS=import \"../libtrivfs/mig-decls.h\";"
+
+ifsock-MIGSFLAGS=\
+ "-DFILE_INTRAN=trivfs_protid_t trivfs_begin_using_protid (io_t)" \
+ "-DFILE_DESTRUCTOR=trivfs_end_using_protid (trivfs_protid_t)" \
+ "-DIFSOCK_IMPORTS=import \"../libtrivfs/mig-decls.h\";"
+
+include ../Makeconf
+
+vpath elfcore.c $(top_srcdir)/exec
+
+symlink: fsysServer.o
+ifsock: ifsockServer.o
+crash: crashServer.o crash_replyUser.o msgServer.o elfcore.o
+password: passwordServer.o
+streamio: device_replyServer.o
+proxy-defpager: default_pagerServer.o default_pagerUser.o
+
+proxy-defpager crash password streamio: ../libports/libports.a ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a
+fifo new-fifo: ../libpipe/libpipe.a
+fwd: ../libfshelp/libfshelp.a ../libports/libports.a
+hello-mt magic null ifsock fifo new-fifo firmlink: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a
+magic: ../libiohelp/libiohelp.a
+hello: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a
+fakeroot: ../libnetfs/libnetfs.a ../libfshelp/libfshelp.a ../libiohelp/libiohelp.a ../libports/libports.a ../libihash/libihash.a
+remap: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a
+mtab: ../libtrivfs/libtrivfs.a ../libfshelp/libfshelp.a ../libports/libports.a ../libihash/libihash.a fsUser.o
+$(targets): ../libshouldbeinlibc/libshouldbeinlibc.a
+
+$(targets): %: %.o
diff --git a/trans/bogus-fifo.c b/trans/bogus-fifo.c
new file mode 100644
index 00000000..acad6e4b
--- /dev/null
+++ b/trans/bogus-fifo.c
@@ -0,0 +1,160 @@
+/* A translator for fifos
+
+ Copyright (C) 1995 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ 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 <hurd.h>
+#include <stdio.h>
+#include <error.h>
+#include <sys/socket.h>
+#include <hurd/paths.h>
+#include <hurd/socket.h>
+#include <hurd/fsys.h>
+#include "fsys_S.h"
+
+/* ---------------------------------------------------------------- */
+
+extern int fsys_server (mach_msg_header_t *, mach_msg_header_t *);
+
+/* The actual fifo, which is just a socket connected to itself. */
+static socket_t fifo;
+
+void
+main (int argc, char **argv)
+{
+ error_t err;
+ char pflocal_name[1024];
+ mach_port_t pflocal;
+ mach_port_t bootstrap, fsys, realnode;
+
+ if (argc != 1)
+ {
+ fprintf(stderr, "Usage: %s", program_invocation_name);
+ exit(-1);
+ }
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(3, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &fsys);
+ err = fsys_startup (bootstrap, fsys, MACH_MSG_TYPE_MAKE_SEND, &realnode);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error(1, err, "starting translator");
+
+ /* Try and connect to the pflocal server */
+ sprintf (pflocal_name, "%s/%d", _SERVERS_SOCKET, PF_LOCAL);
+ pflocal = file_name_lookup (pflocal_name, 0, 0);
+ if (pflocal == MACH_PORT_NULL)
+ error (2, errno, "%s", pflocal_name);
+
+ /* Make a local domain socket to use for our data buffering. */
+ err = socket_create (pflocal, SOCK_STREAM, 0, &fifo);
+ if (err)
+ error (3, err, "%s: socket_create", pflocal_name);
+
+ /* Connect the socket to itself, yielding a fifo! */
+ err = socket_connect2 (fifo, fifo);
+ if (err)
+ error (3, err, "%s: socket_connect2", pflocal_name);
+
+ for (;;)
+ /* We don't ever time out. The problem is, you only want to exit when
+ (1) the pipe is empty (which we can check), and (2) there are no other
+ users (which we can't). If we just drop our ref to the pipe, there
+ still could be a writer holding a ref to it. */
+ mach_msg_server_timeout (fsys_server, 0, fsys, 0, 0);
+}
+
+/* ---------------------------------------------------------------- */
+
+error_t
+S_fsys_getroot (mach_port_t fsys, mach_port_t parent,
+ uid_t *uids, unsigned num_uids, gid_t *gids, unsigned num_gids,
+ int flags,
+ retry_type *do_retry, char *retry_name,
+ mach_port_t *result, mach_msg_type_name_t *result_type)
+{
+ /* Give back a handle on our fifo. */
+ *do_retry = FS_RETRY_NORMAL;
+ *retry_name = '\0';
+ *result = fifo;
+ *result_type = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+}
+
+error_t
+S_fsys_startup (mach_port_t bootstrap, mach_port_t fsys,
+ mach_port_t *real, mach_msg_type_name_t *real_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_goaway (mach_port_t fsys, int flags)
+{
+ /* If there are refs to the fifo left besides ours, it will
+ stay around after we're gone. */
+ exit (0);
+}
+
+error_t
+S_fsys_syncfs (mach_port_t fsys, int wait, int recurse)
+{
+ return 0;
+}
+
+error_t
+S_fsys_set_options (mach_port_t fsys,
+ char *data, mach_msg_type_number_t data_len, int recurse)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_getfile (mach_port_t fsys,
+ uid_t *uids, unsigned num_uids, gid_t *gids, unsigned num_gids,
+ char *handle, unsigned handle_len,
+ mach_port_t *port, mach_msg_type_name_t *port_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_getpriv (mach_port_t fsys,
+ mach_port_t *hostpriv, mach_port_t *devmaster, task_t *fstask)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_init (mach_port_t fsys,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_port_t proc, auth_t auth)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_forward (mach_port_t server, mach_port_t requestor,
+ char *argz, size_t argz_len)
+{
+ return EOPNOTSUPP;
+}
diff --git a/trans/crash.c b/trans/crash.c
new file mode 100644
index 00000000..4a59d450
--- /dev/null
+++ b/trans/crash.c
@@ -0,0 +1,800 @@
+/* GNU Hurd standard crash dump server.
+
+ Copyright (C) 1995, 1996, 1997, 1999, 2000, 2001, 2002, 2006, 2007
+ Free Software Foundation, Inc.
+
+ Written by Roland McGrath.
+
+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 the GNU Hurd; see the file COPYING. If not, write to
+the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <fcntl.h>
+#include <hurd/trivfs.h>
+#include <sys/wait.h>
+#include <error.h>
+#include <argp.h>
+#include <argz.h>
+#include <sys/mman.h>
+
+#include <version.h>
+
+#include "crash_S.h"
+#include "crash_reply_U.h"
+#include "msg_S.h"
+
+
+const char *argp_program_version = STANDARD_HURD_VERSION (crash);
+
+process_t procserver; /* Our proc port, for easy access. */
+
+/* Port bucket we service requests on. */
+struct port_bucket *port_bucket;
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = O_READ|O_WRITE|O_EXEC;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+struct trivfs_control *fsys;
+
+enum crash_action
+{
+ crash_unspecified,
+ crash_suspend,
+ crash_kill,
+ crash_corefile
+};
+#define CRASH_DEFAULT crash_suspend
+#define CRASH_ORPHANS_DEFAULT crash_corefile
+
+static enum crash_action crash_how, crash_orphans_how;
+
+
+/* This is defined in ../exec/elfcore.c, or we could have
+ different implementations for other formats. */
+extern error_t dump_core (task_t task, file_t file, off_t corelimit,
+ int signo, long int sigcode, int sigerror);
+
+/* This data structure describes a crashing task which we have suspended.
+ This is attached to a receive right we have set as the process's message
+ port, so we can get a msg_sig_post RPC to continue or kill the process. */
+
+struct crasher
+ {
+ struct port_info pi;
+
+ /* How to reply to the crash_dump_task RPC. The RPC remains "in
+ progress" while the process is in crash suspension. If the process
+ is resumed with SIGCONT, we will dump a core file and then reply to
+ the RPC. */
+ mach_port_t reply_port;
+ mach_msg_type_name_t reply_type;
+
+ task_t task;
+ file_t core_file;
+ off_t core_limit;
+ int signo, sigcode, sigerror;
+
+ mach_port_t original_msgport; /* Restore on resume. */
+ mach_port_t sidport; /* Session ID port for SIGCONT auth. */
+ process_t proc; /* Proc server port. */
+ };
+
+struct port_class *crasher_portclass;
+
+/* If the process referred to by proc port USERPROC is not orphaned,
+ then send SIGTSTP to all the other members of its pgrp. */
+void
+stop_pgrp (process_t userproc, mach_port_t cttyid)
+{
+ pid_t pid, ppid, pgrp;
+ int orphaned;
+ error_t err;
+ size_t numpids = 20;
+ pid_t pids_[numpids], *pids = pids_;
+ int i;
+
+ err = proc_getpids (userproc, &pid, &ppid, &orphaned);
+ if (err || orphaned)
+ return;
+ err = proc_getpgrp (userproc, pid, &pgrp);
+ if (err)
+ return;
+
+ /* Use USERPROC so that if it's just died we get an error and don't do
+ anything. */
+ err = proc_getpgrppids (userproc, pgrp, &pids, &numpids);
+ if (err)
+ return;
+
+ for (i = 0; i < numpids; i++)
+ if (pids[i] != pid)
+ {
+ mach_port_t msgport;
+ if (proc_getmsgport (userproc, pids[i], &msgport))
+ continue;
+ msg_sig_post (msgport, SIGTSTP, 0, cttyid);
+ mach_port_deallocate (mach_task_self (), msgport);
+ }
+ if (pids != pids_)
+ munmap (pids, numpids);
+}
+
+
+kern_return_t
+S_crash_dump_task (mach_port_t port,
+ mach_port_t reply_port, mach_msg_type_name_t reply_type,
+ task_t task, file_t core_file,
+ int signo, integer_t sigcode, int sigerror,
+ natural_t exc, natural_t code, natural_t subcode,
+ mach_port_t ctty_id)
+{
+ error_t err;
+ struct trivfs_protid *cred;
+ mach_port_t user_proc = MACH_PORT_NULL;
+ enum crash_action how;
+
+ cred = ports_lookup_port (port_bucket, port, trivfs_protid_portclasses[0]);
+ if (! cred)
+ return EOPNOTSUPP;
+
+ how = crash_how;
+ if (crash_how != crash_orphans_how)
+ {
+ /* We must ascertain if this is an orphan before deciding what to do. */
+ err = proc_task2proc (procserver, task, &user_proc);
+ if (!err)
+ {
+ pid_t pid, ppid;
+ int orphan;
+ err = proc_getpids (user_proc, &pid, &ppid, &orphan);
+ if (!err && orphan)
+ how = crash_orphans_how;
+ }
+ }
+
+ switch (how)
+ {
+ default: /* NOTREACHED */
+ err = EGRATUITOUS;
+ break;
+
+ case crash_suspend:
+ /* Suspend the task first thing before being twiddling it. */
+ err = task_suspend (task);
+ if (err)
+ break;
+
+ if (user_proc != MACH_PORT_NULL)
+ err = proc_task2proc (procserver, task, &user_proc);
+ if (! err)
+ {
+ struct crasher *c;
+
+ err = ports_create_port (crasher_portclass, port_bucket,
+ sizeof *c, &c);
+ if (! err)
+ {
+ mach_port_t msgport;
+
+ stop_pgrp (user_proc, ctty_id);
+
+ /* Install our port as the crasher's msgport.
+ We will wait for signals to resume (crash) it. */
+ msgport = ports_get_send_right (c);
+ err = proc_setmsgport (user_proc, msgport, &c->original_msgport);
+ mach_port_deallocate (mach_task_self (), msgport);
+
+ c->reply_port = reply_port;
+ c->reply_type = reply_type;
+ if (proc_getsidport (user_proc, &c->sidport))
+ c->sidport = MACH_PORT_NULL;
+ c->proc = user_proc;
+
+ /* Tell the proc server the crasher stopped. */
+ proc_mark_stop (user_proc, signo, sigcode);
+
+ c->task = task;
+ c->core_file = core_file;
+ c->core_limit = (off_t) -1; /* XXX should core limit in RPC */
+ c->signo = signo;
+ c->sigcode = sigcode;
+ c->sigerror = sigerror;
+
+ err = MIG_NO_REPLY;
+ ports_port_deref (c);
+ }
+ }
+ if (err != MIG_NO_REPLY)
+ task_resume (task);
+ break;
+
+ case crash_corefile:
+ err = task_suspend (task);
+ if (!err)
+ {
+ err = dump_core (task, core_file,
+ (off_t) -1, /* XXX should get core limit in RPC */
+ signo, sigcode, sigerror);
+ task_resume (task);
+ }
+ break;
+
+ case crash_kill:
+ {
+ if (user_proc != MACH_PORT_NULL)
+ err = 0;
+ else
+ err = proc_task2proc (procserver, task, &user_proc);
+ if (!err)
+ err = proc_mark_exit (user_proc, W_EXITCODE (0, signo), sigcode);
+ err = task_terminate (task);
+ if (!err)
+ {
+ mach_port_deallocate (mach_task_self (), task);
+ mach_port_deallocate (mach_task_self (), core_file);
+ mach_port_deallocate (mach_task_self (), ctty_id);
+ }
+ }
+ }
+
+ if (user_proc != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), user_proc);
+
+ ports_port_deref (cred);
+ return err;
+}
+
+
+
+/* Handle an attempt to send a signal to crashing task C. */
+
+static error_t
+signal_crasher (struct crasher *c, int signo, int sigcode, mach_port_t refport)
+{
+ error_t err;
+
+ if (refport != c->task && (refport != c->sidport || signo != SIGCONT))
+ err = EPERM;
+ else
+ switch (signo)
+ {
+ case SIGTERM:
+ case SIGKILL:
+ /* Kill it as asked. */
+ proc_mark_exit (c->proc, W_EXITCODE (0, signo), sigcode);
+ err = task_terminate (c->task);
+ break;
+
+ case SIGCONT:
+ case SIGQUIT:
+ {
+ /* Resuming the process should make it dump core. */
+
+ mach_port_t old;
+
+ /* First, restore its msg port. */
+ err = proc_setmsgport (c->proc, c->original_msgport, &old);
+ mach_port_deallocate (mach_task_self (), old);
+
+ /* Tell the proc server it has resumed. */
+ proc_mark_cont (c->proc);
+
+ /* Reset the proc server port stored in C, and then destroy the
+ receive right. The null proc port tells dead_crasher to dump
+ a core file. */
+ mach_port_deallocate (mach_task_self (), c->proc);
+ c->proc = MACH_PORT_NULL;
+ ports_destroy_right (c);
+ }
+ break;
+
+ default:
+ err = EBUSY;
+ break;
+ }
+
+ ports_port_deref (c);
+ return err;
+}
+
+kern_return_t
+S_msg_sig_post (mach_port_t port,
+ mach_port_t reply_port, mach_msg_type_name_t reply_type,
+ int signo, natural_t sigcode, mach_port_t refport)
+{
+ struct crasher *c = ports_lookup_port (port_bucket, port, crasher_portclass);
+
+ if (! c)
+ return EOPNOTSUPP;
+
+ return signal_crasher (c, signo, sigcode, refport);
+}
+
+kern_return_t
+S_msg_sig_post_untraced (mach_port_t port,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_type,
+ int signo, natural_t sigcode, mach_port_t refport)
+{
+ error_t err;
+ struct crasher *c = ports_lookup_port (port_bucket, port, crasher_portclass);
+
+ if (! c)
+ return EOPNOTSUPP;
+
+ if (signo != 0 && signo != c->signo)
+ return signal_crasher (c, signo, sigcode, refport);
+
+ if (refport != c->task)
+ err = EPERM;
+ else
+ {
+ /* Debugger attaching to the process and continuing it.
+ The debugger is welcome to this crasher, so let's
+ just restore his msgport and forget him. */
+
+ mach_port_t old;
+
+ /* First, restore its msg port. */
+ err = proc_setmsgport (c->proc, c->original_msgport, &old);
+ if (! err)
+ {
+ mach_port_deallocate (mach_task_self (), old);
+
+ /* Tell the proc server it has stopped (again)
+ with the original crash signal. */
+ proc_mark_stop (c->proc, c->signo, c->sigcode);
+
+ /* We don't need to listen on this msgport any more. */
+ ports_destroy_right (c);
+ }
+ }
+
+ ports_port_deref (c);
+ return err;
+}
+
+/* This gets called when the receive right for a crasher message port dies. */
+
+void
+dead_crasher (void *ptr)
+{
+ struct crasher *c = ptr;
+
+ if (c->proc != MACH_PORT_NULL)
+ {
+ /* This message port just died. Clean it up. */
+ mach_port_deallocate (mach_task_self (), c->proc);
+ if (c->reply_port != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), c->reply_port);
+ }
+ else
+ {
+ /* C->proc was cleared in S_msg_sig_post as a marker that
+ this crasher should get a core dump when we clean him up. */
+ error_t err = dump_core (c->task, c->core_file, c->core_limit,
+ c->signo, c->sigcode, c->sigerror);
+ /* Now reply to the crasher's original RPC which started this whole
+ party. He should now report his own death (with core dump iff ERR
+ reports successful dumping) to his proc server. */
+ crash_dump_task_reply (c->reply_port, c->reply_type, err);
+ /* Resume the task so it can receive our reply and die happily. */
+ task_resume (c->task);
+ }
+
+ /* Deallocate the other saved ports. */
+ mach_port_deallocate (mach_task_self (), c->original_msgport);
+ mach_port_deallocate (mach_task_self (), c->sidport);
+ mach_port_deallocate (mach_task_self (), c->task);
+ mach_port_deallocate (mach_task_self (), c->core_file);
+ mach_port_deallocate (mach_task_self (), c->sidport);
+
+ /* The port data structures are cleaned up when we return. */
+
+ /* See if we are going away and this was the last thing keeping us up. */
+ if (ports_count_class (trivfs_cntl_portclasses[0]) == 0)
+ {
+ /* We have no fsys control port, so we are detached from the
+ parent filesystem. Maybe we have no users left either. */
+ if (ports_count_class (trivfs_protid_portclasses[0]) == 0)
+ {
+ /* We have no user ports left. Maybe we have no crashers still
+ around either. */
+ if (ports_count_class (crasher_portclass) == 0)
+ /* Nobody talking. Time to die. */
+ exit (0);
+ ports_enable_class (crasher_portclass);
+ }
+ ports_enable_class (trivfs_protid_portclasses[0]);
+ }
+ ports_enable_class (trivfs_cntl_portclasses[0]);
+}
+
+
+static const struct argp_option options[] =
+{
+ {0,0,0,0,"These options specify the disposition of a crashing process:", 1},
+ {"action", 'a', "ACTION", 0, "Action taken on crashing processes", 1},
+ {"orphan-action", 'O', "ACTION", 0, "Action taken on crashing orphans", 1},
+
+ {0,0,0,0,"These options are synonyms for --action=OPTION:", 2},
+ {"suspend", 's', 0, 0, "Suspend the process", 2},
+ {"kill", 'k', 0, 0, "Kill the process", 2},
+ {"core-file", 'c', 0, 0, "Dump a core file", 2},
+ {"dump-core", 0, 0, OPTION_ALIAS },
+ {0}
+};
+static const char doc[] =
+"Server to handle crashing tasks and dump core files or equivalent.\v"
+"The ACTION values can be `suspend', `kill', or `core-file'.\n\n"
+"If `--orphan-action' is not specified, the `--action' value is used for "
+"orphans. The default is `--action=suspend --orphan-action=core-file'.";
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ error_t parse_action (enum crash_action *how)
+ {
+ if (!strcmp (arg, "suspend"))
+ *how = crash_suspend;
+ else if (!strcmp (arg, "kill"))
+ *how = crash_kill;
+ else if (!strcmp (arg, "core-file"))
+ *how = crash_corefile;
+ else
+ {
+ argp_error (state,
+ "action must be one of: suspend, kill, core-file");
+ return EINVAL;
+ }
+ return 0;
+ }
+
+ switch (opt)
+ {
+ default:
+ return ARGP_ERR_UNKNOWN;
+ case ARGP_KEY_INIT:
+ case ARGP_KEY_ERROR:
+ break;
+
+ case 'a':
+ return parse_action (&crash_how);
+ case 'O':
+ return parse_action (&crash_orphans_how);
+
+ case 's': crash_how = crash_suspend; break;
+ case 'k': crash_how = crash_kill; break;
+ case 'c': crash_how = crash_corefile; break;
+
+ case ARGP_KEY_SUCCESS:
+ if (crash_orphans_how == crash_unspecified)
+ crash_orphans_how = (crash_how == crash_unspecified
+ ? CRASH_ORPHANS_DEFAULT : crash_how);
+ if (crash_how == crash_unspecified)
+ crash_how = CRASH_DEFAULT;
+ break;
+ }
+ return 0;
+}
+
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+ const char *opt;
+
+ switch (crash_how)
+ {
+ case crash_suspend: opt = "--action=suspend"; break;
+ case crash_kill: opt = "--action=kill"; break;
+ case crash_corefile: opt = "--action=core-file"; break;
+ default:
+ return EGRATUITOUS;
+ }
+ err = argz_add (argz, argz_len, opt);
+
+ if (!err)
+ {
+ switch (crash_orphans_how)
+ {
+ case crash_suspend: opt = "--orphan-action=suspend"; break;
+ case crash_kill: opt = "--orphan-action=kill"; break;
+ case crash_corefile: opt = "--orphan-action=core-file"; break;
+ default:
+ return EGRATUITOUS;
+ }
+ err = argz_add (argz, argz_len, opt);
+ }
+
+ return err;
+}
+
+struct argp crash_argp = { options, parse_opt, 0, doc };
+struct argp *trivfs_runtime_argp = &crash_argp;
+
+
+static int
+crash_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int crash_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ extern int msg_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ return (crash_server (inp, outp) ||
+ msg_server (inp, outp) ||
+ trivfs_demuxer (inp, outp));
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ argp_parse (&crash_argp, argc, argv, 0,0,0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+
+ /* Fetch our proc server port for easy use. */
+ procserver = getproc ();
+
+ port_bucket = ports_create_bucket ();
+ trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+ trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+ crasher_portclass = ports_create_class (dead_crasher, 0);
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0,
+ trivfs_cntl_portclasses[0], port_bucket,
+ trivfs_protid_portclasses[0], port_bucket,
+ &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "Contacting parent");
+
+ /* Launch. */
+ do
+ ports_manage_port_operations_multithread (port_bucket, crash_demuxer,
+ 10 * 1000, /* idle thread */
+ 10 * 60 * 1000, /* idle server */
+ 0);
+ /* That returns when 10 minutes pass without an RPC. Try shutting down
+ as if sent fsys_goaway; if we have any users who need us to stay
+ around, this returns EBUSY and we loop to service more RPCs. */
+ while (trivfs_goaway (fsys, 0));
+
+ return 0;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ int count;
+
+ /* Stop new requests. */
+ ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]);
+
+ /* Are there any extant user ports for the /servers/crash file? */
+ count = ports_count_class (trivfs_protid_portclasses[0]);
+ if (count == 0 || (flags & FSYS_GOAWAY_FORCE))
+ {
+ /* No users. Disconnect from the filesystem. */
+ mach_port_deallocate (mach_task_self (), fsys->underlying);
+
+ /* Are there any crasher message ports we are listening on? */
+ count = ports_count_class (crasher_portclass);
+ if (count == 0)
+ /* Nope. We got no reason to live. */
+ exit (0);
+
+ /* Continue babysitting crashing tasks we previously suspended. */
+ ports_enable_class (crasher_portclass);
+
+ /* No more communication with the parent filesystem. */
+ ports_destroy_right (fsys);
+
+ return 0;
+ }
+ else
+ {
+ /* We won't go away, so start things going again... */
+ ports_enable_class (trivfs_protid_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_protid_portclasses[0]);
+
+ return EBUSY;
+ }
+}
+
+/* Stubs for unused msgport RPCs. */
+
+kern_return_t
+S_msg_proc_newids (mach_port_t process,
+ mach_port_t task,
+ pid_t ppid,
+ pid_t pgrp,
+ int orphaned)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_add_auth (mach_port_t process,
+ auth_t auth)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_del_auth (mach_port_t process,
+ mach_port_t task,
+ intarray_t uids,
+ mach_msg_type_number_t uidsCnt,
+ intarray_t gids,
+ mach_msg_type_number_t gidsCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_port (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ mach_port_t *port,
+ mach_msg_type_name_t *portPoly)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_port (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ mach_port_t port)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_ports (mach_port_t process,
+ mach_port_t refport,
+ portarray_t *ports,
+ mach_msg_type_name_t *portsPoly,
+ mach_msg_type_number_t *portsCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_ports (mach_port_t process,
+ mach_port_t refport,
+ portarray_t ports,
+ mach_msg_type_number_t portsCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_int (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ int *value)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_int (mach_port_t process,
+ mach_port_t refport,
+ int which,
+ int value)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_init_ints (mach_port_t process,
+ mach_port_t refport,
+ intarray_t *values,
+ mach_msg_type_number_t *valuesCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_init_ints (mach_port_t process,
+ mach_port_t refport,
+ intarray_t values,
+ mach_msg_type_number_t valuesCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_dtable (mach_port_t process,
+ mach_port_t refport,
+ portarray_t *dtable,
+ mach_msg_type_name_t *dtablePoly,
+ mach_msg_type_number_t *dtableCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_dtable (mach_port_t process,
+ mach_port_t refport,
+ portarray_t dtable,
+ mach_msg_type_number_t dtableCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_fd (mach_port_t process,
+ mach_port_t refport,
+ int fd,
+ mach_port_t *port,
+ mach_msg_type_name_t *portPoly)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_fd (mach_port_t process,
+ mach_port_t refport,
+ int fd,
+ mach_port_t port)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_environment (mach_port_t process,
+ data_t *value,
+ mach_msg_type_number_t *valueCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_environment (mach_port_t process,
+ mach_port_t refport,
+ data_t value,
+ mach_msg_type_number_t valueCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_get_env_variable (mach_port_t process,
+ string_t variable,
+ data_t *value,
+ mach_msg_type_number_t *valueCnt)
+{ return EBUSY; }
+
+kern_return_t
+S_msg_set_env_variable (mach_port_t process,
+ mach_port_t refport,
+ string_t variable,
+ string_t value,
+ boolean_t replace)
+{ return EBUSY; }
+kern_return_t
+S_msg_get_exec_flags (mach_port_t process, mach_port_t refport, int *flags)
+{ return EBUSY; }
+kern_return_t
+S_msg_set_all_exec_flags (mach_port_t process, mach_port_t refport, int flags)
+{ return EBUSY; }
+kern_return_t
+S_msg_set_some_exec_flags (mach_port_t process, mach_port_t refport, int flags)
+{ return EBUSY; }
+kern_return_t
+S_msg_clear_some_exec_flags (mach_port_t process, mach_port_t refport,
+ int flags)
+{ return EBUSY; }
+error_t
+S_msg_report_wait (mach_port_t process, thread_t thread,
+ string_t desc, mach_msg_id_t *rpc)
+{ return EBUSY; }
+error_t
+S_msg_describe_ports (mach_port_t msgport, mach_port_t refport,
+ mach_port_t *ports, mach_msg_type_number_t nports,
+ char **desc, mach_msg_type_number_t *desclen)
+{ return EBUSY; }
diff --git a/trans/fakeroot.c b/trans/fakeroot.c
new file mode 100644
index 00000000..32a34ec4
--- /dev/null
+++ b/trans/fakeroot.c
@@ -0,0 +1,1041 @@
+/* fakeroot -- a translator for faking actions that aren't really permitted
+ Copyright (C) 2002, 2003, 2008, 2013 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 <hurd/netfs.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <error.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 <version.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 (fakeroot);
+
+char *netfs_server_name = "fakeroot";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 16; /* arbitrary */
+
+static auth_t fakeroot_auth_port;
+
+struct netnode
+{
+ hurd_ihash_locp_t idport_locp;/* easy removal pointer in idport ihash */
+ mach_port_t idport; /* port from io_identity */
+ int openmodes; /* O_READ | O_WRITE | O_EXEC */
+ file_t file; /* port on real file */
+
+ unsigned int faked;
+};
+
+#define FAKE_UID (1 << 0)
+#define FAKE_GID (1 << 1)
+#define FAKE_AUTHOR (1 << 2)
+#define FAKE_MODE (1 << 3)
+#define FAKE_DEFAULT (1 << 4)
+
+pthread_mutex_t idport_ihash_lock = PTHREAD_MUTEX_INITIALIZER;
+struct hurd_ihash idport_ihash
+= HURD_IHASH_INITIALIZER (sizeof (struct node)
+ + offsetof (struct netnode, idport_locp));
+
+
+/* Make a new virtual node. Always consumes the ports. If
+ successful, NP will be locked. */
+static error_t
+new_node (file_t file, mach_port_t idport, int locked, int openmodes,
+ struct node **np)
+{
+ error_t err;
+ struct netnode *nn;
+ *np = netfs_make_node_alloc (sizeof *nn);
+ if (*np == 0)
+ {
+ mach_port_deallocate (mach_task_self (), file);
+ if (idport != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), idport);
+ if (locked)
+ pthread_mutex_unlock (&idport_ihash_lock);
+ return ENOMEM;
+ }
+ nn = netfs_node_netnode (*np);
+ nn->file = file;
+ nn->openmodes = openmodes;
+ if (idport != MACH_PORT_NULL)
+ nn->idport = idport;
+ else
+ {
+ ino_t fileno;
+ mach_port_t fsidport;
+ assert (!locked);
+ err = io_identity (file, &nn->idport, &fsidport, &fileno);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), file);
+ free (*np);
+ return err;
+ }
+ }
+
+ if (!locked)
+ pthread_mutex_lock (&idport_ihash_lock);
+ err = hurd_ihash_add (&idport_ihash, nn->idport, *np);
+ if (err)
+ goto lose;
+
+ pthread_mutex_lock (&(*np)->lock);
+ pthread_mutex_unlock (&idport_ihash_lock);
+ return 0;
+
+ lose:
+ pthread_mutex_unlock (&idport_ihash_lock);
+ mach_port_deallocate (mach_task_self (), nn->idport);
+ mach_port_deallocate (mach_task_self (), file);
+ free (*np);
+ return err;
+}
+
+static void
+set_default_attributes (struct node *np)
+{
+ netfs_node_netnode (np)->faked = FAKE_UID | FAKE_GID | FAKE_DEFAULT;
+ np->nn_stat.st_uid = 0;
+ np->nn_stat.st_gid = 0;
+}
+
+static void
+set_faked_attribute (struct node *np, unsigned int faked)
+{
+ netfs_node_netnode (np)->faked |= faked;
+
+ if (netfs_node_netnode (np)->faked & FAKE_DEFAULT)
+ {
+ /* Now that the node has non-default faked attributes, they have to be
+ retained for future accesses. Account for the hash table reference.
+
+ XXX This means such nodes are currently leaked. Hopefully, there
+ won't be too many of them until the translator is shut down, and
+ the data structures should make implementing garbage collection
+ easy enough if it's ever needed, although scalability could be
+ improved. */
+ netfs_nref (np);
+ netfs_node_netnode (np)->faked &= ~FAKE_DEFAULT;
+ }
+}
+
+/* Node NP has no more references; free all its associated storage. */
+void
+netfs_node_norefs (struct node *np)
+{
+ pthread_mutex_unlock (&np->lock);
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ pthread_mutex_lock (&idport_ihash_lock);
+ hurd_ihash_locp_remove (&idport_ihash, netfs_node_netnode (np)->idport_locp);
+ pthread_mutex_unlock (&idport_ihash_lock);
+
+ mach_port_deallocate (mach_task_self (), netfs_node_netnode (np)->file);
+ mach_port_deallocate (mach_task_self (), netfs_node_netnode (np)->idport);
+ free (np);
+
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+}
+
+/* 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
+fakeroot_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");
+ }
+}
+
+/* Given an existing node, make sure it has NEWMODES in its openmodes.
+ If not null, FILE is a port with those openmodes. */
+static error_t
+check_openmodes (struct netnode *nn, int newmodes, file_t file)
+{
+ error_t err = 0;
+
+ if (newmodes &~ nn->openmodes)
+ {
+ /* The user wants openmodes we haven't tried before. */
+
+ if (file != MACH_PORT_NULL && (nn->openmodes & ~newmodes))
+ {
+ /* Intersecting sets.
+ We need yet another new peropen on this node. */
+ mach_port_deallocate (mach_task_self (), file);
+ file = MACH_PORT_NULL;
+ }
+ if (file == MACH_PORT_NULL)
+ {
+ enum retry_type bad_retry;
+ char bad_retryname[1024]; /* XXX */
+ err = dir_lookup (nn->file, "", nn->openmodes | newmodes, 0,
+ &bad_retry, bad_retryname, &file);
+ if (!err && (bad_retry != FS_RETRY_NORMAL
+ || bad_retryname[0] != '\0'))
+ {
+ mach_port_deallocate (mach_task_self (), file);
+ err = EGRATUITOUS;
+ }
+ }
+ if (! err)
+ {
+ /* The new port has more openmodes than
+ the old one. We can just use it now. */
+ mach_port_deallocate (mach_task_self (), nn->file);
+ nn->file = file;
+ nn->openmodes = newmodes;
+ }
+ }
+ else if (file != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), file);
+
+ return err;
+}
+
+/* This is called by netfs_S_fsys_getroot. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *np,
+ int flags, int newnode)
+{
+ return check_openmodes (netfs_node_netnode (np),
+ flags & (O_RDWR|O_EXEC), MACH_PORT_NULL);
+}
+
+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;
+ mach_port_t file;
+ mach_port_t idport, fsidport;
+ ino_t fileno;
+
+ if (!diruser)
+ return EOPNOTSUPP;
+
+ dnp = diruser->po->np;
+
+ mach_port_t dir = netfs_node_netnode (dnp)->file;
+ redo_lookup:
+ 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 (dir != netfs_node_netnode (dnp)->file)
+ mach_port_deallocate (mach_task_self (), dir);
+ if (err)
+ return err;
+
+ switch (*do_retry)
+ {
+ case FS_RETRY_REAUTH:
+ {
+ mach_port_t ref = mach_reply_port ();
+ err = io_reauthenticate (file, ref, MACH_MSG_TYPE_MAKE_SEND);
+ if (! err)
+ {
+ mach_port_deallocate (mach_task_self (), file);
+ err = auth_user_authenticate (fakeroot_auth_port, ref,
+ MACH_MSG_TYPE_MAKE_SEND,
+ &dir);
+ }
+ mach_port_destroy (mach_task_self (), ref);
+ if (err)
+ return err;
+ }
+ filename = retry_name;
+ goto redo_lookup;
+
+ case FS_RETRY_NORMAL:
+ if (retry_name[0] != '\0')
+ {
+ dir = file;
+ filename = retry_name;
+ goto redo_lookup;
+ }
+ break;
+
+ case FS_RETRY_MAGICAL:
+ if (file == MACH_PORT_NULL)
+ {
+ *retry_port = MACH_PORT_NULL;
+ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+ }
+ /* Fallthrough. */
+
+ default:
+ /* Invalid response to our dir_lookup request. */
+ if (file != MACH_PORT_NULL)
+ 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 = io_identity (file, &idport, &fsidport, &fileno);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), file);
+ return err;
+ }
+
+ mach_port_deallocate (mach_task_self (), fsidport);
+
+ redo_hash_lookup:
+ pthread_mutex_lock (&idport_ihash_lock);
+ pthread_mutex_lock (&dnp->lock);
+ /* The hashtable may not hold a true reference on the node. Acquire the
+ refcount lock so that, if a node is found, its reference counter cannot
+ drop to 0 before we get our own reference. */
+ pthread_spin_lock (&netfs_node_refcnt_lock);
+ np = hurd_ihash_find (&idport_ihash, idport);
+ if (np != NULL)
+ {
+ /* We already know about this node. */
+
+ if (np->references == 0)
+ {
+ /* But it might be in the process of being released. If so,
+ unlock the hash table to give the node a chance to actually
+ be removed and retry. */
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ pthread_mutex_unlock (&dnp->lock);
+ pthread_mutex_unlock (&idport_ihash_lock);
+ goto redo_hash_lookup;
+ }
+
+ /* Otherwise, reference it right away. */
+ np->references++;
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+
+ mach_port_deallocate (mach_task_self (), idport);
+
+ if (np == dnp)
+ {
+ /* dnp is already locked. */
+ }
+ else
+ {
+ pthread_mutex_lock (&np->lock);
+ pthread_mutex_unlock (&dnp->lock);
+ }
+
+ err = check_openmodes (netfs_node_netnode (np),
+ (flags & (O_RDWR|O_EXEC)), file);
+ pthread_mutex_unlock (&idport_ihash_lock);
+ }
+ else
+ {
+ pthread_spin_unlock (&netfs_node_refcnt_lock);
+ err = new_node (file, idport, 1, flags, &np);
+ pthread_mutex_unlock (&dnp->lock);
+ if (!err)
+ {
+ set_default_attributes (np);
+ err = netfs_validate_stat (np, diruser->user);
+ }
+ }
+ if (err)
+ goto lose;
+
+ assert (retry_name[0] == '\0' && *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 (np != NULL)
+ netfs_nput (np);
+ return err;
+}
+
+/* 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
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **np)
+{
+ assert (! "should not be here");
+ return EIEIO;
+}
+
+error_t
+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
+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;
+
+ if (netfs_node_netnode (np)->faked & FAKE_UID)
+ st.st_uid = np->nn_stat.st_uid;
+ if (netfs_node_netnode (np)->faked & FAKE_GID)
+ st.st_gid = np->nn_stat.st_gid;
+ if (netfs_node_netnode (np)->faked & FAKE_AUTHOR)
+ st.st_author = np->nn_stat.st_author;
+ if (netfs_node_netnode (np)->faked & FAKE_MODE)
+ st.st_mode = np->nn_stat.st_mode;
+
+ np->nn_stat = st;
+ np->nn_translated = S_ISLNK (st.st_mode) ? S_IFLNK : 0;
+
+ return 0;
+}
+
+/* Various netfs functions will call fshelp_isowner to check whether
+ USER is allowed to do some operation. As fakeroot is not running
+ within the fakeauth'ed environment, USER contains the real
+ user. Hence, we override this check. */
+error_t
+fshelp_isowner (struct stat *st, struct iouser *user)
+{
+ return 0;
+}
+
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np,
+ uid_t uid, uid_t gid)
+{
+ if (uid != ~0U)
+ {
+ set_faked_attribute (np, FAKE_UID);
+ np->nn_stat.st_uid = uid;
+ }
+ if (gid != ~0U)
+ {
+ set_faked_attribute (np, FAKE_GID);
+ np->nn_stat.st_gid = gid;
+ }
+ return 0;
+}
+
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *np, uid_t author)
+{
+ set_faked_attribute (np, FAKE_AUTHOR);
+ np->nn_stat.st_author = author;
+ return 0;
+}
+
+/* Return the mode that the real underlying file should have if the
+ fake mode is being set to MODE. We always give ourselves read and
+ write permission so that we can open the file as root would be able
+ to. We give ourselves execute permission iff any execute bit is
+ set in the fake mode. */
+static inline mode_t
+real_from_fake_mode (mode_t mode)
+{
+ return mode | S_IREAD | S_IWRITE | (((mode << 3) | (mode << 6)) & S_IEXEC);
+}
+
+/* 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
+netfs_attempt_chmod (struct iouser *cred, struct node *np, mode_t mode)
+{
+ if ((mode & S_IFMT) == 0)
+ mode |= np->nn_stat.st_mode & S_IFMT;
+ if ((mode & S_IFMT) != (np->nn_stat.st_mode & S_IFMT))
+ return EOPNOTSUPP;
+
+ /* We don't bother with error checking since the fake mode change should
+ always succeed--worst case a later open will get EACCES. */
+ (void) file_chmod (netfs_node_netnode (np)->file, mode);
+ set_faked_attribute (np, FAKE_MODE);
+ np->nn_stat.st_mode = mode;
+ return 0;
+}
+
+/* The user must define this function. Attempt to turn locked node NP
+ (user CRED) into a symlink with target NAME. */
+error_t
+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
+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
+netfs_attempt_chflags (struct iouser *cred, struct node *np, int flags)
+{
+ return file_chflags (netfs_node_netnode (np)->file, flags);
+}
+
+error_t
+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
+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
+netfs_attempt_statfs (struct iouser *cred, struct node *np, struct statfs *st)
+{
+ return file_statfs (netfs_node_netnode (np)->file, st);
+}
+
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
+{
+ return file_sync (netfs_node_netnode (np)->file, wait, 0);
+}
+
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+error_t
+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
+netfs_attempt_unlink (struct iouser *user, struct node *dir, char *name)
+{
+ return dir_unlink (netfs_node_netnode (dir)->file, name);
+}
+
+error_t
+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
+netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return dir_rmdir (netfs_node_netnode (dir)->file, name);
+}
+
+error_t
+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
+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,
+ real_from_fake_mode (mode), &newfile);
+ pthread_mutex_unlock (&dir->lock);
+ if (err == 0)
+ err = new_node (newfile, MACH_PORT_NULL, 0, O_RDWR|O_EXEC, np);
+ if (err == 0)
+ pthread_mutex_unlock (&(*np)->lock);
+ return err;
+}
+
+error_t
+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
+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
+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
+netfs_report_access (struct iouser *cred, struct node *np, int *types)
+{
+ *types = O_RDWR|O_EXEC;
+ return 0;
+}
+
+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)
+{
+ return dir_readdir (netfs_node_netnode (dir)->file, data, datacnt,
+ entry, nentries, bufsize, amt);
+}
+
+error_t
+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);
+}
+
+kern_return_t
+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;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = check_openmodes (netfs_node_netnode (user->po->np),
+ O_EXEC, MACH_PORT_NULL);
+ file = netfs_node_netnode (user->po->np)->file;
+ if (!err)
+ err = mach_port_mod_refs (mach_task_self (),
+ file, MACH_PORT_RIGHT_SEND, 1);
+ pthread_mutex_unlock (&user->po->np->lock);
+
+ 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 (netfs_node_netnode (user->po->np)->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
+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)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+ *rdobjtype = *wrobjtype = MACH_MSG_TYPE_MOVE_SEND;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = io_map (netfs_node_netnode (user->po->np)->file, rdobj, wrobj);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
+
+error_t
+netfs_S_io_map_cntl (struct protid *user,
+ mach_port_t *obj,
+ mach_msg_type_name_t *objtype)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+ *objtype = MACH_MSG_TYPE_MOVE_SEND;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = io_map_cntl (netfs_node_netnode (user->po->np)->file, obj);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
+
+error_t
+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)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ *idtype = *fsystype = MACH_MSG_TYPE_MOVE_SEND;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = io_identity (netfs_node_netnode (user->po->np)->file,
+ id, fsys, fileno);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
+
+#define NETFS_S_SIMPLE(name) \
+error_t \
+netfs_S_##name (struct protid *user) \
+{ \
+ error_t err; \
+ \
+ if (!user) \
+ return EOPNOTSUPP; \
+ \
+ pthread_mutex_lock (&user->po->np->lock); \
+ err = name (netfs_node_netnode (user->po->np)->file); \
+ pthread_mutex_unlock (&user->po->np->lock); \
+ return err; \
+}
+
+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
+netfs_S_io_prenotify (struct protid *user,
+ vm_offset_t start, vm_offset_t stop)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = io_prenotify (netfs_node_netnode (user->po->np)->file, start, stop);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
+
+error_t
+netfs_S_io_postnotify (struct protid *user,
+ vm_offset_t start, vm_offset_t stop)
+{
+ error_t err;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&user->po->np->lock);
+ err = io_postnotify (netfs_node_netnode (user->po->np)->file, start, stop);
+ pthread_mutex_unlock (&user->po->np->lock);
+ return err;
+}
+
+/* This overrides the library's definition. */
+int
+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 = 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);
+ 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);
+ assert_perror (err); /* XXX should synthesize reply */
+ ports_port_deref (cred);
+ /* We already sent the message, so the server loop shouldn't do it again. */
+ ((mig_reply_header_t *) outp)->RetCode = MIG_NO_REPLY;
+ return 1;
+ }
+ }
+}
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ struct argp argp = { .doc = "\
+A translator for faking privileged access to an underlying filesystem.\v\
+This translator appears to give transparent access to the underlying \
+directory node. However, all accesses are made using the credentials \
+of the translator regardless of the client and the translator fakes \
+success for chown and chmod operations that only root could actually do, \
+reporting the faked IDs and modes in later stat calls, and allows \
+any user to open nodes regardless of permissions as is done for root." };
+
+ /* Parse our command line arguments (all none of them). */
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ fakeroot_auth_port = getauth ();
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ netfs_init ();
+
+ /* Install our own clean routine. */
+ netfs_protid_class->clean_routine = fakeroot_netfs_release_protid;
+
+ /* Get our underlying node (we presume it's a directory) and use
+ that to make the root node of the filesystem. */
+ err = new_node (netfs_startup (bootstrap, O_READ), MACH_PORT_NULL, 0, O_READ,
+ &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;
+ set_faked_attribute (netfs_root_node, FAKE_MODE);
+ pthread_mutex_unlock (&netfs_root_node->lock);
+
+ netfs_server_loop (); /* Never returns. */
+
+ /*NOTREACHED*/
+ return 0;
+}
diff --git a/trans/fifo.c b/trans/fifo.c
new file mode 100644
index 00000000..a9ad2dd2
--- /dev/null
+++ b/trans/fifo.c
@@ -0,0 +1,628 @@
+/* A translator for fifos
+
+ Copyright (C) 1995,96,97,98,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ 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 <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <argp.h>
+
+#include <pthread.h>
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/fsys.h>
+#include <hurd/pipe.h>
+
+#include <version.h>
+
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+
+/* Global options. These defaults are the standard ones, I think... */
+int wait_for_reader = 1, wait_for_writer = 1;
+int one_reader = 1;
+
+/* What kinds of pipes we use. */
+struct pipe_class *fifo_pipe_class;
+
+/* The current fifo that new opens will see, or NULL if there is none. */
+struct pipe *active_fifo = NULL;
+
+/* Lock this when changing ACTIVE_FIFO. */
+pthread_mutex_t active_fifo_lock;
+/* Signal this when ACTIVE_FIFO may have changed. */
+pthread_cond_t active_fifo_changed;
+
+const char *argp_program_version = STANDARD_HURD_VERSION (fifo);
+
+static struct argp_option options[] =
+{
+ { "multiple-readers", 'm', 0, 0, "Allow multiple simultaneous readers" },
+ { "noblock", 'n', 0, 0, "Don't block on open" },
+ { "dgram", 'd', 0, 0, "Reads reflect write record boundaries" },
+ { 0 }
+};
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'm': one_reader = 0; break;
+ case 'n': wait_for_reader = wait_for_writer = 0; break;
+ case 'd': fifo_pipe_class = seqpack_pipe_class; break;
+ default: return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const struct argp argp = {
+ options, parse_opt, 0, "Translator for fifos."
+};
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ fifo_pipe_class = stream_pipe_class;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "Contacting parent");
+
+ /* Launch. */
+ do
+ {
+ ports_enable_class (fsys->protid_class);
+ ports_manage_port_operations_multithread (fsys->pi.bucket,
+ trivfs_demuxer,
+ 30*1000, 5*60*1000, 0);
+ }
+ while (ports_count_class (fsys->protid_class) > 0);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+static error_t
+open_hook (struct trivfs_peropen *po)
+{
+ error_t err = 0;
+ int flags = po->openmodes;
+
+ if (flags & (O_READ | O_WRITE))
+ {
+ pthread_mutex_lock (&active_fifo_lock);
+
+/* Wait until the active fifo has changed so that CONDITION is true. */
+#define WAIT(condition, noblock_err) \
+ while (!err && !(condition)) \
+ { \
+ if (flags & O_NONBLOCK) \
+ { \
+ err = noblock_err; \
+ break; \
+ } \
+ else if (pthread_hurd_cond_wait_np (&active_fifo_changed, \
+ &active_fifo_lock)) \
+ err = EINTR; \
+ }
+
+ if (flags & O_READ)
+ /* When opening for read, what we do depends on what mode this server
+ is running in. The default (if ONE_READER is set) is to only
+ allow one reader at a time, with additional opens for read
+ blocking here until the old reader goes away; otherwise, we allow
+ multiple readers. If WAIT_FOR_WRITER is true, then once we've
+ created a fifo, we also block until someone opens it for writing;
+ otherwise, the first read will block until someone writes
+ something. */
+ {
+ if (one_reader)
+ /* Wait until there isn't any active fifo, so we can make one. */
+ WAIT (!active_fifo || !active_fifo->readers, EWOULDBLOCK);
+
+ if (!err && active_fifo == NULL)
+ /* No other readers, and indeed, no fifo; make one. */
+ {
+ err = pipe_create (fifo_pipe_class, &active_fifo);
+ if (! err)
+ active_fifo->flags &= ~PIPE_BROKEN; /* Avoid immediate EOF. */
+ }
+ if (!err)
+ {
+ pipe_add_reader (active_fifo);
+ pthread_cond_broadcast (&active_fifo_changed);
+ /* We'll unlock ACTIVE_FIFO_LOCK below; the writer code won't
+ make us block because we've ensured that there's a reader
+ for it. */
+
+ if (wait_for_writer)
+ /* Wait until there's a writer. */
+ {
+ WAIT (active_fifo->writers, 0);
+ if (err)
+ /* Back out the new pipe creation. */
+ {
+ pipe_remove_reader (active_fifo);
+ active_fifo = NULL;
+ pthread_cond_broadcast (&active_fifo_changed);
+ }
+ }
+ }
+ }
+
+ if (!err && (flags & O_WRITE))
+ /* Open the active_fifo for writing. If WAIT_FOR_READER is true,
+ then we block until there's someone to read what we wrote,
+ otherwise, if there's no fifo, we create one, which we just write
+ into and leave it for someone to read later. */
+ {
+ if (wait_for_reader)
+ /* Wait until there's a fifo to write to. */
+ WAIT (active_fifo && active_fifo->readers, ENXIO);
+ if (!err && active_fifo == NULL)
+ /* No other readers, and indeed, no fifo; make one. */
+ {
+ err = pipe_create (fifo_pipe_class, &active_fifo);
+ if (!err)
+ active_fifo->flags &= ~PIPE_BROKEN;
+ }
+ if (!err)
+ {
+ pipe_add_writer (active_fifo);
+ pthread_cond_broadcast (&active_fifo_changed);
+ }
+ }
+
+ po->hook = active_fifo;
+
+ pthread_mutex_unlock (&active_fifo_lock);
+ }
+
+ return err;
+}
+
+static void
+close_hook (struct trivfs_peropen *po)
+{
+ int was_active, detach = 0;
+ int flags = po->openmodes;
+ struct pipe *pipe = po->hook;
+
+ if (!pipe)
+ return;
+
+ pthread_mutex_lock (&active_fifo_lock);
+ was_active = (active_fifo == pipe);
+
+ if (was_active)
+ /* See if PIPE should cease to be the user-visible face of this fifo. */
+ detach =
+ ((flags & O_READ) && pipe->readers == 1)
+ || ((flags & O_WRITE) && pipe->writers == 1);
+ else
+ /* Let others have their fun. */
+ pthread_mutex_unlock (&active_fifo_lock);
+
+ if (flags & O_READ)
+ pipe_remove_reader (pipe);
+ if (flags & O_WRITE)
+ pipe_remove_writer (pipe);
+ /* At this point, PIPE may be gone, so we can't look at it again. */
+
+ if (was_active)
+ {
+ if (detach)
+ active_fifo = NULL;
+ pthread_cond_broadcast (&active_fifo_changed);
+ pthread_mutex_unlock (&active_fifo_lock);
+ }
+}
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *) = open_hook;
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ struct pipe *pipe = cred->po->hook;
+
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFIFO;
+
+ if (pipe)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ st->st_size = pipe_readable (pipe, 1);
+ st->st_blocks = st->st_size >> 9;
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ st->st_size = st->st_blocks = 0;
+
+ /* As we try to be clever with large transfers, ask for them. */
+ st->st_blksize = vm_page_size * 16;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ error_t err;
+ int force = (flags & FSYS_GOAWAY_FORCE);
+ struct port_bucket *bucket = ((struct port_info *)cntl)->bucket;
+
+ err = ports_inhibit_bucket_rpcs (bucket);
+ if (err == EINTR || (err && !force))
+ return err;
+
+ if (ports_count_class (cntl->protid_class) > 0 && !force)
+ /* Still some opens, and we're not being forced to go away, so don't. */
+ {
+ ports_enable_class (cntl->protid_class);
+ ports_resume_bucket_rpcs (bucket);
+ return EBUSY;
+ }
+
+ exit (0);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+error_t
+trivfs_S_io_map (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ return EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, size_t *data_len,
+ off_t offs, size_t amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_read (pipe, cred->po->openmodes & O_NONBLOCK, NULL,
+ data, data_len, amount);
+ pthread_mutex_unlock (&pipe->lock);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ size_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ pthread_mutex_lock (&pipe->lock);
+ *amount = pipe_readable (pipe, 1);
+ pthread_mutex_unlock (&pipe->lock);
+ err = 0;
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offset, int whence, off_t *new_offset)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return ESPIPE;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+static error_t
+io_select_common (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec *tsp, int *select_type)
+{
+ struct pipe *pipe;
+ error_t err = 0;
+ int ready = 0;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pipe = cred->po->hook;
+
+ if (*select_type & SELECT_READ)
+ {
+ if (cred->po->openmodes & O_READ)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_wait_readable (pipe, 1, 1);
+ if (err == EWOULDBLOCK)
+ err = 0; /* Not readable, actually not an error. */
+ else
+ ready |= SELECT_READ; /* Data immediately readable (or error). */
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ {
+ err = EBADF;
+ ready |= SELECT_READ; /* Error immediately available... */
+ }
+ if (err)
+ /* Prevent write test from overwriting err. */
+ *select_type &= ~SELECT_WRITE;
+ }
+
+ if (*select_type & SELECT_WRITE)
+ {
+ if (cred->po->openmodes & O_WRITE)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_wait_writable (pipe, 1);
+ if (err == EWOULDBLOCK)
+ err = 0; /* Not writable, actually not an error. */
+ else
+ ready |= SELECT_WRITE; /* Data immediately writable (or error). */
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ {
+ err = EBADF;
+ ready |= SELECT_WRITE; /* Error immediately available... */
+ }
+ }
+
+ if (ready)
+ *select_type = ready;
+ else
+ /* Wait for something to change. */
+ {
+ ports_interrupt_self_on_port_death (cred, reply);
+ err = pipe_pair_select (pipe, pipe, tsp, select_type, 1);
+ }
+
+ return err;
+}
+
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *select_type)
+{
+ return io_select_common (cred, reply, reply_type, NULL, select_type);
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *select_type)
+{
+ return io_select_common (cred, reply, reply_type, &ts, select_type);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+error_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *data, size_t data_len,
+ off_t offs, size_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else
+ {
+ int flags = cred->po->openmodes;
+ struct pipe *pipe = cred->po->hook;
+
+ if (!(flags & O_WRITE))
+ err = EBADF;
+ else
+ {
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_write (pipe, flags & O_NONBLOCK, NULL,
+ data, data_len, amount);
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+error_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t size)
+{
+ return size == 0 ? 0 : EINVAL;
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+error_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Get/set the owner of the IO object. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all
+ objects this affects old-style async IO. Negative values represent
+ pgrps. This has nothing to do with the owner of a file (as
+ returned by io_stat, and as used for various permission checks by
+ filesystems). An owner of 0 indicates that there is no owner. */
+
+error_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ *owner = 0;
+ return 0;
+}
+
+error_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return EINVAL;
+}
diff --git a/trans/firmlink.c b/trans/firmlink.c
new file mode 100644
index 00000000..69d4aaed
--- /dev/null
+++ b/trans/firmlink.c
@@ -0,0 +1,288 @@
+/* A translator for `firmlinks'
+
+ Copyright (C) 1997,98,99,2001,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <error.h>
+#include <sys/mman.h>
+
+#include <hurd/trivfs.h>
+
+#include <version.h>
+
+#include "libtrivfs/trivfs_io_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (firmlink);
+
+static const struct argp_option options[] =
+{
+ { 0 }
+};
+
+static const char args_doc[] = "TARGET";
+static const char doc[] = "A translator for firmlinks."
+"\vA firmlink is sort of half-way between a symbolic link and a hard link:"
+"\n"
+"\nLike a symbolic link, it is `by name', and contains no actual reference to"
+" the target. However, the lookup returns a node which will redirect parent"
+" lookups so that attempts to find the cwd that go through the link will"
+" reflect the link name, not the target name. The target referenced by the"
+" firmlink is looked up in the namespace of the translator, not the client.";
+
+/* Link parameters. */
+static char *target = 0; /* What we translate too. */
+
+/* Parse a single option/argument. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ if (key == ARGP_KEY_ARG && state->arg_num == 0)
+ target = arg;
+ else if (key == ARGP_KEY_ARG || key == ARGP_KEY_NO_ARGS)
+ argp_usage (state);
+ else
+ return ARGP_ERR_UNKNOWN;
+ return 0;
+}
+
+static struct argp argp = { options, parse_opt, args_doc, doc };
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ /* Parse our options... */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (2, err, "Contacting parent");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (fsys->pi.bucket, trivfs_demuxer,
+ 2 * 60 * 1000, 0, 0);
+
+ return 0;
+}
+
+/* Return in LINK the node that TARGET_NAME resolves to, with its parent
+ replaced by PARENT. FLAGS are the flags to open TARGET_NAME with. */
+static error_t
+firmlink (mach_port_t parent, const char *target_name, int flags,
+ mach_port_t *link)
+{
+ error_t err;
+ file_t authed_link;
+ file_t target = file_name_lookup (target_name, flags & ~O_CREAT, 0);
+
+ if (target == MACH_PORT_NULL)
+ return errno;
+
+ err = file_reparent (target, parent, &authed_link);
+ mach_port_deallocate (mach_task_self (), target);
+ mach_port_deallocate (mach_task_self (), parent);
+
+ if (! err)
+ {
+ err = io_restrict_auth (authed_link, link, 0, 0, 0, 0);
+ mach_port_deallocate (mach_task_self (), authed_link);
+ }
+
+ return err;
+}
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ;
+
+/* Return the root node of our file system: A firmlink to TARGET, unless
+ TARGET doesn't exist, in which case we return a symlink-like node. */
+static error_t
+getroot (struct trivfs_control *cntl,
+ mach_port_t reply_port, mach_msg_type_name_t reply_port_type,
+ mach_port_t dotdot,
+ uid_t *uids, u_int nuids, uid_t *gids, u_int ngids,
+ int flags,
+ retry_type *do_retry, char *retry_name,
+ mach_port_t *node, mach_msg_type_name_t *node_type)
+{
+ error_t err = firmlink (dotdot, target, flags, node);
+
+ if (err == ENOENT)
+ /* No target? Act like a link. */
+ return EAGAIN;
+
+ if (! err)
+ {
+ *node_type = MACH_MSG_TYPE_MOVE_SEND;
+ *do_retry = FS_RETRY_REAUTH;
+ retry_name[0] = '\0';
+ }
+
+ return err;
+}
+
+/* Called by trivfs_S_fsys_getroot before any other processing takes place;
+ if the return value is EAGAIN, normal trivfs getroot processing continues,
+ otherwise the rpc returns with that return value. */
+error_t (*trivfs_getroot_hook) () = getroot;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_size = strlen (target);
+ st->st_blocks = 0;
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFLNK;
+}
+
+/* Shutdown the filesystem. */
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ error_t err;
+ int force = (flags & FSYS_GOAWAY_FORCE);
+ struct port_bucket *bucket = ((struct port_info *)cntl)->bucket;
+
+ err = ports_inhibit_bucket_rpcs (bucket);
+ if (err == EINTR || (err && !force))
+ return err;
+
+ if (ports_count_class (cntl->protid_class) > 0 && !force)
+ /* Still some opens, and we're not being forced to go away, so don't. */
+ {
+ ports_enable_class (cntl->protid_class);
+ ports_resume_bucket_rpcs (bucket);
+ return EBUSY;
+ }
+
+ exit (0);
+}
+
+/* We store the file offset in po->hook (ick!) */
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ error_t err = 0;
+
+ if (! cred)
+ err = EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ size_t max = strlen (target);
+ intptr_t start = offs >= 0 ? offs : (intptr_t)cred->po->hook;
+ if (start < 0)
+ return EINVAL;
+ if (start + amount > max)
+ amount = max - start;
+ if (amount > *data_len)
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = (*data == MAP_FAILED) ? errno : 0;
+ if (!err && amount > 0)
+ {
+ memcpy (*data, target + start, amount);
+ if (offs < 0)
+ cred->po->hook = (void *)(start + amount); /* Update PO offset. */
+ }
+ *data_len = amount;
+ }
+
+ return err;
+}
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_msg_type_number_t *amount)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+ else if ((intptr_t)cred->po->hook < 0)
+ return EINVAL;
+ else
+ *amount = strlen (target) - (intptr_t)cred->po->hook;
+ return 0;
+}
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offset, int whence, off_t *new_offset)
+{
+ return EOPNOTSUPP;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *type)
+{
+ return trivfs_S_io_select (cred, reply, reply_type, type);
+}
diff --git a/trans/fwd.c b/trans/fwd.c
new file mode 100644
index 00000000..f30aad1a
--- /dev/null
+++ b/trans/fwd.c
@@ -0,0 +1,51 @@
+/* A translator to start up a central translation server
+
+ Note: most translators that use a central server will look for the server
+ and forward the request to the server if they find one, otherwise doing
+ the translation themselves.
+
+ Copyright (C) 1995, 1998, 1999 Free Software Foundation, Inc.
+
+ Written by Miles Bader <miles@gnu.ai.mit.edu>
+
+ 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 <error.h>
+#include <stdio.h>
+#include <hurd/fshelp.h>
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ if (argc < 2 || *argv[1] == '-')
+ {
+ fprintf (stderr, "Usage: %s SERVER [TRANS_NAME [TRANS_ARG...]]\n",
+ program_invocation_name);
+ return 1;
+ }
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "must be started as a translator");
+
+ err = fshelp_delegate_translation (argv[1], bootstrap, argv + 2);
+ if (err)
+ error (3, err, "%s", argv[1]);
+
+ return 0;
+}
diff --git a/trans/hello-mt.c b/trans/hello-mt.c
new file mode 100644
index 00000000..ba9329a7
--- /dev/null
+++ b/trans/hello-mt.c
@@ -0,0 +1,332 @@
+/* hello-mt.c - A trivial single-file translator, multithreaded version
+ Copyright (C) 1998,99,2001,02,2006 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 */
+
+#define _GNU_SOURCE 1
+
+#include <hurd/trivfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <pthread.h>
+
+#include <version.h>
+
+#include "libtrivfs/trivfs_io_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (hello-mt);
+
+/* The message we return when we are read. */
+static const char hello[] = "Hello, world!\n";
+static char *contents = (char *) hello;
+static size_t contents_len = sizeof hello - 1;
+
+/* This lock protects access to contents and contents_len. */
+static pthread_rwlock_t contents_lock;
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_allow_open = O_READ;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+/* NOTE: This example is not robust: it is possible to trigger some
+ assertion failures because we don't implement the following:
+
+ $ cd /src/hurd/libtrivfs
+ $ grep -l 'assert.*!trivfs_support_read' *.c |
+ xargs grep '^trivfs_S_' | sed 's/^[^:]*:\([^ ]*\).*$/\1/'
+ trivfs_S_io_get_openmodes
+ trivfs_S_io_clear_some_openmodes
+ trivfs_S_io_set_some_openmodes
+ trivfs_S_io_set_all_openmodes
+ trivfs_S_io_readable
+ trivfs_S_io_select
+ $
+
+ For that reason, you should run this as an active translator
+ `settrans -ac testnode /path/to/thello' so that you can see the
+ error messages when they appear. */
+
+/* A hook for us to keep track of the file descriptor state. */
+struct open
+{
+ pthread_mutex_t lock;
+ off_t offs;
+};
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ /* Mark the node as a read-only plain file. */
+ st->st_mode &= ~(S_IFMT | ALLPERMS);
+ st->st_mode |= (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH);
+ st->st_size = contents_len; /* No need to lock for reading one word. */
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ exit (0);
+}
+
+
+static error_t
+open_hook (struct trivfs_peropen *peropen)
+{
+ struct open *op = malloc (sizeof (struct open));
+ if (op == NULL)
+ return ENOMEM;
+
+ /* Initialize the offset. */
+ op->offs = 0;
+ pthread_mutex_init (&op->lock, NULL);
+ peropen->hook = op;
+ return 0;
+}
+
+
+static void
+close_hook (struct trivfs_peropen *peropen)
+{
+ struct open *op = peropen->hook;
+
+ pthread_mutex_destroy (&op->lock);
+ free (op);
+}
+
+
+/* Read data from an IO object. If offset is -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMOUNT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ struct open *op;
+
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+
+ op = cred->po->hook;
+
+ pthread_mutex_lock (&op->lock);
+
+ /* Get the offset. */
+ if (offs == -1)
+ offs = op->offs;
+
+ pthread_rwlock_rdlock (&contents_lock);
+
+ /* Prune the amount they want to read. */
+ if (offs > contents_len)
+ offs = contents_len;
+ if (offs + amount > contents_len)
+ amount = contents_len - offs;
+
+ if (amount > 0)
+ {
+ /* Possibly allocate a new buffer. */
+ if (*data_len < amount)
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ {
+ pthread_mutex_unlock (&op->lock);
+ pthread_rwlock_unlock (&contents_lock);
+ return ENOMEM;
+ }
+
+ /* Copy the constant data into the buffer. */
+ memcpy ((char *) *data, contents + offs, amount);
+
+ /* Update the saved offset. */
+ op->offs += amount;
+ }
+
+ pthread_mutex_unlock (&op->lock);
+
+ pthread_rwlock_unlock (&contents_lock);
+
+ *data_len = amount;
+ return 0;
+}
+
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ struct open *op;
+ error_t err = 0;
+ if (! cred)
+ return EOPNOTSUPP;
+
+ op = cred->po->hook;
+
+ pthread_mutex_lock (&op->lock);
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offs += op->offs;
+ goto check;
+ case SEEK_END:
+ offs += contents_len;
+ case SEEK_SET:
+ check:
+ if (offs >= 0)
+ {
+ *new_offs = op->offs = offs;
+ break;
+ }
+ default:
+ err = EINVAL;
+ }
+
+ pthread_mutex_unlock (&op->lock);
+
+ return err;
+}
+
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
+
+/* If this variable is set, it is called every time a peropen structure
+ is about to be destroyed. */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+
+/* Options processing. We accept the same options on the command line
+ and from fsys_set_options. */
+
+static const struct argp_option options[] =
+{
+ {"contents", 'c', "STRING", 0, "Specify the contents of the virtual file"},
+ {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ switch (opt)
+ {
+ default:
+ return ARGP_ERR_UNKNOWN;
+ case ARGP_KEY_INIT:
+ case ARGP_KEY_SUCCESS:
+ case ARGP_KEY_ERROR:
+ break;
+
+ case 'c':
+ {
+ char *new = strdup (arg);
+ if (new == NULL)
+ return ENOMEM;
+ pthread_rwlock_wrlock (&contents_lock);
+ if (contents != hello)
+ free (contents);
+ contents = new;
+ contents_len = strlen (new);
+ pthread_rwlock_unlock (&contents_lock);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* This will be called from libtrivfs to help construct the answer
+ to an fsys_get_options RPC. */
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+ char *opt;
+
+ pthread_rwlock_rdlock (&contents_lock);
+ err = asprintf (&opt, "--contents=%s", contents) < 0 ? ENOMEM : 0;
+ pthread_rwlock_unlock (&contents_lock);
+
+ if (!err)
+ {
+ err = argz_add (argz, argz_len, opt);
+ free (opt);
+ }
+
+ return err;
+}
+
+static struct argp hello_argp =
+{ options, parse_opt, 0,
+ "A multi-threaded translator providing a warm greeting." };
+
+/* Setting this variable makes libtrivfs use our argp to
+ parse options passed in an fsys_set_options RPC. */
+struct argp *trivfs_runtime_argp = &hello_argp;
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ /* Initialize the lock that will protect CONTENTS and CONTENTS_LEN.
+ We must do this before argp_parse, because parse_opt (above) will
+ use the lock. */
+ pthread_rwlock_init (&contents_lock, NULL);
+
+ /* We use the same argp for options available at startup
+ as for options we'll accept in an fsys_set_options RPC. */
+ argp_parse (&hello_argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "trivfs_startup");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (fsys->pi.bucket, trivfs_demuxer,
+ 10 * 1000, /* idle thread */
+ 10 * 60 * 1000, /* idle server */
+ 0);
+
+ return 0;
+}
diff --git a/trans/hello.c b/trans/hello.c
new file mode 100644
index 00000000..4e88c609
--- /dev/null
+++ b/trans/hello.c
@@ -0,0 +1,293 @@
+/* hello.c - A trivial single-file translator
+ Copyright (C) 1998,1999,2001,02,2006 Free Software Foundation, Inc.
+ Gordon Matzigkeit <gord@fig.org>, 1999
+ 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 */
+
+#define _GNU_SOURCE 1
+
+#include <hurd/trivfs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include <version.h>
+
+#include "libtrivfs/trivfs_io_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (hello);
+
+/* The message we return when we are read. */
+static const char hello[] = "Hello, world!\n";
+static char *contents = (char *) hello;
+static size_t contents_len = sizeof hello - 1;
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_allow_open = O_READ;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+/* NOTE: This example is not robust: it is possible to trigger some
+ assertion failures because we don't implement the following:
+
+ $ cd /src/hurd/libtrivfs
+ $ grep -l 'assert.*!trivfs_support_read' *.c |
+ xargs grep '^trivfs_S_' | sed 's/^[^:]*:\([^ ]*\).*$/\1/'
+ trivfs_S_io_get_openmodes
+ trivfs_S_io_clear_some_openmodes
+ trivfs_S_io_set_some_openmodes
+ trivfs_S_io_set_all_openmodes
+ trivfs_S_io_readable
+ trivfs_S_io_select
+ $
+
+ For that reason, you should run this as an active translator
+ `settrans -ac testnode /path/to/thello' so that you can see the
+ error messages when they appear. */
+
+/* A hook for us to keep track of the file descriptor state. */
+struct open
+{
+ off_t offs;
+};
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ /* Mark the node as a read-only plain file. */
+ st->st_mode &= ~(S_IFMT | ALLPERMS);
+ st->st_mode |= (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH);
+ st->st_size = contents_len;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ exit (0);
+}
+
+
+static error_t
+open_hook (struct trivfs_peropen *peropen)
+{
+ struct open *op = malloc (sizeof (struct open));
+ if (op == NULL)
+ return ENOMEM;
+
+ /* Initialize the offset. */
+ op->offs = 0;
+ peropen->hook = op;
+ return 0;
+}
+
+
+static void
+close_hook (struct trivfs_peropen *peropen)
+{
+ free (peropen->hook);
+}
+
+
+/* Read data from an IO object. If offset is -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMOUNT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ struct open *op;
+
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+ else if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+
+ /* Get the offset. */
+ op = cred->po->hook;
+ if (offs == -1)
+ offs = op->offs;
+
+ /* Prune the amount they want to read. */
+ if (offs > contents_len)
+ offs = contents_len;
+ if (offs + amount > contents_len)
+ amount = contents_len - offs;
+
+ if (amount > 0)
+ {
+ /* Possibly allocate a new buffer. */
+ if (*data_len < amount)
+ {
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ return ENOMEM;
+ }
+
+ /* Copy the constant data into the buffer. */
+ memcpy ((char *) *data, contents + offs, amount);
+
+ /* Update the saved offset. */
+ op->offs += amount;
+ }
+
+ *data_len = amount;
+ return 0;
+}
+
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ struct open *op;
+ error_t err = 0;
+ if (! cred)
+ return EOPNOTSUPP;
+
+ op = cred->po->hook;
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offs += op->offs;
+ goto check;
+ case SEEK_END:
+ offs += contents_len;
+ case SEEK_SET:
+ check:
+ if (offs >= 0)
+ {
+ *new_offs = op->offs = offs;
+ break;
+ }
+ default:
+ err = EINVAL;
+ }
+
+ return err;
+}
+
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
+
+/* If this variable is set, it is called every time a peropen structure
+ is about to be destroyed. */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+
+/* Options processing. We accept the same options on the command line
+ and from fsys_set_options. */
+
+static const struct argp_option options[] =
+{
+ {"contents", 'c', "STRING", 0, "Specify the contents of the virtual file"},
+ {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ switch (opt)
+ {
+ default:
+ return ARGP_ERR_UNKNOWN;
+ case ARGP_KEY_INIT:
+ case ARGP_KEY_SUCCESS:
+ case ARGP_KEY_ERROR:
+ break;
+
+ case 'c':
+ {
+ char *new = strdup (arg);
+ if (new == NULL)
+ return ENOMEM;
+ if (contents != hello)
+ free (contents);
+ contents = new;
+ contents_len = strlen (new);
+ break;
+ }
+ }
+ return 0;
+}
+
+/* This will be called from libtrivfs to help construct the answer
+ to an fsys_get_options RPC. */
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+ char *opt;
+
+ if (asprintf (&opt, "--contents=%s", contents) < 0)
+ return ENOMEM;
+
+ err = argz_add (argz, argz_len, opt);
+
+ free (opt);
+
+ return err;
+}
+
+static struct argp hello_argp =
+{ options, parse_opt, 0, "A translator providing a warm greeting." };
+
+/* Setting this variable makes libtrivfs use our argp to
+ parse options passed in an fsys_set_options RPC. */
+struct argp *trivfs_runtime_argp = &hello_argp;
+
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ /* We use the same argp for options available at startup
+ as for options we'll accept in an fsys_set_options RPC. */
+ argp_parse (&hello_argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "trivfs_startup");
+
+ /* Launch. */
+ ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer, 0);
+
+ return 0;
+}
diff --git a/trans/ifsock.c b/trans/ifsock.c
new file mode 100644
index 00000000..4ed65898
--- /dev/null
+++ b/trans/ifsock.c
@@ -0,0 +1,152 @@
+/* Server for S_IFSOCK nodes
+ Copyright (C) 1994, 1995, 2001, 02, 2006 Free Software Foundation
+
+ 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 <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/paths.h>
+#include <sys/socket.h>
+#include <hurd/socket.h>
+#include <hurd/fsys.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <error.h>
+#include <fcntl.h>
+#include <argp.h>
+
+#include <sys/cdefs.h>
+#ifndef __XSTRING /* Could / should (?) be provided by glibc. */
+#define __XSTRING(x) __STRING(x) /* Expand x, then stringify. */
+#endif
+
+#include <version.h>
+
+#include "ifsock_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (ifsock);
+
+static const char doc[] = "A translator to provide Unix domain sockets."
+"\vThis translator acts as a hook for Unix domain sockets."
+" The pflocal translator on " _SERVERS_SOCKET "/" __XSTRING(PF_LOCAL)
+" implements the sockets.";
+
+mach_port_t address_port;
+
+struct port_class *control_class;
+struct port_class *node_class;
+struct port_bucket *port_bucket;
+
+int trivfs_fstype = FSTYPE_IFSOCK;
+int trivfs_fsid = 0; /* ??? */
+
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = 0;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+int
+demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int ifsock_server (mach_msg_header_t *, mach_msg_header_t *);
+ return trivfs_demuxer (inp, outp) || ifsock_server (inp, outp);
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t pflocal;
+ mach_port_t bootstrap;
+ char buf[512];
+ const struct argp argp = { 0, 0, 0, doc };
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ control_class = ports_create_class (trivfs_clean_cntl, 0);
+ node_class = ports_create_class (trivfs_clean_protid, 0);
+ port_bucket = ports_create_bucket ();
+ trivfs_protid_portclasses[0] = node_class;
+ trivfs_cntl_portclasses[0] = control_class;
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(1, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, control_class, port_bucket,
+ node_class, port_bucket, NULL);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error(2, err, "Contacting parent");
+
+ /* Try and connect to the pflocal server */
+ sprintf (buf, "%s/%d", _SERVERS_SOCKET, PF_LOCAL);
+ pflocal = file_name_lookup (buf, 0, 0);
+
+ if (pflocal == MACH_PORT_NULL)
+ address_port = MACH_PORT_NULL;
+ else
+ {
+ err = socket_fabricate_address (pflocal, AF_LOCAL, &address_port);
+ if (err)
+ address_port = MACH_PORT_NULL;
+ mach_port_deallocate (mach_task_self (), pflocal);
+ }
+
+ /* Launch. */
+ ports_manage_port_operations_one_thread (port_bucket, demuxer, 0);
+ return 0;
+}
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_mode = (st->st_mode & ~S_IFMT) | S_IFSOCK;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ exit (0);
+}
+
+error_t
+S_ifsock_getsockaddr (struct trivfs_protid *cred,
+ mach_port_t *address)
+{
+ int perms;
+ error_t err;
+
+ if (!cred
+ || cred->pi.bucket != port_bucket
+ || cred->pi.class != node_class)
+ return EOPNOTSUPP;
+
+ err = file_check_access (cred->realnode, &perms);
+ if (!err && !(perms & O_READ))
+ err = EACCES;
+
+ if (!err)
+ *address = address_port;
+ return err;
+}
diff --git a/trans/magic.c b/trans/magic.c
new file mode 100644
index 00000000..58084838
--- /dev/null
+++ b/trans/magic.c
@@ -0,0 +1,567 @@
+/* A translator for returning FS_RETRY_MAGIC strings.
+
+ Copyright (C) 1999,2001,02, 03 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/fshelp.h>
+#include <hurd/fsys.h>
+#include <version.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <error.h>
+#include <string.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <argp.h>
+#include <argz.h>
+#include <assert.h>
+
+#include "fsys_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (magic);
+
+/* This structure has all the state about one filesystem.
+ It hangs off trivfs_control->hook. */
+struct magic
+{
+ /* We chain all filesystems together so we can tell easily when they are
+ all unused. */
+ struct trivfs_control *next;
+
+ /* The magic string we return for lookups. */
+ char *magic;
+
+ int directory; /* --directory flag */
+
+ /* Pre-fab contents of dummy directory for dir_readdir.
+ Set up only under --directory. */
+ void *dirbuf;
+ size_t dirbufsize;
+
+ unsigned int nusers; /* Count of users, only with --directory. */
+};
+
+static inline void
+free_magic (struct magic *m)
+{
+ free (m->magic);
+ if (m->dirbuf)
+ munmap (m->dirbuf, m->dirbufsize);
+ free (m);
+}
+
+static struct trivfs_control *all_fsys;
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_DEV;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ struct magic *const m = cred->po->cntl->hook;
+
+ st->st_size = m->dirbufsize;
+ st->st_blocks = getpagesize () / S_BLKSIZE;
+
+ st->st_mode = ((st->st_mode & ~S_IFMT & ~ALLPERMS)
+ | S_IFDIR | S_IXUSR|S_IXGRP|S_IXOTH
+ | (st->st_mode & (S_IRUSR|S_IRGRP|S_IROTH)));
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ struct magic *const m = fsys->hook;
+
+ /* We are single-threaded, so no fancy stuff is needed here. */
+
+ if (m->nusers > 0 && !(flags & FSYS_GOAWAY_FORCE))
+ return EBUSY;
+
+ /* No more communication with the parent filesystem.
+ This running RPC should now be the only ref keeping FSYS alive. */
+ ports_destroy_right (fsys);
+ return 0;
+}
+
+
+/* Clean pointers in a struct trivfs_control when its last reference
+ vanishes before it's freed. This overrides the libtrivfs version
+ so we can clean up our hook data. */
+void
+trivfs_clean_cntl (void *arg)
+{
+ struct trivfs_control *cntl = arg;
+ struct magic *const m = cntl->hook;
+
+ /* Remove us from the list of all filesystems. */
+ struct trivfs_control **lastp = &all_fsys;
+ while (*lastp != cntl)
+ lastp = &((struct magic *) (*lastp)->hook)->next;
+ *lastp = m->next;
+
+ if (all_fsys == 0)
+ /* Nothing more to do in this life. */
+ exit (0);
+
+ mach_port_destroy (mach_task_self (), cntl->filesys_id);
+ mach_port_destroy (mach_task_self (), cntl->file_id);
+ mach_port_deallocate (mach_task_self (), cntl->underlying);
+
+ free_magic (m);
+}
+
+/* This hook is used when running without --directory;
+ it circumvents basically all the trivfs machinery. */
+
+static error_t
+magic_getroot (struct trivfs_control *cntl,
+ mach_port_t reply_port,
+ mach_msg_type_name_t reply_port_type,
+ mach_port_t dotdot,
+ uid_t *uids, u_int nuids, uid_t *gids, u_int ngids,
+ int flags,
+ retry_type *do_retry, char *retry_name,
+ mach_port_t *node, mach_msg_type_name_t *node_type)
+{
+ error_t err;
+ struct magic *const m = cntl->hook;
+
+ if (m->directory)
+ return EAGAIN; /* Do normal trivfs getroot processing. */
+
+ strcpy (retry_name, m->magic);
+ *do_retry = FS_RETRY_MAGICAL;
+ *node = MACH_PORT_NULL;
+ *node_type = MACH_MSG_TYPE_COPY_SEND;
+
+ err = mach_port_deallocate (mach_task_self (), dotdot);
+ assert_perror (err);
+
+ return 0;
+}
+
+/* This hook is used when running with --directory, when
+ we do use all the normal trivfs machinery. We just use
+ the normal trivfs open, but then stash the DOTDOT port
+ in the trivfs_peropen. */
+
+static error_t
+magic_open (struct trivfs_control *cntl,
+ struct iouser *user,
+ mach_port_t dotdot,
+ int flags,
+ mach_port_t realnode,
+ struct trivfs_protid **cred)
+{
+ error_t err = trivfs_open (cntl, user, flags, realnode, cred);
+ if (!err)
+ {
+ /* We consume the reference for DOTDOT. */
+ (*cred)->po->hook = (void *) dotdot;
+ struct magic *const m = cntl->hook;
+ m->nusers++;
+ }
+ return err;
+}
+
+static void
+magic_peropen_destroy (struct trivfs_peropen *po)
+{
+ mach_port_deallocate (mach_task_self (), (mach_port_t) po->hook);
+}
+
+
+/* We have this hook only for simple tracking of the live user ports. */
+static void
+magic_protid_destroy (struct trivfs_protid *cred)
+{
+ struct magic *const m = cred->po->cntl->hook;
+ m->nusers--;
+}
+
+
+/* Do a directory lookup. */
+
+error_t
+trivfs_S_dir_lookup (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *name,
+ int flags,
+ mode_t mode,
+ retry_type *retry_type,
+ char *retry_name,
+ mach_port_t *retrypt,
+ mach_msg_type_name_t *retrypt_type)
+{
+ int perms;
+ error_t err;
+ struct trivfs_protid *newcred;
+ mach_port_t dotdot;
+ struct iouser *user;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (name[0] != '\0')
+ {
+ struct magic *const m = cred->po->cntl->hook;
+
+ if (!m->directory)
+ return ENOTDIR;
+
+ /* We have a real lookup in the dummy directory.
+ Handle `.' and `..' specially, and anything else
+ gets redirected to the magical retry. */
+
+ while (*name == '/')
+ ++name;
+ while (!strncmp (name, "./", 2))
+ {
+ name += 2;
+ while (*name == '/')
+ ++name;
+ }
+
+ if (!strcmp (name, "..") || !strncmp (name, "../", 3))
+ {
+ name += 2;
+ while (*name == '/')
+ ++name;
+ strcpy (retry_name, name);
+ *retry_type = FS_RETRY_REAUTH;
+ *retrypt = (mach_port_t) cred->po->hook;
+ *retrypt_type = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+ }
+ else if (name[0] != '\0' && strcmp (name, "."))
+ {
+ if (m->magic == 0)
+ strcpy (retry_name, name);
+ else
+ {
+ char *p = stpcpy (retry_name, m->magic);
+ *p++ = '/';
+ strcpy (p, name);
+ }
+ *retry_type = FS_RETRY_MAGICAL;
+ *retrypt = MACH_PORT_NULL;
+ *retrypt_type = MACH_MSG_TYPE_COPY_SEND;
+ return 0;
+ }
+ }
+
+ /* This is a null-pathname "reopen" call; do the right thing. */
+
+ /* Burn off flags we don't actually implement */
+ flags &= O_HURD;
+ flags &= ~(O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS);
+
+ /* Validate permissions */
+ if (! trivfs_check_access_hook)
+ file_check_access (cred->realnode, &perms);
+ else
+ (*trivfs_check_access_hook) (cred->po->cntl, cred->user,
+ cred->realnode, &perms);
+ if ((flags & (O_READ|O_WRITE|O_EXEC) & perms)
+ != (flags & (O_READ|O_WRITE|O_EXEC)))
+ return EACCES;
+
+ /* Execute the open */
+
+ dotdot = (mach_port_t) cred->po->hook;
+ err = iohelp_dup_iouser (&user, cred->user);
+ if (err)
+ return err;
+ err = magic_open (cred->po->cntl, user, dotdot, flags,
+ cred->realnode, &newcred);
+ if (err)
+ {
+ iohelp_free_iouser (user);
+ return err;
+ }
+ err = mach_port_mod_refs (mach_task_self (), dotdot,
+ MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+ err = mach_port_mod_refs (mach_task_self (), cred->realnode,
+ MACH_PORT_RIGHT_SEND, +1);
+ assert_perror (err);
+
+ *retry_type = FS_RETRY_NORMAL;
+ *retry_name = '\0';
+ *retrypt = ports_get_right (newcred);
+ *retrypt_type = MACH_MSG_TYPE_MAKE_SEND;
+ ports_port_deref (newcred);
+ return 0;
+}
+
+error_t
+trivfs_S_dir_readdir (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data,
+ size_t *datalen,
+ boolean_t *data_dealloc,
+ int entry,
+ int nentries,
+ vm_size_t bufsiz,
+ int *amount)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ struct magic *const m = cred->po->cntl->hook;
+
+ if (entry > 0)
+ {
+ void *p;
+ int i;
+ i = 0;
+ for (p = m->dirbuf; p < m->dirbuf + m->dirbufsize;
+ p += ((struct dirent *) p)->d_reclen)
+ if (i++ == entry)
+ break;
+ *data = p;
+ *datalen = m->dirbuf + m->dirbufsize - p;
+ *amount = 2 - entry;
+ }
+ else
+ {
+ *data = m->dirbuf;
+ *datalen = m->dirbufsize;
+ *amount = 2;
+ }
+
+ *data_dealloc = 0;
+ return 0;
+}
+
+
+#include <hurd/paths.h>
+#define _SERVERS_MAGIC _SERVERS "magic"
+
+/* To whom should we try to delegate on startup? */
+static const char *delegate = _SERVERS_MAGIC;
+
+static const struct argp_option options[] =
+{
+ {"directory", 'd', 0, 0, "Provide virtual (empty) directory node"},
+ {"use-server", 'U', "NAME", 0,
+ "Delegate to server NAME instead of " _SERVERS_MAGIC},
+ {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ struct magic *const m = state->input;
+
+ switch (opt)
+ {
+ case 'U':
+ /* This is only valid for the startup options, not delegates. */
+ if (all_fsys != 0)
+ return EINVAL;
+ delegate = arg;
+ return 0;
+
+ case 'd':
+ case ARGP_KEY_NO_ARGS:
+ m->directory = 1;
+ return 0;
+
+ case ARGP_KEY_ARG:
+ if (m->magic != 0)
+ {
+ argp_usage (state);
+ return EINVAL;
+ }
+ m->magic = strdup (arg);
+ return m->magic == 0 ? ENOMEM : 0;
+
+ case ARGP_KEY_SUCCESS:
+ if (m->directory)
+ {
+ inline struct dirent *add (struct dirent *d, const char *name)
+ {
+ d->d_fileno = 2; /* random */
+ d->d_type = DT_DIR;
+ d->d_namlen = strlen (name);
+ strcpy (d->d_name, name);
+ d->d_name[d->d_namlen] = '\0';
+ d->d_reclen = &d->d_name[d->d_namlen + 1] - (char *) d;
+ d->d_reclen = ((d->d_reclen + __alignof (struct dirent) - 1)
+ & ~(__alignof (struct dirent) - 1));
+ return (struct dirent *) ((char *) d + d->d_reclen);
+ }
+ struct dirent *d;
+ m->dirbuf = mmap (0, getpagesize (), PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ d = add (m->dirbuf, ".");
+ d = add (d, "..");
+ m->dirbufsize = (char *) d - (char *) m->dirbuf;
+ }
+ return 0;
+ }
+
+ return ARGP_ERR_UNKNOWN;
+}
+
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ struct magic *const m = fsys->hook;
+ return ((m->directory ? argz_add (argz, argz_len, "--directory") : 0)
+ ?: (m->magic ? argz_add (argz, argz_len, m->magic) : 0));
+}
+
+static struct argp argp =
+ {
+ options, parse_opt, "MAGIC",
+ "A translator that returns the magic retry result MAGIC."
+ };
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+ struct magic *m = calloc (1, sizeof *m);
+
+ argp_parse (&argp, argc, argv, 0, 0, m);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ if (delegate != 0)
+ {
+ /* First, try to have the canonical server sitting on /servers/magic
+ take over for us. */
+ err = fshelp_delegate_translation (delegate, bootstrap, argv);
+ if (err == 0)
+ return 0;
+ }
+
+ /* Nope, we are doing it ourselves. */
+
+ trivfs_getroot_hook = &magic_getroot;
+ trivfs_open_hook = &magic_open;
+ trivfs_protid_destroy_hook = &magic_protid_destroy;
+ if (m->directory)
+ trivfs_peropen_destroy_hook = &magic_peropen_destroy;
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "Contacting parent");
+ fsys->hook = m;
+ all_fsys = fsys;
+
+ /* Launch. */
+ while (1)
+ {
+ ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer,
+ 10 * 60 * 1000);
+
+ /* That returns when 10 minutes pass without an RPC. Try shutting down
+ as if sent fsys_goaway; if we have any users who need us to stay
+ around, this returns EBUSY and we loop to service more RPCs. */
+
+ struct trivfs_control *fs = all_fsys;
+ do
+ {
+ struct magic *const m = fs->hook;
+ struct trivfs_control *const next = m->next;
+ trivfs_goaway (fs, 0);
+ fs = next;
+ } while (fs != 0);
+ }
+
+ return 0;
+}
+
+
+/* Handle delegated filesystems. */
+error_t
+trivfs_S_fsys_forward (mach_port_t server,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t requestor,
+ char *argz, size_t argz_len)
+{
+ struct trivfs_protid *cred
+ = ports_lookup_port (all_fsys->pi.bucket, server,
+ trivfs_protid_portclasses[0]);
+ if (!cred)
+ return EOPNOTSUPP;
+ ports_port_deref (cred);
+
+ /* Allocate a new structure for parameters, and parse the arguments
+ to fill it in. */
+ struct magic *m = calloc (1, sizeof *m);
+ if (!m)
+ return ENOMEM;
+
+ int argc = argz_count (argz, argz_len);
+ char *argv[argc + 1];
+ argz_extract (argz, argz_len, argv);
+ error_t err = argp_parse (&argp, argc, argv,
+ ARGP_NO_ERRS | ARGP_NO_HELP, 0, m);
+ if (err)
+ {
+ free_magic (m);
+ return err;
+ }
+
+ /* Now we are ready to start up the filesystem. Contact the parent. */
+ struct trivfs_control *fsys;
+ err = trivfs_startup (requestor, 0,
+ trivfs_cntl_portclasses[0], all_fsys->pi.bucket,
+ trivfs_protid_portclasses[0], all_fsys->pi.bucket,
+ &fsys);
+ if (err)
+ {
+ free_magic (m);
+ return err;
+ }
+ mach_port_deallocate (mach_task_self (), requestor);
+
+ /* The new filesystem is all hooked up.
+ Put it on the list of all filesystems we are serving. */
+ m->next = all_fsys;
+ fsys->hook = m;
+ all_fsys = fsys;
+
+ return 0;
+}
diff --git a/trans/mtab.c b/trans/mtab.c
new file mode 100644
index 00000000..df03b1d3
--- /dev/null
+++ b/trans/mtab.c
@@ -0,0 +1,882 @@
+/* This is an mtab translator.
+
+ Copyright (C) 2013,14 Free Software Foundation, Inc.
+
+ Written by Justus Winter <4winter@informatik.uni-hamburg.de>
+
+ 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 the GNU Hurd. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <fcntl.h>
+#include <hurd.h>
+#include <hurd/trivfs.h>
+#include <inttypes.h>
+#include <mntent.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <version.h>
+
+#include "libtrivfs/trivfs_io_S.h"
+#include "fs_U.h"
+
+static char *target_path = NULL;
+static int insecure = 0;
+static int all_translators = 0;
+
+/* Our control port. */
+struct trivfs_control *control;
+
+/* These kind of objects are created and populated in the open_hook.
+ They keep track of the content and file position of the client. */
+struct mtab
+{
+ pthread_mutex_t lock;
+ char *contents;
+ size_t contents_len;
+ off_t offs;
+};
+
+const char *argp_program_version = STANDARD_HURD_VERSION (mtab);
+
+static const struct argp_option options[] =
+{
+ {"insecure", 'I', 0, 0,
+ "Follow translators not bound to nodes owned by you or root"},
+ {"all-translators", 'A', 0, 0,
+ "List all translators, even those that are probably not "
+ "filesystem translators"},
+ {}
+};
+
+/* Parse a command line option. */
+error_t parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'I':
+ insecure = 1;
+ break;
+
+ case 'A':
+ all_translators = 1;
+ break;
+
+ case ARGP_KEY_ARG:
+ target_path = realpath (arg, NULL);
+ if (! target_path)
+ argp_error (state, "Error while canonicalizing path");
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_usage (state);
+ return EINVAL;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static struct argp argp =
+ {
+ options,
+ parse_opt,
+ "TARGET\tFile name of a node with an active translator",
+ "A translator providing mtab compatible information about active "
+ "and passive translators below TARGET.",
+ };
+
+/* This will be called from libtrivfs to help construct the answer
+ to an fsys_get_options RPC. */
+error_t
+trivfs_append_args (struct trivfs_control *fsys,
+ char **argz, size_t *argz_len)
+{
+ error_t err;
+
+ if (insecure)
+ {
+ err = argz_add (argz, argz_len, target_path);
+ if (err)
+ return err;
+ }
+
+ err = argz_add (argz, argz_len, target_path);
+ return err;
+}
+
+/* Setting this variable makes libtrivfs use our argp to
+ parse options passed in an fsys_set_options RPC. */
+struct argp *trivfs_runtime_argp = &argp;
+
+/* 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;
+}
+
+/* Check if the given struct stat describes a node owned by the
+ current user. */
+int
+is_owner (io_statbuf_t *st)
+{
+ int found = 0;
+ for (size_t i = 0; i < uids_len; i++)
+ if (uids[i] == st->st_uid)
+ {
+ found = 1;
+ break;
+ }
+
+ if (! found)
+ return 0;
+
+ found = 0;
+ for (size_t i = 0; i < gids_len; i++)
+ if (gids[i] == st->st_gid)
+ {
+ found = 1;
+ break;
+ }
+
+ return found;
+}
+
+error_t
+mtab_populate (struct mtab *mtab, const char *path, int insecure);
+
+error_t
+argz_add_device (char **options, size_t *options_len, const char *device);
+
+error_t
+map_device_to_path (const char *device, char **path);
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+
+ err = argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+ if (err)
+ error (1, err, "argument parsing");
+
+ err = get_credentials ();
+ if (err)
+ error (2, err, "getting credentials");
+
+ mach_port_t bootstrap;
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap != MACH_PORT_NULL)
+ {
+ /* Started as a translator. */
+
+ auth_t nullauth;
+ err = auth_makeauth (getauth (),
+ NULL, MACH_MSG_TYPE_COPY_SEND, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ &nullauth);
+ if (err)
+ error (3, err, "dropping credentials");
+
+ err = setauth (nullauth);
+ if (err)
+ error (3, err, "dropping credentials");
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &control);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (4, err, "trivfs_startup");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (control->pi.bucket,
+ trivfs_demuxer,
+ /* idle thread timeout */
+ 30 * 1000,
+ /* idle server timeout */
+ 0,
+ NULL);
+ }
+ else
+ {
+ /* One-shot mode. */
+ struct mtab mtab = { .lock = PTHREAD_MUTEX_INITIALIZER };
+ err = mtab_populate (&mtab, target_path, insecure);
+ if (err)
+ error (5, err, "%s", target_path);
+
+ if (mtab.contents)
+ printf ("%s", mtab.contents);
+ }
+
+ return 0;
+}
+
+error_t
+mtab_add_entry (struct mtab *mtab, const char *entry, size_t length)
+{
+ char *p = realloc (mtab->contents, mtab->contents_len + length + 1);
+ if (! p)
+ return ENOMEM;
+
+ memcpy (&p[mtab->contents_len], entry, length);
+
+ mtab->contents = p;
+ mtab->contents_len += length;
+
+ /* Zero-terminate contents so that we can also interpret it as
+ string. */
+ mtab->contents[mtab->contents_len] = '\0';
+
+ return 0;
+}
+
+/* Check whether the given NODE is a directory on a filesystem
+ translator. */
+static boolean_t
+is_filesystem_translator (file_t node)
+{
+ error_t err;
+ char *data = NULL;
+ size_t datacnt = 0;
+ int amount;
+ err = dir_readdir (node, &data, &datacnt, 0, 1, 0, &amount);
+ if (data != NULL && datacnt > 0)
+ vm_deallocate (mach_task_self (), (vm_address_t) data, datacnt);
+
+ /* Filesystem translators return either no error, or, if NODE has
+ not been looked up with O_READ, EBADF to dir_readdir
+ requests. */
+ switch (err)
+ {
+ case 0:
+ case EBADF:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/* Populates the given MTAB object with the information for PATH. If
+ INSECURE is given, also follow translators bound to nodes not owned
+ by root or the current user. */
+/* XXX split up */
+error_t
+mtab_populate (struct mtab *mtab, const char *path, int insecure)
+{
+ error_t err = 0;
+
+ /* These resources are freed in the epilogue. */
+ file_t node = MACH_PORT_NULL;
+ char *argz = NULL;
+ size_t argz_len = 0;
+ char **argv = NULL;
+ char *type = NULL;
+ char *options = NULL;
+ size_t options_len = 0;
+ char *src = NULL;
+ char *entry = NULL;
+ size_t entry_len = 0;
+ char *children = NULL;
+ size_t children_len = 0;
+
+ if (! insecure)
+ {
+ /* Get the underlying node. */
+ node = file_name_lookup (path, O_NOTRANS, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ err = errno;
+ goto errout;
+ }
+
+ /* Check who owns the node the translator is bound to. */
+ io_statbuf_t st;
+ err = io_stat (node, &st);
+ if (err)
+ goto errout;
+
+ if (st.st_uid != 0 && st.st_gid != 0 && ! is_owner (&st))
+ {
+ err = EPERM;
+ goto errout;
+ }
+
+ mach_port_deallocate (mach_task_self (), node);
+ }
+
+ /* (Re-)do the lookup without O_NOTRANS to get the root node. */
+ node = file_name_lookup (path, 0, 0666);
+ if (node == MACH_PORT_NULL)
+ {
+ err = errno;
+ goto errout;
+ }
+
+ if (! (all_translators || is_filesystem_translator (node)))
+ {
+ err = 0;
+ goto errout;
+ }
+
+ /* Query its options. */
+ err = file_get_fs_options (node, &argz, &argz_len);
+ if (err)
+ {
+ if (err == EOPNOTSUPP)
+ err = 0; /* There's not much we could do then. */
+
+ goto errout;
+ }
+
+ size_t count = argz_count (argz, argz_len);
+ argv = malloc ((count + 1) * sizeof (char *));
+ if (! argv)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ argz_extract (argz, argz_len, argv);
+
+ type = strdup (argv[0]);
+ if (! type)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ for (int i = 1; i < count - 1; i++)
+ {
+ char *v = argv[i];
+
+ if (*v == '-')
+ v++;
+ if (*v == '-')
+ v++;
+
+ err = argz_add (&options, &options_len, v);
+ if (err)
+ goto errout;
+ }
+
+ err = argz_add_device (&options, &options_len, argv[count - 1]);
+ if (err)
+ goto errout;
+
+ argz_stringify (options, options_len, ',');
+
+ string_t source;
+ err = file_get_source (node, source);
+ if (err)
+ goto errout;
+
+ /* Guess based on the last argument. */
+ err = map_device_to_path (source, &src);
+ if (err)
+ goto errout;
+
+ entry_len = asprintf (&entry, "%s %s %s %s 0 0\n", src, path, type,
+ options? options: MNTOPT_DEFAULTS);
+ if (! entry)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ err = mtab_add_entry (mtab, entry, entry_len);
+ if (err)
+ goto errout;
+
+ /* path has an active translator, query its children. */
+ err = file_get_children (node, &children, &children_len);
+ if (err == EOPNOTSUPP)
+ {
+ err = 0;
+ children_len = 0;
+ }
+
+ if (err)
+ goto errout;
+
+ if (children_len)
+ for (char *c = children; c; c = argz_next (children, children_len, c))
+ {
+ char *p = NULL;
+ asprintf (&p, "%s%s%s",
+ path,
+ path[strlen (path) - 1] == '/'? "": "/",
+ c);
+ if (! p)
+ {
+ err = ENOMEM;
+ goto errout;
+ }
+
+ err = mtab_populate (mtab, p, insecure);
+ if (err)
+ {
+ /* There is really not much we can do about errors here. */
+ error (0, err, "%s", p);
+ err = 0;
+ }
+
+ free (p);
+ }
+
+ errout:
+ if (node != MACH_PORT_NULL)
+ mach_port_deallocate (mach_task_self (), node);
+
+ if (argz)
+ vm_deallocate (mach_task_self (), (vm_address_t) argz, argz_len);
+
+ free (argv);
+ free (type);
+ free (options);
+
+ if (src != source)
+ free (src);
+
+ free (entry);
+
+ if (children)
+ vm_deallocate (mach_task_self (), (vm_address_t) children, children_len);
+
+ return err;
+}
+
+/* Decodes the DEVICE string into appropriate OPTIONS. Currently only
+ tmpfs-style size declarations are supported. */
+error_t
+argz_add_device (char **options, size_t *options_len, const char *device)
+{
+ error_t err;
+ char *end = NULL;
+ intmax_t size = strtoimax (device, &end, 0);
+ if (end == NULL || end == device)
+ return 0;
+
+ if (size < 0)
+ return 0;
+
+ switch (*end)
+ {
+ case 'g':
+ case 'G':
+ case 'm':
+ case 'M':
+ case 'k':
+ case 'K':
+ break;
+ default:
+ return 0;
+ }
+
+ /* device specifies a size. */
+ char *arg = NULL;
+ asprintf (&arg, "size=%s", device);
+ if (! arg)
+ return ENOMEM;
+
+ err = argz_add (options, options_len, arg);
+
+ free (arg);
+ return err;
+}
+
+/* Matches [hs]d\ds\d\d?. */
+int
+looks_like_block_device (const char *s)
+{
+ size_t len = strlen (s);
+ if (len != 3 && len != 5 && len != 6)
+ return 0;
+
+ return ((s[0] == 'h' || s[0] == 's') && s[1] == 'd' && isdigit (s[2]) &&
+ (len == 3 || (s[3] == 's' && isdigit (s[4]) &&
+ (len == 5 || isdigit (s[5])))));
+}
+
+/* Map a device string to a file name referencing the appropriate
+ device file. */
+error_t
+map_device_to_path (const char *device, char **path)
+{
+ if (strncmp (device, "device:", 7) == 0)
+ asprintf (path, "/dev/%s", &device[7]);
+ else if (strncmp (device, "/dev/", 5) == 0)
+ *path = strdup (device);
+ else if (looks_like_block_device (device))
+ asprintf (path, "/dev/%s", device);
+ else
+ *path = strdup (device);
+
+ if (! *path)
+ return ENOMEM;
+
+ return 0;
+}
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_allow_open = O_READ;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ /* Mark the node as a read-only plain file. */
+ st->st_mode &= ~(S_IFMT | ALLPERMS);
+ st->st_mode |= (S_IFREG | S_IRUSR | S_IRGRP | S_IROTH);
+ st->st_size = ((struct mtab *) cred->po->hook)->contents_len;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ exit (EXIT_SUCCESS);
+}
+
+static error_t
+open_hook (struct trivfs_peropen *peropen)
+{
+ struct mtab *mtab = malloc (sizeof (struct mtab));
+ if (mtab == NULL)
+ return ENOMEM;
+
+ /* Hook! */
+ peropen->hook = mtab;
+
+ /* Initialize the fields. */
+ pthread_mutex_init (&mtab->lock, NULL);
+ mtab->offs = 0;
+ mtab->contents = NULL;
+ mtab->contents_len = 0;
+
+ /* The mtab object is initialized, but not yet populated. We delay
+ that until that data is really needed. This avoids the following
+ problems:
+
+ Suppose you have
+
+ settrans -ac /foo /hurd/mtab /
+
+ If you now access /foo, the mtab translator will walk the tree of
+ all active translators starting from /. If it visits /foo, it
+ will talk to itself. Previously the translator migitated this by
+ comparing the control port of the translator with its own. This
+ does not work if you got two mtab translators like this:
+
+ settrans -ac /foo /hurd/mtab /
+ settrans -ac /bar /hurd/mtab /
+
+ With a single-threaded mtab server this results in a dead-lock,
+ with a multi-threaded server this will create more and more
+ threads.
+
+ Delaying the data generation until it is really needed cleanly
+ avoids these kind of problems. */
+ return 0;
+}
+
+static void
+close_hook (struct trivfs_peropen *peropen)
+{
+ struct mtab *op = peropen->hook;
+ pthread_mutex_destroy (&op->lock);
+ free (op->contents);
+ free (op);
+}
+
+/* Read data from an IO object. If offset is -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMOUNT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ error_t err = 0;
+ struct mtab *op;
+
+ /* Deny access if they have bad credentials. */
+ if (! cred)
+ return EOPNOTSUPP;
+
+ if (! (cred->po->openmodes & O_READ))
+ return EBADF;
+
+ /* Get the offset. */
+ op = cred->po->hook;
+ pthread_mutex_lock (&op->lock);
+
+ if (op->contents == NULL)
+ {
+ err = mtab_populate (op, target_path, insecure);
+ if (err)
+ goto out;
+ }
+
+ if (offs == -1)
+ offs = op->offs;
+
+ /* Prune the amount they want to read. */
+ if (offs > op->contents_len)
+ offs = op->contents_len;
+ if (offs + amount > op->contents_len)
+ amount = op->contents_len - offs;
+
+ if (amount > 0)
+ {
+ /* Possibly allocate a new buffer. */
+ if (*data_len < amount)
+ {
+ *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ if (*data == MAP_FAILED)
+ {
+ err = ENOMEM;
+ goto out;
+ }
+ }
+
+ /* Copy the constant data into the buffer. */
+ memcpy ((char *) *data, op->contents + offs, amount);
+
+ /* Update the saved offset. */
+ op->offs += amount;
+ }
+
+ *data_len = amount;
+ out:
+ pthread_mutex_unlock (&op->lock);
+ return err;
+}
+
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ error_t err = 0;
+ if (! cred)
+ return EOPNOTSUPP;
+
+ struct mtab *op = cred->po->hook;
+ pthread_mutex_lock (&op->lock);
+
+ if (op->contents == NULL)
+ {
+ err = mtab_populate (op, target_path, insecure);
+ if (err)
+ goto out;
+ }
+
+ switch (whence)
+ {
+ case SEEK_CUR:
+ offs += op->offs;
+ goto check;
+ case SEEK_END:
+ offs += op->contents_len;
+ case SEEK_SET:
+ check:
+ if (offs >= 0)
+ {
+ *new_offs = op->offs = offs;
+ break;
+ }
+ default:
+ err = EINVAL;
+ }
+
+ out:
+ pthread_mutex_unlock (&op->lock);
+ return err;
+}
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+error_t (*trivfs_peropen_create_hook)(struct trivfs_peropen *) = open_hook;
+
+/* If this variable is set, it is called every time a peropen structure
+ is about to be destroyed. */
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+kern_return_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ mach_msg_type_number_t *amount)
+{
+ error_t err = 0;
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_READ))
+ return EINVAL;
+
+ struct mtab *op = cred->po->hook;
+ pthread_mutex_lock (&op->lock);
+
+ if (op->contents == NULL)
+ {
+ error_t err = mtab_populate (op, target_path, insecure);
+ if (err)
+ goto out;
+ }
+
+ *amount = op->contents_len - op->offs;
+ out:
+ pthread_mutex_unlock (&op->lock);
+ return err;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+kern_return_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ))
+ || ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE)))
+ return EBADF;
+
+ *type &= ~SELECT_URG;
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ struct timespec ts,
+ int *type)
+{
+ return trivfs_S_io_select (cred, reply, replytype, type);
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+kern_return_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ *bits = cred->po->openmodes;
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+
+ return 0;
+}
diff --git a/trans/new-fifo.c b/trans/new-fifo.c
new file mode 100644
index 00000000..e71c95ca
--- /dev/null
+++ b/trans/new-fifo.c
@@ -0,0 +1,857 @@
+/* A translator for fifos
+
+ Copyright (C) 1995,96,97,98,2000,02 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ 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 <stdio.h>
+#include <errno.h>
+#include <argp.h>
+#include <unistd.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include <pthread.h>
+#include <hurd.h>
+#include <argz.h>
+#include <hurd/fshelp.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/fsys.h>
+#include <hurd/pipe.h>
+#include <hurd/paths.h>
+
+#include <version.h>
+
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_fsys_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+
+#define DEFAULT_SERVER _SERVERS "fifo";
+
+const char *argp_program_version = STANDARD_HURD_VERSION (new-fifo);
+
+struct port_bucket *port_bucket;
+struct port_class *fifo_port_class, *server_port_class, *fsys_port_class;
+
+/* ---------------------------------------------------------------- */
+
+static const struct argp_option options[] =
+{
+ {"multiple-readers", 'r', 0, 0, "Allow multiple simultaneous readers"},
+ {"noblock", 'n', 0, 0, "Don't block on open"},
+ {"dgram", 'd', 0, 0, "Reflect write record boundaries"},
+ {"server", 's', 0, 0, "Operate in server mode"},
+ {"standalone", 'S', 0, 0, "Don't attempt to use a fifo server"},
+ {"use-server", 'U', "NAME",0, "Attempt use server NAME"},
+ {0,0}
+};
+
+/* Per translator variables. */
+struct fifo_trans
+{
+ /* True if this not a real translator, but instead a translator server,
+ which responds to requests to create translators. */
+ int server;
+
+ /* True if opens for writing should hang until there's a reader. */
+ int wait_for_reader;
+ /* True if opens for reading should hang until there's a writer. */
+ int wait_for_writer;
+ /* True if opens for read should hang until there are no other readers. */
+ int one_reader;
+
+ /* If non-null, the name of a fifo server to do the translation in our
+ stead. */
+ char *use_server;
+
+ /* The translator from which this was initialized. */
+ struct fifo_trans *parent;
+
+ /* What kinds of pipes we use. */
+ struct pipe_class *fifo_pipe_class;
+
+ /* The current fifo that new opens will see, or NULL if there is none. */
+ struct pipe *active_fifo;
+ /* Lock this when changing ACTIVE_FIFO. */
+ pthread_mutex_t active_fifo_lock;
+ /* Signal this when ACTIVE_FIFO may have changed. */
+ pthread_cond_t active_fifo_changed;
+};
+
+/* Return a new FIFO_TRANS in TRANS, initializing it from FROM if it's
+ non-null, where possible. */
+static void
+fifo_trans_create (struct fifo_trans *from, struct fifo_trans **trans)
+{
+ struct fifo_trans *new = malloc (sizeof (struct fifo_trans));
+
+ new->server = 0;
+ pthread_mutex_init (&new->active_fifo_lock, NULL);
+ pthread_cond_init (&new->active_fifo_changed, NULL);
+
+ new->parent = from;
+
+ if (from)
+ /* Inherit things that can be inherited. */
+ {
+ new->wait_for_reader = from->wait_for_reader;
+ new->wait_for_writer = from->wait_for_writer;
+ new->one_reader = from->one_reader;
+ new->use_server = from->use_server;
+ new->fifo_pipe_class = from->fifo_pipe_class;
+ }
+ else
+ /* Otherwise just use default values. */
+ {
+ new->wait_for_reader = 1;
+ new->wait_for_writer = 1;
+ new->one_reader = 1;
+ new->use_server = DEFAULT_SERVER;
+ new->fifo_pipe_class = stream_pipe_class;
+ }
+
+ *trans = new;
+}
+
+static void
+fifo_trans_free (struct fifo_trans *trans)
+{
+ free (trans);
+}
+
+static error_t
+fifo_trans_start (struct fifo_trans *trans, mach_port_t requestor)
+{
+ struct trivfs_control *control;
+ struct port_class *class =
+ (trans->server ? server_port_class : fifo_port_class);
+ error_t
+ err = trivfs_startup (requestor, 0,
+ fsys_port_class, port_bucket, class, port_bucket,
+ &control);
+ if (!err)
+ control->hook = trans;
+ return err;
+}
+
+/* Parse our options. SERVER is true if we are a fifo server providing
+ service for others (in which case we don't print error messages). The
+ results of the parse are put into TRANS. */
+static error_t
+fifo_trans_parse_args (struct fifo_trans *trans, int argc, char **argv,
+ int print_errs)
+{
+ error_t parse_opt (int key, char *arg, struct argp_state *state)
+ {
+ switch (key)
+ {
+ case 'r': trans->one_reader = 0; break;
+ case 'n': trans->wait_for_reader = trans->wait_for_writer = 0; break;
+ case 'd': trans->fifo_pipe_class = seqpack_pipe_class;
+ case 's': trans->server = 1; break;
+ case 'U': trans->use_server = arg; break;
+ case 'S': trans->use_server = 0; break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+ }
+ struct argp argp = {options, parse_opt, 0, "A translator for fifos." };
+ return argp_parse (&argp, argc, argv, print_errs ? 0 : ARGP_SILENT, 0, 0);
+}
+
+/* ---------------------------------------------------------------- */
+
+struct port_class *trivfs_protid_portclasses[2];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 2;
+int trivfs_cntl_nportclasses = 1;
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct fifo_trans *trans;
+ /* Clean up a fsys control node. */
+ void clean_fsys (void *vfsys)
+ {
+ struct trivfs_control *fsys = vfsys;
+ if (fsys->hook)
+ fifo_trans_free (fsys->hook);
+ trivfs_clean_cntl (fsys);
+ }
+
+ fifo_trans_create (0, &trans);
+
+ if (fifo_trans_parse_args (trans, argc, argv, 1) != 0)
+ exit (1);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(1, 0, "must be started as a translator");
+
+ if (!trans->server && trans->use_server)
+ /* Attempt to contact a fifo server to do our work for us. */
+ {
+ err = fshelp_delegate_translation (trans->use_server, bootstrap, argv);
+ if (!err)
+ exit (0);
+ }
+
+ port_bucket = ports_create_bucket ();
+ fifo_port_class = ports_create_class (trivfs_clean_protid, 0);
+ server_port_class = ports_create_class (trivfs_clean_protid, 0);
+ fsys_port_class = ports_create_class (clean_fsys, 0);
+
+ trivfs_protid_portclasses[0] = fifo_port_class;
+ trivfs_protid_portclasses[1] = server_port_class;
+ trivfs_cntl_portclasses[0] = fsys_port_class;
+
+ /* Reply to our parent */
+ fifo_trans_start (trans, bootstrap);
+
+ /* Launch. */
+ do
+ {
+ ports_enable_class (fifo_port_class);
+ ports_manage_port_operations_multithread (port_bucket,
+ trivfs_demuxer,
+ 30*1000, 5*60*1000, 0);
+ }
+ while (ports_count_class (fifo_port_class) > 0);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+static error_t
+fifo_trans_open (struct fifo_trans *trans, int flags, void **hook)
+{
+ error_t err = 0;
+
+ if (flags & (O_READ | O_WRITE))
+ {
+ pthread_mutex_lock (&trans->active_fifo_lock);
+
+/* Wait until the active fifo has changed so that CONDITION is true. */
+#define WAIT(condition, noblock_err) \
+ while (!err && !(condition)) \
+ { \
+ if (flags & O_NONBLOCK) \
+ { \
+ err = noblock_err; \
+ break; \
+ } \
+ else if (pthread_hurd_cond_wait_np (&trans->active_fifo_changed, \
+ &trans->active_fifo_lock)) \
+ err = EINTR; \
+ }
+
+ if (flags & O_READ)
+ /* When opening for read, what we do depends on what mode this server
+ is running in. The default (if ONE_READER is set) is to only
+ allow one reader at a time, with additional opens for read
+ blocking here until the old reader goes away; otherwise, we allow
+ multiple readers. If WAIT_FOR_WRITER is true, then once we've
+ created a fifo, we also block until someone opens it for writing;
+ otherwise, the first read will block until someone writes
+ something. */
+ {
+ if (trans->one_reader)
+ /* Wait until there isn't any active fifo, so we can make one. */
+ WAIT (!trans->active_fifo || !trans->active_fifo->readers,
+ EWOULDBLOCK);
+ if (!err && trans->active_fifo == NULL)
+ /* No other readers, and indeed, no fifo; make one. */
+ err = pipe_create (trans->fifo_pipe_class, &trans->active_fifo);
+ if (!err)
+ {
+ pipe_add_reader (trans->active_fifo);
+ pthread_cond_broadcast (&trans->active_fifo_changed);
+ /* We'll unlock ACTIVE_FIFO_LOCK below; the writer code won't
+ make us block because we've ensured that there's a reader
+ for it. */
+
+ if (trans->wait_for_writer)
+ /* Wait until there's a writer. */
+ {
+ WAIT (trans->active_fifo->writers, 0);
+ if (err)
+ /* Back out the new pipe creation. */
+ {
+ pipe_remove_reader (trans->active_fifo);
+ trans->active_fifo = NULL;
+ pthread_cond_broadcast (&trans->active_fifo_changed);
+ }
+ }
+ else
+ /* Otherwise prevent an immediate eof. */
+ trans->active_fifo->flags &= ~PIPE_BROKEN;
+ }
+ }
+
+ if (!err && (flags & O_WRITE))
+ /* Open the trans->active_fifo for writing. If WAIT_FOR_READER is
+ true, then we block until there's someone to read what we wrote,
+ otherwise, if there's no fifo, we create one, which we just write
+ into and leave it for someone to read later. */
+ {
+ if (trans->wait_for_reader)
+ /* Wait until there's a fifo to write to. */
+ WAIT (trans->active_fifo && trans->active_fifo->readers, 0);
+ if (!err && trans->active_fifo == NULL)
+ /* No other readers, and indeed, no fifo; make one. */
+ {
+ err = pipe_create (trans->fifo_pipe_class, &trans->active_fifo);
+ if (!err)
+ trans->active_fifo->flags &= ~PIPE_BROKEN;
+ }
+ if (!err)
+ {
+ pipe_add_writer (trans->active_fifo);
+ pthread_cond_broadcast (&trans->active_fifo_changed);
+ }
+ }
+
+ *hook = trans->active_fifo;
+ }
+
+ pthread_mutex_unlock (&trans->active_fifo_lock);
+
+ return err;
+}
+
+static void
+fifo_trans_close (struct fifo_trans *trans, struct trivfs_peropen *po)
+{
+ int was_active, going_away = 0;
+ int flags = po->openmodes;
+ struct pipe *pipe = po->hook;
+
+ if (!pipe)
+ return;
+
+ pthread_mutex_lock (&trans->active_fifo_lock);
+ was_active = (trans->active_fifo == pipe);
+
+ if (was_active)
+ /* We're the last reader; when we're gone there is no more joy. */
+ going_away = ((flags & O_READ) && pipe->readers == 1);
+ else
+ /* Let others have their fun. */
+ pthread_mutex_unlock (&trans->active_fifo_lock);
+
+ if (flags & O_READ)
+ pipe_remove_reader (pipe);
+ if (flags & O_WRITE)
+ pipe_remove_writer (pipe);
+ /* At this point, PIPE may be gone, so we can't look at it again. */
+
+ if (was_active)
+ {
+ if (going_away)
+ trans->active_fifo = NULL;
+ pthread_cond_broadcast (&trans->active_fifo_changed);
+ pthread_mutex_unlock (&trans->active_fifo_lock);
+ }
+}
+
+static error_t
+open_hook (struct trivfs_peropen *po)
+{
+ struct fifo_trans *trans = po->cntl->hook;
+
+ if (! trans->server)
+ /* We're opening a normal fifo node. */
+ return fifo_trans_open (trans, po->openmodes, &po->hook);
+ else if (po->openmodes & (O_READ|O_WRITE|O_APPEND))
+ return EPERM;
+ else
+ /* We're a fifo server serving a new fifo. */
+ return 0;
+}
+
+static void
+close_hook (struct trivfs_peropen *po)
+{
+ struct fifo_trans *trans = po->cntl->hook;
+
+ if (! trans->server)
+ /* We're closing a normal fifo node. */
+ fifo_trans_close (trans, po);
+}
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *) = open_hook;
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *) = close_hook;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ struct fifo_trans *trans = cred->po->cntl->hook;
+ if (! trans->server)
+ /* A fifo node */
+ {
+ struct pipe *pipe = cred->po->hook;
+
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFIFO;
+
+ if (pipe)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ st->st_size = pipe_readable (pipe, 1);
+ st->st_blocks = st->st_size >> 9;
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ st->st_size = st->st_blocks = 0;
+
+ /* As we try to be clever with large transfers, ask for them. */
+ st->st_blksize = vm_page_size * 16;
+ }
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ error_t err;
+ int num_opens;
+ int force = flags & FSYS_GOAWAY_FORCE;
+ int unlink = flags & FSYS_GOAWAY_UNLINK;
+ struct fifo_trans *trans = fsys->hook;
+
+ err = ports_inhibit_port_rpcs (fsys);
+ if (err == EINTR || (err && !force))
+ return err;
+
+ num_opens = ports_count_class (fsys->protid_class);
+ if (num_opens > 0 && !force && !unlink)
+ /* Still some opens, and we're not being forced to go away, so don't.*/
+ {
+ ports_enable_class (fsys->protid_class);
+ ports_resume_port_rpcs (fsys);
+ return EBUSY;
+ }
+
+ /* Kill the control connection. */
+ mach_port_deallocate (mach_task_self (), fsys->underlying);
+ fsys->underlying = MACH_PORT_NULL;
+ ports_destroy_right (fsys);
+
+ if (force)
+ /* Kill opens. */
+ {
+ error_t maybe_trash_protid (void *vcred)
+ {
+ struct trivfs_protid *cred = vcred;
+ if (cred->po->cntl == fsys)
+ {
+ ports_destroy_right (cred);
+ ports_interrupt_rpcs (cred);
+ }
+ return 0;
+ }
+ ports_bucket_iterate (((struct port_info *)fsys)->bucket,
+ maybe_trash_protid);
+ }
+
+ if (! trans->parent)
+ /* The root translator, go away, bye bye. */
+ exit (0);
+
+ /* Let things continue; what should die will. */
+ ports_enable_class (fsys->protid_class);
+ ports_resume_port_rpcs (fsys);
+
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+error_t
+trivfs_S_io_map (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ return EOPNOTSUPP;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMT. */
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, size_t *data_len,
+ off_t offs, size_t amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ assert (pipe);
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_read (pipe, cred->po->openmodes & O_NONBLOCK, NULL,
+ data, data_len, amount);
+ pthread_mutex_unlock (&pipe->lock);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ size_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ assert (pipe);
+ pthread_mutex_lock (&pipe->lock);
+ *amount = pipe_readable (pipe, 1);
+ pthread_mutex_unlock (&pipe->lock);
+ err = 0;
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Change current read/write offset */
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offset, int whence, off_t *new_offset)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return ESPIPE;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+static error_t
+io_select_common (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec *tsp, int *select_type)
+{
+ struct pipe *pipe;
+ error_t err = 0;
+ int ready = 0;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pipe = cred->po->hook;
+
+ if (*select_type & SELECT_READ)
+ {
+ if (cred->po->openmodes & O_READ)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_wait_readable (pipe, 1, 1);
+ if (err == EWOULDBLOCK)
+ err = 0; /* Not readable, actually not an error. */
+ else
+ ready |= SELECT_READ; /* Data immediately readable (or error). */
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ {
+ err = EBADF;
+ ready |= SELECT_READ; /* Error immediately available... */
+ }
+ if (err)
+ /* Prevent write test from overwriting err. */
+ *select_type &= ~SELECT_WRITE;
+ }
+
+ if (*select_type & SELECT_WRITE)
+ {
+ if (cred->po->openmodes & O_WRITE)
+ {
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_wait_writable (pipe, 1);
+ if (err == EWOULDBLOCK)
+ err = 0; /* Not writable, actually not an error. */
+ else
+ ready |= SELECT_WRITE; /* Data immediately writable (or error). */
+ pthread_mutex_unlock (&pipe->lock);
+ }
+ else
+ {
+ err = EBADF;
+ ready |= SELECT_WRITE; /* Error immediately available... */
+ }
+ }
+
+ if (ready)
+ *select_type = ready;
+ else
+ /* Wait for something to change. */
+ {
+ ports_interrupt_self_on_port_death (cred, reply);
+ err = pipe_pair_select (pipe, pipe, tsp, select_type, 1);
+ }
+
+ return err;
+}
+
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *select_type)
+{
+ return io_select_common (cred, reply, reply_type, NULL, select_type);
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *select_type)
+{
+ return io_select_common (cred, reply, reply_type, &ts, select_type);
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+error_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *data, size_t data_len,
+ off_t offs, size_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ err = EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_WRITE))
+ err = EBADF;
+ else
+ {
+ struct pipe *pipe = cred->po->hook;
+ pthread_mutex_lock (&pipe->lock);
+ err = pipe_write (pipe, cred->po->openmodes & O_NONBLOCK, NULL,
+ data, data_len, amount);
+ pthread_mutex_unlock (&pipe->lock);
+ }
+
+ return err;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Truncate file. */
+error_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t size)
+{
+ return size == 0 ? 0 : EINVAL;
+}
+
+/* ---------------------------------------------------------------- */
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+error_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Get/set the owner of the IO object. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all
+ objects this affects old-style async IO. Negative values represent
+ pgrps. This has nothing to do with the owner of a file (as
+ returned by io_stat, and as used for various permission checks by
+ filesystems). An owner of 0 indicates that there is no owner. */
+
+error_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ *owner = 0;
+ return 0;
+}
+
+error_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return EINVAL;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* Ask SERVER to provide fsys translation service for us. REQUESTOR is
+ the bootstrap port supplied to the original translator, and ARGV are
+ the command line arguments. If the recipient accepts the request, he
+ (or some delegate) should send fsys_startup to REQUESTOR to start the
+ filesystem up. */
+error_t
+trivfs_S_fsys_forward (mach_port_t server,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t requestor,
+ char *argz, size_t argz_len)
+{
+ error_t err;
+ struct fifo_trans *server_trans, *trans;
+ int argc = argz_count (argz, argz_len);
+ char **argv = alloca (sizeof (char *) * (argc + 1));
+ /* SERVER should be our root node. */
+ struct trivfs_protid *cred =
+ ports_lookup_port (port_bucket, server, server_port_class);
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ server_trans = cred->po->cntl->hook;
+ assert (server_trans->server);
+
+ argz_extract (argz, argz_len, argv);
+
+ /* Make a new translator, inheriting from its server. */
+ fifo_trans_create (server_trans, &trans);
+
+ /* Parse the new arguments to change the defaults. */
+ err = fifo_trans_parse_args (trans, argc, argv, 0);
+
+ if (!err)
+ /* Set our new translator along it's merry way... */
+ fifo_trans_start (trans, requestor);
+
+ ports_port_deref (cred);
+
+ return err;
+}
diff --git a/trans/null.c b/trans/null.c
new file mode 100644
index 00000000..bd082dc8
--- /dev/null
+++ b/trans/null.c
@@ -0,0 +1,340 @@
+/* A translator for providing endless empty space and immediate eof.
+
+ Copyright (C) 1995,96,97,98,99,2001,02,03 Free Software Foundation, Inc.
+ Written by Miles Bader <miles@gnu.org>
+
+ 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 <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <hurd/fsys.h>
+#include <version.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <error.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <argp.h>
+#include <nullauth.h>
+
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+
+const char *argp_program_version = STANDARD_HURD_VERSION (null);
+
+/* Error code to return for write attempts.
+ If zero, they succeed in writing to the bitbucket. */
+static error_t write_error_code;
+
+static const struct argp_option options[] =
+{
+ {"full", 'f', 0, 0, "Cause writes to fail as if to a full disk"},
+ {0}
+};
+
+static error_t
+parse_opt (int opt, char *arg, struct argp_state *state)
+{
+ switch (opt)
+ {
+ case 'f':
+ write_error_code = ENOSPC;
+ return 0;
+ }
+ return ARGP_ERR_UNKNOWN;
+}
+
+static const struct argp argp =
+{ options, parse_opt, 0, "Endless sink and null source" };
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(1, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error(3, err, "Contacting parent");
+
+ err = setnullauth ();
+ if (err)
+ error(4, err, "Dropping privileges");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (fsys->pi.bucket, trivfs_demuxer,
+ 2 * 60 * 1000, 0, 0);
+
+ return 0;
+}
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_DEV;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_blksize = vm_page_size * 256; /* Make transfers LARRRRRGE */
+
+ st->st_size = 0;
+ st->st_blocks = 0;
+
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFCHR;
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ exit (0);
+}
+
+/* Return objects mapping the data underlying this memory object. If
+ the object can be read then memobjrd will be provided; if the
+ object can be written then memobjwr will be provided. For objects
+ where read data and write data are the same, these objects will be
+ equal, otherwise they will be disjoint. Servers are permitted to
+ implement io_map but not io_map_cntl. Some objects do not provide
+ mapping; they will set none of the ports and return an error. Such
+ objects can still be accessed by io_read and io_write. */
+kern_return_t
+trivfs_S_io_map (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ memory_object_t *rdobj,
+ mach_msg_type_name_t *rdtype,
+ memory_object_t *wrobj,
+ mach_msg_type_name_t *wrtype)
+{
+ return EOPNOTSUPP; /* XXX should work! */
+}
+
+/* Read data from an IO object. If offset if -1, read from the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount desired to be read is in AMT. */
+kern_return_t
+trivfs_S_io_read(struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ loff_t offs,
+ mach_msg_type_number_t amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ return EBADF;
+ else
+ {
+ *datalen = 0;
+ return 0;
+ }
+}
+
+/* Tell how much data can be read from the object without blocking for
+ a "long time" (this should be the same meaning of "long time" used
+ by the nonblocking flag. */
+kern_return_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ mach_msg_type_number_t *amount)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_READ))
+ return EINVAL;
+ else
+ *amount = 0;
+ return 0;
+}
+
+/* Change current read/write offset */
+kern_return_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ off_t offset, int whence, off_t *new_offset)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ *new_offset = 0;
+ return 0;
+}
+
+/* SELECT_TYPE is the bitwise OR of SELECT_READ, SELECT_WRITE, and SELECT_URG.
+ Block until one of the indicated types of i/o can be done "quickly", and
+ return the types that are then available. ID_TAG is returned as passed; it
+ is just for the convenience of the user in matching up reply messages with
+ specific requests sent. */
+kern_return_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *type)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (((*type & SELECT_READ) && !(cred->po->openmodes & O_READ))
+ || ((*type & SELECT_WRITE) && !(cred->po->openmodes & O_WRITE)))
+ return EBADF;
+ else
+ *type &= ~SELECT_URG;
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ struct timespec ts,
+ int *type)
+{
+ return trivfs_S_io_select (cred, reply, replytype, type);
+}
+
+/* Write data to an IO object. If offset is -1, write at the object
+ maintained file pointer. If the object is not seekable, offset is
+ ignored. The amount successfully written is returned in amount. A
+ given user should not have more than one outstanding io_write on an
+ object at a time; servers implement congestion control by delaying
+ responses to io_write. Servers may drop data (returning ENOBUFS)
+ if they recevie more than one write when not prepared for it. */
+kern_return_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ char *data, mach_msg_type_number_t datalen,
+ loff_t offs, mach_msg_type_number_t *amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_WRITE))
+ return EBADF;
+ *amt = datalen;
+ return write_error_code;
+}
+
+/* Truncate file. */
+kern_return_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ loff_t size)
+{
+ if (size < 0)
+ return EINVAL;
+ return 0;
+}
+
+/* These four routines modify the O_APPEND, O_ASYNC, O_FSYNC, and
+ O_NONBLOCK bits for the IO object. In addition, io_get_openmodes
+ will tell you which of O_READ, O_WRITE, and O_EXEC the object can
+ be used for. The O_ASYNC bit affects icky async I/O; good async
+ I/O is done through io_async which is orthogonal to these calls. */
+
+kern_return_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes(struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+/* Get/set the owner of the IO object. For terminals, this affects
+ controlling terminal behavior (see term_become_ctty). For all
+ objects this affects old-style async IO. Negative values represent
+ pgrps. This has nothing to do with the owner of a file (as
+ returned by io_stat, and as used for various permission checks by
+ filesystems). An owner of 0 indicates that there is no owner. */
+
+kern_return_t
+trivfs_S_io_get_owner (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ pid_t *owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ *owner = 0;
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_mod_owner (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ pid_t owner)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return EINVAL;
+}
diff --git a/trans/password.c b/trans/password.c
new file mode 100644
index 00000000..344b78ba
--- /dev/null
+++ b/trans/password.c
@@ -0,0 +1,230 @@
+/* Hurd standard password server.
+ Copyright (C) 1999, 2013 Free Software Foundation
+ Written by Mark Kettenis.
+
+ 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <argp.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hurd.h>
+#include <hurd/auth.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+
+#include <ugids.h>
+#include <version.h>
+
+#include "password_S.h"
+
+
+const char *argp_program_version = STANDARD_HURD_VERSION (password);
+
+/* Port bucket we service requests on. */
+struct port_bucket *port_bucket;
+
+/* Trivfs hooks. */
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = 0;
+
+struct port_class *trivfs_protid_portclasses[1];
+struct port_class *trivfs_cntl_portclasses[1];
+int trivfs_protid_nportclasses = 1;
+int trivfs_cntl_nportclasses = 1;
+
+
+static int
+password_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int password_server (mach_msg_header_t *inp, mach_msg_header_t *outp);
+ return password_server (inp, outp) || trivfs_demuxer (inp, outp);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+ const struct argp argp = { 0, 0, 0, "Hurd standard password server." };
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "must be started as a translator");
+
+ port_bucket = ports_create_bucket ();
+ trivfs_cntl_portclasses[0] = ports_create_class (trivfs_clean_cntl, 0);
+ trivfs_protid_portclasses[0] = ports_create_class (trivfs_clean_protid, 0);
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0,
+ trivfs_cntl_portclasses[0], port_bucket,
+ trivfs_protid_portclasses[0], port_bucket,
+ &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (3, err, "Contacting parent");
+
+ /* Launch. */
+ do
+ ports_manage_port_operations_multithread (port_bucket, password_demuxer,
+ 2 * 60 * 1000,
+ 10 * 60 * 1000,
+ 0);
+ /* That returns when 10 minutes pass without an RPC. Try shutting down
+ as if sent fsys_goaway; if we have any users who need us to stay
+ around, this returns EBUSY and we loop to service more RPCs. */
+ while (trivfs_goaway (fsys, 0));
+
+ return 0;
+}
+
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ int count;
+
+ /* Stop new requests. */
+ ports_inhibit_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_inhibit_class_rpcs (trivfs_protid_portclasses[0]);
+
+ /* Are there any extant user ports for the /servers/password file? */
+ count = ports_count_class (trivfs_protid_portclasses[0]);
+ if (count > 0 && !(flags & FSYS_GOAWAY_FORCE))
+ {
+ /* We won't go away, so start things going again... */
+ ports_enable_class (trivfs_protid_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_cntl_portclasses[0]);
+ ports_resume_class_rpcs (trivfs_protid_portclasses[0]);
+
+ return EBUSY;
+ }
+
+ exit (0);
+}
+
+
+/* Implement password_check_user as described in <hurd/password.defs>. */
+kern_return_t
+S_password_check_user (struct trivfs_protid *cred, uid_t user, char *pw,
+ mach_port_t *port, mach_msg_type_name_t *port_type)
+{
+ struct ugids ugids = UGIDS_INIT;
+ auth_t auth;
+ error_t err;
+
+ char *getpass (const char *prompt, uid_t id, int is_group,
+ void *pwd_or_group, void *hook)
+ {
+ assert (! is_group && id == user);
+ return strdup (pw);
+ }
+
+ if (! cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.bucket != port_bucket ||
+ cred->pi.class != trivfs_protid_portclasses[0])
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ /* Verify password. */
+ err = ugids_add_user (&ugids, user, 1);
+ if (!err)
+ err = ugids_verify (&ugids, 0, 0, getpass, 0, 0, 0);
+
+ if (!err)
+ {
+ auth = getauth ();
+ err = auth_makeauth (auth, 0, MACH_MSG_TYPE_COPY_SEND, 0,
+ ugids.eff_uids.ids, ugids.eff_uids.num,
+ ugids.avail_uids.ids, ugids.avail_uids.num,
+ ugids.eff_gids.ids, ugids.eff_gids.num,
+ ugids.avail_gids.ids, ugids.avail_gids.num,
+ port);
+ mach_port_deallocate (mach_task_self (), auth);
+ *port_type = MACH_MSG_TYPE_MOVE_SEND;
+ }
+
+ ugids_fini (&ugids);
+ return err;
+}
+
+/* Implement password_check_group as described in <hurd/password.defs>. */
+kern_return_t
+S_password_check_group (struct trivfs_protid *cred, uid_t group, char *pw,
+ mach_port_t *port, mach_msg_type_name_t *port_type)
+{
+ struct ugids ugids = UGIDS_INIT;
+ auth_t auth;
+ error_t err;
+
+ char *getpass (const char *prompt, uid_t id, int is_group,
+ void *pwd_or_group, void *hook)
+ {
+ assert (is_group && id == group);
+ return strdup (pw);
+ }
+
+ if (! cred)
+ return EOPNOTSUPP;
+
+ if (cred->pi.bucket != port_bucket ||
+ cred->pi.class != trivfs_protid_portclasses[0])
+ {
+ ports_port_deref (cred);
+ return EOPNOTSUPP;
+ }
+
+ /* Verify password. */
+ err = ugids_add_gid (&ugids, group, 1);
+ if (!err)
+ err = ugids_verify (&ugids, 0, 0, getpass, 0, 0, 0);
+
+ if (!err)
+ {
+ auth = getauth ();
+ err = auth_makeauth (auth, 0, MACH_MSG_TYPE_COPY_SEND, 0,
+ ugids.eff_uids.ids, ugids.eff_uids.num,
+ ugids.avail_uids.ids, ugids.avail_uids.num,
+ ugids.eff_gids.ids, ugids.eff_gids.num,
+ ugids.avail_gids.ids, ugids.avail_gids.num,
+ port);
+ mach_port_deallocate (mach_task_self (), auth);
+ *port_type = MACH_MSG_TYPE_MOVE_SEND;
+ }
+
+ ugids_fini (&ugids);
+ return err;
+}
diff --git a/trans/proxy-defpager.c b/trans/proxy-defpager.c
new file mode 100644
index 00000000..98176577
--- /dev/null
+++ b/trans/proxy-defpager.c
@@ -0,0 +1,279 @@
+/* A translator for providing access to Mach default_pager.defs control calls
+
+ Copyright (C) 2002, 2007 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., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <hurd/trivfs.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <error.h>
+#include <version.h>
+#include <hurd/paths.h>
+
+#include "libtrivfs/trivfs_io_S.h"
+#include "default_pager_S.h"
+#include "default_pager_U.h"
+
+static mach_port_t real_defpager, dev_master;
+
+static error_t
+allowed (mach_port_t port, int mode)
+{
+ struct trivfs_protid *cred = ports_lookup_port
+ (0, port, trivfs_protid_portclasses[0]);
+ if (!cred)
+ return MIG_BAD_ID;
+ error_t result = (cred->po->openmodes & mode) ? 0 : EACCES;
+ ports_port_deref (cred);
+ return result;
+}
+
+kern_return_t
+S_default_pager_object_create (mach_port_t default_pager,
+ memory_object_t *memory_object,
+ vm_size_t object_size)
+{
+ return allowed (default_pager, O_EXEC)
+ ?: default_pager_object_create (real_defpager, memory_object, object_size);
+}
+
+kern_return_t
+S_default_pager_info (mach_port_t default_pager, default_pager_info_t *info)
+{
+ return allowed (default_pager, O_READ)
+ ?: default_pager_info (real_defpager, info);
+}
+
+kern_return_t
+S_default_pager_objects (mach_port_t default_pager,
+ default_pager_object_array_t *objects,
+ mach_msg_type_number_t *objectsCnt,
+ mach_port_array_t *ports,
+ mach_msg_type_number_t *portsCnt)
+{
+ return allowed (default_pager, O_WRITE)
+ ?: default_pager_objects (real_defpager,
+ objects, objectsCnt, ports, portsCnt);
+}
+
+kern_return_t
+S_default_pager_object_pages (mach_port_t default_pager,
+ mach_port_t memory_object,
+ default_pager_page_array_t *pages,
+ mach_msg_type_number_t *pagesCnt)
+{
+ return allowed (default_pager, O_WRITE)
+ ?: default_pager_object_pages (real_defpager, memory_object,
+ pages, pagesCnt);
+}
+
+
+kern_return_t
+S_default_pager_paging_file (mach_port_t default_pager,
+ mach_port_t master_device_port,
+ default_pager_filename_t filename,
+ boolean_t add)
+{
+ return allowed (default_pager, O_WRITE)
+ ?: default_pager_paging_file (real_defpager, dev_master, filename, add)
+ ?: mach_port_deallocate (mach_task_self (), master_device_port);
+}
+
+kern_return_t
+S_default_pager_paging_storage (mach_port_t default_pager,
+ mach_port_t device,
+ recnum_t *runs, mach_msg_type_number_t nruns,
+ default_pager_filename_t name,
+ boolean_t add)
+{
+ return allowed (default_pager, O_WRITE)
+ ?: default_pager_paging_storage (real_defpager, dev_master,
+ runs, nruns, name, add)
+ ?: mach_port_deallocate (mach_task_self (), device);
+}
+
+kern_return_t
+S_default_pager_object_set_size (mach_port_t memory_object,
+ mach_port_seqno_t seqno,
+ vm_size_t object_size_limit)
+{
+ /* This is sent to an object, not the control port. */
+ return MIG_BAD_ID;
+}
+
+
+/* Trivfs hooks */
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 1;
+
+int trivfs_allow_open = O_READ | O_WRITE | O_EXEC;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_blksize = vm_page_size * 256; /* Make transfers LARRRRRGE */
+
+ st->st_size = 0;
+ st->st_blocks = 0;
+
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFCHR;
+}
+
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ exit (0);
+}
+
+kern_return_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ loff_t offs,
+ mach_msg_type_number_t amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return EIO;
+}
+
+kern_return_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ char *data, mach_msg_type_number_t datalen,
+ loff_t offs, mach_msg_type_number_t *amt)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ return EIO;
+}
+
+kern_return_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int *bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int mode)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+kern_return_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ int bits)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+const char *argp_program_version = STANDARD_HURD_VERSION (proxy-defpager);
+
+static const struct argp argp =
+{doc: "\
+Access to control interfaces of Mach default pager.\n\
+This translator should normally be set on " _SERVERS_DEFPAGER "."};
+
+int
+proxy_defpager_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ extern int default_pager_server (mach_msg_header_t *, mach_msg_header_t *);
+
+ return default_pager_server (inp, outp)
+ || trivfs_demuxer (inp, outp);
+}
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+ mach_port_t host_priv;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ err = get_privileged_ports (&host_priv, &dev_master);
+ if (err)
+ error (2, err, "cannot get privileged ports");
+ real_defpager = MACH_PORT_NULL;
+ err = vm_set_default_memory_manager (host_priv, &real_defpager);
+ mach_port_deallocate (mach_task_self (), host_priv);
+ if (err)
+ error (3, err, "vm_set_default_memory_manager");
+ if (real_defpager == MACH_PORT_NULL)
+ error (1, 0, "no default memory manager set!");
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ trivfs_fsid = getpid ();
+
+ /* Reply to our parent. */
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (4, err, "Contacting parent");
+
+ /* Launch. */
+ ports_manage_port_operations_multithread (fsys->pi.bucket,
+ proxy_defpager_demuxer,
+ 2 * 60 * 1000, 0, 0);
+
+ return 0;
+}
diff --git a/trans/remap.c b/trans/remap.c
new file mode 100644
index 00000000..5ee01891
--- /dev/null
+++ b/trans/remap.c
@@ -0,0 +1,152 @@
+/* remap -- a translator for changing paths
+ Copyright (C) 2013 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 <hurd/trivfs.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <argp.h>
+#include <error.h>
+#include <string.h>
+
+#include <version.h>
+
+int trivfs_fstype = FSTYPE_MISC;
+int trivfs_fsid = 0;
+int trivfs_support_read = 0;
+int trivfs_support_write = 0;
+int trivfs_support_exec = 0;
+int trivfs_allow_open = 0;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ /* Don't care */
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *cntl, int flags)
+{
+ exit (0);
+}
+
+struct remap
+{
+ char *from;
+ char *to;
+ struct remap *next;
+};
+
+static struct remap *remaps;
+
+error_t
+trivfs_S_dir_lookup (struct trivfs_protid *diruser,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ 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 remap *remap;
+
+ if (!diruser)
+ return EOPNOTSUPP;
+
+ for (remap = remaps; remap; remap = remap->next)
+ if (!strcmp (remap->from, filename))
+ {
+#ifdef DEBUG
+ fprintf (stderr,"replacing %s with %s\n", remap->from, remap->to);
+ fflush (stderr);
+#endif
+ filename = remap->to;
+ break;
+ }
+
+ *do_retry = FS_RETRY_REAUTH;
+ *retry_port = getcrdir ();
+ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
+ strcpy (retry_name, filename);
+
+ return 0;
+}
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ static char *remap_from;
+
+ switch (key)
+ {
+ case ARGP_KEY_ARG:
+
+ /* Skip heading slashes */
+ while (arg[0] == '/')
+ arg++;
+
+ if (!remap_from)
+ /* First of a pair */
+ remap_from = strdup (arg);
+ else
+ {
+ /* Second of a pair */
+ struct remap *remap = malloc (sizeof (*remap));
+ remap->from = remap_from;
+ remap->to = strdup (arg);
+ remap->next = remaps;
+#ifdef DEBUG
+ fprintf (stderr, "adding remap %s->%s\n", remap->from, remap->to);
+#endif
+ remaps = remap;
+ remap_from = NULL;
+ }
+
+ break;
+ }
+ return 0;
+}
+
+const char *argp_program_version = STANDARD_HURD_VERSION (fakeroot);
+
+int
+main (int argc, char **argv)
+{
+ error_t err;
+ mach_port_t bootstrap;
+
+ struct argp argp = { NULL, parse_opt, "[ FROM1 TO1 [ FROM2 TO2 [ ... ] ] ]", "\
+A translator for remapping directories.\v\
+This translator is to be used as a chroot, within which paths point to the\
+same files as the original root, except a given set of paths, which are\
+remapped to given paths." };
+
+ argp_parse (&argp, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ struct trivfs_control *fsys;
+
+ err = trivfs_startup (bootstrap, 0, 0, 0, 0, 0, &fsys);
+ if (err)
+ error (1, err, "trivfs_startup failed");
+ ports_manage_port_operations_one_thread (fsys->pi.bucket, trivfs_demuxer, 0);
+
+ /*NOTREACHED*/
+ return 0;
+}
diff --git a/trans/streamio.c b/trans/streamio.c
new file mode 100644
index 00000000..54627b73
--- /dev/null
+++ b/trans/streamio.c
@@ -0,0 +1,1189 @@
+/* A translator for handling stream devices.
+
+ Copyright (C) 2001,02 Free Software Foundation, Inc.
+
+ Written by OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
+
+ 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 <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <argp.h>
+#include <error.h>
+
+#include <mach.h>
+#include <device/device.h>
+#include <device/device_request.h>
+
+#include <hurd.h>
+#include <hurd/ports.h>
+#include <hurd/trivfs.h>
+#include <version.h>
+
+#include "libtrivfs/trivfs_fs_S.h"
+#include "libtrivfs/trivfs_io_S.h"
+
+/* The global lock */
+pthread_mutex_t global_lock;
+
+/* Wakeup when device_open is finished */
+pthread_cond_t open_alert;
+
+/* Wakeup for select */
+pthread_cond_t select_alert;
+
+/* Bucket for all out ports */
+struct port_bucket *streamdev_bucket;
+
+/* The buffers we use */
+struct buffer *input_buffer, *output_buffer;
+
+
+/* Information about a buffer. */
+struct buffer
+{
+ /* Point to the head of the buffer. */
+ char *head;
+ /* Point to the tail of the buffer. */
+ char *tail;
+ /* The buffer array size. */
+ size_t size;
+ /* Wakeup when the buffer is not empty or not full. */
+ pthread_cond_t *wait;
+ /* The buffer. */
+ char buf[0];
+};
+
+/* Create a new buffer structure with SIZE, returning the pointer. */
+static inline struct buffer *
+create_buffer (size_t size)
+{
+ struct buffer *new = malloc (sizeof (struct buffer) + size);
+ assert (new);
+ new->head = new->tail = new->buf;
+ new->size = size;
+ new->wait = malloc (sizeof (pthread_cond_t));
+ assert (new->wait);
+ pthread_cond_init (new->wait, NULL);
+ return new;
+}
+
+/* Return the size of B. */
+static inline size_t
+buffer_size (struct buffer *b)
+{
+ return b->tail - b->head;
+}
+
+/* Return how much characters can be read from B. */
+static inline size_t
+buffer_readable (struct buffer *b)
+{
+ return buffer_size (b);
+}
+
+/* Return how much characters can be written to B. */
+static inline size_t
+buffer_writable (struct buffer *b)
+{
+ return b->size - buffer_size (b);
+}
+
+/* Flush B. */
+static inline void
+clear_buffer (struct buffer *b)
+{
+ if (b == 0)
+ return;
+ b->head = b->tail = b->buf;
+ pthread_cond_broadcast (b->wait);
+ pthread_cond_broadcast (&select_alert);
+}
+
+/* Read up to LEN bytes from B to DATA, returning the amount actually read. */
+static inline size_t
+buffer_read (struct buffer *b, void *data, size_t len)
+{
+ size_t max = buffer_size (b);
+
+ if (len > max)
+ len = max;
+
+ memcpy (data, b->head, len);
+ b->head += len;
+
+ if (b->head > b->buf + b->size / 2)
+ {
+ size_t size = buffer_size (b);
+
+ memmove (b->buf, b->head, size);
+ b->head = b->buf;
+ b->tail = b->buf + size;
+ }
+
+ pthread_cond_broadcast (b->wait);
+ pthread_cond_broadcast (&select_alert);
+ return len;
+}
+
+/* Write LEN bytes from DATA to B, returning the amount actually written. */
+static inline size_t
+buffer_write (struct buffer *b, void *data, size_t len)
+{
+ size_t size = buffer_writable (b);
+
+ if (len > size)
+ len = size;
+
+ memcpy (b->tail, data, len);
+ b->tail += len;
+
+ pthread_cond_broadcast (b->wait);
+ pthread_cond_broadcast (&select_alert);
+ return len;
+}
+
+
+/* Open a new device structure for the device NAME with MODE. If an error
+ occurs, the error code is returned, otherwise 0. */
+error_t dev_open (const char *name, dev_mode_t mode);
+
+/* Check if the device is already opened. */
+int dev_already_opened (void);
+
+/* Close the device. */
+void dev_close (void);
+
+/* Read up to AMOUNT bytes, returned in BUF and LEN. If NOWAIT is non-zero
+ and the buffer is empty, then returns EWOULDBLOCK. If an error occurs,
+ the error code is returned, otherwise 0. */
+error_t dev_read (size_t amount, void **buf, size_t *len, int nowait);
+
+/* Return current readable size in AMOUNT. If an error occurs, the error
+ code is returned, otherwise 0. */
+error_t dev_readable (size_t *amount);
+
+/* Write LEN bytes from BUF, returning the amount actually written
+ in AMOUNT. If NOWAIT is non-zero and the buffer is full, then returns
+ EWOULDBLOCK. If an error occurs, the error code is returned,
+ otherwise 0. */
+error_t dev_write (void *buf, size_t len, size_t *amount, int nowait);
+
+/* Try and write out any pending writes to the device. If WAIT is non-zero,
+ will wait for any activity to cease. */
+error_t dev_sync (int wait);
+
+
+
+static struct argp_option options[] =
+{
+ {"rdev", 'n', "ID", 0,
+ "The stat rdev number for this node; may be either a"
+ " single integer, or of the form MAJOR,MINOR"},
+ {"readonly", 'r', 0, 0, "Disallow writing"},
+ {"rdonly", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"ro", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"writable", 'w', 0, 0, "Allow writing"},
+ {"rdwr", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"rw", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {"writeonly", 'W',0, 0, "Disallow reading"},
+ {"wronly", 0, 0, OPTION_ALIAS | OPTION_HIDDEN},
+ {0}
+};
+
+static const char args_doc[] = "DEVICE";
+static const char doc[] = "Translator for stream devices.";
+
+const char *argp_program_version = STANDARD_HURD_VERSION (streamio);
+
+
+static char *stream_name;
+static int rdev;
+static int nperopens;
+
+/* Parse a single option. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case 'r':
+ trivfs_allow_open = O_READ;
+ break;
+ case 'w':
+ trivfs_allow_open = O_RDWR;
+ break;
+ case 'W':
+ trivfs_allow_open = O_WRITE;
+ break;
+
+ case 'n':
+ {
+ char *start = arg;
+ char *end;
+
+ rdev = strtoul (start, &end, 0);
+ if (*end == ',')
+ /* MAJOR,MINOR form */
+ {
+ start = end + 1;
+ rdev = (rdev << 8) + strtoul (start, &end, 0);
+ }
+
+ if (end == start || *end != '\0')
+ {
+ argp_error (state, "%s: Invalid argument to --rdev", arg);
+ return EINVAL;
+ }
+ }
+ break;
+
+ case ARGP_KEY_ARG:
+ stream_name = arg;
+ break;
+
+ case ARGP_KEY_END:
+ if (stream_name == 0)
+ argp_usage (state);
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static const struct argp argp = { options, parse_opt, args_doc, doc };
+
+
+int
+demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ extern int device_reply_server (mach_msg_header_t *, mach_msg_header_t *);
+
+ return (trivfs_demuxer (inp, outp)
+ || device_reply_server (inp, outp));
+}
+
+int
+main (int argc, char *argv[])
+{
+ error_t err;
+ mach_port_t bootstrap;
+ struct trivfs_control *fsys;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (2, 0, "Must be started as a translator");
+
+ streamdev_bucket = ports_create_bucket ();
+
+ err = trivfs_startup (bootstrap, 0,
+ 0, streamdev_bucket, 0, streamdev_bucket,
+ &fsys);
+ if (err)
+ error (3, err, "trivfs_startup");
+
+ pthread_mutex_init (&global_lock, NULL);
+
+ pthread_cond_init (&open_alert, NULL);
+ pthread_cond_init (&select_alert, NULL);
+
+ if (trivfs_allow_open & O_READ)
+ input_buffer = create_buffer (256);
+ if (trivfs_allow_open & O_WRITE)
+ output_buffer = create_buffer (256);
+
+ /* Launch */
+ ports_manage_port_operations_multithread (streamdev_bucket, demuxer,
+ 0, 0, 0);
+
+ return 0;
+}
+
+
+int trivfs_fstype = FSTYPE_DEV;
+int trivfs_fsid = 0;
+
+int trivfs_support_read = 1;
+int trivfs_support_write = 1;
+int trivfs_support_exec = 0;
+
+int trivfs_allow_open = O_READ | O_WRITE;
+
+static error_t
+open_hook (struct trivfs_control *cntl, struct iouser *user, int flags)
+{
+ error_t err;
+ dev_mode_t mode;
+
+ if (flags & O_WRITE & ~trivfs_allow_open)
+ return EROFS;
+ if (flags & O_READ & ~trivfs_allow_open)
+ return EIO;
+
+ if ((flags & (O_READ|O_WRITE)) == 0)
+ return 0;
+
+ /* XXX */
+ if (flags & O_ASYNC)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ mode = 0;
+ if (flags & O_READ)
+ mode |= D_READ;
+ if (flags & O_WRITE)
+ mode |= D_WRITE;
+
+ if (!dev_already_opened ())
+ {
+ err = dev_open (stream_name, mode);
+ if (err)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return err;
+ }
+
+ if (!(flags & O_NONBLOCK))
+ {
+ if (pthread_hurd_cond_wait_np (&open_alert, &global_lock))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EINTR;
+ }
+
+ if (!dev_already_opened ())
+ {
+ pthread_mutex_unlock (&global_lock);
+ return ENODEV;
+ }
+ }
+ }
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t (*trivfs_check_open_hook) (struct trivfs_control *,
+ struct iouser *, int)
+ = open_hook;
+
+static error_t
+po_create_hook (struct trivfs_peropen *po)
+{
+ pthread_mutex_lock (&global_lock);
+ nperopens++;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+error_t (*trivfs_peropen_create_hook) (struct trivfs_peropen *) =
+ po_create_hook;
+
+static void
+po_destroy_hook (struct trivfs_peropen *po)
+{
+ pthread_mutex_lock (&global_lock);
+ nperopens--;
+ if (!nperopens)
+ {
+ if (dev_already_opened ())
+ {
+ clear_buffer (input_buffer);
+ dev_close ();
+ }
+ }
+ pthread_mutex_unlock (&global_lock);
+}
+
+void (*trivfs_peropen_destroy_hook) (struct trivfs_peropen *)
+ = po_destroy_hook;
+
+void
+trivfs_modify_stat (struct trivfs_protid *cred, struct stat *st)
+{
+ st->st_blksize = vm_page_size;
+ st->st_size = 0;
+
+ st->st_rdev = rdev;
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= S_IFCHR;
+ if ((trivfs_allow_open & O_READ) == 0)
+ st->st_mode &= ~(S_IRUSR | S_IRGRP | S_IROTH);
+ if ((trivfs_allow_open & O_WRITE) == 0)
+ st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+}
+
+error_t
+trivfs_goaway (struct trivfs_control *fsys, int flags)
+{
+ error_t err;
+ int force = (flags & FSYS_GOAWAY_FORCE);
+ int nosync = (flags & FSYS_GOAWAY_NOSYNC);
+ struct port_class *root_port_class = fsys->protid_class;
+
+ pthread_mutex_lock (&global_lock);
+
+ if (!dev_already_opened ())
+ exit (0);
+
+ err = ports_inhibit_class_rpcs (root_port_class);
+ if (err == EINTR || (err && !force))
+ {
+ pthread_mutex_unlock (&global_lock);
+ return err;
+ }
+
+ if (force && nosync)
+ exit (0);
+
+ if (!force && ports_count_class (root_port_class) > 0)
+ goto busy;
+
+ if (!nosync)
+ dev_close ();
+ exit (0);
+
+ busy:
+ ports_enable_class (root_port_class);
+ ports_resume_class_rpcs (root_port_class);
+ pthread_mutex_unlock (&global_lock);
+
+ return EBUSY;
+}
+
+
+error_t
+trivfs_S_io_read (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char **data, mach_msg_type_number_t *data_len,
+ loff_t offs, mach_msg_type_number_t amount)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_READ))
+ return EBADF;
+
+ pthread_mutex_lock (&global_lock);
+ err = dev_read (amount, (void **)data, data_len, cred->po->openmodes & O_NONBLOCK);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+error_t
+trivfs_S_io_readable (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ mach_msg_type_number_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_READ))
+ return EBADF;
+
+ pthread_mutex_lock (&global_lock);
+ err = dev_readable (amount);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+error_t
+trivfs_S_io_write (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ char *data, mach_msg_type_number_t data_len,
+ loff_t offs, mach_msg_type_number_t *amount)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_WRITE))
+ return EBADF;
+
+ pthread_mutex_lock (&global_lock);
+ err = dev_write ((void *)data, data_len, amount, cred->po->openmodes & O_NONBLOCK);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+error_t
+trivfs_S_io_seek (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t offs, int whence, off_t *new_offs)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else
+ return ESPIPE;
+}
+
+static error_t
+io_select_common (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec *tsp,
+ int *type)
+{
+ int available;
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ if (!(cred->po->openmodes & O_WRITE) && (*type & SELECT_WRITE))
+ return EBADF;
+
+ *type &= SELECT_READ | SELECT_WRITE;
+
+ if (*type == 0)
+ return 0;
+
+ available = 0;
+
+ while (1)
+ {
+ pthread_mutex_lock (&global_lock);
+ if ((*type & SELECT_READ) && buffer_readable (input_buffer))
+ available |= SELECT_READ;
+ if (output_buffer)
+ {
+ if ((*type & SELECT_WRITE) && buffer_writable (output_buffer))
+ available |= SELECT_WRITE;
+ }
+
+ if (available)
+ {
+ *type = available;
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ if (cred->po->openmodes & O_NONBLOCK)
+ {
+ pthread_mutex_unlock (&global_lock);
+ return EWOULDBLOCK;
+ }
+
+ ports_interrupt_self_on_port_death (cred, reply);
+ err = pthread_hurd_cond_timedwait_np (&select_alert, &global_lock, tsp);
+ if (err)
+ {
+ *type = 0;
+ pthread_mutex_unlock (&global_lock);
+
+ if (err == ETIMEDOUT)
+ err = 0;
+
+ return err;
+ }
+ }
+}
+
+error_t
+trivfs_S_io_select (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *type)
+{
+ return io_select_common (cred, reply, reply_type, NULL, type);
+}
+
+error_t
+trivfs_S_io_select_timeout (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ struct timespec ts,
+ int *type)
+{
+ return io_select_common (cred, reply, reply_type, &ts, type);
+}
+
+error_t
+trivfs_S_file_set_size (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ off_t size)
+{
+ if (!cred)
+ return EOPNOTSUPP;
+ else if (!(cred->po->openmodes & O_WRITE))
+ return EBADF;
+ else if (size < 0)
+ return EINVAL;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_get_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int *bits)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ {
+ *bits = cred->po->openmodes;
+ return 0;
+ }
+}
+
+error_t
+trivfs_S_io_set_all_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int mode)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_set_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_io_clear_some_openmodes (struct trivfs_protid *cred,
+ mach_port_t reply,
+ mach_msg_type_name_t reply_type,
+ int bits)
+{
+ if (! cred)
+ return EOPNOTSUPP;
+ else
+ return 0;
+}
+
+error_t
+trivfs_S_file_sync (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int wait, int omit_metadata)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ err = dev_sync (wait);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+error_t
+trivfs_S_file_syncfs (struct trivfs_protid *cred,
+ mach_port_t reply, mach_msg_type_name_t reply_type,
+ int wait, int dochildren)
+{
+ error_t err;
+
+ if (!cred)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+ err = dev_sync (wait);
+ pthread_mutex_unlock (&global_lock);
+ return err;
+}
+
+
+/* This flag is set if there is an outstanding device_write. */
+static int output_pending;
+
+/* This flag is set if there is an outstanding device_read. */
+static int input_pending;
+
+/* This flag is set if there is an outstanding device_open. */
+static int open_pending;
+
+static char pending_output[IO_INBAND_MAX];
+static int npending_output;
+
+/* This flag is set if EOF is returned. */
+static int eof;
+
+/* The error number. */
+static error_t err;
+
+static struct port_class *phys_reply_class;
+
+/* The Mach device_t representing the stream. */
+static device_t phys_device = MACH_PORT_NULL;
+
+/* The ports we get replies on for device calls. */
+static mach_port_t phys_reply_writes = MACH_PORT_NULL;
+static mach_port_t phys_reply = MACH_PORT_NULL;
+
+/* The port-info structures. */
+static struct port_info *phys_reply_writes_pi;
+static struct port_info *phys_reply_pi;
+
+static device_t device_master;
+
+/* The block size and whole size of the device. */
+static size_t dev_blksize;
+static size_t dev_size;
+
+
+/* Open a new device structure for the device NAME with MODE. If an error
+ occurs, the error code is returned, otherwise 0. */
+/* Be careful that the global lock is already locked. */
+error_t
+dev_open (const char *name, dev_mode_t mode)
+{
+ if (open_pending || (phys_device != MACH_PORT_NULL))
+ return 0;
+
+ err = get_privileged_ports (0, &device_master);
+ if (err)
+ return err;
+
+ phys_reply_class = ports_create_class (0, 0);
+ err = ports_create_port (phys_reply_class, streamdev_bucket,
+ sizeof (struct port_info), &phys_reply_pi);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), device_master);
+ return err;
+ }
+
+ phys_reply = ports_get_right (phys_reply_pi);
+ mach_port_insert_right (mach_task_self (), phys_reply, phys_reply,
+ MACH_MSG_TYPE_MAKE_SEND);
+
+ if (output_buffer)
+ {
+ err = ports_create_port (phys_reply_class, streamdev_bucket,
+ sizeof (struct port_info),
+ &phys_reply_writes_pi);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ mach_port_deallocate (mach_task_self (), device_master);
+ return err;
+ }
+
+ phys_reply_writes = ports_get_right (phys_reply_writes_pi);
+ mach_port_insert_right (mach_task_self (), phys_reply_writes,
+ phys_reply_writes, MACH_MSG_TYPE_MAKE_SEND);
+ }
+
+ err = device_open_request (device_master, phys_reply, mode, name);
+ if (err)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ if (output_buffer)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply_writes);
+ phys_reply_writes = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_writes_pi);
+ phys_reply_writes_pi = 0;
+ }
+ mach_port_deallocate (mach_task_self (), device_master);
+ return err;
+ }
+
+ open_pending = 1;
+ return 0;
+}
+
+kern_return_t
+device_open_reply (mach_port_t reply, int returncode, mach_port_t device)
+{
+ int sizes[DEV_GET_SIZE_COUNT];
+ size_t sizes_len = DEV_GET_SIZE_COUNT;
+ int amount;
+
+ if (reply != phys_reply)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ open_pending = 0;
+ pthread_cond_broadcast (&open_alert);
+
+ if (returncode != 0)
+ {
+ dev_close ();
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ phys_device = device;
+ eof = 0;
+
+ /* Get the block size and the whole size. */
+ err = device_get_status (device, DEV_GET_SIZE, sizes, &sizes_len);
+ if (err == D_INVALID_OPERATION)
+ {
+ /* XXX Assume that the block size is 1 and the whole size is 0. */
+ dev_blksize = 1;
+ dev_size = 0;
+ err = 0;
+ }
+ else if (err == 0)
+ {
+ assert (sizes_len == DEV_GET_SIZE_COUNT);
+
+ dev_blksize = sizes[DEV_GET_SIZE_RECORD_SIZE];
+ dev_size = sizes[DEV_GET_SIZE_DEVICE_SIZE];
+
+ assert (dev_blksize && dev_blksize <= IO_INBAND_MAX);
+ }
+ else
+ {
+ dev_close ();
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ amount = vm_page_size;
+ if (dev_blksize != 1)
+ amount = amount / dev_blksize * dev_blksize;
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+/* Check if the device is already opened. */
+/* Be careful that the global lock is already locked. */
+int
+dev_already_opened (void)
+{
+ return (phys_device != MACH_PORT_NULL);
+}
+
+/* Close the device. */
+/* Be careful that the global lock is already locked. */
+void
+dev_close (void)
+{
+ /* Sync all pending writes. */
+ dev_sync (1);
+
+ device_close (phys_device);
+ mach_port_deallocate (mach_task_self (), phys_device);
+ phys_device = MACH_PORT_NULL;
+
+ mach_port_deallocate (mach_task_self (), phys_reply);
+ phys_reply = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_pi);
+ phys_reply_pi = 0;
+ clear_buffer (input_buffer);
+ input_pending = 0;
+
+ if (output_buffer)
+ {
+ mach_port_deallocate (mach_task_self (), phys_reply_writes);
+ phys_reply_writes = MACH_PORT_NULL;
+ ports_port_deref (phys_reply_writes_pi);
+ phys_reply_writes_pi = 0;
+ clear_buffer (output_buffer);
+ npending_output = 0;
+ output_pending = 0;
+ }
+}
+
+/* Be careful that the global lock is already locked. */
+static error_t
+start_input (int nowait)
+{
+ int size;
+ error_t err;
+ size_t amount;
+
+ size = buffer_writable (input_buffer);
+
+ if (size < dev_blksize || input_pending)
+ return 0;
+
+ amount = vm_page_size;
+ if (dev_blksize != 1)
+ amount = amount / dev_blksize * dev_blksize;
+
+ err = device_read_request_inband (phys_device, phys_reply,
+ nowait? D_NOWAIT : 0,
+ 0, amount);
+ if (err == D_WOULD_BLOCK)
+ err = 0;
+ if (err)
+ dev_close ();
+ else
+ input_pending = 1;
+
+ return err;
+}
+
+/* Read up to AMOUNT bytes, returned in BUF and LEN. If NOWAIT is non-zero
+ and the buffer is empty, then returns EWOULDBLOCK. If an error occurs,
+ the error code is returned, otherwise 0. */
+/* Be careful that the global lock is already locked. */
+error_t
+dev_read (size_t amount, void **buf, size_t *len, int nowait)
+{
+ size_t max, avail;
+
+ if (err)
+ return err;
+
+ while (!buffer_readable (input_buffer))
+ {
+ err = start_input (nowait);
+ if (err)
+ return err;
+
+ if (eof)
+ {
+ *len = 0;
+ return 0;
+ }
+
+ if (nowait)
+ return EWOULDBLOCK;
+
+ if (pthread_hurd_cond_wait_np (input_buffer->wait, &global_lock))
+ return EINTR;
+ }
+
+ avail = buffer_size (input_buffer);
+ max = (amount < avail) ? amount : avail;
+ if (max > *len)
+ vm_allocate (mach_task_self (), (vm_address_t *)buf, max, 1);
+
+ *len = buffer_read (input_buffer, *buf, max);
+ assert (*len == max);
+
+ err = start_input (nowait);
+ return err;
+}
+
+error_t
+device_read_reply_inband (mach_port_t reply, error_t errorcode,
+ char *data, u_int datalen)
+{
+ if (reply != phys_reply)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ input_pending = 0;
+ err = errorcode;
+ if (!err)
+ {
+ if (datalen == 0)
+ {
+ eof = 1;
+ dev_close ();
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+
+ while (datalen)
+ {
+ size_t nwritten;
+
+ while (!buffer_writable (input_buffer))
+ pthread_cond_wait (input_buffer->wait, &global_lock);
+
+ nwritten = buffer_write (input_buffer, data, datalen);
+ data += nwritten;
+ datalen -= nwritten;
+ pthread_cond_broadcast (input_buffer->wait);
+ pthread_cond_broadcast (&select_alert);
+ }
+ }
+ else
+ {
+ dev_close ();
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+ }
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+/* Return current readable size in AMOUNT. If an error occurs, the error
+ code is returned, otherwise 0. */
+/* Be careful that the global lock is already locked. */
+error_t
+dev_readable (size_t *amount)
+{
+ *amount = buffer_size (input_buffer);
+ return 0;
+}
+
+/* Be careful that the global lock is already locked. */
+static error_t
+start_output (int nowait)
+{
+ int size;
+
+ assert (output_buffer);
+
+ size = buffer_size (output_buffer);
+
+ if (size < dev_blksize || output_pending)
+ return 0;
+
+ if (size + npending_output > IO_INBAND_MAX)
+ size = IO_INBAND_MAX - npending_output;
+
+ if (dev_blksize != 1)
+ size = size / dev_blksize * dev_blksize;
+
+ buffer_read (output_buffer, pending_output + npending_output, size);
+ npending_output += size;
+
+ err = device_write_request_inband (phys_device, phys_reply_writes,
+ nowait? D_NOWAIT : 0,
+ 0, pending_output, npending_output);
+ if (err == D_WOULD_BLOCK)
+ err = 0;
+ if (err)
+ dev_close ();
+ else
+ output_pending = 1;
+
+ return err;
+}
+
+/* Write LEN bytes from BUF, returning the amount actually written
+ in AMOUNT. If NOWAIT is non-zero and the buffer is full, then returns
+ EWOULDBLOCK. If an error occurs, the error code is returned,
+ otherwise 0. */
+/* Be careful that the global lock is already locked. */
+error_t
+dev_write (void *buf, size_t len, size_t *amount, int nowait)
+{
+ if (err)
+ return err;
+
+ while (!buffer_writable (output_buffer))
+ {
+ err = start_output (nowait);
+ if (err)
+ return err;
+
+ if (nowait)
+ return EWOULDBLOCK;
+
+ if (pthread_hurd_cond_wait_np (output_buffer->wait, &global_lock))
+ return EINTR;
+ }
+
+ *amount = buffer_write (output_buffer, buf, len);
+ err = start_output (nowait);
+
+ return err;
+}
+
+error_t
+device_write_reply_inband (mach_port_t reply, error_t returncode, int amount)
+{
+ if (reply != phys_reply_writes)
+ return EOPNOTSUPP;
+
+ pthread_mutex_lock (&global_lock);
+
+ output_pending = 0;
+
+ if (!returncode)
+ {
+ if (amount >= npending_output)
+ {
+ npending_output = 0;
+ pthread_cond_broadcast (output_buffer->wait);
+ pthread_cond_broadcast (&select_alert);
+ }
+ else
+ {
+ npending_output -= amount;
+ memmove (pending_output, pending_output + amount, npending_output);
+ }
+ }
+ else
+ dev_close ();
+
+ pthread_mutex_unlock (&global_lock);
+ return 0;
+}
+
+/* Try and write out any pending writes to the device. If WAIT is non-zero,
+ will wait for any activity to cease. */
+/* Be careful that the global lock is already locked. */
+error_t
+dev_sync (int wait)
+{
+ if (err)
+ return err;
+
+ if (!output_buffer || phys_device == MACH_PORT_NULL)
+ return 0;
+
+ while (buffer_readable (output_buffer) >= dev_blksize)
+ {
+ err = start_output (! wait);
+ if (err)
+ return err;
+
+ if (!wait)
+ return 0;
+
+ if (pthread_hurd_cond_wait_np (output_buffer->wait, &global_lock))
+ return EINTR;
+ }
+
+ /* XXX: When the size of output_buffer is non-zero and less than
+ DEV_BLKSIZE, the rest will be ignored or discarded. */
+ return 0;
+}
+
+/* Unused stubs. */
+kern_return_t
+device_read_reply (mach_port_t reply, kern_return_t returncode,
+ io_buf_ptr_t data, mach_msg_type_number_t amount)
+{
+ return EOPNOTSUPP;
+}
+
+kern_return_t
+device_write_reply (mach_port_t reply, kern_return_t returncode, int amount)
+{
+ return EOPNOTSUPP;
+}
diff --git a/trans/symlink.c b/trans/symlink.c
new file mode 100644
index 00000000..845a1121
--- /dev/null
+++ b/trans/symlink.c
@@ -0,0 +1,236 @@
+/* Translator for S_IFLNK nodes
+ Copyright (C) 1994, 2000, 2001, 2002 Free Software Foundation
+
+ 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 <hurd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <argp.h>
+#include <hurd/fsys.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <error.h>
+#include <version.h>
+#include "fsys_S.h"
+
+mach_port_t realnode;
+
+/* We return this for O_NOLINK lookups */
+mach_port_t realnodenoauth;
+
+/* We return this for non O_NOLINK lookups */
+char *linktarget;
+
+extern int fsys_server (mach_msg_header_t *, mach_msg_header_t *);
+
+const char *argp_program_version = STANDARD_HURD_VERSION (symlink);
+
+static const struct argp_option options[] =
+ {
+ { 0 }
+ };
+
+static const char args_doc[] = "TARGET";
+static const char doc[] = "A translator for symlinks."
+"\vA symlink is an alias for another node in the filesystem."
+"\n"
+"\nA symbolic link refers to its target `by name', and contains no actual"
+" reference to the target. The target referenced by the symlink is"
+" looked up in the namespace of the client.";
+
+/* Parse a single option/argument. */
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ if (key == ARGP_KEY_ARG && state->arg_num == 0)
+ linktarget = arg;
+ else if (key == ARGP_KEY_ARG || key == ARGP_KEY_NO_ARGS)
+ argp_usage (state);
+ else
+ return ARGP_ERR_UNKNOWN;
+ return 0;
+}
+
+static struct argp argp = { options, parse_opt, args_doc, doc };
+
+
+int
+main (int argc, char **argv)
+{
+ mach_port_t bootstrap;
+ mach_port_t control;
+ error_t err;
+
+ /* Parse our options... */
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error (1, 0, "Must be started as a translator");
+
+ linktarget = argv[1];
+
+ /* Reply to our parent */
+ mach_port_allocate (mach_task_self (), MACH_PORT_RIGHT_RECEIVE, &control);
+ mach_port_insert_right (mach_task_self (), control, control,
+ MACH_MSG_TYPE_MAKE_SEND);
+ err =
+ fsys_startup (bootstrap, 0, control, MACH_MSG_TYPE_COPY_SEND, &realnode);
+ mach_port_deallocate (mach_task_self (), control);
+ mach_port_deallocate (mach_task_self (), bootstrap);
+ if (err)
+ error (1, err, "Starting up translator");
+
+ io_restrict_auth (realnode, &realnodenoauth, 0, 0, 0, 0);
+ mach_port_deallocate (mach_task_self (), realnode);
+
+ /* Mark us as important. */
+ mach_port_t proc = getproc ();
+ if (proc == MACH_PORT_NULL)
+ error (2, err, "cannot get a handle to our process");
+
+ err = proc_mark_important (proc);
+ /* This might fail due to permissions or because the old proc server
+ is still running, ignore any such errors. */
+ if (err && err != EPERM && err != EMIG_BAD_ID)
+ error (2, err, "Cannot mark us as important");
+
+ mach_port_deallocate (mach_task_self (), proc);
+
+ /* Launch */
+ while (1)
+ {
+ /* The timeout here is 10 minutes */
+ err = mach_msg_server_timeout (fsys_server, 0, control,
+ MACH_RCV_TIMEOUT, 1000 * 60 * 10);
+ if (err == MACH_RCV_TIMED_OUT)
+ exit (0);
+ }
+}
+
+error_t
+S_fsys_getroot (mach_port_t fsys_t,
+ mach_port_t dotdotnode,
+ uid_t *uids, size_t nuids,
+ uid_t *gids, size_t ngids,
+ int flags,
+ retry_type *do_retry,
+ char *retry_name,
+ mach_port_t *ret,
+ mach_msg_type_name_t *rettype)
+{
+ if (flags & O_NOLINK)
+ {
+ /* Return our underlying node. */
+ *ret = realnodenoauth;
+ *rettype = MACH_MSG_TYPE_COPY_SEND;
+ *do_retry = FS_RETRY_REAUTH;
+ retry_name[0] = '\0';
+ return 0;
+ }
+ else
+ {
+ /* Return telling the user to follow the link */
+ strcpy (retry_name, linktarget);
+ if (linktarget[0] == '/')
+ {
+ *do_retry = FS_RETRY_MAGICAL;
+ *ret = MACH_PORT_NULL;
+ *rettype = MACH_MSG_TYPE_COPY_SEND;
+ }
+ else
+ {
+ *do_retry = FS_RETRY_REAUTH;
+ *ret = dotdotnode;
+ *rettype = MACH_MSG_TYPE_MOVE_SEND;
+ }
+ }
+ return 0;
+}
+
+error_t
+S_fsys_startup (mach_port_t bootstrap, int flags, mach_port_t control,
+ mach_port_t *real, mach_msg_type_name_t *realtype)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_goaway (mach_port_t control, int flags)
+{
+ exit (0);
+}
+
+error_t
+S_fsys_syncfs (mach_port_t control,
+ int wait,
+ int recurse)
+{
+ return 0;
+}
+
+error_t
+S_fsys_set_options (mach_port_t control,
+ char *data, mach_msg_type_number_t len,
+ int do_children)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_get_options (mach_port_t control,
+ char **data, mach_msg_type_number_t *len)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_getfile (mach_port_t control,
+ uid_t *uids, size_t nuids,
+ uid_t *gids, size_t ngids,
+ char *handle, size_t handllen,
+ mach_port_t *pt,
+ mach_msg_type_name_t *pttype)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_getpriv (mach_port_t control,
+ mach_port_t *host_priv, mach_msg_type_name_t *host_priv_type,
+ mach_port_t *dev_master, mach_msg_type_name_t *dev_master_type,
+ task_t *fs_task, mach_msg_type_name_t *fs_task_type)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_init (mach_port_t control,
+ mach_port_t reply,
+ mach_msg_type_name_t replytype,
+ mach_port_t proc,
+ auth_t auth)
+{
+ return EOPNOTSUPP;
+}
+
+error_t
+S_fsys_forward (mach_port_t server, mach_port_t requestor,
+ char *argz, size_t argz_len)
+{
+ return EOPNOTSUPP;
+}