/*
 * libkarma/karma.c
 *
 * Copyright (c) Frank Zschockelt <libkarma@freakysoft.de> 2004-2005
 * Copyright (c) Enrique Vidal <evidal@iti.upv.es> 2006
 * Copyright (c) Keith Bennett <keith@mcs.st-and.ac.uk> 2006
 *
 * You may distribute and modify this program under the terms of 
 * the GNU GPL, version 2 or later.
 */

#ifndef __APPLE__
#define _XOPEN_SOURCE 500        /* Needed by pread/pwrite */
#endif

#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <utime.h>

#ifdef __APPLE__
#include <sys/mount.h>
#else
#include <sys/statfs.h>
#include <sys/vfs.h>            /* or <sys/statfs.h> */
#endif

#include "lkarma.h"
#include "karma.h"
#include "karmaUsb.h"
#include "properties.h"
#include "playlist.h"
#include "md5.h"
#include "util.h"
#include "fdb.h"

#ifndef MSG_FIN
#define MSG_FIN 0
#endif

char *usbMountPoint = NULL;
static karma_db_t *db = NULL;

/* -------------------------------------------------------------------------- */
/* Auxiliary Function */
/* -------------------------------------------------------------------------- */
char *lk_karmaUsb_fidToPath(int rio, uint32_t file_id)
{
    int i, len;
    char *path = NULL;

    len = strlen(usbMountPoint) + 17;
    path = malloc(len);
    sprintf(path, "%sfids0/_%.8x", usbMountPoint, file_id);
    len = strlen(path);
    for (i = len; i >= len - 3; i--)
        path[i + 1] = path[i];
    path[len - 3] = '/';
    return path;
}

/* -------------------------------------------------------------------------- */
/* Basic Protocol Functions */
/* -------------------------------------------------------------------------- */
int lk_karmaUsb_connect(char *path)
{
/* ******************** Implemented OMFS mount here ?? ********************** */
/* The path must be absolute to be accesible in lkarmafs;  e.g.: /mnt/karma2/ */
    int len = strlen(path);
    char *fname;
    struct utimbuf time;
    int ret = 0;

    usbMountPoint = malloc(len + 2);
    strcpy(usbMountPoint, path);
    if(path[len - 1] != '/')
        strcat(usbMountPoint, "/");

    lk_karmaUsb_get_device_settings(0);

    time.modtime=0;
    fname=lk_path_string("fileinfo");
    if(utime(fname, &time)==-1){
        free(fname);
        lk_errors_set(E_UTIME);
    }
    return ret;
}

/* -------------------------------------------------------------------------- */
int32_t lk_karmaUsb_hangup(int rio)
{
/* ********************* Implemented OMFS umount here ?? ******************** */
    if (db) {
        if (db->attribs) {
            uint32_t i;
            for (i = 0; i < db->num_attribs; i++)
                if (db->attribs[i].name) free(db->attribs[i].name);
            free(db->attribs);
        }
        free(db);
    }
    if (usbMountPoint)
        free(usbMountPoint);
    return 0;
}

/* -------------------------------------------------------------------------- */
uint32_t lk_karmaUsb_authenticate(int rio, char *pass)
{
    return 0;
}

/* -------------------------------------------------------------------------- */
int lk_karmaUsb_get_storage_details(int rio, uint32_t storage_id,
                                    uint32_t * n_files, uint64_t * s_size,
                                    uint64_t * f_space,
                                    uint32_t * highest_file_id)
{
    int aux;
    struct statfs buf;

    aux = statfs(usbMountPoint, &buf);
    /*perror("+++statfs"); */
    if(aux == -1) {
        lk_errors_set(E_NOMOUNT);
        return -1;
    }

    *n_files = buf.f_files;
    *s_size = buf.f_bsize * (uint64_t) buf.f_blocks;
    *f_space = buf.f_bsize * (uint64_t) buf.f_bfree;
    *highest_file_id = buf.f_files;

    return 0;

/* copied from my test karma */
/*   static uint32_t files = 0,        hfid  = 372613120; */
/*   static uint64_t size  = 19453096, space = 17141096; // 1024-byte blocks */
/*   *n_files = lk_htorl(files);      *highest_file_id = lk_htorl(hfid); */
/*   *s_size  = lk_htorll(1024*size); *f_space = lk_htorll(1024*space); */
}

