/*  dav_fuse7.c: interface to the fuse kernel module FUSE_KERNEL_VERSION 7.
    Copyright (C) 2006, 2007, 2008. 2009 Werner Baumann

    This file is part of davfs2.

    davfs2 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 3 of the License, or
    (at your option) any later version.

    davfs2 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 davfs2; if not, write to the Free Software Foundation,
    Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. */


#include "config.h"

#include <errno.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_LIBINTL_H
#include <libintl.h>
#endif
#ifdef HAVE_STDDEF_H
#include <stddef.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <string.h>
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#include <pthread.h>

#include <stdio.h>

#include <linux/types.h>

#include "defaults.h"
#include "mount_davfs.h"
#include "cache.h"
#include "kernel_interface.h"
#include "linux/fuse.h"
#include "webdav.h"
#include "provide.h"

#ifdef ENABLE_NLS
#define _(String) gettext(String)
#else
#define _(String) String
#endif

/* Data Types */
/*============*/

/* There is no struct fuse_create_out in fuse_kernel.h. */

struct create_out {
    struct fuse_entry_out entry;
    struct fuse_open_out open;
};


/* Private global variables */
/*==========================*/

/* Size of the buffer to communicate with fuse. */
static size_t buf_size;

/* fuse wants the nodeid of the root node to be 1, so we have to translate
   between the real nodeid and what fuse wants. */
static uint64_t root;

/* Send debug messages to syslog if != 0. */
int debug;

int fuse_device;

//Read- and write-lock for all important actions.
extern pthread_mutex_t* mutex_allops;

//suspended read-call
struct read_job {
    struct read_job *next; //next read-call
    unsigned int exec_cnt; //count of executions
    size_t buf_len; //buffer length
    char* buf; //fuse buffer
};
//Queue for read-calls, wich are suspended for a while,
//because the download of the reading file is just in progress
//and not enough data is available to execute the complete
//real-call.
struct read_jobs{
    struct read_job *job_list;
    int job_count;
};
static struct read_jobs read_job_queue={0, 0};

/* Private function prototypes */
/*=============================*/

/* Functions to handle upcalls fromthe kernel module. */

static uint32_t fuse_access(char* buf);

static uint32_t fuse_create(char* buf);

static uint32_t fuse_forget(char* buf);

static uint32_t fuse_getattr(char* buf);

static uint32_t fuse_getxattr(char* buf);

static uint32_t fuse_init(char* buf);

static uint32_t fuse_lookup(char* buf);

static uint32_t fuse_mkdir(char* buf);

static uint32_t fuse_mknod(char* buf);

static uint32_t fuse_open(char* buf);

static uint32_t fuse_read(char* buf);

static uint32_t fuse_readdir(char* buf);

static uint32_t fuse_release(char* buf);

static uint32_t fuse_rename(char* buf);

static uint32_t fuse_setattr(char* buf);

static uint32_t fuse_stat(char* buf);

static uint32_t fuse_write(char* buf);

static uint32_t fuse_sync(char* buf);

static uint32_t fuse_rmdir(char* buf);

static uint32_t fuse_unlink(char* buf);

static uint32_t fuse_listxattr(char* buf);

static void dav_check_cache(void* arg);

static int fuse_execute(char* buf);

static void check_read_queue(void);

static void read_job_dequeue(void);

static int read_job_enqueue(char* buf, size_t buf_len);

static void read_queue_clean(void);

static int fuse_try_read(struct read_job* job);

/* Auxiliary functions. */

static off_t
write_dir_entry(int fd, off_t off, const dav_node *node, const char *name);

static void
set_attr(struct fuse_attr *attr, const dav_node *node);


/* Public functions */
/*==================*/

void
dav_fuse7_loop(int device, size_t bufsize, time_t idle_time,
               dav_is_mounted_fn is_mounted, volatile int *keep_on_running,
               int dbg)
{
    debug = dbg;
    if (debug)
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "fuse kernel version 7");

    buf_size = bufsize;
    fuse_device = device;

    dav_register_kernel_interface(&write_dir_entry, NULL, NULL);

    pthread_t dav_check_cache_thread_id=0;
    if(pthread_create(&dav_check_cache_thread_id, NULL, (void*) dav_check_cache, (void*) keep_on_running)){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "starting thread for dav_check_cache() failed");
        return;
    }

    while (*keep_on_running) {

        mutex_lock(mutex_allops);
        dav_provide_update();
        mutex_unlock(mutex_allops);

        struct timeval tv;
        if(read_job_queue.job_count>0){
            //minimum one read-call is suspended,
            //check the read-queue more frequently
            tv.tv_sec = 0;
            tv.tv_usec = 100000;
        }
        else{
            tv.tv_sec = 1;
            tv.tv_usec = 0;
        }
        fd_set fds;
        FD_ZERO(&fds);
        FD_SET(fuse_device, &fds);
        int ret = select(fuse_device + 1, &fds, NULL, NULL, &tv);
        /* Buffer used for communication with the kernel module (in and out). */
        char *buf = malloc(buf_size);
        if (!buf) {
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR),
                _("can't allocate message buffer"));
            return;
        }

        ssize_t bytes_read = 0;

        if (ret > 0) {
            //Lock access to the fuse device.
            bytes_read = read(fuse_device, buf, buf_size);
            if (bytes_read <= 0) {
                if (debug)
                    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "READ: %s",
                           strerror(errno));
                free(buf); buf=0;
                if (bytes_read == 0 || errno == EINTR || errno == EAGAIN ||
                    errno == ENOENT){
                    check_read_queue();
                    continue;
                }
                else
                    break;
            }
        } else if (ret == 0) {
            free(buf); buf=0;
            if (!is_mounted())
                break;
            else {
                check_read_queue();
                continue;
            }
        } else {
            free(buf); buf=0;
            break;
        }

        if ((size_t) bytes_read < sizeof(struct fuse_in_header)) {
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "not enough bytes read from fuse device: %u",
                    bytes_read);
            free(buf); buf=0;
            check_read_queue();
            continue;
        }

        struct fuse_in_header *ih = (struct fuse_in_header *) buf;
        if (ih->nodeid == 1)
              ih->nodeid = root;

        if(fuse_execute(buf)==-1){
            //read-call failed, enqueue the job
            if(read_job_enqueue(buf, bytes_read)==-2){
                //to many pending jobs - abort this one
                syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "aborting fuse-read");
                struct fuse_out_header *oh = (struct fuse_out_header *) buf;
                oh->error = -EIO;
                oh->len = sizeof(struct fuse_out_header);
                ssize_t n = 0;
                ssize_t w = 0;
                while (n < oh->len && w >= 0) {
                    w = write(fuse_device, buf + n, oh->len - n);
                    n += w;
                }
                free(buf); buf=0;
            }
        }
        check_read_queue();
    }
    if (debug)
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "leaving dav_fuse7_loop");
    read_queue_clean();
}

