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

#include <xfs_locl.h>


/*
 * Create/open entry
 */


NTSTATUS 
xfs_create (PDEVICE_OBJECT device, PIRP irp)
{
    IO_STACK_LOCATION 	*io_stack;
    FILE_OBJECT		*file, *related_file;
    ANSI_STRING		fname_a;
    CHAR		fname_unix[1024];
    NTSTATUS		status;
    struct xfs_node	*node;
    XFS_CCB		*ccb;
    struct xfs_channel  *chan = &XFSGlobalData;
    
    xfs_debug (XDEBVNOPS, "xfs_create\n");

    related_file = NULL;

    io_stack = IoGetCurrentIrpStackLocation(irp);
    ASSERT(io_stack);
    
    file = io_stack->FileObject;
    ASSERT (file);

    if (file->RelatedFileObject)
	xfs_debug (XDEBVNOPS, "xfs_create: have a related object\n");

    status = RtlUnicodeStringToAnsiString(&fname_a, &file->FileName, TRUE);
    if (status != STATUS_SUCCESS)
	try_return(status);
    
    if(fname_a.Length > sizeof (fname_unix)-1)
	try_return(status = STATUS_INVALID_PARAMETER);

    memcpy(&fname_unix, fname_a.Buffer, fname_a.Length);
    fname_unix[fname_a.Length] = '\0';
    RtlFreeAnsiString(&fname_a);
    
    xfs_debug (XDEBVNOPS, "xfs_create: filename = %s\n", fname_unix);

    status = xfs_get_root (chan);
    if (NT_SUCCESS(status)) {
	irp->IoStatus.Status = status;
	IoCompleteRequest(irp, IO_NO_INCREMENT);
	return status;
    }	

    node = xfs_dnlc_lookup (chan->root, fname_unix);
    if (node == NULL) {
	irp->IoStatus.Status = STATUS_NO_SUCH_FILE;
	irp->IoStatus.Information = FILE_DOES_NOT_EXIST;
	IoCompleteRequest(irp, IO_NO_INCREMENT);
	return status;
    }

    ccb = xfs_get_ccb();

    node->ccb = ccb;
    ccb->node = node;

    file->FsContext = node;
    file->FsContext2 = ccb;
    
    node->ReferenceCount = 1;
    node->OpenHandleCount = 1;

 try_exit:

    irp->IoStatus.Status = status;
    irp->IoStatus.Information = FILE_OPENED;
    IoCompleteRequest(irp, IO_NO_INCREMENT);

    return status;
}


/*
 * This is really a cleanup function that is called
 * when the file isn't cached anymore.
 */

NTSTATUS 
xfs_close (PDEVICE_OBJECT device, PIRP irp)
{
    IO_STACK_LOCATION	*io_stack;
    FILE_OBJECT		*file;
    XFS_CCB		*ccb;
    struct xfs_node	*node;
    
    xfs_debug (XDEBVNOPS, "xfs_close\n");

    io_stack = IoGetCurrentIrpStackLocation(irp);
    ASSERT(io_stack);
    
    file = io_stack->FileObject;
    ASSERT (file);

    ccb = (XFS_CCB *) file->FsContext2;
    node = ccb->node;

    ASSERT (ccb && node);
    
    xfs_release_ccb (ccb);

    --node->ReferenceCount;
    ASSERT (node->ReferenceCount >= 0);
    ASSERT (node->ReferenceCount >= 0);

    irp->IoStatus.Status = STATUS_SUCCESS;
    irp->IoStatus.Information = FILE_OPENED;
    IoCompleteRequest(irp, IO_NO_INCREMENT);

    return STATUS_SUCCESS;
}

/*
 *
 */

NTSTATUS 
xfs_read (PDEVICE_OBJECT device, PIRP irp)
{
    xfs_debug (XDEBVNOPS, "xfs_read\n");

    irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
    irp->IoStatus.Information = 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
     
    return STATUS_NO_MEDIA_IN_DEVICE;
}

/*
 *
 */

