/*++

Copyright (c) 1989  Microsoft Corporation

Module Name:

    CreateNp.c

Abstract:

    This module implements the File Create Named Pipe routine for NPFS called
    by the dispatch driver.

Author:

    Gary Kimura     [GaryKi]    04-Sep-1990

Revision History:

--*/

#include "NpProcs.h"

//
//  The debug trace level
//

#define Dbg                              (DEBUG_TRACE_CREATE_NAMED_PIPE)


#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, NpCommonCreateNamedPipe)
#pragma alloc_text(PAGE, NpCreateExistingNamedPipe)
#pragma alloc_text(PAGE, NpCreateNewNamedPipe)
#pragma alloc_text(PAGE, NpFsdCreateNamedPipe)
#endif


NTSTATUS
NpFsdCreateNamedPipe (
    IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This routine implements the FSD part of the NtCreateNamedPipeFile
    API call.

Arguments:

    NpfsDeviceObject - Supplies the device object to use.

    Irp - Supplies the Irp being processed

Return Value:

    NTSTATUS - The Fsd status for the Irp

--*/

{
    NTSTATUS Status;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpFsdCreateNamedPipe\n", 0);

    //
    //  Call the common create routine.
    //

    FsRtlEnterFileSystem();

    Status = NpCommonCreateNamedPipe( NpfsDeviceObject, Irp );

    FsRtlExitFileSystem();

    //
    //  And return to our caller
    //

    DebugTrace(-1, Dbg, "NpFsdCreateNamedPipe -> %08lx\n", Status );

    return Status;
}

//
//  Internal support routine
//

NTSTATUS
NpCommonCreateNamedPipe (
    IN PNPFS_DEVICE_OBJECT NpfsDeviceObject,
    IN PIRP Irp
    )

/*++

Routine Description:

    This is the common routine for creating/opening a file.

Arguments:

    Irp - Supplies the Irp to process

Return Value:

    NTSTATUS - the return status for the operation

--*/

{
    NTSTATUS Status;
    PIO_STACK_LOCATION IrpSp;

    PFILE_OBJECT FileObject;
    PFILE_OBJECT RelatedFileObject;
    UNICODE_STRING FileName;
    ACCESS_MASK DesiredAccess;
    ULONG Options;
    USHORT ShareAccess;
    PNAMED_PIPE_CREATE_PARAMETERS Parameters;
    NAMED_PIPE_TYPE NamedPipeType;
    READ_MODE ServerReadMode;
    COMPLETION_MODE ServerCompletionMode;
    ULONG MaximumInstances;
    ULONG InboundQuota;
    ULONG OutboundQuota;
    LARGE_INTEGER DefaultTimeout;
    BOOLEAN TimeoutSpecified;
    PEPROCESS CreatorProcess;
    BOOLEAN CaseInsensitive = TRUE; //**** Make all searches case insensitive
    PFCB Fcb;
    ULONG CreateDisposition;
    UNICODE_STRING RemainingPart;
    LIST_ENTRY DeferredList;

    PAGED_CODE();

    InitializeListHead (&DeferredList);
    //
    //  Reference our input parameters to make things easier
    //

    IrpSp                = IoGetCurrentIrpStackLocation( Irp );
    FileObject           = IrpSp->FileObject;
    RelatedFileObject    = IrpSp->FileObject->RelatedFileObject;
    FileName             = *(PUNICODE_STRING)&IrpSp->FileObject->FileName;
    DesiredAccess        = IrpSp->Parameters.CreatePipe.SecurityContext->DesiredAccess;
    Options              = IrpSp->Parameters.CreatePipe.Options;
    ShareAccess          = IrpSp->Parameters.CreatePipe.ShareAccess;
    Parameters           = IrpSp->Parameters.CreatePipe.Parameters;
    NamedPipeType        = Parameters->NamedPipeType;
    ServerReadMode       = Parameters->ReadMode;
    ServerCompletionMode = Parameters->CompletionMode;
    MaximumInstances     = Parameters->MaximumInstances;
    InboundQuota         = Parameters->InboundQuota;
    OutboundQuota        = Parameters->OutboundQuota;
    DefaultTimeout       = Parameters->DefaultTimeout;
    TimeoutSpecified     = Parameters->TimeoutSpecified;
    CreatorProcess       = IoGetRequestorProcess( Irp );

    DebugTrace(+1, Dbg, "NpCommonCreateNamedPipe\n", 0 );
    DebugTrace( 0, Dbg, "NpfsDeviceObject     = %08lx\n", NpfsDeviceObject );
    DebugTrace( 0, Dbg, "Irp                  = %08lx\n", Irp );
    DebugTrace( 0, Dbg, "FileObject           = %08lx\n", FileObject );
    DebugTrace( 0, Dbg, "RelatedFileObject    = %08lx\n", RelatedFileObject );
    DebugTrace( 0, Dbg, "FileName             = %Z\n",    &FileName );
    DebugTrace( 0, Dbg, "DesiredAccess        = %08lx\n", DesiredAccess );
    DebugTrace( 0, Dbg, "Options              = %08lx\n", Options );
    DebugTrace( 0, Dbg, "ShareAccess          = %08lx\n", ShareAccess );
    DebugTrace( 0, Dbg, "Parameters           = %08lx\n", Parameters );
    DebugTrace( 0, Dbg, "NamedPipeType        = %08lx\n", NamedPipeType );
    DebugTrace( 0, Dbg, "ServerReadMode       = %08lx\n", ServerReadMode );
    DebugTrace( 0, Dbg, "ServerCompletionMode = %08lx\n", ServerCompletionMode );
    DebugTrace( 0, Dbg, "MaximumInstances     = %08lx\n", MaximumInstances );
    DebugTrace( 0, Dbg, "InboundQuota         = %08lx\n", InboundQuota );
    DebugTrace( 0, Dbg, "OutboundQuota        = %08lx\n", OutboundQuota );
    DebugTrace( 0, Dbg, "DefaultTimeout       = %08lx\n", DefaultTimeout );
    DebugTrace( 0, Dbg, "TimeoutSpecified     = %08lx\n", TimeoutSpecified );
    DebugTrace( 0, Dbg, "CreatorProcess       = %08lx\n", CreatorProcess );

    //
    //  Extract the create disposition
    //

    CreateDisposition = (Options >> 24) & 0x000000ff;

    //
    //  Acquire exclusive access to the Vcb.
    //

    NpAcquireExclusiveVcb();
    try {

        //
        //  If there is a related file object then this is a relative open
        //  and it better be the root dcb.  Both the then and the else clause
        //  return an Fcb.
        //

        if (RelatedFileObject != NULL) {

            PDCB Dcb;

            Dcb = RelatedFileObject->FsContext;

            if (NodeType(Dcb) != NPFS_NTC_ROOT_DCB ||
                FileName.Length < 2 || FileName.Buffer[0] == L'\\') {

                DebugTrace(0, Dbg, "Bad file name\n", 0);

                try_return( Status = STATUS_OBJECT_NAME_INVALID );
            }

            Status = NpFindRelativePrefix( Dcb, &FileName, CaseInsensitive, &RemainingPart, &Fcb);
            if (!NT_SUCCESS (Status)) {
                try_return(NOTHING);
            }

        } else {

            //
            //  The only nonrelative name we allow are of the form "\pipe-name"
            //

            if ((FileName.Length <= 2) || (FileName.Buffer[0] != L'\\')) {

                DebugTrace(0, Dbg, "Bad file name\n", 0);

                try_return( Status = STATUS_OBJECT_NAME_INVALID );
            }

            Fcb = NpFindPrefix( &FileName, CaseInsensitive, &RemainingPart );
        }

        //
        //  If the remaining name is empty then we better have an fcb
        //  otherwise we were given a illegal object name.
        //

        if (RemainingPart.Length == 0) {

            if (Fcb->NodeTypeCode == NPFS_NTC_FCB) {

                DebugTrace(0, Dbg, "Create existing named pipe, Fcb = %08lx\n", Fcb );

                Irp->IoStatus = NpCreateExistingNamedPipe( Fcb,
                                                           FileObject,
                                                           DesiredAccess,
                                                           IrpSp->Parameters.CreatePipe.SecurityContext->AccessState,
                                                           (KPROCESSOR_MODE)(FlagOn(IrpSp->Flags, SL_FORCE_ACCESS_CHECK) ?
                                                                             UserMode : Irp->RequestorMode),
                                                           CreateDisposition,
                                                           ShareAccess,
                                                           ServerReadMode,
                                                           ServerCompletionMode,
                                                           InboundQuota,
                                                           OutboundQuota,
                                                           CreatorProcess,
                                                           &DeferredList );
                Status = Irp->IoStatus.Status;

            } else {

                DebugTrace(0, Dbg, "Illegal object name\n", 0);

                Status = STATUS_OBJECT_NAME_INVALID;
            }

        } else {

            //
            //  The remaining name is not empty so we better have the root Dcb
            //

            if (Fcb->NodeTypeCode == NPFS_NTC_ROOT_DCB) {

                DebugTrace(0, Dbg, "Create new named pipe, Fcb = %08lx\n", Fcb );

                Irp->IoStatus = NpCreateNewNamedPipe( Fcb,
                                                      FileObject,
                                                      FileName,
                                                      DesiredAccess,
                                                      IrpSp->Parameters.CreatePipe.SecurityContext->AccessState,
                                                      CreateDisposition,
                                                      ShareAccess,
                                                      NamedPipeType,
                                                      ServerReadMode,
                                                      ServerCompletionMode,
                                                      MaximumInstances,
                                                      InboundQuota,
                                                      OutboundQuota,
                                                      DefaultTimeout,
                                                      TimeoutSpecified,
                                                      CreatorProcess,
                                                      &DeferredList );
                Status = Irp->IoStatus.Status;

            } else {

                DebugTrace(0, Dbg, "Illegal object name\n", 0);

                Status = STATUS_OBJECT_NAME_INVALID;
            }
        }


    try_exit: NOTHING;
    } finally {

        NpReleaseVcb( );

        //
        // complete any deferred IRPs now we have dropped the locks
        //
        NpCompleteDeferredIrps (&DeferredList);

        NpCompleteRequest( Irp, Status );

        DebugTrace(-1, Dbg, "NpCommonCreateNamedPipe -> %08lx\n", Status);
    }

    return Status;
}


//
//  Internal support routine
//

IO_STATUS_BLOCK
NpCreateNewNamedPipe (
    IN PROOT_DCB RootDcb,
    IN PFILE_OBJECT FileObject,
    IN UNICODE_STRING FileName,
    IN ACCESS_MASK DesiredAccess,
    IN PACCESS_STATE AccessState,
    IN ULONG CreateDisposition,
    IN USHORT ShareAccess,
    IN ULONG NamedPipeType,
    IN ULONG ServerReadMode,
    IN ULONG ServerCompletionMode,
    IN ULONG MaximumInstances,
    IN ULONG InboundQuota,
    IN ULONG OutboundQuota,
    IN LARGE_INTEGER DefaultTimeout,
    IN BOOLEAN TimeoutSpecified,
    IN PEPROCESS CreatorProcess,
    IN PLIST_ENTRY DeferredList
    )

/*++

Routine Description:

    This routine performs the operation for creating a new named pipe
    Fcb and its first instance.  This routine does not complete any
    IRP, it preforms its function and then returns an iosb.

Arguments:

    RootDcb - Supplies the root dcb where this is going to be added

    FileObject - Supplies the file object associated with the first
        instance of the named pipe

    FileName - Supplies the name of the named pipe (not qualified i.e.,
        simply "pipe-name" and not "\pipe-name"

    DesiredAccess - Supplies the callers desired access

    AccessState - Supplies the access state from the irp

    CreateDisposition - Supplies the callers create disposition flags

    ShareAccess - Supplies the caller specified share access

    NamedPipeType - Supplies the named type type

    ServerReadMode - Supplies the named pipe read mode

    ServerCompletionMode - Supplies the named pipe completion mode

    MaximumInstances - Supplies the maximum instances for the named pipe

    InboundQuota - Supplies the inbound quota amount

    OutboundQuota - Supplies the outbound quota amount

    DefaultTimeout - Supplies the default time out value

    TimeoutSpecified - Indicates if the time out value was supplied by the
        caller.

    CreatorProcess - Supplies the process creating the named pipe

    DeferredList - List of IRP's to complete after we release the locks

Return Value:

    IO_STATUS_BLOCK - Returns the appropriate status for the operation

--*/

{
    IO_STATUS_BLOCK Iosb={0};

    NAMED_PIPE_CONFIGURATION NamedPipeConfiguration;
    PSECURITY_DESCRIPTOR NewSecurityDescriptor, CachedSecurityDescriptor;

    PFCB Fcb;
    PCCB Ccb;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpCreateNewNamedPipe\n", 0 );

    //
    //  Check the parameters that must be supplied for a new named pipe
    //  (i.e., the create disposition, timeout, and max instances better
    //  be greater than zero)
    //

    if (!TimeoutSpecified || MaximumInstances <= 0) {
        Iosb.Status = STATUS_INVALID_PARAMETER;
        return Iosb;
    }

    //
    //  The default timeout needs to be less than zero otherwise it
    //  is an absolute time out which doesn't make sense.
    //
    if (DefaultTimeout.QuadPart >= 0) {
        Iosb.Status = STATUS_INVALID_PARAMETER;
        return Iosb;
    }

    if (CreateDisposition == FILE_OPEN) {
        Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND;
        return Iosb;
    }

    //
    //  Determine the pipe configuration
    //
    if (ShareAccess == (FILE_SHARE_READ | FILE_SHARE_WRITE)) {
        NamedPipeConfiguration = FILE_PIPE_FULL_DUPLEX;
    } else if (ShareAccess == FILE_SHARE_READ) {
        NamedPipeConfiguration = FILE_PIPE_OUTBOUND;
    } else if (ShareAccess == FILE_SHARE_WRITE) {
        NamedPipeConfiguration = FILE_PIPE_INBOUND;
    } else {
        Iosb.Status = STATUS_INVALID_PARAMETER;
        return Iosb;
    }
    //
    //  Check that if named pipe type is byte stream then the read mode is
    //  not message mode
    //
    if ((NamedPipeType == FILE_PIPE_BYTE_STREAM_TYPE) &&
        (ServerReadMode == FILE_PIPE_MESSAGE_MODE)) {
        Iosb.Status = STATUS_INVALID_PARAMETER;
        return Iosb;
    }
    //
    //  Create a new fcb and ccb for the named pipe
    //

    Iosb.Status = NpCreateFcb( RootDcb,
                               &FileName,
                               MaximumInstances,
                               DefaultTimeout,
                               NamedPipeConfiguration,
                               NamedPipeType,
                               &Fcb );
    if (!NT_SUCCESS (Iosb.Status)) {
        return Iosb;
    }

    Iosb.Status = NpCreateCcb( Fcb,
                               FileObject,
                               FILE_PIPE_LISTENING_STATE,
                               ServerReadMode,
                               ServerCompletionMode,
                               InboundQuota,
                               OutboundQuota,
                               &Ccb );
    if (!NT_SUCCESS (Iosb.Status)) {
        NpDeleteFcb( Fcb, DeferredList );
        return Iosb;
    }

    //
    //  Set the security descriptor in the Fcb
    //

    SeLockSubjectContext( &AccessState->SubjectSecurityContext );

    Iosb.Status = SeAssignSecurity( NULL,
                                    AccessState->SecurityDescriptor,
                                    &NewSecurityDescriptor,
                                    FALSE,
                                    &AccessState->SubjectSecurityContext,
                                    IoGetFileObjectGenericMapping(),
                                    PagedPool );

    SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );

    if (!NT_SUCCESS(Iosb.Status)) {

        DebugTrace(0, Dbg, "Error calling SeAssignSecurity\n", 0 );

        NpDeleteCcb( Ccb, DeferredList );
        NpDeleteFcb( Fcb, DeferredList );
        return Iosb;
    }
    Iosb.Status = ObLogSecurityDescriptor (NewSecurityDescriptor,
                                           &CachedSecurityDescriptor,
                                           1);
    NpFreePool (NewSecurityDescriptor);
    if (!NT_SUCCESS(Iosb.Status)) {

        DebugTrace(0, Dbg, "Error calling ObLogSecurityDescriptor\n", 0 );

        NpDeleteCcb( Ccb, DeferredList );
        NpDeleteFcb( Fcb, DeferredList );
        return Iosb;
    }

    Fcb->SecurityDescriptor = CachedSecurityDescriptor;
    //
    //  Set the file object back pointers and our pointer to the
    //  server file object.
    //

    NpSetFileObject( FileObject, Ccb, Ccb->NonpagedCcb, FILE_PIPE_SERVER_END );
    Ccb->FileObject[ FILE_PIPE_SERVER_END ] = FileObject;

    //
    //  Check to see if we need to notify outstanding Irps for any
    //  changes (i.e., we just added a named pipe).
    //

    NpCheckForNotify( RootDcb, TRUE, DeferredList );

    //
    //  Set our return status
    //

    Iosb.Status = STATUS_SUCCESS;
    Iosb.Information = FILE_CREATED;

    DebugTrace(-1, Dbg, "NpCreateNewNamedPipe -> %08lx\n", Iosb.Status);

    return Iosb;
}


