/*
 * Copyright (c) 1995 - 2000 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: xfs_node.c,v 1.1 2000/05/08 05:20:51 lha Exp $ */

#include <xfs_locl.h>

/*
 * XXX smp
 */

void
xfs_vref (struct xfs_node *node)
{
    node->HoldCount++;
}

/*
 * XXX smp
 */

void
xfs_vrele (struct xfs_node *node)
{
    node->HoldCount--;
}

/*
 * Find a node on channel `chan´ that have `handle´. If not found
 * return NULL
 */

static struct xfs_node *
xfs_node_find_lock (struct xfs_channel *chan, struct xfs_handle *handle)
{
    struct xfs_node *head, *node;
    
    head = &chan->nodes;
    node = head->next;
    
    for (; node != head ; node = node->next) {
	if (xfs_handle_eq (&node->handle, handle)) {
	    xfs_vref (node);
	    return node;
	}
    }
    
    return NULL;
}

/*
 * Find a node on channel `chan´ that have `handle´. If not found
 * return NULL
 */

struct xfs_node *
xfs_node_find (struct xfs_channel *chan, struct xfs_handle *handle)
{
    struct  xfs_node *node;
    KIRQL   iqrl;

    KeAcquireSpinLock(&chan->NodeListSpinLock, &iqrl);
    node = xfs_node_find_lock (chan, handle);
    KeReleaseSpinLock(&chan->NodeListSpinLock, iqrl);

    return node;
}

/*
 * Allocate a xfs_node structure for either a zone or non-paged pool.
 * Set approprivate flags. NodeListSpinLock and ZoneAllocationSpinLock
 * might be locked
 */


int
xfs_new_node (struct xfs_channel *chan,
	      struct xfs_msg_node *node,
	      struct xfs_node **npp)
{
    struct xfs_node *result;
    BOOLEAN zonep = TRUE;
    KIRQL   iqrl_node;
    KIRQL   iqrl_zone;

    KeAcquireSpinLock (&chan->NodeListSpinLock, &iqrl_node);

    result = xfs_node_find_lock (chan, &node->handle);
    if (result == NULL) {
	KeAcquireSpinLock (&chan->ZoneAllocationSpinLock, &iqrl_zone);
	
	if (!ExIsFullZone (&chan->NodeZoneHeader)) {
	    result = ExAllocateFromZone (&chan->NodeZoneHeader);
	    KeReleaseSpinLock (&chan->ZoneAllocationSpinLock, iqrl_zone);
	} else {
	    KeReleaseSpinLock(&chan->ZoneAllocationSpinLock, iqrl_zone);
	    
	    result = ExAllocatePool(NonPagedPool, AlignPointer(sizeof(*node)));
	    zonep = FALSE;
	}
	
	if (result == NULL)
	    XFSPanic (STATUS_INSUFFICIENT_RESOURCES, AlignPointer(sizeof(*result)), 0);
	
	RtlZeroMemory(result, AlignPointer(sizeof(*result)));

	/*
	 * Enter on list
	 */
	
	result->next = &chan->nodes;
	result->prev = chan->nodes.prev;
	result->prev->next = result;
	chan->nodes.prev = result;
	
	result->chan = chan;
    }
    KeReleaseSpinLock (&chan->NodeListSpinLock, iqrl_node);

    /*
     * Install attributes
     */

    xfs_attr2vattr(&node->attr, result);
#if 0
    result->vn->v_type = result->attr.va_type;
#endif
    XFS_TOKEN_SET(result, XFS_ATTR_R, XFS_ATTR_MASK);
    bcopy(node->id, result->id, sizeof(result->id));
    bcopy(node->rights, result->rights, sizeof(result->rights));
    DATA_FROM_XNODE(result) = NULL;
    result->handle = node->handle;

    if (!zonep)
	XFS_SETFLAGS(result->flags, XFS_FCB_NOT_FROM_ZONE);

    KeReleaseSpinLock (&chan->NodeListSpinLock, iqrl_node);

    *npp = result;

    return STATUS_SUCCESS;
}

/*
 * Free node
 */