NTSTATUS 
xfs_write (PDEVICE_OBJECT device, PIRP irp)
{
    xfs_debug (XDEBVNOPS, "xfs_write\n");

    irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
    irp->IoStatus.Information = 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
     
    return STATUS_NO_MEDIA_IN_DEVICE;
}

/*
 *
 */

NTSTATUS 
xfs_fileinfo (PDEVICE_OBJECT device, PIRP irp)
{
    xfs_debug (XDEBVNOPS, "xfs_fileinfo\n");

    irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
    irp->IoStatus.Information = 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
     
    return STATUS_NO_MEDIA_IN_DEVICE;
}

/*
 *
 */

NTSTATUS 
xfs_flush (PDEVICE_OBJECT device, PIRP irp)
{
    xfs_debug (XDEBVNOPS, "xfs_flush\n");

    irp->IoStatus.Status = STATUS_SUCCESS;
    irp->IoStatus.Information = 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
     
    return STATUS_SUCCESS;
}

long xfs_efilter(EXCEPTION_POINTERS *e)
{
    NTSTATUS	ecode;
    long	ret = EXCEPTION_EXECUTE_HANDLER;

    ecode = e->ExceptionRecord->ExceptionCode;
    
    if ((ecode == STATUS_IN_PAGE_ERROR) && (e->ExceptionRecord->NumberParameters >= 3)) {
	ecode = e->ExceptionRecord->ExceptionInformation[2];
    }

    if (!(FsRtlIsNtstatusExpected(ecode))) {
	ret = EXCEPTION_CONTINUE_SEARCH;
    }

    return ret;
}


/*
 *
 */

