/*
 * 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: ontopfs.c,v 1.15 1998/11/08 12:44:25 lha Exp $ */

#include "milko_locl.h"

#ifndef FILESYSTEM_ONTOP

/* make sure the file isn't empty */
RCSID("$Id: ontopfs.c,v 1.15 1998/11/08 12:44:25 lha Exp $ - Not compiled in");	

#else /* FILESYSTEM_ONTOP */

RCSID("$Id: ontopfs.c,v 1.15 1998/11/08 12:44:25 lha Exp $");

#define MILKO_MAXPART  25

#define OT_ENTRY_ALLOC 1
#define OT_ENTRY_SPARE 1  /* ALLOC and SPARE are never used the same time... */
#define OT_ENTRY_DIR   2

static int
findvolume(int32_t volid, struct ontop_vol **vol)
{
    int i, fd, error;
    struct stat sb;
    
    *vol = calloc(1, sizeof(*vol));
    if (vol == NULL)
	return errno;

    snprintf((*vol)->name, MAXPATHLEN, "/vicepX/vol%08d");
    
    for (i = 'a'  ;  i < 'a' + MILKO_MAXPART; i++) {
	(*vol)->name[6] = i;
	fd = open ((*vol)->name, O_RDWR, 0600);
	if (fd != -1)
	    break;
    }
    
    if (i == 'a' + MILKO_MAXPART) {
	free(*vol);
	*vol = NULL;
	return NULL;
    }

    error = fstat(fd, &sb);
    if (error || (!S_ISDIR(sb.st_mode) && (errno = ENOTDIR) == ENOTDIR)) {
	close(fd);
	free(*vol);
	*vol = NULL;
	return errno;
    }

    /* 
     * XXX iopen: Save a fd to the part if there is none yet saved.
     */
    close(fd);

    return 0;
}

static int
ontop_init(char *path, void **data)
{
    char partpath[MAXPATHLEN];
    int32_t volid;
    struct ontop_part *part;
    struct ontop_vol *vol;
    int i, error;

    assert (path);

    ontop_initworker();

    i = 0;
    while (*path != ':' && *path) 
	partpath[i++] = *path++;
    
    if (!*path)
	return EINVAL;
    
    partpath[i] = '\0';
    ++path;
    volid = atoi(path);
    
    error = ontop_open_part(partpath, &part);
    if (error)
	return error;

    error = ontop_open_volume(part, volid, &vol);

    *data = vol;

    ontop_release_part(part);

    return error;
}

static int
ontop_close(void *data, int reson)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;

    assert (vol);
    ontop_close_volume(vol);

    return 0;
}

static int
ontop_vnode2AFSFetchStatus(const MilkoCred *cred, struct ontop_vnode *vnode, 
			   AFSFetchStatus *status)
{
    /* XXX Parse the acltable and set this aproproate */
    status->FileType = vnode->FileType;
    status->CallerAccess = AREAD | ALIST | AWRITE | AINSERT | AADMIN | ADELETE;
    status->AnonymousAccess = AREAD | ALIST | AWRITE | AINSERT | AADMIN | ADELETE;

    /* XXX This is not really right, just an approximation */

    if (!(AREAD & status->CallerAccess || 
	  AREAD & status->AnonymousAccess ||
	  (status->FileType = TYPE_DIR && (
	      ALIST & status->CallerAccess ||
	      ALIST & status->CallerAccess)))) {

	memset(status, 0, sizeof(*status));
	return EPERM;
    }

    status->InterfaceVersion = vnode->InterfaceVersion;
    status->LinkCount = vnode->LinkCount;
    status->DataVersion = vnode->DataVersion;
    status->Author = vnode->Author;
    status->Owner = vnode->Owner;
    status->UnixModeBits = vnode->UnixModeBits;
    switch (vnode->FileType) {
    case TYPE_FILE:
	break;
    case TYPE_DIR:
	status->UnixModeBits |= 0040000;  
	break;
    case TYPE_LINK:
	status->UnixModeBits |= 0120000;
	break;
    default:
	abort();
    }
    status->ParentVnode = vnode->ParentVnode;
    status->ParentUnique = vnode->ParentUnique;
    status->ServerModTime = vnode->ModTime;
    status->Group = vnode->Group;
    status->ClientModTime = vnode->ModTime;
    status->SyncCount = 0;
    

    return 0;
}

