summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debug.h2
-rw-r--r--lib.c28
-rw-r--r--lib.h13
-rw-r--r--lnode.c168
-rw-r--r--lnode.h54
-rw-r--r--ncache.c2
-rw-r--r--ncache.h3
-rw-r--r--node.c511
-rw-r--r--node.h71
-rw-r--r--nsmux.c876
-rw-r--r--nsmux.h43
-rw-r--r--options.c2
-rw-r--r--options.h2
13 files changed, 1260 insertions, 515 deletions
diff --git a/debug.h b/debug.h
index c04b5be3d..d4e79cea7 100644
--- a/debug.h
+++ b/debug.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
diff --git a/lib.c b/lib.c
index aa0ff0be8..c05e08eae 100644
--- a/lib.c
+++ b/lib.c
@@ -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*/
+/*----------------------------------------------------------------------------*/
diff --git a/lib.h b/lib.h
index 6335efcfe..776d475e2 100644
--- a/lib.h
+++ b/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
@@ -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__*/
diff --git a/lnode.c b/lnode.c
index aed83a0f5..2e6d34eb5 100644
--- a/lnode.c
+++ b/lnode.c
@@ -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*/
/*----------------------------------------------------------------------------*/
diff --git a/lnode.h b/lnode.h
index 674102523..08a70adef 100644
--- a/lnode.h
+++ b/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
@@ -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__*/
+
diff --git a/ncache.c b/ncache.c
index 12462d316..5d39c0d38 100644
--- a/ncache.c
+++ b/ncache.c
@@ -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
diff --git a/ncache.h b/ncache.h
index 124d514e6..fd08158b1 100644
--- a/ncache.h
+++ b/ncache.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
@@ -40,6 +40,7 @@
/*----------------------------------------------------------------------------*/
/*----------------------------------------------------------------------------*/
+/*--------Types---------------------------------------------------------------*/
/*A cache chain*/
struct ncache
{
diff --git a/node.c b/node.c
index a37803e15..fc3bc2aa7 100644
--- a/node.c
+++ b/node.c
@@ -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*/
/*----------------------------------------------------------------------------*/
diff --git a/node.h b/node.h
index 72c244e8c..6cec6ddfe 100644
--- a/node.h
+++ b/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
@@ -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__*/
diff --git a/nsmux.c b/nsmux.c
index d512d7722..2ee209740 100644
--- a/nsmux.c
+++ b/nsmux.c
@@ -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
diff --git a/nsmux.h b/nsmux.h
index ee469f2d5..a38874d68 100644
--- a/nsmux.h
+++ b/nsmux.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
@@ -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
diff --git a/options.c b/options.c
index c69cd2806..55a0ea5ad 100644
--- a/options.c
+++ b/options.c
@@ -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
diff --git a/options.h b/options.h
index 60f757204..cbedb3db6 100644
--- a/options.h
+++ b/options.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