NTSTATUS 
xfs_dirctl (PDEVICE_OBJECT device, PIRP irp)
{
    PIO_STACK_LOCATION		io_stack;
    unsigned long		BufferLength = 0;
    unsigned long		StartingIndexForSearch = 0;
    UNICODE_STRING		*PtrSearchPattern = NULL;
    FILE_INFORMATION_CLASS 	FileInformationClass;
    unsigned long		FileIndex = 0;
    ULONG			Flags = 0;
    NTSTATUS			RC = STATUS_SUCCESS;
    unsigned char		*Buffer = NULL;
    FILE_DIRECTORY_INFORMATION	fd_info;
    FILE_FULL_DIR_INFORMATION	ffd_info;
    FILE_BOTH_DIR_INFORMATION	fbd_info;
    FILE_NAMES_INFORMATION	fn_info;
    ANSI_STRING			fname_a;
    UNICODE_STRING		fname_u;
    EXTENDED_IO_STACK_LOCATION	*eio_stack;
    int				i;
    int				bytes_written = 0;
    FILE_OBJECT			*file;
    XFS_CCB			*ccb = NULL;
    struct xfs_node		*node = NULL;
    
    xfs_debug (XDEBVNOPS, "xfs_dirctl\n");

    io_stack = IoGetCurrentIrpStackLocation(irp);
    ASSERT(io_stack);
    eio_stack = (EXTENDED_IO_STACK_LOCATION *) io_stack;

    file = io_stack->FileObject;
    ASSERT (file);

    ccb = (XFS_CCB *) file->FsContext2;
    node = ccb->node;

    try {
	switch (io_stack->MinorFunction) {
	case IRP_MN_QUERY_DIRECTORY:
	    BufferLength = eio_stack->Parameters.QueryDirectory.Length;
	    PtrSearchPattern = eio_stack->Parameters.QueryDirectory.FileName;
	    FileInformationClass = eio_stack->Parameters.QueryDirectory.FileInformationClass;
	    FileIndex = eio_stack->Parameters.QueryDirectory.FileIndex;
	    Flags = eio_stack->Flags;
	    
	    xfs_debug(XDEBVNOPS, "xfs_dirctl: BufferLength: %d\n",
		      BufferLength);
	    xfs_debug(XDEBVNOPS, "xfs_dirctl: File_Info: %d\n",
		      FileInformationClass);
	    xfs_debug(XDEBVNOPS, "xfs_dirctl: FileIndex: %d\n",
		      FileIndex);
	    xfs_debug(XDEBVNOPS, "xfs_dirctl: Flags: %x\n",
		      Flags);
	    
	    for (i=0; i<sizeof(EXTENDED_IO_STACK_LOCATION)/4; i++)
		xfs_debug(XDEBVNOPS, "xfs_dirctl: io_stack: %04x\n",
			  ((unsigned int *)eio_stack)[i]);
	    
	    if (PtrSearchPattern == NULL) {
		xfs_debug(XDEBVNOPS, "xfs_dirctl: no search pattern\n");
		
		if (ccb->SearchPattern == NULL) {
		    
		} else {
 		     // We should ignore the search pattern in the CCB and instead,
 		     // use the user-supplied pattern for this particular query
		     // directory request.
		}
	    }

	    /* Where to start */

	    if (FileIndex) {
		StartingIndexForSearch = FileIndex;
	    } else if ((io_stack->Flags & SL_RESTART_SCAN) == SL_RESTART_SCAN) {
		StartingIndexForSearch = 0;
	    } else {
		StartingIndexForSearch = ccb->ByteOffset;
	    }

	    if (ccb->ByteOffset > 2) {

		xfs_debug(XDEBVNOPS, "xfs_dirctl: ByteOffset > 1\n");

		RC = STATUS_NO_MORE_FILES;
		irp->IoStatus.Status = RC;
		irp->IoStatus.Information = 0;
		IoCompleteRequest(irp, IO_NO_INCREMENT);
		
		return RC;
	    }
	    

	    if (irp->MdlAddress) {
		Buffer = MmGetSystemAddressForMdl(irp->MdlAddress);
		xfs_debug(XDEBVNOPS, "xfs_dirctl: use MdlAddress\n");
	    } else {
		Buffer = irp->UserBuffer;
		xfs_debug(XDEBVNOPS, "xfs_dirctl: use UserBuffer: %x\n",
			  Buffer);
	    }

	    xfs_debug(XDEBVNOPS, "xfs_dirctl: fileinformationclass: %x\n",
		      FileInformationClass);
	    xfs_debug(XDEBVNOPS, "xfs_dirctl: irp->flags: %x\n", irp->Flags);
	    
	    switch (FileInformationClass) {
		
	    case FileDirectoryInformation:
		fd_info.NextEntryOffset = 0;
		fd_info.FileIndex = 4711;
		fd_info.CreationTime.LowPart = 0;
		fd_info.CreationTime.HighPart = 0;
		fd_info.LastAccessTime.LowPart = 0;
		fd_info.LastAccessTime.HighPart = 0;
		fd_info.LastWriteTime.LowPart = 0;
		fd_info.LastWriteTime.HighPart = 0;
		fd_info.ChangeTime.LowPart = 0;
		fd_info.ChangeTime.HighPart = 0;
		fd_info.EndOfFile.LowPart = 0;
		fd_info.EndOfFile.HighPart = 0;
		fd_info.AllocationSize.LowPart = 0;
		fd_info.AllocationSize.HighPart = 0;
		fd_info.FileAttributes = 0;
		
		RtlInitAnsiString(&fname_a, "foo");
		RtlAnsiStringToUnicodeString(&fname_u, &fname_a, TRUE);
		fd_info.FileNameLength = fname_u.Length;
		
		RtlCopyBytes(Buffer, &fd_info, sizeof(fd_info));
		RtlCopyBytes(((unsigned char *) Buffer) +
			     (unsigned long) &(((FILE_DIRECTORY_INFORMATION *) 0)->FileName),
			     fname_u.Buffer, fname_u.Length);
		bytes_written += (unsigned long) &(((FILE_DIRECTORY_INFORMATION *) 0)->FileName);
		bytes_written += fname_u.Length;
		RC = STATUS_SUCCESS;
		break;
	    case FileFullDirectoryInformation:
		ffd_info.NextEntryOffset = 0;
		ffd_info.FileIndex = 4711;
		ffd_info.CreationTime.LowPart = 0;
		ffd_info.CreationTime.HighPart = 0;
		ffd_info.LastAccessTime.LowPart = 0;
		ffd_info.LastAccessTime.HighPart = 0;
		ffd_info.LastWriteTime.LowPart = 0;
		ffd_info.LastWriteTime.HighPart = 0;
		ffd_info.ChangeTime.LowPart = 0;
		ffd_info.ChangeTime.HighPart = 0;
		ffd_info.EndOfFile.LowPart = 0;
		ffd_info.EndOfFile.HighPart = 0;
		ffd_info.AllocationSize.LowPart = 0;
		ffd_info.AllocationSize.HighPart = 0;
		ffd_info.FileAttributes = 0;
		ffd_info.EaSize = 0;
		
		RtlInitAnsiString(&fname_a, "foo");
		RtlAnsiStringToUnicodeString(&fname_u, &fname_a, TRUE);
		ffd_info.FileNameLength = fname_u.Length;
		
		RtlCopyBytes(Buffer, &ffd_info, sizeof(ffd_info));
		RtlCopyBytes(((unsigned char *) Buffer) +
			     (unsigned long) &(((FILE_FULL_DIR_INFORMATION *) 0)->FileName),
			     fname_u.Buffer, fname_u.Length);
		bytes_written += (unsigned long) &(((FILE_FULL_DIR_INFORMATION *) 0)->FileName);
		bytes_written += fname_u.Length;
		RC = STATUS_SUCCESS;
		break;
	    case FileBothDirectoryInformation:
		fbd_info.NextEntryOffset = 0;
		fbd_info.FileIndex = 4711;
		fbd_info.CreationTime.LowPart = 0;
		fbd_info.CreationTime.HighPart = 0;
		fbd_info.LastAccessTime.LowPart = 0;
		fbd_info.LastAccessTime.HighPart = 0;
		fbd_info.LastWriteTime.LowPart = 0;
		fbd_info.LastWriteTime.HighPart = 0;
		fbd_info.ChangeTime.LowPart = 0;
		fbd_info.ChangeTime.HighPart = 0;
		fbd_info.EndOfFile.LowPart = 0;
		fbd_info.EndOfFile.HighPart = 0;
		fbd_info.AllocationSize.LowPart = 0;
		fbd_info.AllocationSize.HighPart = 0;
		fbd_info.FileAttributes = 0;
		fbd_info.EaSize = 0;

		RtlCopyBytes(fbd_info.ShortName, "foo", 3);	
		fbd_info.ShortNameLength = 3;
	
		RtlInitAnsiString(&fname_a, "foo");
		RtlAnsiStringToUnicodeString(&fname_u, &fname_a, TRUE);
		fbd_info.FileNameLength = fname_u.Length;
		
		RtlCopyBytes(Buffer, &fbd_info, sizeof(fbd_info));
		bytes_written += 
		    (unsigned long) &(((FILE_BOTH_DIR_INFORMATION *) 0)->FileName);
		RtlCopyBytes(((unsigned char *) Buffer) +
			     (unsigned long) &(((FILE_BOTH_DIR_INFORMATION *) 0)->FileName),
			     fname_u.Buffer, fname_u.Length);
		bytes_written += fname_u.Length;
		RC = STATUS_SUCCESS;
		break;
	    case FileNamesInformation:
		fn_info.NextEntryOffset = 0;
		fn_info.FileIndex = 4711;
		RtlInitAnsiString(&fname_a, "foo");
		RtlAnsiStringToUnicodeString(&fname_u, &fname_a, TRUE);
		fn_info.FileNameLength = fname_u.Length;
		
		RtlCopyBytes(Buffer, &fn_info, sizeof(fn_info));
		RtlCopyBytes(((unsigned char *) Buffer) +
			     (unsigned long) &(((FILE_NAMES_INFORMATION *) 0)->FileName),
			     fname_u.Buffer, fname_u.Length);
		bytes_written +=
		    (unsigned long) &(((FILE_NAMES_INFORMATION *) 0)->FileName);
		bytes_written +=
		    fname_u.Length;
		RC = STATUS_SUCCESS;
		break;
	    default:
		RC = STATUS_INVALID_DEVICE_REQUEST;
	    }
	    
	    ccb->ByteOffset = ccb->ByteOffset + 1;

	    irp->IoStatus.Status = RC;
	    
	    if (RC == STATUS_SUCCESS)
		irp->IoStatus.Information = bytes_written;
	    else
		irp->IoStatus.Information = 0;
	    
	    IoCompleteRequest(irp, IO_NO_INCREMENT);
	    
	    return RC;
	    break;
	case IRP_MN_NOTIFY_CHANGE_DIRECTORY:
#if 0
	    RC = XFSNotifyChangeDirectory(PtrIrpContext, PtrIrp, 
					  PtrIoStackLocation, PtrFileObject,
					  PtrFCB, PtrCCB);
#endif
	    RC = STATUS_NOT_IMPLEMENTED;
	    irp->IoStatus.Status = RC;
	    irp->IoStatus.Information = 0;
	    IoCompleteRequest(irp, IO_NO_INCREMENT);
	    
	    return RC;
	    break;
	default:
	    RC = STATUS_INVALID_DEVICE_REQUEST;
	    irp->IoStatus.Status = RC;
	    irp->IoStatus.Information = 0;
	    
	    IoCompleteRequest(irp, IO_NO_INCREMENT);
	    return RC;
	    break;
	}
    } except (xfs_efilter(GetExceptionInformation())) {
	RC = STATUS_INVALID_DEVICE_REQUEST;
	irp->IoStatus.Status = RC;
	irp->IoStatus.Information = 0;
	IoCompleteRequest(irp, IO_NO_INCREMENT);
	return RC;
    }
}