void
xfs_free_node (struct xfs_node *node)
{
    KIRQL iqrl_zone;
    KIRQL iqrl_node;
    struct xfs_channel *chan = node->chan;

    ASSERT(node);

    if (XFS_VALID_DATAHANDLE(node))
	xfs_close_data_handle (node);

    KeAcquireSpinLock (&chan->NodeListSpinLock, &iqrl_node);

    /*
     * Remove from list
     */

    node->next->prev = node->prev;
    node->prev->next = node->next;
    node->next = node->prev = NULL;
    KeReleaseSpinLock (&chan->NodeListSpinLock, iqrl_node);

    if (! XFS_TESTFLAGS(node->flags, XFS_FCB_NOT_FROM_ZONE)) {
	KeAcquireSpinLock(&chan->ZoneAllocationSpinLock, &iqrl_zone);
	
	ExFreeToZone(&chan->NodeZoneHeader, node);

	KeReleaseSpinLock(&chan->ZoneAllocationSpinLock, iqrl_zone);
    } else {
	ExFreePool(node);
    }
    
    return;
}

void
xfs_close_data_handle (struct xfs_node *t)
{
    ASSERT (DATA_FROM_XNODE(t));
    ObDereferenceObject (DATA_FROM_XNODE(t));
    DATA_FROM_XNODE(t) = NULL;
}

/*
 *
 */

int
xfs_fhlookup (struct xfs_fhandle_t *fh, HANDLE *cache_node)
{
    return STATUS_SUCCESS;
}

/*
 *
 */

int
xfs_fhget (const char *path, struct xfs_fhandle_t *fh)
{
    return STATUS_SUCCESS;
}

/*
 *
 */

int
xfs_open_file (const char *fname, HANDLE RelatedFile, 
	       int Disposition, int CreateOptions, 
	       HANDLE *ret_handle, FILE_OBJECT **ret_object)
{
    UNICODE_STRING fname_u;
    OBJECT_ATTRIBUTES objattr;
    IO_STATUS_BLOCK iosb;
    HANDLE handle;
    FILE_OBJECT *object;
    OBJECT_HANDLE_INFORMATION obj_info;
    int status;

    InitializeObjectAttributes(&objattr, &fname_u, OBJ_CASE_INSENSITIVE,
                               RelatedFile, NULL);

    status = IoCreateFile(&handle, MAXIMUM_ALLOWED, &objattr, &iosb,
			  NULL, FILE_ATTRIBUTE_NORMAL,
			  FILE_SHARE_READ|FILE_SHARE_WRITE,
			  Disposition, CreateOptions, 
                          NULL, 0, CreateFileTypeNone,
                          NULL, 0);
    if (NT_SUCCESS(status)) {
	if (ret_object) {
	    status = ObReferenceObjectByHandle(handle, MAXIMUM_ALLOWED,
					       *IoFileObjectType, KernelMode,
					       &object, &obj_info);
	    if (NT_SUCCESS(status))
		*ret_object = object;
	}	
	
	if (ret_handle && NT_SUCCESS(status))
	    *ret_handle = handle;
	else
	    ZwClose (handle);
    }
    return status;
}

/*
 *
 */

