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 /netfs.c |
Initial import, second try.
Diffstat (limited to 'netfs.c')
-rw-r--r-- | netfs.c | 660 |
1 files changed, 660 insertions, 0 deletions
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; +} |