summaryrefslogtreecommitdiff
path: root/netfs-sample
diff options
context:
space:
mode:
Diffstat (limited to 'netfs-sample')
-rw-r--r--netfs-sample/README13
-rwxr-xr-xnetfs-sample/build1
-rw-r--r--netfs-sample/debug.h60
-rw-r--r--netfs-sample/errors0
-rw-r--r--netfs-sample/filtered0
-rw-r--r--netfs-sample/filterfs.c1236
-rw-r--r--netfs-sample/filterfs.h453
-rw-r--r--netfs-sample/lib.c193
-rw-r--r--netfs-sample/lib.h82
-rw-r--r--netfs-sample/lnode.c305
-rw-r--r--netfs-sample/lnode.h143
-rw-r--r--netfs-sample/ncache.c221
-rw-r--r--netfs-sample/ncache.h100
-rw-r--r--netfs-sample/node.c803
-rw-r--r--netfs-sample/node.h168
-rw-r--r--netfs-sample/options.c255
-rw-r--r--netfs-sample/options.h64
17 files changed, 4097 insertions, 0 deletions
diff --git a/netfs-sample/README b/netfs-sample/README
new file mode 100644
index 00000000..112cce59
--- /dev/null
+++ b/netfs-sample/README
@@ -0,0 +1,13 @@
+FILTERFS TRANSLATOR
+
+WHAT'S THIS?
+filterfs is a GNU/Hurd translator that filters the contents of the directory it
+is set upon according to the result of execution of a given command.
+
+WHAT'S IT FOR?
+This translator is intended as a preparatory exercise for the namespace-based
+translator selection project (see http://www.bddebian.com/~wiki/community/gsoc/),
+so it does not really have a stand-alone importance :-)
+
+DEVELOPER
+Sergiu Ivanov <unlimitedscolobb@gmail.com>
diff --git a/netfs-sample/build b/netfs-sample/build
new file mode 100755
index 00000000..2555e2ca
--- /dev/null
+++ b/netfs-sample/build
@@ -0,0 +1 @@
+gcc -Wall -g -lnetfs -lfshelp -liohelp -lthreads -lports -lihash -lshouldbeinlibc -o filterfs filterfs.c node.c lnode.c ncache.c options.c lib.c 2>&1 | tee errors
diff --git a/netfs-sample/debug.h b/netfs-sample/debug.h
new file mode 100644
index 00000000..606d7e79
--- /dev/null
+++ b/netfs-sample/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/tmp/filterfs.dbg"
+/*----------------------------------------------------------------------------*/
+#ifdef DEBUG
+ /*Initializes the log*/
+# define INIT_LOG() filterfs_dbg = fopen(DEBUG_OUTPUT, "wt")
+ /*Closes the log*/
+# define CLOSE_LOG() fclose(filterfs_dbg)
+ /*Prints a debug message and flushes the debug output*/
+# define LOG_MSG(fmt, args...) {fprintf(filterfs_dbg, fmt"\n", ##args);\
+ fflush(filterfs_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 * filterfs_dbg;
+/*----------------------------------------------------------------------------*/
+
+#endif /*__DEBUG_H__*/
diff --git a/netfs-sample/errors b/netfs-sample/errors
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netfs-sample/errors
diff --git a/netfs-sample/filtered b/netfs-sample/filtered
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/netfs-sample/filtered
diff --git a/netfs-sample/filterfs.c b/netfs-sample/filterfs.c
new file mode 100644
index 00000000..d768e18b
--- /dev/null
+++ b/netfs-sample/filterfs.c
@@ -0,0 +1,1236 @@
+/*----------------------------------------------------------------------------*/
+/*filter.c*/
+/*----------------------------------------------------------------------------*/
+/*The filtering translator*/
+/*----------------------------------------------------------------------------*/
+/*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 "filterfs.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 = "filterfs";
+/*----------------------------------------------------------------------------*/
+/*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 * filterfs_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;
+ }
+
+ /*Checks whether the given name satisfied the required property*/
+ int
+ check_property
+ (
+ const char * name
+ )
+ {
+ /*If there is no property*/
+ if(!property)
+ /*no filtering will be applied, any name is OK*/
+ return 0;
+
+ /*The number of occurrences of PROPERTY_PARAM in the property*/
+ int param_entries_count = 0;
+
+ /*The pointer to the current occurrence of PROPERTY_PARAM*/
+ char * occurrence = strstr(property, PROPERTY_PARAM);
+
+ /*Count the number of occurrences*/
+ for(; occurrence;
+ occurrence = strstr(occurrence + 1, PROPERTY_PARAM),
+ ++param_entries_count);
+
+ /*Compute the length of the property param*/
+ size_t property_param_len = strlen(PROPERTY_PARAM);
+
+ /*The length of the property*/
+ size_t property_len = (property) ? (strlen(property)) : (0);
+
+ /*Everything OK at first*/
+ err = 0;
+
+ /*Compute the length of the full name once*/
+ size_t full_name_len = strlen(dir->nn->lnode->path) + 1 + strlen(name) + 1;
+
+ /*Try to allocate the required space*/
+ char * full_name = malloc(full_name_len);
+ if(!full_name)
+ {
+ err = ENOMEM;
+ return 0;
+ }
+
+ /*Initialize `full_name` as a valid string*/
+ full_name[0] = 0;
+
+ /*Construct the full name*/
+ strcpy(full_name, dir->nn->lnode->path);
+ strcat(full_name, "/");
+ strcat(full_name, name);
+
+ LOG_MSG("netfs_attempt_lookup: Applying filter to %s...", full_name);
+
+ /*Compute the space required for the final filtering command*/
+ size_t sz = property_len + (strlen(full_name) - property_param_len)
+ * param_entries_count;
+
+ /*Try to allocate the space for the command*/
+ char * cmd = malloc(sz);
+ if(!cmd)
+ {
+ free(full_name);
+ err = ENOMEM;
+ return 0;
+ }
+
+ /*Initialize `cmd` as a valid string*/
+ cmd[0] = 0;
+
+ /*The current occurence of PROPERTY_PARAM in property*/
+ char * p = strstr(property, PROPERTY_PARAM);
+
+ /*The pointer to the current position in the property*/
+ char * propp = property;
+
+ /*While the command has not been constructed*/
+ for(; p; p = strstr(propp, PROPERTY_PARAM))
+ {
+ /*add the new part of the property to the command*/
+ strncat(cmd, propp, p - propp);
+
+ /*add the filename to the command*/
+ strcat(cmd, full_name);
+
+ /*LOG_MSG("\tcmd = '%s'", cmd);*/
+
+ /*advance the pointer in the property*/
+ propp = p + property_param_len;
+
+ /*LOG_MSG("\tpropp points at '%s'", propp);*/
+ }
+
+ /*Copy the rest of the property to the command*/
+ strcat(cmd, propp);
+
+ /*LOG_MSG("node_entries_get: The filtering command: '%s'.", cmd);*/
+
+ /*Execute the command*/
+ int xcode = WEXITSTATUS(system(cmd));
+
+ /*Return the exit code of the command*/
+ return xcode;
+ }/*check_property*/
+
+ /*If the given name does not satisfy the property*/
+ if(check_property(name) != 0)
+ {
+ /*unlock the directory*/
+ mutex_unlock(&dir->lock);
+
+ /*no such file in the directory*/
+ return ENOENT;
+ }
+
+ /*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 = FILTERFS_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 filterfs*/
+ 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/netfs-sample/filterfs.h b/netfs-sample/filterfs.h
new file mode 100644
index 00000000..4470a7fb
--- /dev/null
+++ b/netfs-sample/filterfs.h
@@ -0,0 +1,453 @@
+/*----------------------------------------------------------------------------*/
+/*filter.h*/
+/*----------------------------------------------------------------------------*/
+/*The definitions for the filtering translator*/
+/*----------------------------------------------------------------------------*/
+/*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 __FILTERFS_H__
+#define __FILTERFS_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 for the root node*/
+#define FILTERFS_ROOT_INODE 1
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Global Variables----------------------------------------------------*/
+/*A mapped time value for filterfs*/
+/*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;
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------Structures----------------------------------------------------------*/
+/*A single entry in a directory*/
+struct filterfs_dir_entry
+ {
+ char * name; /*name of the entry*/
+ size_t hv; /*hash value of NAME*/
+
+ struct node * node; /*the active node referred to by this node (may be 0)*/
+ /*NETFS_NDOE_REFCNT_LOCK should be hel whule frobbing this*/
+
+ struct stat stat; /*status information*/
+
+ char * symlink_target; /*the name of the node this entry might refer to*/
+
+ time_t stat_timestamp; /*the moment at which the status information was retreieved*/
+
+ /*the directory to which this entry belongs*/
+ struct filterfs_dir * dir;
+
+ /*link to the entry in hash bucket and the address of the previous entry's
+ (or hashtable's) pointer to this entry*/
+ /*If self_p is null, then this entry is deleted and awaiting
+ the final disposal*/
+ struct filterfs_dir_entry * next, ** self_p;
+
+ /*the next and the previous entries in the 'directory order'; 0 if there
+ are none*/
+ struct filterfs_dir_entry * ordered_next, ** ordered_self_p;
+
+ /*when the presence/absence of this file was last checked*/
+ time_t name_timestamp;
+
+ /*used for removing this entry*/
+ hurd_ihash_locp_t inode_locp;
+
+ /*a field for a negative result of lookup*/
+ int noent : 1;
+
+ /*a marker for gabage collecting*/
+ int valid : 1;
+ };/*struct filterfs_dir_entry*/
+/*----------------------------------------------------------------------------*/
+/*A directory*/
+struct filterfs_dir
+ {
+ /*the number of entries in the hash table*/
+ size_t num_entries;
+
+ /*the number of entries that have nodes attached*/
+ /*We keep an additional reference to our node if there is any,
+ to prevent it from going away*/
+ size_t num_live_entries;
+
+ /*the hash table for entries*/
+ struct filterfs_dir_entry ** htable;
+ size_t htable_len; /*the lenght of the hash table*/
+
+ /*the list of entries in 'directory order'.*/
+ /*The entries are listed in a linked list using the ordered_next
+ and ordered_self_p fields in each entry. Not all entries in htable
+ need to be in this list*/
+ struct filterfs_dir_entry * ordered;
+
+ /*the filesystem node corresponding to this directory*/
+ struct node * node;
+
+ /*the filesystem this directory is in*/
+ struct filterfs * fs;
+
+ /*when the presence/absence of this file was last checked*/
+ time_t name_timestamp;
+
+ /*used for removing this entry*/
+ hurd_ihash_locp_t inode_locp;
+
+ /*Stuff for detecting bulk stats (?)*/
+ /*Might be reduntant for this project*/
+
+ /*the timestamp of the first sample in bulk_stat_count1, rounded to
+ BULK_STAT_PERIOD seconds*/
+ time_t buld_stat_base_stamp;
+
+ /*the number of stats done in the period
+ [bulk_stat_base_stamp, bulk_stat_base_stamp + BULK_STAT_PERIOD]*/
+ unsigned bulk_stat_count_first_half;
+
+ /*the number of stats done in the period
+ [bulk_stat_bast_stamp + BULK_STAT_PERIOD,
+ bulk_stat_bast_stamp + 2 * BULK_STAT_PERIOD]*/
+ unsigned bulk_stat_count_second_half;
+ };/*struct filterfs_dir*/
+/*----------------------------------------------------------------------------*/
+/*A particular filesystem*/
+struct filterfs
+ {
+ /*the root of the filesystem*/
+ struct node * root;
+
+ /*inode numbers*/
+ /*Assigned sequentially in the order of creation*/
+ ino_t next_inode;
+
+ /*the identifier of the filesystem (?)*/
+ int fsid;
+
+ /*a hash table mapping inode numbers to directory entries*/
+ struct hurd_ihash inode_mappings;
+
+ /*the lock for the hash table*/
+ spin_lock_t inode_mappings_lock;
+ };/*struct filterfs*/
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------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
diff --git a/netfs-sample/lib.c b/netfs-sample/lib.c
new file mode 100644
index 00000000..aa0ff0be
--- /dev/null
+++ b/netfs-sample/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/netfs-sample/lib.h b/netfs-sample/lib.h
new file mode 100644
index 00000000..6335efcf
--- /dev/null
+++ b/netfs-sample/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/netfs-sample/lnode.c b/netfs-sample/lnode.c
new file mode 100644
index 00000000..33394ed0
--- /dev/null
+++ b/netfs-sample/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 filterfs 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 filterfs
+ (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 filterfs 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 filterfs 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/netfs-sample/lnode.h b/netfs-sample/lnode.h
new file mode 100644
index 00000000..7c42c8d4
--- /dev/null
+++ b/netfs-sample/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/netfs-sample/ncache.c b/netfs-sample/ncache.c
new file mode 100644
index 00000000..12462d31
--- /dev/null
+++ b/netfs-sample/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/netfs-sample/ncache.h b/netfs-sample/ncache.h
new file mode 100644
index 00000000..124d514e
--- /dev/null
+++ b/netfs-sample/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/netfs-sample/node.c b/netfs-sample/node.c
new file mode 100644
index 00000000..602660b6
--- /dev/null
+++ b/netfs-sample/node.c
@@ -0,0 +1,803 @@
+/*----------------------------------------------------------------------------*/
+/*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 "filterfs.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;
+
+ /*Obtain the path to the current node*/
+ char * path_to_node = node->nn->lnode->path;
+
+ /*The number of PROPERTY_PARAMs in the property*/
+ int param_entries_count = 0;
+
+ /*The length of the property*/
+ size_t property_len = (property) ? (strlen(property)) : (0);
+
+ /*The length of PROPERTY_PARAM*/
+ size_t property_param_len = strlen(PROPERTY_PARAM);
+
+ /*The full name and the filtering command*/
+ char * full_name = NULL, * cmd = NULL;
+
+ /*The lengths of the full name and the filtering command in chunks*/
+ size_t full_name_size = 1, cmd_size = 1;
+
+ /*If some property was indeed specified*/
+ if(property_len != 0)
+ {
+ /*the pointer to the current occurrence of PROPERTY_PARAM*/
+ char * occurrence = strstr(property, PROPERTY_PARAM);
+
+ /*count the number of occurrences*/
+ for(; occurrence;
+ occurrence = strstr(occurrence + 1, PROPERTY_PARAM),
+ ++param_entries_count);
+
+ /*try allocate the memory for the fullname and the filtering command*/
+ full_name = malloc(full_name_size * STRING_CHUNK);
+ if(!full_name)
+ return ENOMEM;
+
+ cmd = malloc(cmd_size * STRING_CHUNK);
+ if(!cmd)
+ {
+ free(full_name);
+ return ENOMEM;
+ }
+ }
+
+ /*Obtain the length of the path*/
+ size_t pathlen = strlen(path_to_node);
+
+ /*Checks if the given file satisfies the property. Zero value means that
+ the entry must be filtered out*/
+ int
+ check_property
+ (
+ const char * name /*the name of the file*/
+ )
+ {
+ /*If there is no property*/
+ if(!property)
+ /*no filtering will be applied, any name is OK*/
+ return 0;
+
+ /*Everything OK at first*/
+ err = 0;
+
+ /*Compute the length of the full name once*/
+ size_t full_name_len = pathlen + 1 + strlen(name) + 1;
+
+ /*See how much space (in chunks) is required for the full name*/
+ int chunks = full_name_size;
+ for(; full_name_len > chunks * STRING_CHUNK; ++chunks);
+
+ /*If more memory is requied*/
+ if(chunks > full_name_size)
+ {
+ /*free the old full name*/
+ free(full_name);
+
+ /*try to allocate the new memory*/
+ full_name = malloc(chunks * STRING_CHUNK);
+ if(!full_name)
+ {
+ err = ENOMEM;
+ free(cmd); /*the string for the command is definitely allocated here*/
+ return 0;
+ }
+
+ /*store the new size*/
+ full_name_size = chunks;
+ }
+
+ /*Initialize `full_name` as a valid string*/
+ full_name[0] = 0;
+
+ /*Construct the full name*/
+ strcpy(full_name, path_to_node);
+ strcat(full_name, "/");
+ strcat(full_name, name);
+
+ /*LOG_MSG("node_entries_get: Applying filter to %s...", full_name);*/
+
+ /*Compute the space required for the final filtering command*/
+ size_t sz = property_len + (strlen(full_name) - property_param_len)
+ * param_entries_count;
+
+ /*See how much space (in chunks) is required for the command*/
+ for(chunks = cmd_size; sz > chunks * STRING_CHUNK; ++chunks);
+
+ /*If more memory is requied*/
+ if(chunks > cmd_size)
+ {
+ /*free the old command*/
+ free(cmd);
+
+ /*try to allocate the new memory*/
+ cmd = malloc(chunks * STRING_CHUNK);
+ if(!cmd)
+ {
+ err = ENOMEM;
+ free(full_name); /*the string for the full name is
+ definitely allocated here*/
+ return 0;
+ }
+
+ /*store the new size*/
+ cmd_size = chunks;
+ }
+
+ /*Initialize `cmd` as a valid string*/
+ cmd[0] = 0;
+
+ /*The current occurence of PROPERTY_PARAM in property*/
+ char * p = strstr(property, PROPERTY_PARAM);
+
+ /*The pointer to the current position in the property*/
+ char * propp = property;
+
+ /*While the command has not been constructed*/
+ for(; p; p = strstr(propp, PROPERTY_PARAM))
+ {
+ /*add the new part of the property to the command*/
+ strncat(cmd, propp, p - propp);
+
+ /*add the filename to the command*/
+ strcat(cmd, full_name);
+
+ /*LOG_MSG("\tcmd = '%s'", cmd);*/
+
+ /*advance the pointer in the property*/
+ propp = p + property_param_len;
+
+ /*LOG_MSG("\tpropp points at '%s'", propp);*/
+ }
+
+ /*Copy the rest of the property to the command*/
+ strcat(cmd, propp);
+
+ /*LOG_MSG("node_entries_get: The filtering command: '%s'.", cmd);*/
+
+ /*Execute the command*/
+ int xcode = WEXITSTATUS(system(cmd));
+
+ /*Return the exit code of the command*/
+ return xcode;
+ }/*check_property*/
+
+ /*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;
+
+ /*The exit code of property*/
+ int good;
+
+ /*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;
+
+ /*check if the current dirent has the property*/
+ good = check_property(name);
+ if(err)
+ break;
+
+ /*If the current entry is not good, skip it*/
+ if(good != 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);
+
+ /*Free the full name and the command (if these are present at all)*/
+ if(full_name)
+ free(full_name);
+ if(cmd)
+ free(cmd);
+
+ /*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 filterfs 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 filterfs 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/netfs-sample/node.h b/netfs-sample/node.h
new file mode 100644
index 00000000..dcd618d4
--- /dev/null
+++ b/netfs-sample/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 filterfs 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/netfs-sample/options.c b/netfs-sample/options.c
new file mode 100644
index 00000000..c39d3f2b
--- /dev/null
+++ b/netfs-sample/options.c
@@ -0,0 +1,255 @@
+/*----------------------------------------------------------------------------*/
+/*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 "Shows the contents of DIR filtered according to PROPERTY.\
+ If DIR is not specified, ~/ is assumed."
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------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 filterfs 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"},
+ {OPT_LONG_PROPERTY, OPT_PROPERTY, "PROPERTY", 0,
+ "The command which will act as a filter"}
+ };
+/*----------------------------------------------------------------------------*/
+/*Argp options only meaningful for startupp 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 filtering command*/
+char * property = NULL;
+/*----------------------------------------------------------------------------*/
+/*The directory to filter*/
+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 OPT_PROPERTY:
+ {
+ /*try to duplicate the filtering command*/
+ property = strdup(arg);
+ if(!property)
+ error(EXIT_FAILURE, ENOMEM, "Could not strdup the property");
+
+ break;
+ }
+ case ARGP_KEY_ARG: /*the directory to filter*/
+ {
+ /*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: Filtering 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/netfs-sample/options.h b/netfs-sample/options.h
new file mode 100644
index 00000000..0f917bb0
--- /dev/null
+++ b/netfs-sample/options.h
@@ -0,0 +1,64 @@
+/*----------------------------------------------------------------------------*/
+/*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 property according to which filtering will be performed*/
+#define OPT_PROPERTY 'p'
+/*----------------------------------------------------------------------------*/
+/*The corresponding long options*/
+#define OPT_LONG_CACHE_SIZE "cache-size"
+#define OPT_LONG_PROPERTY "property"
+/*----------------------------------------------------------------------------*/
+/*Makes a long option out of option name*/
+#define OPT_LONG(o) "--"o
+/*----------------------------------------------------------------------------*/
+/*The substring of the property which shall be replaced by the filename*/
+#define PROPERTY_PARAM "{}"
+/*----------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------*/
+/*--------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 filtering command*/
+extern char * property;
+/*----------------------------------------------------------------------------*/
+/*The directory to filter*/
+extern char * dir;
+/*----------------------------------------------------------------------------*/
+#endif /*__OPTIONS_H__*/