summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoritz Schulte <moritz@duesseldorf.ccc.de>2002-12-07 17:47:13 +0000
committerMoritz Schulte <moritz@duesseldorf.ccc.de>2002-12-07 17:47:13 +0000
commit38bdab8153e5a253552b5e431e7a8f53a6d42285 (patch)
tree9695c0e0132a1273cbbd67d25c64029777b8c951
initial import
-rw-r--r--AUTHORS1
-rw-r--r--BUGS6
-rw-r--r--COPYING2
-rw-r--r--Makefile64
-rw-r--r--README2
-rw-r--r--TODO7
-rw-r--r--lib.c116
-rw-r--r--lib.h72
-rw-r--r--lnode.c184
-rw-r--r--lnode.h77
-rw-r--r--main.c127
-rw-r--r--ncache.c151
-rw-r--r--ncache.h59
-rw-r--r--netfs.c914
-rw-r--r--node.c416
-rw-r--r--node.h118
-rw-r--r--options.c182
-rw-r--r--options.h42
-rw-r--r--ulfs.c189
-rw-r--r--ulfs.h74
-rw-r--r--unionfs.h90
-rw-r--r--version.h29
22 files changed, 2922 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..23e2cc8
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+* Moritz Schulte <moritz@duesseldorf.ccc.de>
diff --git a/BUGS b/BUGS
new file mode 100644
index 0000000..9d90f8a
--- /dev/null
+++ b/BUGS
@@ -0,0 +1,6 @@
+Known bugs:
+
+ * it leaks memory, it seems that some lnodes are not destroyed,
+ * unionfs hangs when looking up recursive links, which point into the
+ unionfs
+ * emacs hangs when trying to save files in unionfs
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..5cc9607
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,2 @@
+This program is licensed under the terms of the GNU General Public
+License.
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..67dd3ae
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,64 @@
+# Hurd unionfs
+# Copyright (C) 2001, 2002 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.
+
+CFLAGS += -Wall -g -D_FILE_OFFSET_BITS=64 -std=gnu99 \
+ -DDEBUG
+LDFLAGS += -lnetfs -lfshelp -liohelp -lthreads \
+ -lports -lihash -lshouldbeinlibc
+
+all: unionfs
+
+unionfs: main.o node.o lnode.o ulfs.o ncache.o netfs.o \
+ lib.o options.o
+ $(CC) -o $@ main.o node.o lnode.o ulfs.o options.o \
+ ncache.o netfs.o lib.o $(LDFLAGS)
+
+unionfs.static: main.o node.o lnode.o ulfs.o ncache.o netfs.o \
+ lib.o options.o
+ $(CC) -static -o $@ main.o node.o lnode.o ulfs.o \
+ ncache.o netfs.o lib.o options.o $(LDFLAGS)
+
+main.o: main.c
+ $(CC) $(CFLAGS) -c $<
+
+node.o: node.c
+ $(CC) $(CFLAGS) -c $<
+
+lnode.o: lnode.c
+ $(CC) $(CFLAGS) -c $<
+
+ulfs.o: ulfs.c
+ $(CC) $(CFLAGS) -c $<
+
+ncache.o: ncache.c
+ $(CC) $(CFLAGS) -c $<
+
+netfs.o: netfs.c
+ $(CC) $(CFLAGS) -c $<
+
+lib.o: lib.c
+ $(CC) $(CFLAGS) -c $<
+
+options.o: options.c
+ $(CC) $(CFLAGS) -c $<
+
+.PHONY: clean
+
+clean:
+ rm -rf *.o unionfs
diff --git a/README b/README
new file mode 100644
index 0000000..d703616
--- /dev/null
+++ b/README
@@ -0,0 +1,2 @@
+This is an `unionfs' server for the Hurd. The semantics are similar
+to BSD's unionfs.
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..790221f
--- /dev/null
+++ b/TODO
@@ -0,0 +1,7 @@
+Todo list for unionfs:
+
+ * implement filesystem notification support;
+ only update nodes when needed
+ * cache dirents (?)
+ * verify that the locking is correct
+ * increase performance
diff --git a/lib.c b/lib.c
new file mode 100644
index 0000000..29af81f
--- /dev/null
+++ b/lib.c
@@ -0,0 +1,116 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+#define _GNU_SOURCE
+
+#include <hurd/netfs.h>
+#include <error.h>
+#include <dirent.h>
+#include <errno.h>
+
+#include "lib.h"
+
+/* Lock, which must be held, during printing of debugging
+ messages. */
+struct mutex debug_msg_lock = MUTEX_INITIALIZER;
+
+/* Fetch directory entries for DIR; store the raw data as returned by
+ the dir_readdir RPC in *DIRENT_DATA and a list of pointers to the
+ dirent structures in *DIRENT_LIST. */
+error_t
+dir_entries_get (file_t dir,
+ char **dirent_data, struct dirent ***dirent_list)
+{
+ error_t err;
+ size_t data_size;
+ int entries_num;
+ char *data;
+
+ err = dir_readdir (dir, &data, &data_size, 0, -1, 0, &entries_num);
+ if (! err)
+ {
+ struct dirent **list;
+
+ list = malloc (sizeof (struct dirent *) * (entries_num + 1));
+ if (list)
+ {
+ struct dirent *dp;
+ int i;
+
+ for (i = 0, dp = (struct dirent *) data;
+ i < entries_num;
+ i++, dp = (struct dirent *) ((char *) dp + dp->d_reclen))
+ *(list + i) = dp;
+ *(list + i) = NULL;
+
+ *dirent_data = data;
+ *dirent_list = list;
+ }
+ else
+ err = ENOMEM;
+ }
+ return err;
+}
+
+/* Lookup the file named NAME beneath DIR (or the cwd, if DIR is not a
+ valid port. Try to open with FLAGS0 first, and if that fails with
+ FLAGS1; MODE is the mode to user for newly created files. On
+ success, stat the looked up port and store it in *PORT, the
+ according stat information are stored in *STAT. */
+error_t
+file_lookup (file_t dir, char *name, int flags0, int flags1,
+ int mode, file_t *port, struct stat *stat)
+{
+ error_t err;
+ file_t p;
+ struct stat s;
+
+ file_t do_file_lookup (file_t d, char *n, int f, int m)
+ {
+ if (port_valid (d))
+ p = file_name_lookup_under (d, n, f, m);
+ else if (errno == EACCES)
+ p = file_name_lookup (n, f, m);
+ return p;
+ }
+
+ p = do_file_lookup (dir, name, flags0, mode);
+ if (! port_valid (p))
+ p = do_file_lookup (dir, name, flags1, mode);
+
+ if (port_valid (p))
+ {
+ if (stat)
+ {
+ err = io_stat (p, &s);
+ if (err)
+ port_dealloc (p);
+ }
+ }
+ else
+ err = errno;
+
+ if (! err)
+ {
+ *port = p;
+ if (stat)
+ *stat = s;
+ }
+ return err;
+}
diff --git a/lib.h b/lib.h
new file mode 100644
index 0000000..c81900e
--- /dev/null
+++ b/lib.h
@@ -0,0 +1,72 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+#ifndef INCLUDED_LIB_H
+#define INCLUDED_LIB_H
+
+#include <hurd.h>
+#include <dirent.h>
+#include <stddef.h>
+
+/* Returned directory entries are aligned to blocks this many bytes
+ long. Must be a power of two. */
+#define DIRENT_ALIGN 4
+#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name)
+
+/* Length is structure before the name + the name + '\0', all padded
+ to a four-byte alignment. */
+#define DIRENT_LEN(name_len) \
+ ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \
+ & ~(DIRENT_ALIGN - 1))
+
+/* These macros remove some Mach specific code from the server
+ itself. */
+#define port_null MACH_PORT_NULL
+#define port_dealloc(p) mach_port_deallocate (mach_task_self (), (p))
+#define port_valid(p) ((p) != port_null)
+
+/* Fetch directory entries for DIR; store the raw data as returned by
+ the dir_readdir RPC in *DIRENT_DATA and a list of pointers to the
+ dirent structures in *DIRENT_LIST. */
+error_t dir_entries_get (file_t dir,
+ char **dirent_data, struct dirent ***dirent_list);
+
+/* Lookup the file named NAME beneath DIR (or the cwd, if DIR is not a
+ valid port. Try to open with FLAGS0 first, and if that fails with
+ FLAGS1; MODE is the mode to user for newly created files. On
+ success, stat the looked up port and store it in *PORT, the
+ according stat information are stored in *STAT. */
+error_t file_lookup (file_t dir, char *name, int flags0, int flags1, int mode,
+ file_t *port, struct stat *stat);
+
+extern struct mutex debug_msg_lock;
+
+/* Support for debugging messages. */
+#define debug_msg_send(fmt, args...) \
+ do \
+ { \
+ mutex_lock (&debug_msg_lock); \
+ fprintf (stderr, "%s:%i: ", __FILE__, __LINE__); \
+ fprintf (stderr, fmt , ## args); \
+ putc ('\n', stderr); \
+ mutex_unlock (&debug_msg_lock); \
+ } \
+ while (0)
+
+#endif
diff --git a/lnode.c b/lnode.c
new file mode 100644
index 0000000..9b242bb
--- /dev/null
+++ b/lnode.c
@@ -0,0 +1,184 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+/* `light node' management. See unionfs.h for an explanation of light
+ nodes. */
+
+#define _GNU_SOURCE
+
+#include <hurd/netfs.h>
+#include <error.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lnode.h"
+#include "lib.h"
+#include "unionfs.h"
+
+/* Create a new light node as an entry with the name NAME and store it
+ in *NODE. The new node is not locked and contains a single
+ reference. */
+error_t
+lnode_create (char *name, lnode_t **node)
+{
+ lnode_t *node_new = malloc (sizeof (lnode_t));
+ error_t err = 0;
+
+ debug_msg ("lnode_create for name: %s", name);
+
+ if (! node_new)
+ err = ENOMEM;
+ else
+ {
+ char *name_cp = NULL;
+
+ if (name)
+ name_cp = strdup (name);
+ if (name && (! name_cp))
+ {
+ err = ENOMEM;
+ free (node_new);
+ }
+ else
+ {
+ node_new->name = name_cp;
+ node_new->name_len = name_cp ? strlen (name_cp) : 0;
+ node_new->flags = 0;
+ node_new->node = NULL;
+ node_new->next = NULL;
+ node_new->prevp = NULL;
+ node_new->dir = NULL;
+ node_new->entries = NULL;
+ node_new->references = 1;
+ mutex_init (&node_new->lock);
+ mutex_lock (&node_new->lock);
+ *node = node_new;
+ }
+ }
+ return err;
+}
+
+/* Destroy a light node. */
+void
+lnode_destroy (lnode_t *node)
+{
+ debug_msg ("lnode_destroy for name: %s", node->name);
+ free (node->name);
+ free (node);
+}
+
+/* Install the node in the node tree; add a reference to DIR, which
+ must be locked. */
+void
+lnode_install (lnode_t *dir, lnode_t *node)
+{
+ lnode_ref_add (dir);
+ node->next = dir->entries;
+ node->prevp = &dir->entries;
+ if (dir->entries)
+ dir->entries->prevp = &node->next;
+ dir->entries = node;
+ node->dir = dir;
+}
+
+/* Uninstall the node from the node tree; remove a reference from the
+ lnode containing NODE. */
+void
+lnode_uninstall (lnode_t *node)
+{
+ lnode_ref_remove (node->dir);
+ *node->prevp = node->next;
+ if (node->next)
+ node->next->prevp = &node->next;
+}
+
+/* Add a reference to NODE, which must be locked. */
+void
+lnode_ref_add (lnode_t *node)
+{
+ node->references++;
+}
+
+/* Remove a reference to NODE, which must be locked. If that was the
+ last reference, destroy the node, otherwise simply unlock NODE. */
+void
+lnode_ref_remove (lnode_t *node)
+{
+ assert (node->references);
+ if (! --node->references)
+ {
+ lnode_uninstall (node);
+ lnode_destroy (node);
+ }
+ else
+ mutex_unlock (&node->lock);
+}
+
+/* Get a light node by it's name. The looked up node is locked and
+ got one reference added. */
+error_t
+lnode_get (lnode_t *dir, char *name,
+ lnode_t **node)
+{
+ error_t err = 0;
+ lnode_t *n;
+
+ for (n = dir->entries; n && strcmp (n->name, name); n = n->next);
+ if (n)
+ {
+ mutex_lock (&n->lock);
+ lnode_ref_add (n);
+ *node = n;
+ }
+ else
+ err = ENOENT;
+
+ return err;
+}
+
+/* Construct the full path for a given light node. */
+error_t
+lnode_path_construct (lnode_t *node,
+ char **path)
+{
+ error_t err = 0;
+ int p_len = 1;
+ lnode_t *n;
+ char *p;
+
+ for (n = node; n && n->dir; n = n->dir)
+ p_len += n->name_len + (n->dir->dir ? 1 : 0);
+
+ p = malloc (p_len);
+ if (! p)
+ err = ENOMEM;
+ else
+ {
+ *(p + --p_len) = 0;
+ for (n = node; n && n->dir; n = n->dir)
+ {
+ p_len -= n->name_len;
+ strncpy (p + p_len, n->name, n->name_len);
+ if (n->dir->dir)
+ *(p + --p_len) = '/';
+ }
+ *path = p;
+ }
+ return err;
+}
diff --git a/lnode.h b/lnode.h
new file mode 100644
index 0000000..b43b7b2
--- /dev/null
+++ b/lnode.h
@@ -0,0 +1,77 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+/* `light node' management. */
+
+#ifndef INCLUDED_LNODE_H
+#define INCLUDED_LNODE_H
+
+#include <hurd/netfs.h>
+#include <error.h>
+
+struct lnode
+{
+ char *name; /* The name of this light node. */
+ int name_len; /* This is used quite often and since
+ NAME does not change, just
+ calculate it once. */
+ int flags; /* Associated flags. */
+ int references; /* References to this light node. */
+ struct node *node; /* Reference to the real node. */
+ struct lnode *next, **prevp; /* Light nodes are connected in a
+ linked list. */
+ struct lnode *dir; /* The light node this light node is
+ contained int. */
+ struct lnode *entries; /* A reference to the list containing
+ the entries of this light node. */
+ struct mutex lock; /* A lock. */
+};
+typedef struct lnode lnode_t;
+
+/* Create a new light node as an entry with the name NAME and store it
+ in *NODE. The new node is not locked and contains a single
+ reference. */
+error_t lnode_create (char *name, lnode_t **node);
+
+/* Destroy a light node. */
+void lnode_destroy (lnode_t *node);
+
+/* Install the node in the node tree; add a reference to DIR. */
+void lnode_install (lnode_t *dir, lnode_t *node);
+
+/* Uninstall the node from the node tree; remove a reference from the
+ lnode containing NODE. */
+void lnode_uninstall (lnode_t *node);
+
+/* Add a reference to NODE, which must be locked. */
+void lnode_ref_add (lnode_t *node);
+
+/* Remove a reference to NODE, which must be locked. If that was the
+ last reference, destroy the node, otherwise simply unlock NODE. */
+void lnode_ref_remove (lnode_t *node);
+
+/* Get a light node by it's name. The looked up node is locked and
+ got one reference added. */
+error_t lnode_get (lnode_t *dir, char *name,
+ lnode_t **node);
+
+/* Construct the full path for a given light node. */
+error_t lnode_path_construct (lnode_t *node, char **path);
+
+#endif
diff --git a/main.c b/main.c
new file mode 100644
index 0000000..2b8ebd2
--- /dev/null
+++ b/main.c
@@ -0,0 +1,127 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+#define _GNU_SOURCE
+
+#include <hurd/netfs.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "version.h"
+
+#include "unionfs.h"
+#include "ncache.h"
+#include "ulfs.h"
+#include "lnode.h"
+#include "node.h"
+#include "options.h"
+
+char *netfs_server_name = "unionfs";
+char *netfs_server_version = HURD_VERSION;
+
+/* Not really needed, since unionfs doesn't manage symlinks. */
+int netfs_maxsymlinks = 0;
+
+/* Flags describing certain properties of the unionfs. */
+int unionfs_flags;
+
+/* The filesystem id (the pid). */
+pid_t fsid;
+
+/* A port to the underlying node. */
+mach_port_t underlying_node;
+
+/* stat information for the underlying node. */
+struct stat underlying_node_stat;
+
+/* Mapped time, used for updating node information. */
+volatile struct mapped_time_value *maptime;
+
+/* Used by netfs_set_options to handle runtime option parsing. */
+struct argp *netfs_runtime_argp = &argp_runtime;
+
+/* Main entry point. */
+int
+main (int argc, char **argv)
+{
+ mach_port_t bootstrap_port;
+ error_t err = 0;
+
+ /* Argument parsing. */
+ argp_parse (&argp_startup, argc, argv, ARGP_IN_ORDER, 0, 0);
+
+ err = node_create_root (&netfs_root_node);
+ if (err)
+ error (EXIT_FAILURE, err, "failed to create root node");
+
+ /* netfs initialization. */
+ task_get_bootstrap_port (mach_task_self (), &bootstrap_port);
+ netfs_init ();
+ underlying_node = netfs_startup (bootstrap_port, O_READ);
+
+ err = node_init_root (netfs_root_node);
+ if (err)
+ error (EXIT_FAILURE, err, "failed to initialize root node");
+
+ /* Map the time, used for updating node information. */
+ err = maptime_map (0, 0, &maptime);
+ if (err)
+ error (EXIT_FAILURE, err, "maptime_map");
+
+ /* More initialiazation. */
+ ncache_init (ncache_size);
+
+ /* Here we adjust the root node permissions. */
+ err = io_stat (underlying_node, &underlying_node_stat);
+
+ if (err)
+ error (EXIT_FAILURE, err, "io_stat");
+
+ fsid = getpid ();
+ netfs_root_node->nn_stat = underlying_node_stat;
+ netfs_root_node->nn_stat.st_ino = UNIONFS_ROOT_INODE; /* FIXME. */
+ netfs_root_node->nn_stat.st_fsid = fsid;
+ netfs_root_node->nn_stat.st_mode = S_IFDIR | (underlying_node_stat.st_mode
+ & ~S_IFMT & ~S_ITRANS);
+ netfs_root_node->nn_translated = netfs_root_node->nn_stat.st_mode;
+
+ /* If the underlying node isn't a directory, enhance the stat
+ information. */
+ if (! S_ISDIR (underlying_node_stat.st_mode))
+ {
+ if (underlying_node_stat.st_mode & S_IRUSR)
+ netfs_root_node->nn_stat.st_mode |= S_IXUSR;
+ if (underlying_node_stat.st_mode & S_IRGRP)
+ netfs_root_node->nn_stat.st_mode |= S_IXGRP;
+ if (underlying_node_stat.st_mode & S_IROTH)
+ netfs_root_node->nn_stat.st_mode |= S_IXOTH;
+ }
+
+ /* Update the timestamps of the root node. */
+ fshelp_touch (&netfs_root_node->nn_stat,
+ TOUCH_ATIME | TOUCH_MTIME | TOUCH_CTIME, maptime);
+
+ /* Start serving clients. */
+ for (;;)
+ netfs_server_loop ();
+}
diff --git a/ncache.c b/ncache.c
new file mode 100644
index 0000000..4c83ac0
--- /dev/null
+++ b/ncache.c
@@ -0,0 +1,151 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+#define _GNU_SOURCE
+
+#include <hurd/netfs.h>
+#include <error.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "ncache.h"
+#include "lib.h"
+#include "unionfs.h"
+
+/* The node cache. */
+ncache_t ncache;
+
+/* Cache size, may be overwritten by the user. */
+int ncache_size = NCACHE_SIZE;
+
+/* Initialize the node cache, set the maximum number of allowed nodes
+ in the cache to SIZE_MAX. */
+void
+ncache_init (int size_max)
+{
+ ncache.mru = NULL;
+ ncache.lru = NULL;
+ ncache.size_max = size_max;
+ ncache.size_current = 0;
+ mutex_init (&ncache.lock);
+}
+
+/* Remove the given node NODE from the cache. */
+static void
+ncache_node_remove (node_t *node)
+{
+ struct netnode *nn = node->nn;
+
+ if (nn->ncache_next)
+ nn->ncache_next->nn->ncache_prev = nn->ncache_prev;
+ if (nn->ncache_prev)
+ nn->ncache_prev->nn->ncache_next = nn->ncache_next;
+ if (ncache.mru == node)
+ ncache.mru = nn->ncache_next;
+ if (ncache.lru == node)
+ ncache.lru = nn->ncache_prev;
+ nn->ncache_next = NULL;
+ nn->ncache_prev = NULL;
+ ncache.size_current--;
+}
+
+void
+ncache_reset (void)
+{
+ node_t *node;
+
+ mutex_lock (&ncache.lock);
+ while ((node = ncache.mru))
+ ncache_node_remove (node);
+ mutex_unlock (&ncache.lock);
+}
+
+/* Lookup the node for the light node LNODE. If it does not exist
+ anymore in the cache, create a new node. Store the looked up node
+ in *NODE. */
+error_t
+ncache_node_lookup (lnode_t *lnode, node_t **node)
+{
+ error_t err = 0;
+ node_t *n;
+
+ if (lnode->node)
+ {
+ debug_msg ("ncache_node_lookup for lnode: %s (found in cache)",
+ lnode->name);
+ n = lnode->node;
+ netfs_nref (n);
+ }
+ else
+ {
+ debug_msg ("ncache_node_lookup for lnode: %s (newly created)",
+ lnode->name);
+ err = node_create (lnode, &n);
+ }
+
+ if (! err)
+ {
+ mutex_lock (&n->lock);
+ *node = n;
+ }
+ return err;
+}
+
+/* Add the given node NODE to the node cache; remove
+ least-recently-used nodes, if needed. */
+void
+ncache_node_add (node_t *node)
+{
+ mutex_lock (&ncache.lock);
+
+ debug_msg ("adding node to cache: %s", node->nn->lnode->name);
+
+ if (ncache.size_max > 0 || ncache.size_current > 0)
+ {
+ if (ncache.mru != node)
+ {
+ if (node->nn->ncache_next || node->nn->ncache_prev)
+ /* Node is already in the cache. */
+ ncache_node_remove (node);
+ else
+ /* Add a reference from the cache. */
+ netfs_nref (node);
+
+ node->nn->ncache_next = ncache.mru;
+ node->nn->ncache_prev = NULL;
+ if (ncache.mru)
+ ncache.mru->nn->ncache_prev = node;
+ if (! ncache.lru)
+ ncache.lru = node;
+ ncache.mru = node;
+ ncache.size_current++;
+ }
+ }
+
+ /* Forget the least used nodes. */
+ while (ncache.size_current > ncache.size_max)
+ {
+ struct node *lru = ncache.lru;
+ debug_msg ("removing cached node: %s", lru->nn->lnode->name);
+ ncache_node_remove (lru);
+ netfs_nrele (lru);
+ }
+
+ mutex_unlock (&ncache.lock);
+}
diff --git a/ncache.h b/ncache.h
new file mode 100644
index 0000000..8fa7d10
--- /dev/null
+++ b/ncache.h
@@ -0,0 +1,59 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+#ifndef INCLUDED_NCACHE_H
+#define INCLUDED_NCACHE_H
+
+#include <error.h>
+#include <hurd/netfs.h>
+
+#include "node.h"
+
+typedef struct ncache
+{
+ node_t *mru; /* Reference to the mru end of the
+ cache chain. */
+ node_t *lru; /* Reference to the lru end of the
+ cache chain. */
+ int size_max; /* Maximal number of nodes to
+ cache. */
+ int size_current; /* Current number of nodes in the
+ cache. */
+ struct mutex lock; /* A lock. */
+} ncache_t;
+
+/* Cache size, may be overwritten by the user. */
+extern int ncache_size;
+
+/* Initialize the node cache, set the maximum number of allowed nodes
+ in the cache to SIZE_MAX. */
+void ncache_init (int size_max);
+
+/* Lookup the node for the light node LNODE. If it does not exist
+ anymore in the cache, create a new node. Store the looked up node
+ in *NODE. */
+error_t ncache_node_lookup (lnode_t *lnode, node_t **node);
+
+void ncache_reset (void);
+
+/* Add the given node NODE to the node cache; remove
+ least-recently-used nodes, if needed. */
+void ncache_node_add (node_t *node);
+
+#endif
diff --git a/netfs.c b/netfs.c
new file mode 100644
index 0000000..bd07d11
--- /dev/null
+++ b/netfs.c
@@ -0,0 +1,914 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+#define _GNU_SOURCE
+
+#include <hurd/netfs.h>
+#include <error.h>
+#include <argz.h>
+#include <stddef.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <hurd/paths.h>
+#include <sys/mman.h>
+
+#include "unionfs.h"
+#include "ulfs.h"
+#include "node.h"
+#include "lib.h"
+#include "ncache.h"
+#include "options.h"
+
+/* Return an argz string describing the current options. Fill *ARGZ
+ with a pointer to newly malloced storage holding the list and *LEN
+ to the length of that storage. */
+error_t
+netfs_append_args (char **argz, size_t *argz_len)
+{
+ error_t err = 0;
+
+ ulfs_iterate
+ {
+ if (! err)
+ if (unionfs_flags & FLAG_UNIONFS_MODE_DEBUG)
+ err = argz_add (argz, argz_len,
+ OPT_LONG (OPT_LONG_DEBUG));
+ if (! err)
+ if (ulfs->flags & FLAG_ULFS_WRITABLE)
+ err = argz_add (argz, argz_len,
+ OPT_LONG (OPT_LONG_WRITABLE));
+ if (! err)
+ {
+ if (ulfs->path)
+ err = argz_add (argz, argz_len, ulfs->path);
+ else
+ err = argz_add (argz, argz_len,
+ OPT_LONG (OPT_LONG_UNDERLYING));
+ }
+ }
+ return err;
+}
+
+/* Make sure that NP->nn_stat is filled with current information.
+ CRED identifies the user responsible for the operation. */
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+ error_t err = 0;
+
+ if (np != netfs_root_node)
+ {
+ if (! (np->nn->flags & FLAG_NODE_ULFS_UPTODATE))
+ err = node_update (np);
+ if (! err)
+ {
+ int done = 0;
+
+ node_ulfs_iterate_unlocked (np)
+ if ((! done) && port_valid (node_ulfs->port))
+ {
+ err = io_stat (node_ulfs->port, &np->nn_stat);
+ if (! err)
+ np->nn_translated = np->nn_stat.st_mode;
+ done = 1;
+ }
+ if (! done)
+ err = ENOENT; /* FIXME? */
+ }
+ }
+ return err;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on
+ locked node NP, to change the owner to UID and the group to GID. */
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np,
+ uid_t uid, uid_t gid)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt a chauthor call for the user specified by CRED
+ on locked node NP, thereby changing the author to AUTHOR. */
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *np,
+ uid_t author)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt a chmod call for the user specified by CRED on
+ locked node NODE, to change the mode to MODE. Unlike the normal
+ Unix and Hurd meaning of chmod, this function is also used to
+ attempt to change files into other types. If such a transition is
+ attempted which is impossible, then return EOPNOTSUPP. */
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *np,
+ mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to turn locked node NP (user CRED) into a symlink with
+ target NAME. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred, struct node *np,
+ char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to turn NODE (user CRED) into a device. TYPE is either
+ S_IFBLK or S_IFCHR. NP is locked. */
+error_t
+netfs_attempt_mkdev (struct iouser *cred, struct node *np,
+ mode_t type, dev_t indexes)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to set the passive translator record for FILE to ARGZ (of
+ length ARGZLEN) for user CRED. NP is locked. */
+error_t
+netfs_set_translator (struct iouser *cred, struct node *np,
+ char *argz, size_t argzlen)
+{
+ return EOPNOTSUPP;
+}
+
+/* For locked node NODE with S_IPTRANS set in its mode, look up the
+ name of its translator. Store the name into newly malloced
+ storage, and return it in *ARGZ; set *ARGZ_LEN to the total length. */
+error_t
+netfs_get_translator (struct node *node, char **argz,
+ size_t *argz_len)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt a chflags call for the user specified by CRED
+ on locked node NP, to change the flags to FLAGS. */
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np,
+ int flags)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt a utimes call for the user specified by CRED on
+ locked node NP, to change the atime to ATIME and the mtime to
+ MTIME. If ATIME or MTIME is null, then set to the current time. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *np,
+ struct timespec *atime, struct timespec *mtime)
+{
+ return 0;
+}
+
+/* This should attempt to set the size of the locked file NP (for user
+ CRED) to SIZE bytes long. */
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *np,
+ off_t size)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should attempt to fetch filesystem status information for the
+ remote filesystem, for the user CRED. NP is locked. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *np,
+ struct statfs *st)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should sync the locked file NP completely to disk, for the
+ user CRED. If WAIT is set, return only after the sync is
+ completely finished. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np,
+ int wait)
+{
+ return EOPNOTSUPP;
+}
+
+/* This should sync the entire remote filesystem. If WAIT is set,
+ return only after the sync is completely finished. */
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+/* lookup */
+
+/* We don't use this functions, but it has to be defined. */
+error_t
+netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **node)
+{
+ return EOPNOTSUPP;
+}
+
+/* Delete NAME in DIR (which is locked) for USER. */
+error_t
+netfs_attempt_unlink (struct iouser *user, struct node *dir,
+ char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to rename the directory FROMDIR to TODIR. Note that neither
+ of the specific nodes are locked. */
+error_t
+netfs_attempt_rename (struct iouser *user, struct node *fromdir,
+ char *fromname, struct node *todir,
+ char *toname, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to create a new directory named NAME in DIR (which is
+ locked) for USER with mode MODE. */
+error_t
+netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to remove directory named NAME in DIR (which is locked) for
+ USER. */
+error_t
+netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ return EOPNOTSUPP;
+}
+
+/* Create a link in DIR with name NAME to FILE for USER. Note that
+ neither DIR nor FILE are locked. If EXCL is set, do not delete the
+ target. Return EEXIST if NAME is already found in DIR. */
+error_t
+netfs_attempt_link (struct iouser *user, struct node *dir,
+ struct node *file, char *name, int excl)
+{
+ return EOPNOTSUPP;
+}
+
+/* Attempt to create an anonymous file related to DIR (which is
+ locked) for USER with MODE. Set *NP to the returned file upon
+ success. No matter what, unlock DIR. */
+error_t
+netfs_attempt_mkfile (struct iouser *user, struct node *dir,
+ mode_t mode, struct node **np)
+{
+ return EOPNOTSUPP;
+}
+
+/* (We don't use this function!) Attempt to create a file named NAME
+ in DIR (which is locked) for USER with MODE. Set *NP to the new
+ node upon return. On any error, clear *NP. *NP should be locked
+ on success; no matter what, unlock DIR before returning. */
+error_t
+netfs_attempt_create_file (struct iouser *user, struct node *dir,
+ char *name, mode_t mode, struct node **np)
+{
+ return EOPNOTSUPP;
+}
+
+/* Read the contents of locked node NP (a symlink), for USER, into
+ BUF. */
+error_t
+netfs_attempt_readlink (struct iouser *user, struct node *np,
+ char *buf)
+{
+ return EOPNOTSUPP;
+}
+
+/* libnetfs uses this functions once. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *np,
+ int flags, int newnode)
+{
+ error_t err = 0;
+
+ if (! err && (flags & O_READ))
+ err = fshelp_access (&np->nn_stat, S_IREAD, user);
+ if (! err && (flags & O_WRITE))
+ err = fshelp_access (&np->nn_stat, S_IWRITE, user);
+ if (! err && (flags & O_EXEC))
+ err = fshelp_access (&np->nn_stat, S_IEXEC, user);
+
+ return err;
+}
+
+/* Read from the locked file NP for user CRED starting at OFFSET and
+ continuing for up to *LEN bytes. Put the data at DATA. Set *LEN
+ to the amount successfully read upon return. */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ *len = 0;
+ return 0;
+}
+
+/* Write to the locked file NP for user CRED starting at OFSET and
+ continuing for up to *LEN bytes from DATA. Set *LEN to the amount
+ successfully written upon return. */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ /* Since unionfs only manages directories... */
+ return EISDIR;
+}
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and
+ O_EXEC) in *TYPES for locked file NP and user CRED. */
+error_t
+netfs_report_access (struct iouser *cred, struct node *np,
+ int *types)
+{
+ *types = 0;
+ if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+}
+
+/* Create a new user from the specified UID and GID arrays. */
+struct iouser *
+netfs_make_user (uid_t *uids, int nuids, uid_t *gids, int ngids)
+{
+ return NULL;
+}
+
+/* Node NP has no more references; free all its associated storage. */
+void
+netfs_node_norefs (struct node *np)
+{
+ node_destroy (np);
+}
+
+error_t
+netfs_attempt_lookup_improved (struct iouser *user, struct node *dir,
+ char *name, struct node **np,
+ int flags, int lastcomp,
+ mach_port_t *port,
+ mach_msg_type_name_t *port_type)
+{
+ mach_port_t p;
+ error_t err;
+
+ mutex_lock (&dir->nn->lnode->lock);
+
+ err = fshelp_access (&dir->nn_stat, S_IEXEC, user);
+ if ((! err) && (! *name || ! strcmp (name, ".")))
+ {
+ /* The same node is wanted. */
+ *np = dir;
+ netfs_nref (*np);
+ }
+ else if ((! err) && (! strcmp (name, "..")))
+ {
+ /* We have to get the according light node first. */
+ lnode_t *lnode = dir->nn->lnode;
+ node_t *node;
+
+ err = ncache_node_lookup (lnode->dir, &node);
+ if (! err)
+ {
+ *np = node;
+ }
+ }
+ else if (! err)
+ {
+ lnode_t *dir_lnode = dir->nn->lnode;
+ struct stat statbuf;
+ lnode_t *lnode = NULL;
+
+ /* Lookup the node by it's name on the underlying
+ filesystems. */
+
+ err = node_update (dir);
+
+ /* We have to unlock this node while doing lookups. */
+ mutex_unlock (&dir_lnode->lock);
+ mutex_unlock (&dir->lock);
+ if (! err)
+ err = node_lookup_file (dir, name, flags & ~O_NOLINK,
+ &p, &statbuf);
+ mutex_lock (&dir->lock);
+ mutex_lock (&dir_lnode->lock);
+
+ if ((! err) && S_ISDIR (statbuf.st_mode))
+ {
+ node_t *node;
+
+ /* We don't need this port directly. */
+ port_dealloc (p);
+
+ /* The found node is a directory, so we have to manage the
+ node. First we need the light node. */
+
+ err = lnode_get (dir_lnode, name, &lnode);
+ if (err == ENOENT)
+ {
+ /* It does not exist, we have to create it. */
+
+ err = lnode_create (name, &lnode);
+ if (! err)
+ lnode_install (dir_lnode, lnode);
+ }
+
+ if (! err)
+ /* Now we have a light node. */
+ err = ncache_node_lookup (lnode, &node);
+
+ if (lnode)
+ /* This unlocks the node for us. */
+ lnode_ref_remove (lnode);
+
+ if (! err)
+ {
+ /* Got the node. */
+ *np = node;
+ }
+ }
+ else if (! err)
+ {
+ /* The found node is not a directory. */
+ if (! lastcomp)
+ {
+ /* We have not reached the last path component yet. */
+ port_dealloc (p);
+ err = ENOTDIR;
+ }
+ if (! err)
+ {
+ mach_port_t p_restricted;
+
+ /* A file node is successfully looked up. */
+ err = io_restrict_auth (p, &p_restricted,
+ user->uids->ids, user->uids->num,
+ user->gids->ids, user->gids->num);
+ port_dealloc (p);
+ if (! err)
+ {
+ /* Successfully restricted. */
+ *port = p_restricted;
+ *port_type = MACH_MSG_TYPE_MOVE_SEND;
+ }
+ }
+ }
+ }
+
+
+ if (err)
+ *np = NULL;
+ else if (*np)
+ {
+ mutex_unlock (&(*np)->lock);
+ ncache_node_add (*np);
+ }
+
+ mutex_unlock (&dir->nn->lnode->lock);
+ mutex_unlock (&dir->lock);
+ return err;
+}
+
+/* We need our own, special implementation of netfs_S_dir_lookup,
+ because libnetfs does not (yet?) know about cases, in which the
+ servers wants to return (foreign) ports directly to the user,
+ instead of usual node structures. */
+
+#define OPENONLY_STATE_MODES (O_CREAT|O_EXCL|O_NOLINK|O_NOTRANS|O_NONBLOCK)
+
+fshelp_fetch_root_callback1_t _netfs_translator_callback1;
+fshelp_fetch_root_callback2_t _netfs_translator_callback2;
+
+error_t
+netfs_S_dir_lookup (struct protid *diruser,
+ char *filename,
+ int flags,
+ mode_t mode,
+ retry_type *do_retry,
+ char *retry_name,
+ mach_port_t *retry_port,
+ mach_msg_type_name_t *retry_port_type)
+{
+ int create; /* true if O_CREAT flag set */
+ int excl; /* true if O_EXCL flag set */
+ int mustbedir = 0; /* true if the result must be S_IFDIR */
+ int lastcomp = 0; /* true if we are at the last component */
+ int newnode = 0; /* true if this node is newly created */
+ int nsymlinks = 0;
+ struct node *dnp, *np;
+ char *nextname;
+ error_t error;
+ struct protid *newpi;
+ struct iouser *user;
+
+ if (!diruser)
+ return EOPNOTSUPP;
+
+ create = (flags & O_CREAT);
+ excl = (flags & O_EXCL);
+
+ /* Skip leading slashes */
+ while (*filename == '/')
+ filename++;
+
+ *retry_port_type = MACH_MSG_TYPE_MAKE_SEND;
+ *do_retry = FS_RETRY_NORMAL;
+ *retry_name = '\0';
+
+ if (*filename == '\0')
+ {
+ /* Set things up in the state expected by the code from gotit: on. */
+ dnp = 0;
+ np = diruser->po->np;
+ mutex_lock (&np->lock);
+ netfs_nref (np);
+ goto gotit;
+ }
+
+ dnp = diruser->po->np;
+
+ mutex_lock (&dnp->lock);
+
+ netfs_nref (dnp); /* acquire a reference for later netfs_nput */
+
+ do
+ {
+ assert (!lastcomp);
+
+ /* Find the name of the next pathname component */
+ nextname = index (filename, '/');
+
+ if (nextname)
+ {
+ *nextname++ = '\0';
+ while (*nextname == '/')
+ nextname++;
+ if (*nextname == '\0')
+ {
+ /* These are the rules for filenames ending in /. */
+ nextname = 0;
+ lastcomp = 1;
+ mustbedir = 1;
+ create = 0;
+ }
+ else
+ lastcomp = 0;
+ }
+ else
+ lastcomp = 1;
+
+ np = 0;
+
+ retry_lookup:
+
+ if ((dnp == netfs_root_node || dnp == diruser->po->shadow_root)
+ && filename[0] == '.' && filename[1] == '.' && filename[2] == '\0')
+ if (dnp == diruser->po->shadow_root)
+ /* We're at the root of a shadow tree. */
+ {
+ *do_retry = FS_RETRY_REAUTH;
+ *retry_port = diruser->po->shadow_root_parent;
+ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
+ if (! lastcomp)
+ strcpy (retry_name, nextname);
+ error = 0;
+ mutex_unlock (&dnp->lock);
+ goto out;
+ }
+ else if (diruser->po->root_parent != MACH_PORT_NULL)
+ /* We're at a real translator root; even if DIRUSER->po has a
+ shadow root, we can get here if its in a directory that was
+ renamed out from under it... */
+ {
+ *do_retry = FS_RETRY_REAUTH;
+ *retry_port = diruser->po->root_parent;
+ *retry_port_type = MACH_MSG_TYPE_COPY_SEND;
+ if (!lastcomp)
+ strcpy (retry_name, nextname);
+ error = 0;
+ mutex_unlock (&dnp->lock);
+ goto out;
+ }
+ else
+ /* We are global root */
+ {
+ error = 0;
+ np = dnp;
+ netfs_nref (np);
+ }
+ else
+ /* Attempt a lookup on the next pathname component. */
+ error = netfs_attempt_lookup_improved (diruser->user, dnp,
+ filename, &np, flags, lastcomp,
+ retry_port, retry_port_type);
+
+ /* At this point, DNP is unlocked */
+
+ /* Implement O_EXCL flag here */
+ if (lastcomp && create && excl && !error && np)
+ error = EEXIST;
+
+ /* Create the new node if necessary */
+ if (lastcomp && create && error == ENOENT)
+ {
+ mode &= ~(S_IFMT | S_ISPARE | S_ISVTX);
+ mode |= S_IFREG;
+ mutex_lock (&dnp->lock);
+ /* FIXME, new interface needed! */
+ error = netfs_attempt_create_file (diruser->user, dnp,
+ filename, mode, &np);
+
+ /* If someone has already created the file (between our lookup
+ and this create) then we just got EEXIST. If we are
+ EXCL, that's fine; otherwise, we have to retry the lookup. */
+ if (error == EEXIST && !excl)
+ {
+ mutex_lock (&dnp->lock);
+ goto retry_lookup;
+ }
+
+ newnode = 1;
+ }
+
+ /* All remaining errors get returned to the user */
+ if (error)
+ goto out;
+
+ if (np)
+ {
+ mutex_lock (&np->lock);
+ error = netfs_validate_stat (np, diruser->user);
+ mutex_unlock (&np->lock);
+ if (error)
+ goto out;
+ }
+
+ if (np
+ && S_ISLNK (np->nn_translated)
+ && (!lastcomp
+ || mustbedir /* "foo/" must see that foo points to a dir */
+ || !(flags & (O_NOLINK|O_NOTRANS))))
+ {
+ size_t nextnamelen, newnamelen, linklen;
+ char *linkbuf;
+
+ /* Handle symlink interpretation */
+ if (nsymlinks++ > netfs_maxsymlinks)
+ {
+ error = ELOOP;
+ goto out;
+ }
+
+ linklen = np->nn_stat.st_size;
+
+ nextnamelen = nextname ? strlen (nextname) + 1 : 0;
+ newnamelen = nextnamelen + linklen + 1;
+ linkbuf = alloca (newnamelen);
+
+ error = netfs_attempt_readlink (diruser->user, np, linkbuf);
+ if (error)
+ goto out;
+
+ if (nextname)
+ {
+ linkbuf[linklen] = '/';
+ memcpy (linkbuf + linklen + 1, nextname,
+ nextnamelen - 1);
+ }
+ linkbuf[nextnamelen + linklen] = '\0';
+
+ if (linkbuf[0] == '/')
+ {
+ /* Punt to the caller */
+ *do_retry = FS_RETRY_MAGICAL;
+ *retry_port = MACH_PORT_NULL;
+ strcpy (retry_name, linkbuf);
+ goto out;
+ }
+
+ filename = linkbuf;
+ if (lastcomp)
+ {
+ lastcomp = 0;
+
+ /* Symlinks to nonexistent files aren't allowed to cause
+ creation, so clear the flag here. */
+ create = 0;
+ }
+ netfs_nput (np);
+ mutex_lock (&dnp->lock);
+ np = 0;
+ }
+ else
+ {
+ /* Normal nodes here for next filename component */
+ filename = nextname;
+ netfs_nrele (dnp);
+
+ if (lastcomp)
+ dnp = 0;
+ else
+ {
+ dnp = np;
+ np = 0;
+ }
+ }
+ }
+ while (filename && *filename);
+
+ /* At this point, NP is the node to return. */
+ gotit:
+
+ if (mustbedir && ! np)
+ {
+ error = ENOTDIR;
+ goto out;
+ }
+
+ if (np)
+ error = netfs_check_open_permissions (diruser->user, np,
+ flags, newnode);
+
+ if (error)
+ goto out;
+
+ flags &= ~OPENONLY_STATE_MODES;
+
+ if (np)
+ {
+ error = iohelp_dup_iouser (&user, diruser->user);
+ if (error)
+ goto out;
+
+ newpi = netfs_make_protid (netfs_make_peropen (np, flags, diruser->po),
+ user);
+ if (! newpi)
+ {
+ iohelp_free_iouser (user);
+ error = errno;
+ goto out;
+ }
+
+ *retry_port = ports_get_right (newpi);
+ ports_port_deref (newpi);
+ }
+
+ out:
+ if (np)
+ netfs_nput (np);
+ if (dnp)
+ netfs_nrele (dnp);
+ return error;
+}
+
+/* Fill the array *DATA of size BUFSIZE with up to NENTRIES dirents
+ from DIR (which is locked) starting with entry ENTRY for user CRED.
+ The number of entries in the array is stored in *AMT and the number
+ of bytes in *DATACNT. If the supplied buffer is not large enough
+ to hold the data, it should be grown. */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *dir,
+ int first_entry, int num_entries, char **data,
+ mach_msg_type_number_t *data_len,
+ vm_size_t max_data_len, int *data_entries)
+{
+ node_dirent_t *dirent_start, *dirent_current;
+ node_dirent_t *dirent_list = NULL;
+ size_t size = 0;
+ int count = 0;
+ char *data_p;
+ error_t err;
+
+ int bump_size (const char *name)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ size_t new_size = size + DIRENT_LEN (strlen (name));
+
+ if (max_data_len > 0 && new_size > max_data_len)
+ return 0;
+ size = new_size;
+ count++;
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ int add_dirent (const char *name, ino_t fileno, int type)
+ {
+ if (num_entries == -1 || count < num_entries)
+ {
+ struct dirent hdr;
+ size_t name_len = strlen (name);
+ size_t sz = DIRENT_LEN (name_len);
+
+ if (sz > size)
+ return 0;
+ else
+ size -= sz;
+
+ hdr.d_fileno = fileno;
+ hdr.d_reclen = sz;
+ hdr.d_type = type;
+ hdr.d_namlen = name_len;
+
+ memcpy (data_p, &hdr, DIRENT_NAME_OFFS);
+ strcpy (data_p + DIRENT_NAME_OFFS, name);
+ data_p += sz;
+ count++;
+
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+ err = node_entries_get (dir, &dirent_list);
+
+ if (! err)
+ {
+ for (dirent_start = dirent_list, count = 2;
+ dirent_start && first_entry > count;
+ dirent_start = dirent_start->next, count++);
+
+ count = 0;
+
+ /* Make space for the `.' and `..' entries. */
+ if (first_entry == 0)
+ bump_size (".");
+ if (first_entry <= 1)
+ bump_size ("..");
+
+ /* See how much space we need for the result. */
+ for (dirent_current = dirent_start;
+ dirent_current;
+ dirent_current = dirent_current->next)
+ if (! bump_size (dirent_current->dirent->d_name))
+ break;
+
+ *data = mmap (0, size, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+ err = ((void *) *data == (void *) -1) ? errno : 0;
+ }
+
+ if (! err)
+ {
+ data_p = *data;
+ *data_len = size;
+ *data_entries = count;
+ count = 0;
+
+ /* Add `.' and `..' entries. */
+ if (first_entry == 0)
+ add_dirent (".", 2, DT_DIR);
+ if (first_entry <= 1)
+ add_dirent ("..", 2, DT_DIR);
+
+ for (dirent_current = dirent_start;
+ dirent_current;
+ dirent_current = dirent_current->next)
+ if (! add_dirent (dirent_current->dirent->d_name,
+ 2 /* FIXME */,
+ dirent_current->dirent->d_type))
+ break;
+ }
+
+ if (dirent_list)
+ node_entries_free (dirent_list);
+
+ fshelp_touch (&dir->nn_stat, TOUCH_ATIME, maptime);
+
+ return err;
+}
diff --git a/node.c b/node.c
new file mode 100644
index 0000000..7cb6fac
--- /dev/null
+++ b/node.c
@@ -0,0 +1,416 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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 <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;
+ else
+ {
+ node_new = netfs_make_node (netnode_new);
+ if (! node_new)
+ {
+ err = ENOMEM;
+ free (netnode_new);
+ }
+ else
+ {
+ node_new->nn->ulfs = NULL;
+ err = node_ulfs_init (node_new);
+ if (err)
+ node_destroy (node_new);
+ else
+ {
+ 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;
+
+ debug_msg ("node_update for lnode: %s", node->nn->lnode->name);
+
+ if (! node_is_root (node))
+ {
+ mutex_lock (&netfs_root_node->lock);
+ err = lnode_path_construct (node->nn->lnode, &path);
+ if (! err)
+ {
+ node_ulfs_t *root_ulfs;
+ struct stat stat;
+ file_t port;
+ int i = 0;
+
+ root_ulfs = netfs_root_node->nn->ulfs;
+ node_ulfs_iterate_unlocked (node)
+ {
+ if (! (node_ulfs->flags & FLAG_NODE_ULFS_FIXED))
+ {
+ /* 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)
+ {
+ 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;
+}
+
+/* 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 && port_valid (node_ulfs->port))
+ {
+ err = file_lookup (node_ulfs->port, name,
+ flags | O_NOTRANS, O_NOTRANS,
+ 0, &p, &stat);
+ if (! err)
+ {
+ 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 if (1) //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;
+ else
+ {
+ 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;
+ 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)
+ {
+ node_dirent_t *node_dirent;
+ error_t e = 0;
+
+ 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;
+ }
+ else
+ {
+ /* Create new entry. */
+
+ node_dirent_t *node_dirent_new;
+
+ node_dirent_new = malloc (sizeof (node_dirent_t));
+ if (node_dirent_new)
+ {
+ int name_len = strlen (name);
+ int size = DIRENT_LEN (name_len);
+ struct dirent *dirent_new;
+
+ dirent_new = malloc (size);
+ if (dirent_new)
+ {
+ /* 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;
+ }
+ else
+ {
+ free (node_dirent_new);
+ e = ENOMEM;
+ }
+ }
+ else
+ e = ENOMEM;
+ }
+ return e;
+ }
+
+ node_ulfs_iterate_unlocked(node)
+ {
+ if (port_valid (node_ulfs->port))
+ {
+ err = dir_entries_get (node_ulfs->port,
+ &dirent_data, &dirent_list);
+ if (! err)
+ {
+ 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);
+ }
+ }
+ }
+
+ 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;
+
+ err = lnode_create (NULL, &lnode);
+ if (! err)
+ {
+ err = node_create (lnode, &node);
+ mutex_unlock (&lnode->lock);
+ }
+ else
+ lnode_destroy (lnode);
+
+ if (! err)
+ *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;
+
+ mutex_lock (&ulfs_lock);
+
+ err = node_ulfs_init (node);
+ if (! err)
+ {
+ ulfs_t *ulfs;
+ int i = 0;
+
+ node_ulfs_iterate_unlocked (node)
+ {
+ if (! err)
+ err = ulfs_get_num (i, &ulfs);
+ if (! err)
+ {
+ 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;
+ else
+ {
+ node_ulfs->flags |= FLAG_NODE_ULFS_FIXED;
+ i++;
+ }
+ }
+ }
+ }
+
+ mutex_unlock (&ulfs_lock);
+ return err;
+}
diff --git a/node.h b/node.h
new file mode 100644
index 0000000..4d30417
--- /dev/null
+++ b/node.h
@@ -0,0 +1,118 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+#ifndef INCLUDED_NODE_H
+#define INCLUDED_NODE_H
+
+#include <error.h>
+#include <sys/stat.h>
+#include <hurd/netfs.h>
+
+typedef struct node node_t;
+
+#include "lnode.h"
+
+/* per-ulfs data for each node. */
+struct node_ulfs
+{
+ int flags; /* Flags associated for this
+ underlying filesystem. */
+ file_t port; /* A port to the underlying
+ filesystem. */
+};
+typedef struct node_ulfs node_ulfs_t;
+
+/* Flags. */
+
+/* The according port should not be updated. */
+#define FLAG_NODE_ULFS_FIXED 0x00000001
+
+struct netnode
+{
+ lnode_t *lnode; /* A reference to the according light
+ node. */
+ int flags; /* Associated flags. */
+ node_ulfs_t *ulfs; /* Array holding data for each
+ underlying filesystem. */
+ int ulfs_num; /* Number of entries in ULFS. */
+ node_t *ncache_next;
+ node_t *ncache_prev;
+};
+typedef struct netnode netnode_t;
+
+/* Flags. */
+#define FLAG_NODE_INVALIDATE 0x00000001
+#define FLAG_NODE_ULFS_UPTODATE 0x00000002
+
+typedef struct node_dirent
+{
+ struct dirent *dirent;
+ struct node_dirent *next;
+} node_dirent_t;
+
+/* 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);
+
+/* Destroy a node, remove one reference from the associated light
+ node. */
+void node_destroy (node_t *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);
+
+/* 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 *stat);
+
+/* Initialize per-ulfs data structures for NODE. The ulfs_lock must
+ be held by the caller. */
+error_t node_ulfs_init (node_t *node);
+
+/* 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);
+
+/* Free DIRENTS. */
+void node_entries_free (node_dirent_t *dirents);
+
+/* Create the root node (and it's according lnode) and store it in
+ *ROOT_NODE. */
+error_t node_create_root (node_t **root_node);
+
+/* Initialize the ports to the underlying filesystems for the root
+ node. */
+error_t node_init_root (node_t *node);
+
+/* Return non-zero, if NODE is the root node. */
+#define node_is_root(node) (node)->nn->lnode->dir ? 0 : 1
+
+/* Iterate over the per-ulfs data in NODE, which must be locked by the
+ caller. */
+#define node_ulfs_iterate_unlocked(node) \
+ for (node_ulfs_t *node_ulfs = (node)->nn->ulfs; \
+ node_ulfs < (node)->nn->ulfs + (node)->nn->ulfs_num; \
+ node_ulfs++)
+
+#endif
diff --git a/options.c b/options.c
new file mode 100644
index 0000000..225d649
--- /dev/null
+++ b/options.c
@@ -0,0 +1,182 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+/* Argument parsing. */
+
+#define _GNU_SOURCE
+
+#include <argp.h>
+#include <error.h>
+
+#include "options.h"
+#include "ulfs.h"
+#include "ncache.h"
+#include "unionfs.h"
+#include "node.h"
+#include "version.h"
+
+/* This variable is set to a non-zero value after parsing of the
+ startup options. Whenever the argument parser is later called to
+ modify the underlying filesystems of the root node, the root node
+ is initialized accordingly directly by the parser. */
+int parsing_startup_options_finished;
+
+/* Argp options common to the runtime and startup parser. */
+const struct argp_option argp_common_options[] =
+ {
+ { OPT_LONG_UNDERLYING, OPT_UNDERLYING, 0, 0,
+ "add the underlying node to the unionfs" },
+ { OPT_LONG_WRITABLE, OPT_WRITABLE, 0, 0,
+ "specify the following filesystem as writable" },
+ { OPT_LONG_DEBUG, OPT_DEBUG, 0, OPTION_HIDDEN,
+ "send debugging messages to stderr" },
+ { OPT_LONG_CACHE_SIZE, OPT_CACHE_SIZE, "SIZE", 0,
+ "specify the maximum number of nodes in the cache" },
+ { 0, 0, 0, 0, "Runtime options:", 1 },
+ { OPT_LONG_REMOVE, OPT_REMOVE, 0, 0,
+ "remove the following filesystem", 1 },
+ { 0 }
+ };
+
+/* Argp options only meaningful for startup parsing. */
+const struct argp_option argp_startup_options[] =
+ {
+ { 0 }
+ };
+
+/* Argp parser function for the common oprtions. */
+error_t
+argp_parse_common_options (int key, char *arg, struct argp_state *state)
+{
+ static int ulfs_flags = 0, ulfs_remove = 0, ulfs_modified = 0;
+ error_t err = 0;
+
+ switch (key)
+ {
+ case OPT_WRITABLE: /* --writable */
+ ulfs_flags |= FLAG_ULFS_WRITABLE;
+ break;
+
+ case OPT_DEBUG: /* --debug */
+ unionfs_flags |= FLAG_UNIONFS_MODE_DEBUG;
+ break;
+
+ case OPT_CACHE_SIZE: /* --cache-size */
+ ncache_size = strtol (arg, NULL, 10);
+ break;
+
+ case OPT_REMOVE: /* --remove */
+ ulfs_remove = 1;
+ break;
+
+ case OPT_UNDERLYING: /* --underlying */
+ case ARGP_KEY_ARG:
+ if (ulfs_remove)
+ {
+ err = ulfs_unregister (arg);
+ if (err == ENOENT)
+ /* It is not a fatal error, when the user tries to remove
+ a filesystem, which is not used by unionfs. */
+ err = 0;
+ }
+ else
+ err = ulfs_register (arg, ulfs_flags);
+ if (err)
+ error (EXIT_FAILURE, err, "ulfs_register");
+ ulfs_modified = 1;
+ ulfs_flags = ulfs_remove = 0;
+ break;
+
+ case ARGP_KEY_END:
+ ulfs_flags = ulfs_remove = 0;
+ if (ulfs_modified && parsing_startup_options_finished)
+ {
+ err = node_init_root (netfs_root_node);
+ if (err)
+ error (EXIT_FAILURE, err, "failed to initialize root node");
+ }
+ ncache_reset ();
+ ulfs_modified = 0;
+ if (! parsing_startup_options_finished)
+ parsing_startup_options_finished = 1;
+ break;
+
+ default:
+ err = ARGP_ERR_UNKNOWN;
+ break;
+ }
+
+ return err;
+}
+
+/* Argp parser function for the startup oprtions. */
+error_t
+argp_parse_startup_options (int key, char *arg, struct argp_state *state)
+{
+ error_t err = 0;
+
+ switch (key)
+ {
+ default:
+ err = ARGP_ERR_UNKNOWN;
+ break;
+ }
+
+ return err;
+}
+
+/* Argp parser for only the common options. */
+const struct argp argp_parser_common_options =
+ { argp_common_options, argp_parse_common_options, 0, 0, 0 };
+
+/* Argp parser for only the startup options. */
+struct argp argp_parser_startup_options =
+ { argp_startup_options, argp_parse_startup_options, 0, 0, 0 };
+
+/* The children parser for runtime arguments. */
+const struct argp_child argp_children_runtime[] =
+ {
+ { &argp_parser_common_options },
+ { &netfs_std_runtime_argp },
+ { 0 }
+ };
+
+/* The children parser for startup arguments. */
+const struct argp_child argp_children_startup[] =
+ {
+ { &argp_parser_startup_options },
+ { &argp_parser_common_options },
+ { &netfs_std_startup_argp },
+ { 0 }
+ };
+
+const char *argp_program_version = STANDARD_HURD_VERSION (unionfs);
+const char *argp_program_bug_address =
+"Moritz Schulte <moritz@duesseldorf.ccc.de>";
+
+#define ARGS_DOC "FILESYSTEMS ..."
+#define DOC "Hurd unionfs server"
+
+/* The final argp parser for runtime arguments. */
+struct argp argp_runtime =
+ { 0, 0, 0, 0, argp_children_runtime };
+
+/* The final argp parser for startup arguments. */
+struct argp argp_startup =
+ { 0, 0, ARGS_DOC, DOC, argp_children_startup };
diff --git a/options.h b/options.h
new file mode 100644
index 0000000..2e86828
--- /dev/null
+++ b/options.h
@@ -0,0 +1,42 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+/* Argument parsing. */
+
+/* The possible short options. */
+#define OPT_UNDERLYING 'u'
+#define OPT_WRITABLE 'w'
+#define OPT_DEBUG 'd'
+#define OPT_CACHE_SIZE 'c'
+#define OPT_REMOVE 'r'
+
+/* The long options. */
+#define OPT_LONG_UNDERLYING "underlying"
+#define OPT_LONG_WRITABLE "writable"
+#define OPT_LONG_DEBUG "debug"
+#define OPT_LONG_CACHE_SIZE "cache-size"
+#define OPT_LONG_REMOVE "remove"
+
+#define OPT_LONG(o) "--" o
+
+/* The final argp parser for runtime arguments. */
+extern struct argp argp_startup;
+
+/* The final argp parser for startup arguments. */
+extern struct argp argp_runtime;
diff --git a/ulfs.c b/ulfs.c
new file mode 100644
index 0000000..bfcae1e
--- /dev/null
+++ b/ulfs.c
@@ -0,0 +1,189 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+/* Underlying filesystem management. */
+
+#define _GNU_SOURCE
+
+#include <hurd/netfs.h>
+#include <stdlib.h>
+#include <error.h>
+#include <string.h>
+
+#include "ulfs.h"
+
+/* The start of the ulfs chain. */
+ulfs_t *ulfs_chain_start;
+
+/* The end of the ulfs chain, we need this, to go through the chain in
+ reversed order. */
+ulfs_t *ulfs_chain_end;
+
+/* Number of registered underlying filesystems. */
+unsigned int ulfs_num;
+
+/* The lock protecting the ulfs data structures. */
+struct mutex ulfs_lock = MUTEX_INITIALIZER;
+
+/* Create a new ulfs element. */
+error_t
+ulfs_create (char *path, ulfs_t **ulfs)
+{
+ ulfs_t *ulfs_new = malloc (sizeof (ulfs_t));
+ error_t err = 0;
+
+ if (! ulfs_new)
+ err = ENOMEM;
+ else
+ {
+ char *path_cp = path ? strdup (path) : NULL;
+
+ if (path && (! path_cp))
+ {
+ err = ENOMEM;
+ free (ulfs_new);
+ }
+ else
+ {
+ ulfs_new->path = path_cp;
+ ulfs_new->flags = 0;
+ ulfs_new->next = NULL;
+ ulfs_new->prev = NULL;
+ ulfs_new->prevp = NULL;
+ *ulfs = ulfs_new;
+ }
+ }
+ return err;
+}
+
+/* Destroy an ulfs element. */
+void
+ulfs_destroy (ulfs_t *ulfs)
+{
+ free (ulfs->path);
+ free (ulfs);
+}
+
+/* Install ULFS into the linked list of registered filesystems. */
+void
+ulfs_install (ulfs_t *ulfs)
+{
+ ulfs->next = ulfs_chain_start;
+ ulfs->prev = NULL;
+ ulfs->prevp = &ulfs_chain_start;
+ if (ulfs_chain_start)
+ {
+ ulfs_chain_start->prev = ulfs;
+ ulfs_chain_start->prevp = &ulfs->next;
+ }
+ else
+ ulfs_chain_end = ulfs;
+
+ ulfs_chain_start = ulfs;
+}
+
+/* Remove ULFS from the linked list of registered filesystems. */
+void
+ulfs_uninstall (ulfs_t *ulfs)
+{
+ *ulfs->prevp = ulfs->next;
+ if (ulfs->next)
+ {
+ ulfs->next->prev = ulfs->prev;
+ ulfs->next->prevp = &ulfs->next;
+ }
+ else
+ ulfs_chain_end = ulfs->prev;
+}
+
+/* Get an ulfs element by it's index. */
+error_t
+ulfs_get_num (int num, ulfs_t **ulfs)
+{
+ error_t err = EINVAL;
+ ulfs_t *u;
+ int i;
+
+ for (u = ulfs_chain_start, i = 0;
+ u && i < num;
+ u = u->next, i++);
+ if (u)
+ {
+ err = 0;
+ *ulfs = u;
+ }
+ return err;
+}
+
+/* Get an ulfs element by the associated path. */
+error_t
+ulfs_get_path (char *path, ulfs_t **ulfs)
+{
+ error_t err = ENOENT;
+ ulfs_t *u;
+
+ for (u = ulfs_chain_start;
+ u && (! (((! path) && path == u->path)
+ || (path && u->path && (! strcmp (path, u->path)))));
+ u = u->next);
+ if (u)
+ {
+ err = 0;
+ *ulfs = u;
+ }
+ return err;
+}
+
+/* Register a new underlying filesystem. */
+error_t
+ulfs_register (char *path, int flags)
+{
+ ulfs_t *ulfs;
+ error_t err;
+
+ mutex_lock (&ulfs_lock);
+ err = ulfs_create (path, &ulfs);
+ if (! err)
+ {
+ ulfs->flags = flags;
+ ulfs_install (ulfs);
+ ulfs_num++;
+ }
+ mutex_unlock (&ulfs_lock);
+ return err;
+}
+
+/* Unregister an underlying filesystem. */
+error_t
+ulfs_unregister (char *path)
+{
+ ulfs_t *ulfs;
+ error_t err;
+
+ mutex_lock (&ulfs_lock);
+ err = ulfs_get_path (path, &ulfs);
+ if (! err)
+ {
+ ulfs_uninstall (ulfs);
+ ulfs_destroy (ulfs);
+ ulfs_num--;
+ }
+ mutex_unlock (&ulfs_lock);
+ return err;
+}
diff --git a/ulfs.h b/ulfs.h
new file mode 100644
index 0000000..61e5496
--- /dev/null
+++ b/ulfs.h
@@ -0,0 +1,74 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+/* Underlying filesystem management. */
+
+#ifndef INCLUDED_ULFS_H
+#define INCLUDED_ULFS_H
+
+/* The structure for each registered underlying filesystem. */
+typedef struct ulfs
+{
+ char *path;
+ int flags;
+ struct ulfs *next, *prev, **prevp;
+} ulfs_t;
+
+/* Flags. */
+
+/* The according ulfs is marked writable. */
+#define FLAG_ULFS_WRITABLE 0x00000001
+
+/* The start of the ulfs chain. */
+extern ulfs_t *ulfs_chain_start;
+
+/* The end of the ulfs chain, we need this, to go through the chain in
+ reversed order. */
+extern ulfs_t *ulfs_chain_end;
+
+/* Number of registered underlying filesystems. */
+extern unsigned int ulfs_num;
+
+/* The lock protecting the ulfs data structures. */
+extern struct mutex ulfs_lock;
+
+/* Register a new underlying filesystem. */
+error_t ulfs_register (char *path, int flags);
+
+/* Unregister an underlying filesystem. */
+error_t ulfs_unregister (char *path);
+
+/* Get an ULFS element by it's index. */
+error_t ulfs_get_num (int num, ulfs_t **ulfs);
+
+/* Get an ulfs element by the associated path. */
+error_t ulfs_get_path (char *path, ulfs_t **ulfs);
+
+#define ulfs_iterate \
+ for (ulfs_t *ulfs = (mutex_lock (&ulfs_lock), \
+ ulfs_chain_end); \
+ ulfs || (mutex_unlock (&ulfs_lock), 0); \
+ ulfs = ulfs->prev)
+
+#define ulfs_iterate_unlocked \
+ for (ulfs_t *ulfs = ulfs_chain_end; \
+ ulfs; \
+ ulfs = ulfs->prev)
+
+#endif
diff --git a/unionfs.h b/unionfs.h
new file mode 100644
index 0000000..8f5a087
--- /dev/null
+++ b/unionfs.h
@@ -0,0 +1,90 @@
+/* Hurd unionfs
+ Copyright (C) 2001, 2002 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. */
+
+/* This unionfs knows about two different kind of nodes: `light nodes'
+ (in short: lnode) and `nodes' (or: netfs nodes) as used by
+ libnetfs. They have different tasks and therefore this division
+ makes sense.
+
+ lnodes form the filesystem tree as seen by the user; most
+ importantly they contain the `name' of the node.
+
+ lnodes are small and cheap, they are not cached (nodes are).
+
+ lnodes are usually created when a node is looked up and destroyed
+ when that node gets destroyed; but there are also reasons for
+ lnodes _not_ being destroyed.
+
+ The distinction makes it possible to keep certain information for
+ the unionfs in these lnodes while netfs nodes don't have to stay in
+ memory.
+
+ lnodes have to be looked up first before a node is looked up. Each
+ lnode contains a pointer to the netfs node, which might be NULL in
+ case the netfs node is not in memory anymore. */
+
+/* General information and properties for the unionfs. */
+
+#ifndef INCLUDED_UNIONFS_H
+#define INCLUDED_UNIONFS_H
+
+#include <hurd/netfs.h>
+#include <sys/types.h>
+
+#include "node.h"
+#include "lib.h"
+
+/* Default maximum number of nodes in the cache. */
+#define NCACHE_SIZE 256
+
+/* The inode for the root node. */
+#define UNIONFS_ROOT_INODE 1
+
+/* Flags for UNIONFS_FLAGS. */
+
+/* Print debugging messages to stderr. */
+#define FLAG_UNIONFS_MODE_DEBUG 0x00000001
+/* Use copy-on-write. */
+#define FLAG_UNIONFS_MODE_COW 0x00000002
+
+/* Flags describing certain properties of the unionfs. */
+extern int unionfs_flags;
+
+/* The filesystem id (the pid). */
+extern pid_t fsid;
+
+/* Mapped time, used for updating node information. */
+extern volatile struct mapped_time_value *maptime;
+
+/* A port to the underlying node. */
+extern mach_port_t underlying_node;
+
+/* stat information for the underlying node. */
+extern struct stat underlying_node_stat;
+
+/* Send a debugging message, if unionfs is in debugging mode. */
+#define debug_msg(fmt, args...) \
+ do \
+ { \
+ if (unionfs_flags & FLAG_UNIONFS_MODE_DEBUG) \
+ debug_msg_send (fmt , ## args); \
+ } \
+ while (0)
+
+#endif
diff --git a/version.h b/version.h
new file mode 100644
index 0000000..8969db3
--- /dev/null
+++ b/version.h
@@ -0,0 +1,29 @@
+/* Hurd version
+ Copyright (C) 1996, 1997, 1999 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd 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, or (at
+ your option) any later version.
+
+ The GNU Hurd 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, USA. */
+
+#ifndef HURD_VERSION
+#define HURD_VERSION "0.3"
+#endif
+
+/* The standard way to print versions for --version. */
+#define STANDARD_HURD_VERSION(s) \
+ #s " (GNU Hurd) " HURD_VERSION
+#define STANDARD_HURD_VERSION_EXTRA(s, extra) \
+ #s " (GNU Hurd; " extra ") " HURD_VERSION