/*#define FSDB 1*/
/* ----------------------------------------------------------------
 *   FILE
 *	be-fsstubs.c
 *	
 *   DESCRIPTION
 *	support for filesystem operations on large objects
 *
 *   SUPPORT ROUTINES
 *
 *   INTERFACE ROUTINES
 * int LOopen(char *fname, int mode);
 * int LOclose(int fd);
 * bytea LOread(int fd, int len);
 * int LOwrite(int fd, char *buf);
 * int LOlseek(int fd,int offset,int whence);
 * int LOcreat(char *path, int mode,int objtype);
 * int LOtell(int fd);
 * int LOftruncate(int fd);
 * bytea LOstat(int fd);
 * int LOmkdir(char *path, int mode);
 * int LOrmdir(char *path);
 * int LOunlink(char *path);
 *	
 *   NOTES
 *      This should be moved to a more appropriate place.  It is here
 *      for lack of a better place.
 *
 *   IDENTIFICATION
 *	$Header: /usr/local/dev/postgres/mastertree/src/lib/libpq/RCS/be-fsstubs.c,v 1.14 1992/06/15 08:23:27 dpassage Exp $
 * ----------------------------------------------------------------
 */

/*
 * Builtin functions for open/close/read/write operations on large objects.
 *
 * The functions of the same name in the ADT implementations of large objects
 * should be made less general, e.g. GregLOOpen, etc.  These stubs are meant 
 * to handle any large object.
 *
 * These functions operate in the current portal variable context, which 
 * means the large object descriptors hang around between transactions and 
 * are not deallocated until explicitly closed, or until the portal is 
 * closed.
 *
 *
 * int LOopen(char *fname, int mode);
 *     return file descriptor that can subsequently be used in LOread/write/
 *     close.
 * int LOclose(int fd);
 * bytea LOread(int fd, int len);
 * int LOwrite(int fd, char *buf);
 * int LOlseek(int fd,int offset,int whence);
 * int LOcreat(char *path, int mode, int objtype);
 * int LOtell(int fd);
 * int LOftruncate(int fd);
 * bytea LOstat(int fd);
 * int LOmkdir(char *path, int mode);
 * int LOrmdir(char *path);
 * int LOunlink(char *path);
 *
 * Implementation:
 *    LOopen given filename and mode (ignored):
 *       map filename to OID of large object (FilenameToOID).
 *       map OID to large object cookie (LOassocOIDandLargeObjDesc).
 *       call LO dependent open on cookie.
 *       allocate a file descriptor in table corresponding to result of open.
 *       return file descriptor.
 *      (if at any step, there is an error, e.g. OID not found, table full,
 *       return an error.)
 *    LOclose given file descriptor
 *       call LO dependent close on cookie.
 *       deallocate file descriptor in table.
 *    LOread given file descriptor, length
 *       map length to object dependent addressing, e.g. bytes to blocks.
 *       call LO dependent read on cookie.
 *       return variable length byte array.
 *    LOwrite given file descriptor, byte array
 *       map length of array to object dependent addressing
 *       call LO dependent write on cookie (possibly reading and re-writing
 *       partial blocks).
 */

#include "tmp/c.h"
#include "tmp/postgres.h"
#include "tmp/simplelists.h"
#include "tmp/libpq.h"
#include "tmp/libpq-fs.h"
/*#include "utils/large_object.h"*/
#include "utils/mcxt.h"
#include "catalog/pg_lobj.h"
#include "catalog/pg_naming.h"

#include "utils/log.h"

static GlobalMemory fscxt = NULL;

/* HAck protos.  move to .h later */
/* lo_api.c */
void *LOCreate ARGS((char *path , int open_mode ));
void *LOOpen ARGS((struct varlena *object , int open_mode ));
void LOClose ARGS((void *obj_desc ));
int LOUnixStat ARGS((void *obj_desc , struct pgstat *stbuf ));
int LOSeek ARGS((void *obj_desc , int offset , int whence ));
int LOTell ARGS((void *obj_desc ));
int LORead ARGS((void *obj_desc , char *buf , int n ));
int LOWrite ARGS((void *obj_desc , char *buf , int n ));

/* inv_api.c */
void *inv_create ARGS((char *name, int mode ));
void  *inv_open ARGS((struct varlena *object, int flags ));
int inv_read ARGS((void *f , char *dbuf , int nbytes ));
int inv_write ARGS((void *f , char *dbuf , int nbytes ));
void inv_close ARGS((void *f ));
int inv_seek ARGS((void *f , int loc ));
int inv_tell ARGS((void *obj_desc ));
int inv_stat ARGS((void *obj_desc , struct pgstat *stbuf ));

static int noaction() { return  -1; } /* error */
static void *noactionnull() { return (void *) NULL; } /* error */
/* These should be easy to code */

/* This is ordered upon objtype values.  Make sure that entries are sorted
   properly. */

