diff options
author | Stefan Siegl <stesie@brokenpipe.de> | 2006-01-30 22:37:59 +0000 |
---|---|---|
committer | Stefan Siegl <stesie@brokenpipe.de> | 2006-01-30 22:37:59 +0000 |
commit | ad506a98d82194fd632409b687a0d7fd61a1eb4c (patch) | |
tree | 2eba0f18e696113b9a6667942bfd9bbbb13b5a5d /src | |
parent | a88f3f1d9f48e360d03baef9f7c14e4f6e311f10 (diff) |
moved headers into new include/ directory, sources moved into new src/ directory.
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 18 | ||||
-rw-r--r-- | src/fuse_i.h | 225 | ||||
-rw-r--r-- | src/main.c | 363 | ||||
-rw-r--r-- | src/netfs.c | 1572 | ||||
-rw-r--r-- | src/netnode.c | 175 | ||||
-rw-r--r-- | src/node.c | 69 |
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; +} |