/* -------------------------------------------------------------------------- */
int lk_karmaUsb_get_device_settings(int rio)
{
#define DEVICE_SETTINGS_FILE "device_settings"
#define MIN_DEV_SET_LENGTH 256   /* typical lengths are > 350 */
    static char buf[512];
    static uint32_t cached_device_generation = 0;
    static int32_t cached_serial = -1;
    int fd, ret = 0;
    char path[1024]="";

    if (cached_serial == -1) {
        ret = strlen(usbMountPoint);
        strncat(path, usbMountPoint, 1024);
        strncat(path+ret, DEVICE_SETTINGS_FILE, 1024-ret);
        fd = open(path, O_RDONLY);
        if(fd != -1)
            ret = read(fd, buf, 512);
        close(fd);
        if(ret < MIN_DEV_SET_LENGTH) {
            lk_errors_set(E_NODEVSET);
            cached_device_generation = 0;
            cached_serial = 0;
        } else {
            lk_karma_parse_settings(buf);
            cached_device_generation = device_generation;
            cached_serial = serial;
        }
    }

    device_generation = cached_device_generation;
    serial = cached_serial;

    return 0;
}

/* -------------------------------------------------------------------------- */
int32_t lk_karmaUsb_request_io_lock(int rio, uint32_t type)
{
/* ****************************** UNNEEDED ********************************** */
    return 0;
}

int32_t lk_karmaUsb_release_io_lock(int rio)
{
/* ****************************** UNNEEDED ********************************** */
    return 0;
}

/* smalldb helpers */

static int read32(FILE * fp, uint32_t * dest)
{
    int count = fread(dest, sizeof(uint32_t), 1, fp);
    *dest = lk_rtohl(*dest);
    return count == 1;
}

static int read16(FILE * fp, uint32_t * dest)
{
    uint16_t tmp;
    int count = fread(&tmp, sizeof(uint16_t), 1, fp);
    *dest = tmp;
    return count == 1;
}

static int read8(FILE * fp, uint32_t * dest)
{
    uint8_t tmp;
    int count = fread(&tmp, sizeof(uint8_t), 1, fp);
    *dest = tmp;
    return count == 1;
}

static
int is_deleted(karma_db_t * db, uint32_t index)
{
    if((index >> 3) >= db->dmap_size)
        return 0;
    return db->dmap[index >> 3] & (1 << (index % 8));
}