static struct {
    int bigcookie; 
#define SMALL_INT 0	 /* is integer wrapped in a varlena */
#define BIG 1  /* must be passed as varlena*/
    void *(*LOcreate) ARGS((char *,int)); /* name,mode -> runtime-cookie */
    void *(*LOopen) ARGS((void *,int)); /* persistant-cookie,mode -> runtime-cookie */
    void (*LOclose) ARGS((void *)); /* runtime-cookie */
    int (*LOread) ARGS((void *,char *,int)); /* rt-cookie,buf,length -> bytecount*/
    int (*LOwrite) ARGS((void *,char *,int)); /*rt-cookie,buf,length -> bytecount*/
    int (*LOseek) ARGS((void *,int,int)); /*rt-cookie,offset,whence -> bytecount */
    int (*LOtell) ARGS((void *)); /*rt-cookie ->bytecount*/
    int (*LOunixstat) ARGS((void *,struct pgstat *)); /* rt-cookie,stat ->errorcode*/
} LOprocs[] = {
    /* Inversion */
    { BIG, inv_create, inv_open, inv_close, inv_read, inv_write,
	inv_seek, inv_tell, inv_stat},
    /* Unix */
    { BIG, LOCreate, LOOpen, LOClose, LORead, LOWrite,
	LOSeek, LOTell, LOUnixStat}
};


int LOopen(fname,mode)
     char *fname;
     int mode;
{
    oid loOID;
    struct varlena *lobjCookie;
    char *lobjDesc;
    int fd;
    int objtype;
    MemoryContext currentContext;

#if FSDB
    elog(NOTICE,"LOopen(%s,%d)",fname,mode);
#endif
    loOID = FilenameToOID(fname);

    if (loOID == InvalidObjectId) { /* lookup failed */
	if ((mode  & O_CREAT) != O_CREAT) {
#if FSDB
	elog(NOTICE,"LOopen lookup failed");
#endif
	    return -1;
	} else {
	    /* Have to choose a particular type */
	    objtype = Unix;
	    return LOcreat(fname,mode,objtype);
	}
    }
    
    lobjCookie = LOassocOIDandLargeObjDesc(&objtype,loOID);
    
    if (lobjCookie == NULL) { /* lookup failed, perhaps a dir? */
#if FSDB
	elog(NOTICE,"LOopen assoc failed");
#endif
	return -1;
    } else {
#if FSDB
	elog(NOTICE,"LOopen assoc succeeded");
#endif
    }
  
    /* temp debug testing */
    {
	if (fscxt == NULL) {
	    fscxt = CreateGlobalMemory("Filesystem");
	}
	currentContext = MemoryContextSwitchTo((MemoryContext)fscxt);
#if 0
	/* switch context to portal cxt */
	{
	    PortalEntry * curPort;
	    curPort = be_currentportal();
	    currentContext = MemoryContextSwitchTo(curPort->portalcxt);
	}
#endif
	switch (LOprocs[objtype].bigcookie) {
	  case SMALL_INT:
	    lobjDesc = (char *)
	      LOprocs[objtype].LOopen((void *) *((int *)VARDATA(lobjCookie)),mode);
	    break;
	  case BIG: 
	    lobjDesc = (char *) LOprocs[objtype].LOopen(lobjCookie,mode);
	    break;
	}
	    
	if (lobjDesc == NULL) {
	    MemoryContextSwitchTo(currentContext);
#if FSDB
	    elog(NOTICE,"LOopen objtype specific open failed");
#endif
	    return -1;
	}
	fd = NewLOfd(lobjDesc,objtype);
	/* switch context back to orig. */
	MemoryContextSwitchTo(currentContext);
    }
    return fd;
}

#define MAX_LOBJ_FDS 256
char  *cookies[MAX_LOBJ_FDS];
int lotype[MAX_LOBJ_FDS];

#if deadcode
static int current_objaddr[MAX_LOBJ_FDS]; /* current address in LO's terms */
static int current_objoffset[MAX_LOBJ_FDS]; /* offset in LO's terms */
#endif

int LOclose(fd)
     int fd;
{
    void DeleteLOfd();

    if (fd >= MAX_LOBJ_FDS) {
	elog(WARN,"LOclose(%d) out of range",fd);
	return -2;
    }
    if (cookies[fd] == NULL) {
	elog(WARN,"LOclose(%d) invalid file descriptor",fd);
	return -3;
    }
#if FSDB
	elog(NOTICE,"LOclose(%d)",fd);
#endif
    /* Kemnitz large objects */
    {
	MemoryContext currentContext;
	Assert(fscxt != NULL);
	currentContext = MemoryContextSwitchTo((MemoryContext)fscxt);
	
	LOprocs[lotype[fd]].LOclose(cookies[fd]);

	MemoryContextSwitchTo(currentContext);
    }
    DeleteLOfd(fd);
    return 0;
}

/*
 *  We assume the large object supports byte oriented reads and seeks so
 *  that our work is easier.
 */