static int
fdgetsize(int fd, int32_t *size)
{
    struct stat sb;
    int error;

    assert (size);

    error = fstat(fd, &sb);
    if (error)
	return errno;

    *size = sb.st_size;
    return 0;
}

/*
 * update_vnode
 *
 * Update `vnode' with the `storestatus'. 
 *   Returns TRUE if vnode is updated.
 */

static Bool
update_vnode(const MilkoCred *cred, struct ontop_vnode *vnode, 
	     const AFSStoreStatus *storestatus)
{
    assert (vnode && storestatus);

    /* XXX auth */

    if (storestatus->Mask & SS_MODTIME)
	vnode->ModTime = storestatus->ClientModTime;

    /* 
     *  XXX This is not right, but what should we do instead ? 
     *   if mem (pts user) == system:administrator then do
     *   else if Owner != pts user then complain
     *   else do, ie user might give-away a file
     */

    if (storestatus->Mask & SS_OWNER)
	vnode->Owner = storestatus->Owner;
    if (storestatus->Mask & SS_GROUP) 
	vnode->Group = storestatus->Group;
    if (storestatus->Mask & SS_MODEBITS)
	vnode->UnixModeBits = 0666 & storestatus->UnixModeBits;
#if 0
    if (storestatus->Mask & SS_SEGSIZE)
	/* XXX ? */;
#endif
    if ((storestatus->Mask & 0x1f) != 0)
	return TRUE;
    return FALSE;
}

static int 
ontop_getstatus(const MilkoCred *cred, void *data, const AFSFid *fid, 
		AFSFetchStatus *status)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error;
    struct stat sb;
    int fd;
    ontop_vnode *vnode;

    error = ontop_attach(vol);
    if (error)
	return error;

    error = ontop_open_vnode(vol, fid->Vnode, &vnode, &fd);
    if (error)
	goto err_out;

    if (vnode->Unique != fid->Unique) {
	error = ENOENT;
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }

    error = ontop_vnode2AFSFetchStatus(cred, vnode, status);
    if (error) {
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }

    error = fstat(fd, &sb);
    if (error == 0)
	status->Length = sb.st_size;
    
    ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);

 err_out:    
    ontop_release(vol);

    return error;
}

static int
ontop_setstatus(const MilkoCred *cred, void *data, 
		const AFSFid *fid, const AFSStoreStatus *sstatus,
		AFSFetchStatus *status)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error;
    struct stat sb;
    int fd;
    ontop_vnode *vnode;

    error = ontop_attach(vol);
    if (error)
	return error;

    error = ontop_open_vnode(vol, fid->Vnode, &vnode, &fd);
    if (error)
	goto err_out;

    if (vnode->Unique != fid->Unique) {
	error = ENOENT;
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }


    error = update_vnode(cred, vnode, sstatus);
    if (error) {
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	error = EPERM;
	goto err_out;
    }

    error = ontop_vnode2AFSFetchStatus(cred, vnode, status);
    if (error) {
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }

    error = fstat(fd, &sb);
    if (error == 0)
	status->Length = sb.st_size;
    
    ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);

 err_out:    
    ontop_release(vol);

    return error;
}