//
//  Internal support routine
//

IO_STATUS_BLOCK
NpCreateExistingNamedPipe (
    IN PFCB Fcb,
    IN PFILE_OBJECT FileObject,
    IN ACCESS_MASK DesiredAccess,
    IN PACCESS_STATE AccessState,
    IN KPROCESSOR_MODE RequestorMode,
    IN ULONG CreateDisposition,
    IN USHORT ShareAccess,
    IN ULONG ServerReadMode,
    IN ULONG ServerCompletionMode,
    IN ULONG InboundQuota,
    IN ULONG OutboundQuota,
    IN PEPROCESS CreatorProcess,
    IN PLIST_ENTRY DeferredList
    )

/*++

Routine Description:

    This routine performs the operation for creating a new instance of
    an existing named pipe.  This routine does not complete any
    IRP, it preforms its function and then returns an iosb.

Arguments:

    Fcb - Supplies the Fcb for the named pipe being created

    FileObject - Supplies the file object associated with this
        instance of the named pipe

    DesiredAccess - Supplies the callers desired access

    CreateDisposition - Supplies the callers create disposition flags

    ShareAccess - Supplies the caller specified share access

    ServerReadMode - Supplies the named pipe read mode

    ServerCompletionMode - Supplies the named pipe completion mode

    InboundQuota - Supplies the inbound quota amount

    OutboundQuota - Supplies the outbound quota amount

    CreatorProcess - Supplies the process creating the named pipe

    DeferredList - List of IRP's to complete after we release the locks

Return Value:

    IO_STATUS_BLOCK - Returns the appropriate status for the operation

--*/

