/*
 * 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.
 */

#include "milko_locl.h"
#include <sl.h>

RCSID("$Id: ontop_subr.c,v 1.12 1998/11/09 20:44:47 lha Exp $");

static void
OntopThread(void *data);

int ontop_precreatevnode(struct ontop_vol *vol, int panic);

/* 
 *
 */


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Get ontop HEADER stuff
 */


static int
lseek_read_get_header(struct ontop_vol *vol)
{
    char *buf;
    int error = 0;
    int size = ONTOP_HEADER_SIZE;

    buf = malloc(ONTOP_HEADER_SIZE);
    if (buf == NULL)
	return ENOMEM;
    
    if (lseek(vol->fd, 0, SEEK_SET) !=0) {
	error = errno;
	goto err_out;
    }

    if (read(vol->fd, buf, ONTOP_HEADER_SIZE) != ONTOP_HEADER_SIZE) {
	error = EIO;
	goto err_out;
    }

    if (ydr_decode_ontop_header(&vol->header, buf, &size) == 0) {
	error = errno;
	goto err_out;
    }
    
 err_out:
    free(buf);
    return error;
}

static int
lseek_write_put_header(struct ontop_vol *vol)
{
    char *buf;
    int error = 0;
    int size = ONTOP_HEADER_SIZE;

    buf = malloc(ONTOP_HEADER_SIZE);
    if (buf == NULL)
	return ENOMEM;
    
    if (ydr_encode_ontop_header(&vol->header, buf, &size) == 0) {
	error = errno;
	goto err_out;
    }

    if (lseek(vol->fd, 0, SEEK_SET) !=0) {
	error = errno;
	goto err_out;
    }

    if (write(vol->fd, buf, ONTOP_HEADER_SIZE) != ONTOP_HEADER_SIZE) {
	error = EIO;
	goto err_out;
    }

 err_out:
    free(buf);
    return error;
}


/*
 *
 */

static int
get_header(struct ontop_vol *vol, int readwritep, 
	   int readinp, struct ontop_header **hdr)
{
    int error=0;

    assert (vol && hdr);

#ifdef PARANOID
    *hdr = NULL;
#endif

    assert (vol->fd != -1);

    if (readwritep) {
	ObtainWriteLock(&vol->hdrlock);
	vol->flags.rwp = TRUE;
    } else {
	ObtainReadLock(&vol->hdrlock);
	vol->flags.rwp = FALSE;
    }

    if (vol->flags.validp == FALSE && readinp) {
	/* XXX mmap, lseek, what else (aio) ? */
	error = lseek_read_get_header(vol);
	if (error)
	    return error;
	vol->flags.validp = FALSE;
    }

    ++vol->refcount;
    *hdr = &vol->header;

    return error;
}


/*
 * Release header when we are done with it.
 * Set writep if we want it to be flushed to disk,
 * the header has to be "opened" write to be able to 
 * be written.
 */