/* Try to execute suspended read-calls. */
static void
check_read_queue(void)
{
    struct read_job* cached_read_job = read_job_queue.job_list;
    if(cached_read_job){
        struct fuse_in_header *ih = (struct fuse_in_header *) cached_read_job->buf;
        if(ih->opcode != FUSE_READ){
            //WTF?! 
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "unknown fuse call in read queue: %d", ih->opcode);
            read_job_dequeue();
            return;
        }

        //try this call, may be enough data is now available
        if(!fuse_try_read(cached_read_job)){
            //read-call executed successfully or aborted, try next read 
            read_job_dequeue();
        }
    }
}

/* Private functions */
/*===================*/

/* Try to execute a suspended read-call. */
static int
fuse_try_read(struct read_job* job)
{
    struct fuse_out_header *oh = (struct fuse_out_header *) job->buf;
    ssize_t n = 0;
    ssize_t w = 0;

    mutex_lock(mutex_allops);

    __u32 read_ret = fuse_read(job->buf);

    if(read_ret == -ENODATA){
        //read failed again, missing data
        job->exec_cnt++;
        if(debug)
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "fuse-read from queue failed - must wait for incoming data (%d)", job->exec_cnt);
        if(job->exec_cnt<720){
            //still wait for incoming data
            mutex_unlock(mutex_allops);
            return -1;
        }
        else{
            //approximately 4 read attempts per second
            //abort this read-call after ~3 minutes
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "aborting fuse-read from queue");
            oh->error = -EINTR; //signal interrupt
            oh->len = sizeof(struct fuse_out_header);
        }
    }
    else
        oh->len = read_ret;

    if (debug && oh->len)
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "RET: %s",
                strerror(-oh->error));

    while (n < oh->len && w >= 0) {
        w = write(fuse_device, job->buf + n, oh->len - n);
        n += w;
    }

    mutex_unlock(mutex_allops);

    return 0;
}

