initial checkin
authorMiklos Szeredi <miklos@szeredi.hu>
Tue, 10 Feb 2004 11:35:43 +0000 (11:35 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Tue, 10 Feb 2004 11:35:43 +0000 (11:35 +0000)
lufis/.cvsignore [new file with mode: 0644]
lufis/Makefile [new file with mode: 0644]
lufis/README [new file with mode: 0644]
lufis/dircache.c [new file with mode: 0644]
lufis/dircache.h [new file with mode: 0644]
lufis/list.h [new file with mode: 0644]
lufis/lufis.c [new file with mode: 0644]
lufis/options.c [new file with mode: 0644]

diff --git a/lufis/.cvsignore b/lufis/.cvsignore
new file mode 100644 (file)
index 0000000..dc5eb9e
--- /dev/null
@@ -0,0 +1 @@
+lufis
diff --git a/lufis/Makefile b/lufis/Makefile
new file mode 100644 (file)
index 0000000..713e509
--- /dev/null
@@ -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 (file)
index 0000000..b3ee9c7
--- /dev/null
@@ -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 (file)
index 0000000..0498355
--- /dev/null
@@ -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 (file)
index 0000000..3c33d25
--- /dev/null
@@ -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 (file)
index 0000000..f3cb6f5
--- /dev/null
@@ -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 (file)
index 0000000..24b9a84
--- /dev/null
@@ -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 (file)
index 0000000..44f3663
--- /dev/null
@@ -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;
+}
+
+