summaryrefslogtreecommitdiff
path: root/console-client/trans.c
diff options
context:
space:
mode:
authorMarco Gerards <marco@gnu.org>2005-01-06 21:43:53 +0000
committerMarco Gerards <marco@gnu.org>2005-01-06 21:43:53 +0000
commit38fc4e4d1cef98a8b3d60b717647a9c65d91307f (patch)
tree805792e935ebbf353301070209d0af1aea1d908f /console-client/trans.c
parent4456094d61a6f5baabadc18096fc1f516921b736 (diff)
2005-01-06 Marco Gerards <metgerards@student.han.nl>
* Makefile (SRCS): Add `trans.c'. (LCLHDRS): Add `mach-inputdev.h'. (HURDLIBS): Add `netfs', `fshelp' and `iohelp'. (modules): Add `pc_mouse'. (pc_kbd.so.$(hurd-version)): Add `kdioctlServer.o' and `kbd-repeat.c'. (pc_mouse.so.$(hurd-version)): New variable. * console.c: Include <trans.h>. (DEFAULT_CONSOLE_NODE): New macro. (saved_id, saved_cons, consnode_path): New variables. (console_move_mouse): New function. (console_switch_away): New function. (console_switch_back): Likewise. (cons_vcons_set_mousecursor_pos): Likewise. (cons_vcons_set_mousecursor_status): Likewise. (options): Add the option `--console-node'. (parse_opt): Parse the options that were added to `options'. (main): Setup the console client translator node. * display.h (display_ops): New members `set_mousecursor_pos' and `set_mousecursor_status'. * driver.c (driver_start): Change the type of `i' to `unsigned int'. * driver.h (driver_ops): New members `save_status' and `restore_status'. * input.h (console_switch_away): New prototype. (console_switch_back): Likewise. (console_move_mouse): Likewise. * kbd-repeat.c: New file. * mach-inputdev.h: Likewise. * pc-mouse.c: Likewise. * trans.c: Likewise. * trans.h: Likewise. * pc-kbd.c: Include <argp.h> and "mach-inputdev.h". (DEFAULT_REPEATER_NODE): New macro. (repeater_node, cnode): New variables. (kev_type, mouse_motion, Scancode, m_deltaX, m_deltaY, MOUSE_LEFT) (MOUSE_MIDDLE, MOUSE_RIGHT, MOUSE_MOTION, KEYBD_EVENT) (IOCPARM_MASK, IOC_OUT, IOC_IN, _IOC, _IOR, _IOW, KDSKBDMODE, (KB_EVENT, KB_ASCII, KDGKBDTYPE, KB_VANILLAKB, KDSETLEDS): Removed. (gnumach_v1_input_next): Call the repeater when repeating is active. (doc, options, argp): New variables. (parse_opt): New function. (pc_kbd_init): Function rewritten. (pc_kbd_start): Initialize the repeater, when it is active. (pc_kbd_fini): Destroy the console node. * vga.c (vga_mousecursor): New struct. (vga_mousecursor_t): New type. (mousecursor): New variable. (hide_mousecursor): New function. (draw_mousecursor): Likewise. (vga_display_restore_status): Likewise. (vga_display_update): Likewise. (vga_set_mousecursor_pos): Likewise. (vga_set_mousecursor_status): Likewise. (vga_display_scroll): Update the mousecursor state. (driver_vga_ops): Add `vga_display_restore_status'. (vga_display_op): Add `vga_display_update', `vga_set_mousecursor_pos' and `vga_set_mousecursor_status'.
Diffstat (limited to 'console-client/trans.c')
-rw-r--r--console-client/trans.c806
1 files changed, 806 insertions, 0 deletions
diff --git a/console-client/trans.c b/console-client/trans.c
new file mode 100644
index 00000000..83342bcc
--- /dev/null
+++ b/console-client/trans.c
@@ -0,0 +1,806 @@
+/* trans.c -- Control a translator node for the repeaters.
+ Copyright (C) 2004, 2005 Free Software Foundation, Inc.
+ Written by Marco Gerards.
+
+ 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 <fcntl.h>
+#include <maptime.h>
+#include <stddef.h>
+#include <dirent.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <hurd/hurd_types.h>
+#include <error.h>
+#include <version.h>
+
+#include "trans.h"
+
+
+char *netfs_server_name = "console";
+char *netfs_server_version = HURD_VERSION;
+int netfs_maxsymlinks = 0;
+
+/* Handy source of time. */
+static volatile struct mapped_time_value *console_maptime;
+
+static consnode_t node_list = 0;
+
+struct netnode
+{
+ consnode_t node;
+};
+
+typedef mach_msg_header_t request_t;
+
+
+int
+console_demuxer (mach_msg_header_t *inp,
+ mach_msg_header_t *outp)
+{
+ int ret;
+ struct protid *user = (struct protid *) inp;
+ request_t *inop = (request_t *) inp;
+
+ ret = netfs_demuxer (inp, outp);
+ if (ret)
+ return ret;
+
+ user = ports_lookup_port (netfs_port_bucket, inop->msgh_local_port, netfs_protid_class);
+ if (!user)
+ return ret;
+
+ /* Don't do anything for the root node. */
+ if (user->po->np == netfs_root_node)
+ {
+ ports_port_deref (user);
+ return 0;
+ }
+
+ if (!ret && user->po->np->nn->node->demuxer)
+ ret = user->po->np->nn->node->demuxer (inp, outp);
+
+ ports_port_deref (user);
+ return ret;
+}
+
+
+
+/* Make sure that NP->nn_stat is filled with current information. CRED
+ identifies the user responsible for the operation. */
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+ return 0;
+}
+
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the owner to UID and the group to GID. */
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np,
+ uid_t uid, uid_t gid)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This should attempt a chauthor call for the user specified by CRED on node
+ NODE, to change the author to AUTHOR. */
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *np,
+ uid_t author)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This should attempt a chmod call for the user specified by CRED on 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)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred, struct node *np,
+ char *name)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or
+ S_IFCHR. */
+error_t
+netfs_attempt_mkdev (struct iouser *cred, struct node *np,
+ mode_t type, dev_t indexes)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This should attempt a chflags call for the user specified by CRED on node
+ NODE, to change the flags to FLAGS. */
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np,
+ int flags)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This should attempt a utimes call for the user specified by CRED on
+ locked node NP, to change the atime to ATIME and the mtime to
+ MTIME. If ATIME or MTIME is null, then set to the current
+ time. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *np,
+ struct timespec *atime, struct timespec *mtime)
+{
+ error_t err = fshelp_isowner (&np->nn_stat, cred);
+ int flags = TOUCH_CTIME;
+
+ if (! err)
+ {
+ if (mtime)
+ {
+ np->nn_stat.st_mtime = mtime->tv_sec;
+ np->nn_stat.st_mtime_usec = mtime->tv_nsec / 1000;
+ }
+ else
+ flags |= TOUCH_MTIME;
+
+ if (atime)
+ {
+ np->nn_stat.st_atime = atime->tv_sec;
+ np->nn_stat.st_atime_usec = atime->tv_nsec / 1000;
+ }
+ else
+ flags |= TOUCH_ATIME;
+
+ fshelp_touch (&np->nn_stat, flags, console_maptime);
+ }
+ return err;
+
+}
+
+
+/* This should attempt to set the size of the locked file NP (for user
+ CRED) to SIZE bytes long. */
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *np,
+ loff_t size)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt to fetch filesystem status information for the
+ remote filesystem, for the user CRED. NP is locked. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *np,
+ fsys_statfsbuf_t *st)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This should sync the locked file NP completely to disk, for the
+ user CRED. If WAIT is set, return only after the sync is
+ completely finished. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np,
+ int wait)
+{
+ return 0;
+}
+
+
+/* This should sync the entire remote filesystem. If WAIT is set,
+ return only after the sync is completely finished. */
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+
+/* Lookup NAME in DIR (which is locked) for USER; set *NP to the found
+ name upon return. If the name was not found, then return ENOENT.
+ On any error, clear *NP. (*NP, if found, should be locked and a
+ reference to it generated. This call should unlock DIR no matter
+ what.) */
+error_t
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **node)
+{
+ error_t err;
+ consnode_t cn;
+
+ *node = 0;
+ err = fshelp_access (&dir->nn_stat, S_IEXEC, user);
+ if (err)
+ goto out;
+
+ if (strcmp (name, ".") == 0)
+ {
+ /* Current directory -- just add an additional reference to DIR
+ and return it. */
+ netfs_nref (dir);
+ *node = dir;
+ goto out;
+ }
+
+ if (strcmp (name, "..") == 0)
+ {
+ err = EAGAIN;
+ goto out;
+ }
+
+ for (cn = node_list; cn; cn = cn->next)
+ if (!strcmp (name, cn->name))
+ {
+ if (cn->node == NULL)
+ {
+ struct netnode *nn = calloc (1, sizeof *nn);
+ if (nn == NULL)
+ {
+ err = ENOMEM;
+ goto out;
+ }
+
+ *node = netfs_make_node (nn);
+
+ nn->node = cn;
+ (*node)->nn_stat = netfs_root_node->nn_stat;
+ (*node)->nn_stat.st_mode = S_IFCHR | (netfs_root_node->nn_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+ (*node)->nn_stat.st_ino = 5;
+ cn->node = *node;
+ goto out;
+ }
+ else
+ {
+ *node = cn->node;
+
+ netfs_nref (*node);
+ goto out;
+ }
+ }
+
+ err = ENOENT;
+
+ out:
+ mutex_unlock (&dir->lock);
+ if (err)
+ *node = 0;
+ else
+ mutex_lock (&(*node)->lock);
+
+ if (!err && *node != dir && (*node)->nn->node->open)
+ (*node)->nn->node->open ();
+
+ return err;
+}
+
+
+error_t
+netfs_S_io_seek (struct protid *user, off_t offset,
+ int whence, off_t *newoffset)
+{
+ /* XXX: Will all nodes be device nodes? */
+ if (!user)
+ return EOPNOTSUPP;
+ else
+ return ESPIPE;
+}
+
+
+error_t
+netfs_S_io_select (struct protid *user, mach_port_t reply,
+ mach_msg_type_name_t replytype, int *type)
+{
+ struct node *np;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ np = user->po->np;
+
+ if (np->nn->node->select)
+ return np->nn->node->select (user, reply, replytype, type);
+ return EOPNOTSUPP;
+}
+
+
+/* Delete NAME in DIR (which is locked) for USER. */
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir,
+ char *name)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to rename the directory FROMDIR to TODIR. Note that neither
+ of the specific nodes are locked. */
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to create a new directory named NAME in DIR (which is
+ locked) for USER with mode MODE. */
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to remove directory named NAME in DIR (which is locked) for
+ USER. */
+error_t
+netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Create a link in DIR with name NAME to FILE for USER. Note that
+ neither DIR nor FILE are locked. If EXCL is set, do not delete the
+ target. Return EEXIST if NAME is already found in DIR. */
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to create an anonymous file related to DIR (which is
+ locked) for USER with MODE. Set *NP to the returned file upon
+ success. No matter what, unlock DIR. */
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **np)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Attempt to create a file named NAME in DIR (which is locked) for
+ USER with MODE. Set *NP to the new node upon return. On any
+ error, clear *NP. *NP should be locked on success; no matter what,
+ unlock DIR before returning. */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **np)
+{
+ *np = 0;
+ mutex_unlock (&dir->lock);
+ return EOPNOTSUPP;
+}
+
+
+/* Read the contents of locked node NP (a symlink), for USER, into
+ BUF. */
+error_t
+netfs_attempt_readlink (struct iouser *user, struct node *np,
+ char *buf)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* Locked node NP is being opened by USER, with FLAGS. NEWNODE is
+ nonzero if we just created this node. Return an error if we should
+ not permit the open to complete because of a permission
+ restriction. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *np,
+ int flags, int newnode)
+{
+ error_t err = 0;
+
+ if (flags & O_READ)
+ err = fshelp_access (&np->nn_stat, S_IREAD, user);
+ if (!err && (flags & O_WRITE))
+ err = fshelp_access (&np->nn_stat, S_IWRITE, user);
+ if (!err && (flags & O_EXEC))
+ err = fshelp_access (&np->nn_stat, S_IEXEC, user);
+ return err;
+
+}
+
+
+/* This function will never be called. It is only used when a node is
+ a symlink or by io_read, which is overridden. */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np,
+ loff_t offset, size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}
+
+
+/* This function will never be called. It is only called from
+ io_write, which is overridden. */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np,
+ loff_t offset, size_t *len, void *data)
+{
+ return EOPNOTSUPP;
+}
+
+
+error_t
+netfs_S_io_read (struct protid *user,
+ char **data,
+ mach_msg_type_number_t *datalen,
+ off_t offset,
+ mach_msg_type_number_t amount)
+{
+ struct node *np;
+
+ if (!user)
+ return EOPNOTSUPP;
+ np = user->po->np;
+
+ if (np->nn->node->read)
+ return np->nn->node->read (user, data, datalen, offset, amount);
+ return EOPNOTSUPP;
+}
+
+
+error_t
+netfs_S_io_write (struct protid *user,
+ char *data,
+ mach_msg_type_number_t datalen,
+ off_t offset,
+ mach_msg_type_number_t *amount)
+{
+ struct node *np;
+
+ if (!user)
+ return EOPNOTSUPP;
+
+ np = user->po->np;
+ if (np->nn->node->read)
+ return np->nn->node->write (user, data, datalen, offset, amount);
+ return EOPNOTSUPP;
+}
+
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and
+ O_EXEC) in *TYPES for locked file NP and user CRED. */
+error_t
+netfs_report_access (struct iouser *cred, struct node *np,
+ int *types)
+{
+ *types = 0;
+ if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+
+}
+
+/* Node NP has no more references; free all its associated storage. */
+void netfs_node_norefs (struct node *np)
+
+{
+ if (np->nn->node->close)
+ np->nn->node->close ();
+ np->nn->node->node = 0;
+
+ spin_unlock (&netfs_node_refcnt_lock);
+ free (np->nn);
+ free (np);
+}
+
+
+/* Returned directory entries are aligned to blocks this many bytes long.
+ Must be a power of two. */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+/* Length is structure before the name + the name + '\0', all
+ padded to a four-byte alignment. */
+#define DIRENT_LEN(name_len) \
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+ & ~(DIRENT_ALIGN - 1))
+
+/* Fill the array *DATA of size BUFSIZE with up to NENTRIES dirents
+ from DIR (which is locked) starting with entry ENTRY for user CRED.
+ The number of entries in the array is stored in *AMT and the number
+ of bytes in *DATACNT. If the supplied buffer is not large enough
+ to hold the data, it should be grown. */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int first_entry, int num_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ error_t err;
+ int count = 0;
+ size_t size = 0; /* Total size of our return block. */
+ consnode_t cn = node_list;
+ consnode_t first_node;
+
+
+ /* Add the length of a directory entry for NAME to SIZE and return true,
+ unless it would overflow MAX_DATA_LEN or NUM_ENTRIES, in which case
+ return false. */
+ int bump_size (const char *name)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ size_t new_size = size + DIRENT_LEN (strlen (name));
+ if (max_data_len > 0 && new_size > max_data_len)
+ return 0;
+ size = new_size;
+ count++;
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ if (dir != netfs_root_node)
+ return ENOTDIR;
+
+ for (first_node = node_list, count = 2;
+ first_node && first_entry > count;
+ first_node = first_node->next);
+ count++;
+
+ count = 0;
+
+ /* Make space for the `.' and `..' entries. */
+ if (first_entry == 0)
+ bump_size (".");
+ if (first_entry <= 1)
+ bump_size ("..");
+
+ for (cn = first_node; cn; cn = cn->next)
+ bump_size (cn->name);
+
+
+ /* Allocate it. */
+ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = ((void *) *data == (void *) -1) ? errno : 0;
+
+ if (! err)
+ /* Copy out the result. */
+ {
+ char *p = *data;
+
+ int add_dir_entry (const char *name, ino_t fileno, int type)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ struct dirent hdr;
+ size_t name_len = strlen (name);
+ size_t sz = DIRENT_LEN (name_len);
+
+ if (sz > size)
+ return 0;
+ else
+ size -= sz;
+
+ hdr.d_fileno = fileno;
+ hdr.d_reclen = sz;
+ hdr.d_type = type;
+ hdr.d_namlen = name_len;
+
+ memcpy (p, &hdr, DIRENT_NAME_OFFS);
+ strcpy (p + DIRENT_NAME_OFFS, name);
+ p += sz;
+
+ count++;
+
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ *data_len = size;
+ *data_entries = count;
+
+ count = 0;
+
+ /* Add `.' and `..' entries. */
+ if (first_entry == 0)
+ add_dir_entry (".", 2, DT_DIR);
+ if (first_entry <= 1)
+ add_dir_entry ("..", 2, DT_DIR);
+
+ /* Fill in the real directory entries. */
+ for (cn = first_node; cn; cn = cn->next)
+ if (!add_dir_entry (cn->name, cn->id, DT_CHR))
+ break;
+ }
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, console_maptime);
+ return err;
+}
+
+
+
+static any_t
+console_client_translator (any_t unused)
+{
+ error_t err;
+
+ do
+ {
+ ports_manage_port_operations_multithread (netfs_port_bucket,
+ console_demuxer,
+ 1000 * 60 * 2,
+ 1000 * 60 * 10,
+ 0);
+ err = netfs_shutdown (0);
+ }
+ while (err);
+ return 0;
+}
+
+
+/* Create a node with the name NAME and return it in *CN. */
+error_t
+console_create_consnode (const char *name, consnode_t *cn)
+{
+ *cn = malloc (sizeof (struct consnode));
+ if (!*cn)
+ return ENOMEM;
+
+ (*cn)->name = strdup (name);
+ if (!(*cn)->name)
+ {
+ free (cn);
+ return ENOMEM;
+ }
+
+ return 0;
+}
+
+
+/* Destroy the node CN. */
+void
+console_destroy_consnode (consnode_t cn)
+{
+ if (!cn)
+ return;
+ free (cn->name);
+ free (cn);
+}
+
+
+/* Register the node CN with the translator. */
+void
+console_register_consnode (consnode_t cn)
+{
+ cn->node = 0;
+ cn->next = node_list;
+ node_list = cn;
+}
+
+
+/* Unregister the node CN from the translator. */
+void
+console_unregister_consnode (consnode_t cn)
+{
+ if (!cn)
+ return;
+
+ if (node_list == cn)
+ node_list = cn->next;
+ else
+ {
+ consnode_t prev = node_list;
+
+ for (prev = node_list; prev->next != cn; prev = prev->next)
+ ;
+
+ prev->next = cn->next;
+ }
+}
+
+
+error_t
+console_setup_node (char *path)
+{
+ mach_port_t underlying;
+ mach_port_t bootstrap;
+ error_t err;
+ struct stat ul_stat;
+ file_t node;
+ struct port_info *newpi;
+ mach_port_t right;
+
+ node = file_name_lookup (path, O_CREAT|O_NOTRANS, 0664);
+ if (node == MACH_PORT_NULL)
+ return errno;
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ netfs_init ();
+
+ /* Create the root node (some attributes initialized below). */
+ netfs_root_node = netfs_make_node (0);
+ if (! netfs_root_node)
+ error (1, ENOMEM, "Cannot create root node");
+
+ err = maptime_map (0, 0, &console_maptime);
+ if (err)
+ error (1, err, "Cannot map time");
+
+ err = ports_create_port (netfs_control_class, netfs_port_bucket, sizeof (struct port_info), &newpi);
+ right = ports_get_send_right (newpi);
+ err = file_set_translator (node, 0, FS_TRANS_EXCL | FS_TRANS_SET, 0, 0, 0,
+ right, MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), right);
+ underlying = node;
+
+ err = io_stat (node, &ul_stat);
+ if (err)
+ error (1, err, "Cannot stat underlying node");
+
+ netfs_root_node->nn_stat.st_ino = 2;
+ netfs_root_node->nn_stat.st_uid = ul_stat.st_uid;
+ netfs_root_node->nn_stat.st_gid = ul_stat.st_gid;
+ netfs_root_node->nn_stat.st_author = ul_stat.st_author;
+ netfs_root_node->nn_stat.st_mode = S_IFDIR | (ul_stat.st_mode & ~S_IFMT & ~S_ITRANS);
+ netfs_root_node->nn_stat.st_fsid = getpid ();
+ netfs_root_node->nn_stat.st_nlink = 1;
+ netfs_root_node->nn_stat.st_size = 0;
+ netfs_root_node->nn_stat.st_blocks = 0;
+ netfs_root_node->nn_stat.st_fstype = FSTYPE_MISC;
+ netfs_root_node->nn_translated = 0;
+
+ /* If the underlying node isn't a directory, propagate read permission to
+ execute permission since we need that for lookups. */
+ if (! S_ISDIR (ul_stat.st_mode))
+ {
+ if (ul_stat.st_mode & S_IRUSR)
+ netfs_root_node->nn_stat.st_mode |= S_IXUSR;
+ if (ul_stat.st_mode & S_IRGRP)
+ netfs_root_node->nn_stat.st_mode |= S_IXGRP;
+ if (ul_stat.st_mode & S_IROTH)
+ netfs_root_node->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ fshelp_touch (&netfs_root_node->nn_stat, TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME,
+ console_maptime);
+
+ cthread_detach (cthread_fork (console_client_translator, NULL));
+
+ return 0;
+}