gnunet-fuse

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

commit d9bd9a1c1c4d1c9938e522c2d53c07ffed2db3be
parent 81779498a0370a52bf376c736cc664325d340548
Author: David Barksdale <amatus.amongus@gmail.com>
Date:   Thu, 12 Jul 2007 06:15:47 +0000

Added support for creating modifying files

Diffstat:
MChangeLog | 4+++-
MMakefile.am | 1+
Mconfigure.ac | 7++++---
Mdirectory.c | 365++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mdirent.c | 350+++++++++++++++++++++++++++++++++++++------------------------------------------
Mgetattr.c | 11++++++-----
Mgnfs.h | 40+++++++++++++++++++++++++++-------------
Mmain.c | 16++++++++++++----
Mmknod.c | 16++++++++--------
Mread.c | 6+++---
Mreaddir.c | 14+++++++-------
Arelease.c | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mspecial_file.c | 40++++++++++++++++++++++++++++------------
Mwrite.c | 11+++++++----
14 files changed, 639 insertions(+), 343 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,4 +1,6 @@ -2007-06-18 David Barksdale <amatus@gnu.org> 0.7.2a +2007-07-12 David Barksdale <amatus@gnu.org> 0.7.2-2 +* Added support for creating and modifying files +2007-06-18 David Barksdale <amatus@gnu.org> 0.7.2-1 * Modified configure script to better detect compatability with GNUnet 2007-06-18 David Barksdale <amatus@gnu.org> 0.7.2 * Gratuitous version incrementation diff --git a/Makefile.am b/Makefile.am @@ -8,6 +8,7 @@ gnunet_fs_SOURCES = \ open.c \ read.c \ readdir.c \ + release.c \ special_file.c \ write.c \ gnfs.h \ diff --git a/configure.ac b/configure.ac @@ -1,10 +1,11 @@ -AC_INIT(gnunet-fuse, 0.7.2a) +AC_INIT(gnunet-fuse, 0.7.2) AM_INIT_AUTOMAKE AM_CONFIG_HEADER(config.h) AC_PROG_CC AM_PROG_CC_C_O -dnl AC_PROG_LIBTOOL +AC_C_CHAR_UNSIGNED + export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH PKG_CHECK_MODULES(GLIB, [glib-2.0]) @@ -12,7 +13,7 @@ PKG_CHECK_MODULES(FUSE, [fuse >= 2.6]) # test for GNUnet core gnunet=0 -AC_MSG_CHECKING(for GNUnet core) +AC_MSG_CHECKING([for GNUnet core]) AC_ARG_WITH(gnunet, [ --with-gnunet=PFX Base of GNUnet installation], [AC_MSG_RESULT([$with_gnunet]) diff --git a/directory.c b/directory.c @@ -40,24 +40,19 @@ static void dpcb(unsigned long long totalBytes, static int tt(void *cls) { (void)cls; - return fuse_interrupted() ? SYSERR : OK; + if(closing) + return OK; + if(fuse_interrupted()) + return SYSERR; + return OK; } -struct dir_for_each_data -{ - gn_dir_for_each_callback cb; - void *data; - struct dirent *de; -}; - -static int dir_for_each_cb(const ECRS_FileInfo *fi, const HashCode512 *key, +static int dir_cache_cb(const ECRS_FileInfo *fi, const HashCode512 *key, int isRoot, void *data) { - struct dir_for_each_data *d = data; - struct dirent *de; + struct dirent *de, *deparent = data; gchar *filename, *newpath, type; size_t len, rlen; - int ret; (void)key; @@ -83,133 +78,311 @@ static int dir_for_each_cb(const ECRS_FileInfo *fi, const HashCode512 *key, type = DE_FILE; /* Create newpath, the path to this entry */ - rlen = strlen(d->de->de_path); + rlen = strlen(deparent->de_path); newpath = MALLOC(rlen + len + 1); - strcpy(newpath, d->de->de_path); - if(d->de->de_path[rlen - 1] != G_DIR_SEPARATOR) + strcpy(newpath, deparent->de_path); + if(deparent->de_path[rlen - 1] != G_DIR_SEPARATOR) strcat(newpath, G_DIR_SEPARATOR_S); strcat(newpath, filename); - /* Create a new dirent for this entry */ - de = gn_dirent_new(newpath, fi->uri, fi->meta, type); + /* Create a new dirent for this entry only if one doesn't already exist + * and the only place that can be is in the cache */ + de = gn_dirent_get(newpath); + if(de == NULL) + { + de = gn_dirent_new(newpath, fi->uri, fi->meta, type); - /* Add it to the cache */ - gn_dirent_cache(de); + /* Add it to the cache (creates its own ref)*/ + /* NB: the lock on deparent is enough to guarantee that another + * thread hasn't added this dirent to the cache in the time + * between the above check and this insert */ + gn_dirent_cache(de); + } - /* Call the callback function */ - ret = d->cb(de, filename, d->data); + /* Add it to the directory's list (steals our ref)*/ + GE_ASSERT(ectx, + !g_hash_table_lookup(deparent->de_dir_hash, de->de_basename)); + g_hash_table_replace(deparent->de_dir_hash, de->de_basename, de); /* Clean up */ - gn_dirent_put(de); FREE(filename); FREE(newpath); - return ret; + return OK; } -static gboolean hash_foreach_cb(gpointer key, gpointer value, gpointer data) +static int directory_cache_locked(struct dirent *de) { - struct dir_for_each_data *d = data; - struct dirent *de = value; - char *filename = strrchr(de->de_path, G_DIR_SEPARATOR) + 1; + struct ECRS_MetaData *md; + void *mem; int ret; + guint64 len; - (void)key; + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: for '%s'\n", + __FUNCTION__, de->de_path); + len = ECRS_fileSize(de->de_fi.uri); + mem = MALLOC(len); + ret = ECRS_downloadPartialFile(ectx, cfg, de->de_fi.uri, + "/dev/null", anonymity, 0, len, YES, dpcb, mem, tt, + NULL); + if(ret != OK) + { + GE_LOG(ectx, GE_BULK | GE_USER | GE_ERROR, + "%s: failed to download directory\n", + __FUNCTION__); + FREE(mem); + return -1; + } + de->de_dir_hash = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + (GDestroyNotify)gn_dirent_put); + ECRS_listDirectory(ectx, mem, len, &md, dir_cache_cb, de); + FREE(mem); + ECRS_freeMetaData(md); + de->de_cached = 1; + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: cached %d entries\n", __FUNCTION__, + g_hash_table_size(de->de_dir_hash)); + return 0; +} + +struct dir_foreach_data +{ + gn_dir_foreach_callback cb; + void *data; +}; + +static gboolean dir_foreach_callback(gpointer key, gpointer value, + gpointer data) +{ + struct dirent *de = value; + struct dir_foreach_data *d = data; - gn_dirent_ref(de); - ret = d->cb(de, filename, d->data); - gn_dirent_put(de); - return ret == SYSERR; + (void)key; + return d->cb(de, d->data) == -1; } -int gn_directory_for_each(struct dirent *de, gn_dir_for_each_callback cb, +/* + * Call cb for each element in a directory + */ +int gn_directory_foreach(struct dirent *de, gn_dir_foreach_callback cb, void *data) { - struct ECRS_MetaData *md; - struct dir_for_each_data d; - void *mem; - int ret; - guint64 len; + struct dir_foreach_data d; + int ret = 0; if(de->de_type != DE_DIR) return -1; - d.cb = cb; - d.data = data; - d.de = de; - if(SEMAPHORE_DOWN(de->de_file_sema, YES) == SYSERR) + if(SEMAPHORE_DOWN(de->de_sema, YES) == SYSERR) return -1; - if(de->de_dirty) + if(!de->de_cached) { - g_hash_table_find(de->de_dir, hash_foreach_cb, &d); - SEMAPHORE_UP(de->de_file_sema); - return 0; + ret = directory_cache_locked(de); + if(ret == -1) + goto out; } - len = ECRS_fileSize(de->de_fi.uri); - mem = MALLOC(len); - ret = ECRS_downloadPartialFile(ectx, cfg, de->de_fi.uri, "/dev/null", - anonymity, 0, len, YES, dpcb, mem, tt, NULL); - SEMAPHORE_UP(de->de_file_sema); - if(ret != OK) + d.cb = cb; + d.data = data; + g_hash_table_find(de->de_dir_hash, dir_foreach_callback, &d); +out: + SEMAPHORE_UP(de->de_sema); + return ret; +} + +/* + * Finds 'filename' in directory 'de' and returns a reference or NULL + */ +struct dirent *gn_directory_find(struct dirent *de, const gchar *filename) +{ + struct dirent *ret = NULL; + + if(de->de_type != DE_DIR) + return NULL; + if(SEMAPHORE_DOWN(de->de_sema, YES) == SYSERR) + return NULL; + if(!de->de_cached) { - GE_LOG(ectx, GE_BULK | GE_USER | GE_ERROR, - "%s: failed to download directory\n", __FUNCTION__); - ret = -1; - goto out; + if(directory_cache_locked(de) == -1) + goto out; } - ECRS_listDirectory(ectx, mem, len, &md, dir_for_each_cb, &d); - ECRS_freeMetaData(md); - ret = 0; + ret = g_hash_table_lookup(de->de_dir_hash, filename); + if(ret != NULL) + gn_dirent_ref(ret); out: - FREE(mem); + SEMAPHORE_UP(de->de_sema); return ret; } -static int directory_insert_callback(struct dirent *de, const gchar *filename, - void *data) +int gn_directory_insert(struct dirent *de, struct dirent *dechild) { - struct dir_for_each_data *d = data; + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: for '%s'\n", + __FUNCTION__, dechild->de_path); - (void)filename; - gn_dirent_ref(de); - g_hash_table_replace(d->de->de_dir, de->de_path, de); - return OK; + /* Lock our path */ + if(gn_lock_path(de) == -1) + return -1; + + /* Cache ourselfs (because we're going to become dirty) */ + if(!de->de_cached) + { + if(directory_cache_locked(de) == -1) + { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: failed to cache parent dir\n", + __FUNCTION__); + gn_unlock_path(de, GN_UNLOCK_CLEAN); + return -1; + } + } + + /* If we're already in there, bail out */ + if(g_hash_table_lookup(de->de_dir_hash, dechild->de_basename)) + { + gn_unlock_path(de, GN_UNLOCK_CLEAN); + return -1; + } + + /* Insert the child in our de_dir_hash */ + gn_dirent_ref(dechild); + g_hash_table_replace(de->de_dir_hash, dechild->de_basename, dechild); + + /* Cache the dirent */ + gn_dirent_cache(dechild); + + /* Mark our path dirty */ + gn_unlock_path(de, GN_UNLOCK_ALL_DIRTY); + return 0; } -int gn_directory_insert(struct dirent *de_dir, struct dirent *de_new) +static void upcb(guint64 totalBytes, guint64 completedBytes, cron_t eta, + void *closure) { - struct dir_for_each_data d; - void *mem; - guint64 len; + (void)totalBytes; + (void)completedBytes; + (void)eta; + (void)closure; +} + +struct dir_upload_data +{ + ECRS_FileInfo *fis; + int count; + int failed; +}; + +static gboolean dir_upload_callback(gpointer key, gpointer value, gpointer data) +{ + struct dirent *de = value; + struct dir_upload_data *d = data; int ret = 0; - struct ECRS_MetaData *md; - if(SEMAPHORE_DOWN(de_dir->de_file_sema, YES) == SYSERR) - return -1; - if(!de_dir->de_dirty) + (void)key; + if(SEMAPHORE_DOWN(de->de_sema, YES) == SYSERR) + { + d->failed = 1; + return 1; + } + if(de->de_dirty) { - de_dir->de_dirty = 1; - de_dir->de_dir = g_hash_table_new_full(g_str_hash, g_str_equal, - NULL, (GDestroyNotify)gn_dirent_put); - d.cb = directory_insert_callback; - d.de = de_dir; - len = ECRS_fileSize(de_dir->de_fi.uri); - mem = MALLOC(len); - ret = ECRS_downloadPartialFile(ectx, cfg, de_dir->de_fi.uri, - "/dev/null", anonymity, 0, len, YES, dpcb, mem, tt, - NULL); - if(ret != OK) + if(de->de_type == DE_FILE) { - GE_LOG(ectx, GE_BULK | GE_USER | GE_ERROR, - "%s: failed to download directory\n", - __FUNCTION__); - ret = -1; - goto out; + if(de->de_fi.uri == NULL) + { + goto out; + } + } + else + { + if(gn_directory_upload_locked(de) == -1) + { + d->failed = 1; + ret = 1; + goto out; + } } - ECRS_listDirectory(ectx, mem, len, &md, dir_for_each_cb, &d); - ECRS_freeMetaData(md); } - gn_dirent_ref(de_new); - g_hash_table_replace(de_dir->de_dir, de_new->de_path, de_new); + d->fis[d->count].uri = ECRS_dupUri(de->de_fi.uri); + d->fis[d->count].meta = ECRS_dupMetaData(de->de_fi.meta); + d->count++; out: - SEMAPHORE_UP(de_dir->de_file_sema); + SEMAPHORE_UP(de->de_sema); return ret; } + +/* + * Make a directory clean, de_sema must be locked + */ +int gn_directory_upload_locked(struct dirent *de) +{ + int i, ret, fd; + char *buf, filename[] = GN_MKSTEMP_FILE; + guint64 len; + struct ECRS_URI *uri; + struct dir_upload_data d; + + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: called for '%s'\n", __FUNCTION__, de->de_path); + + /* We may be already clean */ + if(!de->de_dirty) + return 0; + + /* Collect FileInfo from hash table and make a GNUnet directory */ + d.fis = MALLOC(g_hash_table_size(de->de_dir_hash) * sizeof(*d.fis)); + d.count = 0; + d.failed = 0; + g_hash_table_find(de->de_dir_hash, dir_upload_callback, &d); + if(d.failed) + { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_ERROR, + "%s: failed\n", __FUNCTION__); + return -1; + } + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: creating dir of %d elements\n", __FUNCTION__, d.count); + ret = ECRS_createDirectory(ectx, &buf, &len, d.count, d.fis, + de->de_fi.meta); + for(i = 0; i < d.count; i++) + { + ECRS_freeUri(d.fis[i].uri); + ECRS_freeMetaData(d.fis[i].meta); + } + FREE(d.fis); + if(ret == SYSERR) + { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_ERROR, + "%s: ECRS_createDirectory failed\n", + __FUNCTION__); + return -1; + } + + /* Write the GNUnet directory out to a file and upload it */ + fd = mkstemp(filename); + if(fd == -1) + { + GE_LOG_STRERROR_FILE(ectx, GE_BULK | GE_DEVELOPER + | GE_ERROR, "mkstemp", filename); + return -1; + } + write(fd, buf, len); + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: wrote to %lld bytes to '%s'\n", __FUNCTION__, len, + filename); + ret = ECRS_uploadFile(ectx, cfg, filename, NO, anonymity, priority, + -1, upcb, NULL, tt, NULL, &uri); + close(fd); + unlink(filename); + if(ret == SYSERR) + { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_ERROR, + "%s: ECRS_uploadFile failed\n", __FUNCTION__); + return -1; + } + + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: done\n", __FUNCTION__); + /* Update the dirent info with our new URI and mark it clean */ + if(de->de_fi.uri != NULL) + ECRS_freeUri(de->de_fi.uri); + de->de_fi.uri = uri; + de->de_dirty = 0; + return 0; +} diff --git a/dirent.c b/dirent.c @@ -32,9 +32,9 @@ struct SEMAPHORE *path_sema; */ void gn_dirent_ref(struct dirent *de) { - MUTEX_LOCK(de->de_mutex); + MUTEX_LOCK(de->de_refs_mutex); de->de_refs++; - MUTEX_UNLOCK(de->de_mutex); + MUTEX_UNLOCK(de->de_refs_mutex); } /* @@ -58,35 +58,38 @@ struct dirent *gn_dirent_get(const gchar *path) */ void gn_dirent_put(struct dirent *de) { - MUTEX_LOCK(de->de_mutex); + MUTEX_LOCK(de->de_refs_mutex); de->de_refs--; - if(de->de_refs < 1) + if(de->de_refs >= 1) { - MUTEX_UNLOCK(de->de_mutex); - MUTEX_DESTROY(de->de_mutex); - FREE(de->de_path); - SEMAPHORE_DESTROY(de->de_file_sema); - if(de->de_fi.uri != NULL) - ECRS_freeUri(de->de_fi.uri); - if(de->de_fi.meta != NULL) - ECRS_freeMetaData(de->de_fi.meta); - if(de->de_dirty) + MUTEX_UNLOCK(de->de_refs_mutex); + return; + } + MUTEX_UNLOCK(de->de_refs_mutex); + MUTEX_DESTROY(de->de_refs_mutex); + FREE(de->de_path); + SEMAPHORE_DESTROY(de->de_sema); + if(de->de_fi.uri != NULL) + ECRS_freeUri(de->de_fi.uri); + if(de->de_fi.meta != NULL) + ECRS_freeMetaData(de->de_fi.meta); + if(de->de_type == DE_DIR) + { + if(de->de_cached) { - if(de->de_type == DE_FILE) - { - close(de->de_fd); - unlink(de->de_filename); - FREE(de->de_filename); - } - else - { - g_hash_table_destroy(de->de_dir); - } + g_hash_table_destroy(de->de_dir_hash); + } + } + else + { + if(de->de_cached) + { + close(de->de_fd); + unlink(de->de_filename); + FREE(de->de_filename); } - FREE(de); - return; } - MUTEX_UNLOCK(de->de_mutex); + FREE(de); } void gn_dirent_cache_init(void) @@ -106,18 +109,21 @@ struct dirent *gn_dirent_new(const gchar *path, struct ECRS_URI *uri, de = MALLOC(sizeof(*de)); de->de_path = STRDUP(path); - de->de_mutex = MUTEX_CREATE(0); + de->de_basename = strrchr(de->de_path, G_DIR_SEPARATOR) + 1; + de->de_refs_mutex = MUTEX_CREATE(0); de->de_refs = 1; de->de_type = type; - de->de_file_sema = SEMAPHORE_CREATE(1); + de->de_sema = SEMAPHORE_CREATE(1); if(uri != NULL) { - de->de_fi.uri = ECRS_dupUri(uri); de->de_dirty = 0; + de->de_cached = 0; + de->de_fi.uri = ECRS_dupUri(uri); } else { de->de_dirty = 1; + de->de_cached = 1; if(type == DE_FILE) { char filename[] = GN_MKSTEMP_FILE; @@ -127,12 +133,14 @@ struct dirent *gn_dirent_new(const gchar *path, struct ECRS_URI *uri, } else { - de->de_dir = g_hash_table_new_full(g_str_hash, - g_str_equal, NULL, + g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)gn_dirent_put); } } - de->de_fi.meta = ECRS_dupMetaData(meta); + if(meta == NULL) + de->de_fi.meta = ECRS_createMetaData(); + else + de->de_fi.meta = ECRS_dupMetaData(meta); return de; } @@ -141,77 +149,62 @@ struct dirent *gn_dirent_new(const gchar *path, struct ECRS_URI *uri, */ void gn_dirent_cache(struct dirent *de) { - GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, - "%s: path '%s'\n", __FUNCTION__, de->de_path); /* TODO: Here we need to see if the cache has gotten too big and empty - * it */ - gn_dirent_ref(de); + * it. + * XXX: But what about diry entries?? */ if(SEMAPHORE_DOWN(path_sema, YES) == SYSERR) return; + GE_ASSERT(ectx, !g_hash_table_lookup(path_hash, de->de_path)); g_hash_table_replace(path_hash, de->de_path, de); - SEMAPHORE_UP(path_sema); -} - -struct dirent_find_data -{ - const gchar *name; - struct dirent **de; - gboolean found; -}; - -static gboolean dirent_find(struct dirent *de, const gchar *filename, - void *data) -{ - struct dirent_find_data *dfd = data; - - if(dfd->found == 1 || strcmp(filename, dfd->name) != 0) - return OK; - - dfd->found = 1; gn_dirent_ref(de); - *dfd->de = de; - return SYSERR; /* It would be nice if this actually worked */ + SEMAPHORE_UP(path_sema); } /* - * Retrieve a dirent with a reference given it's (normalized) path. + * Call 'cb' for each element in 'path', treats the empty string as "/" */ -struct dirent *gn_dirent_find(const gchar *path) +int gn_path_foreach(const gchar *path, gn_dir_foreach_callback cb, void *data) { struct dirent *de, *next_de; size_t len, plen; - gchar *ppath; - struct dirent_find_data dfd; + gchar *ppath, *filename; /* Start de off at the root */ de = root_de; gn_dirent_ref(de); - /* Root shortcut */ - if(strcmp(path, G_DIR_SEPARATOR_S) == 0) - return de; - - /* Allocate partial path buffer and start it at the root */ + /* Allocate partial path buffer */ len = strlen(path); ppath = MALLOC(len + 1); plen = 0; - /* Save pointer to next de for the callback function */ - dfd.de = &next_de; - - /* While we haven't reached the end of path, we have work to do */ - while(plen < len) + /* Loop through each path element */ + for( ; ; ) { + /* Do callback for current element */ + if(cb(de, data)) + break; + + /* Do we have any more work to do? */ + if(plen >= len || path[plen + 1] == '\0' + || path[plen + 1] == G_DIR_SEPARATOR) + { + break; + } + /* Save pointer to ppath end */ - dfd.name = &ppath[plen + 1]; + filename = &ppath[plen + 1]; /* Cat next path component */ - ppath[plen++] = G_DIR_SEPARATOR; - for(;path[plen] != '\0' && path[plen] != G_DIR_SEPARATOR;plen++) + ppath[plen] = G_DIR_SEPARATOR; + for(plen++; path[plen] != '\0' && path[plen] != G_DIR_SEPARATOR; + plen++) + { ppath[plen] = path[plen]; + } ppath[plen] = '\0'; - /* Look it up in the cache first */ + /* Look it up in the cache first */ next_de = gn_dirent_get(ppath); /* If we found it then continue */ @@ -226,154 +219,125 @@ struct dirent *gn_dirent_find(const gchar *path) } /* We need to find it by listing its parent directory, de */ - if(de->de_type != DE_DIR) - { - gn_dirent_put(de); - de = NULL; - break; - } - dfd.found = 0; - gn_directory_for_each(de, dirent_find, &dfd); + next_de = gn_directory_find(de, filename); - /* Not found? Then why did they ask us? */ - if(!dfd.found) + /* Not found? */ + if(next_de == NULL) { - GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, - "%s: not found!\n", __FUNCTION__); gn_dirent_put(de); de = NULL; break; } - - /* Continue to the next path element */ GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: found in directory '%s'\n", __FUNCTION__, next_de->de_path); + + /* Continue to the next path element */ gn_dirent_put(de); de = next_de; } /* Done */ FREE(ppath); - - return de; + if(de == NULL) + return -1; + gn_dirent_put(de); + return 0; } -struct upload_data +static gboolean dirent_find_callback(struct dirent *de, void *data) { - int count; - ECRS_FileInfo *fis; -}; + struct dirent **d = data; -static void upload_foreach(gpointer key, gpointer value, gpointer data) -{ - struct dirent *de = value; - struct upload_data *d = data; + if(*d != NULL) + gn_dirent_put(*d); + *d = de; + gn_dirent_ref(*d); + return 0; +} - (void)key; +/* + * Retrieve a dirent with a reference given it's (normalized) path. + */ +struct dirent *gn_dirent_find(const gchar *path) +{ + struct dirent *de = NULL; - if(SEMAPHORE_DOWN(de->de_file_sema, YES) == SYSERR) - return; - if(de->de_dirty) + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: called for '%s'\n", __FUNCTION__, path); + if(gn_path_foreach(path, dirent_find_callback, &de) == -1) { - if(de->de_type == DE_FILE) - { - if(de->de_fi.uri == NULL) - goto out; - } - else - { - gn_dirent_upload_locked(de); - } + if(de != NULL) + gn_dirent_put(de); + return NULL; } - d->fis[d->count].uri = ECRS_dupUri(de->de_fi.uri); - d->fis[d->count].meta = ECRS_dupMetaData(de->de_fi.meta); - d->count++; -out: - SEMAPHORE_UP(de->de_file_sema); -} - -static void upcb(guint64 totalBytes, guint64 completedBytes, cron_t eta, - void *closure) -{ - (void)totalBytes; - (void)completedBytes; - (void)eta; - (void)closure; + return de; } -static int tt(void *closure) +static gboolean lock_path_callback(struct dirent *de, void *data) { - (void)closure; + struct dirent **detmp = data; - return fuse_interrupted() ? SYSERR : OK; + if(SEMAPHORE_DOWN(de->de_sema, YES) == -1) + return 1; + gn_dirent_ref(de); + *detmp = de; + return 0; } /* - * Make a dirty dirent clean - it should be a good tradeoff to only upload - * changes to directories here and upload changes to files on release + * Locks each element in a path. */ -int gn_dirent_upload_locked(struct dirent *de) +int gn_lock_path(struct dirent *de) { - int i, ret, fd; - struct upload_data d; - char *buf, filename[] = GN_MKSTEMP_FILE; - guint64 len; - struct ECRS_URI *uri; + struct dirent *detmp = NULL; - GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, - "%s: called for '%s'\n", __FUNCTION__, de->de_path); - - /* We may be already clean */ - if(!de->de_dirty) - return 0; - - /* Collect FileInfo from hash table and make a GNUnet directory*/ - d.count = 0; - d.fis = MALLOC(g_hash_table_size(de->de_dir) * sizeof(*d.fis)); - g_hash_table_foreach(de->de_dir, upload_foreach, &d); - ret = ECRS_createDirectory(ectx, &buf, &len, d.count, d.fis, - de->de_fi.meta); - for(i = 0; i < d.count; i++) - { - ECRS_freeUri(d.fis[i].uri); - ECRS_freeMetaData(d.fis[i].meta); - } - if(ret == SYSERR) + if(gn_path_foreach(de->de_path, lock_path_callback, &detmp) == -1) { GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_ERROR, - "%s: ECRS_createDirectory failed\n", - __FUNCTION__); + "%s: failed!\n", __FUNCTION__); + /* Back out all the locks we aquired */ + if(detmp != NULL) + gn_unlock_path(detmp, GN_UNLOCK_CLEAN); return -1; } - FREE(d.fis); + return 0; +} - /* Write the GNUnet directory out to a file and upload it */ - fd = mkstemp(filename); - if(fd == -1) - { - GE_LOG_STRERROR_FILE(ectx, GE_BULK | GE_DEVELOPER - | GE_ERROR, "mkstemp", filename); - return -1; - } - write(fd, buf, len); - ret = ECRS_uploadFile(ectx, cfg, filename, NO, anonymity, priority, - -1, upcb, NULL, tt, NULL, &uri); - close(fd); - unlink(filename); - if(ret == SYSERR) +struct unlock_path_data +{ + int dirty; + struct dirent *de; +}; + +static gboolean unlock_path_callback(struct dirent *de, void *data) +{ + struct unlock_path_data *d = data; + + if(d->dirty == GN_UNLOCK_ALL_DIRTY) + de->de_dirty = 1; + else if(d->dirty == GN_UNLOCK_ANCESTORS_DIRTY && de != d->de) + de->de_dirty = 1; + SEMAPHORE_UP(de->de_sema); + gn_dirent_put(de); + return 0; +} + +/* + * Un-lock each element in a path and set the dirty state + */ +int gn_unlock_path(struct dirent *de, int dirty) +{ + struct unlock_path_data d; + + d.dirty = dirty; + d.de = de; + if(gn_path_foreach(de->de_path, unlock_path_callback, &d) == -1) { GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_ERROR, - "%s: ECRS_uploadFile failed\n", __FUNCTION__); + "%s: failed!\n", __FUNCTION__); return -1; } - - /* Update the dirent info with our new URI and mark it clean */ - if(de->de_fi.uri != NULL) - ECRS_freeUri(de->de_fi.uri); - de->de_fi.uri = uri; - g_hash_table_destroy(de->de_dir); - de->de_dirty = 0; return 0; } @@ -391,15 +355,23 @@ static void dpcb(unsigned long long totalBytes, (void)cls; } +static int tt(void *closure) +{ + (void)closure; + return fuse_interrupted() ? SYSERR : OK; +} + /* - * Download a file for writing, de_file_sema must be held. + * Download a file for writing, de_sema must be held. */ int gn_dirent_download_locked(struct dirent *de) { char filename[] = GN_MKSTEMP_FILE; - /* We may already be dirty */ - if(!de->de_dirty) + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: called for '%s'\n", + __FUNCTION__, de->de_path); + /* We may already be cached */ + if(de->de_cached) return 0; /* Do the download */ @@ -412,16 +384,20 @@ int gn_dirent_download_locked(struct dirent *de) } de->de_filename = STRDUP(filename); + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: downloading '%s'\n", __FUNCTION__, de->de_filename); if(ECRS_downloadFile(ectx, cfg, de->de_fi.uri, filename, anonymity, dpcb, NULL, tt, NULL) == SYSERR) { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_ERROR, + "%s: download failed\n", __FUNCTION__); close(de->de_fd); + unlink(de->de_filename); FREE(de->de_filename); return -1; } - /* Mark ourselves dirty, we don't have to mark our parent directory - * dirty until we sync this file back into GNUnet, see above. */ - de->de_dirty = 1; + /* Mark ourselves cached */ + de->de_cached = 1; return 0; } diff --git a/getattr.c b/getattr.c @@ -38,7 +38,8 @@ int gn_getattr(const char *path, struct stat *stbuf) memset(stbuf, 0, sizeof(*stbuf)); stbuf->st_mode = 0555 | S_IFREG; stbuf->st_nlink = 1; - stbuf->st_size = 0; + /* sysfs uses 4096 for variable sized files */ + stbuf->st_size = 4096; return 0; } @@ -55,9 +56,9 @@ int gn_getattr(const char *path, struct stat *stbuf) stbuf->st_mode = 0777; stbuf->st_mode |= de->de_type == DE_DIR ? S_IFDIR : S_IFREG; stbuf->st_nlink = 1; - if(SEMAPHORE_DOWN(de->de_file_sema, YES) == SYSERR) + if(SEMAPHORE_DOWN(de->de_sema, YES) == SYSERR) return -EIO; - if(de->de_dirty) + if(de->de_cached) { if(de->de_type == DE_FILE && disk_file_size(ectx, de->de_filename, &size, NO) == SYSERR) @@ -65,7 +66,7 @@ int gn_getattr(const char *path, struct stat *stbuf) GE_LOG(ectx, GE_BULK | GE_USER | GE_ERROR, "%s: disk_file_size failed for '%s'\n", __FUNCTION__, de->de_filename); - SEMAPHORE_UP(de->de_file_sema); + SEMAPHORE_UP(de->de_sema); gn_dirent_put(de); return -EIO; } @@ -74,7 +75,7 @@ int gn_getattr(const char *path, struct stat *stbuf) { size = ECRS_fileSize(de->de_fi.uri); } - SEMAPHORE_UP(de->de_file_sema); + SEMAPHORE_UP(de->de_sema); gn_dirent_put(de); stbuf->st_size = size; return 0; diff --git a/gnfs.h b/gnfs.h @@ -35,26 +35,33 @@ #define URI_FILE ".uri" #define URI_LEN 4 #define GN_MKSTEMP_FILE "/tmp/gnfs.XXXXXX" +#define GN_EMPTY_FILE_URI "gnunet://ecrs/chk/00000000000000000000000000000000" \ + "00000000000000000000000000000000000000000000000000000000000000000000" \ + "000.0000000000000000000000000000000000000000000000000000000000000000" \ + "000000000000000000000000000000000000000.0" struct dirent { gchar *de_path; - struct MUTEX *de_mutex; + gchar *de_basename; + struct MUTEX *de_refs_mutex; gint de_refs; gchar de_type; #define DE_FILE 'f' #define DE_DIR 'd' - /* Access of anything below this must lock de_file_sema */ - struct SEMAPHORE *de_file_sema; + /* Access of anything below this must lock de_sema */ + struct SEMAPHORE *de_sema; + /* Cached entries have their entire contents in memory or on disk */ + gboolean de_cached; + /* Dirty entires have been changed and not published in GNUnet (implies + * cached) */ gboolean de_dirty; - /* de_fi.uri is valid only if de_dirty is not set */ ECRS_FileInfo de_fi; - /* This is valid only if de_dirty is set */ union { - /* For directories */ - GHashTable *de_dir; - /* For files */ + /* For cached directories */ + GHashTable *de_dir_hash; + /* For cached files */ struct { gint de_fd; @@ -63,11 +70,11 @@ struct dirent }; }; -typedef int (*gn_dir_for_each_callback)(struct dirent *de, - const gchar *filename, void *data); +typedef gboolean (*gn_dir_foreach_callback)(struct dirent *de, void *data); extern struct GC_Configuration *cfg; extern struct GE_Context *ectx; +extern int closing; extern unsigned int anonymity; extern unsigned int priority; extern int uri_files; @@ -82,13 +89,19 @@ void gn_dirent_put(struct dirent *de); void gn_dirent_cache_init(void); void gn_dirent_cache(struct dirent *de); struct dirent *gn_dirent_find(const gchar *path); -int gn_dirent_upload_locked(struct dirent *de); +int gn_lock_path(struct dirent *de); +int gn_unlock_path(struct dirent *de, int dirty); +#define GN_UNLOCK_CLEAN 0 +#define GN_UNLOCK_ALL_DIRTY 1 +#define GN_UNLOCK_ANCESTORS_DIRTY 2 int gn_dirent_download_locked(struct dirent *de); /* directory.c */ -int gn_directory_for_each(struct dirent *de, gn_dir_for_each_callback cb, +int gn_directory_foreach(struct dirent *de, gn_dir_foreach_callback cb, void *data); -int gn_directory_insert(struct dirent *de_dir, struct dirent *de_new); +struct dirent *gn_directory_find(struct dirent *de, const gchar *filename); +int gn_directory_insert(struct dirent *de, struct dirent *dechild); +int gn_directory_upload_locked(struct dirent *de); /* FUSE function files */ int gn_getattr(const char *path, struct stat *stbuf); @@ -100,6 +113,7 @@ int gn_read(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi); int gn_write(const char *path, char *buf, size_t size, off_t offset, struct fuse_file_info *fi); +int gn_release(const char *path, struct fuse_file_info *fi); /* special_file.c */ char *gn_dirname(const char *path, char **file); diff --git a/main.c b/main.c @@ -36,6 +36,9 @@ struct GE_Context *ectx; static char *cfgFilename = DEFAULT_CLIENT_CONFIG_FILE; static char *cfgLogfile = "/tmp/gnunet_fuse.log"; +/* Flag to indicate that we are shutting down */ +int closing = 0; + /* Level of anonymity for downloading and uploading files */ unsigned int anonymity = 1; @@ -74,6 +77,7 @@ static struct fuse_operations fops = .open = gn_open, .read = gn_read, .write = gn_write, + .release = gn_release, }; static struct CommandLineOption gn_options[] = @@ -205,11 +209,15 @@ int main(int argc, char **argv) GE_LOG(ectx, GE_BULK | GE_USER | GE_DEBUG, "fuse_main returned\n"); /* Save root uri */ + closing = 1; buf = gn_get_special_file(G_DIR_SEPARATOR_S URI_FILE); - ftruncate(root_fd, 0); - lseek(root_fd, SEEK_SET, 0); - write(root_fd, buf, strlen(buf)); - FREE(buf); + if(buf != NULL) + { + ftruncate(root_fd, 0); + lseek(root_fd, SEEK_SET, 0); + write(root_fd, buf, strlen(buf)); + FREE(buf); + } out_close_root: disk_file_close(ectx, argv[i], root_fd); quit: diff --git a/mknod.c b/mknod.c @@ -28,8 +28,10 @@ int gn_mknod(const char *path, mode_t mode, dev_t rdev) { struct dirent *de, *newde; + struct ECRS_URI *uri; struct ECRS_MetaData *meta; char *parent, *file; + int ret; (void)rdev; GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: for '%s'\n", @@ -59,19 +61,17 @@ int gn_mknod(const char *path, mode_t mode, dev_t rdev) FREE(parent); return -ENOENT; } + uri = ECRS_stringToUri(ectx, GN_EMPTY_FILE_URI); meta = ECRS_createMetaData(); ECRS_addToMetaData(meta, EXTRACTOR_FILENAME, file); - newde = gn_dirent_new(path, NULL, meta, DE_FILE); + newde = gn_dirent_new(path, uri, meta, DE_FILE); ECRS_freeMetaData(meta); - if(gn_directory_insert(de, newde) == -1) - { - gn_dirent_put(de); - gn_dirent_put(newde); - FREE(parent); - return -EIO; - } + ECRS_freeUri(uri); + ret = gn_directory_insert(de, newde); gn_dirent_put(de); gn_dirent_put(newde); FREE(parent); + if(ret == -1) + return -EIO; return 0; } diff --git a/read.c b/read.c @@ -119,12 +119,12 @@ int gn_read(const char *path, char *buf, size_t size, off_t offset, size = -ENOENT; goto out; } - if(SEMAPHORE_DOWN(de->de_file_sema, YES) == SYSERR) + if(SEMAPHORE_DOWN(de->de_sema, YES) == SYSERR) { size = -EIO; goto out; } - if(de->de_dirty) + if(de->de_cached) { slen = pread(de->de_fd, buf, size, offset); if(slen == -1) @@ -155,7 +155,7 @@ int gn_read(const char *path, char *buf, size_t size, off_t offset, size = -ENODATA; } out_sema_up: - SEMAPHORE_UP(de->de_file_sema); + SEMAPHORE_UP(de->de_sema); out: gn_dirent_put(de); return size; diff --git a/readdir.c b/readdir.c @@ -37,8 +37,7 @@ struct readdir_callback_data const char *prefix; }; -static int readdir_callback(struct dirent *de, const gchar *filename, - void *data) +static int readdir_callback(struct dirent *de, void *data) { struct readdir_callback_data *d = data; @@ -46,15 +45,16 @@ static int readdir_callback(struct dirent *de, const gchar *filename, if(d->prefix != NULL) { - char *buf = MALLOC(strlen(d->prefix) + strlen(filename) + 1); + char *buf = MALLOC(strlen(d->prefix) + strlen(de->de_basename) + + 1); - sprintf(buf, "%s%s", d->prefix, filename); + sprintf(buf, "%s%s", d->prefix, de->de_basename); d->filler(d->buf, buf, NULL, 0); FREE(buf); } else { - d->filler(d->buf, filename, NULL, 0); + d->filler(d->buf, de->de_basename, NULL, 0); } return OK; } @@ -94,7 +94,7 @@ int gn_readdir(const char *path, void *buf, fuse_fill_dir_t filler, d.filler = filler; d.buf = buf; d.prefix = ".uri."; - ret = gn_directory_for_each(de, readdir_callback, &d); + ret = gn_directory_foreach(de, readdir_callback, &d); if(ret == -1) { ret = -ENOENT; @@ -104,7 +104,7 @@ int gn_readdir(const char *path, void *buf, fuse_fill_dir_t filler, d.filler = filler; d.buf = buf; d.prefix = NULL; - ret = gn_directory_for_each(de, readdir_callback, &d); + ret = gn_directory_foreach(de, readdir_callback, &d); if(ret == -1) ret = -ENOENT; out: diff --git a/release.c b/release.c @@ -0,0 +1,101 @@ +/* + * release.c - FUSE release function + * + * This file is part of gnunet-fuse. + * Copyright (C) 2007 David Barksdale + * + * gnunet-fuse is free software; you can redistribute it and/or + * modify if under the terms of version 2 of the GNU General Public License + * as published by the Free Software Foundation. + * + * gnunet-fuse is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <unistd.h> +#include <fuse.h> +#include "gnfs.h" + +static void upcb(guint64 totalBytes, guint64 completedBytes, cron_t eta, + void *closure) +{ + (void)totalBytes; + (void)completedBytes; + (void)eta; + (void)closure; +} + +static int tt(void *closure) +{ + (void)closure; + + return fuse_interrupted() ? SYSERR : OK; +} + +int gn_release(const char *path, struct fuse_file_info *fi) +{ + struct dirent *de; + struct ECRS_URI *uri; + gboolean dirty = GN_UNLOCK_CLEAN; + + (void)fi; + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: for '%s'\n", + __FUNCTION__, path); + + /* Don't do anything for special files */ + if(gn_exists_special_file(path)) + return 0; + + /* If it doesn't exist we don't care */ + de = gn_dirent_find(path); + if(de == NULL) + return 0; + if(de->de_type != DE_FILE) + { + /* XXX does this get called on directories?? */ + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: not a file\n", __FUNCTION__); + gn_dirent_put(de); + return 0; + } + + /* Lock our path */ + if(gn_lock_path(de) == -1) + return 0; + + /* If we're not dirty then we're done */ + if(!de->de_dirty) + goto out; + + /* Take this opportunity */ + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: uploading file\n", __FUNCTION__); + if(ECRS_uploadFile(ectx, cfg, de->de_filename, NO, anonymity, priority, + -1, upcb, NULL, tt, NULL, &uri) == SYSERR) + { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_ERROR, + "%s: upload failed\n", __FUNCTION__); + goto out; + } + if(de->de_fi.uri != NULL) + ECRS_freeUri(de->de_fi.uri); + de->de_fi.uri = uri; + de->de_cached = 0; + de->de_dirty = 0; + close(de->de_fd); + unlink(de->de_filename); + FREE(de->de_filename); + + /* Now we must mark every containing directory dirty */ + dirty = GN_UNLOCK_ANCESTORS_DIRTY; +out: + gn_unlock_path(de, dirty); + gn_dirent_put(de); + return 0; +} diff --git a/special_file.c b/special_file.c @@ -90,20 +90,22 @@ char *gn_get_special_file(const char *path) de = gn_dirent_find(parent); if(de == NULL) goto out; - if(SEMAPHORE_DOWN(de->de_file_sema, YES) == SYSERR) + if(SEMAPHORE_DOWN(de->de_sema, YES) == SYSERR) { gn_dirent_put(de); goto out; } if(de->de_dirty) { - /* TODO: publish data here */ - SEMAPHORE_UP(de->de_file_sema); - gn_dirent_put(de); - goto out; + if(gn_directory_upload_locked(de) == -1) + { + SEMAPHORE_UP(de->de_sema); + gn_dirent_put(de); + goto out; + } } buf = ECRS_uriToString(de->de_fi.uri); - SEMAPHORE_UP(de->de_file_sema); + SEMAPHORE_UP(de->de_sema); gn_dirent_put(de); buf = REALLOC(buf, strlen(buf) + 2); strcat(buf, "\n"); @@ -119,20 +121,34 @@ char *gn_get_special_file(const char *path) FREE(actual_file); if(de == NULL) goto out; - if(SEMAPHORE_DOWN(de->de_file_sema, YES) == SYSERR) + if(SEMAPHORE_DOWN(de->de_sema, YES) == SYSERR) { gn_dirent_put(de); goto out; } if(de->de_dirty) { - /* TODO: publish data here */ - SEMAPHORE_UP(de->de_file_sema); - gn_dirent_put(de); - goto out; + if(de->de_type == DE_DIR) + { + if(gn_directory_upload_locked(de) == -1) + { + SEMAPHORE_UP(de->de_sema); + gn_dirent_put(de); + goto out; + } + } + else + { + if(de->de_fi.uri == NULL) + { + SEMAPHORE_UP(de->de_sema); + gn_dirent_put(de); + goto out; + } + } } buf = ECRS_uriToString(de->de_fi.uri); - SEMAPHORE_UP(de->de_file_sema); + SEMAPHORE_UP(de->de_sema); gn_dirent_put(de); buf = REALLOC(buf, strlen(buf) + 2); strcat(buf, "\n"); diff --git a/write.c b/write.c @@ -56,13 +56,13 @@ int gn_write(const char *path, char *buf, size_t size, off_t offset, goto out; } - /* We must be dirty */ - if(SEMAPHORE_DOWN(de->de_file_sema, YES) == SYSERR) + /* We must be cached */ + if(SEMAPHORE_DOWN(de->de_sema, YES) == SYSERR) { size = -EIO; goto out; } - if(!de->de_dirty) + if(!de->de_cached) { if(gn_dirent_download_locked(de) == -1) { @@ -77,8 +77,11 @@ int gn_write(const char *path, char *buf, size_t size, off_t offset, size = -errno; else size = slen; + + /* Mark us dirty */ + de->de_dirty = 1; out_unlock: - SEMAPHORE_UP(de->de_file_sema); + SEMAPHORE_UP(de->de_sema); out: gn_dirent_put(de); return size;