diff options
Diffstat (limited to 'trans')
-rw-r--r-- | trans/Makefile | 67 | ||||
-rw-r--r-- | trans/bogus-fifo.c | 160 | ||||
-rw-r--r-- | trans/crash.c | 800 | ||||
-rw-r--r-- | trans/fakeroot.c | 1041 | ||||
-rw-r--r-- | trans/fifo.c | 628 | ||||
-rw-r--r-- | trans/firmlink.c | 288 | ||||
-rw-r--r-- | trans/fwd.c | 51 | ||||
-rw-r--r-- | trans/hello-mt.c | 332 | ||||
-rw-r--r-- | trans/hello.c | 293 | ||||
-rw-r--r-- | trans/ifsock.c | 152 | ||||
-rw-r--r-- | trans/magic.c | 567 | ||||
-rw-r--r-- | trans/mtab.c | 882 | ||||
-rw-r--r-- | trans/new-fifo.c | 857 | ||||
-rw-r--r-- | trans/null.c | 340 | ||||
-rw-r--r-- | trans/password.c | 230 | ||||
-rw-r--r-- | trans/proxy-defpager.c | 279 | ||||
-rw-r--r-- | trans/remap.c | 152 | ||||
-rw-r--r-- | trans/streamio.c | 1189 | ||||
-rw-r--r-- | trans/symlink.c | 236 |
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; +} |