static int read_attribute(FILE * fp, smalldb_attrib_t * attrib,
                          char **prop_strs, int nfiles, size_t * allocated_size)
{
    int i;
    uint32_t otmpnum, tmpnum;
    char buf[BUFSIZ] = "";
    smalldb_type_t type = attrib->type;
    size_t needed_size;
    char *table = NULL;
    uint32_t table_len = 0;
    int ret = 0;
    long ptr, end_ptr = 0;

    buf[sizeof(buf) - 1] = 0;

    ptr = ftell(fp);
    if(type == SDB_STRING) {
        if(!read32(fp, &table_len))
            goto err;
        ptr = ftell(fp);
        fseek(fp, 4 * nfiles, SEEK_CUR);
    } else if(type == SDB_BLOB) {
        ptr = ftell(fp);
        fseek(fp, 4 * nfiles, SEEK_CUR);
        if(!read32(fp, &table_len))
            goto err;
    }
    if(type == SDB_BLOB || type == SDB_STRING) {
        if(table_len > 0) {
            table = malloc(table_len);
            if(!table)
                goto err;
            if(fread(table, 1, table_len, fp) != table_len)
                goto err;
        }
        end_ptr = ftell(fp);
        fseek(fp, ptr, SEEK_SET);
        if(type == SDB_BLOB) {
            if(!read32(fp, &otmpnum))
                goto err;
        }
    }

    for (i = 0; i < nfiles; i++) {
        switch (type) {
            case SDB_LE32:
            case SDB_BLOB:
            case SDB_STRING:
                if(!read32(fp, &tmpnum))
                    goto err;
                break;
            case SDB_LE16:
                if(!read16(fp, &tmpnum))
                    goto err;
                break;
            case SDB_CHAR:
                if(!read8(fp, &tmpnum))
                    goto err;
                break;
        }
        if(type != SDB_BLOB && type != SDB_STRING) {
            snprintf(buf, sizeof(buf) - 2, "%s=%d\n", attrib->name, tmpnum);
        } else if(type == SDB_STRING && table) {
            char *v = &table[tmpnum];
            if(v)
                snprintf(buf, sizeof(buf) - 2, "%s=%s\n", attrib->name, v);
        } else if(type == SDB_BLOB && table) {
            int len;
            char *v;

            if(i == nfiles - 1)
                len = table_len - otmpnum;
            else
                len = tmpnum - otmpnum;

            v = lk_playlist_escape(&table[otmpnum], len);
            if(v) {
                snprintf(buf, sizeof(buf) - 2, "%s=%s\n", attrib->name, v);
                free(v);
            }
            otmpnum = tmpnum;
        }

        if(strlen(buf) == 0)
            snprintf(buf, sizeof(buf) - 2, "%s=\n", attrib->name);

        if(!prop_strs[i]) {
            needed_size = strlen(buf) + 1;
            while(needed_size > *allocated_size)
                *allocated_size *= 2;
            prop_strs[i] = calloc(*allocated_size, 1);
            if(!prop_strs[i])
                goto err;
        }

        needed_size = strlen(prop_strs[i]) + strlen(buf) + 1;
        if(needed_size > *allocated_size) {
            while(needed_size > *allocated_size)
                *allocated_size *= 2;
            prop_strs[i] = realloc(prop_strs[i], *allocated_size);
            if(!prop_strs[i])
                goto err;
        }
        strcat(prop_strs[i], buf);
    }
    if(end_ptr > ptr)
        fseek(fp, end_ptr, SEEK_SET);
    ret = 1;
  err:
    if(table)
        free(table);
    return ret;
}

