/*----------------------------------------------------------------------------*/ /*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 . 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 #include #include #include #include /*----------------------------------------------------------------------------*/ #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*/ /*----------------------------------------------------------------------------*/