//Execute fuse operation in the main thread.
static int
fuse_execute(char* buf)
{

    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    int is_read = 0;
    __u32 read_ret = 0;

    mutex_lock(mutex_allops); 
    
    switch (ih->opcode) {
        case FUSE_LOOKUP:
            oh->len = fuse_lookup(buf);
            break;
        case FUSE_FORGET:
            oh->len = fuse_forget(buf);
            oh->error = 0;
            break;
        case FUSE_GETATTR:
            oh->len = fuse_getattr(buf);
            break;
        case FUSE_SETATTR:
            oh->len = fuse_setattr(buf);
            break;
        case FUSE_READLINK:
            if (debug)
                syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_READLINK:");
            oh->error = -ENOSYS;
            oh->len = sizeof(struct fuse_out_header);
            break;
        case FUSE_SYMLINK:
            if (debug)
                syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_SYMLINK:");
            oh->error = -ENOSYS;
            oh->len = sizeof(struct fuse_out_header);
            break;
        case FUSE_MKNOD:
            oh->len = fuse_mknod(buf);
            break;
        case FUSE_MKDIR:
            oh->len = fuse_mkdir(buf);
            break;
        case FUSE_UNLINK:
            oh->len = fuse_unlink(buf);
            break;
        case FUSE_RMDIR:
            oh->len = fuse_rmdir(buf);
            break;
        case FUSE_RENAME:
            oh->len = fuse_rename(buf);
            break;
        case FUSE_LINK:
            if (debug)
                syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_LINK:");
            oh->error = -ENOSYS;
            oh->len = sizeof(struct fuse_out_header);
            break;
        case FUSE_OPEN:
            oh->len = fuse_open(buf);
            break;
        case FUSE_READ:
            read_ret = fuse_read(buf);
            is_read = 1;
            break;
        case FUSE_WRITE:
            oh->len = fuse_write(buf);
            break;
        case FUSE_STATFS:
            oh->len = fuse_stat(buf);
            break;
        case FUSE_RELEASE:
            oh->len = fuse_release(buf);
            break;
        case FUSE_FSYNC:
            oh->len = fuse_sync(buf);
            break;
        case FUSE_SETXATTR:
            if (debug)
                syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_GETXATTR:");
            oh->error = -ENOSYS;
            oh->len = sizeof(struct fuse_out_header);
            break;
        case FUSE_GETXATTR:
            oh->len = fuse_getxattr(buf);
            break;
        case FUSE_LISTXATTR:
            oh->len = fuse_listxattr(buf);
            break;
        case FUSE_REMOVEXATTR:
            if (debug)
                syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_REMOVEXATTR:");
            oh->error = -ENOSYS;
            oh->len = sizeof(struct fuse_out_header);
            break;
        case FUSE_FLUSH:
            if (debug)
                syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG),
                       "FUSE_FLUSH: ignored");
            oh->error = 0;
            oh->len = sizeof(struct fuse_out_header);
            break;
        case FUSE_INIT:
            oh->len = fuse_init(buf);
            break;
        case FUSE_OPENDIR:
            oh->len = fuse_open(buf);
            break;
        case FUSE_READDIR:
            oh->len = fuse_readdir(buf);
            break;
        case FUSE_RELEASEDIR:
            oh->len = fuse_release(buf);
            break;
        case FUSE_FSYNCDIR:
            if (debug)
                syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_FSYNCDIR:");
            oh->error = -ENOSYS;
            oh->len = sizeof(struct fuse_out_header);
            break;
        case FUSE_ACCESS:
            oh->len = fuse_access(buf);
            break;
        case FUSE_CREATE:
            oh->len = fuse_create(buf);
            break;
        default:
            if (debug)
                syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG),
                       "UNKNOWN FUSE CALL %i", ih->opcode);
            oh->error = -ENOSYS;
            oh->len = sizeof(struct fuse_out_header);
            break;
    }

    
    mutex_unlock(mutex_allops);

    if(is_read){
        if(read_ret == -ENODATA){
            //read-call failed, insufficient data.
            //Enqueue call in the read-queue and try again later.
            if(debug)
                syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "fuse-read failed - must wait for incoming data"); 
            return -1;
        }
        else
            oh->len = read_ret;
    }

    if (debug && oh->len)
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "RET: %s",
                strerror(-oh->error));

    ssize_t n = 0;
    ssize_t w = 0;
    while (n < oh->len && w >= 0) {
        w = write(fuse_device, buf + n, oh->len - n);
        n += w;
    }

    free(buf); buf=0;
    return 0;
}

static int
read_job_enqueue(char *buf, size_t buf_len)
{
    struct read_job *p, **pp;
    if (!buf)
        return -1;
    if(read_job_queue.job_count>10){
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "queueing fuse-read failed - too many pending calls");
        return -2;
    }
    p = (struct read_job*)calloc(1, sizeof(struct read_job));
    if (!p)
        return -3;
    p->buf = buf;
    p->buf_len = buf_len;
    p->exec_cnt = 0;
    for (pp = &read_job_queue.job_list; *pp; pp = &((*pp)->next));
    *pp = p;
    read_job_queue.job_count++;
    return 0;
}

static void
read_job_dequeue(void)
{
    struct read_job *p2;
    struct read_job *p1;

    p1 = read_job_queue.job_list;
    if(p1){
        p2 = p1->next;
        free(p1->buf);
        p1->buf = 0;
        free(p1);
        read_job_queue.job_list = p2;
        read_job_queue.job_count--;
    }
}

static void
read_queue_clean(void)
{
    struct read_job *p2;
    struct read_job *p1;

    for(p1 = read_job_queue.job_list; p1;){
        p2 = p1->next;
        free(p1->buf);
        p1->buf = 0;
        free(p1);
        p1 = p2;
    }
    read_job_queue.job_count = 0;
}

/* Execute the function dav_tidy_cache in an endless loop for checking the cache. */
static void
dav_check_cache(void* arg)
{
    volatile int *keep_on_running = (volatile int *) arg;

    while(*keep_on_running){
        dav_tidy_cache();
        sleep(3); 
    }

    dav_close_webdav(0);
}

/* Functions to handle upcalls from the kernel module.
   The cache module only uses data types from the C-library. For file access,
   mode and the like it only uses symbolic constants defined in the C-library.
   So the main purpose of this functions is to translate from kernel specific
   types and constants to types and constants from the C-library, and back.
   All of this functions return the amount of data in buf that is to be
   send to the kernel module. */


static uint32_t
fuse_unlink(char* buf)
{

    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;

    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_UNLINK:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  p 0x%llx, %s",
            ih->nodeid, (char *) (buf + sizeof(struct fuse_in_header)));
    }

    oh->error = dav_remove((dav_node *) ((size_t) ih->nodeid),
                    (char *) (buf + sizeof(struct fuse_in_header)), ih->uid);

    if (oh->error != 0)
        oh->error *= -1;

    return sizeof(struct fuse_out_header);
}