static int 
ontop_getdata(const MilkoCred *cred, void *data, struct rx_call *rxcall,
	      const AFSFid *fid, const int32_t offset, const int32_t len,
	      AFSFetchStatus *status)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error;
    ontop_vnode *vnode;
    int fd;

    error = ontop_attach(vol);
    if (error)
	return error;

    error = ontop_open_vnode(vol, fid->Vnode, &vnode, &fd);
    if (error)
	goto err_out;

    if (vnode->Unique != fid->Unique) {
	error = ENOENT;
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }

    error = ontop_vnode2AFSFetchStatus(cred, vnode, status);
    if (error) {
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }

    {
	int32_t reallen = 0;
	struct stat sb;

	error = fstat(fd, &sb);
	if (error)
	    goto err_out;
	status->Length = sb.st_size;

	if (len + offset > sb.st_size) {
	    error = EIO;
	    rx_Write(rxcall, &reallen, sizeof(reallen));
	    goto err_out;
	}
	reallen = htonl(len);
	rx_Write(rxcall, &reallen, sizeof(reallen));
    }
    mcopyfd2rx(fd, rxcall, offset, len);
    
    error = ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);

 err_out:    
    ontop_release(vol);

    return error;
}

static int 
ontop_putdata(const MilkoCred *cred, void *data, struct rx_call *rxcall,
	      const AFSFid *fid, const AFSStoreStatus *sstatus, 
	      const int32_t offset, const int32_t len, 
	      AFSFetchStatus *status, int32_t filelen)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error;
    ontop_vnode *vnode;
    int fd;
    struct stat sb;

    error = ontop_open_vnode(vol, fid->Vnode, &vnode, &fd);
    if (error)
	goto err_out;

    if (vnode->Unique != fid->Unique) {
	error = ENOENT;
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }

    error = fstat(fd, &sb);
    if (error) {
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }
    
    if (filelen < sb.st_size)
	ftruncate(fd, filelen);
    
    mcopyrx2fd(rxcall, fd, offset, len);

    error = ontop_vnode2AFSFetchStatus(cred, vnode, status);
    if (error) {
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }
    
    
    error = ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);

 err_out:    
    /*    ontop_release(vol); */

    return error;
}

/* 
 * createentry - common code between MakeDir, CreateFile and SymLink.
 * 
 * Check it the `name' exist in `parentfid' 
 * If it does't insert the name into parentfid create a new `vnode'
 * and init the statuspart with `storestatus'.
 *
 * Return opened `vnode'. If `flags|OT_ENTRY_DIR' is set, then make sure
 * the vnode gets a odd entry.
 *
 * This don't set the filetype of the vnode.
 * The vnode is returned opened (and not stored to disk).
 *
 */

static int
createentry(const MilkoCred *cred, struct ontop_vol *vol, 
	    const AFSFid *parentfid, const char *name,
	    const AFSStoreStatus *storestatus,
	    const int flags, AFSFid *newfid, AFSFetchStatus *statusparent,
	    ontop_vnode **vnode, int *rfd)
{
    int error;
    ontop_vnode *parentvnode;
    int parentfd;
    int fd;
    int32_t filelen;
    mbuf *the_mbuf;
    VenusFid vnewfid;
    VenusFid vparentfid;

    error = ontop_attach(vol);
    if (error)
	return error;

    error = ontop_open_vnode(vol, parentfid->Vnode, &parentvnode, &parentfd);
    if (error)
 	goto err_out;


    if (parentvnode->ReadOnlyp) {
	ontop_close_vnode(vol, parentfid->Vnode, *vnode, parentfd, FALSE);
	goto err_out;
    }


    error = fdgetsize(parentfd, &filelen);
    if (error) {
	ontop_close_vnode(vol, parentfid->Vnode, parentvnode, fd, FALSE);
	goto err_out;
    }

    error = mbuf_create(&the_mbuf, parentfd, 0, filelen, 
			MBUF_WRITE);
    if (error) {
	ontop_close_vnode(vol, parentfid->Vnode, parentvnode, parentfd, FALSE);
	goto err_out;
    }

    vparentfid.fid = *parentfid;
    vparentfid.Cell = 0;
    error = mdir_lookup (the_mbuf, 
			 vparentfid,
			 name, &vnewfid);

    if (error != ENOENT) {
	ontop_close_vnode(vol, parentfid->Vnode, parentvnode, parentfd, FALSE);
	error = EEXIST;
	mbuf_end(the_mbuf);
	goto err_out;
    }

    if (flags & OT_ENTRY_ALLOC) {
	error = ontop_alloc_vnode(vol, flags & OT_ENTRY_DIR, &newfid->Vnode);
	if (error) {
	    mbuf_end(the_mbuf);
	    ontop_close_vnode(vol, parentfid->Vnode, parentvnode, -1, FALSE);
	    goto err_out;
	}
    }

    error = ontop_open_vnode(vol, newfid->Vnode, vnode, &fd);
    if (error) {
	mbuf_end(the_mbuf);
	ontop_close_vnode(vol, parentfid->Vnode, parentvnode, -1, FALSE);
	goto err_out;
    }

    if (flags & OT_ENTRY_ALLOC) {
	(*vnode)->LinkCount = 1;
	(*vnode)->InterfaceVersion = 1;
	(*vnode)->DataVersion++;
	
	newfid->Unique = ++(*vnode)->Unique;
	newfid->Volume = parentfid->Volume;
    } else {
	++(*vnode)->LinkCount;
    }	

    ++parentvnode->LinkCount;

    /* XXX paranoia about the name ? */
    mdir_creat(the_mbuf, name, *newfid);

    ontop_vnode2AFSFetchStatus(cred, parentvnode, statusparent);
    error = fdgetsize(parentfd, &statusparent->Length);
    if (error)
	goto err_out2;

    mbuf_end(the_mbuf);
    parentfd = -1;

    if (rfd) 
	*rfd = fd;
    else
	close(fd);

    update_vnode(cred, *vnode, storestatus);

 err_out2:
    ontop_close_vnode(vol, parentfid->Vnode, parentvnode, parentfd, FALSE);

 err_out:    
    ontop_release(vol);

    return error;
}