/* -------------------------------------------------------------------------- */
/* Essential Functions */
/* -------------------------------------------------------------------------- */
static int read_file_info(char *fname, char *path, int *offset, int *tlen,
                          char **properties)
{
    int oneFile, zeroFile;
    int got, ret, len, k;
    char realFile[1024], fidLine[32], *aux;
    struct stat statbuf;
    char *str;
    uint32_t fid;

    len = strlen(path);
    if (len < 1024) {
        memcpy(realFile, path, len);
        k = strlen(fname);
        if (len+k+1 < 1024)
            memcpy(realFile+len, fname, k+1);
    }
/*  fprintf(stderr, "--> %s\n", realFile); */
    realFile[strlen(realFile) - 1] = '1';
    oneFile = open(realFile, O_RDONLY);
    if(oneFile == -1) {
/*      fprintf(stderr,"\nWarning: wrong file (%s)\n", realFile); */
        lk_errors_set(E_NOPROP);
        return 1;
    }
    ret = fstat(oneFile, &statbuf);
    if(ret != 0) {
        lk_errors_set(E_NOPROP);
        return 1;
    }
    len = statbuf.st_size + 34;
    if(*tlen < len + *offset) {
        while(*tlen < len + *offset)
            *tlen += 1024;
        *properties = realloc(*properties, *tlen);
    }
    strncpy(fidLine, rindex(path, '_') + 1, 16);
    fidLine[strlen(fidLine) - 1] = '\0';
    strncat(fidLine, fname, 16 - strlen(fidLine));
    fid = strtol(fidLine, NULL, 16);
/*  fprintf(stderr,"%s %5d, %4d %7d, ", fidLine, fid, *numFiles, *offset); */
    sprintf(fidLine, "fid=%d\n", fid);
    aux = strcpy(*properties + *offset, fidLine);
    *offset += strlen(fidLine);
    got = read(oneFile, *properties + *offset, len);
/*  fprintf(stderr,"%3d %d\n", got, strlen(*properties)); */
    for (k = 0; k < got; k++)  /* illegal null bytes are converted to ' ' */
        if(*(*properties + *offset + k) == 0)
            *(*properties + *offset + k) = ' ';
    if(got == -1 || got > len - 2) {
        lk_errors_set(E_FAILEDREQ);
    }
/*  fprintf(stderr, "\n%s\n-------\n", *properties+*offset);  */
    *offset += got;
    if(strstr(*properties + *offset - got, "type=playlist") != NULL) {
        realFile[strlen(realFile) - 1] = '0';
        zeroFile = open(realFile, O_RDONLY);
        if(zeroFile == -1) {
            lk_errors_set(E_NOPROP);
            return 1;
        }
        ret = fstat(zeroFile, &statbuf);
        if(ret != 0) {
            lk_errors_set(E_NOPROP);
            return 1;
        }
        len = 4 * statbuf.st_size + strlen("playlist=\n");
        if(*tlen < len + *offset) {
            while(*tlen < len + *offset)
                *tlen += 1024;
            *properties = realloc(*properties, *tlen);
        }
        strncpy(*properties + *offset, "playlist=", len);
        *offset += strlen("playlist=");
        got = read(zeroFile, (*properties + *offset), len);
        close(zeroFile);
        str = lk_playlist_escape(*properties + *offset, got);
        got = strlen(str);
        memcpy(*properties + *offset, str, got);
        free(str);
        if(got == -1 || got > len - 2) {
            lk_errors_set(E_FAILEDREQ);
        }
        *offset += got;
        *(*properties + *offset) = '\n';
        (*offset)++;
    }
    *(*properties + *offset) = '\n';
    (*offset)++;
    close(oneFile);
    return 0;
}

static int32_t read_properties(char **properties)
{
    DIR *dp0, *dp;
    struct dirent *de0, *de;
    char path1[1024] = "", path[1024] = "";
    int numFiles = 0, offset = 0, tlen = 0, ret;

    strncpy(path1, usbMountPoint, 1023 - strlen("/fids0/_00000/100"));
    strcat(path1, "/fids0/");
    dp0 = opendir(path1);
    if(!dp0) {
        mk_path(path1);
        dp0 = opendir(path1);
    
        if (!dp0) {
            lk_errors_set(E_NODIR);
            return -1;
        }
    }
    while((de0 = readdir(dp0)) != NULL) {
        if(strncmp(de0->d_name, "_0", 2) != 0)
            continue;
        strcpy(path, path1);
        strcat(path, de0->d_name);
        strcat(path, "/");
        dp = opendir(path);
        if(dp == NULL) {
            lk_errors_set(E_NODIR);
            return -1;
        }
        while((de = readdir(dp)) != NULL) {
            if((strcmp(de->d_name, ".")) && (strcmp(de->d_name, "..")) &&
               de->d_name[strlen(de->d_name) - 1] == '0') {
                ret = read_file_info(de->d_name, path, &offset,
                                     &tlen, properties);
                if (!ret)
                    numFiles++;
/*
                fprintf(stderr, "\rReading: %3d files. Please wait... ",
                        numFiles);
*/
            }
        }
        closedir(dp);
    }
    closedir(dp0);
    if(*properties)
        *(*properties + offset) = '\0';
    else {
        lk_errors_set(E_FAILEDREQ);
        return -1;
    }
/*  fprintf(stderr, "------------------------------\n%s\n", *properties); */
    return 0;
}

