diff options
author | root <root@(null).(none)> | 2009-05-03 17:20:00 +0200 |
---|---|---|
committer | root <root@(null).(none)> | 2009-05-03 17:20:00 +0200 |
commit | e0faf22f31c48fb27b43c1825897d26e58feafc4 (patch) | |
tree | 65a09372b31e08a3a865bd0a88cd2718bafcd643 /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.c | 803 |
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*/ +/*----------------------------------------------------------------------------*/ |