diff options
-rw-r--r-- | lufis/.cvsignore | 1 | ||||
-rw-r--r-- | lufis/Makefile | 10 | ||||
-rw-r--r-- | lufis/README | 32 | ||||
-rw-r--r-- | lufis/dircache.c | 474 | ||||
-rw-r--r-- | lufis/dircache.h | 75 | ||||
-rw-r--r-- | lufis/list.h | 171 | ||||
-rw-r--r-- | lufis/lufis.c | 742 | ||||
-rw-r--r-- | lufis/options.c | 301 |
8 files changed, 1806 insertions, 0 deletions
diff --git a/lufis/.cvsignore b/lufis/.cvsignore new file mode 100644 index 0000000..dc5eb9e --- /dev/null +++ b/lufis/.cvsignore @@ -0,0 +1 @@ +lufis diff --git a/lufis/Makefile b/lufis/Makefile new file mode 100644 index 0000000..713e509 --- /dev/null +++ b/lufis/Makefile @@ -0,0 +1,10 @@ +CC = gcc +CFLAGS = -Wall -W -g +LDLIBS = -lfuse -lpthread -ldl -rdynamic +CPPFLAGS := -D_FILE_OFFSET_BITS=64 +#CPPFLAGS += -DDEBUG + +lufis: lufis.o options.o dircache.o + +clean: + rm -f *.o lufis diff --git a/lufis/README b/lufis/README new file mode 100644 index 0000000..b3ee9c7 --- /dev/null +++ b/lufis/README @@ -0,0 +1,32 @@ +This is a modified LUFS daemon, which uses the FUSE kernel module. + +Disclaimer: + +This is currently just a proof-of-concept implementation, not a +finished product. That said it seems perfectly stable. Comments, are +welcome at <mszeredi@inf.bme.hu>. + +Installation: + + - install lufs (0.9.7) + - install fuse (1.1) + - enter 'make' in this directory + +Usage, for exampe: + + ./lufis fs=sshfs,host=kempelen,username=mszeredi /mnt/lufis/ -s + +The '-s' option is important! + +For help on FUSE specific options see: + + ./lufis fs=localfs -h + +and + + ./lufis fs=localfs / -- -h + +For LUFS specific options (can only be specified in the first +argument) see the README in the LUFS distribution + + diff --git a/lufis/dircache.c b/lufis/dircache.c new file mode 100644 index 0000000..0498355 --- /dev/null +++ b/lufis/dircache.c @@ -0,0 +1,474 @@ +/* + * dircache.c + * Copyright (C) 2002 Florin Malita <mali@go.ro> + * + * This file is part of LUFS, a free userspace filesystem implementation. + * See http://lufs.sourceforge.net/ for updates. + * + * LUFS 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 of the License, or + * (at your option) any later version. + * + * LUFS 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 <stdlib.h> +#include <string.h> +#include <pthread.h> + +#include <sys/stat.h> + +#include <lufs/proto.h> +#include <lufs/fs.h> + +#include "list.h" +#include "dircache.h" + +static char root_dir[]="/"; +static char current_dir[]="."; + +static unsigned long +hash(char *name){ + unsigned long res = 0; + unsigned int i; + + for(i = 0; i < strlen(name); i++) + if(name[i] != '/') + res = 0x21413 * (res + name[i]); + + return res % NBUCKETS; +} + +static void +delete_dir(struct directory *d){ + struct list_head *p, *tmp; + struct direntry *de; + + TRACE("in"); + list_for_each_safe(p, tmp, &d->d_entries){ + de = list_entry(p, struct direntry, e_list); + list_del(&de->e_list); + free(de->e_name); + if(de->e_link) + free(de->e_link); + free(de); + } + + list_del(&d->d_list); + free(d->d_name); + free(d); + + TRACE("out"); +} + +struct dir_cache* +lu_cache_create(struct list_head *cfg){ + struct dir_cache *cache; + int i; + const char *c; + + TRACE("creating dir cache..."); + + if(!(cache = malloc(sizeof(struct dir_cache)))) + return NULL; + + memset(cache, 0, sizeof(struct dir_cache)); + + for(i = 0; i < NBUCKETS; i++) + INIT_LIST_HEAD(&cache->buckets[i]); + + pthread_mutex_init(&cache->lock, NULL); + + cache->ttl = DEF_TTL; + if((c = lu_opt_getchar(cfg, "LUFSD", "DirCacheTTL")) && atoi(c)) + cache->ttl = atoi(c); + if((c = lu_opt_getchar(cfg, "MOUNT", "dir_cache_ttl")) && atoi(c)) + cache->ttl = atoi(c); + + cache->entries = DEF_NENTRIES; + if((c = lu_opt_getchar(cfg, "LUFSD", "DirCacheEntries")) && atoi(c)) + cache->entries = atoi(c); + if((c = lu_opt_getchar(cfg, "MOUNT", "dir_cache_entries")) && atoi(c)) + cache->entries = atoi(c); + + TRACE("entries: %d, ttl: %d", cache->entries, cache->ttl); + + return cache; +} + +void +lu_cache_destroy(struct dir_cache *cache){ + struct list_head *p, *tmp; + int i; + + for(i = 0; i < NBUCKETS; i++){ + list_for_each_safe(p, tmp, &cache->buckets[i]){ + delete_dir(list_entry(p, struct directory, d_list)); + } + } + + free(cache); +} + +static struct directory* +search(struct dir_cache *cache, char *dir){ + struct list_head *p, *tmp; + struct directory *d; + int hsh; + + hsh = hash(dir); + + TRACE("search %s in bucket %u, size=%u", dir, hsh, cache->lengths[hsh]); + + list_for_each_safe(p, tmp, &cache->buckets[hsh]){ + d = list_entry(p, struct directory, d_list); + + if(time(NULL) - d->d_stamp >= (unsigned long) cache->ttl){ + TRACE("%s expired...", d->d_name); + delete_dir(d); + cache->lengths[hsh]--; + TRACE("directory deleted"); + }else if(!strcmp(dir, d->d_name)){ + TRACE("%s found", dir); + d->d_stamp = time(NULL); + return d; + } + } + + TRACE("dir not found"); + return NULL; +} + +int +lu_cache_lookup(struct dir_cache *cache, char *dir, char *file, struct lufs_fattr *fattr, char *link, int buflen){ + struct directory *d; + struct direntry *de; + struct list_head *p; + int res = -1; + + TRACE("looking up %s in dir %s", file, dir); + + pthread_mutex_lock(&cache->lock); + + if(!(d = search(cache, dir))) + goto out; + + list_for_each(p, &d->d_entries){ + de = list_entry(p, struct direntry, e_list); + if(!strcmp(file, de->e_name)){ + TRACE("file found"); + + memcpy(fattr, &de->e_attr, sizeof(struct lufs_fattr)); + if(link){ + if(de->e_link){ + if(snprintf(link, buflen, "%s", de->e_link) >= buflen){ + WARN("link too long!"); + link[buflen - 1] =0; + } + }else{ + link[0] = 0; + } + } + + res = 0; + goto out; + } + } + + TRACE("file not found!"); + + out: + pthread_mutex_unlock(&cache->lock); + return res; +} + +static void +shrink(struct dir_cache *cache, int hsh){ + struct directory *dir; + + TRACE("shrinking bucket %u, len=%u", hsh, cache->lengths[hsh]); + + if(list_empty(&cache->buckets[hsh])) + return; + + dir = list_entry(cache->buckets[hsh].prev, struct directory, d_list); + + TRACE("deleting dir %s", dir->d_name); + + delete_dir(dir); + cache->lengths[hsh]--; +} + +static void +check_dir(struct directory *d){ + struct list_head *p, *tmp; + struct direntry *e; + struct lufs_fattr dummy; + int dot = 0, dotdot = 0; + + memset(&dummy, 0, sizeof(struct lufs_fattr)); + dummy.f_nlink = 1; + dummy.f_uid = dummy.f_gid = 1; + dummy.f_mode = S_IFDIR | S_IRWXU | S_IRGRP | S_IXGRP; + dummy.f_mtime = dummy.f_atime = dummy.f_ctime = time(NULL); + dummy.f_size = 512; + + do{ + list_for_each_safe(p, tmp, &d->d_entries){ + e = list_entry(p, struct direntry, e_list); + + if(!strcmp(e->e_name, ".")){ + TRACE("'.' entry found"); + list_del(&e->e_list); + list_add(&e->e_list, &d->d_entries); + dot = 1; + continue; + } + + if(!strcmp(e->e_name, "..")){ + TRACE("'..' entry found"); + list_del(&e->e_list); + if(!dot) + list_add(&e->e_list, &d->d_entries); + else + list_add(&e->e_list, d->d_entries.next); + + dotdot = 1; + } + } + + if(!dot) + lu_cache_add2dir(d, ".", NULL, &dummy); + + if(!dotdot) + lu_cache_add2dir(d, "..", NULL, &dummy); + + }while((!dot) || (!dotdot)); + +} + +void +lu_cache_add_dir(struct dir_cache *cache, struct directory *d){ + struct directory *dir; + int hsh; + + hsh = hash(d->d_name); + + TRACE("adding dir %s to bucket %i", d->d_name, hsh); + + check_dir(d); + + pthread_mutex_lock(&cache->lock); + + if((dir = search(cache, d->d_name))){ + TRACE("directory already in cache, deleting..."); + delete_dir(dir); + cache->lengths[hsh]--; + } + + d->d_stamp = time(NULL); + + list_add(&d->d_list, &cache->buckets[hsh]); + cache->lengths[hsh]++; + + while(cache->lengths[hsh] > cache->entries) + shrink(cache, hsh); + + pthread_mutex_unlock(&cache->lock); + + TRACE("out"); +} + +int lu_cache_readdir(struct dir_cache *cache, char *dir, + fuse_dirh_t h, fuse_dirfil_t filler) +{ + struct directory *d; + struct direntry *de; + struct list_head *p; + int res = -1; + + TRACE("reading directory %s", dir); + + pthread_mutex_lock(&cache->lock); + + if(!(d = search(cache, dir))) + goto out; + + list_for_each(p, &d->d_entries){ + de = list_entry(p, struct direntry, e_list); + filler(h, de->e_name, 0); + } + + d->d_stamp = time(NULL); + + res = 0; + + out: + pthread_mutex_unlock(&cache->lock); + TRACE("out"); + return res; +} + +int +lu_cache_lookup_file(struct dir_cache *cache, char *file, struct lufs_fattr *fattr, char *link, int buflen){ + int res; + + char *sep, *dir; + + if(!(sep = strrchr(file, '/'))){ + WARN("separator not present!"); + return -1; + } + + *sep = 0; + + if(sep == file) + dir = root_dir; + else + dir = file; + + if(*(sep+1)) + file = sep + 1; + else + file = current_dir; + + TRACE("dir: %s, file: %s", dir, file); + + res = lu_cache_lookup(cache, dir, file, fattr, link, buflen); + *sep = '/'; + + return res; +} + +int +lu_cache_invalidate(struct dir_cache *cache, char *file){ + struct directory *d; + char *sep, *dir; + + if(!(sep = strrchr(file, '/'))){ + WARN("separator not present!"); + return -1; + } + + *sep = 0; + + if(sep == file) + dir = root_dir; + else + dir = file; + + TRACE("invalidating dir %s", dir); + + pthread_mutex_lock(&cache->lock); + + if(!(d = search(cache, dir))){ + *sep = '/'; + pthread_mutex_unlock(&cache->lock); + return -1; + } + + d->d_stamp = 0; + + pthread_mutex_unlock(&cache->lock); + *sep = '/'; + + return 0; +} + +struct directory* +lu_cache_mkdir(char *dir){ + struct directory *res; + + TRACE("create dir %s", dir); + + if(!(res = malloc(sizeof(struct directory)))){ + WARN("out of mem!"); + return NULL; + } + + memset(res, 0, sizeof(struct directory)); + + if(!(res->d_name = malloc(strlen(dir) + 1))){ + WARN("out of mem!"); + free(res); + return NULL; + } + + INIT_LIST_HEAD(&res->d_entries); + res->d_stamp = time(NULL); + strcpy(res->d_name, dir); + + return res; +} + +int +lu_cache_add2dir(struct directory *d, char *fname, char *link, struct lufs_fattr *fattr){ + struct direntry *de; + + TRACE("adding %s->%s to %s", fname, link, d->d_name); + + if(!(de = malloc(sizeof(struct direntry)))) + goto fail; + + + if(!(de->e_name = malloc(strlen(fname) + 1))) + goto fail_de; + + + if(link) + de->e_link = malloc(strlen(link) + 1); + else + de->e_link = malloc(2); + + if(!de->e_link) + goto fail_ename; + + memcpy(&de->e_attr, fattr, sizeof(struct lufs_fattr)); + strcpy(de->e_name, fname); + if(link) + strcpy(de->e_link, link); + else + strcpy(de->e_link, ""); + + list_add_tail(&de->e_list, &d->d_entries); + + return 0; + + fail_ename: + free(de->e_name); + fail_de: + free(de); + fail: + WARN("out of mem!"); + return -1; +} + +void +lu_cache_killdir(struct directory *d){ + struct list_head *p, *tmp; + struct direntry *de; + + TRACE("in"); + + list_for_each_safe(p, tmp, &d->d_entries){ + de = list_entry(p, struct direntry, e_list); + list_del(&de->e_list); + free(de->e_name); + if(de->e_link) + free(de->e_link); + free(de); + } + + free(d->d_name); + free(d); + +} diff --git a/lufis/dircache.h b/lufis/dircache.h new file mode 100644 index 0000000..3c33d25 --- /dev/null +++ b/lufis/dircache.h @@ -0,0 +1,75 @@ +/* + * dircache.h + * Copyright (C) 2002 Florin Malita <mali@go.ro> + * + * This file is part of LUFS, a free userspace filesystem implementation. + * See http://lufs.sourceforge.net/ for updates. + * + * LUFS 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 of the License, or + * (at your option) any later version. + * + * LUFS 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 + */ + +#ifndef _DIRCACHE_H_ +#define _DIRCACHE_H_ + +#include <fuse.h> + +#ifdef __cplusplus +extern "C" { +#endif + + +#define NBUCKETS 7 +#define DEF_NENTRIES 6 +#define DEF_TTL 20 + +struct list_head; + +struct direntry{ + char *e_name; + char *e_link; + struct list_head e_list; + struct lufs_fattr e_attr; +}; + +struct directory{ + char *d_name; + struct list_head d_entries; + struct list_head d_list; + unsigned long d_stamp; +}; + +struct dir_cache{ + int ttl; + int entries; + pthread_mutex_t lock; + struct list_head buckets[NBUCKETS]; + int lengths[NBUCKETS]; +}; + +struct dir_cache* lu_cache_create(struct list_head*); +void lu_cache_destroy(struct dir_cache*); + +int lu_cache_lookup_file(struct dir_cache*, char*, struct lufs_fattr*, char*, int); +void lu_cache_add(struct dir_cache*, char*, char*, struct lufs_fattr*, char*); +int lu_cache_readdir(struct dir_cache *cache, char *dir, + fuse_dirh_t h, fuse_dirfil_t filler); +int lu_cache_invalidate(struct dir_cache*, char*); + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lufis/list.h b/lufis/list.h new file mode 100644 index 0000000..f3cb6f5 --- /dev/null +++ b/lufis/list.h @@ -0,0 +1,171 @@ +#ifndef _LINUX_LIST_H +#define _LINUX_LIST_H + +/* + * Simple doubly linked list implementation. + * + * Some of the internal functions ("__xxx") are useful when + * manipulating whole lists rather than single entries, as + * sometimes we already know the next/prev entries and we can + * generate better code by using them directly rather than + * using the generic single-entry routines. + */ + +struct list_head { + struct list_head *next, *prev; +}; + +#define LIST_HEAD_INIT(name) { &(name), &(name) } + +#define LIST_HEAD(name) \ + struct list_head name = LIST_HEAD_INIT(name) + +#define INIT_LIST_HEAD(ptr) do { \ + (ptr)->next = (ptr); (ptr)->prev = (ptr); \ +} while (0) + +/* + * Insert a new entry between two known consecutive entries. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_add(struct list_head * nnew, + struct list_head * prev, + struct list_head * next) +{ + next->prev = nnew; + nnew->next = next; + nnew->prev = prev; + prev->next = nnew; +} + +/** + * list_add - add a new entry + * @new: new entry to be added + * @head: list head to add it after + * + * Insert a new entry after the specified head. + * This is good for implementing stacks. + */ +static __inline__ void list_add(struct list_head *nnew, struct list_head *head) +{ + __list_add(nnew, head, head->next); +} + +/** + * list_add_tail - add a new entry + * @nnew: new entry to be added + * @head: list head to add it before + * + * Insert a new entry before the specified head. + * This is useful for implementing queues. + */ +static __inline__ void list_add_tail(struct list_head *nnew, struct list_head *head) +{ + __list_add(nnew, head->prev, head); +} + +/* + * Delete a list entry by making the prev/next entries + * point to each other. + * + * This is only for internal list manipulation where we know + * the prev/next entries already! + */ +static __inline__ void __list_del(struct list_head * prev, + struct list_head * next) +{ + next->prev = prev; + prev->next = next; +} + +/** + * list_del - deletes entry from list. + * @entry: the element to delete from the list. + * Note: list_empty on entry does not return true after this, the entry is in an undefined state. + */ +static __inline__ void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); +} + +/** + * list_del_init - deletes entry from list and reinitialize it. + * @entry: the element to delete from the list. + */ +static __inline__ void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); +} + +/** + * list_empty - tests whether a list is empty + * @head: the list to test. + */ +static __inline__ int list_empty(struct list_head *head) +{ + return head->next == head; +} + +/** + * list_splice - join two lists + * @list: the new list to add. + * @head: the place to add it in the first list. + */ +static __inline__ void list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + + if (first != list) { + struct list_head *last = list->prev; + struct list_head *at = head->next; + + first->prev = head; + head->next = first; + + last->next = at; + at->prev = last; + } +} + +/** + * list_entry - get the struct for this entry + * @ptr: the &struct list_head pointer. + * @type: the type of the struct this is embedded in. + * @member: the name of the list_struct within the struct. + */ +#define list_entry(ptr, type, member) \ + ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) + +/** + * list_for_each - iterate over a list + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); \ + pos = pos->next) + +/** + * list_for_each_safe - iterate over a list safe against removal of list entry + * @pos: the &struct list_head to use as a loop counter. + * @n: another &struct list_head to use as temporary storage + * @head: the head for your list. + */ +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) + +/** + * list_for_each_prev - iterate over a list in reverse order + * @pos: the &struct list_head to use as a loop counter. + * @head: the head for your list. + */ +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; pos != (head); \ + pos = pos->prev) + + +#endif diff --git a/lufis/lufis.c b/lufis/lufis.c new file mode 100644 index 0000000..24b9a84 --- /dev/null +++ b/lufis/lufis.c @@ -0,0 +1,742 @@ +#define _GNU_SOURCE + +#include <lufs/fs.h> +#include <lufs/proto.h> +#include <fuse.h> +#include "list.h" +#include "dircache.h" +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dlfcn.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +struct fs_operations { + void *(*init)(struct list_head*, struct dir_cache*, struct credentials*, void**); + void (*free)(void*); + int (*mount)(void*); + void (*umount)(void*); + int (*readdir)(void*, char*, struct directory*); + int (*stat)(void*, char*, struct lufs_fattr*); + int (*mkdir)(void*, char*, int); + int (*rmdir)(void*, char*); + int (*create)(void*, char*, int); + int (*unlink)(void*, char*); + int (*rename)(void*, char*, char*); + int (*open)(void*, char*, unsigned); + int (*release)(void*, char*); + int (*read)(void*, char*, long long, unsigned long, char*); + int (*write)(void*, char*, long long, unsigned long, char*); + int (*readlink)(void*, char*, char*, int); + int (*link)(void*, char*, char*); + int (*symlink)(void*, char*, char*); + int (*setattr)(void*, char*, struct lufs_fattr*); +}; + +static struct fs_operations lu_fops; +static void *lu_dlhandle; +static struct list_head lu_cfg; +static void *lu_global_ctx; +static struct credentials lu_cred; +static void *lu_context; +static struct dir_cache *lu_cache; + +#define BUF_SIZE 1024 +#define PASSWD "/etc/passwd" +#define GROUP "/etc/group" + +int lu_check_to(int rd_fd, int wr_fd, int time_out){ + fd_set rd, wr; + int res, maxfd = 0; + struct timeval tv; + + FD_ZERO(&rd); + FD_ZERO(&wr); + + if(rd_fd){ + FD_SET(rd_fd, &rd); + maxfd = rd_fd > maxfd ? rd_fd : maxfd; + } + + if(wr_fd){ + FD_SET(wr_fd, &wr); + maxfd = wr_fd > maxfd ? wr_fd : maxfd; + } + + tv.tv_sec = time_out; + tv.tv_usec = 0; + + do{ + res = select(maxfd + 1, &rd, &wr, NULL, &tv); + + }while((res < 0) && (errno == EINTR)); + + if(res > 0) + return 0; + + if(res < 0){ + WARN("select call failed: %s", strerror(errno)); + return -errno; + } + + WARN("operation timed out!"); + + return -ETIMEDOUT; +} + +int lu_atomic_read(int fd, char *buf, int len, int time_out){ + int res, offset = 0; + + do{ + if((time_out) && ((res = lu_check_to(fd, 0, time_out)) < 0)) + return res; + + do{ + res = read(fd, buf + offset, len - offset); + }while((res < 0) && (errno == EINTR)); + + if(res <= 0){ + WARN("read call failed: %s", strerror(errno)); + return (res < 0) ? -errno : (offset > 0 ? offset : -EPIPE); + } + + offset += res; + + }while(offset < len); + + return offset; +} + +int lu_atomic_write(int fd, char *buf, int len, int time_out){ + int res, offset = 0; + + do{ + if((time_out) && ((res = lu_check_to(0, fd, time_out)) < 0)) + return res; + + do{ + res = write(fd, buf + offset, len - offset); + }while((res < 0) && (errno == EINTR)); + + if(res <= 0){ + WARN("write call failed: %s", strerror(errno)); + return (res < 0) ? -errno : (offset > 0 ? offset : -EPIPE); + } + + offset += res; + + }while(offset < len); + + return offset; +} + +static int get_filesystem(const char *fs) +{ + char *buf; + void *dlhandle; + + if(!(buf = (char*)malloc(strlen(fs) + 32))) + return -1; + + sprintf(buf, "liblufs-%s.so", fs); + if(!(dlhandle = dlopen(buf, RTLD_LAZY))){ + ERROR(dlerror()); + goto fail; + } + + sprintf(buf, "%s_init", fs); + if(!(lu_fops.init = (void*(*)(struct list_head*, struct dir_cache*, struct credentials*, void**))dlsym(dlhandle, buf))){ + ERROR(dlerror()); + goto fail_fops; + } + + sprintf(buf, "%s_free", fs); + if(!(lu_fops.free = (void(*)(void*))dlsym(dlhandle, buf))){ + ERROR(dlerror()); + goto fail_fops; + } + + sprintf(buf, "%s_mount", fs); + if(!(lu_fops.mount = (int(*)(void*))dlsym(dlhandle, buf))){ + ERROR(dlerror()); + goto fail_fops; + } + + sprintf(buf, "%s_umount", fs); + if(!(lu_fops.umount = (void(*)(void*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_readdir", fs); + if(!(lu_fops.readdir = (int(*)(void*, char*, struct directory*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_stat", fs); + if(!(lu_fops.stat = (int(*)(void*, char*, struct lufs_fattr*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_mkdir", fs); + if(!(lu_fops.mkdir = (int(*)(void*, char*, int))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_rmdir", fs); + if(!(lu_fops.rmdir = (int(*)(void*, char*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_create", fs); + if(!(lu_fops.create = (int(*)(void*, char*, int))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_unlink", fs); + if(!(lu_fops.unlink = (int(*)(void*, char*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_rename", fs); + if(!(lu_fops.rename = (int(*)(void*, char*, char*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_open", fs); + if(!(lu_fops.open = (int(*)(void*, char*, unsigned))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_release", fs); + if(!(lu_fops.release = (int(*)(void*, char*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_read", fs); + if(!(lu_fops.read = (int(*)(void*, char*, long long, unsigned long, char*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_write", fs); + if(!(lu_fops.write = (int(*)(void*, char*, long long, unsigned long, char*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_readlink", fs); + if(!(lu_fops.readlink = (int(*)(void*, char*, char*, int))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_link", fs); + if(!(lu_fops.link = (int(*)(void*, char*, char*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_symlink", fs); + if(!(lu_fops.symlink = (int(*)(void*, char*, char*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + sprintf(buf, "%s_setattr", fs); + if(!(lu_fops.setattr = (int(*)(void*, char*, struct lufs_fattr*))dlsym(dlhandle, buf))) + ERROR(dlerror()); + + lu_dlhandle = dlhandle; + free(buf); + return 0; + + fail_fops: + dlclose(dlhandle); + fail: + free(buf); + return -1; +} + +static int lu_getattr_native(const char *path, struct lufs_fattr *fattr) +{ + if(!lu_fops.stat) + return -ENOSYS; + + memset(fattr, 0, sizeof(struct lufs_fattr)); + if(lu_cache_lookup_file(lu_cache, (char *) path, fattr, NULL, 0) < 0) { + if(lu_fops.stat(lu_context, (char *) path, fattr) < 0) + return -ENOENT; + } + return 0; +} + +static int lu_getattr(const char *path, struct stat *stbuf) +{ + struct lufs_fattr fattr; + int res; + + res = lu_getattr_native(path, &fattr); + if(res < 0) + return res; + + stbuf->st_mode = fattr.f_mode; + stbuf->st_nlink = fattr.f_nlink; + stbuf->st_uid = fattr.f_uid; + stbuf->st_gid = fattr.f_gid; + stbuf->st_size = fattr.f_size; + stbuf->st_atime = fattr.f_atime; + stbuf->st_mtime = fattr.f_mtime; + stbuf->st_ctime = fattr.f_ctime; + stbuf->st_blksize = fattr.f_blksize; + stbuf->st_blocks = fattr.f_blocks; + + return 0; +} + +static int lu_readlink(const char *path, char *buf, size_t size) +{ + int len; + struct lufs_fattr fattr; + + if(!lu_fops.readlink) + return -ENOSYS; + + if(lu_cache_lookup_file(lu_cache, (char *) path, &fattr, buf, size) < 0 || + strcmp(buf, "") == 0) { + if((len = lu_fops.readlink(lu_context, (char *) path, buf, size)) < 0) + return -EPERM; + + /* FUSE leaves one extra char free at the end */ + buf[len] = '\0'; + } + + return 0; +} + +static int lu_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) +{ + struct directory *dir; + + if(!lu_fops.readdir) + return -ENOSYS; + + if(lu_cache_readdir(lu_cache, (char *) path, h, filler) < 0){ + if(!(dir = lu_cache_mkdir((char *) path))) + return -1; + + if(lu_fops.readdir(lu_context, (char *) path, dir) < 0){ + lu_cache_killdir(dir); + return -1; + } + lu_cache_add_dir(lu_cache, dir); + + if(lu_cache_readdir(lu_cache, (char *) path, h, filler) < 0) { + return -EPERM; + } + } + return 0; +} + +static int lu_mknod(const char *path, mode_t mode, dev_t rdev) +{ + (void) rdev; + if(!S_ISREG(mode) || !lu_fops.create) + return -ENOSYS; + + if(lu_fops.create(lu_context, (char *) path, mode) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) path); + return 0; +} + +static int lu_mkdir(const char *path, mode_t mode) +{ + if(!lu_fops.mkdir) + return -ENOSYS; + + if(lu_fops.mkdir(lu_context, (char *) path, mode) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) path); + return 0; +} + +static int lu_unlink(const char *path) +{ + if(!lu_fops.unlink) + return -ENOSYS; + + if(lu_fops.unlink(lu_context, (char *) path) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) path); + return 0; +} + +static int lu_rmdir(const char *path) +{ + if(!lu_fops.rmdir) + return -ENOSYS; + + if(lu_fops.rmdir(lu_context, (char *) path) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) path); + return 0; +} + +static int lu_symlink(const char *from, const char *to) +{ + if(!lu_fops.symlink) + return -ENOSYS; + + if(lu_fops.symlink(lu_context, (char *) from, (char *) to) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) to); + return 0; +} + +static int lu_rename(const char *from, const char *to) +{ + if(!lu_fops.rename) + return -ENOSYS; + + if(lu_fops.rename(lu_context, (char *) from, (char *) to) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) from); + lu_cache_invalidate(lu_cache, (char *) to); + return 0; +} + +static int lu_link(const char *from, const char *to) +{ + if(!lu_fops.link) + return -ENOSYS; + + if(lu_fops.link(lu_context, (char *) from, (char *) to) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) from); + lu_cache_invalidate(lu_cache, (char *) to); + return 0; +} + +static int lu_chmod(const char *path, mode_t mode) +{ + int res; + struct lufs_fattr fattr; + + if(!lu_fops.setattr) + return -ENOSYS; + + res = lu_getattr_native(path, &fattr); + if(res < 0) + return res; + + fattr.f_mode = mode; + if(lu_fops.setattr(lu_context, (char *) path, &fattr) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) path); + return 0; +} + +static int lu_chown(const char *path, uid_t uid, gid_t gid) +{ + int res; + struct lufs_fattr fattr; + + if(!lu_fops.setattr) + return -ENOSYS; + + res = lu_getattr_native(path, &fattr); + if(res < 0) + return res; + + if(uid != (uid_t) -1) + fattr.f_uid = uid; + if(gid != (gid_t) -1) + fattr.f_gid = gid; + if(lu_fops.setattr(lu_context, (char *) path, &fattr) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) path); + return 0; + +} + +static int lu_truncate(const char *path, off_t size) +{ + int res; + struct lufs_fattr fattr; + + if(!lu_fops.setattr) + return -ENOSYS; + + res = lu_getattr_native(path, &fattr); + if(res < 0) + return res; + + fattr.f_size = size; + if(lu_fops.setattr(lu_context, (char *) path, &fattr) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) path); + return 0; +} + +static int lu_utime(const char *path, struct utimbuf *buf) +{ + int res; + struct lufs_fattr fattr; + + if(!lu_fops.setattr) + return -ENOSYS; + + res = lu_getattr_native(path, &fattr); + if(res < 0) + return res; + + fattr.f_atime = buf->actime; + fattr.f_mtime = buf->modtime; + if(lu_fops.setattr(lu_context, (char *) path, &fattr) < 0) + return -EPERM; + + lu_cache_invalidate(lu_cache, (char *) path); + return 0; +} + + +static int lu_open(const char *path, int flags) +{ + if(!lu_fops.open) + return -ENOSYS; + + if(lu_fops.open(lu_context, (char *) path, flags) < 0) + return -EPERM; + + return 0; +} + +static int lu_read(const char *path, char *buf, size_t size, off_t offset) +{ + int res; + if(!lu_fops.read) + return -ENOSYS; + + if((res = lu_fops.read(lu_context, (char *) path, offset, size, buf)) < 0) + return -EPERM; + + return res; +} + +static int lu_write(const char *path, const char *buf, size_t size, + off_t offset) +{ + if(!lu_fops.write) + return -ENOSYS; + + if(lu_fops.write(lu_context, (char *) path, offset, size, (char *) buf) < 0) + return -EPERM; + + return size; +} + +static int lu_release(const char *path, int flags) +{ + (void) flags; + if(!lu_fops.release) + return -ENOSYS; + + if(lu_fops.release(lu_context, (char *) path) < 0) + return -EPERM; + + return 0; +} + +static int load_credentials(void) +{ + static char buf[BUF_SIZE]; + char srch_str[MAX_LEN + 4]; + long int uid, gid; + int res, offset, chunk, readlen; + char *c; + + TRACE("loading remote credentials for %s", lu_cred.user); + + if((!lu_fops.open) || (!lu_fops.read) || (!lu_fops.release)){ + WARN("unsupported operation"); + return -1;; + } + + lu_cred.uid = lu_cred.gid = -1; + + if(lu_fops.open(lu_context, PASSWD, O_RDONLY) < 0){ + TRACE("could not open %s", PASSWD); + return -1; + } + + sprintf(srch_str, "\n%s:", lu_cred.user); + chunk = strlen(srch_str) + 64; + readlen = BUF_SIZE - chunk - 1; + + memset(buf, 32, chunk); + offset = 0; + + do{ + res = lu_fops.read(lu_context, PASSWD, offset, readlen, (buf + chunk)); + if(res > 0){ + *(buf + chunk + res) = 0; + + if((c = strstr(buf, srch_str))){ + TRACE("username found!"); + if(!(c = strchr(c + strlen(srch_str), ':'))){ + TRACE("separator not found!"); + }else{ + if(sscanf(c , ":%li:%li:", &uid, &gid) != 2){ + TRACE("uid/gid not found!"); + }else{ + TRACE("uid: %li, gid: %li", uid, gid); + + lu_cred.uid = uid; + lu_cred.gid = gid; + + break; + } + } + } + + memcpy(buf, buf + BUF_SIZE - chunk - 1, chunk); + offset += res; + } + }while(res == readlen); + + lu_fops.release(lu_context, PASSWD); + + if(res <= 0){ + TRACE("read failed"); + return -1; + } + + + if(lu_fops.open(lu_context, GROUP, O_RDONLY) < 0){ + TRACE("could not open %s", GROUP); + return -1; + } + + sprintf(srch_str, ":%li:", (long)lu_cred.gid); + chunk = strlen(srch_str) + 64; + readlen = BUF_SIZE - chunk - 1; + + memset(buf, 32, chunk); + offset = 0; + + do{ + res = lu_fops.read(lu_context, GROUP, offset, readlen, (buf + chunk)); + if(res > 0){ + *(buf + chunk + res) = 0; + + if((c = strstr(buf, srch_str))){ + TRACE("group found!"); + if(!(c = (char*)memrchr(buf, '\n', (c - buf)))){ + TRACE("separator not found!"); + }else{ + *(strchr(c, ':')) = 0; + if(strlen(c + 1) >= MAX_LEN){ + TRACE("groupname too long"); + }else{ + strcpy(lu_cred.group, c + 1); + TRACE("group: %s", lu_cred.group); + break; + } + } + } + + memcpy(buf, buf + BUF_SIZE - chunk - 1, chunk); + offset += res; + } + }while(res == readlen); + + lu_fops.release(lu_context, GROUP); + + if(res <= 0){ + TRACE("read failed"); + return -1; + } + + return 0; +} + +static int lufis_init(int *argcp, char **argvp[]) +{ + int argc = *argcp; + char **argv = *argvp; + int res; + char *opts; + const char *fs_name; + + if(argc < 2) { + fprintf(stderr, "usage: %s opts\n", argv[0]); + return -1; + } + + INIT_LIST_HEAD(&lu_cfg); + opts = argv[1]; + if(lu_opt_parse(&lu_cfg, "MOUNT", opts) < 0){ + ERROR("could not parse options!"); + return -1; + } + + (*argcp)--; + (*argvp)++; + argv[1] = argv[0]; + + + lu_cache = lu_cache_create(&lu_cfg); + if(!lu_cache) + return -1; + + if(!(fs_name = lu_opt_getchar(&lu_cfg, "MOUNT", "fs"))){ + ERROR("you need to specify a file system!"); + return -1; + } + + res = get_filesystem(fs_name); + if(res == -1) + return -1; + + if(!(lu_context = lu_fops.init(&lu_cfg, lu_cache, &lu_cred, &lu_global_ctx))) { + ERROR("could not initialize file system!"); + return -1; + } + + res = lu_fops.mount(lu_context); + if(res) { + if(load_credentials() < 0) + TRACE("could not load credentials."); + else + TRACE("credentials loaded."); + } else { + WARN("fs mount failed..."); + } + + return 0; +} + +static struct fuse_operations lu_oper = { + .getattr = lu_getattr, + .readlink = lu_readlink, + .getdir = lu_getdir, + .mknod = lu_mknod, + .mkdir = lu_mkdir, + .symlink = lu_symlink, + .unlink = lu_unlink, + .rmdir = lu_rmdir, + .rename = lu_rename, + .link = lu_link, + .chmod = lu_chmod, + .chown = lu_chown, + .truncate = lu_truncate, + .utime = lu_utime, + .open = lu_open, + .read = lu_read, + .write = lu_write, + .release = lu_release, +}; + +int main(int argc, char *argv[]) +{ + int res; + + res = lufis_init(&argc, &argv); + if(res == -1) + exit(1); + + fuse_main(argc, argv, &lu_oper); + return 0; +} diff --git a/lufis/options.c b/lufis/options.c new file mode 100644 index 0000000..44f3663 --- /dev/null +++ b/lufis/options.c @@ -0,0 +1,301 @@ +/* + * options.c + * Copyright (C) 2002 Florin Malita <mali@go.ro> + * + * This file is part of LUFS, a free userspace filesystem implementation. + * See http://lufs.sourceforge.net/ for updates. + * + * LUFS 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 of the License, or + * (at your option) any later version. + * + * LUFS 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 <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <lufs/fs.h> + +#include "list.h" + +struct option { + char *key; + char *value; + struct list_head list; +}; + +struct domain { + char *name; + struct list_head properties; + struct list_head list; +}; + + +static void +trim(char *buf){ + int b,e; + + if(!buf[0]) + return; + + for(b = 0; (buf[b] == ' ') || (buf[b] == '\t'); b++); + for(e = strlen(buf) - 1; (e >= 0) && ((buf[e] == ' ') || (buf[e] == '\t')); e--); + if(e < 0) + e = strlen(buf) - 1; + + buf[e + 1] = 0; + + if(b) + strcpy(buf, &buf[b]); + +} + +static struct domain* +find_domain(struct list_head *conf, char *name){ + struct list_head *p; + struct domain *cls; + + list_for_each(p, conf){ + cls = list_entry(p, struct domain, list); + if(!strcmp(name, cls->name)){ + TRACE("domain found"); + return cls; + } + } + + return NULL; +} + +int +lu_opt_loadcfg(struct list_head *conf, char *file){ + struct domain *class; + struct option *prop; + FILE *f; + static char buf[1024]; + char *i, *j; + char *cls, *key, *val; + + TRACE("loading config from %s", file); + + if(!(f = fopen(file, "r"))){ + WARN("could not open file for reading!"); + return -1; + } + + while(fgets(buf, 1024, f)){ + + buf[strlen(buf) - 1] = 0; + + if((i = strchr(buf, '#'))) + *i = 0; + + if((i = strchr(buf, '='))){ + if((j = strstr(buf, "::"))){ + cls = buf; + key = j + 2; + val = i + 1; + + *i = 0; + *j = 0; + + trim(cls); + trim(key); + trim(val); + + TRACE("class: #%s#", cls); + TRACE("key: #%s#", key); + TRACE("val: #%s#", val); + + if(!(class = find_domain(conf, cls))){ + TRACE("class not found, creating..."); + + if(!(class = malloc(sizeof(struct domain)))){ + WARN("out of mem!"); + break; + } + + memset(class, 0, sizeof(struct domain)); + + if(!(class->name = malloc(strlen(cls) + 1))){ + WARN("out of mem!"); + free(class); + break; + } + + strcpy(class->name, cls); + INIT_LIST_HEAD(&class->properties); + + list_add(&class->list, conf); + } + + if(!(prop = malloc(sizeof(struct option)))){ + WARN("out of mem!"); + break; + } + + if(!(prop->key = malloc(strlen(key) + 1))){ + WARN("out of mem!"); + free(prop); + break; + } + + if(!(prop->value = malloc(strlen(val) + 1))){ + WARN("out of mem!"); + free(prop->key); + free(prop); + break; + } + + strcpy(prop->key, key); + strcpy(prop->value, val); + + list_add(&prop->list, &class->properties); + } + } + + } + + + fclose(f); + + return 0; +} + + +const char* +lu_opt_getchar(struct list_head *conf, char *cls, char *key){ + struct domain *class; + struct option *prop; + struct list_head *p; + + TRACE("retrieving %s::%s", cls, key); + + if(!(class = find_domain(conf, cls))) + return NULL; + + list_for_each(p, &class->properties){ + prop = list_entry(p, struct option, list); + + if(!strcmp(key, prop->key)){ + TRACE("key found"); + return prop->value; + } + } + + TRACE("key not found"); + + return NULL; +} + +int +lu_opt_getint(struct list_head *conf, char *domain, char *key, long int *result, int base){ + char *end; + const char *val; + long int res; + + if(!(val = lu_opt_getchar(conf, domain, key))) + return -1; + + res = strtol(val, &end, base); + + if(*end) + return -1; + + *result = res; + return 0; +} + +int +lu_opt_parse(struct list_head *conf, char *domain, char *opts){ + struct domain *class; + struct option *prop; + char *p, *sep; + + if(!(class = find_domain(conf, domain))){ + TRACE("domain not found, creating..."); + + if(!(class = malloc(sizeof(struct domain)))){ + WARN("out of mem!"); + return -1; + } + + memset(class, 0, sizeof(struct domain)); + + if(!(class->name = malloc(strlen(domain) + 1))){ + WARN("out of mem!"); + free(class); + return -1; + } + + strcpy(class->name, domain); + INIT_LIST_HEAD(&class->properties); + + list_add(&class->list, conf); + } + + for(p = strtok(opts, ","); p; p = strtok(NULL, ",")){ + if(!strstr(p, "password")) + TRACE("option: %s", p); + + if(!(prop = malloc(sizeof(struct option)))){ + WARN("out of mem!"); + return -1; + } + + if((sep = strchr(p, '='))) + *sep = 0; + + if(!(prop->key = malloc(strlen(p) + 1))){ + WARN("out of mem!"); + free(prop); + return -1; + } + strcpy(prop->key, p); + + if(sep){ + TRACE("option with parameter"); + + if((strlen(sep + 1) >= MAX_LEN) || !(prop->value = malloc(strlen(sep + 1) + 1))){ + WARN("out of mem!"); + free(prop->key); + free(prop); + return -1; + } + strcpy(prop->value, sep + 1); + + if(strstr(p, "password")){ + TRACE("hiding password..."); + memset(sep + 1, ' ', strlen(sep + 1)); + } + }else{ + TRACE("flag"); + + if(!(prop->value = malloc(2))){ + WARN("out of mem!"); + free(prop->key); + free(prop); + return -1; + } + strcpy(prop->value, ""); + + } + + list_add(&prop->list, &class->properties); + + } + + return 0; +} + + |