static void scan_playlist(uint32_t id, uint32_t * nfiles,
                          size_t * allocated_size, char **prop_strs)
{
    playlist_t *pl = &db->playlists[id];
    char *ptr = pl->playlist;
    char *str, *prop;
    char buf[BUFSIZ] = "";
    uint32_t i, tmp32, fid, idx;
    uint16_t tmp16;
    char tmp8;
    size_t len = 0, plen = 1;
    int tmpnum;

    if(!ptr)
        return;

    idx = pl->index;

    if(!prop_strs[idx]) {
        prop_strs[idx] = calloc(*allocated_size, 1);
        if(!prop_strs[idx])
            goto err;
    }

    prop = prop_strs[idx];
    if(idx < *nfiles) {
        for (str = prop + 4; *str && *str != '\n'; str++) ;
        *str = '\0';
        fid = (uint32_t) atol(prop + 4);
        memcpy(&tmp32, ptr, sizeof(tmp32));
        if(fid != tmp32)
            fprintf(stderr, "error: fids do not match\n");
    } else
        *nfiles = idx + 1;

    for (i = 0; i < db->num_attribs; i++) {
        str = NULL;
        switch (db->attribs[i].type) {
            case SDB_STRING:
                str = ptr;
                if(str)
                    snprintf(buf, sizeof(buf) - 2, "%s=%s\n",
                             db->attribs[i].name, str);
                else
                    *buf = '\0';
                ptr += strlen(str) + 1;
                if(memcmp(db->attribs[i].name, "rid", 3) == 0)
                    ptr = pl->playlist + pl->len -
                          (db->num_attribs - i - 1) * sizeof(tmp32);
                break;
            case SDB_BLOB:
                memcpy(&tmp32, ptr, sizeof(tmp32));
                ptr += sizeof(tmp32);
                str = lk_playlist_escape(ptr, tmp32);
                if(str) {
                    snprintf(buf, sizeof(buf) - 2, "%s=%s\n",
                             db->attribs[i].name, str);
                    free(str);
                } else
                    *buf = '\0';
                ptr += tmp32;
                tmpnum = tmp32;
                break;
            case SDB_LE32:
                memcpy(&tmp32, ptr, sizeof(tmp32));
                ptr += sizeof(tmp32);
                len += sizeof(tmp32);
                tmpnum = tmp32;
                break;
            case SDB_LE16:
                memcpy(&tmp16, ptr, sizeof(tmp16));
                ptr += sizeof(tmp16);
                len += sizeof(tmp16);
                tmpnum = tmp16;
                break;
            case SDB_CHAR:
                memcpy(&tmp8, ptr, sizeof(tmp8));
                ptr += sizeof(tmp8);
                len += sizeof(tmp8);
                if(memcmp(db->attribs[i].name, "marked", 6) == 0)
                    ptr += sizeof(tmp8);
                tmpnum = tmp8;
                break;
        }
        if(!str)
            snprintf(buf, sizeof(buf) - 2, "%s=%d\n",
                     db->attribs[i].name, tmpnum);
        len = strlen(buf);
        plen += len;
        if(plen > *allocated_size) {
            while(plen > *allocated_size)
                *allocated_size *= 2;
            prop_strs[idx] = realloc(prop_strs[idx], *allocated_size);
            if(!prop_strs[idx])
                goto err;
            if(prop != prop_strs[idx])
                prop = prop_strs[idx] + plen - len;
        }

        memcpy(prop, buf, len);
        prop += len;
    }
    *prop = '\0';
    return;
  err:
    fprintf(stderr, "scan_playlist: error allocating memory\n");
}

static void delete_file(char *props)
{
    unsigned int fid;

    /* The first entry is always the fid */
    fid = atoi(&props[4]);
    unlink(lk_karmaUsb_fidToPath(0, fid));
}

