summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorStefan Siegl <stesie@brokenpipe.de>2006-01-30 22:37:59 +0000
committerStefan Siegl <stesie@brokenpipe.de>2006-01-30 22:37:59 +0000
commitad506a98d82194fd632409b687a0d7fd61a1eb4c (patch)
tree2eba0f18e696113b9a6667942bfd9bbbb13b5a5d /src
parenta88f3f1d9f48e360d03baef9f7c14e4f6e311f10 (diff)
moved headers into new include/ directory, sources moved into new src/ directory.
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am18
-rw-r--r--src/fuse_i.h225
-rw-r--r--src/main.c363
-rw-r--r--src/netfs.c1572
-rw-r--r--src/netnode.c175
-rw-r--r--src/node.c69
6 files changed, 2422 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
new file mode 100644
index 000000000..4d1cd4d36
--- /dev/null
+++ b/src/Makefile.am
@@ -0,0 +1,18 @@
+# Makefile.am
+# Used by automake and configure to create Makefile.
+#
+# Copyright (C) 2005,2006 Stefan Siegl <stesie@brokenpipe.de>, Germany
+
+lib_LTLIBRARIES = libfuse.la
+
+libfuse_la_LDFLAGS = -version-number 0:0:1
+libfuse_la_SOURCES = \
+ main.c \
+ netfs.c \
+ netnode.c \
+ node.c
+
+noinst_HEADERS = \
+ fuse_i.h
+
+DEFAULT_INCLUDES = -I. -I$(top_srcdir) -I$(top_srcdir)/include \ No newline at end of file
diff --git a/src/fuse_i.h b/src/fuse_i.h
new file mode 100644
index 000000000..e6fd4e43e
--- /dev/null
+++ b/src/fuse_i.h
@@ -0,0 +1,225 @@
+/**********************************************************
+ * fuse_i.h
+ *
+ * Copyright (C) 2004, 2005 by Stefan Siegl <ssiegl@gmx.de>, Germany
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Publice License,
+ * version 2 or any later. The license is contained in the COPYING
+ * file that comes with the libfuse distribution.
+ *
+ * libfuse internals ...
+ */
+
+#ifndef FUSE_INTERNAL_H
+#define FUSE_INTERNAL_H
+
+#ifdef FUSE_USE_VERSION
+# include "fuse.h"
+#else
+# define FUSE_USE_VERSION 23
+# include "fuse.h"
+# include "fuse_compat.h"
+#endif
+
+/* write out message to stderr, that some routine is not yet implemented,
+ * thus misbehaviour must be accepted. */
+#define NOT_IMPLEMENTED() \
+ fprintf(stderr, PACKAGE ": functionality not yet implemented in " \
+ __FILE__ ":%d\nyou're welcome to put your effort to here.\n\n", \
+ __LINE__)
+
+/* pointer to the fuse_operations structure of this translator process */
+extern const struct fuse_operations *fuse_ops;
+extern const struct fuse_operations_compat2 *fuse_ops_compat;
+
+#define FUSE_OP_HAVE(a) ((fuse_ops) ? \
+ (fuse_ops->a != NULL) : (fuse_ops_compat->a != NULL))
+#define FUSE_OP_CALL(a,b...) ((fuse_ops) ? \
+ (fuse_ops->a(b)) : (fuse_ops_compat->a(b)))
+
+/*****************************************************************************
+ *** netnodes (in memory representation of libfuse's files or directories) ***
+ *****************************************************************************/
+struct netnode;
+struct netnode {
+ /* full pathname of the referenced file or directory */
+ char *path;
+
+ /* inode number assigned to the node, unique id that mustn't be
+ * changed - throughout the life of this translator
+ */
+ ino_t inode;
+
+ /* information about the opened file
+ */
+ struct fuse_file_info info;
+
+ /* pointer to our parent's netnode, if any */
+ struct netnode *parent;
+
+ /* reference to our libnetfs node, if any
+ * (lock must be held on read or write attempt)
+ */
+ struct node *node;
+
+ /* lock for *node pointer */
+ struct mutex lock;
+
+ /* whether this is an anonymous node, i.e. it has to be deleted,
+ * after the last node associated to it, was removed.
+ */
+ unsigned anonymous :1;
+
+ /* whether the netnode was touched since reading it from disk,
+ * i.e. the was a write access that may not be synced to disk
+ */
+ unsigned may_need_sync :1;
+};
+
+/* make a new netnode for a specific path, with specified parent
+ * parent == NULL means root node, i.e. "/" ...
+ */
+struct netnode *fuse_make_netnode(struct netnode *parent, const char *path);
+
+/* scan the whole netnode hash and call fuse_ops->fsync on every node,
+ * that may be out of sync
+ */
+error_t fuse_sync_filesystem(void);
+
+
+
+/*****************************************************************************
+ *** nodes (structures used by the GNU Hurd, i.e. libnetfs) ***
+ *****************************************************************************/
+
+/* make a new node for a specific netnode */
+struct node *fuse_make_node(struct netnode *nn);
+
+
+
+/*****************************************************************************
+ *** various parameters which can be used to change libfuse's behaviour ***
+ *****************************************************************************/
+struct _libfuse_params {
+ /* whether or not the filesystem sets the inode number */
+ unsigned use_ino : 1;
+
+ /* whether uid,gid,umask-parameters are specified or not */
+ unsigned force_uid : 1;
+ unsigned force_gid : 1;
+ unsigned force_umask : 1;
+
+ /* whether either root or other users should be allowed to use the
+ * filesystem (this overrides uid/gid/umask checking!) */
+ unsigned deflt_perms : 1;
+ unsigned allow_other : 1;
+ unsigned allow_root : 1;
+
+ /* whether to disable multithreading (if using fuse_main) */
+ unsigned disable_mt : 1;
+
+ /* the uid and gid to set and which umask to apply (if bitfields are set) */
+ uid_t uid;
+ gid_t gid;
+ mode_t umask;
+};
+
+extern struct _libfuse_params libfuse_params;
+
+
+
+/*****************************************************************************
+ *** debug cruft ***
+ *****************************************************************************/
+
+/* the port where to write out debug messages to, NULL to omit these */
+extern FILE *debug_port;
+
+#define DEBUG(cat,msg...) \
+ if(debug_port) \
+ fprintf(debug_port, PACKAGE ": " cat ": " msg)
+
+#define FUNC_PROLOGUE_(func_name, fmt...) \
+ do \
+ { \
+ const char *debug_func_name = func_name; \
+ DEBUG("tracing", "entering %s (" __FILE__ ":%d) ", \
+ debug_func_name, __LINE__); \
+ if(debug_port) \
+ { \
+ fmt; \
+ fprintf(debug_port, "\n"); \
+ }
+
+#define FUNC_PROLOGUE(func_name) \
+ FUNC_PROLOGUE_(func_name, (void)0)
+
+#define FUNC_PROLOGUE_FMT(func_name, fmt...) \
+ FUNC_PROLOGUE_(func_name, fprintf(debug_port, fmt))
+
+#define FUNC_PROLOGUE_NODE(func_name, node) \
+ FUNC_PROLOGUE_FMT(func_name, "node=%s", (node)->nn->path)
+
+#define FUNC_EPILOGUE_NORET() \
+ DEBUG("tracing", "leaving %s\n", debug_func_name); \
+ } while(0);
+
+#define FUNC_RETURN_(ret, fmt) \
+ { \
+ int retval = (ret); \
+ DEBUG("tracing", "leaving %s (" __FILE__ ":%d) ret=%d ", \
+ debug_func_name, __LINE__, retval); \
+ if(debug_port) \
+ { \
+ if(retval) \
+ fprintf(debug_port, "(%s) ", strerror(retval)); \
+ fmt; \
+ fprintf(debug_port, "\n"); \
+ } \
+ return retval; \
+ }
+
+#define FUNC_EPILOGUE_(ret, fmt) \
+ FUNC_RETURN_(ret, fmt) \
+ } while(0)
+
+#define FUNC_RETURN_FMT(ret, fmt...) \
+ FUNC_RETURN_(ret, fprintf(debug_port, fmt))
+
+#define FUNC_EPILOGUE_FMT(ret, fmt...) \
+ FUNC_EPILOGUE_(ret, fprintf(debug_port, fmt))
+
+#define FUNC_RETURN(ret) \
+ FUNC_RETURN_(ret, (void)0)
+
+#define FUNC_EPILOGUE(ret) \
+ FUNC_EPILOGUE_(ret, (void)0)
+
+
+
+/* malloc debugging */
+#if 0
+static char *_strdup(const char *s, const char *f, int l) {
+void *ptr = strdup(s);
+DEBUG("strdup", "ptr=%8p [%s:%d]\n", ptr, f, l);
+return ptr;
+}
+#undef strdup
+#define strdup(s) _strdup(s, __FILE__, __LINE__)
+
+static void *_malloc(size_t sz, const char *f, int l) {
+void *ptr = malloc(sz);
+DEBUG("malloc", "ptr=%8p [%s:%d]\n", ptr, f, l);
+return ptr;
+}
+#define malloc(s) _malloc(s, __FILE__, __LINE__)
+
+static void _free(void *ptr, const char *f, int l) {
+DEBUG(" free", "ptr=%8p [%s:%d]\n", ptr, f, l);
+free(ptr);
+}
+#define free(s) _free(s, __FILE__, __LINE__)
+#endif /* malloc debugging */
+
+#endif /* FUSE_INTERNAL_H */
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 000000000..46ab357d9
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,363 @@
+/**********************************************************
+ * main.c
+ *
+ * Copyright (C) 2004, 2005 by Stefan Siegl <ssiegl@gmx.de>, Germany
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Publice License,
+ * version 2 or any later. The license is contained in the COPYING
+ * file that comes with the libfuse distribution.
+ *
+ * translator startup code (and argp handling)
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <error.h>
+#include <hurd/netfs.h>
+
+#include "fuse_i.h"
+#include "fuse.h"
+
+/* global variables, needed for netfs */
+char *netfs_server_name = PACKAGE;
+char *netfs_server_version = VERSION;
+int netfs_maxsymlinks = 12;
+
+/* libfuse filesystem configuration */
+struct _libfuse_params libfuse_params = { 0 };
+
+/* pointer to the fuse_operations structure of this translator process */
+const struct fuse_operations *fuse_ops = NULL;
+const struct fuse_operations_compat2 *fuse_ops_compat = NULL;
+
+/* the port where to write out debug messages to, NULL to omit these */
+FILE *debug_port = NULL;
+
+/* magic number, passed from fuse_mount to fuse_new */
+#define FUSE_MAGIC ((int) 0x66757365)
+
+
+
+/* Interpret a __single__ mount option
+ * The option itself `opt' may be modified during this call.
+ */
+static int
+fuse_parse_opt(char *opt)
+{
+ char *ptrptr;
+ char *option = strtok_r(opt, "=", &ptrptr);
+ char *value = strtok_r(NULL, "=", &ptrptr);
+
+ DEBUG("parse_opt", "option `%s' => `%s'\n", option, value);
+
+ if(! strcmp(option, "use_ino"))
+ libfuse_params.use_ino = 1;
+
+ else if(! strcmp(option, "default_permissions"))
+ libfuse_params.deflt_perms = 1;
+
+ else if(! strcmp(option, "allow_other"))
+ libfuse_params.allow_other = 1;
+
+ else if(! strcmp(option, "allow_root"))
+ libfuse_params.allow_root = 1;
+
+ else if(! strcmp(option, "uid"))
+ {
+ char *endptr;
+ if(! value || (libfuse_params.uid = strtol(value, &endptr, 10),
+ endptr == value))
+ {
+ fprintf(stderr, PACKAGE_NAME ": missing or invalid argument to "
+ "mount option '%s': %s\n", option, value);
+ return EINVAL;
+ }
+
+ libfuse_params.force_uid = 1;
+ }
+
+ else if(! strcmp(option, "gid"))
+ {
+ char *endptr;
+ if(! value || (libfuse_params.gid = strtol(value, &endptr, 10),
+ endptr == value))
+ {
+ fprintf(stderr, PACKAGE_NAME ": missing or invalid argument to "
+ "mount option '%s': %s\n", option, value);
+ return EINVAL;
+ }
+
+ libfuse_params.force_gid = 1;
+ }
+
+ else if(! strcmp(option, "umask"))
+ {
+ char *endptr;
+ if(! value || (libfuse_params.umask = strtol(value, &endptr, 8),
+ endptr == value))
+ {
+ fprintf(stderr, PACKAGE_NAME ": missing or invalid argument to "
+ "mount option '%s': %s\n", option, value);
+ return EINVAL;
+ }
+
+ libfuse_params.force_umask = 1;
+ }
+
+ else
+ {
+ fprintf(stderr, PACKAGE_NAME ": unsupported mount option: %s\n", option);
+ return EINVAL;
+ }
+
+ return 0;
+}
+
+/* Parse a single (or a comma-separated list) of mount options
+ * (those that are specified using `-o' on the command line for example)
+ */
+static int
+fuse_parse_opts(const char *opts)
+{
+ if(! opts) return 0; /* ... why did'ya call us? */
+
+ char *copy = strdup(opts); /* copy string to allow strtok calls */
+ if(! copy) return ENOMEM;
+
+ char *ptrptr, *token, *tok_me = copy;
+ while((token = strtok_r(tok_me, ",", &ptrptr)))
+ {
+ tok_me = NULL;
+ if(fuse_parse_opt(token))
+ {
+ free(copy);
+ return EINVAL;
+ }
+ }
+
+ free(copy);
+ return 0;
+}
+
+
+
+/* Parse the command line arguments given to fuse_main (or _compat function).
+ * If --help specified, output help string and call exit. If there are any
+ * options that shall be passed to the file system itself, return them.
+ */
+static void
+fuse_parse_argv(int argc, char *argv[])
+{
+ const char *translat_path = argv[0];
+
+ /* parse command line arguments */
+ int opt;
+ FILE *opt_help = NULL;
+
+ while((opt = getopt(argc, argv, "d::o:hs")) >= 0)
+ switch(opt)
+ {
+ case 'd':
+ if(optarg)
+ debug_port = fopen(optarg, "w");
+ if(! debug_port)
+ debug_port = stderr;
+
+ setvbuf(debug_port, NULL, _IONBF, 0);
+ fprintf(debug_port, "translator %s starting up.\n", translat_path);
+ break;
+
+ case 'o':
+ assert(optarg);
+ if(fuse_parse_opts(optarg))
+ exit(1);
+ break;
+
+ case 'h':
+ opt_help = stdout;
+ break;
+
+ case 's':
+ libfuse_params.disable_mt = 1;
+ break;
+
+ case '?':
+ default:
+ opt_help = stderr;
+ break;
+ }
+
+ if(opt_help)
+ {
+ fprintf(opt_help,
+ "usage: %s [FUSE options]\n\n"
+ "FUSE Options:\n"
+ " -d[FILENAME] "
+ "enable debug output (default=stderr)\n"
+ " -s disable multi-threaded operation\n"
+ /* " -r "
+ * "mount read only (equivalent to '-o ro')\n" */
+ " -o opt,[opt...] mount options\n"
+ " -h print help\n"
+ "\n"
+ "Mount options:\n"
+ " default_permissions enable permission checking\n"
+ " allow_other allow access to other users\n"
+ " allow_root allow access to root\n"
+ " use_ino let filesystem set inode numbers\n"
+ /* " readdir_ino "
+ * "try to fill in d_ino in readdir\n" */
+ " umask set file permissions (octal)\n"
+ " uid set file owner\n"
+ " gid set file group\n",
+ translat_path);
+
+ exit(opt_help == stdout ? 0 : 1);
+ }
+}
+
+
+
+/* Main function of FUSE. (compatibility one for old Fuse API) */
+int
+fuse_main_compat2(int argc, char *argv[],
+ const struct fuse_operations_compat2 *op)
+{
+ fuse_parse_argv(argc, argv);
+
+ int fd = fuse_mount(NULL, NULL);
+ return (libfuse_params.disable_mt ? fuse_loop : fuse_loop_mt)
+ (fuse_new_compat2(fd, NULL, op));
+}
+
+
+
+/* Main function of FUSE.
+ * named fuse_main_real, since originial fuse.h defines a macro renaming it */
+int
+fuse_main_real(int argc, char *argv[],
+ const struct fuse_operations *op, size_t op_size)
+{
+ fuse_parse_argv(argc, argv);
+
+ int fd = fuse_mount(NULL, NULL);
+ return (libfuse_params.disable_mt ? fuse_loop : fuse_loop_mt)
+ (fuse_new(fd, NULL, op, op_size));
+}
+
+
+
+/* Create a new FUSE filesystem, actually there's nothing for us to do
+ * on the Hurd.
+ *
+ * (Compatibility function for the old Fuse API)
+ */
+struct fuse *
+fuse_new_compat2(int fd, const char *opts,
+ const struct fuse_operations_compat2 *op)
+{
+ if(fd != FUSE_MAGIC)
+ return NULL;
+
+ if(fuse_parse_opts(opts))
+ return NULL;
+
+ fuse_ops_compat = op;
+
+ return (void *) FUSE_MAGIC; /* we don't have a fuse structure, sorry. */
+}
+
+
+
+/* Create a new FUSE filesystem, actually there's nothing for us to do
+ * on the Hurd.
+ */
+struct fuse *
+fuse_new(int fd, const char *opts,
+ const struct fuse_operations *op, size_t op_size)
+{
+ (void) op_size; /* FIXME, see what the real Fuse library does with
+ * this argument */
+
+ if(fd != FUSE_MAGIC)
+ return NULL;
+
+ if(fuse_parse_opts(opts))
+ return NULL;
+
+ fuse_ops = op;
+
+ return (void *) FUSE_MAGIC; /* we don't have a fuse structure, sorry. */
+}
+
+
+
+/* Create a new mountpoint for our fuse filesystem, i.e. do the netfs
+ * initialization stuff ...
+ */
+int
+fuse_mount(const char *mountpoint, const char *opts)
+{
+ (void) mountpoint; /* we don't care for the specified mountpoint, as
+ * we need to be set up using settrans ... */
+
+ if(fuse_parse_opts(opts))
+ return 0;
+
+ mach_port_t bootstrap, ul_node;
+
+ /* netfs initialization. */
+
+ task_get_bootstrap_port(mach_task_self(), &bootstrap);
+
+ netfs_init();
+
+ ul_node = netfs_startup(bootstrap, 0);
+
+ /* create our root node */
+ {
+ struct netnode *root = fuse_make_netnode(NULL, "/");
+ netfs_root_node = fuse_make_node(root);
+ }
+
+ if(! netfs_root_node)
+ {
+ perror(PACKAGE ": cannot create rootnode");
+ return -EAGAIN;
+ }
+
+ return FUSE_MAGIC;
+}
+
+
+
+int
+fuse_loop(struct fuse *f)
+{
+ if(f != ((void *) FUSE_MAGIC))
+ return -1;
+
+ static int server_timeout = 1000 * 60 * 10; /* ten minutes, just like in
+ * init-loop.c of libnetfs */
+
+ ports_manage_port_operations_one_thread(netfs_port_bucket,
+ netfs_demuxer,
+ server_timeout);
+ return 0;
+}
+
+
+int
+fuse_loop_mt(struct fuse *f)
+{
+ if(f != ((void *) FUSE_MAGIC))
+ return -1;
+
+ netfs_server_loop();
+ return 0;
+}
diff --git a/src/netfs.c b/src/netfs.c
new file mode 100644
index 000000000..0fdb5c4a6
--- /dev/null
+++ b/src/netfs.c
@@ -0,0 +1,1572 @@
+/**********************************************************
+ * netfs.c
+ *
+ * Copyright(C) 2004, 2005 by Stefan Siegl <ssiegl@gmx.de>, Germany
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Publice License,
+ * version 2 or any later. The license is contained in the COPYING
+ * file that comes with the libfuse distribution.
+ *
+ * callback functions for libnetfs
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include <hurd/netfs.h>
+
+#include <stdio.h>
+
+#include "fuse_i.h"
+#include "fuse.h"
+
+
+
+
+
+/* fuse_dirhandle, passed to ops->getdir to store our information */
+struct fuse_dirhandle {
+ int first_entry; /* index of first entry to return (counted down
+ * in helper function) */
+ int num_entries; /* number of further entries we may write out to the
+ * buffer, counted down in callback function*/
+ int count; /* number of elements, we already wrote to buffer */
+ size_t size; /* number of further bytes we may write to buffer */
+ struct dirent *hdrpos; /* where to write next dirent structure */
+ size_t maxlen; /* (allocated) length of filename field, def 256 */
+ struct netnode *parent; /* netnode of the dir which's content we list */
+
+ char *abspath;
+ char *filename;
+};
+
+/* Check whether to allow access to a node, testing the allow_root and
+ * allow_other flag. This does not check whether default permissions
+ * are okay to allow access.
+ *
+ * Return: 0 if access is to be granted, EPERM otherwise
+ *
+ * Sidenote: This function is mainly called from netfs_validate_stat which in
+ * turn is called by any other of the netfs-operation-functions. This
+ * is, we don't have to call this from all of the operation-functions
+ * since netfs_validate_stat is called anyways!
+ */
+static error_t
+test_allow_root_or_other (struct iouser *cred)
+{
+ FUNC_PROLOGUE("test_allow_root_or_other");
+
+ /* if allow_other is set, access is okay in any case */
+ if(libfuse_params.allow_other)
+ FUNC_RETURN_FMT(0, "allow_other is set");
+
+ unsigned int i;
+ uid_t proc_uid = getuid();
+ for(i = 0; i < cred->uids->num; i ++)
+ {
+ DEBUG("test_allow", "testing for uid=%d\n", cred->uids->ids[i]);
+
+ if(cred->uids->ids[i] == 0 && libfuse_params.allow_root)
+ FUNC_RETURN_FMT(0, "allowing access for root");
+
+ if(cred->uids->ids[i] == proc_uid)
+ FUNC_RETURN_FMT(0, "allowing access for owner");
+ }
+
+ /* iouser is not the "filesystem-owner" and allow_other is not set,
+ * or iouser is root but allow_root is not set either */
+ FUNC_EPILOGUE(EPERM);
+}
+
+
+
+/* Make sure that NP->nn_stat is filled with current information. CRED
+ identifies the user responsible for the operation.
+
+ If the user `CRED' is not allowed to perform any operation (considering
+ the allow_root and allow_other flags), return EPERM. */
+error_t
+netfs_validate_stat (struct node *node, struct iouser *cred)
+{
+ FUNC_PROLOGUE_NODE("netfs_validate_stat", node);
+ error_t err = EOPNOTSUPP;
+
+ if(test_allow_root_or_other(cred))
+ FUNC_RETURN(EPERM);
+
+ if(FUSE_OP_HAVE(getattr))
+ err = -FUSE_OP_CALL(getattr, node->nn->path, &node->nn_stat);
+
+ if(! err)
+ {
+ if(! libfuse_params.use_ino)
+ node->nn_stat.st_ino = node->nn->inode;
+
+ node->nn_stat.st_dev = getpid();
+ node->nn_stat.st_blksize = 1 << 12; /* there's probably no sane default,
+ * use 4 kB for the moment */
+
+ if(libfuse_params.force_uid)
+ node->nn_stat.st_uid = libfuse_params.uid;
+
+ if(libfuse_params.force_gid)
+ node->nn_stat.st_gid = libfuse_params.gid;
+
+ if(libfuse_params.force_umask)
+ node->nn_stat.st_mode &= ~libfuse_params.umask;
+ }
+
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Read the contents of NODE (a symlink), for USER, into BUF. */
+error_t netfs_attempt_readlink (struct iouser *user, struct node *node,
+ char *buf)
+{
+ FUNC_PROLOGUE_NODE("netfs_attempt_readlink", node);
+ error_t err;
+
+ if((err = netfs_validate_stat(node, user))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_access(&node->nn_stat, S_IREAD, user))))
+ goto out;
+
+ if(FUSE_OP_HAVE(readlink))
+ err = -FUSE_OP_CALL(readlink, node->nn->path, buf, INT_MAX);
+ else
+ err = EOPNOTSUPP;
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE
+ to the new node upon return. On any error, clear *NODE. *NODE 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 **node)
+{
+ FUNC_PROLOGUE("netfs_attempt_create_file");
+ error_t err;
+ char *path = NULL;
+
+ if((err = netfs_validate_stat(dir, user))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_checkdirmod(&dir->nn_stat, NULL, user))))
+ goto out;
+
+ if(! FUSE_OP_HAVE(mknod))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ if(! (path = malloc(strlen(dir->nn->path) + strlen(name) + 2)))
+ {
+ err = ENOMEM;
+ goto out;
+ }
+
+ sprintf(path, "%s/%s", dir->nn->path, name);
+
+ /* FUSE expects us to use mknod function to create files. This is allowed
+ * on Linux, however neither the Hurd nor the POSIX standard consider that
+ */
+ err = -FUSE_OP_CALL(mknod, path, (mode & ALLPERMS) | S_IFREG, 0);
+
+ out:
+ if(err)
+ *node = NULL;
+ else
+ {
+ /* create a new (net-)node for this file */
+ struct netnode *nn = fuse_make_netnode(dir->nn, path);
+
+ if(nn)
+ {
+ nn->may_need_sync = 1;
+ *node = fuse_make_node(nn);
+ }
+ else
+ {
+ *node = NULL;
+ err = ENOMEM;
+ }
+ }
+
+ free(path); /* fuse_make_netnode strdup'ed it. */
+
+ if(*node)
+ mutex_lock(&(*node)->lock);
+
+ mutex_unlock (&dir->lock);
+
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* This should attempt a chmod call for the user specified by CRED on node
+ NODE, to change the owner to UID and the group to GID. */
+error_t netfs_attempt_chown (struct iouser *cred, struct node *node,
+ uid_t uid, uid_t gid)
+{
+ FUNC_PROLOGUE_NODE("netfs_attempt_chown", node);
+ error_t err;
+
+ if((err = netfs_validate_stat(node, cred))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_isowner(&node->nn_stat, cred))))
+ goto out;
+
+ if(! FUSE_OP_HAVE(chown))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ /* FIXME, make sure, that user CRED is not able to change permissions
+ * to somebody who is not. That is, don't allow $unpriv_user to change
+ * owner to e.g. root.
+ */
+
+ err = -FUSE_OP_CALL(chown, node->nn->path, uid, gid);
+ node->nn->may_need_sync = 1;
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* This should attempt to fetch filesystem status information for the remote
+ filesystem, for the user CRED. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *node,
+ fsys_statfsbuf_t *st)
+{
+ FUNC_PROLOGUE_NODE("netfs_attempt_statfs", node);
+ error_t err;
+
+ if(test_allow_root_or_other(cred))
+ err = EPERM;
+
+ else if(FUSE_OP_HAVE(statfs))
+ err = -FUSE_OP_CALL(statfs, node->nn->path, st);
+
+ else
+ err = EOPNOTSUPP;
+
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Attempt to create a new directory named NAME in DIR for USER with mode
+ MODE. */
+error_t netfs_attempt_mkdir (struct iouser *user, struct node *dir,
+ char *name, mode_t mode)
+{
+ FUNC_PROLOGUE("netfs_attempt_mkdir");
+ error_t err;
+ char *path = NULL;
+
+ if((err = netfs_validate_stat(dir, user))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_checkdirmod(&dir->nn_stat, NULL, user))))
+ goto out;
+
+ if(! FUSE_OP_HAVE(mkdir))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ if(! (path = malloc(strlen(dir->nn->path) + strlen(name) + 2)))
+ {
+ err = ENOMEM;
+ goto out;
+ }
+
+ sprintf(path, "%s/%s", dir->nn->path, name);
+
+ err = -FUSE_OP_CALL(mkdir, path, mode & ALLPERMS);
+
+ out:
+ /* we don't need to make a netnode already, lookup will be called and do
+ * that for us.
+ *
+ * FIXME we must make sure, that the netnode will have may_need_sync is set
+ */
+ free(path);
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* This should attempt a chflags call for the user specified by CRED on node
+ NODE, to change the flags to FLAGS. */
+error_t netfs_attempt_chflags (struct iouser *cred, struct node *node,
+ int flags)
+{
+ (void) cred;
+ (void) flags;
+
+ FUNC_PROLOGUE_NODE("netfs_attempt_chflags", node);
+ NOT_IMPLEMENTED();
+ FUNC_EPILOGUE(EOPNOTSUPP);
+}
+
+
+
+/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we
+ just created this node. Return an error if we should not permit the open
+ to complete because of a permission restriction. */
+error_t
+netfs_check_open_permissions (struct iouser *user, struct node *node,
+ int flags, int newnode)
+{
+ (void) newnode;
+
+ FUNC_PROLOGUE_FMT("netfs_check_open_permissions", "node=%s, flags=%d",
+ node->nn->path, flags);
+ error_t err = 0;
+
+ if((err = netfs_validate_stat(node, user)))
+ goto out;
+
+ if(libfuse_params.deflt_perms)
+ {
+ if (flags & O_READ)
+ err = fshelp_access (&node->nn_stat, S_IREAD, user);
+
+ if (!err && (flags & O_WRITE))
+ err = fshelp_access (&node->nn_stat, S_IWRITE, user);
+
+ if (!err && (flags & O_EXEC))
+ err = fshelp_access (&node->nn_stat, S_IEXEC, user);
+ }
+
+ /* store provided flags for later open/read/write/release operation call.
+ *
+ * If O_EXEC is set, make sure O_RDONLY is set, this is, if you want to
+ * execute a binary, only O_EXEC is set, but we want to read the binary
+ * into memory. */
+ if(! err)
+ {
+ node->nn->info.flags = flags;
+ if(flags & O_EXEC) node->nn->info.flags |= O_RDONLY;
+ }
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* This should attempt a chmod call for the user specified by CRED on 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 *node,
+ mode_t mode)
+{
+ FUNC_PROLOGUE_NODE("netfs_attempt_chmod", node);
+ error_t err;
+
+ if((err = netfs_validate_stat(node, cred))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_isowner(&node->nn_stat, cred))))
+ goto out;
+
+ if(! FUSE_OP_HAVE(chmod))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ err = -FUSE_OP_CALL(chmod, node->nn->path, mode);
+ node->nn->may_need_sync = 1;
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Attempt to create an anonymous file related to DIR for USER with MODE.
+ Set *NODE 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 **node)
+{
+ FUNC_PROLOGUE("netfs_attempt_mkfile");
+ error_t err;
+ char name[20];
+ static int num = 0;
+
+ if(! (err = netfs_validate_stat(dir, user))
+ && libfuse_params.deflt_perms)
+ err = fshelp_checkdirmod(&dir->nn_stat, NULL, user);
+
+ if(! err && ! FUSE_OP_HAVE(mknod))
+ err = EOPNOTSUPP;
+
+ if(err)
+ {
+ /* dir has to be unlocked no matter what ... */
+ mutex_unlock (&dir->lock);
+ goto out;
+ }
+
+ /* call netfs_attempt_create_file with O_EXCL and O_CREAT bits set */
+ mode |= O_EXCL | O_CREAT;
+
+ do
+ {
+ snprintf(name, sizeof(name), ".libfuse-%06d", num ++);
+ err = netfs_attempt_create_file(user, dir, name, mode, node);
+
+ if(err == EEXIST)
+ mutex_lock(&dir->lock); /* netfs_attempt_create_file just unlocked
+ * it for us, however we need to call it once
+ * more ...
+ */
+ }
+ while(err == EEXIST);
+
+ out:
+ if(err)
+ *node = 0;
+ else
+ (*node)->nn->anonymous = 1; /* mark netnode of created file as anonymous,
+ * i.e. mark it as to delete in noref routine
+ */
+
+ /* we don't have to mark the netnode as may_need_sync, as
+ * netfs_attempt_create_file has already done that for us
+ */
+
+ /* mutex_unlock (&dir->lock);
+ * netfs_attempt_create_file already unlocked the node for us.
+ */
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* This should sync the entire remote filesystem. If WAIT is set, return
+ only after sync is completely finished. */
+error_t netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ (void) wait; /* there's no such flag in libfuse */
+
+ FUNC_PROLOGUE("netfs_attempt_syncfs");
+ error_t err;
+
+ if(test_allow_root_or_other(cred))
+ err = EPERM;
+ else
+ err = fuse_sync_filesystem();
+
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* This should sync the file NODE completely to disk, for the user CRED. If
+ WAIT is set, return only after sync is completely finished. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *node, int wait)
+{
+ (void) wait; /* there's no such flag in libfuse */
+
+ FUNC_PROLOGUE_NODE("netfs_attempt_sync", node);
+ error_t err;
+
+ if((err = netfs_validate_stat(node, cred))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_access(&node->nn_stat, S_IWRITE, cred))))
+ goto out;
+
+ if(! FUSE_OP_HAVE(fsync))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ if(fuse_ops)
+ err = -fuse_ops->fsync(node->nn->path, 0, &node->nn->info);
+ else
+ err = -fuse_ops_compat->fsync(node->nn->path, 0);
+
+ if(! err)
+ node->nn->may_need_sync = 0;
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Delete NAME in DIR for USER.
+ * The node DIR is locked, and shall stay locked
+ */
+error_t netfs_attempt_unlink (struct iouser *user, struct node *dir,
+ char *name)
+{
+ FUNC_PROLOGUE("netfs_attempt_unlink");
+ error_t err;
+ struct node *node = NULL;
+
+ err = netfs_attempt_lookup(user, dir, name, &node);
+ assert(dir != node);
+ mutex_lock(&dir->lock); /* re-lock directory, since netfs_attempt_lookup
+ * unlocked it for us
+ */
+ if(err)
+ goto out;
+
+ mutex_unlock(&node->lock);
+
+ if((err = netfs_validate_stat(dir, user))
+ || (err = netfs_validate_stat(node, user))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_checkdirmod(&dir->nn_stat, &node->nn_stat, user))))
+ goto out;
+
+ if(FUSE_OP_HAVE(unlink))
+ err = -FUSE_OP_CALL(unlink, node->nn->path);
+ else
+ err = EOPNOTSUPP;
+
+ /* TODO free associated netnode. really?
+ * FIXME, make sure nn->may_need_sync is set */
+ out:
+ if(node)
+ netfs_nrele(node);
+
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* This should attempt to set the size of the file NODE (for user CRED) to
+ SIZE bytes long. */
+error_t netfs_attempt_set_size (struct iouser *cred, struct node *node,
+ loff_t size)
+{
+ FUNC_PROLOGUE_NODE("netfs_attempt_set_size", node);
+ error_t err;
+
+ if((err = netfs_validate_stat(node, cred))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_access(&node->nn_stat, S_IWRITE, cred))))
+ goto out;
+
+ if(FUSE_OP_HAVE(truncate))
+ err = -FUSE_OP_CALL(truncate, node->nn->path, size);
+ else
+ err = EOPNOTSUPP;
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or
+ S_IFCHR. */
+error_t netfs_attempt_mkdev (struct iouser *cred, struct node *node,
+ mode_t type, dev_t indexes)
+{
+ FUNC_PROLOGUE_NODE("netfs_attempt_mkdev", node);
+ error_t err;
+
+ /* check permissions
+ * XXX, shall we check permissions of the parent directory as well,
+ * since we're going to unlink files?
+ */
+ if((err = netfs_validate_stat(node, cred))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_access(&node->nn_stat, S_IWRITE, cred))))
+ goto out;
+
+ /* check whether the operations are available at all */
+ if(! FUSE_OP_HAVE(mknod))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ /* we need to unlink the existing node, therefore, if unlink is not
+ * available, we cannot turn *node into a device.
+ */
+ if(! FUSE_OP_HAVE(unlink))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ /* unlink the already existing node, to be able to create the (new)
+ * device file
+ */
+ if((err = -FUSE_OP_CALL(unlink, node->nn->path)))
+ goto out;
+
+ err = -FUSE_OP_CALL(mknod, node->nn->path,
+ type & (ALLPERMS | S_IFBLK | S_IFCHR), indexes);
+
+ node->nn->may_need_sync = 1;
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC)
+ in *TYPES for file NODE and user CRED. */
+error_t
+netfs_report_access (struct iouser *cred, struct node *node, int *types)
+{
+ FUNC_PROLOGUE_NODE("netfs_report_access", node);
+ error_t err;
+ *types = 0;
+
+ if((err = netfs_validate_stat(node, cred)))
+ goto out;
+
+ if(libfuse_params.deflt_perms)
+ {
+ if(fshelp_access (&node->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+
+ if(fshelp_access (&node->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+
+ if(fshelp_access (&node->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ }
+ else
+ /* check_allow_root_or_other allowed to get here, this is, we're
+ * allowed to access the file. Default permissions checking is disabled,
+ * thus, grant access
+ */
+ *types |= O_READ | O_WRITE | O_EXEC;
+
+ out:
+ FUNC_EPILOGUE(0);
+}
+
+
+/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If
+ the name was not found, then return ENOENT. On any error, clear *NODE.
+ (*NODE, if found, should be locked, this call should unlock DIR no matter
+ what.) */
+error_t netfs_attempt_lookup (struct iouser *user, struct node *dir,
+ char *name, struct node **node)
+{
+ FUNC_PROLOGUE_FMT("netfs_attempt_lookup", "name=%s, dir=%s",
+ name, dir->nn->path);
+
+ error_t err;
+
+ if((err = netfs_validate_stat(dir, user))
+ || (libfuse_params.deflt_perms
+ && ((err = fshelp_access(&dir->nn_stat, S_IREAD, user))
+ || (err = fshelp_access(&dir->nn_stat, S_IEXEC, user)))))
+ goto out;
+ else
+ err = ENOENT; /* default to return ENOENT */
+
+ if(! strcmp(name, "."))
+ {
+ /* lookup for current directory, return another refernce to it */
+ netfs_nref(dir);
+ *node = dir;
+ err = 0; /* it's alright ... */
+ }
+
+ else if(! strcmp(name, ".."))
+ {
+ if(dir->nn->parent)
+ {
+ /* okay, there is a parent directory, return a reference to that */
+ *node = fuse_make_node(dir->nn->parent);
+ err = 0;
+ }
+ else
+ /* cannot go up from top directory */
+ err = EAGAIN;
+ }
+
+ else if(FUSE_OP_HAVE(getattr))
+ {
+ /* lookup for common file */
+ struct netnode *nn;
+ char *path;
+ struct stat stbuf;
+
+ if(asprintf(&path, "%s/%s",
+ dir->nn->parent ? dir->nn->path : "", name) < 0)
+ {
+ err = ENOMEM;
+ goto out;
+ }
+
+ if(! (err = -FUSE_OP_CALL(getattr, path, &stbuf)))
+ if(! (nn = fuse_make_netnode(dir->nn, path)) ||
+ ! (*node = fuse_make_node(nn)))
+ err = ENOMEM;
+
+ free(path); /* fuse_make_netnode strdup()s the pathname */
+ }
+
+out:
+ mutex_unlock(&dir->lock);
+
+ if(err)
+ *node = NULL;
+ else
+ mutex_lock(&(*node)->lock);
+
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* 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, but
+ 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)
+{
+ FUNC_PROLOGUE_FMT("netfs_attempt_link", "link=%s/%s, to=%s",
+ dir->nn->path, name, file->nn->path);
+ error_t err;
+ struct node *node = NULL;
+
+ mutex_lock(&dir->lock);
+
+ if((err = netfs_attempt_lookup(user, dir, name, &node)))
+ goto out_nounlock; /* netfs_attempt_lookup unlocked dir */
+
+ assert(dir != node);
+ mutex_lock(&dir->lock);
+
+ if((err = netfs_validate_stat(node, user))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_checkdirmod(&dir->nn_stat, &node->nn_stat, user))))
+ goto out;
+
+ if(! FUSE_OP_HAVE(link)) {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ if(! excl && FUSE_OP_HAVE(unlink))
+ /* EXCL is not set, therefore we may remove the target, i.e. call
+ * unlink on it. Ignoring return value, as it's mostly not interesting,
+ * since the file does not exist in most case
+ */
+ (void) FUSE_OP_CALL(unlink, node->nn->path);
+
+ err = -FUSE_OP_CALL(link, file->nn->path, node->nn->path);
+
+ /* TODO
+ * create a netnode with the may_need_sync flag set!! */
+
+ out:
+ mutex_unlock(&dir->lock);
+
+ mutex_unlock(&node->lock);
+ netfs_nrele(node);
+
+ out_nounlock:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Attempt to remove directory named NAME in DIR for USER.
+ * directory DIR is locked, and shall stay locked. */
+error_t netfs_attempt_rmdir (struct iouser *user,
+ struct node *dir, char *name)
+{
+ FUNC_PROLOGUE("netfs_attempt_rmdir");
+ error_t err;
+ struct node *node = NULL;
+
+ err = netfs_attempt_lookup(user, dir, name, &node);
+ assert(dir != node);
+ mutex_lock(&dir->lock); /* netfs_attempt_lookup unlocked dir */
+
+ if(err)
+ goto out_nounlock;
+
+ if((err = netfs_validate_stat(node, user))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_checkdirmod(&dir->nn_stat, &node->nn_stat, user))))
+ goto out;
+
+ if(FUSE_OP_HAVE(rmdir))
+ err = -FUSE_OP_CALL(rmdir, node->nn->path);
+ else
+ err = EOPNOTSUPP;
+
+
+ /* TODO free associated netnode. really?
+ * FIXME, make sure nn->may_need_sync is set */
+
+ out:
+ mutex_unlock(&node->lock);
+ netfs_nrele(node);
+
+ out_nounlock:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* This should attempt a chauthor call for the user specified by CRED on node
+ NODE, to change the author to AUTHOR. */
+error_t netfs_attempt_chauthor (struct iouser *cred, struct node *node,
+ uid_t author)
+{
+ (void) cred;
+ (void) author;
+
+ FUNC_PROLOGUE_NODE("netfs_attempt_chauthor", node);
+ NOT_IMPLEMENTED();
+ FUNC_EPILOGUE(EROFS);
+}
+
+
+
+/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */
+error_t netfs_attempt_mksymlink (struct iouser *cred, struct node *node,
+ char *name)
+{
+ FUNC_PROLOGUE_NODE("netfs_attempt_mksymlink", node);
+ error_t err;
+
+ /* check permissions
+ * XXX, shall we check permissions of the parent directory as well,
+ * since we're going to unlink files?
+ */
+ if((err = netfs_validate_stat(node, cred))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_access(&node->nn_stat, S_IWRITE, cred))))
+ goto out;
+
+ /* we need to unlink the existing node, therefore, if unlink is not
+ * available, we cannot create symlinks
+ */
+ if(! FUSE_OP_HAVE(unlink))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ /* symlink function available? if not, fail. */
+ if(! FUSE_OP_HAVE(symlink))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ /* try to remove the existing node (probably an anonymous file) */
+ if((err = -FUSE_OP_CALL(unlink, node->nn->path)))
+ goto out;
+
+ err = -FUSE_OP_CALL(symlink, name, node->nn->path);
+
+ /* we don't have to adjust nodes/netnodes, as these are already existing.
+ * netfs_attempt_mkfile did that for us.
+ */
+ if(! err)
+ node->nn->may_need_sync = 1;
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Note that in this one call, 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)
+{
+ FUNC_PROLOGUE("netfs_attempt_rename");
+ error_t err;
+ struct node *fromnode;
+ char *topath = NULL;
+
+ if(! (topath = malloc(strlen(toname) + strlen(todir->nn->path) + 2)))
+ {
+ err = ENOMEM;
+ goto out_nounlock;
+ }
+
+ mutex_lock(&fromdir->lock);
+
+ if(netfs_attempt_lookup(user, fromdir, fromname, &fromnode))
+ {
+ err = ENOENT;
+ goto out_nounlock; /* netfs_attempt_lookup unlocked fromdir and locked
+ * fromnode for us.
+ */
+ }
+
+ mutex_lock(&todir->lock);
+
+ if((err = netfs_validate_stat(fromdir, user))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_checkdirmod(&fromdir->nn_stat,
+ &fromnode->nn_stat, user))))
+ goto out;
+
+ if((err = netfs_validate_stat(todir, user))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_checkdirmod(&todir->nn_stat, NULL, user))))
+ goto out;
+
+ if(! FUSE_OP_HAVE(rename))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ sprintf(topath, "%s/%s", todir->nn->path, toname);
+
+ if(! excl && FUSE_OP_HAVE(unlink))
+ /* EXCL is not set, therefore we may remove the target, i.e. call
+ * unlink on it. Ignoring return value, as it's mostly not interesting,
+ * since the file does not exist in most cases
+ */
+ (void) FUSE_OP_CALL(unlink, topath);
+
+ err = -FUSE_OP_CALL(rename, fromnode->nn->path, topath);
+
+ if(! err)
+ {
+ struct netnode *nn;
+
+ if(! (nn = fuse_make_netnode(todir->nn, topath)))
+ goto out;
+
+ nn->may_need_sync = 1;
+ /* FIXME mark fromnode to destroy it's associated netnode */
+ }
+
+ out:
+ free(topath);
+ mutex_unlock(&todir->lock);
+
+ mutex_unlock(&fromnode->lock);
+ netfs_nrele(fromnode);
+
+ out_nounlock:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Write to the file NODE for user CRED starting at OFSET and continuing for up
+ to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon
+ return. */
+error_t netfs_attempt_write (struct iouser *cred, struct node *node,
+ loff_t offset, size_t *len, void *data)
+{
+ FUNC_PROLOGUE_NODE("netfs_attempt_write", node);
+ error_t err;
+
+ if((err = netfs_validate_stat(node, cred))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_access(&node->nn_stat, S_IWRITE, cred))))
+ goto out;
+
+ if(! FUSE_OP_HAVE(write))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ node->nn->info.writepage = 0; /* cannot distinct on the Hurd :( */
+
+ if(fuse_ops && fuse_ops->open)
+ if((err = fuse_ops->open(node->nn->path, &node->nn->info)))
+ goto out;
+
+ int sz = fuse_ops ?
+ (fuse_ops->write(node->nn->path, data, *len, offset, &node->nn->info)) :
+ (fuse_ops_compat->write(node->nn->path, data, *len, offset));
+
+ /* FIXME: open, flush and release handling probably should be changed
+ * completely, I mean, we probably should do fuse_ops->open in
+ * the netfs_check_open_permissions function and leave it open
+ * until the node is destroyed.
+ *
+ * This way we wouldn't be able to report any errors back.
+ */
+ if(sz >= 0 && fuse_ops && fuse_ops->flush)
+ err = fuse_ops->flush(node->nn->path, &node->nn->info);
+
+ if(fuse_ops && fuse_ops->open && fuse_ops->release)
+ fuse_ops->release(node->nn->path, &node->nn->info);
+
+ if(sz < 0)
+ err = -sz;
+ else
+ *len = sz;
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* This should attempt a utimes call for the user specified by CRED on node
+ NODE, to change the atime to ATIME and the mtime to MTIME. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *node,
+ struct timespec *atime, struct timespec *mtime)
+{
+ FUNC_PROLOGUE_NODE("netfs_attempt_utimes", node);
+ error_t err;
+
+ /* test whether operation is supported and permission are sufficient */
+ if((err = netfs_validate_stat(node, cred))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_isowner(&node->nn_stat, cred))))
+ goto out;
+
+ if(! FUSE_OP_HAVE(utime))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ /* prepare utimebuf for FUSE_OP_HAVE(utime) call */
+ struct utimbuf utb;
+ utb.actime = atime ? atime->tv_sec : node->nn_stat.st_atime;
+ utb.modtime = mtime ? mtime->tv_sec : node->nn_stat.st_mtime;
+
+ err = -FUSE_OP_CALL(utime, node->nn->path, &utb);
+
+ if (! err)
+ {
+ node->nn_stat.st_mtime = utb.modtime;
+ node->nn_stat.st_mtime_usec = 0;
+
+ node->nn_stat.st_atime = utb.actime;
+ node->nn_stat.st_atime_usec = 0;
+
+ node->nn->may_need_sync = 1;
+ }
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* Read from the file NODE 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 *node,
+ loff_t offset, size_t *len, void *data)
+{
+ FUNC_PROLOGUE_NODE("netfs_attempt_read", node);
+ error_t err;
+
+ if((err = netfs_validate_stat(node, cred))
+ || (libfuse_params.deflt_perms
+ && (err = fshelp_access(&node->nn_stat, S_IREAD, cred))))
+ goto out;
+
+ if(! FUSE_OP_HAVE(read))
+ {
+ err = EOPNOTSUPP;
+ goto out;
+ }
+
+ if(fuse_ops && fuse_ops->open)
+ if((err = fuse_ops->open(node->nn->path, &node->nn->info)))
+ goto out;
+
+ int sz = fuse_ops ?
+ (fuse_ops->read(node->nn->path, data, *len, offset, &node->nn->info)) :
+ (fuse_ops_compat->read(node->nn->path, data, *len, offset));
+
+ /* FIXME: open, flush and release handling probably should be changed
+ * completely, I mean, we probably should do fuse_ops->open in
+ * the netfs_check_open_permissions function and leave it open
+ * until the node is destroyed.
+ *
+ * This way we wouldn't be able to report any errors back.
+ */
+ if(sz >= 0 && fuse_ops && fuse_ops->flush)
+ err = fuse_ops->flush(node->nn->path, &node->nn->info);
+
+ if(fuse_ops && fuse_ops->open && fuse_ops->release)
+ fuse_ops->release(node->nn->path, &node->nn->info);
+
+ if(sz < 0)
+ err = -sz;
+ else
+ {
+ err = 0;
+ *len = sz;
+ }
+
+ out:
+ FUNC_EPILOGUE(err);
+}
+
+
+
+/* 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))
+
+/* fuse_get_inode
+ *
+ * look up the inode of the named file (full path) in fuse_use_ino mode,
+ * where we don't have the inode number of parent directories in our structs
+ */
+static ino_t
+fuse_get_inode(const char *name)
+{
+ struct stat stat;
+
+ assert(fuse_ops);
+ assert(fuse_ops->getattr);
+
+ fuse_ops->getattr(name, &stat);
+ return stat.st_ino;
+}
+
+/* callback handler used by netfs_get_dirents to write our dirents
+ * to the mmaped memory (getdir case)
+ */
+static int
+get_dirents_getdir_helper(fuse_dirh_t handle, const char *name,
+ int type, ino_t ino)
+{
+ ino_t inode;
+
+ if(handle->first_entry)
+ {
+ /* skip this entry, it's before the first one we got to write out ... */
+ handle->first_entry --;
+ return 0;
+ }
+
+ if(! (handle->num_entries --))
+ return ENOMEM;
+
+ size_t name_len = strlen(name);
+ size_t dirent_len = DIRENT_LEN(name_len);
+
+ if(dirent_len > handle->size)
+ {
+ handle->num_entries = 0; /* don't write any further data, e.g. in case */
+ return ENOMEM; /* next filename's shorter */
+ }
+ else
+ handle->size -= dirent_len;
+
+ /* look the inode of this element up ... */
+ if(libfuse_params.use_ino)
+ {
+ /* using the inode based api */
+ if(! strcmp(name, "."))
+ inode = fuse_get_inode(handle->parent->path);
+
+ else if(handle->parent->parent && ! strcmp(name, ".."))
+ inode = fuse_get_inode(handle->parent->parent->path);
+
+ else
+ inode = ino;
+ }
+ else
+ {
+ /* using the old (lookup) method ... */
+
+ if(! strcmp(name, "."))
+ inode = handle->parent->inode;
+
+ else if(handle->parent->parent && ! strcmp(name, ".."))
+ inode = handle->parent->parent->inode;
+
+ else
+ {
+ if(name_len > handle->maxlen)
+ {
+ /* allocated field in handle structure is to small, enlarge it */
+ handle->maxlen = name_len << 1;
+ void *a = realloc(handle->abspath, handle->maxlen +
+ (handle->filename - handle->abspath) + 1);
+ if(! a)
+ return ENOMEM;
+
+ handle->filename = a + (handle->filename - handle->abspath);
+ handle->abspath = a;
+ }
+
+ strcpy(handle->filename, name);
+ struct netnode *nn = fuse_make_netnode(handle->parent,
+ handle->abspath);
+
+ if(! nn)
+ return ENOMEM;
+
+ inode = nn->inode;
+ }
+ }
+
+ /* write out struct dirent ... */
+ handle->hdrpos->d_fileno = inode;
+ handle->hdrpos->d_reclen = dirent_len;
+ handle->hdrpos->d_type = type;
+ handle->hdrpos->d_namlen = name_len;
+
+ /* copy file's name ... */
+ memcpy(((void *) handle->hdrpos) + DIRENT_NAME_OFFS, name, name_len + 1);
+
+ /* update hdrpos pointer */
+ handle->hdrpos = ((void *) handle->hdrpos) + dirent_len;
+
+ handle->count ++;
+ return 0;
+}
+
+
+/* callback handler used by netfs_get_dirents to write our dirents
+ * to the mmaped memory
+ *
+ * version for old api
+ */
+static int
+get_dirents_getdir_helper_compat(fuse_dirh_t hdl, const char *name, int type)
+{
+ return get_dirents_getdir_helper(hdl, name, type, 0);
+}
+
+
+static error_t
+get_dirents_getdir(struct node *dir, int first_entry, int num_entries,
+ char **data, mach_msg_type_number_t *data_len,
+ int *data_entries)
+{
+ FUNC_PROLOGUE_NODE("get_dirents_getdir", dir);
+
+ if(! FUSE_OP_HAVE(getdir))
+ FUNC_RETURN(EOPNOTSUPP);
+
+ fuse_dirh_t handle;
+ if(! (handle = malloc(sizeof(struct fuse_dirhandle))))
+ FUNC_RETURN(ENOMEM);
+
+ handle->first_entry = first_entry;
+ handle->num_entries = num_entries;
+ handle->count = 0;
+ handle->size = *data_len;
+
+ /* allocate handle->abspath */
+ size_t path_len = strlen(dir->nn->path);
+ if(! (handle->abspath = malloc((handle->maxlen = 256) + path_len + 2)))
+ {
+ free(handle);
+ FUNC_RETURN(ENOMEM);
+ }
+
+ memcpy(handle->abspath, dir->nn->path, path_len);
+ handle->filename = handle->abspath + path_len;
+
+ /* add a delimiting slash if there are parent directories */
+ if(dir->nn->parent)
+ *(handle->filename ++) = '/';
+
+ handle->parent = dir->nn;
+ handle->hdrpos = (struct dirent*) *data;
+
+ if(fuse_ops)
+ fuse_ops->getdir(dir->nn->path, handle, get_dirents_getdir_helper);
+ else
+ fuse_ops_compat->getdir(dir->nn->path, handle,
+ get_dirents_getdir_helper_compat);
+
+
+ *data_len -= handle->size; /* subtract number of bytes left in the
+ * buffer from the length of the buffer we
+ * got. */
+ *data_entries = handle->count;
+
+ free(handle->abspath);
+ free(handle);
+
+ FUNC_EPILOGUE(0);
+}
+
+
+/* callback handler used by netfs_get_dirents to write our dirents
+ * to the mmaped memory (in readdir case)
+ *
+ * be careful, according to fuse.h `stat' may be NULL!
+ */
+static int
+get_dirents_readdir_helper(void *buf, const char *name,
+ const struct stat *stat, off_t off)
+{
+ fuse_dirh_t handle = buf;
+ ino_t inode;
+
+ if(handle->first_entry && !off)
+ {
+ /* skip this entry, it's before the first one
+ * we got to write out ... */
+ handle->first_entry --;
+ return 0;
+ }
+
+ if(! (handle->num_entries --))
+ return 1;
+
+ size_t name_len = strlen(name);
+ size_t dirent_len = DIRENT_LEN(name_len);
+
+ if(dirent_len > handle->size)
+ {
+ handle->num_entries = 0; /* don't write any further data, e.g. in case */
+ return ENOMEM; /* next filename's shorter */
+ }
+ else
+ handle->size -= dirent_len;
+
+ /* look the inode of this element up ... */
+ if(libfuse_params.use_ino)
+ {
+ /* using the inode based api */
+ if(! strcmp(name, "."))
+ inode = fuse_get_inode(handle->parent->path);
+
+ else if(handle->parent->parent && ! strcmp(name, ".."))
+ inode = fuse_get_inode(handle->parent->parent->path);
+
+ if(! stat)
+ {
+ DEBUG("critical", "use_ino flag set, but stat ptr not available.\n");
+ inode = 0;
+ }
+
+ else
+ inode = stat->st_ino;
+ }
+ else
+ {
+ /* using the old (lookup) method ... */
+
+ if(! strcmp(name, "."))
+ inode = handle->parent->inode;
+
+ else if(handle->parent->parent && ! strcmp(name, ".."))
+ inode = handle->parent->parent->inode;
+
+ else
+ {
+ if(name_len > handle->maxlen)
+ {
+ /* allocated field in handle structure is to small, enlarge it */
+ handle->maxlen = name_len << 1;
+ void *a = realloc(handle->abspath, handle->maxlen +
+ (handle->filename - handle->abspath) + 1);
+ if(! a)
+ return ENOMEM;
+
+ handle->filename = a + (handle->filename - handle->abspath);
+ handle->abspath = a;
+ }
+
+ strcpy(handle->filename, name);
+ struct netnode *nn = fuse_make_netnode(handle->parent,
+ handle->abspath);
+
+ if(! nn)
+ return ENOMEM;
+
+ inode = nn->inode;
+ }
+ }
+
+ /* write out struct dirent ... */
+ handle->hdrpos->d_fileno = inode;
+ handle->hdrpos->d_reclen = dirent_len;
+ handle->hdrpos->d_type = stat ? (stat->st_mode >> 12) : 0;
+ handle->hdrpos->d_namlen = name_len;
+
+ /* copy file's name ... */
+ memcpy(((void *) handle->hdrpos) + DIRENT_NAME_OFFS, name, name_len + 1);
+
+ /* update hdrpos pointer */
+ handle->hdrpos = ((void *) handle->hdrpos) + dirent_len;
+
+ handle->count ++;
+ return 0;
+}
+
+
+static error_t
+get_dirents_readdir(struct node *dir, int first_entry, int num_entries,
+ char **data, mach_msg_type_number_t *data_len,
+ int *data_entries)
+{
+ error_t err;
+ FUNC_PROLOGUE_NODE("get_dirents_readdir", dir);
+
+ if(! (fuse_ops && fuse_ops->readdir))
+ FUNC_RETURN(EOPNOTSUPP);
+
+ fuse_dirh_t handle;
+ if(! (handle = malloc(sizeof(struct fuse_dirhandle))))
+ FUNC_RETURN(ENOMEM);
+
+ handle->first_entry = first_entry;
+ handle->num_entries = num_entries;
+ handle->count = 0;
+ handle->size = *data_len;
+
+ /* allocate handle->abspath */
+ size_t path_len = strlen(dir->nn->path);
+ if(! (handle->abspath = malloc((handle->maxlen = 256) + path_len + 2)))
+ {
+ err = ENOMEM;
+ goto out;
+ }
+
+ memcpy(handle->abspath, dir->nn->path, path_len);
+ handle->filename = handle->abspath + path_len;
+
+ /* add a delimiting slash if there are parent directories */
+ if(dir->nn->parent)
+ *(handle->filename ++) = '/';
+
+ handle->parent = dir->nn;
+ handle->hdrpos = (struct dirent*) *data;
+
+ if(fuse_ops->opendir
+ && (err = fuse_ops->opendir(dir->nn->path, &dir->nn->info)))
+ goto out;
+
+ if((err = fuse_ops->readdir(dir->nn->path, handle,
+ get_dirents_readdir_helper, first_entry,
+ &dir->nn->info)))
+ {
+ fuse_ops->releasedir(dir->nn->path, &dir->nn->info);
+ goto out;
+ }
+
+ if(fuse_ops->releasedir
+ && (err = fuse_ops->releasedir(dir->nn->path, &dir->nn->info)))
+ goto out;
+
+ *data_len -= handle->size; /* subtract number of bytes left in the
+ * buffer from the length of the buffer we
+ * got. */
+ *data_entries = handle->count;
+
+ out:
+ free(handle->abspath);
+ free(handle);
+
+ FUNC_EPILOGUE(err);
+}
+
+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)
+{
+ (void) max_data_len; /* we live with the supplied mmap area in any case,
+ * i.e. never allocate any further memory */
+
+ FUNC_PROLOGUE_NODE("netfs_get_dirents", dir);
+ error_t err;
+
+ if((err = netfs_validate_stat(dir, cred))
+ || (libfuse_params.deflt_perms
+ && ((err = fshelp_access(&dir->nn_stat, S_IREAD, cred))
+ || (err = fshelp_access(&dir->nn_stat, S_IEXEC, cred)))))
+ goto out;
+
+
+ if(fuse_ops && fuse_ops->readdir)
+ err = get_dirents_readdir(dir, first_entry, num_entries, data, data_len,
+ data_entries);
+
+ else if(FUSE_OP_HAVE(getdir))
+ err = get_dirents_getdir(dir, first_entry, num_entries, data, data_len,
+ data_entries);
+
+ else
+ err = EOPNOTSUPP;
+
+ /* TODO: fshelp_touch ATIME here */
+
+ out:
+ FUNC_EPILOGUE_FMT(err, "%d entries.", *data_entries);
+}
+
+
+
+/* Node NP is all done; free all its associated storage. */
+void
+netfs_node_norefs (struct node *node)
+{
+ FUNC_PROLOGUE_NODE("netfs_node_norefs", node);
+ assert(node);
+
+ DEBUG("netnode-lock", "locking netnode, path=%s\n", node->nn->path);
+ mutex_lock(&node->nn->lock);
+
+ if(node->nn->anonymous && FUSE_OP_HAVE(unlink))
+ {
+ /* FIXME, need to lock parent directory structure */
+ FUSE_OP_CALL(unlink, node->nn->path);
+
+ /* FIXME, free associated netnode somehow. */
+ }
+
+ node->nn->node = NULL;
+
+ mutex_unlock(&node->nn->lock);
+ DEBUG("netnode-lock", "netnode unlocked.\n"); /* no ref to node->nn av. */
+
+ FUNC_EPILOGUE_NORET();
+}
diff --git a/src/netnode.c b/src/netnode.c
new file mode 100644
index 000000000..9727097c1
--- /dev/null
+++ b/src/netnode.c
@@ -0,0 +1,175 @@
+/**********************************************************
+ * netnode.c
+ *
+ * Copyright(C) 2004, 2005 by Stefan Siegl <ssiegl@gmx.de>, Germany
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Publice License,
+ * version 2 or any later. The license is contained in the COPYING
+ * file that comes with the libfuse distribution.
+ *
+ * netnode handling
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <error.h>
+#include <string.h>
+#include <rwlock.h>
+#include <hurd/netfs.h>
+
+#include "fuse_i.h"
+#include "fuse.h"
+
+#define HASH_BUCKETS 256
+
+struct hash_element;
+static struct hash_element {
+ struct netnode *nn;
+ struct hash_element *next; /* singly linked list */
+} fuse_netnodes[HASH_BUCKETS] = {{ 0 }};
+
+/* rwlock needs to be held when touching either fuse_netnodes hash or
+ * fuse_next_inode variable
+ */
+struct rwlock fuse_netnodes_lock = RWLOCK_INITIALIZER;
+
+/* next inode number that will be assigned
+ * (fuse_netnodes_lock must be write locked, when touching this)
+ */
+ino_t fuse_next_inode = 1;
+
+
+
+static int
+fuse_netnode_hash_value(const char *path)
+{
+ int hash = 0;
+ int maxlen = 12; /* use the first 12 chars for hash generation only */
+
+ while(maxlen -- && *path)
+ hash += *(path ++);
+
+ return hash % HASH_BUCKETS;
+}
+
+
+
+struct netnode *
+fuse_make_netnode(struct netnode *parent, const char *path)
+{
+ struct hash_element *hash_el;
+ struct netnode *nn;
+ int hash_value = fuse_netnode_hash_value(path);
+
+ DEBUG("netnodes_lock", "aquiring rwlock_reader_lock for %s.\n", path);
+ rwlock_reader_lock(&fuse_netnodes_lock);
+
+ hash_el = &fuse_netnodes[hash_value];
+ if(hash_el->nn)
+ do
+ if(! strcmp(hash_el->nn->path, path))
+ {
+ nn = hash_el->nn;
+ rwlock_reader_unlock(&fuse_netnodes_lock);
+ DEBUG("netnodes_lock", "releasing rwlock_reader_lock.\n");
+
+ return nn;
+ }
+ while((hash_el = hash_el->next));
+
+ rwlock_reader_unlock(&fuse_netnodes_lock);
+ DEBUG("netnodes_lock", "releasing rwlock_reader_lock.\n");
+
+ nn = malloc(sizeof(*nn));
+ if(! nn)
+ return NULL; /* unfortunately we cannot serve a netnode .... */
+
+ nn->path = strdup(path);
+ nn->parent = parent;
+ nn->node = NULL;
+ mutex_init(&nn->lock);
+
+ DEBUG("netnodes_lock", "aquiring rwlock_writer_lock for %s.\n", path);
+ rwlock_writer_lock(&fuse_netnodes_lock);
+
+ nn->inode = fuse_next_inode ++;
+
+ /* unable to find hash element, need to generate a new one */
+ if(! hash_el)
+ hash_el = &fuse_netnodes[hash_value];
+
+ if(hash_el->nn)
+ {
+ struct hash_element *new = malloc(sizeof(*new));
+
+ if(! new) {
+ rwlock_writer_unlock(&fuse_netnodes_lock);
+ DEBUG("netnodes_lock", "releasing rwlock_writer_lock.\n");
+ free(nn);
+ return NULL; /* can't help, sorry. */
+ }
+
+ /* enqueue new hash_element */
+ new->next = hash_el->next;
+ hash_el->next = new;
+
+ hash_el = new;
+ }
+
+ hash_el->nn = nn;
+
+ rwlock_writer_unlock(&fuse_netnodes_lock);
+ DEBUG("netnodes_lock", "releasing rwlock_writer_lock.\n");
+ return nn;
+}
+
+
+/* scan the whole netnode hash and call fuse_ops->fsync on every node,
+ * that may be out of sync
+ */
+error_t
+fuse_sync_filesystem(void)
+{
+ int i;
+ error_t err = 0;
+
+ if(! FUSE_OP_HAVE(fsync))
+ return err; /* success */
+
+ /* make sure, nobody tries to confuse us */
+ rwlock_writer_lock(&fuse_netnodes_lock);
+
+ for(i = 0; i < HASH_BUCKETS; i ++)
+ {
+ struct hash_element *he = &fuse_netnodes[i];
+
+ if(he->nn)
+ do
+ {
+ if(he->nn->may_need_sync)
+ {
+ err = -(fuse_ops ?
+ fuse_ops->fsync(he->nn->path, 0, &he->nn->info) :
+ fuse_ops_compat->fsync(he->nn->path, 0));
+
+ if(err)
+ goto out;
+ else
+ he->nn->may_need_sync = 0;
+ }
+
+ if(he->nn->node)
+ DEBUG("netnode-scan", "netnode=%s has attached node\n",
+ he->nn->path);
+ }
+ while((he = he->next));
+ }
+
+ out:
+ rwlock_writer_unlock(&fuse_netnodes_lock);
+ return err;
+}
diff --git a/src/node.c b/src/node.c
new file mode 100644
index 000000000..a09cc5c6b
--- /dev/null
+++ b/src/node.c
@@ -0,0 +1,69 @@
+/**********************************************************
+ * node.c
+ *
+ * Copyright(C) 2004, 2005 by Stefan Siegl <ssiegl@gmx.de>, Germany
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Publice License,
+ * version 2 or any later. The license is contained in the COPYING
+ * file that comes with the libfuse distribution.
+ *
+ * create (and care for) nodes ...
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <stdio.h>
+#include <error.h>
+#include <string.h>
+#include <hurd/netfs.h>
+
+#include "fuse_i.h"
+#include "fuse.h"
+
+/* create a new node for the specified netnode
+ */
+struct node *
+fuse_make_node(struct netnode *nn)
+{
+ struct node *node;
+
+ DEBUG("fuse_make_node", "creating node for %s.\n", nn->path);
+ DEBUG("netnode-lock", "locking netnode, path=%s\n", nn->path);
+ mutex_lock(&nn->lock);
+
+ if((node = nn->node))
+ {
+ /* there already is a node, therefore return another reference to it */
+ netfs_nref(node);
+ mutex_unlock(&nn->lock);
+ DEBUG("netnode-lock", "UNlocking netnode, path=%s\n", nn->path);
+ DEBUG("fuse_make_node", "reusing already existing node, %s\n", nn->path);
+ return node;
+ }
+
+ if(! (node = netfs_make_node(nn)))
+ {
+ mutex_unlock(&nn->lock);
+ DEBUG("netnode-lock", "UNlocking netnode, path=%s\n", nn->path);
+ return NULL; /* doesn't look to good for us :-( */
+ }
+
+ /* now initialize those stats for which we are reliable ...
+ */
+ node->nn_stat.st_ino = nn->inode;
+ node->nn_stat.st_blksize = 4096; /* depends on our host program, but since
+ * we're expected to fill, assume 4k for now
+ */
+ node->nn_stat.st_rdev = 0;
+
+ /* add pointer to our new node structure to the netnode */
+ nn->node = node;
+
+ mutex_unlock(&nn->lock);
+ DEBUG("netnode-lock", "UNlocking netnode, path=%s\n", nn->path);
+ DEBUG("fuse_make_node", "created a new node for %s.\n", nn->path);
+ return node;
+}