diff options
author | Moritz Schulte <moritz@duesseldorf.ccc.de> | 2002-04-15 23:11:59 +0000 |
---|---|---|
committer | Moritz Schulte <moritz@duesseldorf.ccc.de> | 2002-04-15 23:11:59 +0000 |
commit | 6fa433dc3d76ce6cd0a90fa744bce4f89132d830 (patch) | |
tree | 8b0d76eb82eea350078098aa9e4c79efac87d679 |
Initial import, second try.
-rw-r--r-- | Makefile | 44 | ||||
-rw-r--r-- | lib.c | 63 | ||||
-rw-r--r-- | lib.h | 26 | ||||
-rw-r--r-- | main.c | 129 | ||||
-rw-r--r-- | netfs.c | 660 | ||||
-rw-r--r-- | netio.h | 56 | ||||
-rw-r--r-- | node.c | 222 | ||||
-rw-r--r-- | node.h | 33 | ||||
-rw-r--r-- | protocol.c | 152 | ||||
-rw-r--r-- | protocol.h | 23 | ||||
-rw-r--r-- | version.h | 30 |
11 files changed, 1438 insertions, 0 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..6e5259276 --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +# netio - creates socket ports via the filesystem +# Copyright (C) 2001, 02 Moritz Schulte <moritz@duesseldorf.ccc.de> +# +# 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 of the License, 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 + +CFLAGS = -g -Wall -D_GNU_SOURCE + +netio: main.o netfs.o node.o lib.o protocol.o + gcc $(CFLAGS) -lnetfs -lfshelp -liohelp -lthreads -lports \ + -lihash -lshouldbeinlibc \ + -o netio main.o netfs.o node.o lib.o protocol.o + +main.o: main.c netio.h lib.h node.h protocol.h + gcc $(CFLAGS) -c main.c + +netfs.o: netfs.c netio.h lib.h node.h protocol.h + gcc $(CFLAGS) -c netfs.c + +lib.o: lib.c lib.h + gcc $(CFLAGS) -c lib.c + +node.o: node.c netio.h lib.h protocol.h + gcc $(CFLAGS) -c node.c + +protocol.o: protocol.c netio.h lib.h node.h + gcc $(CFLAGS) -c protocol.c + +clean: + rm -rf *.o netio + +.PHONY: clean @@ -0,0 +1,63 @@ +/* netio - creates socket ports via the filesystem + Copyright (C) 2001, 02 Moritz Schulte <moritz@duesseldorf.ccc.de> + + 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 of the + License, 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 <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <hurd.h> +#include <hurd/socket.h> +#include <hurd/paths.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <errno.h> +#include <error.h> + +#include "lib.h" + +/* Try to alloc SIZE bytes into *MEM. Return ENOMEM if malloc() + failed. */ +error_t +my_malloc (size_t size, void **mem) +{ + *mem = malloc (size); + if (! *mem) + return ENOMEM; + return 0; +} + +/* Open a port to the socket server for the protocol family number NO + and store it in *SOCK. Return 0 on success or an error code. */ +error_t +open_socket_server (int no, pf_t *sock) +{ + char *path; + error_t err; + pf_t port; + err = asprintf (&path, "%s/%i", _SERVERS_SOCKET, no); + if (err < 0) + return ENOMEM; + err = 0; + port = file_name_lookup (path, 0, 0); + free (path); + if (port == MACH_PORT_NULL) + err = errno; + else + *sock = port; + return err; +} @@ -0,0 +1,26 @@ +/* netio - creates socket ports via the filesystem + Copyright (C) 2001, 02 Moritz Schulte <moritz@duesseldorf.ccc.de> + + 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 of the + License, 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 <errno.h> +#include <stdint.h> +#include <hurd/socket.h> + +#define STRING_EQUAL(s1, s2) (! strcmp (s1, s2)) + +error_t my_malloc (size_t size, void **mem); +error_t open_socket_server (int no, pf_t *sock); @@ -0,0 +1,129 @@ +/* netio - creates socket ports via the filesystem + Copyright (C) 2001, 02 Moritz Schulte <moritz@duesseldorf.ccc.de> + + 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 of the + License, 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.h> +#include <hurd/netfs.h> +#include <hurd/paths.h> +#include <argp.h> +#include <errno.h> +#include <error.h> +#include <stdio.h> +#include <sys/socket.h> +#include <unistd.h> + +#include "netio.h" +#include "lib.h" +#include "node.h" +#include "protocol.h" + +#include "version.h" + +const char *argp_program_version = STANDARD_HURD_VERSION (netio); +const char *argp_program_bug_address = +"Moritz Schulte <moritz@duesseldorf.ccc.de>"; +const char *doc = "Hurd netio translator v" NETIO_VERSION; + +/* The underlying node. */ +mach_port_t ul_node; + +/* The socket server, PF_INET for us. */ +pf_t socket_server; + +/* Has to be defined for libnetfs... */ +int netfs_maxsymlinks = 0; + +/* Used for updating node information. */ +volatile struct mapped_time_value *netio_maptime; + +/* argp options. */ +static const struct argp_option netio_options[] = +{ + { 0 } +}; + +/* argp option parser. */ +static error_t +parse_opts (int key, char *arg, struct argp_state *sate) +{ + return 0; +} + +/* Main. */ +int +main (int argc, char **argv) +{ + struct argp netio_argp = { netio_options, parse_opts, + NULL, doc, NULL }; + mach_port_t bootstrap_port; + error_t err; + + argp_parse (&netio_argp, argc, argv, 0, 0, 0); + task_get_bootstrap_port (mach_task_self (), &bootstrap_port); + netfs_init (); + ul_node = netfs_startup (bootstrap_port, 0); + + err = node_make_root_node (&netfs_root_node); + if (err) + error (EXIT_FAILURE, err, "cannot create root node"); + + { + /* Here we adjust the root node permissions. */ + struct stat ul_node_stat; + err = io_stat (ul_node, &ul_node_stat); + if (err) + error (EXIT_FAILURE, err, "cannot stat underlying node"); + netfs_root_node->nn_stat.st_fsid = getpid (); + netfs_root_node->nn_stat = ul_node_stat; + netfs_root_node->nn_stat.st_mode = S_IFDIR | (ul_node_stat.st_mode + & ~S_IFMT & ~S_ITRANS); + + /* If the underlying node isn't a directory, enhance the stat + information, if needed. */ + if (! S_ISDIR (ul_node_stat.st_mode)) + { + if (ul_node_stat.st_mode & S_IRUSR) + netfs_root_node->nn_stat.st_mode |= S_IXUSR; + if (ul_node_stat.st_mode & S_IRGRP) + netfs_root_node->nn_stat.st_mode |= S_IXGRP; + if (ul_node_stat.st_mode & S_IROTH) + netfs_root_node->nn_stat.st_mode |= S_IXOTH; + } + } + + err = maptime_map (0, 0, &netio_maptime); + if (err) + error (EXIT_FAILURE, err, "cannot map time"); + + fshelp_touch (&netfs_root_node->nn_stat, + TOUCH_ATIME|TOUCH_MTIME|TOUCH_CTIME, + netio_maptime); + + err = open_socket_server (PF_INET, &socket_server); + if (err) + error (EXIT_FAILURE, err, "open_socket_server"); + + err = protocol_register_protocols (); + if (err) + error (EXIT_FAILURE, err, "protocol_register_protocols"); + + for (;;) + netfs_server_loop (); + + /* Never reached. */ + exit (0); +} diff --git a/netfs.c b/netfs.c new file mode 100644 index 000000000..149732b8a --- /dev/null +++ b/netfs.c @@ -0,0 +1,660 @@ +/* netio - creates socket ports via the filesystem + Copyright (C) 2001, 02 Moritz Schulte <moritz@duesseldorf.ccc.de> + + 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 of the + License, 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 <hurd/socket.h> +#include <argz.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <errno.h> +#include <stdio.h> +#include <stddef.h> +#include <dirent.h> +#include <sys/mman.h> + +#include "netio.h" +#include "lib.h" +#include "node.h" +#include "protocol.h" + +/* Returned directory entries are aligned to blocks this many bytes + long. Must be a power of two. For netfs_get_dirents. Taken from + hostmux. */ +#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)) + +/* The user must define this function. 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; + err = fshelp_access (&dir->nn_stat, S_IEXEC, user); + if (err) + goto out; + if (STRING_EQUAL (name, ".")) + { + *node = dir; + netfs_nref (*node); + } + else if (STRING_EQUAL (name, "..")) + { + *node = dir->nn->dir; + netfs_nref (*node); + } + else + { + if (dir->nn->flags & PROTOCOL_NODE) + err = node_make_host_node (user, dir, name, node); + else if (dir->nn->flags & HOST_NODE) + err = node_make_port_node (user, dir, name, node); + else if (dir->nn->flags & ROOT_NODE) + { + err = protocol_find_node (name, node); + if (! err) + netfs_nref (*node); + } + else + err = EOPNOTSUPP; /* FIXME? */ + } + + out: + fshelp_touch (&dir->nn_stat, TOUCH_ATIME, netio_maptime); + mutex_unlock (&dir->lock); + if (err) + *node = 0; + else + mutex_lock (&(*node)->lock); + return err; +} + +/* Return an argz string describing the current options. Fill *ARGZ + with a pointer to newly malloced storage holding the list and *LEN + to the length of that storage. */ +error_t +netfs_append_args (char **argz, size_t *argz_len) +{ + return 0; +} + +/* 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 + locked node NP, 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 locked node NP, thereby changing 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 + 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) +{ + error_t err; + + /* We only support permission chmod's on the root node. */ + if (np->nn->dir || + ((mode & S_IFMT) | (np->nn_stat.st_mode & S_IFMT)) + != (np->nn_stat.st_mode & S_IFMT)) + err = EOPNOTSUPP; + else + { + if (! (err = fshelp_isowner (&np->nn_stat, cred))) + { + np->nn_stat.st_mode = mode | (np->nn_stat.st_mode & S_IFMT); + fshelp_touch (&np->nn_stat, TOUCH_CTIME, netio_maptime); + } + } + return err; +} + +/* 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) +{ + return EOPNOTSUPP; +} + +/* Attempt to turn NODE (user CRED) into a device. TYPE is either + S_IFBLK or S_IFCHR. NP is locked. */ +error_t +netfs_attempt_mkdev (struct iouser *cred, struct node *np, + mode_t type, dev_t indexes) +{ + return EOPNOTSUPP; +} + +/* Attempt to set the passive translator record for FILE to ARGZ (of + length ARGZLEN) for user CRED. NP is locked. */ +error_t +netfs_set_translator (struct iouser *cred, struct node *np, + char *argz, size_t argzlen) +{ + return EOPNOTSUPP; +} + +/* For locked node NODE with S_IPTRANS set in its mode, look up the + name of its translator. Store the name into newly malloced + storage, and return it in *ARGZ; set *ARGZ_LEN to the total length. */ +error_t +netfs_get_translator (struct node *node, char **argz, + size_t *argz_len) +{ + return EOPNOTSUPP; +} + +/* This should attempt a chflags call for the user specified by CRED + on locked node NP, 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) +{ + return 0; +} + +/* 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, + off_t size) +{ + return 0; +} + +/* 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, + struct statfs *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 EOPNOTSUPP; +} + +/* 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; +} + +/* 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) +{ + /* Let's allow unlinking of protocol directories. */ + if (dir->nn->dir) + return EOPNOTSUPP; + return protocol_unregister (name); +} + +/* 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; +} + +/* (We don't use this function!) 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) +{ + 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; +} + +/* libnetfs uses this functions once. */ +error_t +netfs_check_open_permissions (struct iouser *user, struct node *np, + int flags, int newnode) +{ + error_t err = 0; + if (! err && (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; +} + +/* Read from the locked file NP for user CRED starting at OFFSET and + continuing for up to *LEN bytes. Put the data at DATA. Set *LEN + to the amount successfully read upon return. */ +error_t +netfs_attempt_read (struct iouser *cred, struct node *np, + off_t offset, size_t *len, void *data) +{ + error_t err = 0; + if (! (np->nn->flags & PORT_NODE)) + *len = 0; + { + addr_port_t addrport; + char *bufp = (char *) data; + mach_msg_type_number_t nread = *len; + mach_port_t *ports; + mach_msg_type_number_t nports; + char *cdata = NULL; + mach_msg_type_number_t clen = 0; + int flags = 0; + + if (err) + goto out; + err = socket_recv (np->nn->sock, &addrport, flags, &bufp, &nread, + &ports, &nports, &cdata, &clen, &flags, + nread); + if (err) + goto out; + + mach_port_deallocate (mach_task_self (), addrport); + vm_deallocate (mach_task_self (), (vm_address_t) cdata, clen); + if (bufp != (char *) data) + { + memcpy ((char *) data, bufp, nread); + vm_deallocate (mach_task_self (), (vm_address_t) bufp, nread); + } + } + out: + return err; +} + +/* Write to the locked file NP for user CRED starting at OFSET and + continuing for up to *LEN bytes from DATA. Set *LEN to the amount + successfully written upon return. */ +error_t +netfs_attempt_write (struct iouser *cred, struct node *np, + off_t offset, size_t *len, void *data) +{ + error_t err = 0; + if (! np->nn->connected) + err = node_connect_socket (np); + if (err) + goto out; + err = socket_send (np->nn->sock, MACH_PORT_NULL, 0, (char *) data, + *len, NULL, MACH_MSG_TYPE_COPY_SEND, 0, NULL, 0, + (int *) len); + out: + return err; +} + +/* 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; +} + +/* Create a new user from the specified UID and GID arrays. */ +struct iouser * +netfs_make_user (uid_t *uids, int nuids, uid_t *gids, int ngids) +{ + return NULL; +} + +/* Node NP has no more references; free all its associated storage. */ +void +netfs_node_norefs (struct node *np) +{ + node_destroy (np); +} + +/* 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. Taken from hostmux. */ +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; + size_t size = 0; /* Total size of our return block. */ + struct node *first_name, *nm; + + /* 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->nn->flags & PORT_NODE) + return ENOTDIR; + + /* Find the first entry. */ + for (first_name = dir->nn->entries, count = 0; + first_name && first_entry > count; + first_name = first_name->next) + count++; + + count = 0; + + /* Make space for the `.' and `..' entries. */ + if (first_entry == 0) + bump_size ("."); + if (first_entry <= 1) + bump_size (".."); + + /* See how much space we need for the result. */ + for (nm = first_name; nm; nm = nm->next) + if (!bump_size (nm->nn->protocol->name)) + break; + + /* 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 (nm = first_name; nm; nm = nm->next) + if (!add_dir_entry (nm->nn->protocol->name, nm->nn->protocol->id, + DT_DIR)) + break; + } + + fshelp_touch (&dir->nn_stat, TOUCH_ATIME, netio_maptime); + return err; +} + +/* We need our special version of netfs_S_io_read. */ + +error_t +block_for_reading (socket_t sock) +{ + mach_port_t reply_port; + int type = SELECT_READ; + error_t err; + do + { + reply_port = mach_reply_port (); + if (reply_port == MACH_PORT_NULL) + return EIEIO; /* FIXME? */ + err = io_select (sock, reply_port, 1000, &type); + } + while (err == EMACH_RCV_TIMED_OUT); + return err; +} + +/* Implement the io_read interface. This is the original + implementation from libnetfs with one special hack at the + beginning. */ +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) +{ + error_t err; + off_t start; + struct node *node; + int alloced = 0; + + if (!user) + return EOPNOTSUPP; + + node = user->po->np; + + /* This is our special hack. */ + if (! node->nn->connected) + err = node_connect_socket (node); + if (err || node->nn->flags & PORT_NODE) + err = block_for_reading (node->nn->sock); + if (err) + return err; + + mutex_lock (&user->po->np->lock); + + if ((user->po->openstat & O_READ) == 0) + { + mutex_unlock (&node->lock); + return EBADF; + } + + if (amount > *datalen) + { + alloced = 1; + *data = mmap (0, amount, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0); + } + *datalen = amount; + + start = (offset == -1 ? user->po->filepointer : offset); + + if (start < 0) + err = EINVAL; + else if (S_ISLNK (node->nn_stat.st_mode)) + /* Read from a symlink. */ + { + off_t size = node->nn_stat.st_size; + + if (start + amount > size) + amount = size - start; + + if (start >= size) + { + *datalen = 0; + err = 0; + } + else if (amount < size || start > 0) + { + char *whole_link = alloca (size); + err = netfs_attempt_readlink (user->user, node, *data); + if (! err) + { + memcpy (*data, whole_link + start, amount); + *datalen = amount; + } + } + else + err = netfs_attempt_readlink (user->user, node, *data); + } + else + /* Read from a normal file. */ + err = netfs_attempt_read (user->user, node, start, datalen, *data); + + if (offset == -1 && !err) + user->po->filepointer += *datalen; + + mutex_unlock (&node->lock); + + if (err && alloced) + munmap (*data, amount); + + if (!err && alloced && (round_page (*datalen) < round_page (amount))) + munmap (*data + round_page (*datalen), + round_page (amount) - round_page (*datalen)); + + return err; +} diff --git a/netio.h b/netio.h new file mode 100644 index 000000000..f0fd7fdfd --- /dev/null +++ b/netio.h @@ -0,0 +1,56 @@ +/* netio - creates socket ports via the filesystem + Copyright (C) 2001, 02 Moritz Schulte <moritz@duesseldorf.ccc.de> + + 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 of the + License, 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/socket.h> +#include <stdint.h> +#include <maptime.h> + +#define NETIO_VERSION "0.1" + +#define OPENONLY_STATE_MODES (O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK) + +#define PROTOCOL_ID_TCP 0x00000001 +#define PROTOCOL_ID_UDP 0x00000002 + +struct protocol +{ + char *name; + int id; + error_t (*socket_open) (struct node *np); +}; + +#define ROOT_NODE 0x00000001 +#define PROTOCOL_NODE 0x00000002 +#define HOST_NODE 0x00000004 +#define PORT_NODE 0x00000008 + +struct netnode +{ + unsigned short int flags; /* Either ROOT_NODE, PROTOCOL_NODE, + HOST_NODE or PORT_NODE. */ + char *host; + uint16_t port; + struct protocol *protocol; + socket_t sock; + addr_port_t addr; + unsigned short int connected; + struct node *entries; + struct node *dir; +}; + +extern volatile struct mapped_time_value *netio_maptime; @@ -0,0 +1,222 @@ +/* netio - creates socket ports via the filesystem + Copyright (C) 2001, 02 Moritz Schulte <moritz@duesseldorf.ccc.de> + + 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 of the + License, 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 <errno.h> +#include <stdlib.h> +#include <sys/socket.h> + +#include "netio.h" +#include "lib.h" + +/* Create a new node in the directory *DIR (if DIR is nonzero) and + store it in *NODE. If CONNECT is true (in which case *DIR has to + be a valid directory node), attach the new node to the list of + directory entries of *DIR. Return 0 on success or an error code. */ +error_t +node_make_new (struct node *dir, int connect, struct node **node) +{ + struct netnode *netnode; + error_t err; + + err = my_malloc (sizeof (struct netnode), (void **) &netnode); + if (err) + return err; + *node = netfs_make_node (netnode); + if (! *node) + { + free (netnode); + return ENOMEM; + } + (*node)->nn->flags = 0; + (*node)->nn->host = 0; + (*node)->nn->port = 0; + (*node)->nn->protocol = 0; + (*node)->nn->sock = MACH_PORT_NULL; + (*node)->nn->addr = MACH_PORT_NULL; + (*node)->nn->connected = 0; + (*node)->nn->entries = 0; + (*node)->nn->dir = dir; + if (dir) + netfs_nref (dir); + + if (connect) + { + (*node)->next = dir->nn->entries; + (*node)->prevp = &dir->nn->entries; + if ((*node)->next) + (*node)->next->prevp = &(*node)->next; + dir->nn->entries = (*node); + } + else + { + (*node)->next = 0; + (*node)->prevp = 0; + } + return err; +} + +/* Destroy the node and release a reference to the parent directory. */ +error_t +node_destroy (struct node *np) +{ + if (np->nn->flags & PORT_NODE) + { + if (np->nn->connected) + socket_shutdown (np->nn->sock, 2); + } + else if (np->nn->flags & HOST_NODE) + free (np->nn->host); + else if (np->nn->flags & PROTOCOL_NODE) + { + free (np->nn->protocol); + *np->prevp = np->next; + if (np->next) + np->next->prevp = np->prevp; + } + assert (np->nn->dir); + spin_unlock (&netfs_node_refcnt_lock); /* FIXME? Is this locking + okay? */ + netfs_nput (np->nn->dir); + spin_lock (&netfs_node_refcnt_lock); + free (np->nn); + free (np); + return 0; +} + +/* Create a new protocol node for *PROTOCOL in the directory *DIR. + Return 0 on success or an error code. */ +error_t +node_make_protocol_node (struct node *dir, struct protocol *protocol) +{ + struct node *node; + error_t err; + err = node_make_new (dir, 1, &node); + if (err) + return err; + node->nn->flags |= PROTOCOL_NODE; + node->nn->protocol = protocol; + node->nn_stat.st_mode = S_IFDIR + | S_IRUSR | S_IXUSR + | S_IRGRP | S_IXGRP + | S_IROTH | S_IXOTH; + node->nn_stat.st_ino = protocol->id; + node->nn_stat.st_dev = netfs_root_node->nn_stat.st_dev; + node->nn_stat.st_uid = netfs_root_node->nn_stat.st_uid; + node->nn_stat.st_gid = netfs_root_node->nn_stat.st_gid; + node->nn_stat.st_size = 0; /* ? */ + node->nn_stat.st_blocks = 0; + node->nn_stat.st_blksize = 0; + fshelp_touch (&node->nn_stat, TOUCH_ATIME | TOUCH_CTIME | TOUCH_MTIME, + netio_maptime); + return err; +} + +/* Create a new host node for HOST in the directory *DIR and store it + in *NODE. Return 0 on success or an error code. */ +error_t +node_make_host_node (struct iouser *user, struct node *dir, char *host, + struct node **node) +{ + struct node *np; + error_t err; + err = node_make_new (dir, 0, &np); + if (err) + return err; + np->nn->host = strdup (host); + if (! np->nn->host) + { + netfs_nrele (np); + err = ENOMEM; + goto out; + } + np->nn->flags |= HOST_NODE; + np->nn->protocol = dir->nn->protocol; + np->nn_stat.st_mode = S_IFDIR | S_IRUSR | S_IXUSR ; + np->nn_stat.st_ino = 1; /* ? */ + np->nn_stat.st_dev = netfs_root_node->nn_stat.st_dev; + np->nn_stat.st_uid = *user->uids->ids; + np->nn_stat.st_gid = *user->gids->ids; + np->nn_stat.st_size = 0; /* ? */ + np->nn_stat.st_blocks = 0; + np->nn_stat.st_blksize = 0; + fshelp_touch (&np->nn_stat, TOUCH_ATIME | TOUCH_CTIME | TOUCH_MTIME, + netio_maptime); + *node = np; + + out: + return err; +} + +/* Create a new port node for PORT in the directory *DIR and store it + in *NODE. Return 0 on success or an error code. */ +error_t +node_make_port_node (struct iouser *user, struct node *dir, + char *port, struct node **node) +{ + struct node *np; + error_t err; + err = node_make_new (dir, 0, &np); + if (err) + return err; + np->nn->flags |= PORT_NODE; + np->nn->host = dir->nn->host; + np->nn->protocol = dir->nn->protocol; + np->nn->port = (uint16_t) strtol (port, 0, 10); + np->nn->flags |= HOST_NODE; + np->nn->protocol = dir->nn->protocol; + np->nn_stat.st_mode = S_IFSOCK | S_IRUSR | S_IWUSR ; + np->nn_stat.st_ino = 1; /* ? */ + np->nn_stat.st_dev = netfs_root_node->nn_stat.st_dev; + np->nn_stat.st_uid = *user->uids->ids; + np->nn_stat.st_gid = *user->gids->ids; + np->nn_stat.st_size = 0; /* ? */ + np->nn_stat.st_blocks = 0; + np->nn_stat.st_blksize = 0; + fshelp_touch (&np->nn_stat, TOUCH_ATIME | TOUCH_CTIME | TOUCH_MTIME, + netio_maptime); + *node = np; + return err; +} + +/* Create the root node. Return 0 on success or an error code. */ +error_t +node_make_root_node (struct node **node) +{ + struct node *np; + error_t err; + err = node_make_new (0, 0, &np); + if (err) + goto out; + np->nn->flags |= ROOT_NODE; + *node = np; + out: + return err; +} + +/* Connect the socket of the node *NP. Return 0 on success or an + error code. */ +error_t +node_connect_socket (struct node *np) +{ + error_t err; + err = (*np->nn->protocol->socket_open) (np); + if (! err) + np->nn->connected = 1; + return err; +} @@ -0,0 +1,33 @@ +/* netio - creates socket ports via the filesystem + Copyright (C) 2001, 02 Moritz Schulte <moritz@duesseldorf.ccc.de> + + 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 of the + License, 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 <errno.h> +#include <hurd/netfs.h> +#include <hurd/iohelp.h> + +error_t node_make_new (struct node *dir, int connect, struct node **node); +error_t node_make_protocol_node (struct node *dir, + struct protocol *protocol); +error_t node_make_host_node (struct iouser *user, struct node *dir, + char *host, struct node **node); +error_t node_make_port_node (struct iouser *user, struct node *dir, + char *port, struct node **node); +error_t node_make_root_node (struct node **the_node); +error_t node_connect_socket (struct node *np); +error_t node_destroy (struct node *np); + diff --git a/protocol.c b/protocol.c new file mode 100644 index 000000000..88dcb5901 --- /dev/null +++ b/protocol.c @@ -0,0 +1,152 @@ +/* netio - creates socket ports via the filesystem + Copyright (C) 2002 Moritz Schulte <moritz@duesseldorf.ccc.de> + + 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 of the + License, 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 <hurd/socket.h> +#include <netinet/in.h> +#include <netdb.h> + +#include "netio.h" +#include "lib.h" +#include "node.h" + +/* Connect the node *NODE with style STYLE (SOCK_STREAM or + SOCK_DGRAM). Used by protocol_socket_open_{tcp,udp}. Return 0 on + success or an error code. */ +error_t +protocol_socket_open_std (struct node *node, int style) +{ + extern pf_t socket_server; + socket_t sock; + addr_port_t aport; + error_t err; + struct sockaddr_in addr; + struct hostent *hostinfo; + + hostinfo = gethostbyname (node->nn->host); + if (hostinfo == NULL) + { + err = h_errno; + goto out; + } + + if ((err = socket_create (socket_server, style, 0, &sock))) + goto out; + addr.sin_family = AF_INET; + addr.sin_addr = *(struct in_addr *) hostinfo->h_addr; + addr.sin_port = htons (node->nn->port); + if ((err = socket_create_address (sock, addr.sin_family, + (char *) &addr, sizeof (addr), &aport))) + goto out; + if ((err = socket_connect (sock, aport))) + goto out; + + node->nn->sock = sock; + node->nn->addr = aport; + + /* FIXME: cleanup missing? */ + + out: + return err; +} + +/* Open a TCP socket for *NODE. Return 0 on success or an error code. */ +error_t +protocol_socket_open_tcp (struct node *node) +{ + return protocol_socket_open_std (node, SOCK_STREAM); +} + +/* Open a UDP socket for *NODE. Return 0 on success or an error code. */ +error_t +protocol_socket_open_udp (struct node *node) +{ + return protocol_socket_open_std (node, SOCK_DGRAM); +} + +/* Store the protocol node for the protocol specified by NAME in + *NODE. Return 0 on success or ENOENT if the node could not be + found. */ +error_t +protocol_find_node (char *name, struct node **node) +{ + struct node *np; + for (np = netfs_root_node->nn->entries; + np && strcmp (np->nn->protocol->name, name); + np = np->next); + if (! np) + return ENOENT; + *node = np; + return 0; +} + +/* Register a protocol specified by ID and NAME, creating an according + node. Sockets for that protocol get opened by SOCKET_OPEN_FUNC. + Return 0 on success or an error code. */ +error_t +protocol_register (int id, char *name, + error_t (*socket_open_func) (struct node *node)) +{ + error_t err; + struct protocol *protocol; + err = my_malloc (sizeof (struct protocol), (void **) &protocol); + if (err) + return err; + protocol->name = strdup (name); + if (! protocol->name) + { + free (protocol); + return ENOMEM; + } + err = node_make_protocol_node (netfs_root_node, protocol); + if (err) + { + free (protocol->name); + free (protocol); + return err; + } + protocol->id = id; + protocol->socket_open = socket_open_func; + return 0; +} + +/* Unregister the protocol specified by NAME, drop the reference to + the protocol node. Return 0 on success or ENOENT if that protocol + could not be found. */ +error_t +protocol_unregister (char *name) +{ + struct node *np; + error_t err; + err = protocol_find_node (name, &np); + if (err) + return err; + netfs_nrele (np); + return 0; +} + +/* Register the protocols - create according nodes. Return 0 on + success or an error code. */ +error_t +protocol_register_protocols (void) +{ + return (protocol_register (PROTOCOL_ID_TCP, "tcp", + protocol_socket_open_tcp) + || protocol_register (PROTOCOL_ID_UDP, "udp", + protocol_socket_open_udp)); +} diff --git a/protocol.h b/protocol.h new file mode 100644 index 000000000..2afae90f9 --- /dev/null +++ b/protocol.h @@ -0,0 +1,23 @@ +/* netio - creates socket ports via the filesystem + Copyright (C) 2002 Moritz Schulte <moritz@duesseldorf.ccc.de> + + 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 of the + License, 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 */ + +error_t protocol_register_protocols (void); +error_t protocol_register (int id, char *name, + error_t (*socket_open_func) (struct node *node)); +error_t protocol_unregister (char *name);; +error_t protocol_find_node (char *name, struct node **node); diff --git a/version.h b/version.h new file mode 100644 index 000000000..54108e57a --- /dev/null +++ b/version.h @@ -0,0 +1,30 @@ +/* Hurd version + Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc. + Written by Thomas Bushnell, n/BSG. + + This file is part of the GNU Hurd. + + The GNU Hurd is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + The GNU Hurd is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#ifndef HURD_VERSION +#define HURD_VERSION "0.2" +#endif + +/* The standard way to print versions for --version. */ +#define _SHV_SEP "; " +#define STANDARD_HURD_VERSION(s, extra...) \ + #s " (GNU Hurd" _SHV_SEP ##extra ") " HURD_VERSION + + |