diff options
-rw-r--r-- | debug.h | 2 | ||||
-rw-r--r-- | lib.c | 28 | ||||
-rw-r--r-- | lib.h | 13 | ||||
-rw-r--r-- | lnode.c | 168 | ||||
-rw-r--r-- | lnode.h | 54 | ||||
-rw-r--r-- | ncache.c | 2 | ||||
-rw-r--r-- | ncache.h | 3 | ||||
-rw-r--r-- | node.c | 511 | ||||
-rw-r--r-- | node.h | 71 | ||||
-rw-r--r-- | nsmux.c | 876 | ||||
-rw-r--r-- | nsmux.h | 43 | ||||
-rw-r--r-- | options.c | 2 | ||||
-rw-r--r-- | options.h | 2 |
13 files changed, 1260 insertions, 515 deletions
@@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -28,6 +28,8 @@ #define _GNU_SOURCE 1 /*----------------------------------------------------------------------------*/ #include <sys/mman.h> +#include <fcntl.h> +#include <hurd/fshelp.h> /*----------------------------------------------------------------------------*/ #include "lib.h" #include "debug.h" @@ -191,3 +193,27 @@ file_lookup return err; }/*file_lookup*/ /*----------------------------------------------------------------------------*/ +/*Checks whether `user` has the right to open the node described by `stat` with + `flags`*/ +error_t +check_open_permissions + ( + struct iouser * user, + io_statbuf_t * stat, + int flags + ) + { + error_t err = 0; + + /*Cheks user's permissions*/ + if(flags & O_READ) + err = fshelp_access(stat, S_IREAD, user); + if(!err && (flags & O_WRITE)) + err = fshelp_access(stat, S_IWRITE, user); + if(!err && (flags & O_EXEC)) + err = fshelp_access(stat, S_IEXEC, user); + + /*Return the result of the check*/ + return err; + }/*check_open_permissions*/ +/*----------------------------------------------------------------------------*/ @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -32,6 +32,7 @@ #include <hurd.h> #include <dirent.h> #include <stddef.h> +#include <hurd/iohelp.h> /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ @@ -79,4 +80,14 @@ file_lookup io_statbuf_t * stat /*store the stat information here*/ ); /*----------------------------------------------------------------------------*/ +/*Checks whether `user` has the right to open the node described by `stat` with + `flags`*/ +error_t +check_open_permissions + ( + struct iouser * user, + io_statbuf_t * stat, + int flags + ); +/*----------------------------------------------------------------------------*/ #endif /*__LIB_H__*/ @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -29,6 +29,7 @@ /*----------------------------------------------------------------------------*/ #include "lnode.h" #include "debug.h" +#include "node.h" /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ @@ -138,9 +139,19 @@ lnode_destroy /*Destroy the name of the node*/ free(node->name); - /*If there is a list of translators*/ - if(node->trans) - free(node->trans); + /*While the list of proxies has not been freed*/ + node_list_t p; + for(p = node->proxies; p;) + { + /*shift the pointer to the head of the list forward*/ + node->proxies = node->proxies->next; + + /*drop the current cell in the list*/ + free(p); + + /*store the current proxy*/ + p = node->proxies; + } /*Destroy the node itself*/ free(node); @@ -307,70 +318,111 @@ lnode_uninstall node->next->prevp = &node->next; }/*lnode_uninstall*/ /*----------------------------------------------------------------------------*/ -/*Constructs a list of translators that were set on the ancestors of `node`*/ +/*Makes the specified lnode aware of another proxy. Both `node` and `proxy` + must be locked*/ error_t -lnode_list_translators +lnode_add_proxy ( lnode_t * node, - char ** trans, /*the malloced list of 0-separated strings*/ - size_t * ntrans /*the number of elements in `trans`*/ + node_t * proxy ) { - /*The size of block of memory for the list of translators*/ - size_t sz = 0; - - /*Used for tracing the lineage of `node`*/ - lnode_t * ln = node; - - /*Used in computing the lengths of lists of translators in every node - we will go through and for constructing the final list of translators*/ - char * p; - - /*The current position in *data (used in filling out the list of - translators)*/ - char * transp; - - /*The length of the current translator name*/ - size_t complen; - - size_t i; - - /*Trace the lineage of the `node` (including itself) and compute the - total length of the list of translators*/ - for(; ln; sz += ln->translen, ln = ln->dir); - - /*Try to allocate a block of memory sufficient for storing the list of - translators*/ - *trans = malloc(sz); - if(!*trans) + /*TODO: Make the list of proxies finite*/ + + /*Create an new cell in the list of proxies*/ + node_list_t p = malloc(sizeof(node_list_t)); + if(!p) return ENOMEM; + + /*If the supplied node references an lnode already + (this should always happen, though)*/ + if(proxy->nn->lnode) + /*remove the reference held by the node to that lnode*/ + lnode_ref_remove(proxy->nn->lnode); + + /*Connect the proxy to the lnode*/ + proxy->nn->lnode = node; + + /*Store the pointer to the proxy in the new cell*/ + p->node = proxy; + + /*Add the new cell to the list of proxies*/ + p->next = node->proxies; + node->proxies = p; - /*No translators at first*/ - *ntrans = 0; + /*Count a new reference to this lnode*/ + lnode_ref_add(node); - /*Again trace the lineage of the `node` (including itself)*/ - for(transp = *trans + sz, ln = node; ln; ln = ln->dir) + /*Everything is OK here*/ + return 0; + }/*lnode_add_proxy*/ +/*----------------------------------------------------------------------------*/ +/*Removes the specified proxy from the list of proxies of the supplied lnode. + `proxy` must not be locked*/ +void +lnode_remove_proxy + ( + lnode_t * node, + node_t * proxy + ) + { + /*A pointer to a cell in the list of proxies*/ + node_list_t p; + + /*Lock the lnode*/ + mutex_lock(&node->lock); + + /*If the first cell in the list contains a reference to `proxy`*/ + if(node->proxies->node == proxy) { - /*Go through each translator name in the list of names*/ - for(i = 0, p = ln->trans + ln->translen - 2; i < ln->ntrans; ++i) - { - /*position p at the beginning of the current component and - compute its length at the same time*/ - for(complen = 0; *p; --p, ++complen); - --p; - - /*move the current position backwards*/ - transp -= complen + 1; + /*store a pointer to the head of the list*/ + p = node->proxies; + + /*shift the head of the list forward*/ + node->proxies = node->proxies->next; + + /*destroy the former head*/ + free(p); + + /*remove a reference from the supplied lnode*/ + lnode_ref_remove(node); + + /*stop right here*/ + mutex_unlock(&node->lock); + return; + } - /*copy the current translator name into the list*/ - strcpy(transp, p + 2); - - /*we've got another translator*/ - ++*ntrans; - } + /*Another pointer to a cell in the list*/ + node_list_t q; + + /*Go through the list of proxy nodes of the given lnode*/ + for(p = node->proxies; p->next; p = p->next) + { + /*If the next cell does not contain a reference to the same node, + as specified in the parameter, skip this entry*/ + if(p->next->node != proxy) + continue; + + /*store a copy of the next element*/ + q = p->next; + + /*unregister the next element from the list*/ + p->next = q->next; + + /*remove the unregistered element*/ + free(q); + + /*stop looping*/ + break; } + + /*Remove a reference from the supplied lnode*/ + lnode_ref_remove(node); - /*Everything OK*/ - return 0; - }/*lnode_list_translators*/ + + /*Unlock the node*/ + mutex_unlock(&node->lock); + + return; + }/*lnode_remove_proxy*/ /*----------------------------------------------------------------------------*/ @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -35,14 +35,25 @@ /*--------Macros--------------------------------------------------------------*/ /*The possible flags in an lnode*/ #define FLAG_LNODE_DIR 0x00000001 /*the lnode is a directory*/ -#define FLAG_LNODE_TRANSLATED 0x00000002 /*all appropriate translators - have already been set on this node*/ /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ +/*--------Types---------------------------------------------------------------*/ /*A candy synonym for the fundamental libnetfs node*/ typedef struct node node_t; /*----------------------------------------------------------------------------*/ +/*A list of lnodes*/ +struct node_element + { + /*the node associated with the current entry*/ + node_t * node; + + /*the next element in the list*/ + struct node_element * next; + };/*struct node_element*/ +/*----------------------------------------------------------------------------*/ +typedef struct node_element * node_list_t; +/*----------------------------------------------------------------------------*/ /*The light node*/ struct lnode { @@ -56,26 +67,19 @@ struct lnode /*the full path to the lnode*/ char * path; - /*the malloced set of translators which have to be stacked upon this node - and upon its children; the corresponding translators will have to decide - on their own whether to accept directories or not*/ - char * trans; - - /*the number of translators listed in `translators`*/ - size_t ntrans; - - /*the length of the list of translators (in bytes)*/ - size_t translen; - /*the associated flags*/ int flags; /*the number of references to this lnode*/ int references; - /*the reference to the real node*/ + /*the reference to the real netfs node*/ node_t * node; + /*the references to the proxy nodes of this lnode; + lnodes do not hold references to */ + node_list_t proxies; + /*the next lnode and the pointer to this lnode from the previous one*/ struct lnode * next, **prevp; @@ -85,7 +89,7 @@ struct lnode /*the beginning of the list of entries contained in this lnode (directory)*/ struct lnode * entries; - /*a lock*/ + /*the lock, protecting this lnode*/ struct mutex lock; };/*struct lnode*/ /*----------------------------------------------------------------------------*/ @@ -159,13 +163,23 @@ lnode_uninstall lnode_t * node ); /*----------------------------------------------------------------------------*/ -/*Constructs a list of translators that were set on the ancestors of `node`*/ +/*Makes the specified lnode aware of another proxy. Both `node` and `proxy` + must be locked*/ error_t -lnode_list_translators +lnode_add_proxy ( lnode_t * node, - char ** trans, /*the malloced list of 0-separated strings*/ - size_t * ntrans /*the number of elements in `trans`*/ + node_t * proxy + ); +/*----------------------------------------------------------------------------*/ +/*Removes the specified proxy from the list of proxies of the supplied lnode. + `proxy` must not be locked*/ +void +lnode_remove_proxy + ( + lnode_t * node, + node_t * proxy ); /*----------------------------------------------------------------------------*/ #endif /*__LNODE_H__*/ + @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -40,6 +40,7 @@ /*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ +/*--------Types---------------------------------------------------------------*/ /*A cache chain*/ struct ncache { @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -33,6 +33,12 @@ #include <sys/mman.h> #include <stdio.h> #include <argz.h> +#include <hurd/fsys.h> + + +/*!!!!!!!REMOVE THIS!!!!!!*/ +#include <sys/file.h> + /*----------------------------------------------------------------------------*/ #include "debug.h" #include "node.h" @@ -63,6 +69,9 @@ node_create /*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; @@ -86,12 +95,18 @@ node_create /*link the lnode to the new node*/ lnode->node = node_new; - lnode_ref_add(lnode); - + /*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 list of translators*/ + node_new->nn->trans = NULL; + node_new->nn->ntrans = node_new->nn->translen = 0; /*store the result of creation in the second parameter*/ *node = node_new; @@ -101,6 +116,64 @@ node_create return err; }/*node_create*/ /*----------------------------------------------------------------------------*/ +/*Derives a new proxy from `lnode`*/ +error_t +node_create_proxy + ( + lnode_t * lnode, + node_t ** node /*store the result here*/ + ) + { + 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; + } + + /*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 list of translators*/ + node_new->nn->trans = NULL; + node_new->nn->ntrans = node_new->nn->translen = 0; + + /*store the result of creation in the second parameter*/ + *node = node_new; + } + + /*Return the result of operations*/ + return err; + }/*node_create_proxy*/ +/*----------------------------------------------------------------------------*/ /*Destroys the specified node and removes a light reference from the associated light node*/ void @@ -116,19 +189,23 @@ node_destroy if(np->nn->port != MACH_PORT_NULL) PORT_DEALLOC(np->nn->port); - /*If the given node is not the root node and there are translators - to kill*/ - if(np->nn->lnode->dir && np->nn->lnode->trans) + /*If there are translators to kill*/ + if(np->nn->lnode->dir && np->nn->trans) { /*kill all translators on the underlying nodes*/ - node_kill_all_translators(np); + node_kill_translators(np); } /*Lock the lnode corresponding to the current node*/ mutex_lock(&np->nn->lnode->lock); - /*Orphan the light node*/ - np->nn->lnode->node = NULL; + /*If the node to be destroyed is a real netfs node*/ + if(np->nn->lnode->node == np) + /*orphan the light node*/ + np->nn->lnode->node = NULL; + else + /*remove a reference to this node from the list of proxies*/ + lnode_remove_proxy(np->nn->lnode, np); /*Remove a reference from the lnode*/ lnode_ref_remove(np->nn->lnode); @@ -632,22 +709,24 @@ node_unlink_file return err; }/*node_unlink_file*/ /*----------------------------------------------------------------------------*/ -/*Sets the given translator on the supplied node*/ -/*This function will normally be called from netfs_attempt_lookup, therefore - it's better that the caller should provide the parent node for `node`.*/ +/*Sets the given translators on the specified node*/ error_t -node_set_translator +node_set_translators ( - node_t * dir, - node_t * node, - const char * trans /*set this on `name`*/ + struct protid * diruser, + node_t * np, + char * trans, /*set these on `node`*/ + size_t ntrans, + int flags, + mach_port_t * port ) { error_t err; - - /*A port for opened to the file*/ 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; @@ -655,10 +734,62 @@ node_set_translator char * argz = NULL; size_t argz_len = 0; + /*The pointer to the name of the translator we are going to start now*/ + char * str; + + /*The index of the translator in the list of strings*/ + size_t idx; + /*The control port for the active translator*/ mach_port_t active_control; + + /*A new element in the list of control ports*/ + port_el_t * p_el; + + /*A copy of the user information, supplied in `user`*/ + struct iouser * user; + + /*A protid for the supplied node*/ + struct protid * newpi; - /*Opens a port to the file at the request of fshelp_start_translator*/ + /*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 ( @@ -666,21 +797,58 @@ node_set_translator mach_port_t * underlying, mach_msg_type_name_t * underlying_type, task_t task, - void * cookie /*some additional information, not used here*/ + void * cookie /*if 0, open the port to the node; otherwise get the port + of the topmost translator on the node*/ ) { - /*Lookup the file we are working with*/ - p = file_name_lookup_under - (dir->nn->port, node->nn->lnode->name, flags, 0); - if(p == MACH_PORT_NULL) - return errno; + /*No errors at first*/ + err = 0; + + /*If a port to the node is wanted*/ + if((int)cookie == 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; + + /*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; - /*Store the result in the parameters*/ - *underlying = p; - *underlying_type = MACH_MSG_TYPE_COPY_SEND; - - /*Here everything is OK*/ - return 0; + LOG_MSG("node_set_translators.open_port: %ld", (long)*underlying); + + /*drop our reference to the port*/ + ports_port_deref(newpi); + } + /*We are not setting the first translator, this one is not required + to sit on the node itself*/ + else + /*in the previous iteration of the loop fsys_getroot gave us the port + to the toplevel translator on the current node, namely `p`; this is + what is required of us now*/ + { + *underlying = p; + *underlying_type = MACH_MSG_TYPE_COPY_SEND; + } + + /*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*/ @@ -715,47 +883,83 @@ node_set_translator return full; }/*put_in_hurd*/ - /*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 str*/ - err = argz_create_sep(ext, ' ', &argz, &argz_len); + /*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; + + /*While all translators have not been looked through*/ + for(str = trans, idx = 0; idx < ntrans; ++idx) + { + /*obtain a copy (possibly extended) of the name*/ + ext = put_in_hurd(str); + if(!ext) + return ENOMEM; - /*Start the translator*/ - err = fshelp_start_translator - ( - open_port, NULL, argz, argz, argz_len, - 60000,/*this is the default in settrans*/ - &active_control - ); - if(err) - return err; + /*TODO: Better argument-parsing?*/ - /*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 - ); - if(err) - return err; + /*obtain the argz version of str*/ + err = argz_create_sep(ext, ' ', &argz, &argz_len); + if(err) + return err; - /*Deallocate the port we have just opened*/ - PORT_DEALLOC(p); + /*start the translator*/ + err = fshelp_start_translator + ( + open_port, (void *)((idx == 0) ? 0 : 1), argz, argz, argz_len, + 60000,/*this is the default in settrans*/ + &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 + ); + /*close the port we have just opened*/ + PORT_DEALLOC(p); + + /*stop, if the attempt to set the translator failed*/ + if(err) + return err; + + /*allocate a new element for the current control port*/ + p_el = malloc(sizeof(struct port_el)); + if(!p_el) + return ENOMEM; + + /*store the current control port in the new cell*/ + p_el->p = active_control; + + /*add the new control port to the list*/ + p_el->next = np->nn->cntl_ports; + np->nn->cntl_ports = p_el; + + /*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*/ + }/*node_set_translators*/ /*----------------------------------------------------------------------------*/ /*Kill the topmost translator for this node*/ /*This function will normally be called from netfs_attempt_lookup, therefore it's better that the caller should provide the parent node for `node`.*/ +/*This is the code for the wrong version of nsmux.*/ error_t node_kill_translator ( @@ -763,88 +967,159 @@ node_kill_translator node_t * node ) { + error_t err = 0; + /*Lookup the file*/ mach_port_t p = - file_name_lookup_under(dir->nn->port, node->nn->lnode->name, O_NOTRANS, 0); + file_name_lookup_under + (dir->nn->port, node->nn->lnode->name, /*O_NOTRANS*/0, 0); + if(!p) + return errno; /*Remove a translator from the node*/ - error_t err = file_set_translator +/*This code was taken from the code of settrans and works only when there is + only one translator in the stack (not including the ext2fs, for example). + For this code to work the O_NOTRANS is required in the lookup.*/ + /*error_t err = file_set_translator ( p, FS_TRANS_SET, FS_TRANS_SET, 0, NULL, 0, MACH_PORT_NULL, MACH_MSG_TYPE_COPY_SEND - ); + );*/ + + /*Obtain the control port of the filesystem to which file `name` belongs*/ + fsys_t fsys; + err = file_getcontrol(p, &fsys); + + char * argz = NULL; + size_t argz_len = 0; + err = file_get_fs_options(p, &argz, &argz_len); /*Close the port*/ PORT_DEALLOC(p); + /*If the control port of the filesystem could not be fetched, stop*/ + if(err) + return err; + + /*Ask the translator to go away*/ + err = fsys_goaway(fsys, 0); + + /*If the translator is busy*/ + if(err) + /*force the translator to go away*/ + err = fsys_goaway(fsys, FSYS_GOAWAY_FORCE); + + /*Deallocate the control port*/ + PORT_DEALLOC(fsys); + /*Return the result of removing the translator*/ return err; }/*node_kill_translator*/ /*----------------------------------------------------------------------------*/ /*Kills all translators on the nodes belonging to the given directory*/ void -node_kill_all_translators +node_kill_translators ( node_t * node ) { - /*Goes through all children of the given node recursively*/ - void - traverse_children_r - ( - node_t * dir, - node_t * np, - size_t ntrans /*the number of translators to remove from all nodes*/ - ) - { - int i; + /*If the node has no translators*/ + if(node->nn->trans == NULL) + /*nothing to do*/ + return; - /*A child of the current node*/ - lnode_t * ln = np->nn->lnode; - - /*If the current node is not a directory*/ - if(!(ln->flags & FLAG_LNODE_DIR)) - { - /*remove the required number of translators from the current node*/ - for(i = 0; i < ntrans + ln->ntrans; ++i) - node_kill_translator(dir, np); - } - else - /*Go through the list of entries belonging to this directory*/ - for(ln = ln->entries; ln; ln = ln->next) - { - /*die, if there is no node corresponding to the current child*/ - /*This is abnormal, because lnodes should normally go away shortly - after the destruction of correpsonding nodes*/ - assert(ln->node); - - /*kill translators for this node*/ - traverse_children_r(np, ln->node, ntrans + ln->ntrans); - } - - /*Obtain a pointer to the lnode corresponding to np*/ - ln = np->nn->lnode; + error_t err = 0; + + /*The current element in the port list*/ + port_el_t * p_el; + + /*While the list of control ports is not empty*/ + for(p_el = node->nn->cntl_ports; p_el; p_el = node->nn->cntl_ports) + { + /*kill the current translator*/ + err = fsys_goaway(p_el->p, 0); - /*Destroy the list of translators*/ - if(ln->trans) - free(ln->trans); + /*If the translator says it is busy, force it to go away*/ + if(err == EBUSY) + err = fsys_goaway(p_el->p, FSYS_GOAWAY_FORCE); - /*No more translators on this node*/ - ln->trans = NULL; - ln->ntrans = ln->translen = 0; + /*move the beginning of the list of control ports forward*/ + node->nn->cntl_ports = p_el->next; - /*There are no translators on this node*/ - ln->flags &= ~FLAG_LNODE_TRANSLATED; - }/*traverse_children_r*/ - - /*Obtain the pointer to the parent node of the current node*/ - node_t * dnp; - ncache_node_lookup(node->nn->lnode->dir, &dnp); + /*destroy the current cell in the list of ports*/ + free(p_el); + } + }/*node_kill_translators*/ +/*----------------------------------------------------------------------------*/ +/*Constructs a list of translators that were set on the ancestors of `node`*/ +/*TODO: Remove node_list_translators.*/ +error_t +node_list_translators + ( + node_t * node, + char ** trans, /*the malloced list of 0-separated strings*/ + size_t * ntrans /*the number of elements in `trans`*/ + ) + { + /*The size of block of memory for the list of translators*/ + size_t sz = 0; + + /*Used for tracing the lineage of `node`*/ + lnode_t * ln = node->nn->lnode; + + /*Used in computing the lengths of lists of translators in every node + we will go through and for constructing the final list of translators*/ + char * p; - /*Kill all translators on the current node*/ - traverse_children_r(dnp, node, node->nn->lnode->ntrans); + /*The current position in *data (used in filling out the list of + translators)*/ + char * transp; + + /*The length of the current translator name*/ + size_t complen; + + size_t i; + + /*Trace the lineage of the `node` (including itself) and compute the + total length of the list of translators*/ + for(; ln; sz += node->nn->translen, ln = ln->dir); + + /*Try to allocate a block of memory sufficient for storing the list of + translators*/ + *trans = malloc(sz); + if(!*trans) + return ENOMEM; - /*Unlock the parent node*/ - mutex_unlock(&dnp->lock); - }/*node_kill_all_translators*/ + /*No translators at first*/ + *ntrans = 0; + + /*Again trace the lineage of the `node` (including itself)*/ + for(transp = *trans + sz, ln = node->nn->lnode; ln; ln = ln->dir) + { + /*Go through each translator name in the list of names*/ + for + ( + i = 0, p = node->nn->trans + node->nn->translen - 2; + i < node->nn->ntrans; ++i + ) + { + /*position p at the beginning of the current component and + compute its length at the same time*/ + for(complen = 0; *p; --p, ++complen); + --p; + + /*move the current position backwards*/ + transp -= complen + 1; + + /*copy the current translator name into the list*/ + strcpy(transp, p + 2); + + /*we've got another translator*/ + ++*ntrans; + } + } + + /*Everything OK*/ + return 0; + }/*lnode_list_translators*/ /*----------------------------------------------------------------------------*/ @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -51,24 +51,47 @@ # define OFFSET_T __off_t #endif /*__USE_FILE_OFFSET64*/ /*----------------------------------------------------------------------------*/ -/*The size of a chunk of a string (for a small optimization in checking - the property)*/ -#define STRING_CHUNK 256 -/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ +/*--------Types---------------------------------------------------------------*/ +/*A list element containing a port*/ +struct port_el + { + /*the port*/ + mach_port_t p; + + /*the next element in the list*/ + struct port_el * next; + };/*struct port_el*/ +/*----------------------------------------------------------------------------*/ +typedef struct port_el port_el_t; +/*----------------------------------------------------------------------------*/ /*The user-defined node for libnetfs*/ struct netnode { /*the reference to the corresponding light node*/ lnode_t * lnode; - /*the flags associated with this node (might be not required)*/ + /*the flags associated with this node*/ int flags; /*a port to the underlying filesystem*/ file_t port; + /*the malloced set of translators which have to be stacked upon this node + and upon its children; the corresponding translators will have to decide + on their own whether to accept directories or not*/ + char * trans; + + /*the number of translators listed in `translators`*/ + size_t ntrans; + + /*the length of the list of translators (in bytes)*/ + size_t translen; + + /*the list of control ports to the translators being set on this node*/ + port_el_t * cntl_ports; + /*the neighbouring entries in the cache*/ node_t * ncache_prev, * ncache_next; };/*struct netnode*/ @@ -104,6 +127,14 @@ node_create node_t ** node /*store the result here*/ ); /*----------------------------------------------------------------------------*/ +/*Derives a new proxy from `lnode`*/ +error_t +node_create_proxy + ( + lnode_t * lnode, + node_t ** node /*store the result here*/ + ); +/*----------------------------------------------------------------------------*/ /*Destroys the specified node and removes a light reference from the associated light node*/ void @@ -165,15 +196,17 @@ node_unlink_file char * name ); /*----------------------------------------------------------------------------*/ -/*Sets the given translator on the supplied node*/ -/*This function will normally be called from netfs_attempt_lookup, therefore - it's better that the caller should provide the parent node for `node`.*/ +/*Sets the given translators on the specified node and returns the port to + the topmost one opened as `flags` require*/ error_t -node_set_translator +node_set_translators ( - node_t * dir, - node_t * node, - const char * trans /*set this on `name`*/ + struct protid * diruser, + node_t * np, + char * trans, /*set these on `node`*/ + size_t ntrans, + int flags, + mach_port_t * port ); /*----------------------------------------------------------------------------*/ /*Kill the topmost translator for this node*/ @@ -189,9 +222,19 @@ node_kill_translator /*Kills all translators on the current node or on all underlying nodes it the current node is a directory*/ void -node_kill_all_translators +node_kill_translators ( node_t * node ); /*----------------------------------------------------------------------------*/ +/*Constructs a list of translators that were set on the ancestors of `node`*/ +/*TODO: Remove node_list_translators.*/ +error_t +node_list_translators + ( + node_t * node, + char ** trans, /*the malloced list of 0-separated strings*/ + size_t * ntrans /*the number of elements in `trans`*/ + ); +/*----------------------------------------------------------------------------*/ #endif /*__NODE_H__*/ @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -33,6 +33,7 @@ #include <argp.h> #include <hurd/netfs.h> #include <fcntl.h> +#include <hurd/paths.h> /*----------------------------------------------------------------------------*/ #include "debug.h" #include "options.h" @@ -99,18 +100,8 @@ netfs_check_open_permissions { LOG_MSG("netfs_check_open_permissions: '%s'", np->nn->lnode->name); - error_t err = 0; - - /*Cheks user's permissions*/ - if(flags & O_READ) - err = fshelp_access(&np->nn_stat, S_IREAD, user); - if(!err && (flags & O_WRITE)) - err = fshelp_access(&np->nn_stat, S_IWRITE, user); - if(!err && (flags & O_EXEC)) - err = fshelp_access(&np->nn_stat, S_IEXEC, user); - - /*Return the result of the check*/ - return err; + /*Cheks user's permissions and return the result*/ + return check_open_permissions(user, &np->nn_stat, flags); }/*netfs_check_open_permissions*/ /*----------------------------------------------------------------------------*/ /*Attempts an utimes call for the user `cred` on node `node`*/ @@ -194,7 +185,7 @@ netfs_validate_stat struct iouser * cred ) { - LOG_MSG("netfs_validate_stat: '%s'", np->nn->lnode->name); + LOG_MSG("netfs_validate_stat: '%s'", (np ? np->nn->lnode->name : "")); error_t err = 0; @@ -220,6 +211,9 @@ netfs_validate_stat /*attempt to stat this file*/ err = io_stat(np->nn->port, &np->nn_stat); + if(S_ISDIR(np->nn_stat.st_mode)) + LOG_MSG("\tIs a directory"); + /*If stat information has been successfully obtained for the file*/ if(!err) /*duplicate the st_mode field of stat structure*/ @@ -509,14 +503,53 @@ netfs_attempt_lookup struct node ** node ) { - LOG_MSG("netfs_attempt_lookup: '%s'", name); + LOG_MSG("netfs_attempt_lookup"); - error_t err = 0; - int i = 0; + /*We should never get here. In any case, we do not want to do this type + of lookup, netfs_attempt_lookup_improved is what we want*/ + return EOPNOTSUPP; + }/*netfs_attempt_lookup*/ +/*----------------------------------------------------------------------------*/ +/*Performs an advanced lookup of file `name` under `dir`. If the lookup of the + last component of the path is requested (`lastcomp` is 1), it is not a + directory and no translators are required (`name` does not contain ',,'), the + function will simply open the required file and return the port in `file`. In + other cases it will create a proxy node and return it in `node`.*/ +error_t +netfs_attempt_lookup_improved + ( + struct iouser * user, + struct node * dir, + char * name, + int flags, + int lastcomp, + node_t ** node, + file_t * file + ) + { + LOG_MSG("netfs_attempt_lookup_improved: '%s'", name); + error_t err = 0; + /*If we are asked to fetch the current directory*/ if(strcmp(name, ".") == 0) { + /*validate the stat information for `dir`*/ + err = netfs_validate_stat(dir, user); + if(err) + { + mutex_unlock(&dir->lock); + return err; + } + + /*If `dir` is not a directory, actually*/ + if(!S_ISDIR(dir->nn_stat.st_mode)) + { + /*unlock the directory and stop right here*/ + mutex_unlock(&dir->lock); + return ENOTDIR; + } + /*add a reference to `dir` and put it into `node`*/ netfs_nref(dir); *node = dir; @@ -527,6 +560,22 @@ netfs_attempt_lookup /*If we are asked to fetch the parent directory*/ else if(strcmp(name, "..") == 0) { + /*validate the stat information for `dir`*/ + err = netfs_validate_stat(dir, user); + if(err) + { + mutex_unlock(&dir->lock); + return err; + } + + /*If `dir` is not a directory, actually*/ + if(!S_ISDIR(dir->nn_stat.st_mode)) + { + /*unlock the directory and stop right here*/ + mutex_unlock(&dir->lock); + return ENOTDIR; + } + /*If the supplied node is not root*/ if(dir->nn->lnode->dir) { @@ -612,7 +661,9 @@ netfs_attempt_lookup error_t lookup ( - char * name /*lookup this*/ + char * name, /*lookup this*/ + int flags, /*lookup `name` in the this way*/ + int proxy /*should a proxy node be created*/ ) { /*Try to lookup the given file in the underlying directory*/ @@ -620,13 +671,8 @@ netfs_attempt_lookup /*If the lookup failed*/ if(p == MACH_PORT_NULL) - { - /*unlock the directory*/ - mutex_unlock(&dir->lock); - /*no such entry*/ return ENOENT; - } /*Obtain the stat information about the file*/ io_statbuf_t stat; @@ -638,20 +684,25 @@ netfs_attempt_lookup /*If this file is not a directory*/ if(err || !S_ISDIR(stat.st_mode)) { - /*do not set the port*/ - p = MACH_PORT_NULL; + /*lookup the required file with the supplied flags*/ + p = file_name_lookup_under(dir->nn->port, name, flags, 0); + if(p == MACH_PORT_NULL) + return EBADF; /*remember we do not have a directory*/ isdir = 0; + + /*If a proxy node is not required*/ + if(!proxy) + /*stop here, we want only the port to the file*/ + return 0; } else { /*lookup the port with the right to read the contents of the directory*/ p = file_name_lookup_under(dir->nn->port, name, O_READ | O_DIRECTORY, 0); if(p == MACH_PORT_NULL) - { return EBADF; /*not enough rights?*/ - } /*we have a directory here*/ isdir = 1; @@ -675,16 +726,25 @@ netfs_attempt_lookup lnode_install(dir->nn->lnode, lnode); } - /*Obtain the node corresponding to this lnode*/ - err = ncache_node_lookup(lnode, node); + /*If we are to create a proxy node*/ + if(proxy) + /*create a proxy node from the given lnode*/ + err = node_create_proxy(lnode, node); + /*If we don't need proxy nodes in this lookup*/ + else + { + /*obtain the node corresponding to this lnode*/ + err = ncache_node_lookup(lnode, node); + + /*remove an extra reference from the lnode*/ + lnode_ref_remove(lnode); + } - /*Remove an extra reference from the lnode*/ - lnode_ref_remove(lnode); - - /*If the lookup in the cache failed*/ + /*If either the lookup in the cache or the creation of a proxy failed*/ if(err) { /*stop*/ + mutex_unlock(&lnode->lock); finalize(); return err; } @@ -702,10 +762,14 @@ netfs_attempt_lookup err = lnode_path_construct(lnode, NULL); if(err) { + mutex_unlock(&lnode->lock); finalize(); return err; } + /*Unlock the lnode*/ + mutex_unlock(&lnode->lock); + /*Now the node is up-to-date*/ (*node)->nn->flags = FLAG_NODE_ULFS_UPTODATE; @@ -736,12 +800,15 @@ netfs_attempt_lookup ++sep; } - /*If the control sequence has been found present*/ + /*If the control sequence has been found*/ if(sep) { + /*the name of the file to lookup*/ + char * name_cpy; + /*copy the name*/ /*just copy the pointer*/ - char * name_cpy = /*strdup*/(name); + name_cpy = /*strdup*/(name); if(!name_cpy) { err = ENOMEM; @@ -756,12 +823,16 @@ netfs_attempt_lookup *(sep++) = 0; *(sep++) = 0; - /*try to lookup a node with the specified name*/ - err = lookup(name_cpy); + /*try to lookup the file with the specified name and create a proxy + node for it (since we need to start translators)*/ + err = lookup(name_cpy, flags, 1); + + /*If the lookup failed*/ if(err) { + /*say that the file has not been found*/ finalize(); - return err; + return ENOENT; } /*duplicate the part of the name containing the list of translators @@ -816,263 +887,500 @@ netfs_attempt_lookup which does not end in a comma and the corresponding terminal 0*/ ++ntrans; ++translen; - - /*If there are no translators set upon the current node*/ - if(!lnode->trans) - { - /*copy the list of translators we have just built in the lnode*/ - lnode->trans = trans; - lnode->ntrans = ntrans; - lnode->translen = translen; - - /*If the node has already been fully translated before and it's - not a directory*/ - /*We don't need to set the whole collection of translators, - both inherited and the ones in lnode->trans. The latter - are only necessary. Directories, on the other hand, cannot - bear translators on them.*/ - if - ( - (lnode->flags & FLAG_LNODE_TRANSLATED) - && !(lnode->flags & FLAG_LNODE_DIR) - ) - { - /*Go through the list of translators*/ - for(str = trans, i = 0; i < ntrans; ++i) - { - /*set the translator specified in the current component*/ - err = node_set_translator(dir, *node, str); - if(err) - { - finalize(); - return 0; /*TODO: A better way to fight errors here*/ - } - - /*skip the current component*/ - str += strlen(str) + 1; - } - /*we don't own the list of translators any more*/ - trans = NULL; - ntrans = 0; - translen = 0; - - /*here the lookup is successful*/ - err = 0; - finalize(); - return err; - } - - /*we don't own the list of translators any more*/ - trans = NULL; - ntrans = 0; - translen = 0; - } - else - { - /*If the current node is not a directory*/ - if(!isdir) - { - /*obtain the minimal of the lengths of the lists of translators*/ - size_t minlen = - (translen < lnode->translen) ? (translen) : (lnode->translen); - - /*the number of similar translators*/ - size_t similar = 0; - - /*the current position in the list*/ - size_t i; - - /*Go through the lists of translators*/ - for(i = 0; i < minlen; ++i) - { - /*If the current characters are different, stop here*/ - if(trans[i] != lnode->trans[i]) - break; - - /*If the current character marks the end of a translator name*/ - if(trans[i] == 0) - /*one more matching translator*/ - ++similar; - } - - /*While all not matching translators have not been removed*/ - size_t j; - for(j = 0; j < lnode->ntrans - similar; ++j) - { - /*remove one more translator*/ - err = node_kill_translator(dir, *node); - if(err) - { - finalize(); - return err; - } - } - - /*obtain the pointer to the first not matching position*/ - str = trans + i; - - /*seek to the beginning of the current translator name*/ - for(; (str > trans) && *str; --str); - - /*While the required new translators have not been set*/ - for(i = 0; i < ntrans - similar; ++i) - { - /*set the current translator on the file*/ - err = node_set_translator(dir, *node, str); - if(err) - { - finalize(); - return err; - } - - /*skip the current translator*/ - str += strlen(str) + 1; - } - } - /*We have looked up a directory*/ - else - { - /*If the length of the lists of translators are the same*/ - if((lnode->translen = translen) && (lnode->ntrans == ntrans)) - { - /*check each character of the list of translators*/ - int i; - for(i = 0; (i < translen) && (lnode->trans[i] == trans[i]); ++i); - - /*If the lists coincide*/ - if(i == translen) - { - /*the lookup has finished successfully here*/ - err = 0; - finalize(); - return err; - } - } - - /*unlock the directory in which we are at the moment*/ - mutex_unlock(&dir->lock); - - /*kill all translators on all children of this node*/ - node_kill_all_translators(*node); - - /*lock the directory back*/ - mutex_lock(&dir->lock); - } - - /*destroy the list of translators in the current node*/ - free(lnode->trans); - - /*put the new list in the node*/ - lnode->trans = trans; - lnode->ntrans = ntrans; - lnode->translen = translen; - - /*all required translators have been already applied on this node*/ - /*lnode->flags |= FLAG_LNODE_TRANSLATED;*/ - - /*we don't own the list of translators any more*/ - trans = NULL; - - /*the lookup is already finished here*/ - err = 0; - finalize(); - return err; - } + /*copy the list of translators we have just built in the new proxy node*/ + (*node)->nn->trans = trans; + (*node)->nn->ntrans = ntrans; + (*node)->nn->translen = translen; + + /*we don't own the list of translators any more*/ + trans = NULL; + ntrans = 0; + translen = 0; } /*The control sequence ',,' has not been found*/ else { - /*simply lookup the provided name*/ - err = lookup(name); + /*simply lookup the provided name, without creating the proxy, if not + necessary (i.e. when the file is not a directory)*/ + err = lookup(name, flags, 0); if(err) { finalize(); return err; } - - /*If there are some translators set on this file*/ - if(lnode->trans) - { - /*unlock the directory*/ - mutex_unlock(&dir->lock); - - /*kill all translators on this node*/ - node_kill_all_translators(*node); - /*lock the directory again*/ - mutex_lock(&dir->lock); - - /*all translators have been succesfully set here*/ - /*lnode->flags |= FLAG_LNODE_TRANSLATED;*/ - - /*the lookup has finished successfully here*/ - /*The translators inherited from parents survive throughout - the previous loop*/ - err = 0; - finalize(); - return err; - } + /*if we have looked up a regular file, store the port to it in *`file`*/ + if(!isdir) + *file = p; } - /*If we have a directory*/ - if(isdir) + /*Everything OK here*/ + finalize(); + return err; + }/*netfs_attempt_lookup_improved*/ +/*----------------------------------------------------------------------------*/ +/*Responds to the RPC dir_lookup*/ +error_t +netfs_S_dir_lookup + ( + struct protid * diruser, + char * filename, + int flags, + mode_t mode, + retry_type * do_retry, + char * retry_name, + mach_port_t * retry_port, + mach_msg_type_number_t * retry_port_type + ) + { + LOG_MSG("netfs_S_dir_lookup: '%s'", filename); + + int create; /* true if O_CREAT flag set */ + int excl; /* true if O_EXCL flag set */ + int mustbedir = 0; /* true if the result must be S_IFDIR */ + int lastcomp = 0; /* true if we are at the last component */ + int newnode = 0; /* true if this node is newly created */ + int nsymlinks = 0; + struct node *dnp, *np; + char *nextname; + error_t error; + struct protid *newpi; + struct iouser *user; + + /*The port to the file for the case when we don't need proxy nodes*/ + file_t file = MACH_PORT_NULL; + + /*The port to the same file with restricted rights*/ + file_t file_restricted = MACH_PORT_NULL; + + /*The stat information about the node pointed at by `file` (for the case + when not proxy nodes are to be created)*/ + io_statbuf_t stat; + + if (!diruser) + return EOPNOTSUPP; + + create = (flags & O_CREAT); + excl = (flags & O_EXCL); + + /* Skip leading slashes */ + while (*filename == '/') + filename++; + + *retry_port_type = MACH_MSG_TYPE_MAKE_SEND; + *do_retry = FS_RETRY_NORMAL; + *retry_name = '\0'; + + if (*filename == '\0') + { + /* Set things up in the state expected by the code from gotit: on. */ + dnp = 0; + np = diruser->po->np; + mutex_lock (&np->lock); + netfs_nref (np); + goto gotit; + } + + dnp = diruser->po->np; + mutex_lock (&dnp->lock); + + netfs_nref (dnp); /* acquire a reference for later netfs_nput */ + + do + { + assert (!lastcomp); + + /* Find the name of the next pathname component */ + nextname = index (filename, '/'); + + if (nextname) + { + *nextname++ = '\0'; + while (*nextname == '/') + nextname++; + if (*nextname == '\0') + { + /* These are the rules for filenames ending in /. */ + nextname = 0; + lastcomp = 1; + mustbedir = 1; + create = 0; + } + else + lastcomp = 0; + } + else + lastcomp = 1; + + np = 0; + + retry_lookup: + + if ((dnp == netfs_root_node || dnp == diruser->po->shadow_root) + && filename[0] == '.' && filename[1] == '.' && filename[2] == '\0') + if (dnp == diruser->po->shadow_root) + /* We're at the root of a shadow tree. */ + { + *do_retry = FS_RETRY_REAUTH; + *retry_port = diruser->po->shadow_root_parent; + *retry_port_type = MACH_MSG_TYPE_COPY_SEND; + if (! lastcomp) + strcpy (retry_name, nextname); + error = 0; + mutex_unlock (&dnp->lock); + goto out; + } + else if (diruser->po->root_parent != MACH_PORT_NULL) + /* We're at a real translator root; even if DIRUSER->po has a + shadow root, we can get here if its in a directory that was + renamed out from under it... */ + { + *do_retry = FS_RETRY_REAUTH; + *retry_port = diruser->po->root_parent; + *retry_port_type = MACH_MSG_TYPE_COPY_SEND; + if (!lastcomp) + strcpy (retry_name, nextname); + error = 0; + mutex_unlock (&dnp->lock); + goto out; + } + else + /* We are global root */ + { + error = 0; + np = dnp; + netfs_nref (np); + } + else { - /*FIXME: It means we've got a directory, and no translators are set on - directories. The point is that nsmux should mainly handle libtrivs-based - translators, which should not operate on directories.*/ - err = 0; - finalize(); - return err; - } + /* Attempt a lookup on the next pathname component. */ + /*error = netfs_attempt_lookup (diruser->user, dnp, filename, &np);*/ + + /*attempt an improved lookup on the next pathname component*/ + error = netfs_attempt_lookup_improved + (diruser->user, dnp, filename, flags, lastcomp, &np, &file); - /*If the current node has already been successfully translated*/ - if(lnode->flags & FLAG_LNODE_TRANSLATED) + /*If no problems have occurred during the lookup and we do not have O_EXCL*/ + if(!error && !excl) { - /*no additional translators must set on this file*/ - err = 0; - finalize(); - return err; + /*If a simple file has been looked up*/ + if(file) + /*just return the port*/ + goto justport; + + /*If a proxy for setting up translators has just been created*/ + if(np->nn->trans) + { + /*set the list of translators on this node*/ + node_set_translators + (diruser, np, np->nn->trans, np->nn->ntrans, flags, &file); + + /*lock the the node and add a new reference*/ + mutex_lock(&np->lock); + netfs_nref(np); + + /*return `file` as the resulting port*/ + goto justport; + } + } } - /*Obtain the list of inherited translators*/ - err = lnode_list_translators(lnode, &trans, &ntrans); - if(err) - { - finalize(); + /* At this point, DNP is unlocked */ + + /* Implement O_EXCL flag here */ + if (lastcomp && create && excl && !error) + error = EEXIST; + + /* Create the new node if necessary */ + if (lastcomp && create && error == ENOENT) + { + mode &= ~(S_IFMT | S_ISPARE | S_ISVTX); + mode |= S_IFREG; + mutex_lock (&dnp->lock); + error = netfs_attempt_create_file (diruser->user, dnp, + filename, mode, &np); + + /* If someone has already created the file (between our lookup + and this create) then we just got EEXIST. If we are + EXCL, that's fine; otherwise, we have to retry the lookup. */ + if (error == EEXIST && !excl) + { + mutex_lock (&dnp->lock); + goto retry_lookup; + } + + newnode = 1; + } + + /* All remaining errors get returned to the user */ + if (error) + goto out; + + error = netfs_validate_stat (np, diruser->user); + if (error) + goto out; + + if ((((flags & O_NOTRANS) == 0) || !lastcomp) + && ((np->nn_translated & S_IPTRANS) + || S_ISFIFO (np->nn_translated) + || S_ISCHR (np->nn_translated) + || S_ISBLK (np->nn_translated) + || fshelp_translated (&np->transbox))) + { + mach_port_t dirport; + + /* A callback function for short-circuited translators. + S_ISLNK and S_IFSOCK are handled elsewhere. */ + error_t short_circuited_callback1 (void *cookie1, void *cookie2, + uid_t *uid, gid_t *gid, + char **argz, size_t *argz_len) + { + struct node *np = cookie1; + error_t err; + + err = netfs_validate_stat (np, diruser->user); + if (err) return err; - } - /*If no translators have to be set*/ - if(ntrans == 0) + switch (np->nn_translated & S_IFMT) { - /*we have looked up what we need*/ - finalize(); - return 0; + case S_IFCHR: + case S_IFBLK: + if (asprintf (argz, "%s%c%d%c%d", + (S_ISCHR (np->nn_translated) + ? _HURD_CHRDEV : _HURD_BLKDEV), + 0, major (np->nn_stat.st_rdev), + 0, minor (np->nn_stat.st_rdev)) < 0) + return ENOMEM; + *argz_len = strlen (*argz) + 1; + *argz_len += strlen (*argz + *argz_len) + 1; + *argz_len += strlen (*argz + *argz_len) + 1; + break; + case S_IFIFO: + if (asprintf (argz, "%s", _HURD_FIFO) < 0) + return ENOMEM; + *argz_len = strlen (*argz) + 1; + break; + default: + return ENOENT; } - /*Go through the list of translators*/ - for(str = trans, i = 0; i < ntrans; ++i) + *uid = np->nn_stat.st_uid; + *gid = np->nn_stat.st_gid; + + return 0; + } + + /* Create an unauthenticated port for DNP, and then + unlock it. */ + error = iohelp_create_empty_iouser (&user); + if (! error) + { + newpi = netfs_make_protid (netfs_make_peropen (dnp, 0, + diruser->po), + user); + if (! newpi) + { + error = errno; + iohelp_free_iouser (user); + } + } + + if (! error) + { + dirport = ports_get_send_right (newpi); + ports_port_deref (newpi); + + error = fshelp_fetch_root (&np->transbox, diruser->po, + dirport, + diruser->user, + lastcomp ? flags : 0, + ((np->nn_translated & S_IPTRANS) + ? _netfs_translator_callback1 + : short_circuited_callback1), + _netfs_translator_callback2, + do_retry, retry_name, retry_port); + /* fetch_root copies DIRPORT for success, so we always should + deallocate our send right. */ + mach_port_deallocate (mach_task_self (), dirport); + } + + if (error != ENOENT) + { + netfs_nrele (dnp); + netfs_nput (np); + *retry_port_type = MACH_MSG_TYPE_MOVE_SEND; + if (!lastcomp && !error) { - /*set the translator specified in the current component*/ - err = node_set_translator(dir, *node, str); - if(err) + strcat (retry_name, "/"); + strcat (retry_name, nextname); + } + return error; + } + + /* ENOENT means there was a hiccup, and the translator vanished + while NP was unlocked inside fshelp_fetch_root; continue as normal. */ + error = 0; + } + + if (S_ISLNK (np->nn_translated) + && (!lastcomp + || mustbedir /* "foo/" must see that foo points to a dir */ + || !(flags & (O_NOLINK|O_NOTRANS)))) + { + size_t nextnamelen, newnamelen, linklen; + char *linkbuf; + + /* Handle symlink interpretation */ + if (nsymlinks++ > netfs_maxsymlinks) + { + error = ELOOP; + goto out; + } + + linklen = np->nn_stat.st_size; + + nextnamelen = nextname ? strlen (nextname) + 1 : 0; + newnamelen = nextnamelen + linklen + 1; + linkbuf = alloca (newnamelen); + + error = netfs_attempt_readlink (diruser->user, np, linkbuf); + if (error) + goto out; + + if (nextname) + { + linkbuf[linklen] = '/'; + memcpy (linkbuf + linklen + 1, nextname, + nextnamelen - 1); + } + linkbuf[nextnamelen + linklen] = '\0'; + + if (linkbuf[0] == '/') + { + /* Punt to the caller */ + *do_retry = FS_RETRY_MAGICAL; + *retry_port = MACH_PORT_NULL; + strcpy (retry_name, linkbuf); + goto out; + } + + filename = linkbuf; + if (lastcomp) + { + lastcomp = 0; + + /* Symlinks to nonexistent files aren't allowed to cause + creation, so clear the flag here. */ + create = 0; + } + netfs_nput (np); + mutex_lock (&dnp->lock); + np = 0; + } + else + { + /* Normal nodes here for next filename component */ + filename = nextname; + netfs_nrele (dnp); + + if (lastcomp) + dnp = 0; + else + { + dnp = np; + np = 0; + } + } + } + while (filename && *filename); + + /* At this point, NP is the node to return. */ + gotit: + + if (mustbedir) + { + netfs_validate_stat (np, diruser->user); + if (!S_ISDIR (np->nn_stat.st_mode)) + { + error = ENOTDIR; + goto out; + } + } + error = netfs_check_open_permissions (diruser->user, np, + flags, newnode); + if (error) + goto out; + + flags &= ~OPENONLY_STATE_MODES; + + error = iohelp_dup_iouser (&user, diruser->user); + if (error) + goto out; + + newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po), + user); + if (! newpi) + { + iohelp_free_iouser (user); + error = errno; + goto out; + } + + *retry_port = ports_get_right (newpi); + ports_port_deref (newpi); + + goto out; + + /*At this point, we have to return `file` as the resulting port*/ + justport: + /*If a directory is definitely wanted*/ + if(mustbedir) + { + /*stat the looked up file*/ + error = io_stat(file, &stat); + if(error) + goto out; + + /*If the file is not a directory*/ + if(!S_ISDIR(stat.st_mode)) { - finalize(); - return 0; /*TODO: A better way to fight errors here*/ + /*this is an error; stop here*/ + error = ENOTDIR; + goto out; } - - /*skip the current component*/ - str += strlen(str) + 1; - } - - /*All translators have been succesfully set on this node*/ - lnode->flags |= FLAG_LNODE_TRANSLATED; - - /*Everything OK here*/ - finalize(); - return err; - }/*netfs_attempt_lookup*/ + + } + + /*Check the user's open permissions for the specified port*/ + error = check_open_permissions(diruser->user, &stat, file); + if(error) + goto out; + + /*Restrict the port to the rights of the user*/ + error = io_restrict_auth + ( + file, &file_restricted, + diruser->user->uids->ids, diruser->user->uids->num, + diruser->user->gids->ids, diruser->user->gids->num + ); + if(error) + goto out; + + /*Put the resulting port in the corresponding receiver parameter*/ + *retry_port = file_restricted; + *retry_port_type = MACH_MSG_TYPE_MOVE_SEND; + + out: + if(error && (file != MACH_PORT_NULL)) + PORT_DEALLOC(file); + if (np) + netfs_nput (np); + if (dnp) + netfs_nrele (dnp); + return error; + }/*netfs_S_dir_lookup*/ /*----------------------------------------------------------------------------*/ /*Deletes `name` in `dir` for `user`*/ error_t @@ -1353,32 +1661,6 @@ netfs_attempt_read error_t err = 0; - /*If there is no port open for the current node*/ - if(np->nn->port == MACH_PORT_NULL) - { - /*the parent node of the current node*/ - node_t * dnp; - - /*obtain the parent node of the the current node*/ - err = ncache_node_lookup(np->nn->lnode->dir, &dnp); - - /*the lookup should never fail here*/ - assert(!err); - - /*open a port to the file we are interested in*/ - mach_port_t p = file_name_lookup_under - (dnp->nn->port, np->nn->lnode->name, O_READ, 0); - - /*put `dnp` back, since we don't need it any more*/ - netfs_nput(dnp); - - if(!p) - return EBADF; - - /*store the port in the node*/ - np->nn->port = p; - } - /*Obtain a pointer to the first byte of the supplied buffer*/ char * buf = data; @@ -1411,8 +1693,6 @@ netfs_attempt_write void * data ) { - LOG_MSG("netfs_attempt_write"); - return 0; }/*netfs_attempt_write*/ /*----------------------------------------------------------------------------*/ @@ -1427,6 +1707,8 @@ netfs_node_norefs node_destroy(np); }/*netfs_node_norefs*/ /*----------------------------------------------------------------------------*/ + +/*----------------------------------------------------------------------------*/ /*Entry point*/ int main @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -47,6 +47,12 @@ /*The inode number for the root node*/ #define NSMUX_ROOT_INODE 1 /*----------------------------------------------------------------------------*/ +/*The special flag character for creating proxy nodes*/ +#define LOOKUP_MAGIC_CHAR '\33' +/*----------------------------------------------------------------------------*/ +/*Bits that are turned off after open*/ +#define OPENONLY_STATE_MODES (O_CREAT | O_EXCL | O_NOLINK | O_NOTRANS) +/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ /*--------Global Variables----------------------------------------------------*/ @@ -60,6 +66,10 @@ extern mach_port_t underlying_node; /*The stat information about the underlying node*/ extern io_statbuf_t underlying_node_stat; /*----------------------------------------------------------------------------*/ +/*The translator callbacks required by netfs_S_dir_lookup*/ +fshelp_fetch_root_callback1_t _netfs_translator_callback1; +fshelp_fetch_root_callback2_t _netfs_translator_callback2; +/*----------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------*/ /*--------Functions-----------------------------------------------------------*/ @@ -145,6 +155,37 @@ netfs_attempt_lookup struct node ** node ); /*----------------------------------------------------------------------------*/ +/*Performs an advanced lookup of file `name` under `dir`. If the lookup of the + last component of the path is requested (`lastcomp` is 1), it is not a + directory and no translators are required (`name` does not contain ',,'), the + function will simply open the required file and return the port in `file`. In + other cases it will create a proxy node and return it in `node`.*/ +error_t +netfs_attempt_lookup_improved + ( + struct iouser * user, + struct node * dir, + char * name, + int flags, + int lastcomp, + node_t ** node, + file_t * file + ); +/*----------------------------------------------------------------------------*/ +/*Responds to the RPC dir_lookup*/ +error_t +netfs_S_dir_lookup + ( + struct protid * diruser, + char * filename, + int flags, + mode_t mode, + retry_type * do_retry, + char * retry_name, + mach_port_t * retry_port, + mach_msg_type_number_t * retry_port_type + ); +/*----------------------------------------------------------------------------*/ /*Deletes `name` in `dir` for `user`*/ error_t netfs_attempt_unlink @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or @@ -5,7 +5,7 @@ /*----------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*----------------------------------------------------------------------------*/ -/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc. +/*Copyright (C) 2001, 2002, 2005, 2008 Free Software Foundation, Inc. Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>. This program is free software; you can redistribute it and/or |