gnunet-fuse

GNUnet file-sharing directory mounting via FUSE
Log | Files | Refs | Submodules | README | LICENSE

commit c2b14e3b8c3dbf84636266abb0bd5cc63ecf7ced
parent e45143f083447c5e3bbec21b12b549964f380a47
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon,  4 Jun 2012 12:13:15 +0000

-locking

Diffstat:
MINSTALL | 9+++++++--
Msrc/fuse/Makefile.am | 3++-
Msrc/fuse/gnunet-fuse.c | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msrc/fuse/gnunet-fuse.h | 66+++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Asrc/fuse/mutex.c | 130+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/fuse/mutex.h | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/fuse/readdir.c | 6+++++-
7 files changed, 383 insertions(+), 25 deletions(-)

diff --git a/INSTALL b/INSTALL @@ -1,8 +1,8 @@ Installation Instructions ************************* -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007, 2008, 2009 Free Software Foundation, Inc. +Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation, +Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright @@ -226,6 +226,11 @@ order to use an ANSI C compiler: and if that doesn't work, install pre-built binaries of GCC for HP-UX. + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `<wchar.h>' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended diff --git a/src/fuse/Makefile.am b/src/fuse/Makefile.am @@ -9,8 +9,9 @@ INCLUDES = \ bin_PROGRAMS = gnunet-fuse gnunet_fuse_SOURCES = \ + gnunet-fuse.c \ + mutex.c mutex.h \ mkdir.c \ - gnunet-fuse.c \ mknod.c \ release.c \ rename.c \ diff --git a/src/fuse/gnunet-fuse.c b/src/fuse/gnunet-fuse.c @@ -51,22 +51,64 @@ static char *directory; * Global mapping of paths to GNUnet URIs (and file names) for * the respective entries. */ -struct GNUNET_CONTAINER_MultiHashMap *map; +static struct GNUNET_CONTAINER_MultiHashMap *map; + +/** + * Mutex for synchronizing access to 'map'. + */ +static struct GNUNET_Mutex *map_mutex; +/** + * Obtain an existing path info entry from the global map. + * + * @param path path the entry represents + * @return NULL if no such path entry exists + */ struct GNUNET_FUSE_PathInfo * -create_path_info (const char *path, const struct GNUNET_FS_Uri *uri) +GNUNET_FUSE_get_path_info (const char *path) { struct GNUNET_FUSE_PathInfo *pi; GNUNET_HashCode path_hash; GNUNET_CRYPTO_hash (path, strlen (path), &path_hash); + GNUNET_mutex_lock (map_mutex); + pi = GNUNET_CONTAINER_multihashmap_get (map, &path_hash); + ++pi->rc; + GNUNET_mutex_unlock (map_mutex); + return pi; +} + + +/** + * Create a new path info entry in the global map. + * + * @param path path the entry represents + * @param uri URI to use for the path + * @return existing path entry if one already exists, otherwise + * new path entry with the desired URI + */ +struct GNUNET_FUSE_PathInfo * +GNUNET_FUSE_path_info_create (const char *path, + const struct GNUNET_FS_Uri *uri, + int is_directory) +{ + struct GNUNET_FUSE_PathInfo *pi; + GNUNET_HashCode path_hash; + + GNUNET_CRYPTO_hash (path, strlen (path), &path_hash); + GNUNET_mutex_lock (map_mutex); pi = GNUNET_CONTAINER_multihashmap_get (map, &path_hash); if (NULL != pi) + { + GNUNET_mutex_unlock (map_mutex); return pi; + } pi = GNUNET_malloc (sizeof (struct GNUNET_FUSE_PathInfo)); pi->path = GNUNET_strdup (path); pi->uri = GNUNET_FS_uri_dup (uri); + pi->lock = GNUNET_mutex_create (GNUNET_YES); + pi->rc = 1; GNUNET_assert (GNUNET_OK == GNUNET_CONTAINER_multihashmap_put (map, &path_hash, pi, @@ -75,25 +117,71 @@ create_path_info (const char *path, const struct GNUNET_FS_Uri *uri) pi->stbuf.st_blocks = 1 + ((pi->stbuf.st_size) / 512); pi->stbuf.st_blksize = 32 * 1024; pi->stbuf.st_mode = (S_IRUSR | S_IRGRP | S_IROTH); /* read-only */ - if (0) /* FIXME: test for directory... */ + if (GNUNET_YES == is_directory) pi->stbuf.st_mode |= (S_IXUSR | S_IXGRP | S_IXOTH); /* allow traversal */ pi->stbuf.st_mode |= S_IFREG; /* regular file */ + GNUNET_mutex_unlock (map_mutex); return pi; } +/** + * Reduce the reference counter of a path info entry. + * + * @param pi entry to decrement the RC of + */ void -delete_path_info (struct GNUNET_FUSE_PathInfo *pi) +GNUNET_FUSE_path_info_done (struct GNUNET_FUSE_PathInfo *pi) { - if (NULL != pi->tmpfile) + if (GNUNET_YES == pi->delete_later) { - GNUNET_break (0 == UNLINK (pi->tmpfile)); - GNUNET_free (pi->tmpfile); + (void) GNUNET_FUSE_path_info_delete (pi); + return; } - GNUNET_free (pi->path); - GNUNET_FS_uri_destroy (pi->uri); - GNUNET_free (pi); + GNUNET_mutex_lock (map_mutex); + --pi->rc; + GNUNET_mutex_unlock (map_mutex); +} + + +/** + * Delete a path info entry from the global map (does not actually + * remove anything from the file system). Also decrements the RC. + * + * @param pi entry to remove + * @return - ENOENT if the file was already deleted, 0 on success + */ +int +GNUNET_FUSE_path_info_delete (struct GNUNET_FUSE_PathInfo *pi) +{ + GNUNET_HashCode path_hash; + int ret; + int rc; + GNUNET_CRYPTO_hash (pi->path, strlen (pi->path), &path_hash); + GNUNET_mutex_lock (map_mutex); + ret = GNUNET_CONTAINER_multihashmap_remove (map, &path_hash, pi); + rc = --pi->rc; + GNUNET_mutex_unlock (map_mutex); + if (0 != rc) + { + pi->delete_later = GNUNET_YES; + } + else + { + if (NULL != pi->tmpfile) + { + GNUNET_break (0 == UNLINK (pi->tmpfile)); + GNUNET_free (pi->tmpfile); + } + GNUNET_free (pi->path); + GNUNET_FS_uri_destroy (pi->uri); + GNUNET_mutex_destroy (pi->lock); + GNUNET_free (pi); + } + if (GNUNET_YES == ret) + return 0; + return - ENOENT; } @@ -110,7 +198,8 @@ cleanup_path_info (void *cls, const GNUNET_HashCode * key, void *value) { struct GNUNET_FUSE_PathInfo *pi = value; - delete_path_info (pi); + ++pi->rc; + (void) GNUNET_FUSE_path_info_delete (pi); return GNUNET_YES; } @@ -148,6 +237,7 @@ run (void *cls, struct GNUNET_FS_Uri *uri; char *emsg; const char *path = "/"; + struct GNUNET_FUSE_PathInfo *pi; ret = 0; if (NULL == source) @@ -172,8 +262,9 @@ run (void *cls, return; } + map_mutex = GNUNET_mutex_create (GNUNET_NO); map = GNUNET_CONTAINER_multihashmap_create (1024); - (void) create_path_info (path, uri); + pi = GNUNET_FUSE_path_info_create (path, uri, GNUNET_YES); if (GNUNET_YES == single_threaded) argc = 5; @@ -192,8 +283,12 @@ run (void *cls, a[argc] = NULL; fuse_main (argc, a, &fops, NULL); } + GNUNET_FUSE_path_info_done (pi); GNUNET_CONTAINER_multihashmap_iterate (map, &cleanup_path_info, NULL); GNUNET_CONTAINER_multihashmap_destroy (map); + map = NULL; + GNUNET_mutex_destroy (map_mutex); + map_mutex = NULL; GNUNET_FS_uri_destroy (uri); } diff --git a/src/fuse/gnunet-fuse.h b/src/fuse/gnunet-fuse.h @@ -33,13 +33,7 @@ #define FUSE_USE_VERSION 26 #include <fuse.h> - - -/** - * Global mapping of paths to GNUnet URIs (and file names) for - * the respective entries. - */ -extern struct GNUNET_CONTAINER_MultiHashMap *map; +#include "mutex.h" /** @@ -66,15 +60,69 @@ struct GNUNET_FUSE_PathInfo * file attributes */ struct stat stbuf; + + /** + * Lock for exclusive access to this struct. + */ + struct GNUNET_Mutex *lock; + + /** + * Reference counter. + */ + unsigned int rc; + + /** + * Should the file be deleted after the RC hits zero? + */ + int delete_later; }; +/** + * Create a new path info entry in the global map. + * + * @param path path the entry represents + * @param uri URI to use for the path + * @param is_directory GNUNET_YES if this entry is for a directory + * @return existing path entry if one already exists, otherwise + * new path entry with the desired URI; in both cases + * the reference counter has been incremented by 1 + */ +struct GNUNET_FUSE_PathInfo * +GNUNET_FUSE_path_info_create (const char *path, + const struct GNUNET_FS_Uri *uri, + int is_directory); + + +/** + * Obtain an existing path info entry from the global map. + * + * @param path path the entry represents + * @return NULL if no such path entry exists, otherwise + * an entry with incremented reference counter (!) + */ struct GNUNET_FUSE_PathInfo * -create_path_info (const char *path, const struct GNUNET_FS_Uri *uri); +GNUNET_FUSE_path_info_get (const char *path); +/** + * Reduce the reference counter of a path info entry. + * + * @param pi entry to decrement the RC of + */ void -delete_path_info (struct GNUNET_FUSE_PathInfo *pi); +GNUNET_FUSE_path_info_done (struct GNUNET_FUSE_PathInfo *pi); + + +/** + * Delete a path info entry from the global map (does not actually + * remove anything from the file system). Also decrements the RC. + * + * @param pi entry to remove + * @return - ENOENT if the file was already deleted, 0 on success + */ +int +GNUNET_FUSE_path_info_delete (struct GNUNET_FUSE_PathInfo *pi); /* FUSE function files */ diff --git a/src/fuse/mutex.c b/src/fuse/mutex.c @@ -0,0 +1,130 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file src/fuse/mutex.c + * @brief implementation of mutual exclusion + */ +#include "gnunet-fuse.h" +#include "mutex.h" + +#include <pthread.h> +#if SOMEBSD +# include <pthread_np.h> +#endif + +#ifndef PTHREAD_MUTEX_NORMAL +#ifdef PTHREAD_MUTEX_TIMED_NP +#define PTHREAD_MUTEX_NORMAL PTHREAD_MUTEX_TIMED_NP +#else +#define PTHREAD_MUTEX_NORMAL NULL +#endif +#endif + +/** + * This prototype is somehow missing in various Linux pthread + * include files. But we need it and it seems to be available + * on all pthread-systems so far. Odd. + */ +#ifndef _MSC_VER +extern int pthread_mutexattr_setkind_np (pthread_mutexattr_t * attr, + int kind); +#endif + + +/** + * @brief Structure for MUTual EXclusion (Mutex). + */ +struct GNUNET_Mutex +{ + pthread_mutex_t pt; +}; + + +struct GNUNET_Mutex * +GNUNET_mutex_create (int isRecursive) +{ + pthread_mutexattr_t attr; + struct GNUNET_Mutex *mut; +#if WINDOWS + attr = NULL; +#endif + + pthread_mutexattr_init (&attr); + if (isRecursive) + { +#if LINUX + GNUNET_assert (0 == pthread_mutexattr_setkind_np + (&attr, PTHREAD_MUTEX_RECURSIVE_NP)); +#elif SOMEBSD || GNUNET_freeBSD || GNUNET_freeBSD5 + GNUNET_assert (0 == pthread_mutexattr_setkind_np + (&attr, PTHREAD_MUTEX_RECURSIVE)); +#elif SOLARIS || OSX || WINDOWS + GNUNET_assert (0 == pthread_mutexattr_settype + (&attr, PTHREAD_MUTEX_RECURSIVE)); +#endif + } + else + { +#if LINUX + GNUNET_assert (0 == pthread_mutexattr_setkind_np + (&attr, PTHREAD_MUTEX_ERRORCHECK_NP)); +#else + GNUNET_assert (0 == pthread_mutexattr_settype + (&attr, PTHREAD_MUTEX_ERRORCHECK)); +#endif + } + mut = GNUNET_malloc (sizeof (struct GNUNET_Mutex)); + GNUNET_assert (0 == pthread_mutex_init (&mut->pt, &attr)); + return mut; +} + + +void +GNUNET_mutex_destroy (struct GNUNET_Mutex * mutex) +{ + GNUNET_assert (0 == pthread_mutex_destroy (&mutex->pt)); + GNUNET_free (mutex); +} + + +void +GNUNET_mutex_lock (struct GNUNET_Mutex * mutex) +{ + if (0 != (errno = pthread_mutex_lock (&mutex->pt))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pthread_mutex_unlock"); + GNUNET_assert (0); + } +} + + +void +GNUNET_mutex_unlock (struct GNUNET_Mutex * mutex) +{ + if (0 != (errno = pthread_mutex_unlock (&mutex->pt))) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "pthread_mutex_unlock"); + GNUNET_assert (0); + } +} + + +/* end of mutex.c */ diff --git a/src/fuse/mutex.h b/src/fuse/mutex.h @@ -0,0 +1,75 @@ +/* + This file is part of GNUnet. + (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2012 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file src/fuse/mutex.h + * @brief pthreads wapper and thread related services + * + * @author Christian Grothoff + * @author Krista Bennett + * @author Gerd Knorr <kraxel@bytesex.org> + * @author Ioana Patrascu + * @author Tzvetan Horozov + */ + +#ifndef MUTEX_H +#define MUTEX_H + +#ifdef __cplusplus +extern "C" +{ +#if 0 /* keep Emacsens' auto-indent happy */ +} +#endif +#endif + + +/** + * @brief Structure for MUTual EXclusion (Mutex). + */ +struct GNUNET_Mutex; + + +struct GNUNET_Mutex * +GNUNET_mutex_create (int isRecursive); + + +void +GNUNET_mutex_destroy (struct GNUNET_Mutex *mutex); + + +void +GNUNET_mutex_lock (struct GNUNET_Mutex *mutex); + + +void +GNUNET_mutex_unlock (struct GNUNET_Mutex *mutex); + + +#if 0 /* keep Emacsens' auto-indent happy */ +{ +#endif +#ifdef __cplusplus +} +#endif + +/* ifndef MUTEX_H */ +#endif +/* end of mutex.h */ diff --git a/src/fuse/readdir.c b/src/fuse/readdir.c @@ -296,12 +296,16 @@ process_directory_entry (void *cls, struct DepContext *dc = cls; struct GNUNET_FUSE_PathInfo *pi; char *path; + int is_directory; GNUNET_asprintf (&path, "%s/%s", dc->path, filename); - pi = create_path_info (path, uri); + is_directory = GNUNET_FS_meta_data_test_for_directory (meta); + if (GNUNET_SYSERR == is_directory) + is_directory = GNUNET_NO; /* if in doubt, say no */ + pi = GNUNET_FUSE_path_info_create (path, uri, is_directory); dc->filler (dc->buf, filename, &pi->stbuf,