static int
delete_entry(const MilkoCred *cred, struct ontop_vol *vol, 
	     const AFSFid *parentfid, const char *name, const int flags,
	     AFSFetchStatus *statusparent)
{
    int error;
    ontop_vnode *parentvnode;
    ontop_vnode *vnode;
    int parentfd;
    int fd;
    int32_t filelen;
    VenusFid vnewfid;
    VenusFid vparentfid;
    mbuf *the_mbuf;

    error = ontop_open_vnode(vol, parentfid->Vnode, &parentvnode, &parentfd);
    if (error)
 	return error;

    if (parentvnode->ReadOnlyp) {
	ontop_close_vnode(vol, parentfid->Vnode, parentvnode, parentfd, FALSE);
	return EROFS;
    }

    if (parentvnode->FileType != TYPE_DIR) {
	ontop_close_vnode(vol, parentfid->Vnode, parentvnode, parentfd, FALSE);
	return ENOTDIR;
    }

    error = fdgetsize(parentfd, &filelen);
    if (error) {
	ontop_close_vnode(vol, parentfid->Vnode, parentvnode, fd, FALSE);
	return EIO;
    }

    error = mbuf_create(&the_mbuf, parentfd, 0, filelen, 
			MBUF_WRITE);
    if (error) {
	ontop_close_vnode(vol, parentfid->Vnode, parentvnode, parentfd, FALSE);
	return EIO;
    }

    vparentfid.fid = *parentfid;
    vparentfid.Cell = 0;
    error = mdir_lookup (the_mbuf, 
			 vparentfid,
			 name, &vnewfid);

    if (error) {
	mbuf_end(the_mbuf);
	ontop_close_vnode(vol, parentfid->Vnode, parentvnode, -1, FALSE);
	return error;
    }
    
    error = ontop_open_vnode(vol, vnewfid.fid.Vnode, &vnode, &fd);
    if (error) {
	mbuf_end(the_mbuf);
	ontop_close_vnode(vol, parentfid->Vnode, parentvnode, -1, FALSE);
 	return error;
    }

    /*
     * If directory check if empty
     */
    if (flags & OT_ENTRY_DIR) {
	if (vnode->FileType == TYPE_DIR &&
	    (vnode->LinkCount != 1 && mdir_emptyp(the_mbuf))) {
	    ontop_close_vnode(vol, vnewfid.fid.Vnode, vnode, fd, FALSE);
	    mbuf_end(the_mbuf);
	    ontop_close_vnode(vol, parentfid->Vnode, parentvnode, -1, FALSE);
	    return ENOTEMPTY;
	}
    } else {
	if (vnode->FileType == TYPE_DIR) {
	    ontop_close_vnode(vol, vnewfid.fid.Vnode, vnode, fd, FALSE);
	    mbuf_end(the_mbuf);
	    ontop_close_vnode(vol, parentfid->Vnode, parentvnode, -1, FALSE);
	    return EISDIR;
	}
    }
    
    /* XXX return values */
    mdir_remove(the_mbuf, name);
    ontop_vnode2AFSFetchStatus(cred, parentvnode, statusparent);
    fdgetsize(fd, &statusparent->Length);
    
    if (!(flags & OT_ENTRY_SPARE))
	error = ontop_free_vnode(vol, vnewfid.fid.Vnode);

    mbuf_end(the_mbuf);
    ontop_close_vnode(vol, parentfid->Vnode, parentvnode, -1, FALSE);

    return 0;
}