static int32_t read_properties_smalldb(char **properties)
{
    /* parse smalldb for all properties */
    FILE *fp = NULL;
    char *tmp, *ptr;
    char **prop_strs = NULL;
    char *propnames = NULL;
    uint32_t tmpnum, nfiles;
    size_t allocated_size = BUFSIZ;    /* of each prop string */
    uint32_t i, j;
    int ret = -1;

    if(!db)
        db = calloc(sizeof(karma_db_t), 1);
    if(!db)
        goto err;

    tmp = calloc(strlen(usbMountPoint) + strlen(RK_SMALLDB) + 1, 1);
    if(!tmp)
        goto err;

    sprintf(tmp, "%s%s", usbMountPoint, RK_SMALLDB);
    fp = fopen(tmp, "rb");
    free(tmp);

    if(!fp)
        goto err;

    if(!read32(fp, &db->version))
        goto err;

    if(!read32(fp, &db->ctime))
        goto err;

    if(!read32(fp, &db->eight))
        goto err;

    if(!read32(fp, &db->three))
        goto err;

    if(!read32(fp, &db->num_attribs))
        goto err;

    db->attribs = malloc(db->num_attribs * sizeof(smalldb_attrib_t));
    if(!db->attribs)
        goto err;

    for (i = 0; i < db->num_attribs; i++) {
        uint32_t type;
        if(!read32(fp, &type))
            goto err;
        db->attribs[i].type = (smalldb_type_t) type;
        if(!read32(fp, &type))
            goto err;
        db->attribs[i].dunno = type;
    }

    if(!read32(fp, &db->prop_size))
        goto err;

    propnames = malloc(db->prop_size);
    if(fread(propnames, 1, db->prop_size, fp) != db->prop_size)
        goto err;

    tmp = strtok(propnames, " ");
    for (i = 0; i < db->num_attribs && *tmp; i++) {
        db->attribs[i].name = strdup(tmp);
        if(i < db->num_attribs - 1)
            tmp = strtok(NULL, " ");
    }
    if(i != db->num_attribs || !read32(fp, &db->num_playlists))
        goto err;

    db->playlists = malloc(db->num_playlists * sizeof(playlist_t));
    if(!db->playlists)
        goto err;

    for (i = 0; i < db->num_playlists; i++) {
        /* playlist section, copy as opaque data for now */
        read32(fp, &db->playlists[i].index);
        read32(fp, &db->playlists[i].len);
        db->playlists[i].playlist = malloc(db->playlists[i].len);
        fread(db->playlists[i].playlist, db->playlists[i].len, 1, fp);
    }
    fseek(fp, 4, SEEK_CUR);
    if(!read32(fp, &nfiles))
        goto err;
    if(nfiles == 0)
        goto out;

    /* we have to check the delete map after all the properties are read in,
     * so build each property string and store it in an array for later.
     */
    prop_strs = calloc((nfiles + db->num_playlists) * sizeof(char *), 1);
    if(!prop_strs)
        goto err;

    for (i = 0; i < db->num_attribs; i++)
        if(!read_attribute(fp, &db->attribs[i], prop_strs, nfiles,
                           &allocated_size))
            goto err;

    /* now read delete string */
    if(!read32(fp, &db->dmap_size))
        goto err;

    db->dmap = malloc(db->dmap_size);
    if(!db->dmap)
        goto err;

    fread(db->dmap, db->dmap_size, 1, fp);

    for (i = 0; i < db->num_playlists; i++)
        scan_playlist(i, &nfiles, &allocated_size, prop_strs);

    /* return concatenated string */
    ptr = tmp = malloc((allocated_size + 1) * nfiles);
    *tmp = 0;
    for (i = 0; i < nfiles; i++) {
        if(prop_strs[i]) {
            if(is_deleted(db, i)) {
                for (j = 0; j < db->num_playlists; j++)
                    if(db->playlists[j].index == i)
                        goto outer;
                delete_file(prop_strs[i]);
                continue;
            }
          outer:
            tmpnum = strlen(prop_strs[i]);
            memcpy(ptr, prop_strs[i], tmpnum);
            ptr += tmpnum;
            *ptr = '\n';
            ptr++;
            free(prop_strs[i]);
        }
    }
    *ptr = '\0';
    *properties = tmp;

  out:
    ret = 0;
  err:
    if(ret != 0)
        printf("Could not read smalldb\n");
    if(propnames)
        free(propnames);
    if(prop_strs)
        free(prop_strs);
    if (db)
    {
        db->dmap_size = 0;
        db->num_playlists = 0;
        if (db->dmap)
            free(db->dmap);
        if (db->playlists)
            free(db->playlists);
    }
    if(fp)
        fclose(fp);
    return ret;
}

