gnunet-fuse

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

commit d1bb6193c4101f40e02f00d3faef1b5618a82eb8
parent 0805e6ac4bcb1e41f810f6d4963ecb746a6a3aca
Author: David Barksdale <amatus.amongus@gmail.com>
Date:   Thu, 28 Jun 2007 05:10:01 +0000

First steps towards write support. You can now create files and write to them, but they are not published.

Diffstat:
MMakefile.am | 2++
Mdirent.c | 135+++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------------
Mgetattr.c | 11+++++------
Mgnfs.h | 7+++++++
Mmain.c | 6++++--
Amknod.c | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mopen.c | 19++++++++++++-------
Mread.c | 12++++++------
Mspecial_file.c | 32++++++++++++++++++--------------
Awrite.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
10 files changed, 313 insertions(+), 73 deletions(-)

diff --git a/Makefile.am b/Makefile.am @@ -4,10 +4,12 @@ gnunet_fs_SOURCES = \ dirent.c \ getattr.c \ main.c \ + mknod.c \ open.c \ read.c \ readdir.c \ special_file.c \ + write.c \ gnfs.h \ gettext.h diff --git a/dirent.c b/dirent.c @@ -21,6 +21,7 @@ #include <unistd.h> #include <glib.h> #include <string.h> +#include <errno.h> #include "gnfs.h" GHashTable *path_hash; @@ -119,7 +120,7 @@ struct dirent *gn_dirent_new(const gchar *path, struct ECRS_URI *uri, de->de_dirty = 1; if(type == DE_FILE) { - char filename[] = "/tmp/gnfsXXXXXX"; + char filename[] = GN_MKSTEMP_FILE; de->de_fd = mkstemp(filename); de->de_filename = STRDUP(filename); @@ -264,9 +265,7 @@ struct upload_data ECRS_FileInfo *fis; }; -int dirent_upload_locked(struct dirent *de); - -void upload_foreach(gpointer key, gpointer value, gpointer data) +static void upload_foreach(gpointer key, gpointer value, gpointer data) { struct dirent *de = value; struct upload_data *d = data; @@ -284,18 +283,17 @@ void upload_foreach(gpointer key, gpointer value, gpointer data) } else { - dirent_upload_locked(de); + gn_dirent_upload_locked(de); } } + d->fis[d->count].uri = ECRS_dupUri(de->de_fi.uri); + d->fis[d->count].meta = ECRS_dupMetaData(de->de_fi.meta); d->count++; - d->fis = REALLOC(d->fis, d->count * sizeof(*d->fis)); - d->fis[d->count - 1].uri = ECRS_dupUri(de->de_fi.uri); - d->fis[d->count - 1].meta = ECRS_dupMetaData(de->de_fi.meta); out: SEMAPHORE_UP(de->de_file_sema); } -void upcb(guint64 totalBytes, guint64 completedBytes, cron_t eta, +static void upcb(guint64 totalBytes, guint64 completedBytes, cron_t eta, void *closure) { (void)totalBytes; @@ -304,65 +302,126 @@ void upcb(guint64 totalBytes, guint64 completedBytes, cron_t eta, (void)closure; } -int tt(void *closure) +static int tt(void *closure) { (void)closure; return fuse_interrupted() ? SYSERR : OK; } -int dirent_upload_locked(struct dirent *de) +/* + * 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 + */ +int gn_dirent_upload_locked(struct dirent *de) { - int i, fd; + int i, ret, fd; struct upload_data d; - char *buf, filename[] = "/tmp/gnfsXXXXXX"; + char *buf, filename[] = GN_MKSTEMP_FILE; guint64 len; struct ECRS_URI *uri; + 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 = NULL; + d.fis = MALLOC(g_hash_table_size(de->de_dir) * sizeof(*d.fis)); g_hash_table_foreach(de->de_dir, upload_foreach, &d); - if(ECRS_createDirectory(ectx, &buf, &len, d.count, d.fis, - de->de_fi.meta) == SYSERR) - goto out; + 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) + { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_ERROR, + "%s: ECRS_createDirectory failed\n", + __FUNCTION__); + return -1; + } + FREE(d.fis); + + /* Write the GNUnet directory out to a file and upload it */ fd = mkstemp(filename); if(fd == -1) - goto out; + { + 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); - if(ECRS_uploadFile(ectx, cfg, filename, NO, anonymity, priority, - -1, upcb, NULL, tt, NULL, &uri) == SYSERR) - goto out_unlink_file; + unlink(filename); + if(ret == SYSERR) + { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_ERROR, + "%s: ECRS_uploadFile 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; -out_unlink_file: - unlink(filename); -out: - for(i = 0; i < d.count; i++) - { - ECRS_freeUri(d.fis[i].uri); - ECRS_freeMetaData(d.fis[i].meta); - } - FREE(d.fis); return 0; } +static void dpcb(unsigned long long totalBytes, + unsigned long long completedBytes, cron_t eta, + unsigned long long lastBlockOffset, const char *lastBlock, + unsigned int lastBlockSize, void *cls) +{ + (void)totalBytes; + (void)completedBytes; + (void)eta; + (void)lastBlockOffset; + (void)lastBlock; + (void)lastBlockSize; + (void)cls; +} + /* - * 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 + * Download a file for writing, de_file_sema must be held. */ -int gn_dirent_upload(struct dirent *de) +int gn_dirent_download_locked(struct dirent *de) { - int ret; + char filename[] = GN_MKSTEMP_FILE; - if(SEMAPHORE_DOWN(de->de_file_sema, YES) == SYSERR) + /* We may already be dirty */ + if(!de->de_dirty) + return 0; + + /* Do the download */ + de->de_fd = mkstemp(filename); + if(de->de_fd == -1) + { + GE_LOG_STRERROR_FILE(ectx, GE_BULK | GE_DEVELOPER + | GE_ERROR, "mkstemp", filename); return -1; - ret = dirent_upload_locked(de); - SEMAPHORE_UP(de->de_file_sema); - return ret; + } + de->de_filename = STRDUP(filename); + + if(ECRS_downloadFile(ectx, cfg, de->de_fi.uri, filename, anonymity, + dpcb, NULL, tt, NULL) == SYSERR) + { + close(de->de_fd); + 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; + return 0; } diff --git a/getattr.c b/getattr.c @@ -19,7 +19,6 @@ */ #include <sys/stat.h> -#include <stdio.h> #include <string.h> #include <errno.h> #include <fuse.h> @@ -30,8 +29,8 @@ int gn_getattr(const char *path, struct stat *stbuf) struct dirent *de; guint64 size = 0; - GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "getattr for '%s'\n", - path); + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: for '%s'\n", + __FUNCTION__, path); /* Check to see if this is a special file */ if(gn_exists_special_file(path)) @@ -47,7 +46,7 @@ int gn_getattr(const char *path, struct stat *stbuf) de = gn_dirent_find(path); if(de == NULL) { - GE_LOG(ectx, GE_BULK | GE_USER | GE_ERROR, + GE_LOG(ectx, GE_BULK | GE_USER | GE_DEBUG, "%s: could not find path '%s'\n", __FUNCTION__, path); return -ENOENT; } @@ -57,7 +56,7 @@ int gn_getattr(const char *path, struct stat *stbuf) 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) - return -ENOENT; + return -EIO; if(de->de_dirty) { if(de->de_type == DE_FILE && disk_file_size(ectx, @@ -68,7 +67,7 @@ int gn_getattr(const char *path, struct stat *stbuf) __FUNCTION__, de->de_filename); SEMAPHORE_UP(de->de_file_sema); gn_dirent_put(de); - return -ENOENT; + return -EIO; } } else diff --git a/gnfs.h b/gnfs.h @@ -34,6 +34,7 @@ #define URI_FILE ".uri" #define URI_LEN 4 +#define GN_MKSTEMP_FILE "/tmp/gnfs.XXXXXX" struct dirent { @@ -81,6 +82,8 @@ 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_dirent_download_locked(struct dirent *de); /* directory.c */ int gn_directory_for_each(struct dirent *de, gn_dir_for_each_callback cb, @@ -91,11 +94,15 @@ int gn_directory_insert(struct dirent *de_dir, struct dirent *de_new); int gn_getattr(const char *path, struct stat *stbuf); int gn_readdir(const char *path, void *buf, fuse_fill_dir_t filler, off_t offset, struct fuse_file_info *fi); +int gn_mknod(const char *path, mode_t mode, dev_t rdev); int gn_open(const char *path, struct fuse_file_info *fi); 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); /* special_file.c */ +char *gn_dirname(const char *path, char **file); int gn_exists_special_file(const char *path); char *gn_get_special_file(const char *path); diff --git a/main.c b/main.c @@ -21,6 +21,7 @@ #include <stdio.h> #include <string.h> #include <unistd.h> +#include <errno.h> #include <glib.h> #include <fuse.h> #include <GNUnet/gnunet_directories.h> @@ -59,7 +60,6 @@ int getopt_configure_argv(CommandLineProcessorContext *ctx, void *scls, (void)scls; (void)cmdLineOption; - fuse_argv = REALLOC(fuse_argv, sizeof(char *) * (fuse_argc + 2)); fuse_argv[fuse_argc] = (char *)value; fuse_argc++; fuse_argv[fuse_argc] = NULL; @@ -70,8 +70,10 @@ static struct fuse_operations fops = { .getattr = gn_getattr, .readdir = gn_readdir, + .mknod = gn_mknod, .open = gn_open, .read = gn_read, + .write = gn_write, }; static struct CommandLineOption gn_options[] = @@ -102,7 +104,7 @@ int main(int argc, char **argv) /* Initialize fuse options */ fuse_argc = 1; - fuse_argv = MALLOC(sizeof(char *) * (fuse_argc + 1)); + fuse_argv = MALLOC(sizeof(char *) * argc); fuse_argv[0] = argv[0]; fuse_argv[1] = NULL; diff --git a/mknod.c b/mknod.c @@ -0,0 +1,77 @@ +/* + * mknod.c - FUSE mknod 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 <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fuse.h> +#include "gnfs.h" + +int gn_mknod(const char *path, mode_t mode, dev_t rdev) +{ + struct dirent *de, *newde; + struct ECRS_MetaData *meta; + char *parent, *file; + + (void)rdev; + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: for '%s'\n", + __FUNCTION__, path); + + /* We only support regular files */ + if(!S_ISREG(mode)) + return -ENOTSUP; + + /* Check for special file */ + if(gn_exists_special_file(path)) + return -EEXIST; + + /* Check for existing file */ + de = gn_dirent_find(path); + if(de != NULL) + { + gn_dirent_put(de); + return -EEXIST; + } + + /* Create new file */ + parent = gn_dirname(path, &file); + de = gn_dirent_find(parent); + if(de == NULL) + { + FREE(parent); + return -ENOENT; + } + meta = ECRS_createMetaData(); + ECRS_addToMetaData(meta, EXTRACTOR_FILENAME, file); + newde = gn_dirent_new(path, NULL, 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; + } + gn_dirent_put(de); + gn_dirent_put(newde); + FREE(parent); + return 0; +} diff --git a/open.c b/open.c @@ -18,31 +18,34 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <stdio.h> -#include <fuse.h> #include <string.h> #include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fuse.h> #include "gnfs.h" int gn_open(const char *path, struct fuse_file_info *fi) { struct dirent *de; - (void)fi; GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: for '%s'\n", __FUNCTION__, path); /* Check for special file */ if(gn_exists_special_file(path)) + { + if(fi->flags & O_WRONLY) + return -EACCES; + if((fi->flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + return -EEXIST; return 0; + } + /* Check for existing file */ de = gn_dirent_find(path); if(de == NULL) - { - GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, - "%s: file not found\n", __FUNCTION__); return -ENOENT; - } if(de->de_type != DE_FILE) { GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, @@ -51,5 +54,7 @@ int gn_open(const char *path, struct fuse_file_info *fi) return -ENOENT; } gn_dirent_put(de); + if((fi->flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) + return -EEXIST; return 0; } diff --git a/read.c b/read.c @@ -82,8 +82,8 @@ int gn_read(const char *path, char *buf, size_t size, off_t offset, (void)fi; - GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "read '%s' %d bytes\n", - path, size); + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: called for '%s' %d bytes\n", __FUNCTION__, path, size); /* Check for special file */ special = gn_get_special_file(path); @@ -109,19 +109,19 @@ int gn_read(const char *path, char *buf, size_t size, off_t offset, if(de == NULL) { GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, - "read: file not found\n"); + "%s: file not found\n", __FUNCTION__); return -ENOENT; } if(de->de_type != DE_FILE) { GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, - "read: not a file\n"); + "%s: not a file\n", __FUNCTION__); size = -ENOENT; goto out; } if(SEMAPHORE_DOWN(de->de_file_sema, YES) == SYSERR) { - size = -EINTR; + size = -EIO; goto out; } if(de->de_dirty) @@ -151,7 +151,7 @@ int gn_read(const char *path, char *buf, size_t size, off_t offset, if(ret != OK) { GE_LOG(ectx, GE_BULK | GE_USER | GE_ERROR, - "%s: failed to download directory\n", __FUNCTION__); + "%s: failed to download file\n", __FUNCTION__); size = -ENODATA; } out_sema_up: diff --git a/special_file.c b/special_file.c @@ -23,6 +23,22 @@ #include <GNUnet/gnunet_ecrs_lib.h> #include "gnfs.h" +char *gn_dirname(const char *path, char **file) +{ + char *parent, *slash; + + parent = STRDUP(path); + slash = strrchr(parent, G_DIR_SEPARATOR); + if(slash != NULL) + { + slash[0] = '\0'; + slash++; + } + if(file != NULL) + *file = slash; + return parent; +} + /* Checks to see if path is the path to a special file */ int gn_exists_special_file(const char *path) { @@ -30,13 +46,7 @@ int gn_exists_special_file(const char *path) char *file, *parent; int ret = 0; - /* Break path into parent and file (dirname and basename kinda) */ - parent = STRDUP(path); - file = strrchr(parent, G_DIR_SEPARATOR); - if(file == NULL) - goto out; - file[0] = '\0'; - file++; + parent = gn_dirname(path, &file); /* Check for special file name */ if(strcmp(file, URI_FILE) == 0) @@ -71,13 +81,7 @@ char *gn_get_special_file(const char *path) struct dirent *de; char *buf = NULL, *file, *parent; - /* Break path into parent and file (dirname and basename kinda) */ - parent = STRDUP(path); - file = strrchr(parent, G_DIR_SEPARATOR); - if(file == NULL) - goto out; - file[0] = '\0'; - file++; + parent = gn_dirname(path, &file); /* Check for special file name */ if(strcmp(file, URI_FILE) == 0) diff --git a/write.c b/write.c @@ -0,0 +1,85 @@ +/* + * write.c - FUSE write 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 + */ + +#define _XOPEN_SOURCE 500 +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <fuse.h> +#include "gnfs.h" + +int gn_write(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + struct dirent *de; + ssize_t slen; + + (void)fi; + + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: called for '%s' %d bytes\n", __FUNCTION__, path, size); + + /* Check for special file */ + if(gn_exists_special_file(path)) + return -EACCES; + + /* Lookup dirent for path */ + de = gn_dirent_find(path); + if(de == NULL) + { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: file not found\n", __FUNCTION__); + return -ENOENT; + } + if(de->de_type != DE_FILE) + { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: not a file\n", __FUNCTION__); + size = -ENOENT; + goto out; + } + + /* We must be dirty */ + if(SEMAPHORE_DOWN(de->de_file_sema, YES) == SYSERR) + { + size = -EIO; + goto out; + } + if(!de->de_dirty) + { + if(gn_dirent_download_locked(de) == -1) + { + size = -EIO; + goto out_unlock; + } + } + + /* Perform write on temp file */ + slen = pwrite(de->de_fd, buf, size, offset); + if(slen == -1) + size = -errno; + else + size = slen; +out_unlock: + SEMAPHORE_UP(de->de_file_sema); +out: + gn_dirent_put(de); + return size; +}