static int 
ontop_makedir(const MilkoCred *cred, void *data, struct rx_call *call,
	      const AFSFid *parentfid,
	      const char *name, const AFSStoreStatus *storestatus,
	      AFSFid *newfid, AFSFetchStatus *newstatus,
	      AFSFetchStatus *statusparent, AFSCallBack *callbackfid,
	      AFSVolSync *volsync)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error;
    int fd;
    ontop_vnode *vnode;
    mbuf *the_mbuf;

    error = createentry(cred, vol, parentfid, name, storestatus,
			OT_ENTRY_ALLOC|OT_ENTRY_DIR, newfid, 
			statusparent, &vnode, &fd);
    if (error)
	return error;

    vnode->FileType = TYPE_DIR;

    error = mbuf_create(&the_mbuf, fd, 0, 0, MBUF_WRITE);
    if (error == 0) {
	mdir_mkdir(the_mbuf, *newfid, *parentfid);

	vnode->UnixModeBits |= 0100; /* XXX: Add execute for the user */
	ontop_vnode2AFSFetchStatus(cred, vnode, newstatus);
	error = fdgetsize(fd, &newstatus->Length);
	
	mbuf_end(the_mbuf);
	fd = -1;
    } 
    ontop_close_vnode(vol, newfid->Vnode, vnode, fd, TRUE);

    return error;
}

static int 
ontop_removedir(const MilkoCred *cred, void *data, struct rx_call *call,
		const AFSFid *parentfid, const char *dirname,
		AFSFetchStatus *dirstatus,
		AFSVolSync *volsync)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error = 0;
    
    /* XXX auth */
    
    error = ontop_attach(vol);
    if (error)
	return error;
    
    error = delete_entry(cred, vol, parentfid, dirname, OT_ENTRY_DIR, 
			 dirstatus);

    ontop_release(vol);

    return error;
}

static int 
ontop_removefile(const MilkoCred *cred, void *data, struct rx_call *call,
		const AFSFid *parentfid, const char *dirname,
		AFSFetchStatus *dirstatus,
		AFSVolSync *volsync)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error = 0;
    
    /* XXX auth */
    
    error = ontop_attach(vol);
    if (error)
	return error;
    
    error = delete_entry(cred, vol, parentfid, dirname, 0, dirstatus);

    ontop_release(vol);

    return error;
}


static int 
ontop_createfile (const MilkoCred *cred, void *data, struct rx_call *call,
		  const AFSFid *parentfid,
		  const char *name,
		  const AFSStoreStatus *storestatus,
		  AFSFid *newfid,
		  AFSFetchStatus *status,
		  AFSFetchStatus *statusparent,
		  AFSCallBack *CallBack,
		  AFSVolSync *a_volSyncP)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error;
    int fd;
    ontop_vnode *vnode;

    error = ontop_attach(vol);
    if (error)
	return error;
    
    error = createentry(cred, vol, parentfid, name, storestatus,
			OT_ENTRY_ALLOC, newfid, statusparent, &vnode, &fd);

    if (error == 0) {
	vnode->FileType = TYPE_FILE;
	ontop_vnode2AFSFetchStatus(cred, vnode, status);
	status->Length = 0;
	ftruncate(fd, 0);
	ontop_close_vnode(vol, newfid->Vnode, vnode, fd, TRUE);
    }
    return error;
}

