summaryrefslogtreecommitdiff
path: root/unionfs/node.c
diff options
context:
space:
mode:
Diffstat (limited to 'unionfs/node.c')
-rw-r--r--unionfs/node.c555
1 files changed, 555 insertions, 0 deletions
diff --git a/unionfs/node.c b/unionfs/node.c
new file mode 100644
index 00000000..cf9a8b47
--- /dev/null
+++ b/unionfs/node.c
@@ -0,0 +1,555 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ Written by Moritz Schulte <moritz@duesseldorf.ccc.de>.
+
+ 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. */
+
+/* node management. */
+
+#define _GNU_SOURCE
+
+#include <hurd/netfs.h>
+#include <stdlib.h>
+#include <error.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <stdio.h>
+
+#include "unionfs.h"
+#include "node.h"
+#include "ulfs.h"
+#include "lib.h"
+
+/* Declarations for functions only used in this file. */
+
+/* Deallocate all ports contained in NODE and free per-ulfs data
+ structures. */
+void node_ulfs_free (node_t *node);
+
+/* Create a new node, derived from a light node, add a reference to
+ the light node. */
+error_t
+node_create (lnode_t *lnode, node_t **node)
+{
+ netnode_t *netnode_new = malloc (sizeof (netnode_t));
+ error_t err = 0;
+ node_t *node_new;
+
+ debug_msg ("node_create for lnode: %s", lnode->name);
+
+ if (! netnode_new)
+ {
+ err = ENOMEM;
+ return err;
+ }
+
+ node_new = netfs_make_node (netnode_new);
+ if (! node_new)
+ {
+ err = ENOMEM;
+ free (netnode_new);
+ return err;
+ }
+
+ node_new->nn->ulfs = NULL;
+
+ err = node_ulfs_init (node_new);
+ if (err)
+ {
+ node_destroy (node_new);
+ return err;
+ }
+
+ lnode->node = node_new;
+ lnode_ref_add (lnode);
+ node_new->nn->lnode = lnode;
+ node_new->nn->flags = 0;
+ node_new->nn->ncache_next = NULL;
+ node_new->nn->ncache_prev = NULL;
+ *node = node_new;
+
+ return err;
+}
+
+/* Destroy a node, remove one reference from the associated light
+ node. */
+void
+node_destroy (node_t *node)
+{
+ debug_msg ("node destroy: %s", node->nn->lnode->name);
+ assert (! (node->nn->ncache_next || node->nn->ncache_prev));
+ node_ulfs_free (node);
+ mutex_lock (&node->nn->lnode->lock);
+ node->nn->lnode->node = NULL;
+ lnode_ref_remove (node->nn->lnode);
+ free (node->nn);
+ free (node);
+}
+
+/* Make sure that all ports to the underlying filesystems of NODE,
+ which must be locked, are uptodate. */
+error_t
+node_update (node_t *node)
+{
+ error_t err = 0;
+ char *path;
+
+ node_ulfs_t *root_ulfs;
+ struct stat stat;
+ file_t port;
+ int i = 0;
+
+ debug_msg ("node_update for lnode: %s", node->nn->lnode->name);
+
+ if (node_is_root (node))
+ return err;
+
+ mutex_lock (&netfs_root_node->lock);
+
+ err = lnode_path_construct (node->nn->lnode, &path);
+ if (err)
+ {
+ mutex_unlock (&netfs_root_node->lock);
+ return err;
+ }
+
+ root_ulfs = netfs_root_node->nn->ulfs;
+
+ node_ulfs_iterate_unlocked (node)
+ {
+
+ if (node_ulfs->flags & FLAG_NODE_ULFS_FIXED)
+ {
+ i++;
+ continue;
+ }
+
+ /* We really have to update the port. */
+ if (port_valid (node_ulfs->port))
+ port_dealloc (node_ulfs->port);
+
+ err = file_lookup ((root_ulfs + i)->port, path,
+ O_READ | O_NOTRANS, O_NOTRANS,
+ 0, &port, &stat);
+
+ if (err)
+ {
+ node_ulfs->port = MACH_PORT_NULL;
+ err = 0;
+ i++;
+ continue;
+ }
+
+ if (stat.st_ino == underlying_node_stat.st_ino
+ && stat.st_fsid == underlying_node_stat.st_fsid)
+ /* It's OUR root node. */
+ err = ELOOP;
+ else
+ {
+ port_dealloc (port);
+ err = file_lookup ((root_ulfs + i)->port, path,
+ O_READ, 0, 0, &port, &stat);
+ }
+
+ if (err)
+ {
+ port = MACH_PORT_NULL;
+ err = 0;
+ }
+ node_ulfs->port = port;
+
+ i++;
+ }
+
+ free (path);
+ node->nn->flags |= FLAG_NODE_ULFS_UPTODATE;
+
+ mutex_unlock (&netfs_root_node->lock);
+
+ return err;
+}
+
+/* Remove all directory named NAME beneath DIR on all underlying filesystems.
+ Fails if we cannot remove all the directories. */
+error_t
+node_dir_remove (node_t *dir, char *name)
+{
+ error_t err = 0;
+
+ node_ulfs_iterate_reverse_unlocked (dir)
+ {
+ if (!port_valid (node_ulfs->port))
+ continue;
+
+ err = dir_rmdir (node_ulfs->port, name);
+ if ((err) && (err != ENOENT))
+ break;
+ }
+
+ return err;
+}
+
+/* Create a directory named NAME beneath DIR on the first (writable) underlying
+ filesystem. */
+error_t
+node_dir_create (node_t *dir, char *name, mode_t mode)
+{
+ error_t err = 0;
+
+ node_ulfs_iterate_unlocked (dir)
+ {
+ if (!port_valid (node_ulfs->port))
+ continue;
+
+ err = dir_mkdir (node_ulfs->port, name, mode);
+
+ if ((!err) || (err == EEXIST) || (err == ENOTDIR))
+ break;
+ }
+
+ return err;
+}
+
+/* Remove all files named NAME beneath DIR on the underlying filesystems
+ with FLAGS as openflags. */
+error_t
+node_unlink_file (node_t *dir, char *name)
+{
+ mach_port_t p;
+ struct stat stat;
+ error_t err = 0;
+ int removed = 0;
+
+ /* Using reverse iteration still have issues. Infact, we could be
+ deleting a file in some underlying filesystem, and keeping those
+ after the first occurring error.
+ FIXME: Check BEFORE starting deletion. */
+
+ node_ulfs_iterate_reverse_unlocked (dir)
+ {
+
+ if (!port_valid (node_ulfs->port))
+ continue;
+
+ err = file_lookup (node_ulfs->port, name,
+ O_NOTRANS, O_NOTRANS,
+ 0, &p, &stat);
+
+ port_dealloc (p);
+
+ if (err == ENOENT)
+ {
+ err = 0;
+ continue;
+ }
+
+ if (err)
+ break;
+
+ err = dir_unlink (node_ulfs->port, name);
+ if ((err) && (err != ENOENT))
+ break;
+
+ if (!err)
+ removed++;
+
+ }
+
+ if ((!err) && (!removed))
+ err = ENOENT;
+
+ return err;
+}
+
+/* Lookup a file named NAME beneath DIR on the underlying filesystems
+ with FLAGS as openflags. Return the first port successfully looked
+ up in *PORT and according stat information in *STAT. */
+error_t
+node_lookup_file (node_t *dir, char *name, int flags,
+ file_t *port, struct stat *s)
+{
+ error_t err = ENOENT;
+ struct stat stat;
+ file_t p;
+
+ node_ulfs_iterate_unlocked (dir)
+ {
+
+ if (err != ENOENT)
+ break;
+
+ if (!port_valid (node_ulfs->port))
+ continue;
+
+ err = file_lookup (node_ulfs->port, name,
+ flags | O_NOTRANS, O_NOTRANS,
+ 0, &p, &stat);
+ if (err)
+ continue;
+
+ if (stat.st_ino == underlying_node_stat.st_ino
+ && stat.st_fsid == underlying_node_stat.st_fsid)
+ /* It's OUR root node. */
+ err = ELOOP;
+ else
+ /* stat.st_mode & S_ITRANS */
+ {
+ port_dealloc (p);
+ err = file_lookup (node_ulfs->port, name,
+ flags, 0, 0, &p, &stat);
+ }
+ }
+
+ if (! err)
+ {
+ *s = stat;
+ *port = p;
+ }
+
+ return err;
+}
+
+/* Deallocate all ports contained in NODE and free per-ulfs data
+ structures. */
+void
+node_ulfs_free (node_t *node)
+{
+
+ node_ulfs_iterate_unlocked (node)
+ {
+ if (port_valid (node_ulfs->port)
+ && node_ulfs->port != underlying_node)
+ port_dealloc (node_ulfs->port);
+ }
+
+ free (node->nn->ulfs);
+}
+
+/* Initialize per-ulfs data structures for NODE. The ulfs_lock must
+ be held by the caller. */
+error_t
+node_ulfs_init (node_t *node)
+{
+ node_ulfs_t *ulfs_new;
+ error_t err = 0;
+
+ ulfs_new = malloc (ulfs_num * sizeof (node_ulfs_t));
+ if (! ulfs_new)
+ {
+ err = ENOMEM;
+ return err;
+ }
+
+ if (node->nn->ulfs)
+ node_ulfs_free (node);
+
+ node->nn->ulfs = ulfs_new;
+ node->nn->ulfs_num = ulfs_num;
+
+ node_ulfs_iterate_unlocked (node)
+ {
+ node_ulfs->flags = 0;
+ node_ulfs->port = port_null;
+ }
+
+ return err;
+}
+
+/* Read the merged directory entries from NODE, which must be
+ locked, into *DIRENTS. */
+error_t
+node_entries_get (node_t *node, node_dirent_t **dirents)
+{
+ struct dirent **dirent_list, **dirent;
+ node_dirent_t *node_dirent_list = NULL;
+ size_t dirent_data_size;
+ char *dirent_data;
+ error_t err = 0;
+
+ /* Add a dirent to the list. If an entry with the specified name
+ already exists, reuse that entry. Otherwise create a new
+ one. */
+ error_t node_dirent_add (char *name, ino_t fileno, int type)
+ {
+ error_t e = 0;
+ node_dirent_t *node_dirent;
+ node_dirent_t *node_dirent_new;
+ struct dirent *dirent_new;
+ int name_len = strlen (name);
+ int size = DIRENT_LEN (name_len);
+
+
+ for (node_dirent = node_dirent_list;
+ node_dirent && strcmp (node_dirent->dirent->d_name, name);
+ node_dirent = node_dirent->next);
+
+ if (node_dirent)
+ {
+ /* Reuse existing entry. */
+
+ node_dirent->dirent->d_fileno = fileno;
+ node_dirent->dirent->d_type = type;
+ return e;
+ }
+
+ /* Create new entry. */
+
+ node_dirent_new = malloc (sizeof (node_dirent_t));
+ if (!node_dirent_new)
+ {
+ e = ENOMEM;
+ return e;
+ }
+
+ dirent_new = malloc (size);
+ if (!dirent_new)
+ {
+ free (node_dirent_new);
+ e = ENOMEM;
+ return e;
+ }
+
+ /* Fill dirent. */
+ dirent_new->d_fileno = fileno;
+ dirent_new->d_type = type;
+ dirent_new->d_reclen = size;
+ strcpy ((char *) dirent_new + DIRENT_NAME_OFFS, name);
+
+ /* Add dirent to the list. */
+ node_dirent_new->dirent = dirent_new;
+ node_dirent_new->next = node_dirent_list;
+ node_dirent_list = node_dirent_new;
+
+ return e;
+ }
+
+ node_ulfs_iterate_unlocked(node)
+ {
+ if (!port_valid (node_ulfs->port))
+ continue;
+
+ err = dir_entries_get (node_ulfs->port, &dirent_data,
+ &dirent_data_size, &dirent_list);
+ if (err)
+ continue;
+
+ for (dirent = dirent_list; (! err) && *dirent; dirent++)
+ if (strcmp ((*dirent)->d_name, ".")
+ && strcmp ((*dirent)->d_name, ".."))
+ err = node_dirent_add ((*dirent)->d_name,
+ (*dirent)->d_fileno,
+ (*dirent)->d_type);
+
+ free (dirent_list);
+ munmap (dirent_data, dirent_data_size);
+ }
+
+ if (err)
+ node_entries_free (node_dirent_list);
+ else
+ *dirents = node_dirent_list;
+
+ return err;
+}
+
+/* Free DIRENTS. */
+void
+node_entries_free (node_dirent_t *dirents)
+{
+ node_dirent_t *dirent, *dirent_next;
+
+ for (dirent = dirents; dirent; dirent = dirent_next)
+ {
+ dirent_next = dirent->next;
+ free (dirent->dirent);
+ free (dirent);
+ }
+}
+
+/* Create the root node (and it's according lnode) and store it in
+ *ROOT_NODE. */
+error_t
+node_create_root (node_t **root_node)
+{
+ lnode_t *lnode;
+ node_t *node;
+ error_t err = 0;
+
+ err = lnode_create (NULL, &lnode);
+ if (err)
+ return err;
+
+ err = node_create (lnode, &node);
+ if (err)
+ {
+ lnode_destroy (lnode);
+ return err;
+ }
+
+ mutex_unlock (&lnode->lock);
+ *root_node = node;
+ return err;
+}
+
+/* Initialize the ports to the underlying filesystems for the root
+ node. */
+
+error_t
+node_init_root (node_t *node)
+{
+ error_t err;
+ ulfs_t *ulfs;
+ int i = 0;
+
+ mutex_lock (&ulfs_lock);
+
+ err = node_ulfs_init (node);
+ if (err)
+ {
+ mutex_unlock (&ulfs_lock);
+ return err;
+ }
+
+ node_ulfs_iterate_unlocked (node)
+ {
+
+ if (err)
+ break;
+
+ err = ulfs_get_num (i, &ulfs);
+ if (err)
+ break;
+
+ if (ulfs->path)
+ node_ulfs->port = file_name_lookup (ulfs->path,
+ O_READ | O_DIRECTORY, 0);
+ else
+ node_ulfs->port = underlying_node;
+
+ if (! port_valid (node_ulfs->port))
+ {
+ err = errno;
+ break;
+ }
+
+ node_ulfs->flags |= FLAG_NODE_ULFS_FIXED;
+ i++;
+ }
+
+ mutex_unlock (&ulfs_lock);
+ return err;
+}