/*
 * Copyright (c) 1998 Kungliga Tekniska Högskolan
 * (Royal Institute of Technology, Stockholm, Sweden).
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the Kungliga Tekniska
 *      Högskolan and its contributors.
 * 
 * 4. Neither the name of the Institute nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */


/* $Id: romedia.c,v 1.13 1998/10/25 21:36:24 art Exp $ */

#include "milko_locl.h"
#if defined(HAVE_DBM_H)
#include <dbm.h>
#elif defined(HAVE_NDBM_H)
#include <ndbm.h>
#else
#error No supported db format...
#endif
#include <fcntl.h>
#include <errno.h>
#include <afs_dir.h>
#include <assert.h>
#include "romedia.h"

#ifdef FILESYSTEM_READONLY

RCSID("$Id: romedia.c,v 1.13 1998/10/25 21:36:24 art Exp $");

int rom_readdir (char *path,
		 void (*)(VenusFid *, const char *name, void *), 
		 void *arg);
    

static void *
rom_init(char *db_name)
{
    DBM *db;

    db = dbm_open(db_name, O_RDONLY, 0600);

    if (db == NULL) {
	fprintf(stderr, "rom_init: cant open %s (%s:%d)\n", 
		db_name, 
		strerror(errno),
		errno);
	return NULL;
    }

    return db;
}

static int
rom_close(void *data, int reson)
{
    DBM *db = (DBM *) data;
    dbm_close(db);
    return 0;
}

static int
rom_senddir (char *path,
	     struct rx_call *call,
	     AFSFid dot,
	     AFSFid dot_dot,
	     mbuf *buf)
{
    int ret;
    mbuf the_mbuf;
    struct dirent *dp;
    DIR *dir;
    AFSFid tmp;
    int32_t i32;

    ret = mbuf_create(&the_mbuf, -1, 0, 0, MBUF_WRITE|MBUF_ANON);
    if (ret)
	return ret;

    ret = mdir_mkdir(&the_mbuf, dot, dot_dot);
    if (ret) 
	goto out;

    if ((dir = opendir(path)) == NULL) 
	goto out;

    while((dp = readdir(dir)) != NULL) {
	struct stat sb;
	char tmppath[MAXPATHLEN];
	
	if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) 
	    continue;
	
	snprintf(tmppath, sizeof(tmppath), "%s/%s", path, dp->d_name);
	
	if (stat(tmppath, &sb) == -1)
	    continue;
	
	tmp.Vnode = sb.st_ino;
	tmp.Unique = 1;
	tmp.Volume = dot.Volume;
	
	ret = mdir_creat(&the_mbuf, dp->d_name, tmp);
	if (ret)
	    goto out;
    }
    closedir(dir);
    
    i32 = htonl(the_mbuf.len);
    if (rx_Write(call, &i32, 4) != 4)
	return -1;
    
    ret = rx_Write(call, the_mbuf.buf, the_mbuf.len) != the_mbuf.len;
    
 out:
    if (buf)
	*buf = the_mbuf;
    else
	mbuf_end(&the_mbuf);
    return ret;
}

static int
romi_getstatus(const MilkoCred *cred, DBM *db, const struct AFSFid *fid, 
	       AFSFetchStatus *status, char *file, int len)
{
    datum   key, contents;
    char type;
    char *str, *strold, *tmp;
    char number[NUMSIZE] ;
    char path[MAXPATHLEN];
    
    assert(fid != NULL && status != NULL);

    memset(number, 0 , sizeof(number));
    
    snprintf(number, NUMSIZE, "%u", fid->Vnode);

    key.dptr =  number ;
    key.dsize = strlen(number);

    contents = dbm_fetch(db,key);
    if (contents.dptr == NULL) 
	return ENOENT;

    if (contents.dsize < 1)
	return ENOENT;

    strold = str = malloc(contents.dsize+1);
    memcpy(str, contents.dptr, contents.dsize);
    str[contents.dsize] = '\0';

    /* 
     * type(char):Length(int):ParentVnode(int):ServerModTime(int): 
     * path(string)
     */

    /* TYPE */

    type = *str;
    str++;
    assert(*str == ':');
    str++;

    /* LENGTH */

    tmp = strchr(str, ':');
    assert(*tmp == ':');
    *tmp = '\0';
    tmp++;
    status->Length = atoi(str);
    str = tmp;

    /* PARENTVNODE */

    tmp = strchr(str, ':');
    assert(*tmp == ':');
    *tmp = '\0';
    tmp++;
    status->ParentVnode = atoi(str);
    str = tmp;

    /* SERVERMODTIME */

    tmp = strchr(str, ':');
    assert(*tmp == ':');
    *tmp = '\0';
    tmp++;
    status->ServerModTime = atoi(str);
    str = tmp;

    /* PATH */

    strncpy(path, str, sizeof(path));
    path[sizeof(path)-1] = '\0';

    free(strold);

    /* XXX gdbm man page say free(), then __cfree then say SIGSEGV
       free(contents.dptr);
    */

    if ((tmp = strchr(path, ':')) != NULL)
	*tmp = '\0';

    switch(type) {
    case 'f':
	status->FileType = TYPE_FILE;
	break;
    case 'd':
	status->FileType = TYPE_DIR;
	break;
    case 'l':
	status->FileType = TYPE_LINK;
	break;
    default:
	assert(0);
	break;
    }
    
    status->Author = status->Owner = 0;
    status->ClientModTime = status->ServerModTime;
    status->Group = 0;
    status->DataVersion = 1;
    status->LinkCount = 1;
    status->ParentUnique = 1;
    
    status->CallerAccess = status->UnixModeBits = status->AnonymousAccess =
	S_IRUSR | S_IXUSR | \
	S_IRGRP | S_IXGRP | \
	S_IROTH | S_IXOTH ;

    if (file) {
	strncpy(file, path, MIN(MAXPATHLEN, sizeof(path)));
	file[MIN(MAXPATHLEN, sizeof(path))-1] = '\0';
    }

    return 0;
}


