/*---------------------------------------------------------------------------*/ /*lnode.c*/ /*---------------------------------------------------------------------------*/ /*Implementation of policies of management of 'light nodes'*/ /*---------------------------------------------------------------------------*/ /*Based on the code of unionfs translator.*/ /*---------------------------------------------------------------------------*/ /*Copyright (C) 2001, 2002, 2005, 2008, 2009 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 /*---------------------------------------------------------------------------*/ #include "lnode.h" #include "debug.h" #include "node.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 */ pthread_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) { /*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 */ pthread_mutex_init (&node_new->lock, NULL); pthread_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 the name of the node */ free (node->name); /*While the list of proxies has not been freed */ node_list_t p; for (p = node->proxies; p;) { /*shift the pointer to the head of the list forward */ node->proxies = node->proxies->next; /*drop the current cell in the list */ free (p); /*store the current proxy */ p = node->proxies; } /*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) { 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 proxy 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 proxy (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 proxy 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 proxy 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 */ pthread_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 */ /*---------------------------------------------------------------------------*/ /*Makes the specified lnode aware of another proxy. Both `node` and `proxy` must be locked*/ error_t lnode_add_proxy (lnode_t * node, node_t * proxy) { /*TODO: Make the list of proxies finite */ /*Create an new cell in the list of proxies */ node_list_t p = malloc (sizeof (node_list_t)); if (!p) return ENOMEM; /*If the supplied node references an lnode already (this should always happen, though) */ if (proxy->nn->lnode) /*remove the reference held by the node to that lnode */ lnode_ref_remove (proxy->nn->lnode); /*Connect the proxy to the lnode */ proxy->nn->lnode = node; /*Store the pointer to the proxy in the new cell */ p->node = proxy; /*Add the new cell to the list of proxies */ p->next = node->proxies; node->proxies = p; /*Count a new reference to this lnode */ lnode_ref_add (node); /*Everything is OK here */ return 0; } /*lnode_add_proxy */ /*---------------------------------------------------------------------------*/ /*Removes the specified proxy from the list of proxies of the supplied lnode. `proxy` must not be locked*/ void lnode_remove_proxy (lnode_t * node, node_t * proxy) { /*A pointer to a cell in the list of proxies */ node_list_t p; /*Lock the lnode */ pthread_mutex_lock (&node->lock); /*If the first cell in the list contains a reference to `proxy` */ if (node->proxies->node == proxy) { /*store a pointer to the head of the list */ p = node->proxies; /*shift the head of the list forward */ node->proxies = node->proxies->next; /*destroy the former head */ free (p); /*remove a reference from the supplied lnode */ lnode_ref_remove (node); /*stop right here */ pthread_mutex_unlock (&node->lock); return; } /*Another pointer to a cell in the list */ node_list_t q; /*Go through the list of proxy nodes of the given lnode */ for (p = node->proxies; p->next; p = p->next) { /*If the next cell does not contain a reference to the same node, as specified in the parameter, skip this entry */ if (p->next->node != proxy) continue; /*store a copy of the next element */ q = p->next; /*unregister the next element from the list */ p->next = q->next; /*remove the unregistered element */ free (q); /*stop looping */ break; } /*Remove a reference from the supplied lnode */ lnode_ref_remove (node); /*Unlock the node */ pthread_mutex_unlock (&node->lock); return; } /*lnode_remove_proxy */ /*---------------------------------------------------------------------------*/