struct varlena *
LOread(fd,len)
     int fd;
     int len;
{
    struct varlena *retval;
    int bytestoread;
    int totalread = 0;

    retval = (struct varlena *)palloc(sizeof(int32) + len);
    totalread = LOprocs[lotype[fd]].LOread(cookies[fd],VARDATA(retval),len);
    VARSIZE(retval) = totalread + sizeof(int32);

    return retval;
}

int LOwrite(fd,wbuf)
     int fd;
     struct varlena *wbuf;
{
    int totalwritten;
    int bytestowrite;

    bytestowrite = VARSIZE(wbuf) - sizeof(int32);
    totalwritten = LOprocs[lotype[fd]].LOwrite(cookies[fd],VARDATA(wbuf),
					       bytestowrite);
    return totalwritten;
}

int
LOlseek(fd,offset,whence)
     int fd,offset,whence;
{
    int pos;

    if (fd >= MAX_LOBJ_FDS) {
	elog(WARN,"LOSeek(%d) out of range",fd);
	return -2;
    }
    return LOprocs[lotype[fd]].LOseek(cookies[fd],offset,whence);
}

int
LOcreat(path,mode,objtype)
     char *path;
     int mode;
     int objtype;
{
    char *lobjDesc;
    int fd;
    MemoryContext currentContext;

    /* prevent garbage code */
    if (objtype != Inversion && objtype != Unix)
	objtype = Unix;

    if (fscxt == NULL) {
	fscxt = CreateGlobalMemory("Filesystem");
    }

    currentContext = MemoryContextSwitchTo((MemoryContext)fscxt);

    lobjDesc = (char *) LOprocs[objtype].LOcreate(path,mode);

    if (lobjDesc == NULL) {
	MemoryContextSwitchTo(currentContext);
	return -1;
    }

    fd = NewLOfd(lobjDesc,objtype);

    /* switch context back to original memory context */
    MemoryContextSwitchTo(currentContext);

    return fd;
}

int LOtell(fd)
     int fd;
{
    if (fd >= MAX_LOBJ_FDS) {
	elog(WARN,"LOtell(%d) out of range",fd);
	return -2;
    }
    if (cookies[fd] == NULL) {
	elog(WARN,"LOtell(%d) cookie is NULL",fd);
	return -3;
    }
    return LOprocs[lotype[fd]].LOtell(cookies[fd]);
}

int LOftruncate()
{
}

struct varlena *
LOstat(path)
     char *path;
{
    struct varlena *ret;
    struct pgstat *st;
    unsigned int nblocks, byte_offset;
    int len;
    int pathOID;
    len = sizeof(struct pgstat);
    ret = (struct varlena *) palloc(len+sizeof(int32));
    VARSIZE(ret) = len + sizeof(int32);
    st = (struct pgstat *)VARDATA(ret);
    bzero(st,len);	/* default values of 0 */

    pathOID =  FilenameToOID(path);
    st->st_ino = pathOID;

    if (!LOisdir(path) && pathOID != InvalidObjectId) {
	int fd = LOopen(path,O_RDONLY);
	LOprocs[lotype[fd]].LOunixstat(cookies[fd],st);
	LOclose(fd);
    } else if (pathOID != InvalidObjectId) { /* isdir */
	st->st_mode = S_IFDIR;
	/* our directoiries don't exist in the filesystem, so give them
	   artificial permissions */
	st->st_uid = getuid(); /* fake uid */
	st->st_mode |= S_IRUSR|S_IWUSR|S_IXUSR;
    } else {
	VARSIZE(ret) = 9; /* 4 + 5 */
    }

    return ret;
}

/*
 * LOrename can be found in naming.c
 */

int LOmkdir(path,mode)
     char *path;
     int mode;
{
    oid oidf;
    /* enter new pathname */
    oidf = LOcreatOID(path,mode);
    if (oidf == InvalidObjectId) {
	return -1;
    } else {
	return 0;
    }
}

int LOrmdir(path)
     char *path;
{
    if (!LOisdir(path))
      return -1;
    else if (!LOisemptydir(path))
      return -1;
    else {
      LOunlink(path);
      return 0;
    }
}

int LOunlink(path)
     char *path;
{
    /* remove large object descriptor */
    /* remove naming entry */
    oid deloid;
    
    deloid = LOunlinkOID(path);
    if (deloid != InvalidObjectId) { /* remove associated file if any */
	LOunassocOID(deloid);
    }
}

/*
 * ------------------------------------
 * Support routines for this file
 * ------------------------------------
 */
int NewLOfd(lobjCookie,objtype)
     char *lobjCookie;
     int objtype;
{
    int i;

    for (i = 0; i < MAX_LOBJ_FDS; i++) {

	if (cookies[i] == NULL) {
	    cookies[i] = lobjCookie;
	    lotype[i] =  objtype;

	    return i;
	}
    }
    return -1;
}

void DeleteLOfd(fd)
     int fd;
{
    cookies[fd] = NULL;
}