{
    IO_STATUS_BLOCK Iosb;

    BOOLEAN AccessGranted;
    ACCESS_MASK GrantedAccess;
    UNICODE_STRING Name;

    PCCB Ccb;

    NAMED_PIPE_CONFIGURATION NamedPipeConfiguration;

    USHORT OriginalShareAccess;

    PPRIVILEGE_SET  Privileges = NULL;

    PAGED_CODE();

    DebugTrace(+1, Dbg, "NpCreateExistingNamedPipe\n", 0 );


    //
    //  To create a new instance of a named pipe the caller
    //  must have "create pipe instance" access.  Even if the
    //  caller didn't explicitly request this access, the call
    //  to us implicitly requests the bit.  So now jam the bit
    //  into the desired access field
    //

    DesiredAccess |= FILE_CREATE_PIPE_INSTANCE;

    //
    //  First do an access check for the user against the Fcb
    //

    SeLockSubjectContext( &AccessState->SubjectSecurityContext );

    AccessGranted = SeAccessCheck( Fcb->SecurityDescriptor,
                                   &AccessState->SubjectSecurityContext,
                                   TRUE,                      // Tokens are locked
                                   DesiredAccess,
                                   0,
                                   &Privileges,
                                   IoGetFileObjectGenericMapping(),
                                   RequestorMode,
                                   &GrantedAccess,
                                   &Iosb.Status );

    if (Privileges != NULL) {

        (VOID) SeAppendPrivileges(
                     AccessState,
                     Privileges
                     );

        SeFreePrivileges( Privileges );
    }

    //
    //  Transfer over the access masks from what is desired to
    //  what we just granted.  Also patch up the maximum allowed
    //  case because we just did the mapping for it.  Note that if
    //  the user didn't ask for maximum allowed then the following
    //  code is still okay because we'll just zero a zero bit.
    //

    if (AccessGranted) {

        AccessState->PreviouslyGrantedAccess |= GrantedAccess;
        AccessState->RemainingDesiredAccess &= ~(GrantedAccess | MAXIMUM_ALLOWED);
    }

    RtlInitUnicodeString( &Name, L"NamedPipe" );

    SeOpenObjectAuditAlarm( &Name,
                            NULL,
                            &FileObject->FileName,
                            Fcb->SecurityDescriptor,
                            AccessState,
                            FALSE,
                            AccessGranted,
                            RequestorMode,
                            &AccessState->GenerateOnClose );

    SeUnlockSubjectContext( &AccessState->SubjectSecurityContext );

    if (!AccessGranted) {
        DebugTrace(0, Dbg, "Access Denied\n", 0 );
        return Iosb;
    }

    //
    //  Check that we're still under the maximum instances count
    //

    if (Fcb->OpenCount >= Fcb->Specific.Fcb.MaximumInstances) {
        Iosb.Status = STATUS_INSTANCE_NOT_AVAILABLE;
        return Iosb;
    }

    if (CreateDisposition == FILE_CREATE) {
        Iosb.Status = STATUS_ACCESS_DENIED;
        return Iosb;
    }

    //
    //  From the pipe configuration determine the share access specified
    //  on the first instance of this pipe. All subsequent instances must
    //  specify the same share access.
    //

    NamedPipeConfiguration = Fcb->Specific.Fcb.NamedPipeConfiguration;

    if (NamedPipeConfiguration == FILE_PIPE_OUTBOUND) {
        OriginalShareAccess = FILE_SHARE_READ;
    } else if (NamedPipeConfiguration == FILE_PIPE_INBOUND) {
        OriginalShareAccess = FILE_SHARE_WRITE;
    } else {
        OriginalShareAccess = (FILE_SHARE_READ | FILE_SHARE_WRITE);
    }

    if (OriginalShareAccess != ShareAccess) {
        Iosb.Status = STATUS_ACCESS_DENIED;
        return Iosb;
    }

    //
    //  Create a new ccb for the named pipe
    //

    Iosb.Status = NpCreateCcb( Fcb,
                               FileObject,
                               FILE_PIPE_LISTENING_STATE,
                               ServerReadMode,
                               ServerCompletionMode,
                               InboundQuota,
                               OutboundQuota,
                               &Ccb );
    if (!NT_SUCCESS (Iosb.Status)) {
        return Iosb;
    }

    //
    //  Wake up anyone waiting for an instance to go into the listening state
    //

    Iosb.Status = NpCancelWaiter (&NpVcb->WaitQueue,
                                  &Fcb->FullFileName,
                                  STATUS_SUCCESS,
                                  DeferredList);
    if (!NT_SUCCESS (Iosb.Status)) {
        Ccb->Fcb->ServerOpenCount -= 1;
        NpDeleteCcb (Ccb, DeferredList);
        return Iosb;
    }

    //
    //  Set the file object back pointers and our pointer to the
    //  server file object.
    //

    NpSetFileObject( FileObject, Ccb, Ccb->NonpagedCcb, FILE_PIPE_SERVER_END );
    Ccb->FileObject[ FILE_PIPE_SERVER_END ] = FileObject;

    //
    //  Check to see if we need to notify outstanding Irps for
    //  changes (i.e., we just added a new instance of a named pipe).
    //

    NpCheckForNotify( Fcb->ParentDcb, FALSE, DeferredList );

    //
    //  Set our return status
    //

    Iosb.Status = STATUS_SUCCESS;
    Iosb.Information = FILE_CREATED;

    return Iosb;
}