static int
ontop_symlink(const MilkoCred *cred, void *data, struct rx_call *call,
	      const AFSFid *parentfid,
	      const char *name,
	      const char *contents,
	      const AFSStoreStatus *storestatus,
	      AFSFid *newfid,
	      AFSFetchStatus *status,
	      AFSFetchStatus *statusparent,
	      AFSVolSync *volsync)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error;
    int fd;
    ontop_vnode *vnode;

    error = ontop_attach(vol);
    if (error)
	return error;

    error = createentry(cred, vol, parentfid, name, storestatus,
			OT_ENTRY_ALLOC, newfid, statusparent, &vnode, &fd);

    if (error == 0) {
	int len = strlen(contents);
	vnode->FileType = TYPE_LINK;
	ontop_vnode2AFSFetchStatus(cred, vnode, status);
	lseek(fd, 0, SEEK_SET);
	write(fd, contents, len);
	status->Length = len;
	ontop_close_vnode(vol, newfid->Vnode, vnode, fd, TRUE);
    }
    ontop_release(vol);
    return error;
}

static int
ot_lookup(struct ontop_vol *vol, const AFSFid *dirfid, 
	  const char *name, AFSFid *fid)
{
    int error;
    ontop_vnode *vnode;
    int fd;
    mbuf *the_mbuf;
    int32_t size;
    VenusFid vfid, vdirfid;

    vdirfid.fid = *dirfid;
    vdirfid.Cell = 0;

    error = ontop_open_vnode(vol, dirfid->Vnode, &vnode, &fd);
    if (error)
	return error;

    if (vnode->FileType != TYPE_DIR) {
	ontop_close_vnode(vol, dirfid->Vnode, vnode, fd, FALSE);
	return ENOTDIR;
    }
    
    fdgetsize(fd, &size);
    error = mbuf_create(&the_mbuf, fd, 0, size, MBUF_READ);
    if (error) {
	ontop_close_vnode(vol, dirfid->Vnode, vnode, fd, FALSE);
	return error;
    }

    error = mdir_lookup(the_mbuf, vdirfid, name, &vfid);
    mbuf_end(the_mbuf);
    ontop_close_vnode(vol, dirfid->Vnode, vnode, fd, FALSE);

    if (error)
	return error;

    *fid = vfid.fid;

    return 0;
}


static int 
ontop_rename(const MilkoCred *cred, void *data, struct rx_call *call,
	     const AFSFid *a_origDirFidP,
	     const char *a_origNameP,
	     const AFSFid *a_newDirFidP,
	     const char *a_newNameP,
	     AFSFetchStatus *a_origDirStatusP,
	     AFSFetchStatus *a_newDirStatusP,
	     AFSVolSync *a_volSyncP)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error;
    ontop_vnode *vnode;
    AFSFid oldfid;
    AFSStoreStatus sstatus;
    
    sstatus.Mask = 0;

    error = ontop_attach(vol);
    if (error)
	return error;

    error = ot_lookup(vol, a_origDirFidP, a_origNameP, &oldfid);
    if (error) {
	ontop_release(vol);
	return error;
    }
    error = createentry(cred, vol, a_newDirFidP, a_newNameP,
			&sstatus, 0, &oldfid, a_newDirStatusP, &vnode, NULL);
    ontop_close_vnode(vol, oldfid.Vnode, vnode, -1, TRUE);
    if (error == 0)
	error = delete_entry(cred, vol, a_origDirFidP, a_origNameP, 
			     OT_ENTRY_SPARE, a_origDirStatusP);

    ontop_release(vol);
    return error;
}