static uint32_t
fuse_access(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_access_in *in = (struct fuse_access_in *)
                                (buf + sizeof(struct fuse_in_header));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_ACCESS:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, f 0%o",
               ih->nodeid, in->mask);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  uid %i", ih->uid);
    }

    oh->error = dav_access((dav_node *) ((size_t) ih->nodeid), ih->uid,
                           in->mask);

    if (oh->error)
        oh->error *= -1;

    return sizeof(struct fuse_out_header);
}


static uint32_t
fuse_create(char* buf)
{
    struct fuse_in_header *ih= (struct fuse_in_header *) buf;
#if FUSE_KERNEL_MINOR_VERSION < 13
    struct fuse_open_in *in = (struct fuse_open_in *)
                              (buf + sizeof(struct fuse_in_header));
    char *name = buf + sizeof(struct fuse_in_header)
                 + sizeof(struct fuse_open_in);
#else
    struct fuse_create_in *in = (struct fuse_create_in *)
                              (buf + sizeof(struct fuse_in_header));
    char *name = buf + sizeof(struct fuse_in_header)
                 + sizeof(struct fuse_create_in);
#endif
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct create_out *out = (struct create_out *)
                             (buf + sizeof(struct fuse_out_header));
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_CREATE:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  name %s",
               name);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, f 0%o",
               ih->nodeid, in->flags);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  pid %i, mode 0%o",
               ih->pid, in->mode);
    }

    int created = 0;
    dav_node *node = NULL;
    oh->error = dav_lookup(&node, (dav_node *) ((size_t) ih->nodeid), name,
                           ih->uid);

    if (!oh->error) {
        if (!node) {
            oh->error = -EIO;
            return sizeof(struct fuse_out_header);
        } else if (in->flags & O_EXCL) {
            oh->error = -EEXIST;
            return sizeof(struct fuse_out_header);
        }
    } else if (oh->error == ENOENT) {
        oh->error = dav_create(&node, (dav_node *) ((size_t) ih->nodeid), name,
                               ih->uid, in->mode & DAV_A_MASK);
        if (oh->error || !node) {
            if (!oh->error)
                oh->error = EIO;
            oh->error *= -1;
            return sizeof(struct fuse_out_header);
        }
        created = 1;
    } else {
        oh->error *= -1;
        return sizeof(struct fuse_out_header);
    }

    int fd = 0;
    oh->error = dav_open(&fd, node, in->flags & ~(O_EXCL | O_CREAT), ih->pid,
                         0, ih->uid, 1);

    if (oh->error || !fd) {
        if (created)
            dav_remove((dav_node *) ((size_t) ih->nodeid), name, ih->uid);
        if (!oh->error)
            oh->error = EIO;
        oh->error *= -1;
        return sizeof(struct fuse_out_header);
    }

    out->entry.nodeid = (size_t) node;
    out->entry.generation = out->entry.nodeid;
    out->entry.entry_valid = 1;
    out->entry.attr_valid = 1;
    out->entry.entry_valid_nsec = 0;
    out->entry.attr_valid_nsec = 0;
    set_attr(&out->entry.attr, node);

    out->open.open_flags = in->flags & (O_ACCMODE | O_APPEND);
    out->open.fh = fd;
    out->open.padding = 0;

    if (debug)
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  fd %i", fd);
    return sizeof(struct fuse_out_header) + sizeof(struct create_out);
}


static uint32_t
fuse_forget(char* buf)
{
    //struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    //struct fuse_forget_in *in = (struct fuse_forget_in *)
    //                              (buf + sizeof(struct fuse_in_header));

    if (debug) {
        //dav_node* node = (dav_node *) ((size_t) ih->nodeid);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_FORGET:");
        //syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, %s", ih->nodeid, node->path);
        //syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  l 0x%llx", in->nlookup); //the number of lookups to forget 
    }

    return 0;
}

static uint32_t
fuse_getattr(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_attr_out *out = (struct fuse_attr_out *)
                                (buf + sizeof(struct fuse_out_header));
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_GETATTR:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx", ih->nodeid);
    }

    oh->error = dav_getattr((dav_node *) ((size_t) ih->nodeid), ih->uid);

    if (oh->error) {
        oh->error *= -1;
        return sizeof(struct fuse_out_header);
    }

    set_attr(&out->attr, (dav_node *) ((size_t) ih->nodeid));
    out->attr_valid = 1;
    out->attr_valid_nsec = 0;
    out->dummy = 0;

    return sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out);
}


static uint32_t
fuse_getxattr(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_getxattr_in *in = (struct fuse_getxattr_in *)
                                  (buf + sizeof(struct fuse_in_header));
    char *name = (char *) (buf + sizeof(struct fuse_in_header)
                           + sizeof(struct fuse_getxattr_in));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_getxattr_out *out = (struct fuse_getxattr_out *)
                                    (buf + sizeof(struct fuse_out_header));
    char *value = (char *) (buf + sizeof(struct fuse_out_header));
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_GETXATTR:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, %s, %i",
               ih->nodeid, name, in->size);
    }

    size_t size = in->size;
    if (size == 0) {
        oh->error = dav_getxattr((dav_node *) ((size_t) ih->nodeid), name,
                                 value, &size, ih->uid);
        if (oh->error) {
            oh->error *= -1;
            return sizeof(struct fuse_out_header);
        }
        out->size = size;
        out->padding = 0;
        return sizeof(struct fuse_out_header)
               + sizeof(struct fuse_getxattr_out);
    } else {
        if (size > (buf_size - sizeof(struct fuse_out_header)))
            size = buf_size - sizeof(struct fuse_out_header);
        oh->error = dav_getxattr((dav_node *) ((size_t) ih->nodeid), name,
                                 value, &size, ih->uid);
        if (oh->error) {
            oh->error *= -1;
            return sizeof(struct fuse_out_header);
        }
        return sizeof(struct fuse_out_header) +  size;
    }
}


