summaryrefslogtreecommitdiff
path: root/devio/devio.c
diff options
context:
space:
mode:
Diffstat (limited to 'devio/devio.c')
-rw-r--r--devio/devio.c451
1 files changed, 451 insertions, 0 deletions
diff --git a/devio/devio.c b/devio/devio.c
new file mode 100644
index 00000000..05343b7e
--- /dev/null
+++ b/devio/devio.c
@@ -0,0 +1,451 @@
+/* A translator for doing I/O to mach kernel devices.
+
+ 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 <hurd/ports.h>
+#include <hurd/pager.h>
+#include <hurd/trivfs.h>
+#include <hurd/fsys.h>
+
+#include <stdio.h>
+#include <error.h>
+#include <getopt.h>
+#include <assert.h>
+#include <fcntl.h>
+
+#include "open.h"
+#include "dev.h"
+#include "ptypes.h"
+
+/* ---------------------------------------------------------------- */
+
+#define USAGE "Usage: %s [OPTION...] DEVICE\n"
+
+static void
+usage(int status)
+{
+ if (status != 0)
+ fprintf(stderr, "Try `%s --help' for more information.\n",
+ program_invocation_name);
+ else
+ {
+ printf(USAGE, program_invocation_name);
+ printf("\
+\n\
+ -d, --devnum=NUM Give DEVICE a device number NUM\n\
+ -r, --readonly Disable writing to DEVICE\n\
+ -p, --seekable Enable seeking if DEVICE is serial\n\
+ -s, --serial Indicate that DEVICE has a single R/W point\n\
+ -b, --buffered, --block Open DEVICE in `block' mode, which allows reads\n\
+ or writes less than a single block and buffers\n\
+ I/O to the actual device. By default, all reads\n\
+ and writes are made directly to the device,\n\
+ with no buffering, and any sub-block-size I/O\n\
+ is padded to the nearest full block.\n\
+ -B NUM, --block-size=NUM Use a block size of NUM, which must be an integer\n\
+ multiple of DEVICE's real block size\n\
+");
+ }
+
+ exit(status);
+}
+
+#define SHORT_OPTIONS "bB:d:D:?rpsu"
+
+static struct option options[] =
+{
+ {"block-size", required_argument, 0, 'B'},
+ {"debug", required_argument, 0, 'D'},
+ {"help", no_argument, 0, '?'},
+ {"devnum", required_argument, 0, 'm'},
+ {"block", no_argument, 0, 'b'},
+ {"buffered", no_argument, 0, 'b'},
+ {"readonly", no_argument, 0, 'r'},
+ {"seekable", no_argument, 0, 'p'},
+ {"serial", no_argument, 0, 's'},
+ {0, 0, 0, 0}
+};
+
+
+/* ---------------------------------------------------------------- */
+
+/* A struct dev for the open kernel device. */
+static struct dev *device = NULL;
+
+/* Desired device parameters specified by the user. */
+static char *device_name = NULL;
+static int device_flags = 0;
+static int device_block_size = 0;
+
+/* A unixy device number to return when the device is stat'd. */
+static int device_number = 0;
+
+/* A stream on which we can print debugging message. */
+FILE *debug = NULL;
+/* A lock to use while doing so. */
+struct mutex debug_lock;
+
+void main(int argc, char *argv[])
+{
+ error_t err;
+ mach_port_t bootstrap;
+ int opt;
+ struct trivfs_control *trivfs_control;
+ mach_port_t realnode, control;
+
+ while ((opt = getopt_long(argc, argv, SHORT_OPTIONS, options, 0)) != EOF)
+ switch (opt)
+ {
+ case 'r': device_flags |= DEV_READONLY; break;
+ case 's': device_flags |= DEV_SERIAL; break;
+ case 'b': device_flags |= DEV_BUFFERED; break;
+ case 'p': device_flags |= DEV_SEEKABLE; break;
+ case 'B': device_block_size = atoi(optarg); break;
+ case 'd': device_number = atoi(optarg); break;
+ case 'D': debug = fopen(optarg, "w+"); setlinebuf(debug); break;
+ case '?': usage(0);
+ default: usage(1);
+ }
+
+ mutex_init(&debug_lock);
+
+ if (device_flags & DEV_READONLY)
+ /* Catch illegal writes at the point of open. */
+ trivfs_allow_open &= ~O_WRITE;
+
+ if (argv[optind] == NULL || argv[optind + 1] != NULL)
+ {
+ fprintf(stderr, USAGE, program_invocation_name);
+ usage(1);
+ }
+
+ device_name = argv[optind];
+
+ _libports_initialize();
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ if (bootstrap == MACH_PORT_NULL)
+ error(2, 0, "Must be started as a translator");
+
+ /* Reply to our parent */
+ control = trivfs_handle_port (MACH_PORT_NULL, PT_FSYS, PT_NODE);
+ err = fsys_startup (bootstrap, control, MACH_MSG_TYPE_MAKE_SEND, &realnode);
+
+ /* Install the returned realnode for trivfs's use */
+ trivfs_control = ports_check_port_type (control, PT_FSYS);
+ assert (trivfs_control);
+ ports_change_hardsoft (trivfs_control, 1);
+ trivfs_control->underlying = realnode;
+ ports_done_with_port (trivfs_control);
+
+ /* Open the device only when necessary. */
+ device = NULL;
+
+ /* Launch. */
+ ports_manage_port_operations_multithread ();
+
+ exit(0);
+}
+
+/* Called whenever someone tries to open our node (even for a stat). We
+ delay opening the kernel device until this point, as we can usefully
+ return errors from here. */
+static error_t
+check_open_hook (struct trivfs_control *trivfs_control,
+ uid_t *uids, u_int nuids,
+ gid_t *gids, u_int ngids,
+ int flags)
+{
+ error_t err = 0;
+
+ if (device == NULL)
+ /* Try and open the device. */
+ {
+ err = dev_open(device_name, device_flags, device_block_size, &device);
+ if (err)
+ device = NULL;
+ if (err && (flags & (O_READ|O_WRITE)) == 0)
+ /* If we're not opening for read or write, then just ignore the
+ error, as this allows stat to word correctly. XXX */
+ err = 0;
+ }
+
+ return err;
+}
+
+static void
+open_hook(struct trivfs_peropen *peropen)
+{
+ if (device)
+ open_create(device, (struct open **)&peropen->hook);
+}
+
+static void
+close_hook(struct trivfs_peropen *peropen)
+{
+ if (peropen->hook)
+ open_free(peropen->hook);
+}
+
+static void
+clean_exit(int status)
+{
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "cleaning up and exiting (status = %d)...\n", status);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ if (device)
+ {
+ dev_close(device);
+ device = NULL;
+ }
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "Bye!\n");
+ fclose(debug);
+ debug = NULL;
+ mutex_unlock(&debug_lock);
+ }
+#endif
+}
+
+/* ---------------------------------------------------------------- */
+/* 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;
+
+int trivfs_protid_porttypes[] = {PT_NODE};
+int trivfs_cntl_porttypes[] = {PT_FSYS};
+int trivfs_protid_nporttypes = 1;
+int trivfs_cntl_nporttypes = 1;
+
+void
+trivfs_modify_stat (struct stat *st)
+{
+ if (device)
+ {
+ vm_size_t size = device->size;
+
+ if (device->block_size > 1)
+ st->st_blksize = device->block_size;
+
+ st->st_size = size;
+ st->st_blocks = size / 512;
+
+ if (dev_is(device, DEV_READONLY))
+ st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= dev_is(device, DEV_BUFFERED) ? S_IFBLK : S_IFCHR;
+ }
+ else
+ /* Try and do things without an open device... */
+ {
+ st->st_blksize = device_block_size;
+ st->st_size = 0;
+ st->st_blocks = 0;
+ st->st_mode &= ~S_IFMT;
+ st->st_mode |= (device_flags & DEV_BUFFERED) ? S_IFBLK : S_IFCHR;
+ if (device_flags & DEV_READONLY)
+ st->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ }
+
+ st->st_fstype = FSTYPE_DEV;
+ st->st_rdev = device_number;
+}
+
+error_t
+trivfs_goaway (int flags, mach_port_t realnode, int ctltype, int pitype)
+{
+ if (device != NULL && !(flags & FSYS_GOAWAY_FORCE))
+ /* By default, don't go away if there are still opens on this device. */
+ return EBUSY;
+
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "trivfs_goaway(0x%x, %d, %d, %d)\n",
+ flags, realnode, ctltype, pitype);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ if (flags & FSYS_GOAWAY_NOSYNC)
+ exit(0);
+ else
+ clean_exit(0);
+}
+
+/* If this variable is set, it is called every time an open happens.
+ UIDS, GIDS, and FLAGS are from the open; CNTL identifies the
+ node being opened. This call need not check permissions on the underlying
+ node. If the open call should block, then return EWOULDBLOCK. Other
+ errors are immediately reflected to the user. If O_NONBLOCK
+ is not set in FLAGS and EWOULDBLOCK is returned, then call
+ trivfs_complete_open when all pending open requests for this
+ file can complete. */
+error_t (*trivfs_check_open_hook)(struct trivfs_control *trivfs_control,
+ uid_t *uids, u_int nuids,
+ gid_t *gids, u_int ngids,
+ int flags)
+ = check_open_hook;
+
+/* If this variable is set, it is called every time a new peropen
+ structure is created and initialized. */
+void (*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;
+
+/* Sync this filesystem. */
+kern_return_t
+trivfs_S_fsys_syncfs (struct trivfs_control *cntl,
+ mach_port_t reply, mach_msg_type_name_t replytype,
+ int wait, int dochildren)
+{
+#ifdef MSG
+ if (debug && device)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "syncing filesystem...\n");
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ if (device)
+ return dev_sync(device, wait);
+ else
+ return 0;
+}
+
+/* ---------------------------------------------------------------- */
+/* Ports hooks */
+
+void (*ports_cleanroutines[])(void *) =
+{
+ [PT_FSYS] = trivfs_clean_cntl,
+ [PT_NODE] = trivfs_clean_protid,
+};
+
+int
+ports_demuxer (mach_msg_header_t *inp, mach_msg_header_t *outp)
+{
+ error_t err;
+#ifdef MSG
+ static int next_msg_num = 0;
+ int msg_num;
+
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ msg_num = next_msg_num++;
+ fprintf(debug, "port_demuxer(%d) [%d]\n", inp->msgh_id, msg_num);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ err = pager_demuxer(inp, outp) || trivfs_demuxer(inp, outp);
+
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "port_demuxer(%d) [%d] done!\n", inp->msgh_id, msg_num);
+ mutex_unlock(&debug_lock);
+ }
+#endif
+
+ return err;
+}
+
+/* This will be called whenever there have been no requests to the server for
+ a significant period of time. NHARD is the number of live hard ports;
+ NSOFT is the number of live soft ports. This function is called while an
+ internal lock is held, so it cannot reliably call any other functions of
+ the ports library. */
+void
+ports_notice_idle (int nhard, int nsoft)
+{
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "ports_notice_idle(%d, %d)\n", nhard, nsoft);
+ mutex_unlock(&debug_lock);
+ }
+ else
+#endif
+ if (nhard == 0)
+ clean_exit(0);
+}
+
+/* This will be called whenever there are no hard ports or soft ports
+ allocated. This function is called while an internal lock is held, so it
+ cannot reliably call any other functions of the ports library. */
+void
+ports_no_live_ports ()
+{
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "ports_no_live_ports()\n");
+ mutex_unlock(&debug_lock);
+ }
+ else
+#endif
+ clean_exit(0);
+}
+
+/* This will be called whenever there are no hard ports allocated but there
+ are still some soft ports. This function is called while an internal lock
+ is held, so it cannot reliably call any other functions of the ports
+ library. */
+void
+ports_no_hard_ports ()
+{
+#ifdef MSG
+ if (debug)
+ {
+ mutex_lock(&debug_lock);
+ fprintf(debug, "ports_no_hard_ports()\n");
+ mutex_unlock(&debug_lock);
+ }
+#endif
+ if (device != NULL)
+ {
+ dev_close(device);
+ device = NULL;
+ }
+}