gnunet-fuse

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

commit 0cda35edf4a02e346f751c169e4fec311c4692cd
parent d9bd9a1c1c4d1c9938e522c2d53c07ffed2db3be
Author: David Barksdale <amatus.amongus@gmail.com>
Date:   Fri, 13 Jul 2007 03:05:50 +0000

Added support for unlinking files, creating directories, and removing directories

Diffstat:
MChangeLog | 3+++
MMakefile.am | 4++++
Mdirectory.c | 45+++++++++++++++++++++++++++++++++++++++++++--
Mdirent.c | 16++++++++++++++--
Mgetattr.c | 42+++++++++++++++++++++++-------------------
Mgnfs.h | 8+++++++-
Mmain.c | 4++++
Amkdir.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mmknod.c | 2+-
Mread.c | 22+++++++++++++++++++---
Armdir.c | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aunlink.c | 68++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Autimens.c | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
13 files changed, 407 insertions(+), 28 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,6 @@ +2007-07-12 David Barksdale <amatus@gnu.org> 0.7.2-3 +* Added support for unlinking files, creating directories, and removing +directories 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 diff --git a/Makefile.am b/Makefile.am @@ -4,12 +4,16 @@ gnunet_fs_SOURCES = \ dirent.c \ getattr.c \ main.c \ + mkdir.c \ mknod.c \ open.c \ read.c \ readdir.c \ release.c \ + rmdir.c \ special_file.c \ + unlink.c \ + utimens.c \ write.c \ gnfs.h \ gettext.h diff --git a/directory.c b/directory.c @@ -96,7 +96,7 @@ static int dir_cache_cb(const ECRS_FileInfo *fi, const HashCode512 *key, /* 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); + gn_dirent_cache_insert(de); } /* Add it to the directory's list (steals our ref)*/ @@ -245,13 +245,54 @@ int gn_directory_insert(struct dirent *de, struct dirent *dechild) g_hash_table_replace(de->de_dir_hash, dechild->de_basename, dechild); /* Cache the dirent */ - gn_dirent_cache(dechild); + gn_dirent_cache_insert(dechild); /* Mark our path dirty */ gn_unlock_path(de, GN_UNLOCK_ALL_DIRTY); return 0; } +int gn_directory_remove(struct dirent *de, struct dirent *dechild) +{ + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: for '%s'\n", + __FUNCTION__, dechild->de_path); + + /* 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_ERROR, + "%s: failed to cache parent dir\n", + __FUNCTION__); + goto out_err; + } + } + + /* Remove from dir_hash */ + if(!g_hash_table_remove(de->de_dir_hash, dechild->de_basename)) + { + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_ERROR, + "%s: not found in dir_hash\n", + __FUNCTION__); + goto out_err; + } + + /* Remove from dirent cache */ + gn_dirent_cache_remove(dechild); + + /* Mark our path dirty */ + gn_unlock_path(de, GN_UNLOCK_ALL_DIRTY); + return 0; +out_err: + gn_unlock_path(de, GN_UNLOCK_CLEAN); + return -1; +} + static void upcb(guint64 totalBytes, guint64 completedBytes, cron_t eta, void *closure) { diff --git a/dirent.c b/dirent.c @@ -133,7 +133,8 @@ struct dirent *gn_dirent_new(const gchar *path, struct ECRS_URI *uri, } else { - g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + de->de_dir_hash = g_hash_table_new_full(g_str_hash, + g_str_equal, NULL, (GDestroyNotify)gn_dirent_put); } } @@ -147,7 +148,7 @@ struct dirent *gn_dirent_new(const gchar *path, struct ECRS_URI *uri, /* * Add a dirent to the cache */ -void gn_dirent_cache(struct dirent *de) +void gn_dirent_cache_insert(struct dirent *de) { /* TODO: Here we need to see if the cache has gotten too big and empty * it. @@ -161,6 +162,17 @@ void gn_dirent_cache(struct dirent *de) } /* + * Remove a dirent from the cache + */ +void gn_dirent_cache_remove(struct dirent *de) +{ + if(SEMAPHORE_DOWN(path_sema, YES) == SYSERR) + return; + g_hash_table_remove(path_hash, de->de_path); + SEMAPHORE_UP(path_sema); +} + +/* * Call 'cb' for each element in 'path', treats the empty string as "/" */ int gn_path_foreach(const gchar *path, gn_dir_foreach_callback cb, void *data) diff --git a/getattr.c b/getattr.c @@ -27,7 +27,7 @@ int gn_getattr(const char *path, struct stat *stbuf) { struct dirent *de; - guint64 size = 0; + int ret = 0; GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, "%s: for '%s'\n", __FUNCTION__, path); @@ -52,31 +52,35 @@ int gn_getattr(const char *path, struct stat *stbuf) return -ENOENT; } - memset(stbuf, 0, sizeof(*stbuf)); - stbuf->st_mode = 0777; - stbuf->st_mode |= de->de_type == DE_DIR ? S_IFDIR : S_IFREG; - stbuf->st_nlink = 1; + /* If it's a cached file just call stat */ if(SEMAPHORE_DOWN(de->de_sema, YES) == SYSERR) + { + gn_dirent_put(de); return -EIO; - if(de->de_cached) + } + if(de->de_cached && de->de_type == DE_FILE) { - if(de->de_type == DE_FILE && disk_file_size(ectx, - de->de_filename, &size, NO) == SYSERR) + ret = stat(path, stbuf); + if(ret == -1) { - 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_sema); - gn_dirent_put(de); - return -EIO; + ret = -errno; + GE_LOG_STRERROR(ectx, GE_BULK | GE_USER | GE_ERROR, + "stat"); + goto out; } + goto out; } + + memset(stbuf, 0, sizeof(*stbuf)); + stbuf->st_mode = 0777; + stbuf->st_mode |= de->de_type == DE_DIR ? S_IFDIR : S_IFREG; + stbuf->st_nlink = 1; + if(de->de_fi.uri != NULL) + stbuf->st_size = ECRS_fileSize(de->de_fi.uri); else - { - size = ECRS_fileSize(de->de_fi.uri); - } + stbuf->st_size = 0; +out: SEMAPHORE_UP(de->de_sema); gn_dirent_put(de); - stbuf->st_size = size; - return 0; + return ret; } diff --git a/gnfs.h b/gnfs.h @@ -87,7 +87,8 @@ struct dirent *gn_dirent_get(const gchar *path); void gn_dirent_ref(struct dirent *de); void gn_dirent_put(struct dirent *de); void gn_dirent_cache_init(void); -void gn_dirent_cache(struct dirent *de); +void gn_dirent_cache_insert(struct dirent *de); +void gn_dirent_cache_remove(struct dirent *de); struct dirent *gn_dirent_find(const gchar *path); int gn_lock_path(struct dirent *de); int gn_unlock_path(struct dirent *de, int dirty); @@ -101,6 +102,7 @@ int gn_directory_foreach(struct dirent *de, gn_dir_foreach_callback cb, void *data); struct dirent *gn_directory_find(struct dirent *de, const gchar *filename); int gn_directory_insert(struct dirent *de, struct dirent *dechild); +int gn_directory_remove(struct dirent *de, struct dirent *dechild); int gn_directory_upload_locked(struct dirent *de); /* FUSE function files */ @@ -108,6 +110,10 @@ 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_mkdir(const char *path, mode_t mode); +int gn_unlink(const char *path); +int gn_rmdir(const char *path); +int gn_utimens(const char *path, const struct timespec ts[2]); 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); diff --git a/main.c b/main.c @@ -74,6 +74,10 @@ static struct fuse_operations fops = .getattr = gn_getattr, .readdir = gn_readdir, .mknod = gn_mknod, + .mkdir = gn_mkdir, + .unlink = gn_unlink, + .rmdir = gn_rmdir, + .utimens = gn_utimens, .open = gn_open, .read = gn_read, .write = gn_write, diff --git a/mkdir.c b/mkdir.c @@ -0,0 +1,71 @@ +/* + * mkdir.c - FUSE mkdir 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_mkdir(const char *path, mode_t mode) +{ + struct dirent *de, *newde; + struct ECRS_MetaData *meta; + char *parent, *file; + int ret; + + (void)mode; + 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)) + return -EEXIST; + + /* Check for existing file */ + de = gn_dirent_find(path); + if(de != NULL) + { + gn_dirent_put(de); + return -EEXIST; + } + + /* Create new directory */ + 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); + ECRS_addToMetaData(meta, EXTRACTOR_MIMETYPE, GNUNET_DIRECTORY_MIME); + newde = gn_dirent_new(path, NULL, meta, DE_DIR); + ECRS_freeMetaData(meta); + 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/mknod.c b/mknod.c @@ -64,13 +64,13 @@ int gn_mknod(const char *path, mode_t mode, dev_t rdev) uri = ECRS_stringToUri(ectx, GN_EMPTY_FILE_URI); meta = ECRS_createMetaData(); ECRS_addToMetaData(meta, EXTRACTOR_FILENAME, file); + FREE(parent); newde = gn_dirent_new(path, uri, meta, DE_FILE); ECRS_freeMetaData(meta); 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 @@ -45,8 +45,17 @@ static void dpcb(unsigned long long totalBytes, (void)completedBytes; (void)eta; - GE_ASSERT(ectx, !(block_end < d->offset)); - GE_ASSERT(ectx, !(lastBlockOffset > buf_end)); + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: lastBlockOffset %llu lastBlockSize %d\n", __FUNCTION__, + lastBlockOffset, lastBlockSize); + + /* Check if this block is entirely before the buffer */ + if(block_end < d->offset) + return; + + /* Check if this block is entirely after the buffer */ + if(lastBlockOffset > buf_end) + return; /* Chop off residue at beginning of block */ if(lastBlockOffset < d->offset) @@ -83,7 +92,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, - "%s: called for '%s' %d bytes\n", __FUNCTION__, path, size); + "%s: called for '%s' %u bytes %lld offset\n", __FUNCTION__, + path, size, offset); /* Check for special file */ special = gn_get_special_file(path); @@ -146,8 +156,14 @@ int gn_read(const char *path, char *buf, size_t size, off_t offset, d.buf = buf; d.size = size; d.offset = offset; + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: calling ECRS_downloadPartialFile %u bytes %lld offset\n", + __FUNCTION__, size, offset); ret = ECRS_downloadPartialFile(ectx, cfg, de->de_fi.uri, "/dev/null", anonymity, offset, size, YES, dpcb, &d, tt, NULL); + GE_LOG(ectx, GE_BULK | GE_DEVELOPER | GE_DEBUG, + "%s: ECRS_downloadPartialFile returned %d\n", + __FUNCTION__, ret); if(ret != OK) { GE_LOG(ectx, GE_BULK | GE_USER | GE_ERROR, diff --git a/rmdir.c b/rmdir.c @@ -0,0 +1,78 @@ +/* + * rmdir.c - FUSE rmdir 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" + +static gboolean rmdir_callback(struct dirent *de, void *data) +{ + int *empty = data; + + (void)de; + *empty = 0; + return 1; +} + +int gn_rmdir(const char *path) +{ + struct dirent *de, *dechild; + char *parent, *file; + int ret, empty = 1; + + 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)) + return -ENOTDIR; + + /* Check for existing file */ + dechild = gn_dirent_find(path); + if(dechild == NULL) + return -ENOENT; + + /* Can't rmdir a non-empty directory */ + gn_directory_foreach(dechild, rmdir_callback, &empty); + if(!empty) + { + gn_dirent_put(dechild); + return -ENOTEMPTY; + } + + /* Remove directory */ + parent = gn_dirname(path, &file); + de = gn_dirent_find(parent); + FREE(parent); + if(de == NULL) + { + gn_dirent_put(dechild); + return -ENOENT; + } + ret = gn_directory_remove(de, dechild); + gn_dirent_put(dechild); + gn_dirent_put(de); + if(ret == -1) + return -EIO; + return 0; +} diff --git a/unlink.c b/unlink.c @@ -0,0 +1,68 @@ +/* + * unlink.c - FUSE unlink 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_unlink(const char *path) +{ + struct dirent *de, *dechild; + char *parent, *file; + int ret; + + 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)) + return -EPERM; + + /* Check for existing file */ + dechild = gn_dirent_find(path); + if(dechild == NULL) + return -ENOENT; + + /* Can't unlink a directory */ + if(dechild->de_type != DE_FILE) + { + gn_dirent_put(dechild); + return -EPERM; + } + + /* Remove file from parent dir */ + parent = gn_dirname(path, &file); + de = gn_dirent_find(parent); + FREE(parent); + if(de == NULL) + { + gn_dirent_put(dechild); + return -ENOENT; + } + ret = gn_directory_remove(de, dechild); + gn_dirent_put(dechild); + gn_dirent_put(de); + if(ret == -1) + return -EIO; + return 0; +} diff --git a/utimens.c b/utimens.c @@ -0,0 +1,72 @@ +/* + * utimens.c - FUSE utimens 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 _GNU_SOURCE +#include <sys/time.h> +#include <string.h> +#include <errno.h> +#include <fuse.h> +#include "gnfs.h" + +int gn_utimens(const char *path, const struct timespec ts[2]) +{ + struct dirent *de; + struct timeval tv[2]; + int ret = 0; + + 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)) + return -EACCES; + + /* Get file or dir */ + de = gn_dirent_find(path); + if(de == NULL) + return -ENOENT; + + /* If it's a cached file just call utime */ + if(SEMAPHORE_DOWN(de->de_sema, YES) == SYSERR) + { + gn_dirent_put(de); + return -EIO; + } + if(de->de_cached && de->de_type == DE_FILE) + { + TIMESPEC_TO_TIMEVAL(&tv[0], &ts[0]); + TIMESPEC_TO_TIMEVAL(&tv[1], &ts[1]); + ret = utimes(path, tv); + if(ret == -1) + { + ret = -errno; + GE_LOG_STRERROR(ectx, GE_BULK | GE_USER | GE_ERROR, + "utimes"); + goto out; + } + goto out; + } + + /* For now we do nothing otherwise */ +out: + SEMAPHORE_UP(de->de_sema); + gn_dirent_put(de); + return ret; +}