void
vattr2xfs_attr (struct xfs_node *node, struct xfs_attr *xa)
{
    bzero(xa, sizeof(*xa));
#if 0
    if (va->va_mode != (mode_t)VNOVAL)
	XA_SET_MODE(xa, va->va_mode);
    if (va->va_nlink != VNOVAL)
	XA_SET_NLINK(xa, va->va_nlink);
    if (va->va_size != VNOVAL)
	XA_SET_SIZE(xa, va->va_size);
    if (va->va_uid != VNOVAL)
	XA_SET_UID(xa, va->va_uid);
    if (va->va_gid != VNOVAL)
	XA_SET_GID(xa, va->va_gid);
    if (va->va_atime.tv_sec != VNOVAL)
	XA_SET_ATIME(xa, va->va_atime.tv_sec);
    if (va->va_mtime.tv_sec != VNOVAL)
	XA_SET_MTIME(xa, va->va_mtime.tv_sec);
    if (va->va_ctime.tv_sec != VNOVAL)
	XA_SET_CTIME(xa, va->va_ctime.tv_sec);
    if (va->va_fileid != VNOVAL)
	XA_SET_FILEID(xa, va->va_fileid);
    switch (va->va_type) {
    case VNON:
	xa->xa_type = XFS_FILE_NON;
	break;
    case VREG:
	xa->xa_type = XFS_FILE_REG;
	break;
    case VDIR:
	xa->xa_type = XFS_FILE_DIR;
	break;
    case VBLK:
	xa->xa_type = XFS_FILE_BLK;
	break;
    case VCHR:
	xa->xa_type = XFS_FILE_CHR;
	break;
    case VLNK:
	xa->xa_type = XFS_FILE_LNK;
	break;
    case VSOCK:
	xa->xa_type = XFS_FILE_SOCK;
	break;
    case VFIFO:
	xa->xa_type = XFS_FILE_FIFO;
	break;
    case VBAD:
	xa->xa_type = XFS_FILE_BAD;
	break;
    default:
	panic("xfs_attr2attr: bad value");
    }
#endif
}

#define SET_TIMEVAL(X, S, N) \
	do { (X)->tv_sec = (S); (X)->tv_nsec = (N); } while(0)

void
xfs_attr2vattr(const struct xfs_attr *xa, struct xfs_node *node)
{
#if 0
    VATTR_NULL(va);
    if (XA_VALID_MODE(xa))
	node->va_mode = xa->xa_mode;
    if (XA_VALID_NLINK(xa))
	node->va_nlink = xa->xa_nlink;
    if (XA_VALID_SIZE(xa))
	node->va_size = xa->xa_size;
    if (XA_VALID_UID(xa))
	node->va_uid = xa->xa_uid;
    if (XA_VALID_GID(xa))
	node->va_gid = xa->xa_gid;
    if (XA_VALID_ATIME(xa)) {
	SET_TIMEVAL(&node->va_atime, xa->xa_atime, 0);
    }
    if (XA_VALID_MTIME(xa)) {
	SET_TIMEVAL(&node->va_mtime, xa->xa_mtime, 0);
    }
    if (XA_VALID_CTIME(xa)) {
	SET_TIMEVAL(&node->va_ctime, xa->xa_ctime, 0);
    }
    if (XA_VALID_FILEID(xa)) {
	node->va_fileid = xa->xa_fileid;
    }
    if (XA_VALID_TYPE(xa)) {
	switch (xa->xa_type) {
	case XFS_FILE_NON:
	    node->va_type = VNON;
	    break;
	case XFS_FILE_REG:
	    node->va_type = VREG;
	    break;
	case XFS_FILE_DIR:
	    node->va_type = VDIR;
	    break;
	case XFS_FILE_BLK:
	    node->va_type = VBLK;
	    break;
	case XFS_FILE_CHR:
	    node->va_type = VCHR;
	    break;
	case XFS_FILE_LNK:
	    node->va_type = VLNK;
	    break;
	case XFS_FILE_SOCK:
	    node->va_type = VSOCK;
	    break;
	case XFS_FILE_FIFO:
	    node->va_type = VFIFO;
	    break;
	case XFS_FILE_BAD:
	    node->va_type = VBAD;
	    break;
	default:
	    panic("xfs_attr2vattr: bad value");
	}
    }
    va->va_flags = 0;
    va->va_blocksize = 8192;
    va->va_bytes = va->va_size;
#endif
}

/*
 *
 */

int
xfs_get_root (struct xfs_channel *chan)
{
    struct xfs_message_getroot msg;
    int error;

    do {
	if (chan->root != NULL)
	    return 0;
	msg.header.opcode = XFS_MSG_GETROOT;
	msg.cred.uid = 0; /* XXX */
	msg.cred.pag = 0; /* XXX */
	error = xfs_message_rpc(chan, &msg.header, sizeof(msg));
	if (error == 0)
	    error = ((struct xfs_message_wakeup *) & msg)->error;
    } while (error == 0);
    return error;
}