static int 
rom_getstatus(const MilkoCred *cred, void *data, const AFSFid *fid, 
	      AFSFetchStatus *status)
{
    return romi_getstatus(cred, (DBM*) data, fid, status, NULL, 0);
}

static int 
rom_getdata(const MilkoCred *cred, void *data, struct rx_call *rxcall,
	    const AFSFid *fid, const int32_t offset, const int32_t len,
	    AFSFetchStatus *status)
{
    int32_t nolen;
    int ret;
    char file[MAXPATHLEN];
    int fd;

    ret = romi_getstatus(cred, (DBM*) data, fid, status, file, MAXPATHLEN);
    if (ret)
	goto error;

    switch (status->FileType) {
    case TYPE_LINK:
    case TYPE_FILE: {
	int32_t nolen = htonl(len);

	if ((fd = open(file, O_RDONLY)) == -1) {
	    ret = errno;
	    goto error;
	}

	nolen = htonl(len);
	if (rx_Write(rxcall, &nolen, 4) != 4) {
	    ret = errno ? -2 : errno;
	    goto error;
	}
	
	ret = mcopyfd2rx (fd, rxcall, offset, len);
	if (ret)
	    goto error;

	goto done;
    }
    case TYPE_DIR: {
	mbuf *buf = NULL, buf2;
	AFSFid dot = { 1 , 1, 1}, dot_dot = {1, 1, 1} ;

	buf = bufcache_get(*fid);
	if (buf &&  
	    buf->off <= offset &&
	    buf->off + buf->len <= offset + len) {

	    fprintf(stderr, "rom_getfile: dir: cachehit\n");

	    nolen = htonl(len);
	    if (rx_Write(rxcall, &nolen, 4) != 4) {
		mbuf_end(buf);
		ret = errno ? -1 : errno ;
		goto error;
	    }

	    ret = mcopybuf2rx(rxcall, buf, offset, len);
	    if (ret) {
		mbuf_end(buf);
		goto error;
	    }
	} else {
	    if (buf) {
		mbuf_end(buf);
		bufcache_remove(*fid);
	    }

	    fprintf(stderr, "rom_getfile: dir: creating new structure\n");

	    dot.Volume = dot_dot.Volume = fid->Volume ; 
	    dot.Vnode = fid->Vnode; 
	    dot_dot.Vnode = status->ParentVnode;
	    
	    ret = rom_senddir (file, rxcall, dot, dot_dot, &buf2);
	    bufcache_add(*fid, &buf2);
	    mbuf_end(&buf2);
	}
	ret = 0;
	goto done;
    }
    default:
	abort();
    }
    abort();
    return -1;

 error:
    fprintf(stderr, "romedia: Error in FetchData %d\n", ret);
    return ret;
 done:
    fprintf(stderr, "romedia: Ended FetchData call %d\n",ret);
    return ret;
}

struct filesystem rom_filesystem = {
    "readonly-preindex",
    "readonly - filesystem $Revision: 1.13 $",
    rom_init,
    rom_close,
    rom_getstatus,
    NULL,
    rom_getdata,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
} ;

#endif /* FILESYSTEM_READONLY */