static uint32_t
fuse_init(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_init_in *in = (struct fuse_init_in *)
                                  (buf + sizeof(struct fuse_in_header));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_init_out *out = (struct fuse_init_out *)
                                (buf + sizeof(struct fuse_out_header));
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_INIT:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  version %i.%i",
               in->major, in->minor);
    }

    dav_node *node;
    oh->error = dav_root(&node, ih->uid);

    if (oh->error || !node) {
        if (!oh->error)
            oh->error = EIO;
        oh->error *= -1;
        return sizeof(struct fuse_out_header);
    }

    root = (size_t) node;
    out->major = FUSE_KERNEL_VERSION;
    out->minor = FUSE_KERNEL_MINOR_VERSION;
	//Set the maximum number of bytes to read-ahead.
	//The default is determined by the kernel.
	out->max_readahead = 0;
	out->flags = 0;
#if FUSE_KERNEL_MINOR_VERSION < 13
	out->unused = 0;
#else
	//Maximum number of outstanding background requests
	//Default: 12
	out->max_background = 0;
	//Congestion starts at 75% of maximum
	out->congestion_threshold = 0;
#endif
    out->max_write = buf_size - sizeof(struct fuse_in_header)
                     - sizeof(struct fuse_write_in) - 4095;

    return sizeof(struct fuse_out_header) + sizeof(struct fuse_init_out);
}


static uint32_t
fuse_listxattr(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_getxattr_in *in = (struct fuse_getxattr_in *)
                                  (buf + sizeof(struct fuse_in_header));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_getxattr_out *out = (struct fuse_getxattr_out *)
                                    (buf + sizeof(struct fuse_out_header));
    char *value = (char *) (buf + sizeof(struct fuse_out_header));
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_LISTXATTR:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, %i",
               ih->nodeid, in->size);
    }

    size_t size = in->size;
    if (size == 0) {
        oh->error = dav_listxattr((dav_node *) ((size_t) ih->nodeid), value,
                                  &size, ih->uid);
        if (oh->error) {
            oh->error *= -1;
            return sizeof(struct fuse_out_header);
        }
        out->size = size;
        out->padding = 0;
        return sizeof(struct fuse_out_header)
               + sizeof(struct fuse_getxattr_out);
    } else {
        if (size > (buf_size - sizeof(struct fuse_out_header)))
            size = buf_size - sizeof(struct fuse_out_header);
        oh->error = dav_listxattr((dav_node *) ((size_t) ih->nodeid), value,
                                  &size, ih->uid);
        if (oh->error) {
            oh->error *= -1;
            return sizeof(struct fuse_out_header);
        }
        return sizeof(struct fuse_out_header) +  size;
    }
}


static uint32_t
fuse_lookup(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    char * name = (char *) (buf + sizeof(struct fuse_in_header));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_entry_out *out = (struct fuse_entry_out *)
                                 (buf + sizeof(struct fuse_out_header));
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_LOOKUP:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  p 0x%llx, %s",
               ih->nodeid, name);
    }

    dav_node *node = NULL;
    oh->error = dav_lookup(&node, (dav_node *) ((size_t) ih->nodeid), name,
                           ih->uid);

    if (oh->error || !node) {
        if (!oh->error)
            oh->error = EIO;
        oh->error *= -1;
        return sizeof(struct fuse_out_header);
    }

    out->nodeid = (size_t) node;
    out->generation = out->nodeid;
    out->entry_valid = 1;
    out->attr_valid = 1;
    out->entry_valid_nsec = 0;
    out->attr_valid_nsec = 0;
    set_attr(&out->attr, node);

    return sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out);
}


static uint32_t
fuse_mkdir(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_mkdir_in *in = (struct fuse_mkdir_in *)
                               (buf + sizeof(struct fuse_in_header));
    char *name = (char *) (buf + sizeof(struct fuse_in_header)
                           + sizeof(struct fuse_mkdir_in));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_entry_out *out = (struct fuse_entry_out *)
                                 (buf + sizeof(struct fuse_out_header));
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_MKDIR:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  p 0x%llx, %s",
               ih->nodeid, name);
    }

    dav_node *node = NULL;
    oh->error = dav_mkdir(&node, (dav_node *) ((size_t) ih->nodeid), name,
                          ih->uid, in->mode & DAV_A_MASK);

    if (oh->error || !node) {
        if (!oh->error)
            oh->error = EIO;
        oh->error *= -1;
        return sizeof(struct fuse_out_header);
    }

    out->nodeid = (size_t) node;
    out->generation = out->nodeid;
    out->entry_valid = 1;
    out->attr_valid = 1;
    out->entry_valid_nsec = 0;
    out->attr_valid_nsec = 0;
    set_attr(&out->attr, node);

    return sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out);
}


