summaryrefslogtreecommitdiff
path: root/netfs-sample/node.c
diff options
context:
space:
mode:
authorroot <root@(null).(none)>2009-05-03 17:20:00 +0200
committerroot <root@(null).(none)>2009-05-03 17:20:00 +0200
commite0faf22f31c48fb27b43c1825897d26e58feafc4 (patch)
tree65a09372b31e08a3a865bd0a88cd2718bafcd643 /netfs-sample/node.c
This is my initial working version.
There is a bug in boot in this version: subhurd sometimes cannot boot.
Diffstat (limited to 'netfs-sample/node.c')
-rw-r--r--netfs-sample/node.c803
1 files changed, 803 insertions, 0 deletions
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*/
+/*----------------------------------------------------------------------------*/