summaryrefslogtreecommitdiff
path: root/src/netfs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/netfs.c')
-rw-r--r--src/netfs.c1572
1 files changed, 1572 insertions, 0 deletions
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();
+}