static uint32_t
fuse_mknod(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_mknod_in *in = (struct fuse_mknod_in *)
                               (buf + sizeof(struct fuse_in_header));
    char *name = (char *) (buf + sizeof(struct fuse_in_header)
                           + sizeof(struct fuse_mknod_in));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_entry_out *out = (struct fuse_entry_out *)
                                 (buf + sizeof(struct fuse_out_header));
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_MKNOD:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  p 0x%llx, m 0%o",
               ih->nodeid, in->mode);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  %s", name);
    }

    if (!S_ISREG(in->mode)) {
        oh->error = -ENOTSUP;
        return sizeof(struct fuse_out_header);
    }

    dav_node *node = NULL;
    oh->error = dav_create(&node, (dav_node *) ((size_t) ih->nodeid), name,
                           ih->uid, in->mode & DAV_A_MASK);

    if (oh->error || !node) {
        if (!oh->error)
            oh->error = EIO;
        oh->error *= -1;
        return sizeof(struct fuse_out_header);
    }

    out->nodeid = (size_t) node;
    out->generation = out->nodeid;
    out->entry_valid = 1;
    out->attr_valid = 1;
    out->entry_valid_nsec = 0;
    out->attr_valid_nsec = 0;
    set_attr(&out->attr, node);

    return sizeof(struct fuse_out_header) + sizeof(struct fuse_entry_out);
}


static uint32_t
fuse_open(char* buf)
{
    struct fuse_in_header *ih= (struct fuse_in_header *) buf;
    struct fuse_open_in *in = (struct fuse_open_in *)
                              (buf + sizeof(struct fuse_in_header));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_open_out *out = (struct fuse_open_out *)
                                (buf + sizeof(struct fuse_out_header));
    if (debug) {
        if (ih->opcode == FUSE_OPENDIR) {
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_OPENDIR:");
        } else {
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_OPEN:");
        }
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, f 0%o",
               ih->nodeid, in->flags);
#if FUSE_KERNEL_MINOR_VERSION < 13
		syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  pid %i, mode 0%o",
				ih->pid, in->mode);
#else
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  pid %i",
               ih->pid);
#endif
    }

    int fd = 0;
    oh->error = dav_open(&fd, (dav_node *) ((size_t) ih->nodeid), in->flags,
                         ih->pid, 0, ih->uid, 0);

    if (oh->error || !fd) {
        if (!oh->error)
            oh->error = EIO;
        oh->error *= -1;
        return sizeof(struct fuse_out_header);
    }

    out->open_flags = in->flags & (O_ACCMODE | O_APPEND);
    out->fh = fd;
    out->padding = 0;

    if (debug)
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  fd %i", fd);
    return sizeof(struct fuse_out_header) + sizeof(struct fuse_open_out);
}


static uint32_t
fuse_read(char* buf)
{
    struct fuse_in_header *ih= (struct fuse_in_header *) buf;
    struct fuse_read_in *in = (struct fuse_read_in *)
                              (buf + sizeof(struct fuse_in_header));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_READ:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, fd %llu",
               ih->nodeid, in->fh);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  pid %i", ih->pid);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  size %u, off %llu",
               in->size, in->offset);
    }

    if (in->size > (buf_size - sizeof(struct fuse_out_header))) {
        oh->error = -EINVAL;
        return sizeof(struct fuse_out_header);
    }

    ssize_t len = 0;
    __s32 read_error = dav_read(&len, (dav_node *) ((size_t) ih->nodeid),
                         in->fh, buf + sizeof(struct fuse_out_header),
                         in->size, in->offset);

    if(read_error == ENODATA)
        return -ENODATA;

    oh->error = read_error;

    if (oh->error)
        oh->error *= -1;

    return len + sizeof(struct fuse_out_header);
}

static uint32_t
fuse_readdir(char* buf)
{
    struct fuse_in_header *ih= (struct fuse_in_header *) buf;
    struct fuse_read_in *in = (struct fuse_read_in *)
                              (buf + sizeof(struct fuse_in_header));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_READDIR:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, fd %llu",
               ih->nodeid, in->fh);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  pid %i", ih->pid);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  size %u, off %llu",
               in->size, in->offset);
    }

    if (in->size > (buf_size - sizeof(struct fuse_out_header))) {
        oh->error = -EINVAL;
        return sizeof(struct fuse_out_header);
    }

    ssize_t len = 0;
    oh->error = dav_read(&len, (dav_node *) ((size_t) ih->nodeid),
                         in->fh, buf + sizeof(struct fuse_out_header),
                         in->size, in->offset);

    if (oh->error)
        oh->error *= -1;

    return len + sizeof(struct fuse_out_header);
}

static uint32_t
fuse_release(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_release_in *in = (struct fuse_release_in *)
                                 (buf + sizeof(struct fuse_in_header));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    if (debug) {
        if (ih->opcode == FUSE_RELEASEDIR) {
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_RELEASEDIR:");
        } else {
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_RELEASE:");
        }
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, f 0%o",
               ih->nodeid, in->flags);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  pid %i, fd %llu",
               ih->pid, in->fh);
    }

    oh->error = dav_close((dav_node *) ((size_t) ih->nodeid), in->fh,
                          in->flags, ih->pid, 0);

    if (oh->error)
        oh->error *= -1;

    return sizeof(struct fuse_out_header);
}

static uint32_t fuse_rmdir(char* buf) {

    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_RMDIR:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  p 0x%llx, %s",
            ih->nodeid, (char *) (buf + sizeof(struct fuse_in_header)));
    }

    oh->error = dav_rmdir((dav_node *) ((size_t) ih->nodeid),
                            (char *) (buf + sizeof(struct fuse_in_header)),
                            ih->uid);

    if (oh->error != 0)
        oh->error *= -1;

    return sizeof(struct fuse_out_header);
}


