summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergiu Ivanov <unlimitedscolobb@gmail.com>2008-07-13 20:26:34 +0300
committerSergiu Ivanov <unlimitedscolobb@gmail.com>2008-07-13 20:26:34 +0300
commitc9c70745fbdb4be82796f94e5601109d9071bb46 (patch)
treeb0ec5e62a5f9ee77bc7f52f90a4bba47e74448a7
Copied the skeleton for mirroring a directory from filterfs.
At the moment nsmux is only capable of creating a read-only mirror of the given directory.
-rw-r--r--README43
-rwxr-xr-xbuild1
-rw-r--r--debug.h60
-rw-r--r--lib.c193
-rw-r--r--lib.h82
-rw-r--r--lnode.c305
-rw-r--r--lnode.h143
-rw-r--r--ncache.c221
-rw-r--r--ncache.h100
-rw-r--r--node.c623
-rw-r--r--node.h168
-rw-r--r--nsmux.c1122
-rw-r--r--nsmux.h339
-rw-r--r--options.c242
-rw-r--r--options.h55
15 files changed, 3697 insertions, 0 deletions
diff --git a/README b/README
new file mode 100644
index 000000000..bedea7396
--- /dev/null
+++ b/README
@@ -0,0 +1,43 @@
+ NSMUX TRANSLATOR
+
+ WHAT'S THIS?
+ nsmux is a GNU/Hurd translator that mirrors the contents of the
+directory it is given and provides the possibility to apply
+namespace-based translator selection to its contents.
+
+ WHAT'S NAMESPACE-BASED TRANSLATOR SELECTION?
+ Namespace-based translator selection is a special technique of
+applying translators without using the 'settrans' command. The main
+idea is using special file names. For example, if you want to the
+contents of file 'file' translated by translator 'x', you have to just
+ask for 'file,,x'. You can also obtain the untranslated version of the
+node 'file' using the following syntax: 'file,,0'. Note how we avoid
+the usage of 'settrans', and, at the same time, maintain the
+advantages of translator-based approach.
+ It is important to mention that the idea described above can be
+applied similarly to directories: if there is a directory 'directory'
+and you ask for 'directory,,x/', you will provided with a directory in
+which every entry will be translated by translator 'x'.
+ You can also filter the translator stack: suppose you have a file
+'file' translated by translators 'x', 'u', 'y', and 'z'. In most
+cases, you can request for 'file,,-u', and you will be provided with
+the node 'file' translated by 'x' only (the only translator which
+comes before 'u'). As usual, this syntax can be equally applied to
+directories.
+ Note that the functionality described in the previous paragraph is
+not hard-coded in the namespace proxy and depends on the decision of
+the special filtering translator '-u'.
+
+ WHAT'S IT FOR?
+ Namespace-based translator selection greatly facilitates the work
+with translators and therefore contributes to enlargening of their
+field of applicability. On the other hand, applying translators to a
+whole directory tree directly increases the capabilities of simple
+translators (libtrivfs based).
+
+ AUTHOR OF THE IDEA
+
+Olaf Buddenhagen (antrik)
+
+ DEVELOPER
+Sergiu Ivanov <unlimitedscolobb@gmail.com>
diff --git a/build b/build
new file mode 100755
index 000000000..2234dad6f
--- /dev/null
+++ b/build
@@ -0,0 +1 @@
+gcc -DDEBUG -Wall -g -lnetfs -lfshelp -liohelp -lthreads -lports -lihash -lshouldbeinlibc -o nsmux nsmux.c node.c lnode.c ncache.c options.c lib.c 2>&1 | tee errors
diff --git a/debug.h b/debug.h
new file mode 100644
index 000000000..c04b5be3d
--- /dev/null
+++ b/debug.h
@@ -0,0 +1,60 @@
+/*----------------------------------------------------------------------------*/
+/*debug.h*/
+/*----------------------------------------------------------------------------*/
+/*Simple facilities for debugging messages*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+#ifndef __DEBUG_H__
+#define __DEBUG_H__
+
+/*----------------------------------------------------------------------------*/
+#include <stdio.h>
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Macros--------------------------------------------------------------*/
+/*Print debug messages here*/
+#define DEBUG_OUTPUT "/var/log/nsmux.dbg"
+/*----------------------------------------------------------------------------*/
+#ifdef DEBUG
+ /*Initializes the log*/
+# define INIT_LOG() nsmux_dbg = fopen(DEBUG_OUTPUT, "wt")
+ /*Closes the log*/
+# define CLOSE_LOG() fclose(nsmux_dbg)
+ /*Prints a debug message and flushes the debug output*/
+# define LOG_MSG(fmt, args...) {fprintf(nsmux_dbg, fmt"\n", ##args);\
+ fflush(nsmux_dbg);}
+#else
+ /*Remove requests for debugging output*/
+# define INIT_LOG()
+# define CLOSE_LOG()
+# define LOG_MSG(fmt, args...)
+#endif /*DEBUG*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Global Variables----------------------------------------------------*/
+/*The file to write debugging info to*/
+extern FILE * nsmux_dbg;
+/*----------------------------------------------------------------------------*/
+
+#endif /*__DEBUG_H__*/
diff --git a/lib.c b/lib.c
new file mode 100644
index 000000000..aa0ff0be8
--- /dev/null
+++ b/lib.c
@@ -0,0 +1,193 @@
+/*----------------------------------------------------------------------------*/
+/*lib.h*/
+/*----------------------------------------------------------------------------*/
+/*Basic routines for filesystem manipulations*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+#define _GNU_SOURCE 1
+/*----------------------------------------------------------------------------*/
+#include <sys/mman.h>
+/*----------------------------------------------------------------------------*/
+#include "lib.h"
+#include "debug.h"
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Fetches directory entries for `dir`*/
+error_t
+dir_entries_get
+ (
+ file_t dir,
+ char ** dirent_data, /*the list of directory entries as returned
+ by dir_readdir*/
+ size_t * dirent_data_size, /*the size of `dirent_data`*/
+ struct dirent *** dirent_list /*the array of pointers to beginnings of
+ dirents in dirent_data*/
+ )
+ {
+ error_t err = 0;
+
+ /*The data (array of dirents(?)) returned by dir_readdir*/
+ char * data;
+
+ /*The size of `data`*/
+ size_t data_size;
+
+ /*The number of entries in `data`*/
+ int entries_num;
+
+ /*Try to read the contents of the specified directory*/
+ err = dir_readdir(dir, &data, &data_size, 0, -1, 0, &entries_num);
+ if(err)
+ return err;
+
+ /*Create a new list of dirents*/
+ struct dirent ** list;
+
+ /*Allocate the memory for the list of dirents and for the
+ finalizing element*/
+ list = malloc(sizeof(struct dirent *) * (entries_num + 1));
+
+ /*If memory allocation has failed*/
+ if(!list)
+ {
+ /*free the result of dir_readdir*/
+ munmap(data, data_size);
+
+ /*return the corresponding error*/
+ return ENOMEM;
+ }
+
+ /*The current directory entry*/
+ struct dirent * dp;
+
+ int i;
+
+ /*Go through every element of the list of dirents*/
+ for
+ (
+ i = 0, dp = (struct dirent *)data;
+ i < entries_num;
+ ++i, dp = (struct dirent *)((char *)dp + dp->d_reclen))
+ /*copy the current value into the list*/
+ *(list + i) = dp;
+
+ /*Nullify the last element of the list*/
+ *(list + i) = NULL;
+
+ /*Copy the required values in the parameters*/
+ *dirent_data = data;
+ *dirent_data_size = data_size;
+ *dirent_list = list;
+
+ /*Return success*/
+ return err;
+ }/*dir_entries_get*/
+/*----------------------------------------------------------------------------*/
+/*Lookup `name` under `dir` (or cwd, if `dir` is invalid)*/
+error_t
+file_lookup
+ (
+ file_t dir,
+ char * name,
+ int flags0, /*try to open with these flags first*/
+ int flags1, /*try to open with these flags, if `flags0` fail*/
+ int mode, /*if the file is to be created, create it with this mode*/
+ file_t * port, /*store the port to the looked up file here*/
+ io_statbuf_t * stat /*store the stat information here*/
+ )
+ {
+ error_t err = 0;
+
+ /*The port to the looked up file*/
+ file_t p;
+
+ /*The stat information about the looked up file*/
+ io_statbuf_t s;
+
+ /*Performs a lookup under 'dir' or in cwd, if `dir` is invalid*/
+ file_t
+ do_file_lookup
+ (
+ file_t dir,
+ char * name, /*lookup this file*/
+ int flags, /*lookup the file with these flags*/
+ int mode /*if a new file is to be created, create it with this mode*/
+ )
+ {
+ /*The result of lookup*/
+ file_t p;
+
+ /*If `dir` is a valid port*/
+ if(dir != MACH_PORT_NULL)
+ /*try to lookup `name` under `dir`*/
+ p = file_name_lookup_under(dir, name, flags, mode);
+ else
+ /*lookup `name` under current cwd*/
+ p = file_name_lookup(name, flags, mode);
+
+ /*Return the result of the lookup*/
+ return p;
+ }/*do_file_lookup*/
+
+ /*Lookup `name` under the suggested `dir`*/
+ p = do_file_lookup(dir, name, flags0, mode);
+
+ /*If the lookup failed*/
+ if(p == MACH_PORT_NULL)
+ /*try to lookup for `name` using alternative flags `flags1`*/
+ p = do_file_lookup(dir, name, flags1, mode);
+
+ /*If the port is valid*/
+ if(p != MACH_PORT_NULL)
+ {
+ /*If stat information is required*/
+ if(stat)
+ {
+ /*obtain stat information for `p`*/
+ err = io_stat(p, &s);
+ if(err)
+ PORT_DEALLOC(p);
+ }
+ }
+ else
+ /*copy `errno` into `err`*/
+ err = errno;
+
+ /*If no errors have happened during lookup*/
+ if(!err)
+ {
+ /*copy the resulting port into *`port`*/
+ *port = p;
+
+ /*fill in the receiver for stat information, if requried*/
+ if(stat)
+ *stat = s;
+ }
+
+ /*Return the result of performing operations*/
+ return err;
+ }/*file_lookup*/
+/*----------------------------------------------------------------------------*/
diff --git a/lib.h b/lib.h
new file mode 100644
index 000000000..6335efcfe
--- /dev/null
+++ b/lib.h
@@ -0,0 +1,82 @@
+/*----------------------------------------------------------------------------*/
+/*lib.h*/
+/*----------------------------------------------------------------------------*/
+/*Declarations of basic routines for filesystem manipulations*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+#ifndef __LIB_H__
+#define __LIB_H__
+
+/*----------------------------------------------------------------------------*/
+#define __USE_FILE_OFFSET64
+/*----------------------------------------------------------------------------*/
+#include <hurd.h>
+#include <dirent.h>
+#include <stddef.h>
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Macros--------------------------------------------------------------*/
+/*Alignment of directory entries*/
+#define DIRENT_ALIGN 4
+/*----------------------------------------------------------------------------*/
+/*The offset of the directory name in the directory entry structure*/
+#define DIRENT_NAME_OFFS offsetof(struct dirent, d_name)
+/*----------------------------------------------------------------------------*/
+/*Computes the length of the structure before the name + the name + 0,
+ all padded to DIRENT_ALIGN*/
+#define DIRENT_LEN(name_len)\
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + DIRENT_ALIGN - 1) &\
+ ~(DIRENT_ALIGN - 1))
+/*----------------------------------------------------------------------------*/
+/*Deallocate the given port for the current task*/
+#define PORT_DEALLOC(p) (mach_port_deallocate(mach_task_self(), (p)))
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Fetches directory entries for `dir`*/
+error_t
+dir_entries_get
+ (
+ file_t dir,
+ char ** dirent_data, /*the list of directory entries as returned
+ by dir_readdir*/
+ size_t * dirent_data_size, /*the size of `dirent_data`*/
+ struct dirent *** dirent_list /*the array of pointers to beginnings of
+ dirents in dirent_data*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Lookup `name` under `dir` (or cwd, if `dir` is invalid)*/
+error_t
+file_lookup
+ (
+ file_t dir,
+ char * name,
+ int flags0, /*try to open with these flags first*/
+ int flags1, /*try to open with these flags, if `flags0` fail*/
+ int mode, /*if the file is to be created, create it with this mode*/
+ file_t * port, /*store the port to the looked up file here*/
+ io_statbuf_t * stat /*store the stat information here*/
+ );
+/*----------------------------------------------------------------------------*/
+#endif /*__LIB_H__*/
diff --git a/lnode.c b/lnode.c
new file mode 100644
index 000000000..285d947bb
--- /dev/null
+++ b/lnode.c
@@ -0,0 +1,305 @@
+/*----------------------------------------------------------------------------*/
+/*lnode.c*/
+/*----------------------------------------------------------------------------*/
+/*Implementation of policies of management of 'light nodes'*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+#define _GNU_SOURCE
+/*----------------------------------------------------------------------------*/
+#include "lnode.h"
+#include "debug.h"
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Adds a reference to the `lnode` (which must be locked)*/
+void
+lnode_ref_add
+ (
+ lnode_t * node
+ )
+ {
+ /*Increment the number of references*/
+ ++node->references;
+ }/*lnode_ref_add*/
+/*----------------------------------------------------------------------------*/
+/*Removes a reference from `node` (which must be locked). If that was the last
+ reference, destroy the node*/
+void
+lnode_ref_remove
+ (
+ lnode_t * node
+ )
+ {
+ /*Fail if the node is not referenced by anybody*/
+ assert(node->references);
+
+ /*Decrement the number of references to `node`*/
+ --node->references;
+
+ /*If there are no references remaining*/
+ if(node->references == 0)
+ {
+ /*uninstall the node from the directory it is in and destroy it*/
+ lnode_uninstall(node);
+ lnode_destroy(node);
+ }
+ else
+ /*simply unlock the node*/
+ mutex_unlock(&node->lock);
+ }/*lnode_ref_remove*/
+/*----------------------------------------------------------------------------*/
+/*Creates a new lnode with `name`; the new node is locked and contains
+ a single reference*/
+error_t
+lnode_create
+ (
+ char * name,
+ lnode_t ** node /*put the result here*/
+ )
+ {
+ /*Allocate the memory for the node*/
+ lnode_t * node_new = malloc(sizeof(lnode_t));
+
+ /*If the memory has not been allocated*/
+ if(!node_new)
+ {
+ /*stop*/
+ return ENOMEM;
+ }
+
+ /*The copy of the name*/
+ char * name_cp = NULL;
+
+ /*If the name exists*/
+ if(name)
+ {
+ /*duplicate it*/
+ name_cp = strdup(name);
+
+ /*If the name has not been duplicated*/
+ if(!name_cp)
+ {
+ /*free the node*/
+ free(node_new);
+
+ /*stop*/
+ return ENOMEM;
+ }
+ }
+
+ /*Setup the new node*/
+ memset(node_new, 0, sizeof(lnode_t));
+ node_new->name = name_cp;
+ node_new->name_len = (name_cp) ? (strlen(name_cp)) : (0);
+
+ /*Setup one reference to this lnode*/
+ node_new->references = 1;
+
+ /*Initialize the mutex and acquire a lock on this lnode*/
+ mutex_init(&node_new->lock);
+ mutex_lock(&node_new->lock);
+
+ /*Store the result in the second parameter*/
+ *node = node_new;
+
+ /*Return success*/
+ return 0;
+ }/*lnode_create*/
+/*----------------------------------------------------------------------------*/
+/*Destroys the given lnode*/
+void
+lnode_destroy
+ (
+ lnode_t * node /*destroy this*/
+ )
+ {
+ /*Destroy the name of the node*/
+ free(node->name);
+
+ /*Destroy the node itself*/
+ free(node);
+ }/*lnode_destroy*/
+/*----------------------------------------------------------------------------*/
+/*Constructs the full path for the given lnode and stores the result both in
+ the parameter and inside the lnode (the same string, actually)*/
+error_t
+lnode_path_construct
+ (
+ lnode_t * node,
+ char ** path /*store the path here*/
+ )
+ {
+ error_t err = 0;
+
+ /*The final path*/
+ char * p;
+
+ /*The final length of the path*/
+ int p_len = 0;
+
+ /*A temporary pointer to an lnode*/
+ lnode_t * n;
+
+ /*While the root node of the proxy filesystem has not been reached*/
+ for(n = node; n && n->dir; n = n->dir)
+ /*add the length of the name of `n` to `p_len` make some space for
+ the delimiter '/', if we are not approaching the root node*/
+ /*p_len += n->name_len + ((n->dir->dir) ? (1) : (0));*/
+ /*There is some path to our root node, so we will anyway have to
+ add a '/'*/
+ p_len += n->name_len + 1;
+
+ /*Include the space for the path to the root node of the proxy
+ (n is now the root of the filesystem)*/
+ p_len += strlen(n->path) + 1;
+
+ /*Try to allocate the space for the string*/
+ p = malloc(p_len * sizeof(char));
+ if(!p)
+ err = ENOMEM;
+ /*If memory allocation has been successful*/
+ else
+ {
+ /*put a terminal 0 at the end of the path*/
+ p[--p_len] = 0;
+
+ /*While the root node of the proxy filesystem has not been reached*/
+ for(n = node; n && n->dir; n = n->dir)
+ {
+ /*compute the position where the name of `n` is to be inserted*/
+ p_len -= n->name_len;
+
+ /*copy the name of the node into the path (omit the terminal 0)*/
+ strncpy(p + p_len, n->name, n->name_len);
+
+ /*If we are not at the root node of the proxy filesystem, add the
+ separator*/
+ /*if(n->dir->dir)
+ p[--p_len] = '/';*/
+ /*we anyway have to add the separator slash*/
+ p[--p_len] = '/';
+ }
+
+ /*put the path to the root node at the beginning of the first path
+ (n is at the root now)*/
+ strncpy(p, n->path, strlen(n->path));
+
+ /*destroy the former path in lnode, if it exists*/
+ if(node->path)
+ free(node->path);
+
+ /*store the new path inside the lnode*/
+ node->path = p;
+
+ /*store the path in the parameter*/
+ if(path)
+ *path = p;
+ }
+
+ /*Return the result of operations*/
+ return err;
+ }/*lnode_path_construct*/
+/*----------------------------------------------------------------------------*/
+/*Gets a light node by its name, locks it and increments its refcount*/
+error_t
+lnode_get
+ (
+ lnode_t * dir, /*search here*/
+ char * name, /*search for this name*/
+ lnode_t ** node /*put the result here*/
+ )
+ {
+ error_t err = 0;
+
+ /*The pointer to the required lnode*/
+ lnode_t * n;
+
+ /*Find `name` among the names of entries in `dir`*/
+ for(n = dir->entries; n && (strcmp(n->name, name) != 0); n = n->next);
+
+ /*If the search has been successful*/
+ if(n)
+ {
+ /*lock the node*/
+ mutex_lock(&n->lock);
+
+ /*increment the refcount of the found lnode*/
+ lnode_ref_add(n);
+
+ /*put a pointer to `n` into the parameter*/
+ *node = n;
+ }
+ else
+ err = ENOENT;
+
+ /*Return the result of operations*/
+ return err;
+ }/*lnode_get*/
+/*----------------------------------------------------------------------------*/
+/*Install the lnode into the lnode tree: add a reference to `dir` (which must
+ be locked)*/
+void
+lnode_install
+ (
+ lnode_t * dir, /*install here*/
+ lnode_t * node /*install this*/
+ )
+ {
+ /*Install `node` into the list of entries in `dir`*/
+ node->next = dir->entries;
+ node->prevp = &dir->entries; /*this node is the first on the list*/
+ if(dir->entries)
+ dir->entries->prevp = &node->next; /*here `prevp` gets the value
+ corresponding to its meaning*/
+ dir->entries = node;
+
+ /*Add a new reference to dir*/
+ lnode_ref_add(dir);
+
+ /*Setup the `dir` link in node*/
+ node->dir = dir;
+ }/*lnode_install*/
+/*----------------------------------------------------------------------------*/
+/*Unistall the node from the node tree; remove a reference from the lnode
+ containing `node`*/
+void
+lnode_uninstall
+ (
+ lnode_t * node
+ )
+ {
+ /*Remove a reference from the parent*/
+ lnode_ref_remove(node->dir);
+
+ /*Make the next pointer in the previous element point to the element,
+ which follows `node`*/
+ *node->prevp = node->next;
+
+ /*If the current node is not the last one, connect the list after removal
+ of the current node*/
+ if(node->next)
+ node->next->prevp = &node->next;
+ }/*lnode_uninstall*/
+/*----------------------------------------------------------------------------*/
diff --git a/lnode.h b/lnode.h
new file mode 100644
index 000000000..7c42c8d4f
--- /dev/null
+++ b/lnode.h
@@ -0,0 +1,143 @@
+/*----------------------------------------------------------------------------*/
+/*lnode.h*/
+/*----------------------------------------------------------------------------*/
+/*Management of cheap 'light nodes'*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+#ifndef __LNODE_H__
+#define __LNODE_H__
+
+/*----------------------------------------------------------------------------*/
+#include <error.h>
+#include <hurd/netfs.h>
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*A candy synonym for the fundamental libnetfs node*/
+typedef struct node node_t;
+/*----------------------------------------------------------------------------*/
+/*The light node*/
+struct lnode
+ {
+ /*the name of the lnode*/
+ char * name;
+
+ /*the length of the name; `name` does not change, and this value is used
+ quite often, therefore calculate it just once*/
+ size_t name_len;
+
+ /*the full path to the lnode*/
+ char * path;
+
+ /*the associated flags*/
+ int flags;
+
+ /*the number of references to this lnode*/
+ int references;
+
+ /*the reference to the real node*/
+ node_t * node;
+
+ /*the next lnode and the pointer to this lnode from the previous one*/
+ struct lnode * next, **prevp;
+
+ /*the lnode (directory) in which this node is contained*/
+ struct lnode * dir;
+
+ /*the beginning of the list of entries contained in this lnode (directory)*/
+ struct lnode * entries;
+
+ /*a lock*/
+ struct mutex lock;
+ };/*struct lnode*/
+/*----------------------------------------------------------------------------*/
+typedef struct lnode lnode_t;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Adds a reference to the `lnode` (which must be locked)*/
+void
+lnode_ref_add
+ (
+ lnode_t * node
+ );
+/*----------------------------------------------------------------------------*/
+/*Removes a reference from `node` (which must be locked). If that was the last
+ reference, destroy the node*/
+void
+lnode_ref_remove
+ (
+ lnode_t * node
+ );
+/*----------------------------------------------------------------------------*/
+/*Creates a new lnode with `name`; the new node is locked and contains
+ a single reference*/
+error_t
+lnode_create
+ (
+ char * name,
+ lnode_t ** node /*put the result here*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Destroys the given lnode*/
+void
+lnode_destroy
+ (
+ lnode_t * node /*destroy this*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Constructs the full path for the given lnode and stores the result both in
+ the parameter and inside the lnode (the same string, actually)*/
+error_t
+lnode_path_construct
+ (
+ lnode_t * node,
+ char ** path /*store the path here*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Gets a light node by its name, locks it and increments its refcount*/
+error_t
+lnode_get
+ (
+ lnode_t * dir, /*search here*/
+ char * name, /*search for this name*/
+ lnode_t ** node /*put the result here*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Install the lnode into the lnode tree: add a reference to `dir` (which must
+ be locked)*/
+void
+lnode_install
+ (
+ lnode_t * dir, /*install here*/
+ lnode_t * node /*install this*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Unistall the node from the node tree; remove a reference from the lnode*/
+void
+lnode_uninstall
+ (
+ lnode_t * node
+ );
+/*----------------------------------------------------------------------------*/
+#endif /*__LNODE_H__*/
diff --git a/ncache.c b/ncache.c
new file mode 100644
index 000000000..12462d316
--- /dev/null
+++ b/ncache.c
@@ -0,0 +1,221 @@
+/*----------------------------------------------------------------------------*/
+/*ncache.h*/
+/*----------------------------------------------------------------------------*/
+/*The implementation of the cache of nodes*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+#define _GNU_SOURCE 1
+/*----------------------------------------------------------------------------*/
+#include "ncache.h"
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Global Variables----------------------------------------------------*/
+/*The global cache chain*/
+ncache_t ncache;
+/*----------------------------------------------------------------------------*/
+/*Cache size (may be overwritten by the user)*/
+int ncache_size = NCACHE_SIZE;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Initializes the node cache*/
+void
+ncache_init
+ (
+ int size_max
+ )
+ {
+ /*Reset the LRU and MRU ends of the list*/
+ ncache.mru = ncache.lru = NULL;
+
+ /*Set the maximal size according to the parameter*/
+ ncache.size_max = size_max;
+
+ /*The cache is empty so far; remark that*/
+ ncache.size_current = 0;
+
+ /*Init the lock*/
+ mutex_init(&ncache.lock);
+ }/*ncache_init*/
+/*----------------------------------------------------------------------------*/
+/*Looks up the lnode and stores the result in `node`; creates a new entry
+ in the cache if the lookup fails*/
+error_t
+ncache_node_lookup
+ (
+ lnode_t * lnode, /*search for this*/
+ node_t ** node /*put the result here*/
+ )
+ {
+ error_t err = 0;
+
+ /*Obtain the pointer to the node corresponding to `lnode`*/
+ node_t * n = lnode->node;
+
+ /*If a node has already been created for the given lnode*/
+ if(n != NULL)
+ {
+ /*count a new reference to 'n'*/
+ netfs_nref(n);
+ }
+ else
+ {
+ /*create a new node for the given lnode and store the result in `n`*/
+ err = node_create(lnode, &n);
+ }
+
+ /*If no errors have occurred during the previous operations*/
+ if(!err)
+ {
+ /*lock the mutex in the looked up node*/
+ mutex_lock(&n->lock);
+
+ /*store the lookup result in `node`*/
+ *node = n;
+ }
+
+ /*Return the result of operations*/
+ return err;
+ }/*ncache_node_lookup*/
+/*----------------------------------------------------------------------------*/
+/*Removes the given node from the cache*/
+static
+void
+ncache_node_remove
+ (
+ node_t * node
+ )
+ {
+ /*Obtain the pointer to the netnode (this contains the information
+ specific of us)*/
+ struct netnode * nn = node->nn;
+
+ /*If there exists a successor of this node in the cache chain*/
+ if(nn->ncache_next)
+ /*remove the reference in the successor*/
+ nn->ncache_next->nn->ncache_prev = nn->ncache_prev;
+ /*If there exists a predecessor of this node in the cache chain*/
+ if(nn->ncache_prev)
+ /*remove the reference in the predecessor*/
+ nn->ncache_prev->nn->ncache_next = nn->ncache_next;
+
+ /*If the node was located at the MRU end of the list*/
+ if(ncache.mru == node)
+ /*shift the MRU end to the next node*/
+ ncache.mru = nn->ncache_next;
+ /*If the node was located at the LRU end of the list*/
+ if(ncache.lru == node)
+ /*shift the LRU end to the previous node*/
+ ncache.lru = nn->ncache_prev;
+
+ /*Invalidate the references inside the node*/
+ nn->ncache_next = nn->ncache_prev = NULL;
+
+ /*Count the removal of a node*/
+ --ncache.size_current;
+ }/*ncache_node_remove*/
+/*----------------------------------------------------------------------------*/
+/*Resets the node cache*/
+void
+ncache_reset(void)
+ {
+ /*The node being currently deleted*/
+ node_t * node;
+
+ /*Acquire a lock on the cache*/
+ mutex_lock(&ncache.lock);
+
+ /*Remove the whole cache chain*/
+ for(node = ncache.mru; node != NULL; ncache_node_remove(node), node = ncache.mru);
+
+ /*Release the lock*/
+ mutex_unlock(&ncache.lock);
+ }/*ncache_reset*/
+/*----------------------------------------------------------------------------*/
+/*Adds the given node to the cache*/
+void
+ncache_node_add
+ (
+ node_t * node /*the node to add*/
+ )
+ {
+ /*Acquire a lock on the cache*/
+ mutex_lock(&ncache.lock);
+
+ /*If there already are some nodes in the cache and it is enabled*/
+ if((ncache.size_max > 0) || (ncache.size_current > 0))
+ {
+ /*If the node to be added is not at the MRU end already*/
+ if(ncache.mru != node)
+ {
+ /*If the node is correctly integrated in the cache*/
+ if((node->nn->ncache_next != NULL) && (node->nn->ncache_prev != NULL))
+ /*remove the old entry*/
+ ncache_node_remove(node);
+ else
+ /*add a new reference to the node*/
+ netfs_nref(node);
+
+ /*put the node at the MRU end of the cache chain*/
+ node->nn->ncache_next = ncache.mru;
+ node->nn->ncache_prev = NULL;
+
+ /*setup the pointer in the old MRU end, if it exists*/
+ if(ncache.mru != NULL)
+ ncache.mru->nn->ncache_prev = node;
+
+ /*setup the LRU end of the cache chain, if it did not exist previously*/
+ if(ncache.lru == NULL)
+ ncache.lru = node;
+
+ /*shift the MRU end to the new node*/
+ ncache.mru = node;
+
+ /*count the addition*/
+ ++ncache.size_current;
+ }
+ }
+
+ /*While the size of the cache is exceeding the maximal size*/
+ node_t * old_lru;
+ for
+ (
+ old_lru = ncache.lru;
+ ncache.size_current > ncache.size_max;
+ old_lru = ncache.lru
+ )
+ {
+ /*remove the current LRU end of the list from the cache*/
+ ncache_node_remove(old_lru);
+
+ /*release the reference to the node owned by this thread*/
+ netfs_nrele(old_lru);
+ }
+
+ /*Release the lock on the cache*/
+ mutex_unlock(&ncache.lock);
+ }/*ncache_node_add*/
+/*----------------------------------------------------------------------------*/
diff --git a/ncache.h b/ncache.h
new file mode 100644
index 000000000..124d514e6
--- /dev/null
+++ b/ncache.h
@@ -0,0 +1,100 @@
+/*----------------------------------------------------------------------------*/
+/*ncache.h*/
+/*----------------------------------------------------------------------------*/
+/*The cache of nodes*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+#ifndef __NCACHE_H__
+#define __NCACHE_H__
+
+/*----------------------------------------------------------------------------*/
+#include <error.h>
+#include <hurd/netfs.h>
+/*----------------------------------------------------------------------------*/
+#include "node.h"
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Macros--------------------------------------------------------------*/
+/*The default maximal cache size*/
+#define NCACHE_SIZE 256
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*A cache chain*/
+struct ncache
+ {
+ /*the MRU end of the cache chain*/
+ node_t * mru;
+
+ /*the LRU end of the cache chain*/
+ node_t * lru;
+
+ /*the maximal number of nodes to cache*/
+ int size_max;
+
+ /*the current length of the cache chain*/
+ int size_current;
+
+ /*a lock*/
+ struct mutex lock;
+ };/*struct ncache*/
+/*----------------------------------------------------------------------------*/
+typedef struct ncache ncache_t;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Global Variables----------------------------------------------------*/
+/*The cache size (may be overwritten by the user)*/
+extern int cache_size;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Initializes the node cache*/
+void
+ncache_init
+ (
+ int size_max
+ );
+/*----------------------------------------------------------------------------*/
+/*Looks up the lnode and stores the result in `node`; creates a new entry
+ in the cache if the lookup fails*/
+error_t
+ncache_node_lookup
+ (
+ lnode_t * lnode, /*search for this*/
+ node_t ** node /*put the result here*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Resets the node cache*/
+void
+ncache_reset(void);
+/*----------------------------------------------------------------------------*/
+/*Adds the given node to the cache*/
+void
+ncache_node_add
+ (
+ node_t * node /*the node to add*/
+ );
+/*----------------------------------------------------------------------------*/
+#endif /*__NCACHE_H__*/
diff --git a/node.c b/node.c
new file mode 100644
index 000000000..a622db2bd
--- /dev/null
+++ b/node.c
@@ -0,0 +1,623 @@
+/*----------------------------------------------------------------------------*/
+/*node.c*/
+/*----------------------------------------------------------------------------*/
+/*Implementation of node management strategies*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+#define _GNU_SOURCE 1
+/*----------------------------------------------------------------------------*/
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <stdio.h>
+/*----------------------------------------------------------------------------*/
+#include "debug.h"
+#include "node.h"
+#include "options.h"
+#include "lib.h"
+#include "nsmux.h"
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Global Variables----------------------------------------------------*/
+/*The lock protecting the underlying filesystem*/
+struct mutex ulfs_lock = MUTEX_INITIALIZER;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Derives a new node from `lnode` and adds a reference to `lnode`*/
+error_t
+node_create
+ (
+ lnode_t * lnode,
+ node_t ** node /*store the result here*/
+ )
+ {
+ error_t err = 0;
+
+ /*Create a new netnode*/
+ netnode_t * netnode_new = malloc(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;
+ }
+
+ /*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;
+ node_new->nn->flags = 0;
+ node_new->nn->ncache_next = node_new->nn->ncache_prev = NULL;
+
+ /*store the result of creation in the second parameter*/
+ *node = node_new;
+ }
+
+ /*Return the result of operations*/
+ return err;
+ }/*node_create*/
+/*----------------------------------------------------------------------------*/
+/*Destroys the specified node and removes a light reference from the
+ associated light node*/
+void
+node_destroy
+ (
+ node_t * np
+ )
+ {
+ /*Die if the node does not belong to node cache*/
+ assert(!np->nn->ncache_next || !np->nn->ncache_prev);
+
+ /*Destroy the port to the underlying filesystem allocated to the node*/
+ PORT_DEALLOC(np->nn->port);
+
+ /*Lock the lnode corresponding to the current node*/
+ mutex_lock(&np->nn->lnode->lock);
+
+ /*Orphan the light node*/
+ np->nn->lnode->node = NULL;
+
+ /*Remove a reference from the lnode*/
+ lnode_ref_remove(np->nn->lnode);
+
+ /*Free the netnode and the node itself*/
+ free(np->nn);
+ free(np);
+ }/*node_destroy*/
+/*----------------------------------------------------------------------------*/
+/*Creates the root node and the corresponding lnode*/
+error_t
+node_create_root
+ (
+ node_t ** root_node /*store the result here*/
+ )
+ {
+ /*Try to create a new lnode*/
+ lnode_t * lnode;
+ error_t err = lnode_create(NULL, &lnode);
+
+ /*Stop, if the creation failed*/
+ if(err)
+ return err;
+
+ /*Try to derive the node corresponding to `lnode`*/
+ node_t * node;
+ err = node_create(lnode, &node);
+
+ /*If the operation failed*/
+ if(err)
+ {
+ /*destroy the created lnode*/
+ lnode_destroy(lnode);
+
+ /*stop*/
+ return err;
+ }
+
+ /*Release the lock on the lnode*/
+ mutex_unlock(&lnode->lock);
+
+ /*Store the result in the parameter*/
+ *root_node = node;
+
+ /*Return the result*/
+ return err;
+ }/*node_create_root*/
+/*----------------------------------------------------------------------------*/
+/*Initializes the port to the underlying filesystem for the root node*/
+error_t
+node_init_root
+ (
+ node_t * node /*the root node*/
+ )
+ {
+ error_t err = 0;
+
+ /*Acquire a lock for operations on the underlying filesystem*/
+ mutex_lock(&ulfs_lock);
+
+ /*Open the port to the directory specified in `dir`*/
+ node->nn->port = file_name_lookup(dir, O_READ | O_DIRECTORY, 0);
+
+ /*If the directory could not be opened*/
+ if(node->nn->port == MACH_PORT_NULL)
+ {
+ /*set the error code accordingly*/
+ err = errno;
+ LOG_MSG("node_init_root: Could not open the port for %s.", dir);
+
+ /*release the lock and stop*/
+ mutex_unlock(&ulfs_lock);
+ return err;
+ }
+
+ LOG_MSG("node_init_root: Port for %s opened successfully.", dir);
+ LOG_MSG("\tPort: 0x%lX", (unsigned long)node->nn->port);
+
+ /*Stat the root node*/
+ err = io_stat(node->nn->port, &node->nn_stat);
+ if(err)
+ {
+ /*deallocate the port*/
+ PORT_DEALLOC(node->nn->port);
+
+ LOG_MSG("node_init_root: Could not stat the root node.");
+
+ /*unlock the mutex and exit*/
+ mutex_unlock(&ulfs_lock);
+ return err;
+ }
+
+ /*Set the path to the corresponding lnode to `dir`*/
+ node->nn->lnode->path = strdup(dir);
+ if(!node->nn->lnode->path)
+ {
+ /*deallocate the port*/
+ PORT_DEALLOC(node->nn->port);
+
+ /*unlock the mutex*/
+ mutex_unlock(&ulfs_lock);
+
+ LOG_MSG("node_init_root: Could not strdup the directory.");
+ return ENOMEM;
+ }
+
+ /*The current position in dir*/
+ char * p = dir + strlen(dir);
+
+ /*Go through the path from end to beginning*/
+ for(; p >= dir; --p)
+ {
+ /*If the current character is a '/'*/
+ if(*p == '/')
+ {
+ /*If p is not the first character*/
+ if(p > dir)
+ {
+ /*if this slash is escaped, skip it*/
+ if(*(p - 1) == '\\')
+ continue;
+ }
+
+ /*advance the pointer to the first character after the slash*/
+ ++p;
+
+ /*stop*/
+ break;
+ }
+ }
+
+ LOG_MSG("node_init_root: The name of root node is %s.", p);
+
+ /*Set the name of the lnode to the last element in the path to dir*/
+ node->nn->lnode->name = strdup(p);
+ /*If the name of the node could not be duplicated*/
+ if(!node->nn->lnode->name)
+ {
+ /*free the name of the path to the node and deallocate teh port*/
+ free(node->nn->lnode->path);
+ PORT_DEALLOC(node->nn->port);
+
+ /*unlock the mutex*/
+ mutex_unlock(&ulfs_lock);
+
+ LOG_MSG("node_init_root: Could not strdup the name of the root node.");
+ return ENOMEM;
+ }
+
+ /*Compute the length of the name of the root node*/
+ node->nn->lnode->name_len = strlen(p);
+
+ /*Release the lock for operations on the undelying filesystem*/
+ mutex_unlock(&ulfs_lock);
+
+ /*Return the result of operations*/
+ return err;
+ }/*node_init_root*/
+/*----------------------------------------------------------------------------*/
+/*Frees a list of dirents*/
+void
+node_entries_free
+ (
+ node_dirent_t * dirents /*free this*/
+ )
+ {
+ /*The current and the next elements*/
+ node_dirent_t * dirent, * dirent_next;
+
+ /*Go through all elements of the list*/
+ for(dirent = dirents; dirent; dirent = dirent_next)
+ {
+ /*store the next element*/
+ dirent_next = dirent->next;
+
+ /*free the dirent stored in the current element of the list*/
+ free(dirent->dirent);
+
+ /*free the current element*/
+ free(dirent);
+ }
+ }/*node_entries_free*/
+/*----------------------------------------------------------------------------*/
+/*Reads the directory entries from `node`, which must be locked*/
+error_t
+node_entries_get
+ (
+ node_t * node,
+ node_dirent_t ** dirents /*store the result here*/
+ )
+ {
+ error_t err = 0;
+
+ /*The list of dirents*/
+ struct dirent ** dirent_list, **dirent;
+
+ /*The head of the list of dirents*/
+ node_dirent_t * node_dirent_list = NULL;
+
+ /*The size of the array of pointers to dirent*/
+ size_t dirent_data_size;
+
+ /*The array of dirents*/
+ char * dirent_data;
+
+ /*Obtain the directory entries for the given node*/
+ err = dir_entries_get
+ (node->nn->port, &dirent_data, &dirent_data_size, &dirent_list);
+ if(err)
+ {
+ return err;
+ }
+
+ /*The new entry in the list*/
+ node_dirent_t * node_dirent_new;
+
+ /*The new dirent*/
+ struct dirent * dirent_new;
+
+ /*LOG_MSG("node_entries_get: Getting entries for %p", node);*/
+
+ /*The name of the current dirent*/
+ char * name;
+
+ /*The length of the current name*/
+ size_t name_len;
+
+ /*The size of the current dirent*/
+ size_t size;
+
+ /*Go through all elements of the list of pointers to dirent*/
+ for(dirent = dirent_list; *dirent; ++dirent)
+ {
+ /*obtain the name of the current dirent*/
+ name = &((*dirent)->d_name[0]);
+
+ /*If the current dirent is either '.' or '..', skip it*/
+ if((strcmp(name, ".") == 0) || (strcmp(name, "..") == 0))
+ continue;
+
+ /*obtain the length of the current name*/
+ name_len = strlen(name);
+
+ /*obtain the length of the current dirent*/
+ size = DIRENT_LEN(name_len);
+
+ /*create a new list element*/
+ node_dirent_new = malloc(sizeof(node_dirent_t));
+ if(!node_dirent_new)
+ {
+ err = ENOMEM;
+ break;
+ }
+
+ /*create a new dirent*/
+ dirent_new = malloc(size);
+ if(!dirent_new)
+ {
+ free(node_dirent_new);
+ err = ENOMEM;
+ break;
+ }
+
+ /*fill the dirent with information*/
+ dirent_new->d_ino = (*dirent)->d_ino;
+ dirent_new->d_type = (*dirent)->d_type;
+ dirent_new->d_reclen = size;
+ strcpy((char *)dirent_new + DIRENT_NAME_OFFS, name);
+
+ /*add the dirent to the list*/
+ node_dirent_new->dirent = dirent_new;
+ node_dirent_new->next = node_dirent_list;
+ node_dirent_list = node_dirent_new;
+ }
+
+ /*If something went wrong in the loop*/
+ if(err)
+ /*free the list of dirents*/
+ node_entries_free(node_dirent_list);
+ else
+ /*store the list of dirents in the second parameter*/
+ *dirents = node_dirent_list;
+
+ /*Free the list of pointers to dirent*/
+ free(dirent_list);
+
+ /*Free the results of listing the dirents*/
+ munmap(dirent_data, dirent_data_size);
+
+ /*Return the result of operations*/
+ return err;
+ }/*node_entries_get*/
+/*----------------------------------------------------------------------------*/
+/*Makes sure that all ports to the underlying filesystem of `node` are up to
+ date*/
+error_t
+node_update
+ (
+ node_t * node
+ )
+ {
+ error_t err = 0;
+
+ /*The full path to this node*/
+ char * path;
+
+ /*Stat information for `node`*/
+ io_statbuf_t stat;
+
+ /*The port to the file corresponding to `node`*/
+ file_t port;
+
+ /*If the specified node is the root node or if it must not be updated*/
+ if(NODE_IS_ROOT(node) || (node->nn->flags & FLAG_NODE_ULFS_FIXED))
+ /*do nothing*/
+ return err; /*return 0; actually*/
+
+ /*Gain exclusive access to the root node of the filesystem*/
+ mutex_lock(&netfs_root_node->lock);
+
+ /*Construct the full path to `node`*/
+ err = lnode_path_construct(node->nn->lnode, &path);
+ if(err)
+ {
+ mutex_unlock(&netfs_root_node->lock);
+ return err;
+ }
+
+ /*Deallocate `node`'s port to the underlying filesystem*/
+ if(node->nn->port)
+ PORT_DEALLOC(node->nn->port);
+
+ /*Try to lookup the file for `node` in its untranslated version*/
+ err = file_lookup
+ (
+ netfs_root_node->nn->port, path, O_READ | O_NOTRANS, O_NOTRANS,
+ 0, &port, &stat
+ );
+ if(err)
+ {
+ node->nn->port = MACH_PORT_NULL;
+ err = 0; /*failure (?)*/
+ return err;
+ }
+
+ /*If the node looked up is actually the root node of the proxy filesystem*/
+ if
+ (
+ (stat.st_ino == underlying_node_stat.st_ino)
+ && (stat.st_fsid == underlying_node_stat.st_fsid)
+ )
+ /*set `err` accordingly*/
+ err = ELOOP;
+ else
+ {
+ /*deallocate the obtained port*/
+ PORT_DEALLOC(port);
+
+ /*obtain the translated version of the required node*/
+ err = file_lookup
+ (netfs_root_node->nn->port, path, O_READ, 0, 0, &port, &stat);
+ }
+
+ /*If there have been errors*/
+ if(err)
+ /*reset the port*/
+ port = MACH_PORT_NULL;
+
+ /*Store the port in the node*/
+ node->nn->port = port;
+
+ /*Remove the flag about the invalidity of the current node and set the
+ flag that the node is up-to-date*/
+ node->nn->flags &= ~FLAG_NODE_INVALIDATE;
+ node->nn->flags |= FLAG_NODE_ULFS_UPTODATE;
+
+ /*Release the lock on the root node of proxy filesystem*/
+ mutex_unlock(&netfs_root_node->lock);
+
+ /*Return the result of operations*/
+ return err;
+ }/*node_update*/
+/*----------------------------------------------------------------------------*/
+/*Computes the size of the given directory*/
+error_t
+node_get_size
+ (
+ node_t * dir,
+ OFFSET_T * off
+ )
+ {
+ error_t err = 0;
+
+ /*The final size*/
+ size_t size = 0;
+
+ /*The number of directory entries*/
+ /*int count = 0;*/
+
+ /*The the node in the directory entries list from which we start counting*/
+ /*node_dirent_t * dirent_start = NULL;*/
+
+ /*The currently analyzed dirent*/
+ node_dirent_t * dirent_current = NULL;
+
+ /*The pointer to the beginning of the list of dirents*/
+ node_dirent_t * dirent_list = NULL;
+
+ /*The first entry we have to analyze*/
+ /*int first_entry = 2;*/
+
+ /*Takes into consideration the name of the current dirent*/
+ void
+ bump_size
+ (
+ const char * name
+ )
+ {
+ /*Increment the current size by the size of the current dirent*/
+ size += DIRENT_LEN(strlen(name));
+
+ /*Count the current dirent*/
+ /*++count;*/
+ }/*bump_size*/
+
+ /*Obtain the list of entries in the current directory*/
+ err = node_entries_get(dir, &dirent_list);
+ if(err)
+ return err;
+
+ /*Obtain the pointer to the dirent which has the number first_entry*/
+ /*Actually, the first element of the list*/
+ /*This code is included in unionfs, but it's completely useless here*/
+ /*for
+ (
+ dirent_start = dirent_list, count = 2;
+ dirent_start && count < first_entry;
+ dirent_start = dirent_start->next, ++count
+ );*/
+
+ /*Reset the count*/
+ /*count = 0;*/
+
+ /*Make space for '.' and '..' entries*/
+ /*This code is included in unionfs, but it's completely useless here*/
+ /*if(first_entry == 0)
+ bump_size(".");
+ if(first_entry <= 1)
+ bump_size("..");*/
+
+ /*See how much space is required for the node*/
+ for
+ (
+ dirent_current = dirent_list/*dirent_start*/; dirent_current;
+ dirent_current = dirent_current->next
+ )
+ bump_size(dirent_current->dirent->d_name);
+
+ /*Free the list of dirents*/
+ node_entries_free(dirent_list);
+
+ /*Return the size*/
+ *off = size;
+ return 0;
+ }/*node_get_size*/
+/*----------------------------------------------------------------------------*/
+/*Remove the file called `name` under `dir`*/
+error_t
+node_unlink_file
+ (
+ node_t * dir,
+ char * name
+ )
+ {
+ error_t err = 0;
+
+ /*The port to the file which will be unlinked*/
+ mach_port_t p;
+
+ /*Stat information about the file which will be unlinked*/
+ io_statbuf_t stat;
+
+ /*If port corresponding to `dir` is invalid*/
+ if(dir->nn->port == MACH_PORT_NULL)
+ /*stop with an error*/
+ return ENOENT; /*FIXME: Is the return value indeed meaningful here?*/
+
+ /*Attempt to lookup the specified file*/
+ err = file_lookup(dir->nn->port, name, O_NOTRANS, O_NOTRANS, 0, &p, &stat);
+ if(err)
+ return err;
+
+ /*Deallocate the obtained port*/
+ PORT_DEALLOC(p);
+
+ /*Unlink file `name` under `dir`*/
+ err = dir_unlink(dir->nn->port, name);
+ if(err)
+ return err;
+
+ return err;
+ }/*node_unlink_file*/
+/*----------------------------------------------------------------------------*/
diff --git a/node.h b/node.h
new file mode 100644
index 000000000..584aa9f3d
--- /dev/null
+++ b/node.h
@@ -0,0 +1,168 @@
+/*----------------------------------------------------------------------------*/
+/*node.h*/
+/*----------------------------------------------------------------------------*/
+/*Node management. Also see lnode.h*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+#ifndef __NODE_H__
+#define __NODE_H__
+
+/*----------------------------------------------------------------------------*/
+#include <error.h>
+#include <sys/stat.h>
+#include <hurd/netfs.h>
+/*----------------------------------------------------------------------------*/
+#include "lnode.h"
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Macros--------------------------------------------------------------*/
+/*Checks whether the give node is the root of the proxy filesystem*/
+#define NODE_IS_ROOT(n) (((n)->nn->lnode->dir) ? (0) : (1))
+/*----------------------------------------------------------------------------*/
+/*Node flags*/
+#define FLAG_NODE_ULFS_FIXED 0x00000001 /*this node should not be updated*/
+#define FLAG_NODE_INVALIDATE 0x00000002 /*this node must be updated*/
+#define FLAG_NODE_ULFS_UPTODATE 0x00000004 /*this node has just been updated*/
+/*----------------------------------------------------------------------------*/
+/*The type of offset corresponding to the current platform*/
+#ifdef __USE_FILE_OFFSET64
+# define OFFSET_T __off64_t
+#else
+# 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
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*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)*/
+ int flags;
+
+ /*a port to the underlying filesystem*/
+ file_t port;
+
+ /*the neighbouring entries in the cache*/
+ node_t * ncache_prev, * ncache_next;
+ };/*struct netnode*/
+/*----------------------------------------------------------------------------*/
+typedef struct netnode netnode_t;
+/*----------------------------------------------------------------------------*/
+/*A list element containing directory entry*/
+struct node_dirent
+ {
+ /*the directory entry*/
+ struct dirent * dirent;
+
+ /*the next element*/
+ struct node_dirent * next;
+ };/*struct node_dirent*/
+/*----------------------------------------------------------------------------*/
+typedef struct node_dirent node_dirent_t;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Global Variables----------------------------------------------------*/
+/*The lock protecting the underlying filesystem*/
+extern struct mutex ulfs_lock;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Derives a new node from `lnode` and adds a reference to `lnode`*/
+error_t
+node_create
+ (
+ 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
+node_destroy
+ (
+ node_t * np
+ );
+/*----------------------------------------------------------------------------*/
+/*Creates the root node and the corresponding lnode*/
+error_t
+node_create_root
+ (
+ node_t ** root_node /*store the result here*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Initializes the port to the underlying filesystem for the root node*/
+error_t
+node_init_root
+ (
+ node_t * node /*the root node*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Frees a list of dirents*/
+void
+node_entries_free
+ (
+ node_dirent_t * dirents /*free this*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Reads the directory entries from `node`, which must be locked*/
+error_t
+node_entries_get
+ (
+ node_t * node,
+ node_dirent_t ** dirents /*store the result here*/
+ );
+/*----------------------------------------------------------------------------*/
+/*Makes sure that all ports to the underlying filesystem of `node` are up to
+ date*/
+error_t
+node_update
+ (
+ node_t * node
+ );
+/*----------------------------------------------------------------------------*/
+/*Computes the size of the given directory*/
+error_t
+node_get_size
+ (
+ node_t * dir,
+ OFFSET_T * off
+ );
+/*----------------------------------------------------------------------------*/
+/*Remove the file called `name` under `dir`*/
+error_t
+node_unlink_file
+ (
+ node_t * dir,
+ char * name
+ );
+/*----------------------------------------------------------------------------*/
+#endif /*__NODE_H__*/
diff --git a/nsmux.c b/nsmux.c
new file mode 100644
index 000000000..8f068f2e1
--- /dev/null
+++ b/nsmux.c
@@ -0,0 +1,1122 @@
+/*----------------------------------------------------------------------------*/
+/*nsmux.c*/
+/*----------------------------------------------------------------------------*/
+/*The filesystem proxy for namespace-based translator selection.*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+#define _GNU_SOURCE 1
+/*----------------------------------------------------------------------------*/
+#include "nsmux.h"
+/*----------------------------------------------------------------------------*/
+#include <error.h>
+#include <argp.h>
+#include <argz.h>
+#include <hurd/netfs.h>
+#include <fcntl.h>
+/*----------------------------------------------------------------------------*/
+#include "debug.h"
+#include "options.h"
+#include "ncache.h"
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Macros--------------------------------------------------------------*/
+/*The state modes use in open*/
+#define OPENONLY_STATE_MODES (O_CREAT | O_EXCL | O_NOLINK | O_NOTRANS \
+ | O_NONBLOCK)
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Global Variables----------------------------------------------------*/
+/*The name of the server*/
+char * netfs_server_name = "nsmux";
+/*----------------------------------------------------------------------------*/
+/*The version of the server*/
+char * netfs_server_version = "0.0";
+/*----------------------------------------------------------------------------*/
+/*The maximal length of a chain of symbolic links*/
+int netfs_maxsymlinks = 12;
+/*----------------------------------------------------------------------------*/
+/*A port to the underlying node*/
+mach_port_t underlying_node;
+/*----------------------------------------------------------------------------*/
+/*Status information for the underlying node*/
+io_statbuf_t underlying_node_stat;
+/*----------------------------------------------------------------------------*/
+/*Mapped time used for updating node information*/
+volatile struct mapped_time_value * maptime;
+/*----------------------------------------------------------------------------*/
+/*The filesystem ID*/
+pid_t fsid;
+/*----------------------------------------------------------------------------*/
+/*The file to print debug messages to*/
+FILE * nsmux_dbg;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Attempts to create a file named `name` in `dir` for `user` with mode `mode`*/
+error_t
+netfs_attempt_create_file
+ (
+ struct iouser * user,
+ struct node * dir,
+ char * name,
+ mode_t mode,
+ struct node ** node
+ )
+ {
+ LOG_MSG("netfs_attempt_create_file");
+
+ /*Unlock `dir` and say that we can do nothing else here*/
+ mutex_unlock(&dir->lock);
+ return EOPNOTSUPP;
+ }/*netfs_attempt_create_file*/
+/*----------------------------------------------------------------------------*/
+/*Return an error if the process of opening a file should not be allowed
+ to complete because of insufficient permissions*/
+error_t
+netfs_check_open_permissions
+ (
+ struct iouser * user,
+ struct node * np,
+ int flags,
+ int newnode
+ )
+ {
+ 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;
+ }/*netfs_check_open_permissions*/
+/*----------------------------------------------------------------------------*/
+/*Attempts an utimes call for the user `cred` on node `node`*/
+error_t
+netfs_attempt_utimes
+ (
+ struct iouser * cred,
+ struct node * node,
+ struct timespec * atime,
+ struct timespec * mtime
+ )
+ {
+ LOG_MSG("netfs_attempt_utimes");
+
+ error_t err = 0;
+
+ /*See what information is to be updated*/
+ int flags = TOUCH_CTIME;
+
+ /*Check if the user is indeed the owner of the node*/
+ err = fshelp_isowner(&node->nn_stat, cred);
+
+ /*If the user is allowed to do utimes*/
+ if(!err)
+ {
+ /*If atime is to be updated*/
+ if(atime)
+ /*update the atime*/
+ node->nn_stat.st_atim = *atime;
+ else
+ /*the current time will be set as the atime*/
+ flags |= TOUCH_ATIME;
+
+ /*If mtime is to be updated*/
+ if(mtime)
+ /*update the mtime*/
+ node->nn_stat.st_mtim = *mtime;
+ else
+ /*the current time will be set as mtime*/
+ flags |= TOUCH_MTIME;
+
+ /*touch the file*/
+ fshelp_touch(&node->nn_stat, flags, maptime);
+ }
+
+ /*Return the result of operations*/
+ return err;
+ }/*netfs_attempt_utimes*/
+/*----------------------------------------------------------------------------*/
+/*Returns the valid access types for file `node` and user `cred`*/
+error_t
+netfs_report_access
+ (
+ struct iouser * cred,
+ struct node * np,
+ int * types
+ )
+ {
+ LOG_MSG("netfs_report_access");
+
+ /*No access at first*/
+ *types = 0;
+
+ /*Check the access and set the required bits*/
+ if(fshelp_access(&np->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if(fshelp_access(&np->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if(fshelp_access(&np->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+
+ /*Everything OK*/
+ return 0;
+ }/*netfs_report_access*/
+/*----------------------------------------------------------------------------*/
+/*Validates the stat data for the node*/
+error_t
+netfs_validate_stat
+ (
+ struct node * np,
+ struct iouser * cred
+ )
+ {
+ LOG_MSG("netfs_validate_stat: '%s'", np->nn->lnode->name);
+
+ error_t err = 0;
+
+ /*If we are not at the root*/
+ if(np != netfs_root_node)
+ {
+ /*If the node is not surely up-to-date*/
+ if(!(np->nn->flags & FLAG_NODE_ULFS_UPTODATE))
+ {
+ /*update it*/
+ err = node_update(np);
+ }
+
+ /*If no errors have yet occurred*/
+ if(!err)
+ {
+ /*If the port to the file corresponding to `np` is valid*/
+ if(np->nn->port != MACH_PORT_NULL)
+ {
+ /*We have a directory here (normally, only they maintain an open port).
+ Generally, our only concern is to maintain an open port in this case*/
+
+ /*attempt to stat this file*/
+ err = io_stat(np->nn->port, &np->nn_stat);
+
+ /*If stat information has been successfully obtained for the file*/
+ if(!err)
+ /*duplicate the st_mode field of stat structure*/
+ np->nn_translated = np->nn_stat.st_mode;
+ }
+ else
+ {
+ /*We, most probably, have something which is not a directory. Therefore
+ we will open the port and close it after the stat, so that additional
+ resources are not consumed.*/
+
+ /*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, 0, 0);
+
+ /*put `dnp` back, since we don't need it any more*/
+ netfs_nput(dnp);
+
+ if(!p)
+ return EBADF;
+
+ /*try to stat the node*/
+ err = io_stat(p, &np->nn_stat);
+
+ /*deallocate the port*/
+ PORT_DEALLOC(p);
+ }
+ }
+ }
+ /*If we are at the root*/
+ else
+ /*put the size of the node into the stat structure belonging to `np`*/
+ node_get_size(np, (OFFSET_T *)&np->nn_stat.st_size);
+
+ /*Return the result of operations*/
+ return err;
+ }/*netfs_validate_stat*/
+/*----------------------------------------------------------------------------*/
+/*Syncs `node` completely to disk*/
+error_t
+netfs_attempt_sync
+ (
+ struct iouser * cred,
+ struct node * node,
+ int wait
+ )
+ {
+ LOG_MSG("netfs_attempt_sync");
+
+ /*Operation is not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_sync*/
+/*----------------------------------------------------------------------------*/
+/*Fetches a directory*/
+error_t
+netfs_get_dirents
+ (
+ struct iouser * cred,
+ struct node * dir,
+ int first_entry,
+ int num_entries,
+ char ** data,
+ mach_msg_type_number_t * data_len,
+ vm_size_t max_data_len,
+ int * data_entries
+ )
+ {
+ LOG_MSG("netfs_get_dirents: '%s'", dir->nn->lnode->name);
+
+ error_t err;
+
+ /*Two pointers required for processing the list of dirents*/
+ node_dirent_t * dirent_start, * dirent_current;
+
+ /*The pointer to the beginning of the list of dirents*/
+ node_dirent_t * dirent_list = NULL;
+
+ /*The size of the current dirent*/
+ size_t size = 0;
+
+ /*The number of dirents added*/
+ int count = 0;
+
+ /*The dereferenced value of parameter `data`*/
+ char * data_p;
+
+ /*Takes into account the size of the given dirent*/
+ int
+ bump_size
+ (
+ const char * name
+ )
+ {
+ /*If the required number of entries has not been listed yet*/
+ if((num_entries == -1) || (count < num_entries))
+ {
+ /*take the current size and take into account the length of the name*/
+ size_t new_size = size + DIRENT_LEN(strlen(name));
+
+ /*If there is a limit for the received size and it has been exceeded*/
+ if((max_data_len > 0) && (new_size > max_data_len))
+ /*a new dirent cannot be added*/
+ return 0;
+
+ /*memorize the new size*/
+ size = new_size;
+
+ /*increase the number of dirents added*/
+ ++count;
+
+ /*everything is OK*/
+ return 1;
+ }
+ else
+ {
+ /*dirents cannot be added*/
+ return 0;
+ }
+ }/*bump_size*/
+
+ /*Adds a dirent to the list of dirents*/
+ int
+ add_dirent
+ (
+ const char * name,
+ ino_t ino,
+ int type
+ )
+ {
+ /*If the required number of dirents has not been listed yet*/
+ if((num_entries == -1) || (count < num_entries))
+ {
+ /*create a new dirent*/
+ struct dirent hdr;
+
+ /*obtain the length of the name*/
+ size_t name_len = strlen(name);
+
+ /*compute the full size of the dirent*/
+ size_t sz = DIRENT_LEN(name_len);
+
+ /*If there is no room for this dirent*/
+ if(sz > size)
+ /*stop*/
+ return 0;
+ else
+ /*take into account the fact that a new dirent has just been added*/
+ size -= sz;
+
+ /*setup the dirent*/
+ hdr.d_ino = ino;
+ hdr.d_reclen = sz;
+ hdr.d_type = type;
+ hdr.d_namlen = name_len;
+
+ /*The following two lines of code reflect the old layout of
+ dirents in the memory. Now libnetfs expects the layout
+ identical to the layout provided by dir_readdir (see dir_entries_get)*/
+
+ /*copy the header of the dirent into the final block of dirents*/
+ memcpy(data_p, &hdr, DIRENT_NAME_OFFS);
+
+ /*copy the name of the dirent into the block of dirents*/
+ strcpy(data_p + DIRENT_NAME_OFFS, name);
+
+ /*This line is commented for the same reason as the two specifically
+ commented lines above.*/
+ /*move the current pointer in the block of dirents*/
+ data_p += sz;
+
+ /*count the new dirent*/
+ ++count;
+
+ /*everything was OK, so say it*/
+ return 1;
+ }
+ else
+ /*no [new] dirents allowed*/
+ return 0;
+ }/*add_dirent*/
+
+ /*List the dirents for node `dir`*/
+ err = node_entries_get(dir, &dirent_list);
+
+ /*If listing was successful*/
+ if(!err)
+ {
+ /*find the entry whose number is `first_entry`*/
+ for
+ (
+ dirent_start = dirent_list, count = 2;
+ dirent_start && (count < first_entry);
+ dirent_start = dirent_start->next, ++count
+ );
+
+ /*reset number of dirents added so far*/
+ count = 0;
+
+ /*make space for entries '.' and '..', if required*/
+ if(first_entry == 0)
+ bump_size(".");
+ if(first_entry <= 1)
+ bump_size("..");
+
+ /*Go through all dirents*/
+ for
+ (
+ dirent_current = dirent_start;
+ dirent_current;
+ dirent_current = dirent_current->next
+ )
+ /*If another dirent cannot be added succesfully*/
+ if(bump_size(dirent_current->dirent->d_name) == 0)
+ /*stop here*/
+ break;
+
+ /*allocate the required space for dirents*/
+ *data = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0);
+
+ /*check if any error occurred*/
+ err = ((void *)*data == MAP_FAILED) ? (errno) : (0);
+ }
+
+ /*If no errors have occurred so far*/
+ if(!err)
+ {
+ /*obtain the pointer to the beginning of the block of dirents*/
+ data_p = *data;
+
+ /*fill the parameters with useful values*/
+ *data_len = size;
+ *data_entries = count;
+
+ /*reset the number of dirents added*/
+ count = 0;
+
+ /*add entries '.' and '..', if required*/
+ if(first_entry == 0)
+ add_dirent(".", 2, DT_DIR);
+ if(first_entry <= 1)
+ add_dirent("..", 2, DT_DIR);
+
+ /*Follow the list of dirents beginning with dirents_start*/
+ for
+ (
+ dirent_current = dirent_start; dirent_current;
+ dirent_current = dirent_current->next
+ )
+ /*If the addition of the current dirent fails*/
+ if
+ (
+ add_dirent
+ (dirent_current->dirent->d_name, dirent_current->dirent->d_fileno,
+ dirent_current->dirent->d_type) == 0
+ )
+ /*stop adding dirents*/
+ break;
+ }
+
+ /*If the list of dirents has been allocated, free it*/
+ if(dirent_list)
+ node_entries_free(dirent_list);
+
+ /*The directory has been read right now, modify the access time*/
+ fshelp_touch(&dir->nn_stat, TOUCH_ATIME, maptime);
+
+ /*Return the result of listing the dirents*/
+ return err;
+ }/*netfs_get_dirents*/
+/*----------------------------------------------------------------------------*/
+/*Looks up `name` under `dir` for `user`*/
+error_t
+netfs_attempt_lookup
+ (
+ struct iouser * user,
+ struct node * dir,
+ char * name,
+ struct node ** node
+ )
+ {
+ LOG_MSG("netfs_attempt_lookup: '%s'", name);
+
+ error_t err = 0;
+
+ /*If we are asked to fetch the current directory*/
+ if(strcmp(name, ".") == 0)
+ {
+ /*add a reference to `dir` and put it into `node`*/
+ netfs_nref(dir);
+ *node = dir;
+
+ /*everything is OK*/
+ return 0;
+ }
+ /*If we are asked to fetch the parent directory*/
+ else if(strcmp(name, "..") == 0)
+ {
+ /*If the supplied node is not root*/
+ if(dir->nn->lnode->dir)
+ {
+ /*The node corresponding to the parent directory must exist here*/
+ assert(dir->nn->lnode->dir->node);
+
+ /*put the parent node of `dir` into the result*/
+ err = ncache_node_lookup(dir->nn->lnode->dir, node);
+ }
+ /*The supplied node is root*/
+ else
+ {
+ /*this node is not included into our filesystem*/
+ err = ENOENT;
+ *node = NULL;
+ }
+
+ /*unlock the directory*/
+ mutex_unlock(&dir->lock);
+
+ /*stop here*/
+ return err;
+ }
+
+ /*Try to lookup the given file in the underlying directory*/
+ mach_port_t p = file_name_lookup_under(dir->nn->port, name, 0, 0);
+
+ /*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;
+ err = io_stat(p, &stat);
+
+ /*Deallocate the obtained port*/
+ PORT_DEALLOC(p);
+
+ /*If this file is not a directory*/
+ if(err || !S_ISDIR(stat.st_mode))
+ {
+ /*do not set the port*/
+ p = MACH_PORT_NULL;
+ }
+ 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?*/
+ }
+ }
+
+ /*The lnode corresponding to the entry we are supposed to fetch*/
+ lnode_t * lnode;
+
+ /*Finalizes the execution of this function*/
+ void
+ finalize(void)
+ {
+ /*If some errors have occurred*/
+ if(err)
+ {
+ /*the user should receive nothing*/
+ *node = NULL;
+
+ /*If there is some port, free it*/
+ if(p != MACH_PORT_NULL)
+ PORT_DEALLOC(p);
+ }
+ /*If there is a node to return*/
+ if(*node)
+ {
+ /*unlock the node*/
+ mutex_unlock(&(*node)->lock);
+
+ /*add the node to the cache*/
+ ncache_node_add(*node);
+ }
+
+ /*Unlock the mutexes in `dir`*/
+ mutex_unlock(&dir->nn->lnode->lock);
+ mutex_unlock(&dir->lock);
+ }/*finalize*/
+
+ /*Try to find an lnode called `name` under the lnode corresponding to `dir`*/
+ err = lnode_get(dir->nn->lnode, name, &lnode);
+
+ /*If such an entry does not exist*/
+ if(err == ENOENT)
+ {
+ /*create a new lnode with the supplied name*/
+ err = lnode_create(name, &lnode);
+ if(err)
+ {
+ finalize();
+ return err;
+ }
+
+ /*install the new lnode into the directory*/
+ lnode_install(dir->nn->lnode, lnode);
+ }
+
+ /*Obtain the node corresponding to this lnode*/
+ err = ncache_node_lookup(lnode, node);
+
+ /*Remove an extra reference from the lnode*/
+ lnode_ref_remove(lnode);
+
+ /*If the lookup in the cache failed*/
+ if(err)
+ {
+ /*stop*/
+ finalize();
+ return err;
+ }
+
+ /*Store the port in the node*/
+ (*node)->nn->port = p;
+
+ /*Construct the full path to the node*/
+ err = lnode_path_construct(lnode, NULL);
+ if(err)
+ {
+ finalize();
+ return err;
+ }
+
+ /*Now the node is up-to-date*/
+ (*node)->nn->flags = FLAG_NODE_ULFS_UPTODATE;
+
+ /*Return the result of performing the operations*/
+ finalize();
+ return err;
+ }/*netfs_attempt_lookup*/
+/*----------------------------------------------------------------------------*/
+/*Deletes `name` in `dir` for `user`*/
+error_t
+netfs_attempt_unlink
+ (
+ struct iouser * user,
+ struct node * dir,
+ char * name
+ )
+ {
+ LOG_MSG("netfs_attempt_unlink");
+
+ return 0;
+ }/*netfs_attempt_unlink*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to rename `fromdir`/`fromname` to `todir`/`toname`*/
+error_t
+netfs_attempt_rename
+ (
+ struct iouser * user,
+ struct node * fromdir,
+ char * fromname,
+ struct node * todir,
+ char * toname,
+ int excl
+ )
+ {
+ LOG_MSG("netfs_attempt_rename");
+
+ /*Operation not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_rename*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to create a new directory*/
+error_t
+netfs_attempt_mkdir
+ (
+ struct iouser * user,
+ struct node * dir,
+ char * name,
+ mode_t mode
+ )
+ {
+ LOG_MSG("netfs_attempt_mkdir");
+
+ return 0;
+ }/*netfs_attempt_mkdir*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to remove directory `name` in `dir` for `user`*/
+error_t
+netfs_attempt_rmdir
+ (
+ struct iouser * user,
+ struct node * dir,
+ char * name
+ )
+ {
+ LOG_MSG("netfs_attempt_rmdir");
+
+ return 0;
+ }/*netfs_attempt_rmdir*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to change the mode of `node` for user `cred` to `uid`:`gid`*/
+error_t
+netfs_attempt_chown
+ (
+ struct iouser * cred,
+ struct node * node,
+ uid_t uid,
+ uid_t gid
+ )
+ {
+ LOG_MSG("netfs_attempt_chown");
+
+ /*Operation is not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_chown*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to change the author of `node` to `author`*/
+error_t
+netfs_attempt_chauthor
+ (
+ struct iouser * cred,
+ struct node * node,
+ uid_t author
+ )
+ {
+ LOG_MSG("netfs_attempt_chauthor");
+
+ /*Operation is not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_chauthor*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to change the mode of `node` to `mode` for `cred`*/
+error_t
+netfs_attempt_chmod
+ (
+ struct iouser * user,
+ struct node * node,
+ mode_t mode
+ )
+ {
+ LOG_MSG("netfs_attempt_chmod");
+
+ /*Operation is not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_chmod*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to turn `node` into a symlink targetting `name`*/
+error_t
+netfs_attempt_mksymlink
+ (
+ struct iouser * cred,
+ struct node * node,
+ char * name
+ )
+ {
+ LOG_MSG("netfs_attempt_mksymlink");
+
+ /*Operation is not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_mksymlink*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to turn `node` into a device; type can be either S_IFBLK or S_IFCHR*/
+error_t
+netfs_attempt_mkdev
+ (
+ struct iouser * cred,
+ struct node * node,
+ mode_t type,
+ dev_t indexes
+ )
+ {
+ LOG_MSG("netfs_attempt_mkdev");
+
+ /*Operation is not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_mkdev*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to set the passive translator record for `file` passing `argz`*/
+error_t
+netfs_set_translator
+ (
+ struct iouser * cred,
+ struct node * node,
+ char * argz,
+ size_t arglen
+ )
+ {
+ LOG_MSG("netfs_set_translator");
+
+ /*Operation is not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_set_translator */
+/*----------------------------------------------------------------------------*/
+/*Attempts to call chflags for `node`*/
+error_t
+netfs_attempt_chflags
+ (
+ struct iouser * cred,
+ struct node * node,
+ int flags
+ )
+ {
+ LOG_MSG("netfs_attempt_chflags");
+
+ /*Operation is not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_chflags*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to set the size of file `node`*/
+error_t
+netfs_attempt_set_size
+ (
+ struct iouser * cred,
+ struct node * node,
+ loff_t size
+ )
+ {
+ LOG_MSG("netfs_attempt_set_size");
+
+ /*Operation is not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_set_size*/
+/*----------------------------------------------------------------------------*/
+/*Fetches the filesystem status information*/
+error_t
+netfs_attempt_statfs
+ (
+ struct iouser * cred,
+ struct node * node,
+ fsys_statfsbuf_t * st
+ )
+ {
+ LOG_MSG("netfs_attempt_statfs");
+
+ /*Operation is not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_statfs*/
+/*----------------------------------------------------------------------------*/
+/*Syncs the filesystem*/
+error_t
+netfs_attempt_syncfs
+ (
+ struct iouser * cred,
+ int wait
+ )
+ {
+ LOG_MSG("netfs_attempt_syncfs");
+
+ /*Everythin OK*/
+ return 0;
+ }/*netfs_attempt_syncfs*/
+/*----------------------------------------------------------------------------*/
+/*Creates a link in `dir` with `name` to `file`*/
+error_t
+netfs_attempt_link
+ (
+ struct iouser * user,
+ struct node * dir,
+ struct node * file,
+ char * name,
+ int excl
+ )
+ {
+ LOG_MSG("netfs_attempt_link");
+
+ /*Operation not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_link*/
+/*----------------------------------------------------------------------------*/
+/*Attempts to create an anonymous file related to `dir` with `mode`*/
+error_t
+netfs_attempt_mkfile
+ (
+ struct iouser * user,
+ struct node * dir,
+ mode_t mode,
+ struct node ** node
+ )
+ {
+ LOG_MSG("netfs_attempt_mkfile");
+
+ /*Unlock the directory*/
+ mutex_unlock(&dir->lock);
+
+ /*Operation not supported*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_mkfile*/
+/*----------------------------------------------------------------------------*/
+/*Reads the contents of symlink `node` into `buf`*/
+error_t
+netfs_attempt_readlink
+ (
+ struct iouser * user,
+ struct node * node,
+ char * buf
+ )
+ {
+ LOG_MSG("netfs_attempt_readlink");
+
+ /*Operation not supported (why?..)*/
+ return EOPNOTSUPP;
+ }/*netfs_attempt_readlink*/
+/*----------------------------------------------------------------------------*/
+/*Reads from file `node` up to `len` bytes from `offset` into `data`*/
+error_t
+netfs_attempt_read
+ (
+ struct iouser * cred,
+ struct node * np,
+ loff_t offset,
+ size_t * len,
+ void * data
+ )
+ {
+ LOG_MSG("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;
+ }
+
+ /*Read the required data from the file*/
+ err = io_read(np->nn->port, (char **)&data, len, offset, *len);
+
+ /*Return the result of reading*/
+ return err;
+ }/*netfs_attempt_read*/
+/*----------------------------------------------------------------------------*/
+/*Writes to file `node` up to `len` bytes from offset from `data`*/
+error_t
+netfs_attempt_write
+ (
+ struct iouser * cred,
+ struct node * node,
+ loff_t offset,
+ size_t * len,
+ void * data
+ )
+ {
+ LOG_MSG("netfs_attempt_write");
+
+ return 0;
+ }/*netfs_attempt_write*/
+/*----------------------------------------------------------------------------*/
+/*Frees all storage associated with the node*/
+void
+netfs_node_norefs
+ (
+ struct node * np
+ )
+ {
+ /*Destroy the node*/
+ node_destroy(np);
+ }/*netfs_node_norefs*/
+/*----------------------------------------------------------------------------*/
+/*Entry point*/
+int
+main
+ (
+ int argc,
+ char ** argv
+ )
+ {
+ /*Start logging*/
+ INIT_LOG();
+ LOG_MSG(">> Starting initialization...");
+
+ /*The port on which this translator will be set upon*/
+ mach_port_t bootstrap_port;
+
+ error_t err = 0;
+
+ /*Parse the command line arguments*/
+ argp_parse(&argp_startup, argc, argv, ARGP_IN_ORDER, 0, 0);
+ LOG_MSG("Command line arguments parsed.");
+
+ /*Try to create the root node*/
+ err = node_create_root(&netfs_root_node);
+ if(err)
+ error(EXIT_FAILURE, err, "Failed to create the root node");
+ LOG_MSG("Root node created.");
+
+ /*Obtain the bootstrap port*/
+ task_get_bootstrap_port(mach_task_self(), &bootstrap_port);
+
+ /*Initialize the translator*/
+ netfs_init();
+
+ /*Obtain a port to the underlying node*/
+ underlying_node = netfs_startup(bootstrap_port, O_READ);
+ LOG_MSG("netfs initialization complete.");
+
+ /*Initialize the root node*/
+ err = node_init_root(netfs_root_node);
+ if(err)
+ error(EXIT_FAILURE, err, "Failed to initialize the root node");
+ LOG_MSG("Root node initialized.");
+ LOG_MSG("\tRoot node address: 0x%lX", (unsigned long)netfs_root_node);
+
+ /*Map the time for updating node information*/
+ err = maptime_map(0, 0, &maptime);
+ if(err)
+ error(EXIT_FAILURE, err, "Failed to map the time");
+ LOG_MSG("Time mapped.");
+
+ /*Initialize the cache with the required number of nodes*/
+ ncache_init(ncache_size);
+ LOG_MSG("Cache initialized.");
+
+ /*Obtain stat information about the underlying node*/
+ err = io_stat(underlying_node, &underlying_node_stat);
+ if(err)
+ error(EXIT_FAILURE, err,
+ "Cannot obtain stat information about the underlying node");
+ LOG_MSG("Stat information for undelying node obtained.");
+
+ /*Obtain the ID of the current process*/
+ fsid = getpid();
+
+ /*Setup the stat information for the root node*/
+ netfs_root_node->nn_stat = underlying_node_stat;
+
+ netfs_root_node->nn_stat.st_ino = NSMUX_ROOT_INODE;
+ netfs_root_node->nn_stat.st_fsid = fsid;
+ netfs_root_node->nn_stat.st_mode = S_IFDIR | (underlying_node_stat.st_mode
+ & ~S_IFMT & ~S_ITRANS); /*we are providing a translated directory*/
+
+ netfs_root_node->nn_translated = netfs_root_node->nn_stat.st_mode;
+
+ /*If the underlying node is not a directory, enhance the permissions
+ of the root node of the proxy filesystem*/
+ if(!S_ISDIR(underlying_node_stat.st_mode))
+ {
+ /*can be read by owner*/
+ if(underlying_node_stat.st_mode & S_IRUSR)
+ /*allow execution by the owner*/
+ netfs_root_node->nn_stat.st_mode |= S_IXUSR;
+ /*can be read by group*/
+ if(underlying_node_stat.st_mode & S_IRGRP)
+ /*allow execution by the group*/
+ netfs_root_node->nn_stat.st_mode |= S_IXGRP;
+ /*can be read by others*/
+ if(underlying_node_stat.st_mode & S_IROTH)
+ /*allow execution by the others*/
+ netfs_root_node->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ /*Update the timestamps of the root node*/
+ fshelp_touch
+ (&netfs_root_node->nn_stat, TOUCH_ATIME | TOUCH_MTIME | TOUCH_CTIME,
+ maptime);
+
+ LOG_MSG(">> Initialization complete. Entering netfs server loop...");
+
+ /*Start serving clients*/
+ for(;;)
+ netfs_server_loop();
+ }/*main*/
+/*----------------------------------------------------------------------------*/
diff --git a/nsmux.h b/nsmux.h
new file mode 100644
index 000000000..ee469f2d5
--- /dev/null
+++ b/nsmux.h
@@ -0,0 +1,339 @@
+/*----------------------------------------------------------------------------*/
+/*nsmux.h*/
+/*----------------------------------------------------------------------------*/
+/*Definitions for filesystem proxy for namespace-based translator selection.*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+#ifndef __NSMUX_H__
+#define __NSMUX_H__
+/*----------------------------------------------------------------------------*/
+#include <stddef.h>
+#include <stdlib.h>
+#include <cthreads.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/time.h>
+#include <hurd/ihash.h>
+#include <hurd/iohelp.h>
+/*----------------------------------------------------------------------------*/
+#include "lib.h"
+#include "node.h"
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Macros--------------------------------------------------------------*/
+/*The inode number for the root node*/
+#define NSMUX_ROOT_INODE 1
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Global Variables----------------------------------------------------*/
+/*A mapped time value*/
+/*Required for a very fast access to time*/
+extern volatile struct mapped_time_value * maptime;
+/*----------------------------------------------------------------------------*/
+/*A port to the underlying node*/
+extern mach_port_t underlying_node;
+/*----------------------------------------------------------------------------*/
+/*The stat information about the underlying node*/
+extern io_statbuf_t underlying_node_stat;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Attempts to create a file named `name` in `dir` for `user` with mode `mode`*/
+error_t
+netfs_attempt_create_file
+ (
+ struct iouser * user,
+ struct node * dir,
+ char * name,
+ mode_t mode,
+ struct node ** node
+ );
+/*----------------------------------------------------------------------------*/
+/*Returns an error if the process of opening a file should not be allowed
+ to complete because of insufficient permissions*/
+error_t
+netfs_check_open_permissions
+ (
+ struct iouser * user,
+ struct node * np,
+ int flags,
+ int newnode
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts an utimes call for the user `cred` on node `node`*/
+error_t
+netfs_attempt_utimes
+ (
+ struct iouser * cred,
+ struct node * node,
+ struct timespec * atime,
+ struct timespec * mtime
+ );
+/*----------------------------------------------------------------------------*/
+/*Returns the valid access types for file `node` and user `cred`*/
+error_t
+netfs_report_access
+ (
+ struct iouser * cred,
+ struct node * np,
+ int * types
+ );
+/*----------------------------------------------------------------------------*/
+/*Validates the stat data for the node*/
+error_t
+netfs_validate_stat
+ (
+ struct node * np,
+ struct iouser * cred
+ );
+/*----------------------------------------------------------------------------*/
+/*Syncs `node` completely to disk*/
+error_t
+netfs_attempt_sync
+ (
+ struct iouser * cred,
+ struct node * node,
+ int wait
+ );
+/*----------------------------------------------------------------------------*/
+/*Fetches a directory*/
+error_t
+netfs_get_dirents
+ (
+ struct iouser * cred,
+ struct node * dir,
+ int first_entry,
+ int num_entries,
+ char ** data,
+ mach_msg_type_number_t * data_len,
+ vm_size_t max_data_len,
+ int * data_entries
+ );
+/*----------------------------------------------------------------------------*/
+/*Looks up `name` under `dir` for `user`*/
+error_t
+netfs_attempt_lookup
+ (
+ struct iouser * user,
+ struct node * dir,
+ char * name,
+ struct node ** node
+ );
+/*----------------------------------------------------------------------------*/
+/*Deletes `name` in `dir` for `user`*/
+error_t
+netfs_attempt_unlink
+ (
+ struct iouser * user,
+ struct node * dir,
+ char * name
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to rename `fromdir`/`fromname` to `todir`/`toname`*/
+error_t
+netfs_attempt_rename
+ (
+ struct iouser * user,
+ struct node * fromdir,
+ char * fromname,
+ struct node * todir,
+ char * toname,
+ int excl
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to create a new directory*/
+error_t
+netfs_attempt_mkdir
+ (
+ struct iouser * user,
+ struct node * dir,
+ char * name,
+ mode_t mode
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to remove directory `name` in `dir` for `user`*/
+error_t
+netfs_attempt_rmdir
+ (
+ struct iouser * user,
+ struct node * dir,
+ char * name
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to change the owner of `node` for user `cred` to `uid`:`gid`*/
+error_t
+netfs_attempt_chown
+ (
+ struct iouser * cred,
+ struct node * node,
+ uid_t uid,
+ uid_t gid
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to change the author of `node` to `author`*/
+error_t
+netfs_attempt_chauthor
+ (
+ struct iouser * cred,
+ struct node * node,
+ uid_t author
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to change the mode of `node` to `mode` for `cred`*/
+error_t
+netfs_attempt_chmod
+ (
+ struct iouser * user,
+ struct node * node,
+ mode_t mode
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to turn `node` into a symlink targetting `name`*/
+error_t
+netfs_attempt_mksymlink
+ (
+ struct iouser * cred,
+ struct node * node,
+ char * name
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to turn `node` into a device; type can be either S_IFBLK or S_IFCHR*/
+error_t
+netfs_attempt_mkdev
+ (
+ struct iouser * cred,
+ struct node * node,
+ mode_t type,
+ dev_t indexes
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to set the passive translator record for `file` passing `argz`*/
+error_t
+netfs_set_translator
+ (
+ struct iouser * cred,
+ struct node * node,
+ char * argz,
+ size_t arglen
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to call chflags for `node`*/
+error_t
+netfs_attempt_chflags
+ (
+ struct iouser * cred,
+ struct node * node,
+ int flags
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to set the size of file `node`*/
+error_t
+netfs_attempt_set_size
+ (
+ struct iouser * cred,
+ struct node * node,
+ loff_t size
+ );
+/*----------------------------------------------------------------------------*/
+/*Fetches the filesystem status information*/
+error_t
+netfs_attempt_statfs
+ (
+ struct iouser * cred,
+ struct node * node,
+ fsys_statfsbuf_t * st
+ );
+/*----------------------------------------------------------------------------*/
+/*Syncs the filesystem*/
+error_t
+netfs_attempt_syncfs
+ (
+ struct iouser * cred,
+ int wait
+ );
+/*----------------------------------------------------------------------------*/
+/*Creates a link in `dir` with `name` to `file`*/
+error_t
+netfs_attempt_link
+ (
+ struct iouser * user,
+ struct node * dir,
+ struct node * file,
+ char * name,
+ int excl
+ );
+/*----------------------------------------------------------------------------*/
+/*Attempts to create an anonymous file related to `dir` with `mode`*/
+error_t
+netfs_attempt_mkfile
+ (
+ struct iouser * user,
+ struct node * dir,
+ mode_t mode,
+ struct node ** node
+ );
+/*----------------------------------------------------------------------------*/
+/*Reads the contents of symlink `node` into `buf`*/
+error_t
+netfs_attempt_readlink
+ (
+ struct iouser * user,
+ struct node * node,
+ char * buf
+ );
+/*----------------------------------------------------------------------------*/
+/*Reads from file `node` up to `len` bytes from `offset` into `data`*/
+error_t
+netfs_attempt_read
+ (
+ struct iouser * cred,
+ struct node * np,
+ loff_t offset,
+ size_t * len,
+ void * data
+ );
+/*----------------------------------------------------------------------------*/
+/*Writes to file `node` up to `len` bytes from offset from `data`*/
+error_t
+netfs_attempt_write
+ (
+ struct iouser * cred,
+ struct node * node,
+ loff_t offset,
+ size_t * len,
+ void * data
+ );
+/*----------------------------------------------------------------------------*/
+/*Frees all storage associated with the node*/
+void
+netfs_node_norefs
+ (
+ struct node * np
+ );
+/*----------------------------------------------------------------------------*/
+#endif /*__NSMUX_H__*/
diff --git a/options.c b/options.c
new file mode 100644
index 000000000..c69cd2806
--- /dev/null
+++ b/options.c
@@ -0,0 +1,242 @@
+/*----------------------------------------------------------------------------*/
+/*options.h*/
+/*----------------------------------------------------------------------------*/
+/*Definitions for parsing the command line switches*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+#define _GNU_SOURCE 1
+/*----------------------------------------------------------------------------*/
+#include <argp.h>
+#include <error.h>
+/*----------------------------------------------------------------------------*/
+#include "debug.h"
+#include "options.h"
+#include "ncache.h"
+#include "node.h"
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Macros--------------------------------------------------------------*/
+/*Short documentation for argp*/
+#define ARGS_DOC "DIR"
+#define DOC "Provides namespace-based translator selection."\
+ " You can dynamically obtain the file 'file' translated by translator"\
+ " 'x' using the syntax: 'file,,x'."
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Forward Declarations------------------------------------------------*/
+/*Argp parser function for the common options*/
+static
+error_t
+argp_parse_common_options
+ (
+ int key,
+ char * arg,
+ struct argp_state * state
+ );
+/*----------------------------------------------------------------------------*/
+/*Argp parser function for the startup options*/
+static
+error_t
+argp_parse_startup_options
+ (
+ int key,
+ char * arg,
+ struct argp_state * state
+ );
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Global Variables----------------------------------------------------*/
+/*This variable is set to a non-zero value after the parsing of starup options
+ is finished*/
+/*Whenever the argument parser is later called to modify the
+ options of the root node will be initialized accordingly directly
+ by the parser*/
+static int parsing_startup_options_finished;
+/*----------------------------------------------------------------------------*/
+/*Argp options common to both the runtime and the startup parser*/
+static const struct argp_option argp_common_options[] =
+ {
+ {OPT_LONG_CACHE_SIZE, OPT_CACHE_SIZE, "SIZE", 0,
+ "The maximal number of nodes in the node cache"}
+ };
+/*----------------------------------------------------------------------------*/
+/*Argp options only meaningful for startup parsing*/
+static const struct argp_option argp_startup_options[] =
+ {
+ {0}
+ };
+/*----------------------------------------------------------------------------*/
+/*Argp parser for only the common options*/
+static const struct argp argp_parser_common_options =
+ {argp_common_options, argp_parse_common_options, 0, 0, 0};
+/*----------------------------------------------------------------------------*/
+/*Argp parser for only the startup options*/
+static const struct argp argp_parser_startup_options =
+ {argp_startup_options, argp_parse_startup_options, 0, 0, 0};
+/*----------------------------------------------------------------------------*/
+/*The list of children parsers for runtime arguments*/
+static const struct argp_child argp_children_runtime[] =
+ {
+ {&argp_parser_common_options},
+ {&netfs_std_runtime_argp},
+ {0}
+ };
+/*----------------------------------------------------------------------------*/
+/*The list of children parsers for startup arguments*/
+static const struct argp_child argp_children_startup[] =
+ {
+ {&argp_parser_startup_options},
+ {&argp_parser_common_options},
+ {&netfs_std_startup_argp},
+ {0}
+ };
+/*----------------------------------------------------------------------------*/
+/*The version of the server for argp*/
+const char * argp_program_version = "0.0";
+/*----------------------------------------------------------------------------*/
+/*The arpg parser for runtime arguments*/
+struct argp argp_runtime =
+ {0, 0, 0, 0, argp_children_runtime};
+/*----------------------------------------------------------------------------*/
+/*The argp parser for startup arguments*/
+struct argp argp_startup =
+ {0, 0, ARGS_DOC, DOC, argp_children_startup};
+/*----------------------------------------------------------------------------*/
+/*The directory to mirror*/
+char * dir = NULL;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Functions-----------------------------------------------------------*/
+/*Argp parser function for the common options*/
+static
+error_t
+argp_parse_common_options
+ (
+ int key,
+ char * arg,
+ struct argp_state * state
+ )
+ {
+ error_t err = 0;
+
+ /*Go through the possible options*/
+ switch(key)
+ {
+ case OPT_CACHE_SIZE:
+ {
+ /*store the new cache-size*/
+ ncache_size = strtol(arg, NULL, 10);
+
+ break;
+ }
+ case ARGP_KEY_ARG: /*the directory to mirror*/
+ {
+ /*try to duplicate the directory name*/
+ dir = strdup(arg);
+ if(!dir)
+ error(EXIT_FAILURE, ENOMEM, "argp_parse_common_options: "
+ "Could not strdup the directory");
+
+ /*fill all trailing spaces with 0*/
+ int i = strlen(dir) - 1;
+ /*for(i = strlen(dir) - 1; (i >= 0) && (dir[i] == ' '); dir[i--] = 0);*/
+ /*the original filename may contain spaces*/
+
+ /*If the last non blank symbol is a '/' and it's not the only one*/
+ if((dir[i] == '/') && (i != 0))
+ /*put 0 instead*/
+ dir[i] = 0;
+
+ LOG_MSG("argp_parse_common_options: Mirroring the directory %s.", dir);
+
+ break;
+ }
+ case ARGP_KEY_END:
+ {
+ /*If parsing of startup options has not finished*/
+ if(!parsing_startup_options_finished)
+ {
+ /*reset the cache*/
+ ncache_reset();
+
+ /*If the directory has not been specified*/
+ if(!dir)
+ {
+ /*assume the directory to be the home directory*/
+ ;
+
+ /*FIXME: The default directory is /var/tmp*/
+ dir = "/var/tmp";
+ }
+
+ /*set the flag that the startup options have already been parsed*/
+ parsing_startup_options_finished = 1;
+ }
+ else
+ {
+/*TODO: Take care of runtime calls modifying the property*/
+ }
+ }
+ /*If the option could not be recognized*/
+ default:
+ {
+ /*set the error code*/
+ err = ARGP_ERR_UNKNOWN;
+ }
+ }
+
+ /*Return the result*/
+ return err;
+ }/*argp_parse_common_options*/
+/*----------------------------------------------------------------------------*/
+/*Argp parser function for the startup options*/
+static
+error_t
+argp_parse_startup_options
+ (
+ int key,
+ char * arg,
+ struct argp_state * state
+ )
+ {
+ /*Do nothing in a beautiful way*/
+ error_t err = 0;
+
+ switch(key)
+ {
+ default:
+ {
+ err = ARGP_ERR_UNKNOWN;
+
+ break;
+ }
+ }
+
+ return err;
+ }/*argp_parse_startup_options*/
+/*----------------------------------------------------------------------------*/
diff --git a/options.h b/options.h
new file mode 100644
index 000000000..60f757204
--- /dev/null
+++ b/options.h
@@ -0,0 +1,55 @@
+/*----------------------------------------------------------------------------*/
+/*options.h*/
+/*----------------------------------------------------------------------------*/
+/*Declarations for parsing the command line switches*/
+/*----------------------------------------------------------------------------*/
+/*Based on the code of unionfs translator.*/
+/*----------------------------------------------------------------------------*/
+/*Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Sergiu Ivanov <unlimitedscolobb@gmail.com>.
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2 of the
+ License, or * (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
+ USA.*/
+/*----------------------------------------------------------------------------*/
+#ifndef __OPTIONS_H__
+#define __OPTIONS_H__
+
+/*----------------------------------------------------------------------------*/
+/*--------Macros--------------------------------------------------------------*/
+/*The possible short options*/
+#define OPT_CACHE_SIZE 'c'
+/*----------------------------------------------------------------------------*/
+/*The corresponding long options*/
+#define OPT_LONG_CACHE_SIZE "cache-size"
+/*----------------------------------------------------------------------------*/
+/*Makes a long option out of option name*/
+#define OPT_LONG(o) "--"o
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Global Variables----------------------------------------------------*/
+/*The argp parser for startup arguments*/
+extern struct argp argp_startup;
+/*----------------------------------------------------------------------------*/
+/*The argp parser for rutime arguments*/
+extern struct argp argp_runtime;
+/*----------------------------------------------------------------------------*/
+/*The number of nodes in cache (see ncache.{c,h})*/
+extern int ncache_size;
+/*----------------------------------------------------------------------------*/
+/*The directory to mirror*/
+extern char * dir;
+/*----------------------------------------------------------------------------*/
+#endif /*__OPTIONS_H__*/