/* .......................................................................... */
int32_t lk_karmaUsb_get_all_file_details(int rio, char **properties)
{
    return read_properties(properties);
}

/* -------------------------------------------------------------------------- */
int32_t lk_karmaUsb_read_file_chunk(int rio, uint64_t offset, uint64_t size,
                                    uint32_t file_id, char **data,
                                    uint64_t * retsize)
{
    int fd;
    int res;
    char *path = NULL;

    path = lk_karmaUsb_fidToPath(rio, file_id);
    fd = open(path, O_RDONLY);
    free(path);
/*
    if(offset==0)
        fprintf(stderr, "--> %s %x %d\n", path, file_id, file_id);
    fprintf(stderr, "---> %s\n", strerror(errno));
*/
    if(fd == -1) {
        close(fd);
        lk_errors_set(E_READ);
        return -1;
    }

    *data = (char *)malloc((size_t) size);
    res = pread(fd, *data, (size_t) size, (size_t) offset);
/*  fprintf(stderr, "--> %d\n", res); */
    if(res >= 0)
        *retsize = res;
    else {
        close(fd);
        lk_errors_set(E_READ);
        return -1;
    }

    close(fd);
    return 0;
}

/* -------------------------------------------------------------------------- */
int32_t lk_karmaUsb_write_file_chunk(int rio, uint64_t offset, uint64_t size,
                                     uint32_t file_id, uint32_t storage_id,
                                     const char *data)
{
#define PERMS S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH
    int fd;
    int res, ret;
    char *path = NULL;

    path = lk_karmaUsb_fidToPath(rio, file_id);

/*
    if(offset==0)
        fprintf(stderr, "--> %s %x %d\n", path, file_id, file_id);
*/
    if((fd = open(path, O_WRONLY | O_CREAT, PERMS)) == EEXIST)
        fd = open(path, O_WRONLY | O_TRUNC, PERMS);

    /* make sure the directory is there and try again */
    if (fd == -1) {
        mk_path(path);

        if((fd = open(path, O_WRONLY | O_CREAT, PERMS)) == EEXIST)
            fd = open(path, O_WRONLY | O_TRUNC, PERMS);
    }
    
    free(path);
/*  fprintf(stderr, "---> %s\n", strerror(errno)); */
    if(fd == -1) {
        close(fd);
        lk_errors_set(E_WRITE);
        return -1;
    }

    res = pwrite(fd, data, (size_t) size, (size_t) offset);
/*  fprintf(stderr, "--> %d\n", res); */
    if(res < 0) {
        close(fd);
        lk_errors_set(E_WRITE);
        return -1;
    }

    close(fd);
    return 0;
}

/* -------------------------------------------------------------------------- */
int32_t lk_karmaUsb_update_file_details(int rio, uint32_t file_id,
                                        char *properties)
{
/* Modify (or create) the *1 file of file_id with properties */
/* ************************* UPDATE smalldb TOO ?? ************************** */
#define PERMS S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH
    int fd;
    int len, res, ret;
    char *path = NULL;

    if(!properties) {
        lk_errors_set(E_FAILEDREQ);
        return -1;
    }

    path = lk_karmaUsb_fidToPath(rio, file_id);
    path[strlen(path) - 1] = '1';
/*  fprintf(stderr, "--> %s %x %d\n", path, file_id, file_id); */

    unlink(path);
    fd = open(path, O_WRONLY | O_CREAT, PERMS);

    if(fd == -1 && errno == ENOENT) {
        res = strlen(path);
        path[res - 4] = '\0';
        ret = mkdir(path, 0755);
        path[res - 4] = '/';
        if(!ret)
            fd = open(path, O_WRONLY | O_CREAT, PERMS);
    }

    free(path);
/*  fprintf(stderr, "---> %s\n", strerror(errno)); */
    if(fd == -1) {
        close(fd);
        lk_errors_set(E_WRITE);
        return -1;
    }

    len = strlen(properties);
    res = write(fd, properties, len);
/*  fprintf(stderr,"--> len=%d %d\n", len, res); */
    if(res == -1) {
        close(fd);
        lk_errors_set(E_WRITE);
        return -1;
    }

    close(fd);
    return 0;
}