void
release_header(struct ontop_vol *vol, int writep)
{
    int rw = vol->flags.rwp;

    assert(vol);
    assert(CheckLock(&vol->hdrlock));
    
    vol->flags.rwp = FALSE;

    if (rw) {
	if (writep) {
	    /* XXX lseek/write, mmap */
	    assert (rw); /* must be opened rw */
	    lseek_write_put_header(vol);
	} else
	    /* flush data, we dont know if its changed */
	    vol->flags.validp = FALSE; 

	ReleaseWriteLock(&vol->hdrlock);
    } else
	ReleaseReadLock(&vol->hdrlock);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Ontop VNODE
 */


#if 0
static void
print_fd_size(char *msg, int fd)
{
    struct stat sb;
    
    if (fstat(fd, &sb) == -1)
	warn("fstat");
    
    printf("%s%ssize is %d\n", msg ? msg : "", msg ? ": " : "", sb.st_size);
}

static void
print_vnode_size(struct ontop_vol *vol, int32_t VnodeNum)
{ 
    int error;
    struct ontop_vnode *vnode;
    int fd;

    error = ontop_open_vnode(vol, VnodeNum, &vnode, &fd);
    if (error)
	warnx("ontop_open_vnode: %d", error);

    print_fd_size(NULL, fd);
    
    error = ontop_close_vnode(vol, VnodeNum, vnode, fd, FALSE);
    if (error)
	warnx("ontop_open_vnode: %d", error);
}
#endif

/*
 * Get the vnode-data, assumes attached volume
 */

static int
get_vnode(struct ontop_vol *vol, int32_t ino, ontop_vnode **vnode)
{
    int error = 0;
    unsigned len;
    char *buf;
    unsigned long where;

    assert (vol && vnode);
    assert (vol->fd != -1);

    buf = calloc(1, ONTOP_VNODE_SIZE);
    if (buf == NULL)
	return errno;

    *vnode = calloc(1, sizeof(**vnode));
    if (*vnode == NULL) {
	free(buf);
	return errno;
    }

    len = lseek(vol->fd, 0L, SEEK_END);
    if (len == -1) { 
	error = errno;
	goto err_out;
    }

#ifdef PARANOID
    if (len % ONTOP_VNODE_SIZE)
	warnx(ONTOP_VNODETABLE " is not %% ONTOP_VNODE_SIZE");
#endif

    where = ONTOP_VNODE_SIZE * ino;
    
    if (len < where) {
	error = ENOENT;
	goto err_out;
    }

    len = lseek(vol->fd, where, SEEK_SET);
    if (len == -1) {
	errno = ENOENT;
	goto err_out;
    }

    if (read(vol->fd, buf, ONTOP_VNODE_SIZE) != ONTOP_VNODE_SIZE) {
	error = ENOENT;
	goto err_out;
    }
    
    len = ONTOP_VNODE_SIZE;
    if (ydr_decode_ontop_vnode(*vnode, buf, &len) == NULL) {
	error = ENOENT;
	goto err_out;
    }

    if ((*vnode)->FileType == TYPE_DIR) {
	assert (ino & 1);
    }

err_out:
    if (error) {
	free(*vnode);
	*vnode = NULL;
    } else
	vol->refcount++;
    free(buf);
    return error;
}


/*
 * store vnode-data, assumes that volume is attached
 */

int
release_vnode(struct ontop_vol *vol, int32_t ino, ontop_vnode *vnode,
	      int updatep)
{
    int error = 0;

    assert (vol && vnode);
    assert (vol->fd != -1);

    if (updatep) {
	char *buf;
	unsigned long where;
	size_t len;

	buf = calloc(1, ONTOP_VNODE_SIZE);
	if (buf == NULL) {
	    error = errno;  
	    goto err_out;
	}
	
	len = lseek(vol->fd, 0L, SEEK_END);
	if (len == -1) { 
	    error = errno;
	    goto err_out;
	}
	
#ifdef PARANOID
	if (len % ONTOP_VNODE_SIZE)
	    warnx(ONTOP_VNODETABLE " is not %% ONTOP_VNODE_SIZE");
#endif
	
	where = ONTOP_VNODE_SIZE * ino;
	
	if (len < where) {
	    error = ENOENT;
	    goto err_out;
	}
	
	len = lseek(vol->fd, where, SEEK_SET);
	if (len == -1) {
	    errno = ENOENT;
	    goto err_out;
	}
	
	len = ONTOP_VNODE_SIZE;
	if (ydr_encode_ontop_vnode(vnode, buf, &len) == NULL) {
	    error = errno;
	    goto err_out;
	}

	if (write(vol->fd, buf, ONTOP_VNODE_SIZE) != ONTOP_VNODE_SIZE) {
	    error = errno;
	    goto err_out;
	}
	
    err_out:
	if (buf)
	    free(buf);
    }

    --vol->refcount;
    assert(vol->refcount >= 0);

    free(vnode);

    return error;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Ontop PRINTING
 */

int
ontop_print_sizes(void)
{
    printf("ONTOP_VNODE_SIZE: %d\n", ONTOP_VNODE_SIZE);
    return 0;
}

char *ontop_store_types[] = {
    "not_initlized",
    "local",
    "innode_link",
    "gotfh"
};

int
print_ontop_vnode(ontop_vnode *vnode)
{
    char *store_type;

    printf("ontop_vnode:\n  next %d\n", vnode->next);
    if(vnode->store_type < 0 || 
       vnode->store_type > sizeof(ontop_store_types))
	store_type = "Invalid storage type";
    else
	store_type = ontop_store_types[vnode->store_type]; 
	   
    printf("  store:\n    type: %s\n    inode: %d\n",
	   store_type,
	   vnode->store_inode);

    if (vnode->used == 0) {
	printf("  Free\n");
	return 0;
    }
	
    printf("  ReadOnlyp: %s\n"
	   "  LinkCount: %d\n"
	   "  FileType: ",
	   vnode->ReadOnlyp ? "yes" : "no",
	   vnode->LinkCount);

    switch (vnode->FileType) {
    case TYPE_DIR:
	printf("dir\n"); break;
    case TYPE_FILE:
	printf("file\n"); break;
    case TYPE_LINK:
	printf("link\n"); break;
    default:
	printf("Unknown FileType\n"); break;
    }

    return 0;
}

/*
 *
 */

int
print_ontop_header(ontop_header *hdr)
{
    assert(hdr);

    printf("ontop-header\n"
	   "  size: %d\n"
	   "  oddfreeNodes: %d\n"
	   "  evenfreeNodes: %d\n"
	   "  evenfreeList: %d\n"
	   "  oldfreeList: %d\n",
	   hdr->size, hdr->oddfree, hdr->evenfree, hdr->evenfreeList,
	   hdr->oddfreeList);

    return 0;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Ontop VNODETABLE (volume global)
 */

static int
open_vnodetable(char *path, int *fd)
{
    char *file;
    int error = 0;

    assert (path && fd);

    asprintf(&file, "%s/" ONTOP_VNODETABLE, path);

    *fd = open(file, O_RDWR | O_CREAT, 0600); /* XXX */
    if (*fd == -1) 
	error = errno ;
    
    free(file);
    return error;
}

int
ontop_open_volume(struct ontop_part *part, int32_t volid, 
		  struct ontop_vol **vol)
{
    assert (part && vol);

    *vol = calloc(1, sizeof(struct ontop_vol));
    if (*vol == NULL)
	return errno;

    snprintf((*vol)->name, sizeof((*vol)->name), 
	     "%s/vol%08d", part->name, volid);

    ontop_use_part(part);

    (*vol)->part = part;
    (*vol)->fd = -1;
    (*vol)->refcount = 0;

    return 0;
}

int
ontop_close_volume(struct ontop_vol *vol)
{
    assert (vol);
    
    assert (vol->refcount == 0);

    ontop_release_part(vol->part);

    free(vol);
    return 0;
}

/*
 * ontop_attach
 */

int
ontop_attach(struct ontop_vol *vol)
{
    int error;
    assert (vol);
    
    if (vol->fd == -1) {
	error = open_vnodetable(vol->name, &vol->fd);
	if (error)
	    return error;
    }
    ++vol->refcount;
    return 0;
}

/*
 * ontop_release
 */

int
ontop_release(struct ontop_vol *vol)
{
    /* XXX cache for speed */

    assert (vol->fd != -1);

    if (--vol->refcount == 0) {
	close(vol->fd); 
	vol->fd = -1;
    }
    return 0;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Ontop VNODE
 */

/*
 * openvnode
 *
 *  This will fetch other stuff too...
 */

static int
openvnode(struct ontop_vol *vol, ontop_vnode *vnode, int *rfd)
{
    int ret;
    assert (vol && vnode && rfd);

    switch (vnode->store_type) {
    case ONTOP_TYPE_LOCAL:
	break;
    case  ONTOP_TYPE_GOTFH:

	ret = fhopen((xfs_cache_handle *) &vnode->storage, O_RDWR);
	if (ret == -1)
	    if (errno == EOPNOTSUPP)
		break;
	    else
		return errno;
	*rfd = ret;
	return 0;
    default:
	/* abort(); */
    }
    {
	char name[MAXPATHLEN];
	ino_t ino;
	
	ino = vnode->store_inode;
	
	snprintf(name, sizeof(name), "%s/%x/%x/%x/%x", vol->name, 
		 (ino >> 24) & 0xff, 
		 (ino >> 16) & 0xff,
		 (ino >> 8) & 0xff,
		 ino & 0xff);
	
	*rfd = open(name, O_RDWR, 0600);
	if (*rfd == -1)
	    return errno;
    }
    return 0;
}

int
ontop_create_vnode(struct ontop_vol *vol, struct ontop_vnode *vnode)
{
    char name[MAXPATHLEN];
    char nodename[MAXPATHLEN];
    int error;
    int fd;
    ino_t ino;
    struct stat sb;
    int saved_errno;

    snprintf(nodename, sizeof(nodename), "%s/inodeXXXXXX", vol->name);
    fd = mkstemp(nodename);
    if (fd == -1)
	return errno;
    error = fstat(fd, &sb);
    if (error) {
	close(fd);
	unlink(nodename);
	return errno;
    }
    ino = sb.st_ino;
    error = ftruncate(fd, 0);
    if (error) {
	saved_errno = errno;
	close(fd);
	return saved_errno;
    }
    close(fd);

    snprintf(name, sizeof(name), "%s/%x", vol->name, 
	     (ino >> 24) & 0xff);
    mkdir(name, 0700);
    snprintf(name, sizeof(name), "%s/%x/%x", vol->name, 
	     (ino >> 24) & 0xff, 
	     (ino >> 16) & 0xff);
    mkdir(name, 0700);
    snprintf(name, sizeof(name), "%s/%x/%x/%x", vol->name, 
	     (ino >> 24) & 0xff, 
	     (ino >> 16) & 0xff,
	     (ino >> 8) & 0xff);
    mkdir(name, 0700);
    snprintf(name, sizeof(name), "%s/%x/%x/%x/%x", vol->name, 
	     (ino >> 24) & 0xff, 
	     (ino >> 16) & 0xff,
	     (ino >> 8) & 0xff,
	     ino & 0xff );

    error = rename(nodename, name);
    if (error)
	return errno;

    vnode->store_inode = ino;

    error = fhget(name, (xfs_cache_handle *) vnode->storage);
    if (error != -1) {
	vnode->store_type = ONTOP_TYPE_GOTFH;
    } else
	vnode->store_type = ONTOP_TYPE_LOCAL;
	

    return 0;
}

/*
 * open_vnode - fill in ontop_vnode struct and return a fd if fd != NULL
 */

int
ontop_open_vnode(struct ontop_vol *vol, int32_t ino,
		 ontop_vnode **vnode, int *rfd)
{
    int error = 0;

    assert (vol && vnode);

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

    if ((ino & 1) == 0)
	ino = ino >> 1;

    error = get_vnode(vol, ino, vnode);
    if (error) {
	ontop_release(vol);
	return error;
    }
    
    if (rfd) 
	error = openvnode(vol, *vnode, rfd);

    ontop_release(vol);

    return error;
}

/*
 *
 */

int
ontop_close_vnode(struct ontop_vol *vol, int32_t ino,
		 ontop_vnode *vnode, int fd, int updatep)
{
    int error = 0;

    assert (vol && vnode);

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

    if ((ino & 1) == 0)
	ino = ino >> 1;

    error = release_vnode(vol, ino, vnode, updatep);

    if (fd != -1) 
	close(fd);

    ontop_release(vol);

    return error;
}

/*
 * Create an empty entry in the the on disk vnode table and
 * the file related to this vnode.
 */

int
ontop_createondisk_vnode(struct ontop_vol *vol, int vnodenum, int next)
{
    int size;
    int error;
    ontop_vnode vnode;
    char buf[ONTOP_VNODE_SIZE];

    memset(&vnode, 0, sizeof(vnode));
    vnode.InterfaceVersion = 1;
    vnode.Unique = 1;
    vnode.DataVersion = 0;
    vnode.store_type = ONTOP_TYPE_LOCAL;
    vnode.magic = ONTOP_MAGIC_VNODE;
    vnode.next = next;
    
    size = ONTOP_VNODE_SIZE;

    error = ontop_create_vnode(vol, &vnode);
    if (error) {
	warnx("Error creating vnode %d (error %s (%d))", vnodenum+1,
	      strerror(error), error);
	return 1;
    }
    
    ydr_encode_ontop_vnode(&vnode, buf,  &size);
    lseek(vol->fd, (vnodenum+1) * ONTOP_VNODE_SIZE, SEEK_SET);
    if (write (vol->fd, buf, ONTOP_VNODE_SIZE) != ONTOP_VNODE_SIZE)
	warn("ontop_create_volume: Could not write vnode %d", vnodenum + 1);
    
    return 0;
}


/*
 * create more vnodes
 */

static int
create_more_vnodes(struct ontop_vol *vol, ontop_header *hdr, int size)
{
    int oldsize;
    int i, next;
    int32_t oldodd, oldeven;
    int32_t newodd, neweven;

    printf("ontop_subr: ontop_create_more_vnodes\n");

    assert (size % 2 == 0);

    oldodd = hdr->oddfreeList;
    oldeven = hdr->evenfreeList;

    oldsize = hdr->size + 1;

    hdr->oddfree += size/2;
    hdr->evenfree += size/2;
    hdr->size += size;

    if (oldsize & 1) {
	newodd = oldsize; neweven = oldsize + 1;
    } else {
	newodd = oldsize + 1; neweven = oldsize;
    }
    for (i = oldsize; i < size + oldsize ; i++) {

	/* last vnode ? */
	if (i == size + oldsize - 1 || 
	    i == size + oldsize - 2) {
	    if (i & 1)
		next = oldodd;
	    else
		next = oldeven;
	} else
	    next = i + 3;

	ontop_createondisk_vnode(vol, i, next);
    }

    hdr->oddfreeList = newodd;
    hdr->evenfreeList = neweven;

    return 0;
}

/*
 * ontop_create_volume
 */

int
ontop_create_volume(struct ontop_part *part, int32_t volid)
{
    int error;
    ontop_header *hdr;
    struct ontop_vol *vol;
    int i, next;

    assert (ONTOP_VNODE_SIZE > ONTOP_HEADER_SIZE);
    assert ((ONTOP_NEWVNODES & 1) == 0);
    assert (ONTOP_NEWVNODES >= 2);

    error = ontop_open_volume(part, volid, &vol);
    if (error)
	return error;
    error = mkdir(vol->name, 0700);
    if (error)
	return errno;

    error = ontop_attach(vol);
    if (error) {
	ontop_release(vol);
	return error;
    }

    error = ftruncate(vol->fd, ONTOP_VNODE_SIZE * (ONTOP_NEWVNODES + 1));
    if (error) 
	goto err_out;

    error = get_header(vol, TRUE, FALSE, &hdr);
    if (error) 
	goto err_out;

    hdr->oddfreeList = 3;
    hdr->evenfreeList = 2;
    hdr->oddfree = ONTOP_NEWVNODES/2;
    hdr->evenfree = ONTOP_NEWVNODES/2;
    hdr->size = ONTOP_NEWVNODES;
    hdr->magic = ONTOP_MAGIC_HEADER;
    hdr->type = ONTOP_HDRTYPE;

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

	/* last vnode ? */
	if (i == ONTOP_NEWVNODES - 1 || i == ONTOP_NEWVNODES - 2)
	    next = 0;
	else
	    next = i + 3;

	ontop_createondisk_vnode(vol, i, next);
    }

    release_header(vol, 1);

    /* Do magic fid (volid,1,1) */
    printf("creating fid (%d,1,1)\n", volid);
    {
	ontop_vnode *vnode;
	int fd;
	mbuf *the_mbuf;
	AFSFid dot = { 1, 1, 1};

	error = ontop_open_vnode(vol, 1, &vnode, &fd);
	if (error) {
	    warnx("ontop_create_volume: ontop_open_vnode failed with %d",
		  error);
	    ontop_release(vol);
	    goto err_out;
	}
		    
	vnode->FileType = TYPE_DIR;
	vnode->Owner = 0; /* XXX can this be done better ? */
	vnode->Group = 0; 
	vnode->Unique = 1; /* Make sure ...*/
	vnode->UnixModeBits = 0644;


	error = mbuf_create(&the_mbuf, fd, 0, 0, MBUF_WRITE);
	if (error) {
	    warnx("ontop_create_volume: mbuf_create failed with %d", error);
	    ontop_close_vnode(vol, 1, vnode, fd, TRUE);
	    ontop_release(vol);
	    goto err_out;
	}

	mdir_mkdir(the_mbuf, dot, dot);

	assert (the_mbuf->len == AFSDIR_PAGESIZE);
	assert (the_mbuf->reallen == AFSDIR_PAGESIZE);

	mbuf_end(the_mbuf);

	error = ontop_close_vnode(vol, 1, vnode, -1, TRUE); /*mbuf_end closes fd*/
    }
    fsync(vol->fd);
    ontop_release(vol);

    return error;

 err_out:
    ontop_release(vol);
    ontop_close_volume(vol);
    return error;
}

/*
 * allocate a new vnode from volume
 *  will attach to the volume
 */

int 
ontop_alloc_vnode(struct ontop_vol *vol, int dirp, int32_t *rino)
{
    ontop_header *hdr;
    struct ontop_vnode *vnode;
    int error;
    int32_t ino;
    int32_t *nextptr = NULL;
    int onlyonce;
    int free;

    assert (vol);

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

    error = get_header(vol, TRUE, FALSE, &hdr);
    if (error) {
	ontop_release(vol);
	return error;
    }

    printf("ontop_alloc_vnode: oddfree: %d evenfree: %d size: %d used: %d\n",
	   hdr->oddfree, hdr->evenfree, hdr->size, hdr->used);
    
    onlyonce = 1;
    do{
	/*
	 * For historic resons directories has to have odd vnode-numbers,
	 * we has special case for that.
	 */

	if (dirp ||
	    (vol->header.oddfree > vol->header.evenfree && 
	     !hdr->compat)) {
	    free = --vol->header.oddfree;
	    nextptr = &vol->header.oddfreeList;
	} else {
	    free = --vol->header.evenfree;
	    nextptr = &vol->header.evenfreeList;
	}
	
	if (free < ONTOP_NEWVNODESLIMIT || *nextptr == 0) {
	    release_header(vol, nextptr == NULL ? FALSE : TRUE );
	    ontop_precreatevnode(vol, *nextptr == 0);
	    get_header(vol, TRUE, FALSE, &hdr);
	}
    } while (onlyonce-- && *nextptr == 0);

    if (*nextptr == 0) {
	release_header(vol, FALSE);
	ontop_release(vol);
	return ENOSPC;
    } 

    ino = *nextptr; 
	
    error = get_vnode(vol, ino, &vnode);
    if (error == 0) {
	*nextptr = vnode->next;
	vnode->used = TRUE;
	vnode->next = 0;

	vnode->LinkCount = 1;

	error = release_vnode(vol, ino, vnode, TRUE);
	vnode = NULL;
	++hdr->used;
    }

    release_header(vol, error == 0 ? TRUE : FALSE);

    ontop_release(vol);

    if (error == 0) {
	if (!dirp)
	    ino = ino << 1;
	*rino = ino;
    } else
	*rino = 0;

    return error;
}

int
ontop_free_vnode(struct ontop_vol *vol, int32_t ino)
{
    int error;
    ontop_header *hdr;
    struct ontop_vnode *vnode;

    assert (vol);

    if ((ino & 1) == 0) 
	ino = ino >> 1;

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

    error = get_header(vol, TRUE, FALSE, &hdr);
    if (error) {
	ontop_release(vol);
	return error;
    }

    printf("ontop_free_vnode: oddfree: %d evenfree: %d size: %d used: %d\n",
	   hdr->oddfree, hdr->evenfree, hdr->size, hdr->used);

    error = get_vnode(vol, ino, &vnode);
    if (error == 0) {
	assert (vnode->used);
	
	--vnode->LinkCount;

	if (vnode->LinkCount == 0) {
	    vnode->used = FALSE;
	    if ((ino & 1) == 1) {
		vnode->next = hdr->oddfreeList;
		hdr->oddfreeList = ino;
		++hdr->oddfree;
	    } else {
		vnode->next = hdr->evenfreeList;
		hdr->evenfreeList = ino;
		++hdr->evenfree;
	    }
	    --hdr->used;
	}
	
	error = release_vnode(vol, ino, vnode, TRUE);
	vnode = NULL;
    }
    
    release_header(vol, error == 0 ? TRUE : FALSE);

    ontop_release(vol);

    return error;
}


int 
ontop_open_part(char *path, struct ontop_part **part)
{
    int error;
    struct stat sb;

    assert (path && part);

    error = stat(path, &sb);
    if (error)
	return errno;

    if (!S_ISDIR(sb.st_mode)) {
	warnx("ontop_open_part: %s is not a directory", path);
	return EINVAL;
    }
    
    *part = calloc(1, sizeof(struct ontop_part));
    if (*part == NULL)
	return errno;

    strncpy((*part)->name, path, sizeof((*part)->name));
    (*part)->name[sizeof((*part)->name)-1] = '\0';
   
    (*part)->refcount = 1;

    return 0;
}

int
ontop_use_part(struct ontop_part *part)
{
    assert (part->refcount > 0);

    ++part->refcount;
    return 0;
}

int 
ontop_release_part(struct ontop_part *part)
{
    assert (part);

    assert (part->refcount > 0);

    if (--part->refcount == 0)
	free(part);

    return 0;
}


/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * Ontop CREATOR
 */


static List *ontopthreadworkingqueue;

typedef enum { CREATE_VNODE } ot_worktype;



struct ontop_workelement {
    ot_worktype type;
    struct ontop_vol *vol;
    int num;
} ;


int
ontop_precreatevnode(struct ontop_vol *vol, int panic)
{
    struct ontop_workelement *el;
    int error;

    assert (ontopthreadworkingqueue);

    if (vol->flags.precreate) {
	if (panic) {
	    LWP_NoYieldSignal((char *) OntopThread);
	    LWP_WaitProcess((char *) vol);
	}
	return 0;
    }
	    
    error = ontop_attach(vol);
    if (error)
	return error;

    el = calloc(sizeof(*el), 1);
    if (el == NULL)
	return ENOMEM;

    el->type = CREATE_VNODE;
    el->vol = vol;
    el->num = ONTOP_NEWVNODES;

    vol->flags.precreate = TRUE;
    if (panic)
	vol->flags.wantsignal = TRUE;

    listaddtail(ontopthreadworkingqueue, el);

    LWP_NoYieldSignal((char *) OntopThread);
    if (panic)
	LWP_WaitProcess((char *) vol);

    return 0;
}

/*
 *
 */

#define THREAD_STACKSIZE (16*1024)
static PROCESS ontopworkingpid;

int
ontop_initworker(void)
{
    static ot_init = 0;
    int error;

    if (ot_init)
	return 0;

    /* init queue */
    ontopthreadworkingqueue = listnew();
    if (ontopthreadworkingqueue == NULL)
	return ENOMEM;

    /* create thread */

    error = LWP_CreateProcess (OntopThread, THREAD_STACKSIZE, LWP_MAX_PRIORITY,
			       NULL, "ontop-workingthread", &ontopworkingpid);
    if (error)
	abort();

    ++ot_init;
    return 0;
}

/*
 *
 */

static void
OntopThread(void *data)
{
    struct ontop_workelement *el;
    ontop_header *hdr;

    while (1) {
	if (listemptyp(ontopthreadworkingqueue))
	    LWP_WaitProcess((char *) OntopThread);

	assert (!listemptyp(ontopthreadworkingqueue));

	el = listdelhead (ontopthreadworkingqueue);
	assert(el);

	switch (el->type) {
	case CREATE_VNODE:
	    assert (el->vol);
	    el->num -= ONTOP_NEWVNODESLIMIT;
	    
	    get_header(el->vol, TRUE, FALSE, &hdr);
	    create_more_vnodes(el->vol, hdr, ONTOP_NEWVNODESLIMIT);
	    release_header(el->vol, TRUE);

	    if (el->vol->flags.wantsignal) {
		el->vol->flags.wantsignal = FALSE;
		LWP_SignalProcess(el->vol);
	    }

	    if (el->num <= 0) {
		el->vol->flags.precreate = FALSE;
		ontop_release(el->vol);
		free(el);
		break;
	    }
	    
	    listaddtail(ontopthreadworkingqueue, el);
	    break;
	default:
	    abort();
	}
	IOMGR_Sleep(1);
    }
}




