diff options
Diffstat (limited to 'hostmux/mux.c')
-rw-r--r-- | hostmux/mux.c | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/hostmux/mux.c b/hostmux/mux.c new file mode 100644 index 00000000..cba29126 --- /dev/null +++ b/hostmux/mux.c @@ -0,0 +1,401 @@ +/* Root hostmux node + + Copyright (C) 1997 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.ai.mit.edu> + 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. */ + +#include <stddef.h> +#include <string.h> +#include <dirent.h> +#include <netdb.h> +#include <sys/socket.h> +#include <arpa/inet.h> + +#include "hostmux.h" + +error_t create_host_node (struct hostmux *mux, struct hostmux_name *name, + struct node **node); + +/* 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)) + +static error_t lookup_host (struct hostmux *mux, const char *host, + struct node **node); /* fwd decl */ + +/* [root] Directory operations. */ + +/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If + the name was not found, then return ENOENT. On any error, clear *NODE. + (*NODE, if found, should be locked, 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; + + if (dir->nn->name) + err = ENOTDIR; + else + err = lookup_host (dir->nn->mux, name, node); + + touch (dir, TOUCH_ATIME); + + mutex_unlock (&dir->lock); + + if (! err) + mutex_lock (&(*node)->lock); + + return err; +} + +/* Implement the netfs_get_directs callback as described in + <hurd/netfs.h>. */ +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 hostmux_name *first_name, *nm; + + if (dir->nn->name) + return ENOTDIR; + + rwlock_reader_lock (&dir->nn->mux->names_lock); + + /* Find the first entry. */ + for (first_name = dir->nn->mux->names; + first_name && first_entry > 0; + first_name = first_name->next) + if (first_name->node) + first_entry--; + + /* See how much space we need for the result. */ + for (nm = first_name, count = 0; + nm && (num_entries == -1 || count < num_entries); + nm = nm->next) + if (nm->node) + { + size_t new_size = size + DIRENT_LEN (strlen (nm->name)); + if (new_size > max_data_len) + break; + size = new_size; + count++; + } + + /* Allocate it. */ + err = vm_allocate (mach_task_self (), (vm_address_t *) data, size, 1); + if (! err) + /* Copy out the result. */ + { + char *p = *data; + + *data_len = size; + *data_entries = count; + + /* See how much space we need for the result. */ + for (nm = first_name, count = 0; + nm && (num_entries == -1 || count < num_entries); + nm = nm->next) + if (nm->node) + { + struct dirent hdr; + size_t name_len = strlen (nm->name); + size_t sz = DIRENT_LEN (name_len); + + if (sz > size) + break; + else + size -= sz; + + hdr.d_fileno = nm->fileno; + hdr.d_reclen = sz; + hdr.d_type = + (strcmp (nm->canon, nm->name) == 0 ? DT_REG : DT_LNK); + hdr.d_namlen = name_len; + + memcpy (p, &hdr, DIRENT_NAME_OFFS); + strcpy (p + DIRENT_NAME_OFFS, nm->name); + p += sz; + + count++; + } + + } + + rwlock_reader_unlock (&dir->nn->mux->names_lock); + + touch (dir, TOUCH_ATIME); + + return err; +} + +/* Host lookup. */ + +/* Free storage allocated consumed by the host mux name NM, but not the node + it points to. */ +static void +free_name (struct hostmux_name *nm) +{ + free ((char *)nm->name); + if (nm->name != nm->canon) + free ((char *)nm->canon); + free (nm); +} + +/* See if there's an existing entry for the name HOST, and if so, return its + node in NODE with an additional references. True is returned iff the + lookup succeeds. If PURGE is true, then any nodes with a null node are + removed. */ +static int +lookup_cached (struct hostmux *mux, const char *host, int purge, + struct node **node) +{ + struct hostmux_name *nm = mux->names, **prevl = &mux->names; + + while (nm) + { + struct hostmux_name *next = nm->next; + + if (strcasecmp (host, nm->name) == 0) + { + spin_lock (&netfs_node_refcnt_lock); + if (nm->node) + nm->node->references++; + spin_unlock (&netfs_node_refcnt_lock); + + if (nm->node) + { + *node = nm->node; + return 1; + } + } + + if (purge && !nm->node) + { + *prevl = nm->next; + free_name (nm); + } + else + prevl = &nm->next; + + nm = next; + } + + return 0; +} + +/* See if there's an existing entry for the name HOST, and if so, return its + node in NODE, with an additional reference, otherwise, create a new node + for the host HE as referred to by HOST, and return that instead, with a + single reference. The type of node created is either a translator node, + if HOST refers to the official name of the host, or a symlink node to the + official name, if it doesn't. */ +static error_t +lookup_hostent (struct hostmux *mux, const char *host, struct hostent *he, + struct node **node) +{ + error_t err; + struct hostmux_name *nm = malloc (sizeof (struct hostmux_name)); + + if (! nm) + return ENOMEM; + + nm->name = strdup (host); + if (strcmp (host, he->h_name) == 0) + nm->canon = nm->name; + else + nm->canon = strdup (he->h_name); + + err = create_host_node (mux, nm, node); + if (err) + { + free_name (nm); + return err; + } + + rwlock_writer_lock (&mux->names_lock); + if (lookup_cached (mux, host, 1, node)) + /* An entry for HOST has already been created between the time we last + looked and now (which is possible because we didn't lock MUX). + Just throw away our version and return the one already in the cache. */ + { + rwlock_writer_unlock (&mux->names_lock); + nm->node->nn->name = 0; /* Avoid touching the mux name list. */ + netfs_nrele (nm->node); /* Free the tentative new node. */ + free_name (nm); /* And the name it was under. */ + } + else + /* Enter NM into MUX's list of names, and return the new node. */ + { + nm->fileno = mux->next_fileno++; /* Now that we hold the lock... */ + nm->next = mux->names; + mux->names = nm; + rwlock_writer_unlock (&mux->names_lock); + } + + return 0; +} + +/* Lookup the host HOST in MUX, and return the resulting node in NODE, with + an additional reference, or an error. */ +static error_t +lookup_host (struct hostmux *mux, const char *host, struct node **node) +{ + int was_cached; + int h_err; + struct hostent _he, *he; + struct in_addr inet_addr; + char hostent_data[2048]; /* XXX what size should this be???? */ + + rwlock_reader_lock (&mux->names_lock); + was_cached = lookup_cached (mux, host, 0, node); + rwlock_reader_unlock (&mux->names_lock); + + if (was_cached) + return 0; + else if (inet_aton (host, &inet_addr)) + { + if (gethostbyaddr_r ((char *)&inet_addr, sizeof inet_addr, AF_INET, + &_he, hostent_data, sizeof hostent_data, + &he, &h_err) == 0) + return lookup_hostent (mux, host, he, node); + else + return ENOENT; + } + else if (gethostbyname_r (host, &_he, hostent_data, sizeof hostent_data, + &he, &h_err) == 0) + return lookup_hostent (mux, host, he, node); + else + return ENOENT; +} + +/* This should sync the entire remote filesystem. If WAIT is set, return + only after sync is completely finished. */ +error_t +netfs_attempt_syncfs (struct iouser *cred, int wait) +{ + 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 *node, uid_t uid, uid_t gid) +{ + if (node->nn->name) + return EOPNOTSUPP; + else + { + struct hostmux *mux = node->nn->mux; + error_t err = file_chown (mux->underlying, uid, gid); + + if (! err) + { + struct hostmux_name *nm; + + /* Change NODE's owner. */ + mux->stat_template.st_uid = uid; + mux->stat_template.st_gid = gid; + node->nn_stat.st_uid = uid; + node->nn_stat.st_gid = gid; + + /* Change the owner of each leaf node. */ + rwlock_reader_lock (&mux->names_lock); + for (nm = mux->names; nm; nm = nm->next) + if (nm->node) + { + nm->node->nn_stat.st_uid = uid; + nm->node->nn_stat.st_gid = gid; + } + rwlock_reader_unlock (&mux->names_lock); + + touch (node, TOUCH_CTIME); + } + + return err; + } +} + +/* 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 *node, uid_t author) +{ + if (node->nn->name) + return EOPNOTSUPP; + else + { + struct hostmux *mux = node->nn->mux; + error_t err = file_chauthor (mux->underlying, author); + + if (! err) + { + struct hostmux_name *nm; + + /* Change NODE's owner. */ + mux->stat_template.st_author = author; + node->nn_stat.st_author = author; + + /* Change the owner of each leaf node. */ + rwlock_reader_lock (&mux->names_lock); + for (nm = mux->names; nm; nm = nm->next) + if (nm->node) + nm->node->nn_stat.st_author = author; + rwlock_reader_unlock (&mux->names_lock); + + touch (node, TOUCH_CTIME); + } + + return err; + } +} + +/* 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 *node, mode_t mode) +{ + mode &= ~S_ITRANS; + if (node->nn->name || ((mode & S_IFMT) != (node->nn_stat.st_mode & S_IFMT))) + return EOPNOTSUPP; + else + { + error_t err = file_chmod (node->nn->mux->underlying, mode & ~S_IFMT); + if (! err) + { + node->nn_stat.st_mode = mode; + touch (node, TOUCH_CTIME); + } + return err; + } +} |