/* -------------------------------------------------------------------------- */
int32_t lk_karmaUsb_delete_file(int rio, uint32_t file_id)
{
    int ret = 0;
    char *path = NULL;

    path = lk_karmaUsb_fidToPath(rio, file_id);
/*  fprintf(stderr, "--> %s %x %d\n", path, file_id, file_id); */
    if(unlink(path))
        ret = -1;

    path[strlen(path) - 1] = '1';
/*  fprintf(stderr, "--> %s\n", path); */
    if(unlink(path))
        ret = -1;

    if(ret == -1)
        lk_errors_set(E_DELETE);

    if (!ret) lk_properties_del_property(file_id);
    free(path);
    return ret;
}

/* -------------------------------------------------------------------------- */
int lk_karmaUsb_write_smalldb(void)
{
    int val = 0;

    errno = 0;
    val = lk_properties_write_smalldb(usbMountPoint, db);
    perror("lk_karmaUsb_write_smalldb()");
    return val;
}

/* -------------------------------------------------------------------------- */
/* Advanced Protocol Functions */
/* -------------------------------------------------------------------------- */
void lk_karmaUsb_load_database(int rio)
{
    int count = 0, aux = 0;
    char *properties = NULL;

    aux = lk_karmaUsb_get_device_settings(rio);
    aux = read_properties(&properties);
    if(properties)
        count = lk_properties_import(properties);
    else {
        lk_errors_set(E_FAILEDREQ);
        return;
    }
    lk_fdb_load(1);
    if(properties)
        free(properties);
}

void lk_karmaUsb_load_database_smalldb(int rio)
{
    int count = 0, aux = 0;
    char *properties = NULL;

    aux = lk_karmaUsb_get_device_settings(rio);
    aux = read_properties_smalldb(&properties);
    if(properties)
        count = lk_properties_import(properties);
    /* else just assume a new blank DB */
    else
        lk_properties_init();
    
    lk_fdb_load(1);
/*  fprintf(stderr, "smalldb: %d Files\n", count); */
    if(properties)
        free(properties);
}

/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* THE LAN VERSIONS USED ONLY INTERNALLY IN karmaLan.c 
int lk_karmaUsb_get_protocol_version(int rio, uint32_t *major_version,
                                     uint32_t *minor_version){return 0;}
int lk_karmaUsb_send_request(int rio, uint32_t identifier, 
                             char * payload, int plen){return 0;}
int32_t lk_karmaUsb_get_authentication_salt(int rio, char **salt)    {return 0;}
*/
/* -------------------------------------------------------------------------- */
/* UNUSED
int     lk_karmaUsb_get_device_details(int rio, char **name, char **version, 
                                       uint32_t *storagedevices){return 0;}
int32_t lk_karmaUsb_get_file_details(int rio, uint32_t file_id, 
                                     char **properties){return 0;}
 
int32_t lk_karmaUsb_update_device_settings(int rio, char *properties){return 0;}
int32_t lk_karmaUsb_prepare(int rio, uint64_t size, 
                            uint32_t *file_id, uint32_t storage_id){return 0;}
int32_t lk_karmaUsb_format_storage(int rio, uint32_t storage_id)     {return 0;}
int32_t lk_karmaUsb_device_operation(int rio, uint64_t size, 
                                     char *data, char **newdata)     {return 0;}
void lk_karmaUsb_update_database(int rio){}
*/
/* -------------------------------------------------------------------------- */