/*
 *
 */

NTSTATUS 
xfs_shutdown (PDEVICE_OBJECT device, PIRP irp)
{
    xfs_debug (XDEBVNOPS, "xfs_shutdown\n");

    irp->IoStatus.Status = STATUS_SUCCESS;
    irp->IoStatus.Information = 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
     
    return STATUS_SUCCESS;
}

/*
 * This is really a close function that is called when
 * the user closes the filehandle.
 */

NTSTATUS 
xfs_cleanup (PDEVICE_OBJECT device, PIRP irp)
{
    IO_STACK_LOCATION	*io_stack;
    FILE_OBJECT		*file;
    XFS_CCB		*ccb;
    struct xfs_node	*node;
    
    xfs_debug (XDEBVNOPS, "xfs_cleanup\n");

    io_stack = IoGetCurrentIrpStackLocation(irp);
    ASSERT(io_stack);
    
    file = io_stack->FileObject;
    ASSERT (file);

    ccb = (XFS_CCB *) file->FsContext2;
    node = ccb->node;

    ASSERT (ccb && node);

    node->OpenHandleCount--;
    if (node->OpenHandleCount == 0) {
	ASSERT (node->ReferenceCount == 0);
	xfs_free_node (node);
    }
    irp->IoStatus.Status = STATUS_SUCCESS;
    irp->IoStatus.Information = 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
     
    return STATUS_SUCCESS;
}

/*
 *
 */

NTSTATUS 
xfs_queryvol (PDEVICE_OBJECT device, PIRP irp)
{ 
    xfs_debug (XDEBVNOPS, "xfs_queryvol\n");

    irp->IoStatus.Status = STATUS_NO_MEDIA_IN_DEVICE;
    irp->IoStatus.Information = 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
    
    return STATUS_NO_MEDIA_IN_DEVICE;
}

/*
 *
 */

NTSTATUS 
xfs_fscontrol (PDEVICE_OBJECT device, PIRP irp)
{ 
    IO_STACK_LOCATION	*io_stack;

    xfs_debug (XDEBVNOPS, "xfs_fscontrol\n");

    io_stack = IoGetCurrentIrpStackLocation(irp);
    ASSERT(io_stack);

    switch (io_stack->MinorFunction) {
    case IRP_MN_USER_FS_REQUEST:
    case IRP_MN_MOUNT_VOLUME:
    case IRP_MN_VERIFY_VOLUME:
    case IRP_MN_LOAD_FILE_SYSTEM:
	break;
    }

    irp->IoStatus.Status = STATUS_SUCCESS;
    irp->IoStatus.Information = 0;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
    
    return STATUS_SUCCESS;
}
