diff options
Diffstat (limited to 'netfs-sample')
-rw-r--r-- | netfs-sample/README | 13 | ||||
-rwxr-xr-x | netfs-sample/build | 1 | ||||
-rw-r--r-- | netfs-sample/debug.h | 60 | ||||
-rw-r--r-- | netfs-sample/errors | 0 | ||||
-rw-r--r-- | netfs-sample/filtered | 0 | ||||
-rw-r--r-- | netfs-sample/filterfs.c | 1236 | ||||
-rw-r--r-- | netfs-sample/filterfs.h | 453 | ||||
-rw-r--r-- | netfs-sample/lib.c | 193 | ||||
-rw-r--r-- | netfs-sample/lib.h | 82 | ||||
-rw-r--r-- | netfs-sample/lnode.c | 305 | ||||
-rw-r--r-- | netfs-sample/lnode.h | 143 | ||||
-rw-r--r-- | netfs-sample/ncache.c | 221 | ||||
-rw-r--r-- | netfs-sample/ncache.h | 100 | ||||
-rw-r--r-- | netfs-sample/node.c | 803 | ||||
-rw-r--r-- | netfs-sample/node.h | 168 | ||||
-rw-r--r-- | netfs-sample/options.c | 255 | ||||
-rw-r--r-- | netfs-sample/options.h | 64 |
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__*/ |