/*---------------------------------------------------------------------------*/ /*node.c*/ /*---------------------------------------------------------------------------*/ /*Implementation of node management strategies*/ /*---------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*---------------------------------------------------------------------------*/ /*Copyright (C) 2001, 2002, 2005, 2008, 2009 Free Software Foundation, Inc. Written by Sergiu Ivanov . 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.*/ /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ #define _GNU_SOURCE 1 /*---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include /*---------------------------------------------------------------------------*/ #include "debug.h" #include "node.h" #include "options.h" #include "lib.h" #include "nsmux.h" #include "ncache.h" /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /*--------Global Variables---------------------------------------------------*/ /*The lock protecting the underlying filesystem*/ pthread_mutex_t ulfs_lock = PTHREAD_MUTEX_INITIALIZER; /*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/ /*--------Functions----------------------------------------------------------*/ /*Derives a new node from `lnode` and adds a reference to `lnode`*/ error_t node_create (lnode_t * lnode, node_t ** node) { error_t err = 0; /*Create a new netnode */ netnode_t *netnode_new = malloc (sizeof (netnode_t)); /*Reset the memory allocated for the new netnode (just in case :-) ) */ memset (netnode_new, 0, sizeof (netnode_t)); /*If the memory could not be allocated */ if (netnode_new == NULL) err = ENOMEM; else { /*create a new node from the netnode */ node_t *node_new = netfs_make_node (netnode_new); /*If the creation failed */ if (node_new == NULL) { /*set the error code */ err = ENOMEM; /*destroy the netnode created above */ free (netnode_new); /*stop */ return err; } node_new->nn->type = NODE_TYPE_NORMAL; /*link the lnode to the new node */ lnode->node = node_new; /*setup the references in the newly created node */ node_new->nn->lnode = lnode; lnode_ref_add (lnode); /*setup the information in the netnode */ node_new->nn->flags = 0; node_new->nn->ncache_next = node_new->nn->ncache_prev = NULL; /*initialize the data fields dealing with positioning this node in the dynamic translator stack */ node_new->nn->dyntrans = NULL; node_new->nn->below = NULL; /*store the result of creation in the second parameter */ *node = node_new; } /*Return the result of operations */ return err; } /*node_create */ /*---------------------------------------------------------------------------*/ /*Derives a new proxy from `lnode`*/ error_t node_create_proxy (lnode_t * lnode, node_t ** node) { error_t err = 0; /*Create a new netnode */ netnode_t *netnode_new = malloc (sizeof (netnode_t)); /*Reset the memory allocated for the new netnode. We do this here since lnode_add_proxy will try to reference the `lnode` in this netnode and will do bad writes to memory. */ memset (netnode_new, 0, sizeof (netnode_t)); /*If the memory could not be allocated */ if (netnode_new == NULL) err = ENOMEM; else { /*create a new node from the netnode */ node_t *node_new = netfs_make_node (netnode_new); /*If the creation failed */ if (node_new == NULL) { /*set the error code */ err = ENOMEM; /*destroy the netnode created above */ free (netnode_new); /*stop */ return err; } node_new->nn->type = NODE_TYPE_PROXY; /*add this new node to the list of proxies of `lnode` */ lnode_add_proxy (lnode, node_new); /*setup the information in the netnode */ node_new->nn->flags = 0; node_new->nn->ncache_next = node_new->nn->ncache_prev = NULL; /*initialize the data fields dealing with positioning this node in the dynamic translator stack */ node_new->nn->dyntrans = NULL; node_new->nn->below = NULL; /*store the result of creation in the second parameter */ *node = node_new; } /*Return the result of operations */ return err; } /*node_create_proxy */ /*---------------------------------------------------------------------------*/ /*Creates a proxy (or a shadow) node for the supplied port*/ error_t node_create_from_port (mach_port_t port, node_t ** node) { error_t err = 0; /*Create a new netnode */ netnode_t * netnode_new = malloc (sizeof (netnode_t)); /*Reset the memory allocated for the new netnode (just in case :-) ) */ memset (netnode_new, 0, sizeof (netnode_t)); /*If the memory could not be allocated */ if (netnode_new == NULL) err = ENOMEM; else { /*create a new node from the netnode */ node_t * node_new = netfs_make_node (netnode_new); /*If the creation failed */ if (node_new == NULL) { /*set the error code */ err = ENOMEM; /*destroy the netnode created above */ free (netnode_new); /*stop */ return err; } /*The caller may want to change this to NODE_TYPE_SHADOW if it's the case. */ node_new->nn->type = NODE_TYPE_PROXY; /*this node is ``orphan'' -- it is not associated to any lnode and has some service functions only */ node_new->nn->lnode = NULL; /*setup the information in the netnode */ node_new->nn->flags = 0; node_new->nn->ncache_next = node_new->nn->ncache_prev = NULL; node_new->nn->port = port; /*initialize the data fields dealing with positioning this node in the dynamic translator stack */ node_new->nn->dyntrans = NULL; node_new->nn->below = NULL; /*store the result of creation in the second parameter */ *node = node_new; } /*Return the result of operations */ return err; } /*node_create_from_port */ /*---------------------------------------------------------------------------*/ /*Destroys the specified node and removes a light reference from the associated light node*/ void node_destroy (node_t * np) { /*Die if the node does not belong to node cache */ assert (!np->nn->ncache_next || !np->nn->ncache_prev); /*Destroy the port to the underlying filesystem allocated to the node */ if (np->nn->port != MACH_PORT_NULL) PORT_DEALLOC (np->nn->port); /*TODO: If this node is a shadow node, kill the translator sitting on this node. */ /*If there is an lnode associated with the current node, detach it */ if (np->nn->lnode) { if (np->nn->lnode->node == np) { pthread_mutex_lock (&np->nn->lnode->lock); /*orphan the light node */ np->nn->lnode->node = NULL; lnode_ref_remove (np->nn->lnode); } else /*remove a reference to this node from the list of proxies */ lnode_remove_proxy (np->nn->lnode, np); } /*Free the netnode and the node itself */ free (np->nn); free (np); } /*node_destroy */ /*---------------------------------------------------------------------------*/ /*Creates the root node and the corresponding lnode*/ error_t node_create_root (node_t ** root_node) { /*Try to create a new lnode */ lnode_t *lnode; error_t err = lnode_create (NULL, &lnode); /*Stop, if the creation failed */ if (err) return err; /*Try to derive the node corresponding to `lnode` */ node_t *node; err = node_create (lnode, &node); /*If the operation failed */ if (err) { /*destroy the created lnode */ lnode_destroy (lnode); /*stop */ return err; } /*Release the lock on the lnode */ pthread_mutex_unlock (&lnode->lock); /*Store the result in the parameter */ *root_node = node; /*Return the result */ return err; } /*node_create_root */ /*---------------------------------------------------------------------------*/ /*Initializes the port to the underlying filesystem for the root node*/ error_t node_init_root (node_t * node /*the root node */ ) { error_t err = 0; /*Acquire a lock for operations on the underlying filesystem */ pthread_mutex_lock (&ulfs_lock); /*Open the port to the directory specified in `dir` */ node->nn->port = file_name_lookup (dir, O_READ | O_DIRECTORY, 0); /*If the directory could not be opened */ if (node->nn->port == MACH_PORT_NULL) { /*set the error code accordingly */ err = errno; LOG_MSG ("node_init_root: Could not open the port for %s.", dir); /*release the lock and stop */ pthread_mutex_unlock (&ulfs_lock); return err; } LOG_MSG ("node_init_root: Port for %s opened successfully.", dir); LOG_MSG ("\tPort: 0x%lX", (unsigned long) node->nn->port); /*Stat the root node */ err = io_stat (node->nn->port, &node->nn_stat); if (err) { /*deallocate the port */ PORT_DEALLOC (node->nn->port); LOG_MSG ("node_init_root: Could not stat the root node."); /*unlock the mutex and exit */ pthread_mutex_unlock (&ulfs_lock); return err; } /*Set the path to the corresponding lnode to `dir` */ node->nn->lnode->path = strdup (dir); if (!node->nn->lnode->path) { /*deallocate the port */ PORT_DEALLOC (node->nn->port); /*unlock the mutex */ pthread_mutex_unlock (&ulfs_lock); LOG_MSG ("node_init_root: Could not strdup the directory."); return ENOMEM; } /*The current position in dir */ char *p = dir + strlen (dir); /*Go through the path from end to beginning */ for (; p >= dir; --p) { /*If the current character is a '/' */ if (*p == '/') { /*If p is not the first character */ if (p > dir) { /*if this slash is escaped, skip it */ if (*(p - 1) == '\\') continue; } /*advance the pointer to the first character after the slash */ ++p; /*stop */ break; } } LOG_MSG ("node_init_root: The name of root node is %s.", p); /*Set the name of the lnode to the last element in the path to dir */ node->nn->lnode->name = strdup (p); /*If the name of the node could not be duplicated */ if (!node->nn->lnode->name) { /*free the name of the path to the node and deallocate teh port */ free (node->nn->lnode->path); PORT_DEALLOC (node->nn->port); /*unlock the mutex */ pthread_mutex_unlock (&ulfs_lock); LOG_MSG ("node_init_root: Could not strdup the name of the root node."); return ENOMEM; } /*Compute the length of the name of the root node */ node->nn->lnode->name_len = strlen (p); /*Release the lock for operations on the undelying filesystem */ pthread_mutex_unlock (&ulfs_lock); /*Return the result of operations */ return err; } /*node_init_root */ /*---------------------------------------------------------------------------*/ /*Frees a list of dirents*/ void node_entries_free (node_dirent_t * dirents) { /*The current and the next elements */ node_dirent_t *dirent, *dirent_next; /*Go through all elements of the list */ for (dirent = dirents; dirent; dirent = dirent_next) { /*store the next element */ dirent_next = dirent->next; /*free the dirent stored in the current element of the list */ free (dirent->dirent); /*free the current element */ free (dirent); } } /*node_entries_free */ /*---------------------------------------------------------------------------*/ /*Reads the directory entries from `node`, which must be locked*/ error_t node_entries_get (node_t * node, node_dirent_t ** dirents) { error_t err = 0; /*The list of dirents */ struct dirent **dirent_list, **dirent; /*The head of the list of dirents */ node_dirent_t *node_dirent_list = NULL; /*The size of the array of pointers to dirent */ size_t dirent_data_size; /*The array of dirents */ char *dirent_data; /*Obtain the directory entries for the given node */ err = dir_entries_get (node->nn->port, &dirent_data, &dirent_data_size, &dirent_list); if (err) { return err; } /*The new entry in the list */ node_dirent_t *node_dirent_new; /*The new dirent */ struct dirent *dirent_new; /*LOG_MSG("node_entries_get: Getting entries for %p", node); */ /*The name of the current dirent */ char *name; /*The length of the current name */ size_t name_len; /*The size of the current dirent */ size_t size; /*Go through all elements of the list of pointers to dirent */ for (dirent = dirent_list; *dirent; ++dirent) { /*obtain the name of the current dirent */ name = &((*dirent)->d_name[0]); /*If the current dirent is either '.' or '..', skip it */ if ((strcmp (name, ".") == 0) || (strcmp (name, "..") == 0)) continue; /*obtain the length of the current name */ name_len = strlen (name); /*obtain the length of the current dirent */ size = DIRENT_LEN (name_len); /*create a new list element */ node_dirent_new = malloc (sizeof (node_dirent_t)); if (!node_dirent_new) { err = ENOMEM; break; } /*create a new dirent */ dirent_new = malloc (size); if (!dirent_new) { free (node_dirent_new); err = ENOMEM; break; } /*fill the dirent with information */ dirent_new->d_ino = (*dirent)->d_ino; dirent_new->d_type = (*dirent)->d_type; dirent_new->d_reclen = size; strcpy ((char *) dirent_new + DIRENT_NAME_OFFS, name); /*add the dirent to the list */ node_dirent_new->dirent = dirent_new; node_dirent_new->next = node_dirent_list; node_dirent_list = node_dirent_new; } /*If something went wrong in the loop */ if (err) /*free the list of dirents */ node_entries_free (node_dirent_list); else /*store the list of dirents in the second parameter */ *dirents = node_dirent_list; /*Free the list of pointers to dirent */ free (dirent_list); /*Free the results of listing the dirents */ munmap (dirent_data, dirent_data_size); /*Return the result of operations */ return err; } /*node_entries_get */ /*---------------------------------------------------------------------------*/ /*Makes sure that all ports to the underlying filesystem of `node` are up to date*/ error_t node_update (node_t * node) { error_t err = 0; /*The full path to this node */ char *path; /*Stat information for `node` */ io_statbuf_t stat; /*The port to the file corresponding to `node` */ file_t port; /*If the specified node is the root node or if it must not be updated */ if (NODE_IS_ROOT (node) || (node->nn->flags & FLAG_NODE_ULFS_FIXED)) /*do nothing */ return err; /*return 0; actually */ /*Gain exclusive access to the root node of the filesystem */ pthread_mutex_lock (&netfs_root_node->lock); /*Construct the full path to `node` */ err = lnode_path_construct (node->nn->lnode, &path); if (err) { pthread_mutex_unlock (&netfs_root_node->lock); return err; } /*Deallocate `node`'s port to the underlying filesystem */ if (node->nn->port) PORT_DEALLOC (node->nn->port); /*Try to lookup the file for `node` in its untranslated version */ err = file_lookup (netfs_root_node->nn->port, path, O_READ | O_NOTRANS, O_NOTRANS, 0, &port, &stat); if (err) { node->nn->port = MACH_PORT_NULL; err = 0; /*failure (?) */ return err; } /*If the node looked up is actually the root node of the proxy filesystem */ if ((stat.st_ino == underlying_node_stat.st_ino) && (stat.st_fsid == underlying_node_stat.st_fsid)) /*set `err` accordingly */ err = ELOOP; else { /*deallocate the obtained port */ PORT_DEALLOC (port); /*obtain the translated version of the required node */ err = file_lookup (netfs_root_node->nn->port, path, O_READ, 0, 0, &port, &stat); } /*If there have been errors */ if (err) /*reset the port */ port = MACH_PORT_NULL; /*Store the port in the node */ node->nn->port = port; /*Remove the flag about the invalidity of the current node and set the flag that the node is up-to-date */ node->nn->flags &= ~FLAG_NODE_INVALIDATE; node->nn->flags |= FLAG_NODE_ULFS_UPTODATE; /*Release the lock on the root node of proxy filesystem */ pthread_mutex_unlock (&netfs_root_node->lock); /*Return the result of operations */ return err; } /*node_update */ /*---------------------------------------------------------------------------*/ /*Computes the size of the given directory*/ error_t node_get_size (node_t * dir, OFFSET_T * off) { error_t err = 0; /*The final size */ size_t size = 0; /*The number of directory entries */ /*int count = 0; */ /*The the node in the directory entries list from which we start counting */ /*node_dirent_t * dirent_start = NULL; */ /*The currently analyzed dirent */ node_dirent_t *dirent_current = NULL; /*The pointer to the beginning of the list of dirents */ node_dirent_t *dirent_list = NULL; /*The first entry we have to analyze */ /*int first_entry = 2; */ /*Takes into consideration the name of the current dirent */ void bump_size (const char *name) { /*Increment the current size by the size of the current dirent */ size += DIRENT_LEN (strlen (name)); /*Count the current dirent */ /*++count; */ } /*bump_size */ /*Obtain the list of entries in the current directory */ err = node_entries_get (dir, &dirent_list); if (err) return err; /*Obtain the pointer to the dirent which has the number first_entry */ /*Actually, the first element of the list */ /*This code is included in unionfs, but it's completely useless here */ /*for ( dirent_start = dirent_list, count = 2; dirent_start && count < first_entry; dirent_start = dirent_start->next, ++count ); */ /*Reset the count */ /*count = 0; */ /*Make space for '.' and '..' entries */ /*This code is included in unionfs, but it's completely useless here */ /*if(first_entry == 0) bump_size("."); if(first_entry <= 1) bump_size(".."); */ /*See how much space is required for the node */ for (dirent_current = dirent_list /*dirent_start */ ; dirent_current; dirent_current = dirent_current->next) bump_size (dirent_current->dirent->d_name); /*Free the list of dirents */ node_entries_free (dirent_list); /*Return the size */ *off = size; return 0; } /*node_get_size */ /*---------------------------------------------------------------------------*/ /*Remove the file called `name` under `dir`*/ error_t node_unlink_file (node_t * dir, char *name) { error_t err = 0; /*The port to the file which will be unlinked */ mach_port_t p; /*Stat information about the file which will be unlinked */ io_statbuf_t stat; /*If port corresponding to `dir` is invalid */ if (dir->nn->port == MACH_PORT_NULL) /*stop with an error */ return ENOENT; /*FIXME: Is the return value indeed meaningful here? */ /*Attempt to lookup the specified file */ err = file_lookup (dir->nn->port, name, O_NOTRANS, O_NOTRANS, 0, &p, &stat); if (err) return err; /*Deallocate the obtained port */ PORT_DEALLOC (p); /*Unlink file `name` under `dir` */ err = dir_unlink (dir->nn->port, name); if (err) return err; return err; } /*node_unlink_file */ /*---------------------------------------------------------------------------*/ /*Starts translator `trans` on the (shadow) node `np`, which should mirror the file `filename`, and returns the port `port` to the root of the translator opened as `flags.`*/ error_t node_set_translator (struct protid * diruser, node_t * np, char * trans, int flags, char * filename, mach_port_t * port) { error_t err; mach_port_t p; /*An unauthenticated port to the directory containing `np` */ mach_port_t unauth_dir; /*A copy (possibly extended) of the name of the translator */ char * ext; /*The holders of argz-transformed translator name and arguments */ char * argz = NULL; size_t argz_len = 0; /*The control port for the active translator */ mach_port_t active_control; /*The PID of the started translator */ pid_t trans_pid; /*A copy of the user information, supplied in `user` */ struct iouser * user; /*A protid for the supplied node */ struct protid * newpi; /*Identity information about the current process (for fsys_getroot) */ uid_t * uids; size_t nuids; gid_t * gids; size_t ngids; /*The retry information returned by fsys_getroot */ string_t retry_name; mach_port_t retry_port; /*Try to get the number of effective UIDs */ nuids = geteuids (0, 0); if (nuids < 0) return EPERM; /*Allocate some memory for the UIDs on the stack */ uids = alloca (nuids * sizeof (uid_t)); /*Fetch the UIDs themselves */ nuids = geteuids (nuids, uids); if (nuids < 0) return EPERM; /*Try to get the number of effective GIDs */ ngids = getgroups (0, 0); if (ngids < 0) return EPERM; /*Allocate some memory for the GIDs on the stack */ gids = alloca (ngids * sizeof (gid_t)); /*Fetch the GIDs themselves */ ngids = getgroups (ngids, gids); if (ngids < 0) return EPERM; /*Opens the port on which to set the new translator */ error_t open_port (int flags, mach_port_t * underlying, mach_msg_type_name_t * underlying_type, task_t task, void *cookie) { err = 0; /*Check, whether the user has the permissions to open this node */ err = check_open_permissions (diruser->user, &np->nn_stat, flags); if (err) return err; if (!np->nn->port) { /*We have to do the lookup only in case the supplied node does not already have a port. */ np->nn->port = file_name_lookup_under (diruser->po->np->nn->port, filename, flags, 0); if (!np->nn->port) return ENOENT; } /*Duplicate the supplied user */ err = iohelp_dup_iouser (&user, diruser->user); if (err) return err; /*Create a protid for this node */ newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po), user); if (!newpi) { iohelp_free_iouser (user); return errno; } LOG_MSG ("node_set_translators.open_port: PASSED"); /*Obtain the resulting port right and set its type appropriately */ *underlying = p = ports_get_send_right (newpi); *underlying_type = MACH_MSG_TYPE_COPY_SEND; /**underlying_type = MACH_MSG_TYPE_COPY_SEND;*/ /**underlying_type = MACH_MSG_TYPE_MOVE_SEND;*/ LOG_MSG ("node_set_translators.open_port: %ld", (long) *underlying); /*Drop our reference to the port */ ports_port_deref (newpi); /*Store the task ID of the new translator */ trans_pid = task2pid(task); /* char buf[256]; char *_buf = buf; size_t len = 256; io_read (p, &_buf, &len, 0, len); LOG_MSG ("node_set_translator: Read from np: '%s'", buf); */ /*Return the result of operations (everything should be okay here) */ return err; } /*open_port */ /*Adds a "/hurd/" at the beginning of the translator name, if required */ char * put_in_hurd (const char *name) { /*If the path to the translator is absolute, return a copy of the name */ /*TODO: A better decision technique on whether we have to add the prefix */ if (name[0] == '/') return strdup (name); /*Compute the length of the name */ size_t len = strlen (name); /*Try to allocate new memory */ char * full = malloc (6 /*strlen("/hurd/") */ + len + 1); if (!full) return NULL; /*Construct the name */ strcpy (full, "/hurd/"); /*LOG_MSG("node_set_translator: %s", full); */ strcpy (full + 6, name); /*LOG_MSG("node_set_translator: %s", full); */ full[6 + len] = 0; /*LOG_MSG("node_set_translator: %s", full); */ /*Return the full path */ return full; } /*put_in_hurd */ /*Obtain the unauthenticated port to the directory */ err = io_restrict_auth (diruser->po->np->nn->port, &unauth_dir, 0, 0, 0, 0); if (err) return err; /*Obtain a copy (possibly extended) of the name */ ext = put_in_hurd (trans); if (!ext) return ENOMEM; /*TODO: Better argument-parsing? */ /*Obtain the argz version of the translator name */ err = argz_create_sep (ext, ' ', &argz, &argz_len); if (err) return err; /*Start the translator */ /*The value 60000 for the timeout is the one found in settrans */ err = fshelp_start_translator (open_port, NULL, argz, argz, argz_len, 60000, &active_control); if (err) return err; /*Attempt to set a translator on the port opened by the previous call */ err = file_set_translator (p, 0, FS_TRANS_SET, 0, argz, argz_len, active_control, MACH_MSG_TYPE_COPY_SEND); PORT_DEALLOC (p); if (err) return err; /*Register the new translator*/ err = trans_register (active_control, trans_pid, &np->nn->dyntrans); LOG_MSG ("node_set_translator: Translator PID: %d", (int)trans_pid); if (err) return err; /*Obtain the port to the top of the newly-set translator */ err = fsys_getroot (active_control, unauth_dir, MACH_MSG_TYPE_COPY_SEND, uids, nuids, gids, ngids, flags, &retry_port, retry_name, &p); if (err) return err; /*Return the port */ *port = p; /*Everything is OK here */ return 0; } /*node_set_translator */ /*---------------------------------------------------------------------------*/ /*Gets the port to the supplied node. */ error_t node_get_port (struct protid * diruser, node_t * np, int flags, mach_port_t * port) { error_t error = 0; /*The new user for the port */ struct iouser * user; /*The protid for the port */ struct protid * newpi; /*Create a port to the supplied node without checking anything. */ flags &= ~OPENONLY_STATE_MODES; error = iohelp_dup_iouser (&user, diruser->user); if (error) return error; newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po), user); if (!newpi) { iohelp_free_iouser (user); error = errno; return error; } *port = ports_get_right (newpi); ports_port_deref (newpi); return error; } /*node_get_port */ /*---------------------------------------------------------------------------*/ /*Gets the send port right to the supplied node. */ error_t node_get_send_port (struct protid * diruser, node_t * np, int flags, mach_port_t * port) { error_t error = 0; /*The new user for the port */ struct iouser * user; /*The protid for the port */ struct protid * newpi; /*Create a port to the supplied node without checking anything. */ flags &= ~OPENONLY_STATE_MODES; error = iohelp_dup_iouser (&user, diruser->user); if (error) return error; newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po), user); if (!newpi) { iohelp_free_iouser (user); error = errno; return error; } *port = ports_get_send_right (newpi); ports_port_deref (newpi); return error; } /*node_get_send_port */ /*---------------------------------------------------------------------------*/