static uint32_t fuse_sync(char* buf) {

    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;

    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_FSYNC:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx",
            ih->nodeid);
    }

    oh->error = dav_sync((dav_node *) ((size_t) ih->nodeid));

    if (oh->error != 0)
        oh->error *= -1;

    return sizeof(struct fuse_out_header);
}

static uint32_t
fuse_rename(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_rename_in *in = (struct fuse_rename_in *)
                                (buf + sizeof(struct fuse_in_header));
    char *old = (char *) (buf + sizeof(struct fuse_in_header)
                          + sizeof(struct fuse_rename_in));
    char *new = old + strlen(old) + 1;
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_RENAME:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  sp 0x%llx, %s",
               ih->nodeid, old);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  dp 0x%llx, %s",
               in->newdir, new);
    }

    if (in->newdir == 1)
        in->newdir = root;
    oh->error = dav_rename((dav_node *) ((size_t) ih->nodeid), old,
                           (dav_node *) ((size_t) in->newdir), new, ih->uid);

    if (oh->error)
        oh->error *= -1;

    return sizeof(struct fuse_out_header);
}


static uint32_t
fuse_setattr(char* buf)
{
    struct fuse_in_header *ih = (struct fuse_in_header *) buf;
    struct fuse_setattr_in *in = (struct fuse_setattr_in *)
                                 (buf + sizeof(struct fuse_in_header));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_attr_out *out = (struct fuse_attr_out *)
                                (buf + sizeof(struct fuse_out_header));
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_SETATTR:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, m 0%o",
               ih->nodeid, in->mode);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  uid %i, gid %i",
               in->uid, in->gid);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  sz %llu, at %llu,",
               in->size, in->atime);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  mt %llu", in->mtime);
    }

    oh->error = dav_setattr((dav_node *) ((size_t) ih->nodeid), ih->uid,
                            in->valid & FATTR_MODE, in->mode,
                            in->valid & FATTR_UID, in->uid,
                            in->valid & FATTR_GID, in->gid,
                            in->valid & FATTR_ATIME, in->atime,
                            in->valid & FATTR_MTIME, in->mtime,
                            in->valid & FATTR_SIZE, in->size);

    if (oh->error) {
        oh->error *= -1;
        return sizeof(struct fuse_out_header);
    }

    set_attr(&out->attr, (dav_node *) ((size_t) ih->nodeid));
    out->attr_valid = 1;
    out->attr_valid_nsec = 0;
    out->dummy = 0;

    return sizeof(struct fuse_out_header) + sizeof(struct fuse_attr_out);
}


static uint32_t
fuse_stat(char* buf)
{
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_statfs_out *out = (struct fuse_statfs_out *)
                                  (buf + sizeof(struct fuse_out_header));
    if (debug)
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_STATFS:");

    __uint64_t block_size = (buf_size - sizeof(struct fuse_in_header)
                                 - sizeof(struct fuse_write_in) - 4095) & ~4095;

    dav_stat *st = calloc(1, sizeof(dav_stat));
    if(!dav_statfs(st, block_size)){
        out->st.blocks  = st->blocks;
        out->st.bfree   = st->bfree;
        out->st.bavail  = st->bavail;
        out->st.files   = st->files;
        out->st.ffree   = st->ffree;
        out->st.bsize   = block_size;
        out->st.namelen = st->namelen;
        out->st.frsize  = 0;
        out->st.padding = 0;
        if(debug){
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  btotal: %llu",  out->st.blocks);
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  bfree:  %llu",  out->st.bfree);
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  files:  %llu",  out->st.files);
        }
        int i;
        for (i = 0; i < 6; i++)
            out->st.spare[i] = 0;
        free(st);
        oh->error = 0;
        return sizeof(struct fuse_out_header) + sizeof(struct fuse_statfs_out);        
    }
    else{
        free(st);
        oh->error = -ENOSYS; //The file system does not support this call.
        return sizeof(struct fuse_out_header);
    }
}


static uint32_t
fuse_write(char* buf)
{
    struct fuse_in_header *ih= (struct fuse_in_header *) buf;
    struct fuse_write_in *in = (struct fuse_write_in *)
                               (buf + sizeof(struct fuse_in_header));
    struct fuse_out_header *oh = (struct fuse_out_header *) buf;
    struct fuse_write_out *out = (struct fuse_write_out *)
                                 (buf + sizeof(struct fuse_out_header));
    if (debug) {
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "FUSE_WRITE:");
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  n 0x%llx, fd %llu",
               ih->nodeid, in->fh);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  pid %i, flags 0%o",
               ih->pid, in->write_flags);
        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "  size %u, off %llu",
               in->size, in->offset);
    }

    if (in->size > (buf_size - sizeof(struct fuse_in_header)
                    - sizeof(struct fuse_write_in))) {
        oh->error = -EINVAL;
        return sizeof(struct fuse_out_header);
    }

    size_t size=0;
    oh->error = dav_write(&size, (dav_node *) ((size_t) ih->nodeid),
                          in->fh, buf + sizeof(struct fuse_in_header)
                          + sizeof(struct fuse_write_in),
                          in->size, in->offset);
    
    if(oh->error == ENOSPC || oh->error == EDQUOT) {
        //cannot write file, not enough space
        //remove the cache file and the corresponding node

        dav_node* node = (dav_node *) ((size_t) ih->nodeid);
        if(debug)
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "not enough space or quota reached (%i). removing file: %s", oh->error, node->cache_path/*node->name*/);
        int ret = dav_remove(node->parent, node->name, ih->uid);
        if(!ret) {
            if(oh->error == ENOSPC)
                avm_event_log(AVM_EVENT_NO_SPACE, node->path);
        }
        else
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "removing file failed: %s", strerror(ret));
        oh->error = ENOSPC;
    }

    if (oh->error) {
        oh->error *= -1;
        return sizeof(struct fuse_out_header);
    }

    out->size = size;
    out->padding = 0;

    return sizeof(struct fuse_out_header) + sizeof(struct fuse_write_out);
}


