diff options
author | Moritz Schulte <moritz@duesseldorf.ccc.de> | 2002-12-07 17:47:13 +0000 |
---|---|---|
committer | Moritz Schulte <moritz@duesseldorf.ccc.de> | 2002-12-07 17:47:13 +0000 |
commit | 38bdab8153e5a253552b5e431e7a8f53a6d42285 (patch) | |
tree | 9695c0e0132a1273cbbd67d25c64029777b8c951 |
initial import
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | BUGS | 6 | ||||
-rw-r--r-- | COPYING | 2 | ||||
-rw-r--r-- | Makefile | 64 | ||||
-rw-r--r-- | README | 2 | ||||
-rw-r--r-- | TODO | 7 | ||||
-rw-r--r-- | lib.c | 116 | ||||
-rw-r--r-- | lib.h | 72 | ||||
-rw-r--r-- | lnode.c | 184 | ||||
-rw-r--r-- | lnode.h | 77 | ||||
-rw-r--r-- | main.c | 127 | ||||
-rw-r--r-- | ncache.c | 151 | ||||
-rw-r--r-- | ncache.h | 59 | ||||
-rw-r--r-- | netfs.c | 914 | ||||
-rw-r--r-- | node.c | 416 | ||||
-rw-r--r-- | node.h | 118 | ||||
-rw-r--r-- | options.c | 182 | ||||
-rw-r--r-- | options.h | 42 | ||||
-rw-r--r-- | ulfs.c | 189 | ||||
-rw-r--r-- | ulfs.h | 74 | ||||
-rw-r--r-- | unionfs.h | 90 | ||||
-rw-r--r-- | version.h | 29 |
22 files changed, 2922 insertions, 0 deletions
@@ -0,0 +1 @@ +* Moritz Schulte <moritz@duesseldorf.ccc.de> @@ -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 @@ -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 @@ -0,0 +1,2 @@ +This is an `unionfs' server for the Hurd. The semantics are similar +to BSD's unionfs. @@ -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 @@ -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; +} @@ -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 @@ -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; +} @@ -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 @@ -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 @@ -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; +} @@ -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; +} @@ -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; @@ -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; +} @@ -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 |