static int
ontop_link(const MilkoCred *cred, void *data, struct rx_call *call,
	   const AFSFid *parentfid,
	   const char *name,
	   const AFSFid *existingfid,
	   AFSFetchStatus *status,
	   AFSFetchStatus *existingstatus,
	   AFSVolSync *volsync)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int error;
    ontop_vnode *vnode;
    AFSFid oldfid;
    AFSStoreStatus sstatus;
    
    sstatus.Mask = 0;

    error = ontop_attach(vol);
    if (error)
	return error;

    error = ot_lookup(vol, parentfid, name, &oldfid);
    if (error != ENOENT) {
	ontop_release(vol);
	return error;
    }
    error = createentry(cred, vol, parentfid, name,
			&sstatus, 0, (AFSFid *) existingfid, 
			existingstatus, &vnode, NULL);
    if (error == 0) {
	error = ontop_vnode2AFSFetchStatus(cred, vnode, status);
	ontop_close_vnode(vol, oldfid.Vnode, vnode, -1, TRUE);
    }

    ontop_release(vol);
    return error;
}

static int
ontop_fetchacl(const MilkoCred *cred,
	       void *data,
	       const AFSFid *fid,
	       AFSOpaque *opaque,
	       AFSFetchStatus *status)
{
    struct ontop_vol *vol = (struct ontop_vol*) data;
    int i, error, fd;
    ontop_vnode *vnode;
    struct AFSAcl *acl = NULL;
    struct stat sb;

    error = ontop_attach(vol);
    if (error)
	return error;

    acl = calloc(sizeof(struct AFSAcl) + 
		 (sizeof(struct AFSAclEntry) * (vnode->numacl - 1)), 
		 1);
    if (acl == 0) {
	error = ENOMEM;
	goto err_out;
    }

    error = ontop_open_vnode(vol, fid->Vnode, &vnode, &fd);
    if (error)
	goto err_out;
    
    if (vnode->Unique != fid->Unique) {
	error = ENOENT;
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }

    for (i = 0; i < vnode->numacl; i++) {
	snprintf(acl->acl_entry[i].acle_name, PR_MAXNAMELEN,
		 "%d", vnode->acl[i].id);
	acl->acl_entry[i].acle_right = vnode->acl[i].rights;
    }

    error = ontop_vnode2AFSFetchStatus(cred, vnode, status);
    if (error) {
	ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);
	goto err_out;
    }

    error = fstat(fd, &sb);
    if (error == 0)
	status->Length = sb.st_size;

    ontop_close_vnode(vol, fid->Vnode, vnode, fd, FALSE);

    error = afsacl_generate(acl,(void **) &opaque->val, &opaque->len);
    
    err_out:
    if (acl)
	free(acl);
    ontop_release(vol);
    
    return error;
}

static int
ontop_storeacl(const MilkoCred *cred, void *data,
	       const AFSFid *a_dirFidP,
	       const AFSOpaque *a_ACLToStoreP,
	       AFSFetchStatus *a_dirNewStatP,
	       AFSVolSync *a_volSyncP)
{
    return EPERM;
}

/*
 * The definition of the ONTOP filesystem
 */

struct filesystem ontop_filesystem = {
    "ontop",
    "ontop - filesystem $Revision: 1.15 $",
    ontop_init,		/* fs_init */
    ontop_close,	/* fs_close */

    ontop_getstatus,	/* fs_getstatus */
    ontop_setstatus,	/* fs_setstatus */

    ontop_getdata,	/* fs_getdata */
    ontop_putdata,	/* fs_putdata */

    ontop_makedir,	/* fs_makedir */
    ontop_removedir,	/* fs_removedir */

    ontop_createfile,	/* fs_createfile */
    ontop_rename,	/* fs_rename */
    ontop_removefile,	/* fs_removefile */

    ontop_symlink,	/* fs_symlink */
    ontop_link,		/* fs_link */

    ontop_fetchacl,	/* fs_fetchacl */
    ontop_storeacl,	/* fs_storeacl */
    NULL		/* fs_giveupcallback */
} ;

#endif /* FILESYSTEM_ONTOP */