/* Auxiliary functions. */

/* Writes a struct fuse_dirent to file with file descriptor fd.
   fd     : An open file descriptor to write to.
   off    : The current file size.
   name   : File name; if NULL, the last, empty entry is written.
   return value : New size of the file. -1 in case of an error. */
static off_t
write_dir_entry(int fd, off_t off, const dav_node *node, const char *name)
{
    if (!name)
        return off;

    struct fuse_dirent entry;
    size_t head = offsetof(struct fuse_dirent, name);
    size_t reclen = (head + strlen(name) + sizeof(uint64_t) -1)
                    & ~(sizeof(uint64_t) - 1);

    entry.ino = (((size_t) node) == root) ? 1 : (size_t) node;
    entry.off = off + reclen;
    entry.namelen = strlen(name);
    entry.type = (node->mode & S_IFMT) >> 12;

    size_t size = 0;
    ssize_t ret = 0;
    while (ret >= 0 && size < head) {
        ret = write(fd, (char *) &entry + size, head - size);
        if (ret < 0) {
            int err_code = errno;
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "writing dir entry failed: %s", strerror(err_code));
            if(err_code == ENOSPC && resize_cache()){
                //Write failed, because no space is left.
                //Free space may be now available, because the cache was resized.
                //Try the write call a 2nd time.
                ret = write(fd, (char *) &entry + size, head - size);
                if(ret < 0){
                    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "2nd attempt to write dir entry failed: %s", strerror(errno));
                    return -1;
                }
                else{
                    if(debug)
                        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "2nd attempt to write dir entry succeeded");
                }
            }
            else
                return -1;
        }
        size += ret;
    }
    if (size != head)
        return -1;

    ret = 0;
    while (ret >= 0 && size < (head + entry.namelen)) {
        ret = write(fd, name + size - head, entry.namelen - size + head);
        if (ret < 0) {
            int err_code = errno;
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "writing dir entry failed: %s", strerror(err_code));
            if(err_code == ENOSPC && resize_cache()){
                //Write failed, because no space is left.
                //Free space may be now available, because the cache was resized.
                //Try the write call a 2nd time.
                ret = write(fd, name + size - head, entry.namelen - size + head);
                if(ret < 0){
                    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "2nd attempt to write dir entry failed: %s", strerror(errno));
                    return -1;
                }
                else{
                    if(debug)
                        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "2nd attempt to write dir entry succeeded");
                }
            }
            else
                return -1;
        }
        size += ret;
    }
    if (size != (head + entry.namelen))
        return -1;

    ret = 0;
    while (ret >= 0 && size < reclen) {
        ret = write(fd, "\0", 1);
        if (ret < 0) {
            int err_code = errno;
            syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "writing dir entry failed: %s", strerror(err_code));
            if(err_code == ENOSPC && resize_cache()){
                //Write failed, because no space is left.
                //Free space may be now available, because the cache was resized.
                //Try the write call a 2nd time.
                ret = write(fd, "\0", 1);
                if(ret < 0){
                    syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_ERR), "2nd attempt to write dir entry failed: %s", strerror(errno));
                    return -1;
                }
                else{
                    if(debug)
                        syslog(LOG_MAKEPRI(LOG_DAEMON, LOG_DEBUG), "2nd attempt to write dir entry succeeded");
                }
            }
            else
                return -1;
        }
        size += ret;
    }
    if (size != reclen)
        return -1;

    return off + reclen;
}


static void
set_attr(struct fuse_attr *attr, const dav_node *node)
{
    attr->ino = (((size_t) node) == root) ? 1 : (size_t) node;
    attr->size = node->size;
    attr->blocks = (node->size + 511) / 512;
    attr->atime = node->atime;
    attr->mtime = node->mtime;
    attr->ctime = node->ctime;
    attr->atimensec = 0;
    attr->mtimensec = 0;
    attr->ctimensec = 0;
    attr->mode = node->mode;
    if (S_ISDIR(node->mode)) {
        attr->nlink = node->nref;
    } else {
        attr->nlink = 1;
    }
    attr->uid = node->uid;
    attr->gid = node->gid;
    attr->rdev = 0;
#if FUSE_KERNEL_MINOR_VERSION >= 10
	//Allow the userspace filesystem to supply a blksize value to be
	//returned by stat() and friends.
	//If the field is zero, it defaults to the old PAGE_CACHE_SIZE value.
    attr->blksize = 0;
    attr->padding = 0;
#endif
}