diff options
Diffstat (limited to '')
-rw-r--r-- | private/nw/rdr/create.c | 3398 |
1 files changed, 3398 insertions, 0 deletions
diff --git a/private/nw/rdr/create.c b/private/nw/rdr/create.c new file mode 100644 index 000000000..e50c9ad68 --- /dev/null +++ b/private/nw/rdr/create.c @@ -0,0 +1,3398 @@ +/*++ + +Copyright (c) 1993 Microsoft Corporation + +Module Name: + + Create.c + +Abstract: + + This module implements the File Create routine for the NetWare + redirector called by the dispatch driver. + +Author: + + Colin Watson [ColinW] 19-Dec-1992 + Manny Weiser [MannyW] 15-Feb-1993 + +Revision History: + +--*/ + +#include "Procs.h" + +NTSTATUS +NwCommonCreate ( + IN PIRP_CONTEXT IrpContext + ); + +IO_STATUS_BLOCK +OpenRedirector( + IN PIRP_CONTEXT IrpContext, + ULONG DesiredAccess, + ULONG ShareAccess, + PFILE_OBJECT FileObject + ); + +IO_STATUS_BLOCK +CreateRemoteFile( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING DriveName + ); + +IO_STATUS_BLOCK +ChangeDirectory( + PIRP_CONTEXT IrpContext, + PVCB Vcb, + PICB Icb + ); + +IO_STATUS_BLOCK +CreateDir( + PIRP_CONTEXT IrpContext, + PVCB Vcb, + PICB Icb + ); + +NTSTATUS +FileOrDirectoryExists( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PICB Icb, + PUNICODE_STRING Name, + OUT PBOOLEAN IsAFile + ); + +IO_STATUS_BLOCK +OpenFile( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PICB Icb, + IN BYTE SearchFlags, + IN BYTE ShareFlags + ); + +IO_STATUS_BLOCK +CreateNewFile( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PICB Icb, + IN BYTE SearchFlags, + IN BYTE ShareFlags + ); + +IO_STATUS_BLOCK +CreateOrOverwriteFile( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PICB Icb, + IN BYTE CreateAttributes, + IN BYTE OpenFlags, + IN BOOLEAN CreateOperation + ); + +IO_STATUS_BLOCK +OpenRenameTarget( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDCB Dcb, + IN PICB* Icb + ); + +IO_STATUS_BLOCK +CreatePrintJob( + PIRP_CONTEXT IrpContext, + PVCB Vcb, + PICB Icb, + PUNICODE_STRING DriveName + ); + +VOID +CloseFile( + PIRP_CONTEXT pIrpContext, + PICB pIcb + ); + + +// BUGBUG HACK HACK + +BOOLEAN +MmDisableModifiedWriteOfSection ( + IN PSECTION_OBJECT_POINTERS SectionObjectPointer + ); + +// +// The debug trace level +// + +#define Dbg (DEBUG_TRACE_CREATE) + +#ifdef ALLOC_PRAGMA +#pragma alloc_text( PAGE, NwFsdCreate ) +#pragma alloc_text( PAGE, NwCommonCreate ) +#pragma alloc_text( PAGE, ReadAttachEas ) +#pragma alloc_text( PAGE, OpenRedirector ) +#pragma alloc_text( PAGE, CreateRemoteFile ) +#pragma alloc_text( PAGE, ChangeDirectory ) +#pragma alloc_text( PAGE, CreateDir ) +#pragma alloc_text( PAGE, FileOrDirectoryExists ) +#pragma alloc_text( PAGE, OpenFile ) +#pragma alloc_text( PAGE, CreateNewFile ) +#pragma alloc_text( PAGE, CreateOrOverwriteFile ) +#pragma alloc_text( PAGE, OpenRenameTarget ) +#pragma alloc_text( PAGE, CreatePrintJob ) +#pragma alloc_text( PAGE, CloseFile ) +#endif + + +NTSTATUS +NwFsdCreate ( + IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp + ) + +/*++ + +Routine Description: + + This routine implements the FSD part of the NtCreateFile and NtOpenFile + API calls. + +Arguments: + + DeviceObject - Supplies the device object for the redirector. + + Irp - Supplies the Irp being processed + +Return Value: + + NTSTATUS - The Fsd status for the Irp + +--*/ + +{ + NTSTATUS Status; + PIRP_CONTEXT IrpContext = NULL; + BOOLEAN TopLevel; + + PAGED_CODE(); + + TimerStart(Dbg); + DebugTrace(+1, Dbg, "NwFsdCreate\n", 0); + + // + // Call the common create routine, with block allowed if the operation + // is synchronous. + // + + FsRtlEnterFileSystem(); + TopLevel = NwIsIrpTopLevel( Irp ); + + try { + + IrpContext = AllocateIrpContext( Irp ); + Status = NwCommonCreate( IrpContext ); + + } except( NwExceptionFilter( Irp, GetExceptionInformation() )) { + + if ( IrpContext == NULL ) { + + // + // If we couldn't allocate an irp context, just complete + // irp without any fanfare. + // + + Status = STATUS_INSUFFICIENT_RESOURCES; + Irp->IoStatus.Status = Status; + Irp->IoStatus.Information = 0; + IoCompleteRequest ( Irp, IO_NETWORK_INCREMENT ); + + } else { + + // + // We had some trouble trying to perform the requested + // operation, so we'll abort the I/O request with + // the error Status that we get back from the + // execption code + // + + Status = NwProcessException( IrpContext, GetExceptionCode() ); + } + } + + if ( IrpContext ) { + NwDequeueIrpContext( IrpContext, FALSE ); + NwCompleteRequest( IrpContext, Status ); + } + + if ( TopLevel ) { + NwSetTopLevelIrp( NULL ); + } + FsRtlExitFileSystem(); + + // + // And return to our caller + // + + DebugTrace(-1, Dbg, "NwFsdCreate -> %08lx\n", Status ); + + TimerStop(Dbg,"NwFsdCreate"); + + return Status; + + UNREFERENCED_PARAMETER(DeviceObject); +} + + +NTSTATUS +NwCommonCreate ( + IN PIRP_CONTEXT IrpContext + ) + +/*++ + +Routine Description: + + This is the common routine for creating/opening a file called by + both the fsd and fsp threads. + +Arguments: + + IrpContext - Supplies the context information for the IRP to process + +Return Value: + + NTSTATUS - the return status for the operation + +--*/ + +{ + IO_STATUS_BLOCK Iosb; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + + PFILE_OBJECT FileObject; + ACCESS_MASK DesiredAccess; + USHORT ShareAccess; + ULONG Options; + BOOLEAN CreateTreeConnection; + BOOLEAN DeleteOnClose; + BOOLEAN DeferredLogon; + BOOLEAN DereferenceCodeSection = FALSE; + BOOLEAN OpenedTreeHandle = FALSE; + + UNICODE_STRING CreateFileName; + UNICODE_STRING Drive; + UNICODE_STRING Server; + UNICODE_STRING Volume; + UNICODE_STRING Path; + UNICODE_STRING FileName; + UNICODE_STRING UserName, Password; + ULONG ShareType; + WCHAR DriveLetter; + DWORD dwExtendedCreate = FALSE; + + PSCB Scb = NULL; + PICB Icb; + UNICODE_STRING DefaultServer; + + PAGED_CODE(); + + // + // Get the current IRP stack location + // + + Irp = IrpContext->pOriginalIrp; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + DebugTrace(+1, Dbg, "NwCommonCreate\n", 0 ); + DebugTrace( 0, Dbg, "Irp = %08lx\n", Irp ); + DebugTrace( 0, Dbg, "->Flags = %08lx\n", Irp->Flags ); + DebugTrace( 0, Dbg, "->FileObject = %08lx\n", IrpSp->FileObject ); + DebugTrace( 0, Dbg, " ->RelatedFileObject = %08lx\n", IrpSp->FileObject->RelatedFileObject ); + DebugTrace( 0, Dbg, " ->FileName = \"%wZ\"\n", &IrpSp->FileObject->FileName ); + DebugTrace( 0, Dbg, "->AllocationSize.LowPart = %08lx\n", Irp->Overlay.AllocationSize.LowPart ); + DebugTrace( 0, Dbg, "->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart ); + DebugTrace( 0, Dbg, "->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer ); + DebugTrace( 0, Dbg, "->IrpSp->Flags = %08lx\n", IrpSp->Flags ); + DebugTrace( 0, Dbg, "->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess ); + DebugTrace( 0, Dbg, "->Options = %08lx\n", IrpSp->Parameters.Create.Options ); + DebugTrace( 0, Dbg, "->Disposition = %08lx\n", (IrpSp->Parameters.Create.Options >> 24) & 0x000000ff); + DebugTrace( 0, Dbg, "->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes ); + DebugTrace( 0, Dbg, "->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess ); + DebugTrace( 0, Dbg, "->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength ); + + CreateFileName = IrpSp->FileObject->FileName; + Options = IrpSp->Parameters.Create.Options; + DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess; + ShareAccess = IrpSp->Parameters.Create.ShareAccess; + + CreateTreeConnection = BooleanFlagOn( Options, FILE_CREATE_TREE_CONNECTION ); + DeleteOnClose = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE ); + + DefaultServer.Buffer = NULL; + + // + // Make sure the input large integer is valid + // + + if (Irp->Overlay.AllocationSize.HighPart != 0) { + + DebugTrace(-1, Dbg, "NwCommonCreate -> STATUS_INVALID_PARAMETER\n", 0); + return STATUS_INVALID_PARAMETER; + } + + // + // Fail requests that don't have the proper impersonation level. + // + + if ( IrpSp->Parameters.Create.SecurityContext ) { + + if ( IrpSp->Parameters.Create.SecurityContext->SecurityQos ) { + + if ( IrpSp->Parameters.Create.SecurityContext->SecurityQos->ImpersonationLevel < + SecurityImpersonation ) { + + DebugTrace(-1, Dbg, "NwCommonCreate -> Insufficient impersation level.\n", 0); + return STATUS_ACCESS_DENIED; + } + } + } + + Iosb.Status = STATUS_SUCCESS; + + FileObject = IrpSp->FileObject; + IrpContext->pNpScb = NULL; + IrpContext->Specific.Create.UserUid = + GetUid(&IrpSp->Parameters.Create.SecurityContext->AccessState->SubjectSecurityContext); + + try { + + if ( IrpSp->FileObject->RelatedFileObject != NULL ) { + + // + // If we open a handle then the DereferenceCodeSection flag + // will be set to false. The dereference will eventually + // happen when the file is closed. + // + + NwReferenceUnlockableCodeSection(); + DereferenceCodeSection = TRUE; + + // + // Record the relative file name for this open. + // + + IrpContext->Specific.Create.FullPathName = CreateFileName; + + Iosb = CreateRemoteFile( IrpContext, NULL ); + + // + // If we succeeded, we want to keep the code section + // referenced because we have opened a handle. + // + + if ( NT_SUCCESS( Iosb.Status ) ) { + DereferenceCodeSection = FALSE; + } + + try_return( Iosb.Status ); + } + + Iosb.Status = CrackPath ( + &CreateFileName, + &Drive, + &DriveLetter, + &Server, + &Volume, + &Path, + &FileName, + NULL ); + + if ( !NT_SUCCESS(Iosb.Status)) { + try_return(Iosb.Status); + } + + // + // Remember this good info. + // + + IrpContext->Specific.Create.VolumeName = Volume; + IrpContext->Specific.Create.PathName = Path; + IrpContext->Specific.Create.DriveLetter = DriveLetter; + IrpContext->Specific.Create.FileName = FileName; + IrpContext->Specific.Create.FullPathName = CreateFileName; + + RtlInitUnicodeString( &IrpContext->Specific.Create.UidConnectName, NULL ); + + // + // For now assume default username and password + // + + ShareType = RESOURCETYPE_ANY; + RtlInitUnicodeString( &UserName, NULL ); + RtlInitUnicodeString( &Password, NULL ); + + if ( Server.Length == 0) { + + // + // Opened the redirector itself + // + + Iosb = OpenRedirector( + IrpContext, + DesiredAccess, + ShareAccess, + FileObject ); + + } else if ( Server.Length == Volume.Length - sizeof( WCHAR ) ) { + + if (IpxHandle == 0 ) { + + // + // We're not bound to the transport and the user is not + // opening the redirector to tell us to bind so return failed. + // + + try_return( Iosb.Status = STATUS_REDIRECTOR_NOT_STARTED ); + } + + NwReferenceUnlockableCodeSection(); + DereferenceCodeSection = TRUE; + + // + // If the only requested access is FILE_LIST_DIRECTORY, + // defer the logon. This will allow all CreateScb to + // succeed with when the user or password is invalid, so + // that the user can see volumes, or enumerate servers + // on the server. + // + + if ( (DesiredAccess & ~( FILE_LIST_DIRECTORY | SYNCHRONIZE ) ) == 0 ) { + DeferredLogon = TRUE; + } else { + DeferredLogon = FALSE; + } + + // + // Server = "Server", Volume = "\Server" + // + + if ( Server.Length == sizeof(WCHAR) && Server.Buffer[0] == L'*') { + + // + // Attempt to open \\*, open a handle to the preferred + // server + // + + PLOGON Logon; + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + Logon = FindUser( &IrpContext->Specific.Create.UserUid, FALSE); + ASSERT( Logon != NULL ); + + // + // Capture the name to avoid holding Rcb or referencing + // the logon structure. + // + + Iosb.Status = DuplicateUnicodeStringWithString ( + &DefaultServer, + &Logon->ServerName, + PagedPool); + + NwReleaseRcb( &NwRcb ); + + if (!NT_SUCCESS(Iosb.Status)) { + try_return( Iosb.Status ); + } + + // + // If the user specified a preferred server and we managed + // to capture the name, try and connect to it. + // + + if (DefaultServer.Length != 0) { + + Iosb.Status = CreateScb( + &Scb, + IrpContext, + &DefaultServer, + NULL, + NULL, + NULL, + DeferredLogon, + FALSE ); + + } else { + + // + // Record that we could not get to the server specified + // in the login structure and that we should attempt to + // use the nearest server. + // + + Iosb.Status = STATUS_BAD_NETWORK_PATH; + } + + if ( !NT_SUCCESS(Iosb.Status)) { + + PNONPAGED_SCB NpScb; + + // + // First dequeue the IRP context, in case it was left + // on an SCB queue. + // + + NwDequeueIrpContext( IrpContext, FALSE ); + + // + // Cannot get to the Preferred server so use any + // server we have a connection to. + // + + + NpScb = SelectConnection( NULL ); + + if (NpScb != NULL ) { + + Scb = NpScb->pScb; + + Iosb.Status = CreateScb( + &Scb, + IrpContext, + &NpScb->ServerName, + NULL, + NULL, + NULL, + DeferredLogon, + FALSE ); + + // + // Release the SCB reference we obtained from + // SelectConnection(). + // + + NwDereferenceScb( NpScb ); + } + } + + if ( !NT_SUCCESS(Iosb.Status)) { + + // + // First dequeue the IRP context, in case it was left + // on an SCB queue. + // + + NwDequeueIrpContext( IrpContext, FALSE ); + + // + // Let CreateScb try and find a nearest server to talk + // to. + // + + Iosb.Status = CreateScb( + &Scb, + IrpContext, + NULL, + NULL, + NULL, + NULL, + DeferredLogon, + FALSE ); + } + + if ( !NT_SUCCESS(Iosb.Status)) { + try_return( Iosb.Status ); + } + + } else { + + // + // On handle opens to a server or tree we support the concept + // of an open with supplemental credentials. In this case, we return + // a handle to the server or a dir server using the provided + // credentials regardless of whether or not there are existing + // connections to the resource. This is primarily for admin + // tools like OleDs. + // + + ReadAttachEas( Irp, &UserName, &Password, &ShareType, &dwExtendedCreate ); + + if ( dwExtendedCreate ) { + ASSERT( UserName.Length > 0 ); + IrpContext->Specific.Create.fExCredentialCreate = TRUE; + IrpContext->Specific.Create.puCredentialName = &UserName; + } + + // + // Attempt to open \\server + // + + Iosb.Status = CreateScb( + &Scb, + IrpContext, + &Server, + NULL, + &UserName, + &Password, + DeferredLogon, + DeleteOnClose ); + + if ( ( Iosb.Status == STATUS_REMOTE_NOT_LISTENING ) || + ( Iosb.Status == STATUS_BAD_NETWORK_PATH ) || + ( Iosb.Status == STATUS_UNSUCCESSFUL ) ) { + + // + // If we couldn't find the server or something + // inexplicable occurred, attempt to open \\tree. + // + + Iosb.Status = NdsCreateTreeScb( IrpContext, + &Scb, // dest scb + &Server, // tree we want + &UserName, + &Password, + DeferredLogon, + DeleteOnClose ); + + if ( !NT_SUCCESS( Iosb.Status ) ) { + try_return( Iosb.Status ); + } + + OpenedTreeHandle = TRUE; + + } else if ( !NT_SUCCESS( Iosb.Status ) ) { + + // + // If we failed to get the bindery server for + // some legitimate reason, bail out now. + // + try_return( Iosb.Status ); + } + + // + // We must have a connection at this point. We don't tree + // connect the dir server since it's virtual. + // + + if ( !OpenedTreeHandle && CreateTreeConnection && !DeleteOnClose ) { + TreeConnectScb( Scb ); + } + + } + + // + // Now create the ICB. + // + + ASSERT( Iosb.Status == STATUS_SUCCESS ); + ASSERT( Scb != NULL ); + + Icb = NwCreateIcb( NW_NTC_ICB_SCB, Scb ); + Icb->FileObject = FileObject; + NwSetFileObject( FileObject, NULL, Icb ); + + // + // Indicate that the SCB was opened. + // + + Icb->State = ICB_STATE_OPENED; + + // + // Is this a tree handle? + // + + Icb->IsTreeHandle = OpenedTreeHandle; + + } else { + + NwReferenceUnlockableCodeSection(); + DereferenceCodeSection = TRUE; + + DeferredLogon = FALSE; + + if ( CreateTreeConnection ) { + + // + // We ignore the extended create attribute here because + // we DO NOT support extended credential creates to random + // files and directories! + // + + ReadAttachEas( Irp, &UserName, &Password, &ShareType, NULL ); + + if ( DeleteOnClose ) { + + // + // Opening a directory to delete a volume. Do not + // force logon. + // + + DeferredLogon = TRUE; + } + } + + IrpContext->Specific.Create.ShareType = ShareType; + IrpContext->Specific.Create.NdsCreate = FALSE; + + Iosb.Status = CreateScb( + &Scb, + IrpContext, + &Server, + NULL, + &UserName, + &Password, + DeferredLogon, + DeleteOnClose ); + + if ( Iosb.Status == STATUS_REMOTE_NOT_LISTENING || + Iosb.Status == STATUS_BAD_NETWORK_PATH || + Iosb.Status == STATUS_UNSUCCESSFUL ) { + + // + // If we couldn't find the server or something + // inexplicable occurred, attempt to open \\tree. + // + + IrpContext->Specific.Create.NdsCreate = TRUE; + IrpContext->Specific.Create.NeedNdsData = TRUE; + + Iosb.Status = NdsCreateTreeScb( IrpContext, + &Scb, + &Server, + &UserName, + &Password, + DeferredLogon, + DeleteOnClose ); + } + + // + // If we have success, then there's a volume to connect. + // + + if ( NT_SUCCESS( Iosb.Status ) ) { + + NTSTATUS CreateScbStatus; + + ASSERT( Scb != NULL ); + + // + // Remember the status from create SCB, since it might + // be an interesting warning. + // + + CreateScbStatus = Iosb.Status; + + // + // We catch this exception in case we have to retry the + // create on the NDS path. This sucks, as does the + // exception structure in this code right now, but it's + // legacy and now is not the time to change it. + // + + try { + + Iosb = CreateRemoteFile( + IrpContext, + &Drive ); + + } except ( EXCEPTION_EXECUTE_HANDLER ) { + + Iosb.Status = GetExceptionCode(); + } + + // + // If this is a server whose name is the same as the tree + // that it is a member of, and the create was marked as + // non-nds and it failed, retry an nds create. + // + + if ( ( !NT_SUCCESS( Iosb.Status) ) && + ( !(IrpContext->Specific.Create.NdsCreate) ) && + ( RtlEqualUnicodeString( &(Scb->pNpScb->ServerName), + &(Scb->NdsTreeName), + TRUE ) ) ) { + + IrpContext->Specific.Create.NdsCreate = TRUE; + IrpContext->Specific.Create.NeedNdsData = TRUE; + + Iosb = CreateRemoteFile( + IrpContext, + &Drive ); + + // + // If this fails, it will raise status before setting IOSB + // and we'll return the status from the original create, + // which is the more interesting one. + // + + } + + // + // If we successfully open the remote file, return the + // CreateScb status instead. + // + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = CreateScbStatus; + } + + } + } + + // + // If we succeeded, we want to keep the code section + // referenced because we have opened a handle. + // + + if ( NT_SUCCESS( Iosb.Status ) ) { + DereferenceCodeSection = FALSE; + } + + try_exit: NOTHING; + } finally { + + + // + // Track the Scb in the IrpContext, not in the local Scb + // variable since we may have been routed to another server + // in process. + // + + if ( Scb != NULL ) { + ASSERT( IrpContext->pNpScb ); + NwDereferenceScb( IrpContext->pNpScb ); + } + + if ( DefaultServer.Buffer != NULL ) { + FREE_POOL( DefaultServer.Buffer ); + } + + DebugTrace(-1, Dbg, "NwCommonCreate -> %08lx\n", Iosb.Status); + + if ( DereferenceCodeSection ) { + NwDereferenceUnlockableCodeSection (); + } + + } + + // + // Map a timeout error to server not found, so that MPR will + // try to connect on the next network provider instead of giving up, + // which is wrong wrong wrong. + // + + if ( Iosb.Status == STATUS_REMOTE_NOT_LISTENING ) { + Iosb.Status = STATUS_BAD_NETWORK_PATH; + } + + // + // Map an unbound transport error to server not found, so that MPR + // will try to connect on the next provider. + // + + if ( Iosb.Status == STATUS_NETWORK_UNREACHABLE ) { + Iosb.Status = STATUS_BAD_NETWORK_PATH; + } + + return Iosb.Status; +} + + +NTSTATUS +ReadAttachEas( + IN PIRP Irp, + OUT PUNICODE_STRING UserName, + OUT PUNICODE_STRING Password, + OUT PULONG ShareType, + OUT PDWORD CredentialExtension + ) + +/*++ + +Routine Description: + + This routine processes the EAs provided when the caller attempts + to attach to a remote server. + + Note: This routine does not create additional storage for the names. + It is the callers responsibility to save them if required. + +Arguments: + + Irp - Supplies all the information + + UserName - Returns the value of the User name EA + + Password - Returns the value of the password EA + + ShareType - Returns the value of the share type EA + + CredentialExtension - Returns whether or not this create + should use the provided credentials for an credential + extended connection. This is primarily for OleDs + accessing the ds in multiple security contexts. + +Return Value: + + NTSTATUS - Status of operation + +--*/ +{ + + PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp); + PFILE_FULL_EA_INFORMATION EaBuffer = Irp->AssociatedIrp.SystemBuffer; + + PAGED_CODE(); + + RtlInitUnicodeString( UserName, NULL ); + RtlInitUnicodeString( Password, NULL ); + *ShareType = RESOURCETYPE_ANY; + if ( CredentialExtension ) { + *CredentialExtension = FALSE; + } + + DebugTrace(+1, Dbg, "ReadAttachEas....\n", 0); + + if ( EaBuffer != NULL) { + + while (TRUE) { + ULONG EaNameLength = EaBuffer->EaNameLength; + + if (strcmp(EaBuffer->EaName, EA_NAME_USERNAME) == 0) { + + UserName->Length = EaBuffer->EaValueLength; + UserName->MaximumLength = EaBuffer->EaValueLength; + UserName->Buffer = (PWSTR)(EaBuffer->EaName+EaNameLength+1); + + } else if (strcmp(EaBuffer->EaName, EA_NAME_PASSWORD) == 0) { + + Password->Length = EaBuffer->EaValueLength; + Password->MaximumLength = EaBuffer->EaValueLength; + Password->Buffer = (PWSTR)(EaBuffer->EaName+EaNameLength+1); + + } else if (strcmp(EaBuffer->EaName, EA_NAME_TYPE) == 0) { + + *ShareType = *(ULONG UNALIGNED *)(EaBuffer->EaName+EaNameLength+1); + + } else if (strcmp(EaBuffer->EaName, EA_NAME_CREDENTIAL_EX) == 0) { + + if ( CredentialExtension ) { + *CredentialExtension = TRUE; + DebugTrace(0, Dbg, "ReadAttachEas signals a credential extension.\n", 0 ); + } + + } else { + DebugTrace(0, Dbg, "ReadAttachEas Unknown EA -> %s\n", EaBuffer->EaName); + } + + if (EaBuffer->NextEntryOffset == 0) { + break; + } else { + EaBuffer = (PFILE_FULL_EA_INFORMATION) ((PCHAR) EaBuffer+EaBuffer->NextEntryOffset); + } + } + } + + DebugTrace(-1, Dbg, "ReadAttachEas -> %08lx\n", STATUS_SUCCESS); + + return STATUS_SUCCESS; + +} + + +IO_STATUS_BLOCK +OpenRedirector( + IN PIRP_CONTEXT IrpContext, + ULONG DesiredAccess, + ULONG ShareAccess, + PFILE_OBJECT FileObject + ) + +/*++ + +Routine Description: + + This routines opens a handle to the redirector device. + +Arguments: + + IrpContext - Supplies all the information + + DesiredAccess - The requested access to the redirector. + + ShareAccess - The requested share access to the redirector. + + FileObject - A pointer to the caller file object. + +Return Value: + + IO_STATUS_BLOCK - Status of operation + +--*/ + +{ + IO_STATUS_BLOCK iosb; + + PAGED_CODE(); + + // + // Note that the object manager will only allow an administrator + // to open the redir itself. This is good. + // + + DebugTrace(+1, Dbg, "NwOpenRedirector\n", 0); + + NwAcquireExclusiveRcb( &NwRcb, TRUE ); + + try { + + // + // Set the new share access + // + + if (!NT_SUCCESS(iosb.Status = IoCheckShareAccess( DesiredAccess, + ShareAccess, + FileObject, + &NwRcb.ShareAccess, + TRUE ))) { + + DebugTrace(0, Dbg, "bad share access\n", 0); + + try_return( NOTHING ); + } + + NwSetFileObject( FileObject, NULL, &NwRcb ); + ++NwRcb.OpenCount; + + // + // Set the return status. + // + + iosb.Status = STATUS_SUCCESS; + iosb.Information = FILE_OPENED; + + try_exit: NOTHING; + } finally { + + NwReleaseRcb( &NwRcb ); + DebugTrace(-1, Dbg, "NwOpenRedirector -> Iosb.Status = %08lx\n", iosb.Status); + + } + + // + // Return to the caller. + // + + return iosb; +} + + +IO_STATUS_BLOCK +CreateRemoteFile( + IN PIRP_CONTEXT IrpContext, + IN PUNICODE_STRING DriveName + ) +/*++ + +Routine Description: + + This routines opens a remote file or directory. + +Arguments: + + IrpContext - Supplies all the information + + DriveName - The drive name. One of three forms X:, LPTx, or NULL. + +Return Value: + + IO_STATUS_BLOCK - Status of operation + +--*/ +{ + IO_STATUS_BLOCK Iosb; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + + ULONG DesiredAccess; + ULONG ShareAccess; + PFILE_OBJECT FileObject; + + UNICODE_STRING FileName; + PFILE_OBJECT RelatedFileObject; + ULONG Options; + ULONG FileAttributes; + + BOOLEAN CreateDirectory; + BOOLEAN OpenDirectory; + BOOLEAN DirectoryFile; + BOOLEAN NonDirectoryFile; + BOOLEAN DeleteOnClose; + BOOLEAN OpenTargetDirectory; + ULONG AllocationSize; + + // Unhandled open features. + + // PFILE_FULL_EA_INFORMATION EaBuffer; + // ULONG EaLength; + // BOOLEAN SequentialOnly; + // BOOLEAN NoIntermediateBuffering; + // BOOLEAN IsPagingFile; + // BOOLEAN NoEaKnowledge; + + ULONG CreateDisposition; + + PFCB Fcb = NULL; + PICB Icb = NULL; + PDCB Dcb; + PVCB Vcb = NULL; + PSCB Scb; + + BOOLEAN IsAFile; + BOOLEAN MayBeADirectory = FALSE; + BOOLEAN OwnOpenLock = FALSE; + BOOLEAN SetShareAccess = FALSE; + + BYTE SearchFlags; + BYTE ShareFlags; + + BOOLEAN CreateTreeConnection; + PUNICODE_STRING VolumeName; + + NTSTATUS Status; + UNICODE_STRING NdsConnectName; + WCHAR ConnectBuffer[MAX_NDS_NAME_CHARS]; + BOOLEAN MadeUidNdsName = FALSE; + + PAGED_CODE(); + + Irp = IrpContext->pOriginalIrp; + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess; + ShareAccess = IrpSp->Parameters.Create.ShareAccess; + FileObject = IrpSp->FileObject; + OpenTargetDirectory = BooleanFlagOn( IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY ); + + // + // It is ok to attempt a reconnect if this request fails with a + // connection error. + // + + SetFlag( IrpContext->Flags, IRP_FLAG_RECONNECTABLE ); + + + try { + + // + // Reference our input parameters to make things easier + // + + RelatedFileObject = FileObject->RelatedFileObject; + + // + // We actually want the parsed file name. + // FileName = FileObject->FileName; + // + FileName = IrpContext->Specific.Create.FullPathName; + Options = IrpSp->Parameters.Create.Options; + FileAttributes = IrpSp->Parameters.Create.FileAttributes; + AllocationSize = Irp->Overlay.AllocationSize.LowPart; + + // + // Short circuit an attempt to open a wildcard name. + // + + if ( FsRtlDoesNameContainWildCards( &FileName ) ) { + try_return( Iosb.Status = STATUS_OBJECT_NAME_INVALID ); + } + + // Decipher Option flags and values + // + + DirectoryFile = BooleanFlagOn( Options, FILE_DIRECTORY_FILE ); + NonDirectoryFile = BooleanFlagOn( Options, FILE_NON_DIRECTORY_FILE ); + DeleteOnClose = BooleanFlagOn( Options, FILE_DELETE_ON_CLOSE ); + + // + // Things we currently ignore, because netware servers don't support it. + // + + // SequentialOnly = BooleanFlagOn( Options, FILE_SEQUENTIAL_ONLY ); + // NoIntermediateBuffering = BooleanFlagOn( Options, FILE_NO_INTERMEDIATE_BUFFERING ); + // NoEaKnowledge = BooleanFlagOn( Options, FILE_NO_EA_KNOWLEDGE ); + // EaBuffer = Irp->AssociatedIrp.SystemBuffer; + // EaLength = IrpSp->Parameters.Create.EaLength; + // IsPagingFile = BooleanFlagOn( IrpSp->Flags, SL_OPEN_PAGING_FILE ); + + if ( BooleanFlagOn( Options, FILE_CREATE_TREE_CONNECTION ) ) { + CreateDisposition = FILE_OPEN; + } else { + CreateDisposition = (Options >> 24) & 0x000000ff; + } + + CreateDirectory = (BOOLEAN)(DirectoryFile && + ((CreateDisposition == FILE_CREATE) || + (CreateDisposition == FILE_OPEN_IF))); + + OpenDirectory = (BOOLEAN)(DirectoryFile && + ((CreateDisposition == FILE_OPEN) || + (CreateDisposition == FILE_OPEN_IF))); + + Dcb = NULL; + if ( RelatedFileObject != NULL ) { + + PNONPAGED_DCB NonPagedDcb; + + NonPagedDcb = RelatedFileObject->FsContext; + Dcb = NonPagedDcb->Fcb; + + // + // If there is a related file object then this is a relative open + // and it better be a DCB. + // + + if ( NodeType( Dcb ) != NW_NTC_DCB ) { + + DebugTrace(0, Dbg, "Bad file name\n", 0); + Iosb.Status = STATUS_OBJECT_NAME_INVALID; + try_return( Iosb ); + } + + + // + // Obtain SCB pointers. + // + + IrpContext->pScb = Dcb->Scb; + IrpContext->pNpScb = Dcb->Scb->pNpScb; + } + + // + // We are about ready to send a packet. Append this IRP context + // the SCB workqueue, and wait until it gets to the front. + // + + NwAppendToQueueAndWait( IrpContext ); + ASSERT( IrpContext->pNpScb->Requests.Flink == &IrpContext->NextRequest ); + + // + // Acquire the Global FCB resource to ensure that one thread + // can't access the half created FCB of another thread. + // + + NwAcquireOpenLock( ); + OwnOpenLock = TRUE; + + // + // Find the volume for this file. + // + + CreateTreeConnection = BooleanFlagOn( Options, FILE_CREATE_TREE_CONNECTION ); + + if ( CreateTreeConnection ) { + VolumeName = &IrpContext->Specific.Create.FullPathName; + } else { + VolumeName = &IrpContext->Specific.Create.VolumeName; + } + + if ( Dcb == NULL ) { + +RetryFindVcb: + + Vcb = NwFindVcb( + IrpContext, + VolumeName, + IrpContext->Specific.Create.ShareType, + IrpContext->Specific.Create.DriveLetter, + CreateTreeConnection, + ( BOOLEAN )( CreateTreeConnection && DeleteOnClose ) ); + + if ( Vcb == NULL ) { + + // + // If this create failed because we need nds data, get + // the data from the ds and resubmit the request. + // + + if ( IrpContext->Specific.Create.NdsCreate && + IrpContext->Specific.Create.NeedNdsData ) { + + // + // Release the open resource so we can move around. + // + + NwReleaseOpenLock( ); + OwnOpenLock = FALSE; + + // + // Take the volume name and build the server/share + // connect name. + // + + NdsConnectName.Buffer = ConnectBuffer; + NdsConnectName.MaximumLength = sizeof( ConnectBuffer ); + NdsConnectName.Length = 0; + + // + // Get the ds information. We may jump servers here. + // + + Status = NdsMapObjectToServerShare( IrpContext, + &Scb, + &NdsConnectName, + CreateTreeConnection, + &(IrpContext->Specific.Create.dwNdsOid) ); + + if( !NT_SUCCESS( Status ) ) { + ExRaiseStatus( Status ); + } + + // + // Make sure we are on the scb queue after all the + // possible server jumping. + // + + NwAppendToQueueAndWait( IrpContext ); + + NwAcquireOpenLock( ); + OwnOpenLock = TRUE; + + // + // Prepend the Uid to the server/share name. + // + + MergeStrings( &IrpContext->Specific.Create.UidConnectName, + &Scb->UnicodeUid, + &NdsConnectName, + PagedPool ); + + MadeUidNdsName = TRUE; + + // + // We have the data, so re-do the connect. + // + + IrpContext->Specific.Create.NeedNdsData = FALSE; + goto RetryFindVcb; + + } else { + + // + // If this was an open to delete a tree connect, and we failed + // to find the VCB, simply return the error. + // + + Iosb.Status = STATUS_BAD_NETWORK_PATH; + try_return ( Iosb ); + + } + + } + + } else { + + Vcb = Dcb->Vcb; + NwReferenceVcb( Vcb ); + + } + + ASSERT( Vcb->Scb == IrpContext->pScb ); + + // + // If this is the target name for a rename then we want to find the + // DCB for the parent directory. + // + + if (OpenTargetDirectory) { + + Iosb = OpenRenameTarget(IrpContext, Vcb, Dcb, &Icb ); + if (Icb != NULL) { + Fcb = Icb->SuperType.Fcb; + } + try_return ( Iosb ); + + } + + // + // Find the FCB for this file. If the FCB exists, we get a + // referenced pointer. Otherwise a new FCB is created. + // + + Fcb = NwFindFcb( IrpContext->pScb, Vcb, &FileName, Dcb ); + + // + // Check the share access for this file. The share access + // is updated if access is granted. + // + + if ( Fcb->IcbCount > 0 ) { + + Iosb.Status = IoCheckShareAccess( + DesiredAccess, + ShareAccess, + FileObject, + &Fcb->ShareAccess, + TRUE ); + + if ( !NT_SUCCESS( Iosb.Status ) ) { + try_return( Iosb ); + } + + } else { + + IoSetShareAccess( + DesiredAccess, + ShareAccess, + FileObject, + &Fcb->ShareAccess ); + } + + SetShareAccess = TRUE; + + // + // Now create the ICB. + // + + Icb = NwCreateIcb( NW_NTC_ICB, Fcb ); + Icb->FileObject = FileObject; + NwSetFileObject( FileObject, Fcb->NonPagedFcb, Icb ); + +#ifndef QFE_BUILD + + // + // Supply a resource for the modified page write to grab when + // writing mem mapped files. We do this because it is imposed + // on us by the system, we do not require the resource for any + // real serialization. + // + + Fcb->NonPagedFcb->Header.Flags = 0; + Fcb->NonPagedFcb->Header.Resource = NULL; + +#endif + +#ifdef NWFASTIO + // + // Initialize private cache map so that the i/o system will call + // our fast path. + // + + FileObject->PrivateCacheMap = (PVOID)1; +#endif + + IrpContext->Icb = Icb; + + // + // Allocate an 8 bit PID for this ICB. Use different thread so + // each Wow program gets its own id. This is because if the same id + // has locks using two handles and closes just one of them the locks + // on that handle are not discarded. + // + + Iosb.Status = NwMapPid( (ULONG)PsGetCurrentThread(), &Icb->Pid ); + + if ( !NT_SUCCESS( Iosb.Status ) ) { + try_return( Iosb.Status ); + } + + // + // Try to figure out what it is we're expected to open. + // + + Iosb.Status = STATUS_SUCCESS; + + if ( FlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE ) ) { + + // + // Opening a print queue job. + // + + Iosb = CreatePrintJob( IrpContext, Vcb, Icb, DriveName ); + + } else if ( DirectoryFile || + ( Fcb->State == FCB_STATE_OPENED && + Fcb->NodeTypeCode == NW_NTC_DCB ) ) { + + // + // Opening a directory. + // + + MayBeADirectory = TRUE; + + switch ( CreateDisposition ) { + + case FILE_OPEN: + Iosb = ChangeDirectory( IrpContext, Vcb, Icb ); + break; + + case FILE_CREATE: + Iosb = CreateDir( IrpContext, Vcb, Icb ); + break; + + case FILE_OPEN_IF: + Iosb.Status = FileOrDirectoryExists( IrpContext, + Vcb, + Icb, + &Icb->SuperType.Fcb->RelativeFileName, + &IsAFile ); + + // + // If the opener specified a directory, fail this request + // if the object is a file. + // + + if ( NT_SUCCESS( Iosb.Status ) && IsAFile ) { + Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND; + } else if ( !NT_SUCCESS( Iosb.Status )) { + Iosb = CreateDir( IrpContext, Vcb, Icb ); + } + break; + + case FILE_SUPERSEDE: + case FILE_OVERWRITE: + case FILE_OVERWRITE_IF: + Iosb.Status = STATUS_INVALID_PARAMETER; + break; + + default: + KeBugCheck( RDR_FILE_SYSTEM ); + + } + + } else { + + SearchFlags = NtAttributesToNwAttributes( FileAttributes ); + ShareFlags = NtToNwShareFlags( DesiredAccess, ShareAccess ); + + IsAFile = NonDirectoryFile || + (Fcb->State == FCB_STATE_OPENED && + Fcb->NodeTypeCode == NW_NTC_FCB ); + // + // Assume we are opening a file. If that fails, and it makes + // sense try to open a directory. + // + + switch ( CreateDisposition ) { + + case FILE_OPEN: + + // + // If the disposition is FILE_OPEN try to avoid an unneeded + // open, for some desired access types. + // + + switch ( DesiredAccess & ~SYNCHRONIZE ) { + + case FILE_WRITE_ATTRIBUTES: + case FILE_READ_ATTRIBUTES: + case DELETE: + + Iosb.Status = FileOrDirectoryExists( + IrpContext, + Vcb, + Icb, + &Icb->SuperType.Fcb->RelativeFileName, + &IsAFile ); + + if ( !IsAFile) { + MayBeADirectory = TRUE; + } + + // + // Fail open of read only file for delete access, + // since the netware server won't fail the delete. + // + + if ( NT_SUCCESS( Iosb.Status ) && + CreateDisposition == DELETE && + FlagOn( Icb->NpFcb->Attributes, NW_ATTRIBUTE_READ_ONLY ) ) { + + Iosb.Status = STATUS_ACCESS_DENIED; + } + + if ( ( Iosb.Status == STATUS_OBJECT_NAME_NOT_FOUND ) && + ( (DesiredAccess & ~SYNCHRONIZE) == DELETE ) ) { + // + // we may not have scan rights. fake the return as OK. + // NW allows the delete without scan rights. + // + Iosb.Status = STATUS_SUCCESS; + } + + break; + + default: + + Iosb = OpenFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags ); + + if ( ( Iosb.Status == STATUS_OBJECT_NAME_NOT_FOUND || + Iosb.Status == STATUS_FILE_IS_A_DIRECTORY ) + && !IsAFile) { + + // + // Opener didn't specify file or directory, and open + // file failed. So try open directory. + // + + Iosb = ChangeDirectory( IrpContext, Vcb, Icb ); + MayBeADirectory = TRUE; + + } else if ( (Iosb.Status == STATUS_SHARING_VIOLATION) && + ((ShareFlags == (NW_OPEN_FOR_READ | NW_DENY_WRITE)) || + (ShareFlags == (NW_OPEN_FOR_READ)))) { + + // + // if the file was already open exclusive (eg. GENERIC_EXECUTE) + // then a debugger opening it again for read will fail with + // sharing violation. In this case, we will try open exclusive + // again to see if that passes. + // + + ShareFlags |= NW_OPEN_EXCLUSIVE ; + ShareFlags &= ~(NW_DENY_WRITE | NW_DENY_READ); + Iosb = OpenFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags ); + } + + break; + + } + + break; + + case FILE_CREATE: + Iosb = CreateNewFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags ); + break; + + case FILE_OPEN_IF: + Iosb.Status = FileOrDirectoryExists( IrpContext, + Vcb, + Icb, + &Icb->SuperType.Fcb->RelativeFileName, + &IsAFile ); + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb = OpenFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags ); + } else { + Iosb = CreateNewFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags ); + } + + if ( !NT_SUCCESS( Iosb.Status ) && !IsAFile) { + + // + // Opener didn't specify file or directory, and open + // file and create new file both failed. So try open + // or create directory. + // + + MayBeADirectory = TRUE; + Iosb.Status = FileOrDirectoryExists( + IrpContext, + Vcb, + Icb, + &Icb->SuperType.Fcb->RelativeFileName, + &IsAFile); + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Information = FILE_OPENED; + } else { + Iosb = CreateDir( IrpContext, Vcb, Icb ); + } + } + + break; + + // + // None of the below make sense for directories so if the + // file operation fails, just return the failure status + // to the user. + // + + case FILE_SUPERSEDE: + case FILE_OVERWRITE_IF: + + // + // Actually, if Overwrite is chosen, we are supposed to + // get the attributes for a file and OR them with the + // new attributes. + // + + Iosb = CreateOrOverwriteFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags, FALSE ); + break; + + case FILE_OVERWRITE: + Iosb.Status = FileOrDirectoryExists( + IrpContext, + Vcb, + Icb, + &Icb->SuperType.Fcb->RelativeFileName, + &IsAFile ); + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb = CreateOrOverwriteFile( IrpContext, Vcb, Icb, SearchFlags, ShareFlags, FALSE ); + } + + break; + + default: + KeBugCheck( RDR_FILE_SYSTEM ); + } + + + } + +try_exit: NOTHING; + + } finally { + + if ( Vcb != NULL ) { + NwDereferenceVcb( Vcb, IrpContext, FALSE ); + } + + if ( MadeUidNdsName ) { + FREE_POOL( IrpContext->Specific.Create.UidConnectName.Buffer ); + } + + if ( AbnormalTermination() || !NT_SUCCESS( Iosb.Status ) ) { + + // + // Remove the share access if necessary + // + + if ( SetShareAccess ) { + IoRemoveShareAccess( FileObject, &Fcb->ShareAccess ); + } + + // + // Failed to create + // + + if ( Icb != NULL ) { + + if ( Icb->Pid != 0 ) { + NwUnmapPid( Icb->Pid, NULL ); + } + + NwDeleteIcb( NULL, Icb ); + } + + // + // If this was a tree connect, derefence the extra + // reference on the VCB. + // + + if ( CreateTreeConnection && !DeleteOnClose ) { + if ( Vcb != NULL ) { + NwDereferenceVcb( Vcb, IrpContext, FALSE ); + } + } + + NwDequeueIrpContext( IrpContext, FALSE ); + + } else { + + Icb->State = ICB_STATE_OPENED; + if ( Fcb->State == FCB_STATE_OPEN_PENDING ) { + Fcb->State = FCB_STATE_OPENED; + } + + if ( DeleteOnClose && !CreateTreeConnection ) { + SetFlag( Fcb->Flags, FCB_FLAGS_DELETE_ON_CLOSE ); + } + + FileObject->SectionObjectPointer = &Fcb->NonPagedFcb->SegmentObject; + + if ( MayBeADirectory ) { + + // + // We successfully opened the file as a directory. + // If the DCB is newly created, it will be marked + // type FCB, update it. + // + + Fcb->NodeTypeCode = NW_NTC_DCB; + } + + NwDequeueIrpContext( IrpContext, FALSE ); + + } + + if ( OwnOpenLock ) { + NwReleaseOpenLock( ); + } + + } + + return( Iosb ); +} + + +IO_STATUS_BLOCK +ChangeDirectory( + PIRP_CONTEXT IrpContext, + PVCB Vcb, + PICB Icb + ) +/*++ + +Routine Description: + + This routines sets the directory for a remote drive. + +Arguments: + + IrpContext - Supplies all the information + + Vcb - A pointer to the VCB for the remote drive. + + Icb - A pointer to the file we are opening. + +Return Value: + + IO_STATUS_BLOCK - Status of operation + +--*/ +{ + IO_STATUS_BLOCK Iosb; + PFCB Fcb; + BYTE Attributes; + BOOLEAN FirstTime = TRUE; + + PAGED_CODE(); + + // + // No need to send a packet if we are opening the root of the volume. + // + + if ( Icb->SuperType.Fcb->RelativeFileName.Length == 0 ) { + + Iosb.Status = STATUS_SUCCESS; + Iosb.Information = FILE_OPENED; + + return( Iosb ); + } + +Retry: + + if ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) { + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "FwbbJ", + NCP_SEARCH_FILE, + -1, + Vcb->Specific.Disk.Handle, + SEARCH_ALL_DIRECTORIES, + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "N==_b", + 14, + &Attributes ); + } + + + } else { + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "LbbWDbDbC", + NCP_LFN_GET_INFO, + Vcb->Specific.Disk.LongNameSpace, + Vcb->Specific.Disk.LongNameSpace, + SEARCH_ALL_DIRECTORIES, + LFN_FLAG_INFO_ATTRIBUTES | + LFN_FLAG_INFO_MODIFY_TIME, + Vcb->Specific.Disk.VolumeNumber, + Vcb->Specific.Disk.Handle, + 0, + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "N_b", + 4, + &Attributes ); + } + + // + // Unfortunately, this succeeds even if the file in question + // is not a directory. + // + + if ( NT_SUCCESS( Iosb.Status ) && + ( !FlagOn( Attributes, NW_ATTRIBUTE_DIRECTORY ) ) ) { + + Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND; + } + } + + if ((Iosb.Status == STATUS_INVALID_HANDLE) && + (FirstTime)) { + + // + // Check to see if Volume handle is invalid. Caused when volume + // is unmounted and then remounted. + // + + FirstTime = FALSE; + + NwReopenVcbHandle( IrpContext, Vcb ); + + goto Retry; + } + + Fcb = Icb->SuperType.Fcb; + + Fcb->NonPagedFcb->Attributes = (UCHAR)Attributes; + SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ); + + // + // Set information field assuming success. It will be ignored + // if the NCP failed. + // + + Iosb.Information = FILE_OPENED; + + if ( Iosb.Status == STATUS_UNSUCCESSFUL ) { + Iosb.Status = STATUS_OBJECT_PATH_NOT_FOUND; + } + + return( Iosb ); +} + + +IO_STATUS_BLOCK +CreateDir( + PIRP_CONTEXT IrpContext, + PVCB Vcb, + PICB Icb + ) +/*++ + +Routine Description: + + This routines create a new directory. + +Arguments: + + IrpContext - Supplies all the information + + Vcb - A pointer to the VCB for the remote drive. + +Return Value: + + IO_STATUS_BLOCK - Status of operation + +--*/ +{ + IO_STATUS_BLOCK Iosb; + + PAGED_CODE(); + + if ( Icb->SuperType.Fcb->RelativeFileName.Length == 0 ) { + Iosb.Status = STATUS_ACCESS_DENIED; + return( Iosb ); + } + + if ( !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) { + + if (!IsFatNameValid(&Icb->SuperType.Fcb->RelativeFileName)) { + + Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD; + + return( Iosb ); + + } + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "SbbJ", + NCP_DIR_FUNCTION, NCP_CREATE_DIRECTORY, + Vcb->Specific.Disk.Handle, + 0xFF, + &Icb->SuperType.Fcb->RelativeFileName ); + + } else { + + Iosb.Status = ExchangeWithWait( + IrpContext, + SynchronousResponseCallback, + "LbbWDDWbDbC", + NCP_LFN_OPEN_CREATE, + Vcb->Specific.Disk.LongNameSpace, + LFN_FLAG_OM_CREATE, + 0, // Search Flags, + 0, // Return Info Mask + NW_ATTRIBUTE_DIRECTORY, + 0x00ff, // Desired access + Vcb->Specific.Disk.VolumeNumber, + Vcb->Specific.Disk.Handle, + 0, // Short directory flag + &Icb->SuperType.Fcb->RelativeFileName ); + + } + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "N" ); + } + + // + // Set information field assuming success. It will be ignored + // if the NCP failed. + // + + Iosb.Information = FILE_CREATED; + + if ( Iosb.Status == STATUS_UNSUCCESSFUL ) { + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + } + + return( Iosb ); +} + + +NTSTATUS +FileOrDirectoryExists( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PICB Icb OPTIONAL, + PUNICODE_STRING Name, + OUT PBOOLEAN IsAFile + ) +/*++ + +Routine Description: + + This routines looks to see if a file or directory exists. + +Arguments: + + IrpContext - Supplies allx the information + + Vcb - A pointer to the VCB for the remote drive. + + Icb - A pointer to the ICB for the file we are looking for. + + Name - Fully qualified name. + + IsAFile - Returns TRUE is the found file is a file, FALSE if it is + a directory. Return nothing if the function returns FALSE. + +Return Value: + + IO_STATUS_BLOCK - Status of operation + +--*/ +{ + ULONG Attributes; + ULONG FileSize; + USHORT LastModifiedDate; + USHORT LastModifiedTime; + USHORT CreationDate; + USHORT CreationTime = DEFAULT_TIME; + USHORT LastAccessDate; + NTSTATUS Status; + PFCB Fcb; + BOOLEAN FirstTime = TRUE; + + PAGED_CODE(); + + // + // No need to send a packet if we are searching for the root of the volume. + // + + if ( Name->Length == 0 ) { + *IsAFile = FALSE; + + return( STATUS_SUCCESS ); + } + + // + // Decide how to handle this request. If we have an ICB, use the FCB + // to determine the file name type, otherwise we have to make the + // decision here. + // + + if ( Icb != NULL && + !BooleanFlagOn( Icb->SuperType.Fcb->Flags, FCB_FLAGS_LONG_NAME ) || + + Vcb->Specific.Disk.LongNameSpace == LFN_NO_OS2_NAME_SPACE || + + IsFatNameValid( Name ) ) { +Retry: + // + // First try a file + // + + IrpContext->ResponseLength = 0; + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "FwbbJ", + NCP_SEARCH_FILE, + -1, + Vcb->Specific.Disk.Handle, + SEARCH_ALL_FILES, + Name ); + + if ( NT_SUCCESS( Status ) ) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "N==_b-dwwww", + 14, + &Attributes, + &FileSize, + &CreationDate, + &LastAccessDate, + &LastModifiedDate, + &LastModifiedTime ); + } + + if ((Status == STATUS_INVALID_HANDLE) && + (FirstTime)) { + + // + // Check to see if Volume handle is invalid. Caused when volume + // is unmounted and then remounted. + // + + FirstTime = FALSE; + + NwReopenVcbHandle( IrpContext, Vcb ); + + goto Retry; + } + + if ( Status == STATUS_UNSUCCESSFUL ) { + + // + // Not a file, Is it a directory? + // + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "FwbbJ", + NCP_SEARCH_FILE, + -1, + Vcb->Specific.Disk.Handle, + SEARCH_ALL_DIRECTORIES, + Name ); + + if ( NT_SUCCESS( Status ) ) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "N==_b", + 14, + &Attributes ); + } + + // + // If the exchange or ParseResponse fails then exit with not found + // + + if ( !NT_SUCCESS( Status ) ) { + return( STATUS_OBJECT_NAME_NOT_FOUND ); + } + + *IsAFile = FALSE; + ASSERT( (Attributes & NW_ATTRIBUTE_DIRECTORY) != 0 ); + + } else { + + if ( Status == STATUS_UNEXPECTED_NETWORK_ERROR && + IrpContext->ResponseLength >= sizeof( NCP_RESPONSE ) ) { + + // + // Work-around for netware bug. If netware returns short + // packet, just return success. We exit prematurely + // because we have no attributes to record. + // + + Icb = NULL; + *IsAFile = TRUE; + return ( STATUS_SUCCESS ); + } + + if ( !NT_SUCCESS( Status ) ) { + return( Status ); + } + + *IsAFile = TRUE; + ASSERT( ( Attributes & NW_ATTRIBUTE_DIRECTORY ) == 0 ); + + } + + } else { + + Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "LbbWDbDbC", + NCP_LFN_GET_INFO, + Vcb->Specific.Disk.LongNameSpace, + Vcb->Specific.Disk.LongNameSpace, + SEARCH_ALL_DIRECTORIES, + LFN_FLAG_INFO_ATTRIBUTES | + LFN_FLAG_INFO_FILE_SIZE | + LFN_FLAG_INFO_MODIFY_TIME | + LFN_FLAG_INFO_CREATION_TIME, + Vcb->Specific.Disk.VolumeNumber, + Vcb->Specific.Disk.Handle, + 0, + Name ); + + if ( NT_SUCCESS( Status ) ) { + Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "N_e=e_xx_xx_x", + 4, + &Attributes, + &FileSize, + 6, + &CreationTime, + &CreationDate, + 4, + &LastModifiedTime, + &LastModifiedDate, + 4, + &LastAccessDate ); + } + + // + // If the exchange or ParseResponse fails then exit with not found + // + + if ( !NT_SUCCESS( Status ) ) { + return( STATUS_OBJECT_NAME_NOT_FOUND ); + } + + if ( Attributes & NW_ATTRIBUTE_DIRECTORY) { + *IsAFile = FALSE; + } else { + *IsAFile = TRUE; + } + } + + // + // If the caller supplied an ICB, update the FCB attributes. + // We'll use this info if the caller does a query attributes + // on the ICB. + // + + if ( Icb != NULL && *IsAFile ) { + + Fcb = Icb->SuperType.Fcb; + ASSERT( Fcb->NodeTypeCode == NW_NTC_FCB ); + + Fcb->NonPagedFcb->Attributes = (UCHAR)Attributes; + Fcb->NonPagedFcb->Header.FileSize.QuadPart = FileSize; + Fcb->LastModifiedDate = LastModifiedDate; + Fcb->LastModifiedTime = LastModifiedTime; + Fcb->CreationTime = CreationTime; + Fcb->CreationDate = CreationDate; + Fcb->LastAccessDate = LastAccessDate; + + DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes ); + DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart ); + DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate ); + DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime ); + DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime ); + DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate ); + DebugTrace( 0, Dbg, "LastAccDate -> %08lx\n", Fcb->LastAccessDate ); + + SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ); + } + + return( STATUS_SUCCESS ); +} + + +IO_STATUS_BLOCK +OpenFile( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PICB Icb, + IN BYTE Attributes, + IN BYTE OpenFlags + ) +/*++ + +Routine Description: + + This routines sets opens a file on a netware server. It fails if + the file does not exist. + +Arguments: + + IrpContext - Supplies all the information + + Vcb - A pointer to the VCB for the remote drive. + + Icb - A pointer to the ICB we are opening. + + Attributes - Open attributes. + + OpenFlags - Open mode and sharing mode flags. + +Return Value: + + IO_STATUS_BLOCK - Status of operation + +--*/ +{ + IO_STATUS_BLOCK Iosb; + PFCB Fcb; + + PAGED_CODE(); + + // + // No need to send a packet if we are trying to open the root of + // the volume as a file. + // + + if ( Icb->SuperType.Fcb->RelativeFileName.Length == 0 ) { + Iosb.Status = STATUS_FILE_IS_A_DIRECTORY; + return( Iosb ); + } + + Fcb = Icb->SuperType.Fcb; + ASSERT( NodeType( Fcb ) == NW_NTC_FCB ); + + // + // Send the open request and wait for the response. + // + + if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) { + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "FbbbJ", + NCP_OPEN_FILE, + Vcb->Specific.Disk.Handle, + SEARCH_ALL_FILES, + OpenFlags, + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( ( ReadExecOnlyFiles ) && + ( !NT_SUCCESS( Iosb.Status ) ) ) { + + // + // Retry the open with the appropriate flags for + // execute only files. + // + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "FbbbJ", + NCP_OPEN_FILE, + Vcb->Specific.Disk.Handle, + SEARCH_EXEC_ONLY_FILES, + OpenFlags, + &Icb->SuperType.Fcb->RelativeFileName ); + } + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nr=_b-dwwww", + Icb->Handle, + sizeof( Icb->Handle ), + 14, + &Fcb->NonPagedFcb->Attributes, + &Fcb->NonPagedFcb->Header.FileSize, + &Fcb->CreationDate, + &Fcb->LastAccessDate, + &Fcb->LastModifiedDate, + &Fcb->LastModifiedTime ); + + Fcb->CreationTime = DEFAULT_TIME; + + } + + } else { + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "LbbWDDWbDbC", + NCP_LFN_OPEN_CREATE, + Vcb->Specific.Disk.LongNameSpace, + LFN_FLAG_OM_OPEN, + NW_ATTRIBUTE_HIDDEN | NW_ATTRIBUTE_SYSTEM, // Search Flags, + LFN_FLAG_INFO_ATTRIBUTES | + LFN_FLAG_INFO_FILE_SIZE | + LFN_FLAG_INFO_MODIFY_TIME | + LFN_FLAG_INFO_CREATION_TIME, + 0, // Create attributes + OpenFlags, // Desired access + Vcb->Specific.Disk.VolumeNumber, + Vcb->Specific.Disk.Handle, + 0, // Short directory flag + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( ( ReadExecOnlyFiles ) && + ( !NT_SUCCESS( Iosb.Status ) ) ) { + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "LbbWDDWbDbC", + NCP_LFN_OPEN_CREATE, + Vcb->Specific.Disk.LongNameSpace, + LFN_FLAG_OM_OPEN, + NW_ATTRIBUTE_EXEC_ONLY, + LFN_FLAG_INFO_ATTRIBUTES | + LFN_FLAG_INFO_FILE_SIZE | + LFN_FLAG_INFO_MODIFY_TIME | + LFN_FLAG_INFO_CREATION_TIME, + 0, // Create attributes + OpenFlags, // Desired access + Vcb->Specific.Disk.VolumeNumber, + Vcb->Specific.Disk.Handle, + 0, // Short directory flag + &Icb->SuperType.Fcb->RelativeFileName ); + } + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Ne_e=e_xx_xx_x", + &Icb->Handle[2], + 6, + &Fcb->NonPagedFcb->Attributes, + &Fcb->NonPagedFcb->Header.FileSize, + 6, + &Fcb->CreationTime, + &Fcb->CreationDate, + 4, + &Fcb->LastModifiedTime, + &Fcb->LastModifiedDate, + 4, + &Fcb->LastAccessDate ); + } + } + + if ( NT_SUCCESS( Iosb.Status ) ) { + + // + // NT does not allow you to open a read only file for write access. + // Netware does. To fake NT semantics, check to see if we should + // fail the open that the netware server just succeeded. + // + + if ( ( Fcb->NonPagedFcb->Attributes & NW_ATTRIBUTE_READ_ONLY ) && + ( OpenFlags & NW_OPEN_FOR_WRITE ) ) { + + CloseFile( IrpContext, Icb ); + Iosb.Status = STATUS_ACCESS_DENIED; + } + + SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ); + Icb->HasRemoteHandle = TRUE; + + + DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes ); + DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart ); + DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate ); + DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime ); + DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate ); + DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime ); + DebugTrace( 0, Dbg, "LastAccDate -> %08lx\n", Fcb->LastAccessDate ); + + } + + // + // Set information field assuming success. It will be ignored + // if the NCP failed. + // + + Iosb.Information = FILE_OPENED; + + if ( Iosb.Status == STATUS_UNSUCCESSFUL ) { + Iosb.Status = STATUS_OBJECT_NAME_NOT_FOUND; + } + + return( Iosb ); +} + + +IO_STATUS_BLOCK +CreateNewFile( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PICB Icb, + IN BYTE CreateAttributes, + IN BYTE OpenFlags + ) +/*++ + +Routine Description: + + This routines creates a new file on a netware server. It fails + if the file exists. + +Arguments: + + IrpContext - Supplies all the information + + Vcb - A pointer to the VCB for the remote drive. + + Icb - A pointer to the ICB we are opening. + + CreateAttributes - Create attributes. + + OpenFlags - Open mode and sharing mode flags. + +Return Value: + + IO_STATUS_BLOCK - Status of operation + +--*/ +{ + IO_STATUS_BLOCK Iosb; + PFCB Fcb; + UCHAR DelayedAttributes; + BOOLEAN CloseAndReopen; + + PAGED_CODE(); + + // + // If the user opens the file for shared access, then we will need to + // create the file close, then reopen it (since we have no NCP to say + // create with shared access). If the file is being created read-only, + // and the creator requests write access then we pull the additional + // trick of creating the file without the read-only, and set it later, + // so that the second open can succeed. + // + + CloseAndReopen = FALSE; + DelayedAttributes = 0; + + if ( OpenFlags != NW_OPEN_EXCLUSIVE ) { + CloseAndReopen = TRUE; + + if ( ( CreateAttributes & NW_ATTRIBUTE_READ_ONLY ) && + ( OpenFlags & NW_OPEN_FOR_WRITE ) ) { + + DelayedAttributes = CreateAttributes; + CreateAttributes = 0; + } + } + + // + // Send the create request and wait for the response. + // + + Fcb = Icb->SuperType.Fcb; + + if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) { + + if (!IsFatNameValid(&Icb->SuperType.Fcb->RelativeFileName)) { + + Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD; + + return( Iosb ); + + } + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "FbbJ", // NCP Create New File + NCP_CREATE_NEW_FILE, + Vcb->Specific.Disk.Handle, + CreateAttributes, + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nr=_b-dwwww", + Icb->Handle, sizeof( Icb->Handle ), + 14, + &Fcb->NonPagedFcb->Attributes, + &Fcb->NonPagedFcb->Header.FileSize, + &Fcb->CreationDate, + &Fcb->LastAccessDate, + &Fcb->LastModifiedDate, + &Fcb->LastModifiedTime ); + + Fcb->CreationTime = DEFAULT_TIME; + + } + + } else { + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "LbbWDDWbDbC", + NCP_LFN_OPEN_CREATE, + Vcb->Specific.Disk.LongNameSpace, + LFN_FLAG_OM_CREATE, + 0, // Search Flags + LFN_FLAG_INFO_ATTRIBUTES | + LFN_FLAG_INFO_FILE_SIZE | + LFN_FLAG_INFO_MODIFY_TIME | + LFN_FLAG_INFO_CREATION_TIME, + CreateAttributes, + 0, // Desired access + Vcb->Specific.Disk.VolumeNumber, + Vcb->Specific.Disk.Handle, + 0, // Short directory flag + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Ne_e=e_xx_xx_x", + &Icb->Handle[2], + 6, + &Fcb->NonPagedFcb->Attributes, + &Fcb->NonPagedFcb->Header.FileSize, + 6, + &Fcb->CreationTime, + &Fcb->CreationDate, + 4, + &Fcb->LastModifiedTime, + &Fcb->LastModifiedDate, + 4, + &Fcb->LastAccessDate ); + } + } + + if ( NT_SUCCESS( Iosb.Status ) ) { + SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ); + Icb->HasRemoteHandle = TRUE; + DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes ); + DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart ); + DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate ); + DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime ); + DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate ); + DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime ); + DebugTrace( 0, Dbg, "LastAcceDate-> %08lx\n", Fcb->LastAccessDate ); + } + + if ( Iosb.Status == STATUS_UNSUCCESSFUL ) { + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + } + + if ( !NT_SUCCESS( Iosb.Status ) ) { + return( Iosb ); + } + + + // + // We've created the file, and the users wants shared access to the + // file. Close the file and reopen in sharing mode. + // + + if ( CloseAndReopen ) { + CloseFile( IrpContext, Icb ); + Iosb = OpenFile( IrpContext, Vcb, Icb, CreateAttributes, OpenFlags ); + } + + // + // If we need to set attributes, do it now. Ignore errors, if any. + // + + if ( DelayedAttributes != 0 ) { + + ExchangeWithWait( + IrpContext, + SynchronousResponseCallback, + "FbbbU", + NCP_SET_FILE_ATTRIBUTES, + DelayedAttributes, + Fcb->Vcb->Specific.Disk.Handle, + SEARCH_ALL_FILES, + &Fcb->RelativeFileName ); + + } + + // + // Set information field assuming success. It will be ignored + // if the NCP failed. + // + + Iosb.Information = FILE_CREATED; + return( Iosb ); +} + + +IO_STATUS_BLOCK +CreateOrOverwriteFile( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PICB Icb, + IN BYTE CreateAttributes, + IN BYTE OpenFlags, + IN BOOLEAN CreateOperation + ) +/*++ + +Routine Description: + + This routines creates a file on a netware server. If the file + exists it is overwritten. + +Arguments: + + IrpContext - Supplies all the information + + Vcb - A pointer to the VCB for the remote drive. + + Icb - A pointer to the ICB we are opening. + + Attributes - Open attributes. + + OpenFlags - Open mode and sharing mode flags. + +Return Value: + + IO_STATUS_BLOCK - Status of operation + +--*/ +{ + IO_STATUS_BLOCK Iosb; + PFCB Fcb; + UCHAR DelayedAttributes; + BOOLEAN CloseAndReopen; + + PAGED_CODE(); + + Fcb = Icb->SuperType.Fcb; + + // + // Send the request and wait for the response. + // + + if ( !BooleanFlagOn( Fcb->Flags, FCB_FLAGS_LONG_NAME ) ) { + + if (!IsFatNameValid(&Icb->SuperType.Fcb->RelativeFileName)) { + + Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD; + + return( Iosb ); + + } + + // + // If the user opens the file for shared access, then we will need to + // create the file close, then reopen it (since we have no NCP to say + // create with shared access). If the file is being created read-only, + // and the creator requests write access then we pull the additional + // trick of creating the file without the read-only, and set it later, + // so that the second open can succeed. + // + + if ( ( CreateAttributes & NW_ATTRIBUTE_READ_ONLY ) && + ( OpenFlags & NW_OPEN_FOR_WRITE ) ) { + + DelayedAttributes = CreateAttributes; + CreateAttributes = 0; + } else { + DelayedAttributes = 0; + } + + // + // Dos namespace create always returns the file exclusive. + // + + if (!FlagOn(OpenFlags, NW_OPEN_EXCLUSIVE)) { + CloseAndReopen = TRUE; + } else { + CloseAndReopen = FALSE; + } + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "FbbJ", + NCP_CREATE_FILE, + Vcb->Specific.Disk.Handle, + CreateAttributes, + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Nr=_b-dwwww", + Icb->Handle, + sizeof( Icb->Handle ), + 14, + &Fcb->NonPagedFcb->Attributes, + &Fcb->NonPagedFcb->Header.FileSize, + &Fcb->CreationDate, + &Fcb->LastAccessDate, + &Fcb->LastModifiedDate, + &Fcb->LastModifiedTime ); + + Fcb->CreationTime = DEFAULT_TIME; + + } + + // + // We've created the file, and the users wants shared access to the + // file. Close the file and reopen in sharing mode. + // + + if (( NT_SUCCESS( Iosb.Status ) ) && + ( CloseAndReopen )) { + + CloseFile( IrpContext, Icb ); + Iosb = OpenFile( IrpContext, Vcb, Icb, CreateAttributes, OpenFlags ); + } + + if ( DelayedAttributes != 0 ) { + ExchangeWithWait( + IrpContext, + SynchronousResponseCallback, + "FbbbU", + NCP_SET_FILE_ATTRIBUTES, + DelayedAttributes, + Fcb->Vcb->Specific.Disk.Handle, + SEARCH_ALL_FILES, + &Fcb->RelativeFileName ); + } + + } else { + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "LbbWDDWbDbC", + NCP_LFN_OPEN_CREATE, + Vcb->Specific.Disk.LongNameSpace, + LFN_FLAG_OM_OVERWRITE, + 0, // Search Flags + LFN_FLAG_INFO_ATTRIBUTES | + LFN_FLAG_INFO_FILE_SIZE | + LFN_FLAG_INFO_MODIFY_TIME | + LFN_FLAG_INFO_CREATION_TIME, + CreateAttributes, + OpenFlags, // DesiredAccess + Vcb->Specific.Disk.VolumeNumber, + Vcb->Specific.Disk.Handle, + 0, // Short directory flag + &Icb->SuperType.Fcb->RelativeFileName ); + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "Ne_e=e_xx_xx_x", + &Icb->Handle[2], + 6, + &Fcb->NonPagedFcb->Attributes, + &Fcb->NonPagedFcb->Header.FileSize, + 6, + &Fcb->CreationTime, + &Fcb->CreationDate, + 4, + &Fcb->LastModifiedTime, + &Fcb->LastModifiedDate, + 4, + &Fcb->LastAccessDate ); + } + } + + if ( NT_SUCCESS( Iosb.Status ) ) { + SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ); + Icb->HasRemoteHandle = TRUE; + DebugTrace( 0, Dbg, "Attributes -> %08lx\n", Fcb->NonPagedFcb->Attributes ); + DebugTrace( 0, Dbg, "FileSize.Low-> %08lx\n", Fcb->NonPagedFcb->Header.FileSize.LowPart ); + DebugTrace( 0, Dbg, "ModifiedDate-> %08lx\n", Fcb->LastModifiedDate ); + DebugTrace( 0, Dbg, "ModifiedTime-> %08lx\n", Fcb->LastModifiedTime ); + DebugTrace( 0, Dbg, "CreationDate-> %08lx\n", Fcb->CreationDate ); + DebugTrace( 0, Dbg, "CreationTime-> %08lx\n", Fcb->CreationTime ); + DebugTrace( 0, Dbg, "LastAccDate -> %08lx\n", Fcb->LastAccessDate ); + } else { + return( Iosb ); + } + + // + // Set information field assuming success. It will be ignored + // if the NCP failed. + // + + if ( CreateOperation) { + Iosb.Information = FILE_CREATED; + } else { + Iosb.Information = FILE_OVERWRITTEN; + } + + return( Iosb ); +} + + +IO_STATUS_BLOCK +OpenRenameTarget( + IN PIRP_CONTEXT IrpContext, + IN PVCB Vcb, + IN PDCB Dcb, + IN PICB* Icb + ) +/*++ + +Routine Description: + + This routine opens a directory. If the filename provided specifies + a directory then the file/directory to be renamed will be put in this + directory. + + If the target foo\bar does not exist or is a file then the source of + the rename must be a file and will end up in the directory foo with + the name bar + +Arguments: + + IrpContext - Supplies all the information + + Vcb - A pointer to the VCB for the remote drive. + + Dcb - A pointer to the DCB for relative opens. If NULL the FileName + is an full path name. If non NUL the FileName is relative to + this directory. + + Icb - A pointer to where the address of the Icb is to be stored. + +Return Value: + + NT_STATUS - Status of operation + +--*/ +{ + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + + IO_STATUS_BLOCK Iosb; + PFCB Fcb; + BOOLEAN FullNameIsAFile; + BOOLEAN FullNameExists; + BOOLEAN PathIsAFile; + +#if 0 + UNICODE_STRING Drive; + WCHAR DriveLetter; + UNICODE_STRING Server; + UNICODE_STRING Volume; + UNICODE_STRING FileName; +#endif + UNICODE_STRING Path; + UNICODE_STRING FullName; + UNICODE_STRING CompleteName; + UNICODE_STRING VcbName; + PWCH pTrailingSlash; + + USHORT i; + USHORT DcbNameLength; + + PAGED_CODE(); + + DebugTrace(+1, Dbg, "OpenRenameTarget\n", 0); + + // + // Get the current IRP stack location + // + + Irp = IrpContext->pOriginalIrp; + + IrpSp = IoGetCurrentIrpStackLocation( Irp ); + + // + // Build a complete filename of the form \g:\server\volume\dir1\file + // + + if ( Dcb != NULL ) { + + // + // Strip to UID portion of the DCB name. + // + + for ( i = 0 ; i < Dcb->FullFileName.Length / sizeof(WCHAR) ; i++ ) { + if ( Dcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR ) { + break; + } + } + + ASSERT( Dcb->FullFileName.Buffer[i] == OBJ_NAME_PATH_SEPARATOR ); + + // + // Now build the full name by appending the file name to the DCB name. + // + + DcbNameLength = Dcb->FullFileName.Length - ( i * sizeof(WCHAR) ); + CompleteName.Length = DcbNameLength + IrpSp->FileObject->FileName.Length + sizeof( WCHAR); + CompleteName.MaximumLength = CompleteName.Length; + CompleteName.Buffer = ALLOCATE_POOL_EX( PagedPool, CompleteName.Length ); + + RtlCopyMemory( + CompleteName.Buffer, + Dcb->FullFileName.Buffer + i, + DcbNameLength ); + + CompleteName.Buffer[ DcbNameLength / sizeof(WCHAR) ] = L'\\'; + + RtlCopyMemory( + CompleteName.Buffer + DcbNameLength / sizeof(WCHAR ) + 1, + IrpSp->FileObject->FileName.Buffer, + IrpSp->FileObject->FileName.Length ); + + Dcb = NULL; + + } else { + + CompleteName = IrpSp->FileObject->FileName; + + } + + // + // Calculate the VCB name, without the UID prefix. + // + + VcbName.Buffer = wcschr( Vcb->Name.Buffer, L'\\' ); + VcbName.Length = Vcb->Name.Length - + ( (PCHAR)VcbName.Buffer - (PCHAR)Vcb->Name.Buffer ); + + // + // Calculate the target relative name. This is simply the complete + // name minus the VcbName and the leading backslash. + // + + FullName.Buffer = CompleteName.Buffer + ( VcbName.Length / sizeof(WCHAR) ) + 1; + FullName.Length = CompleteName.Length - + ( (PCHAR)FullName.Buffer - (PCHAR)CompleteName.Buffer ); + + // + // Calculate the target directory relative name. This the the target + // full name, minus the last component of the name. + // + + pTrailingSlash = FullName.Buffer + FullName.Length / sizeof(WCHAR) - 1; + for ( i = 0; i < FullName.Length ; i += sizeof(WCHAR) ) { + if ( *pTrailingSlash == L'\\' ) { + break; + } + --pTrailingSlash; + } + + + Path.Buffer = FullName.Buffer; + + if ( i == FullName.Length ) { + + // + // If no trailing slash was found, the the target path is the + // root directory. + // + + Path.Length = 0; + + } else { + + Path.Length = (PCHAR)pTrailingSlash - (PCHAR)FullName.Buffer; + + } + +#if 0 + Iosb.Status = CrackPath( + &CompleteName, + &Drive, + &DriveLetter, + &Server, + &Volume, + &Path, + &FileName, + &FullName ); +#endif + + Iosb.Status = FileOrDirectoryExists( IrpContext, + Vcb, + NULL, + &Path, + &PathIsAFile ); + + if ( !NT_SUCCESS( Iosb.Status) ) { + + // The directory containing the file does not exist + + return(Iosb); + } + + Iosb.Status = FileOrDirectoryExists( IrpContext, + Vcb, + NULL, + &FullName, + &FullNameIsAFile ); + + if ( !NT_SUCCESS( Iosb.Status ) ) { + FullNameExists = FALSE; + Iosb.Information = FILE_DOES_NOT_EXIST; + } else { + FullNameExists = TRUE; + Iosb.Information = 0; + } + + DebugTrace( 0, Dbg, "FullNameExists = %08lx\n", FullNameExists); + DebugTrace( 0, Dbg, "FullNameIsAFile = %08lx\n", FullNameIsAFile); + + try { + UNICODE_STRING TargetPath; + + // + // Find the FCB for this file. If the FCB exists, we get a + // referenced pointer. Otherwise a new FCB is created. + // The file is the complete path minus the target filename. + // + + TargetPath = CompleteName; + + Fcb = NwFindFcb( IrpContext->pScb, Vcb, &TargetPath, Dcb ); + + // + // Now create the ICB. + // + + *Icb = NwCreateIcb( NW_NTC_ICB, Fcb ); + + (*Icb)->FileObject = IrpSp->FileObject; + NwSetFileObject( IrpSp->FileObject, Fcb->NonPagedFcb, *Icb ); + (*Icb)->Exists = FullNameExists; + (*Icb)->IsAFile = FullNameIsAFile; + + try_return(Iosb.Status = STATUS_SUCCESS); + +try_exit: NOTHING; + + } finally { + + + if ( AbnormalTermination() || !NT_SUCCESS( Iosb.Status ) ) { + + // + // Failed to create + // + + if ( *Icb != NULL ) { + NwDeleteIcb( NULL, *Icb ); + *Icb = NULL; + } + } + } + + DebugTrace(-1, Dbg, "OpenRenameTarget\n", Iosb.Status); + + return( Iosb ); +} + + +IO_STATUS_BLOCK +CreatePrintJob( + PIRP_CONTEXT IrpContext, + PVCB Vcb, + PICB Icb, + PUNICODE_STRING DriveName + ) +/*++ + +Routine Description: + + This routines create a new directory. + +Arguments: + + IrpContext - Supplies all the information + + Vcb - A pointer to the VCB for the remote print queue. + + Icb - A pointer to the newly created ICB. + + DriveName - LPTx + +Return Value: + + IO_STATUS_BLOCK - Status of operation + +--*/ +{ + IO_STATUS_BLOCK Iosb; + PFCB Fcb; + ANSI_STRING ODriveName; + static CHAR LptName[] = "LPT" ; + + PAGED_CODE(); + + // + // Make sure the print queue name is correct. + // + + if ( Icb->SuperType.Fcb->RelativeFileName.Length != 0 ) { + Iosb.Status = STATUS_OBJECT_PATH_SYNTAX_BAD; + return( Iosb ); + } + + // + // Send a create queue job packet, and wait the response. + // + + if ((DriveName->Length == 0 ) || + (!NT_SUCCESS(RtlUnicodeStringToOemString( &ODriveName, DriveName, TRUE )))) { + // + // if we dont have a name, use the string "LPT". we do this because + // some printers insist on a name. + // + + RtlInitString(&ODriveName, LptName); + } + + Iosb.Status = ExchangeWithWait ( + IrpContext, + SynchronousResponseCallback, + "Sd_ddw_b_r_bbwwww_x-x_", // Format string + NCP_ADMIN_FUNCTION, NCP_CREATE_QUEUE_JOB, + Vcb->Specific.Print.QueueId,// Queue ID + 6, // Skip bytes + 0xffffffff, // Target Server ID number + 0xffffffff, 0xffff, // Target Execution time + 11, // Skip bytes + 0x00, // Job Control Flags + 26, // Skip bytes + ODriveName.Buffer, ODriveName.Length, // Description + 50 - ODriveName.Length , // Description pad + 0, // Version number + 8, // Tab Size + 1, // Number of copies + NwPrintOptions, // Control Flags + 0x3C, // Maximum lines BUGBUG + 0x84, // Maximum characters BUGBUG + 22, // Skip bytes + &IrpContext->pScb->UserName, 12, // Banner Name + &Vcb->ShareName, 12, // Header Name + 1+14+80 // null last string & skip rest of client area + ); + + // + // free the string if it was allocated + // + if (ODriveName.Buffer != LptName) + RtlFreeAnsiString(&ODriveName); + + if ( NT_SUCCESS( Iosb.Status ) ) { + Iosb.Status = ParseResponse( + IrpContext, + IrpContext->rsp, + IrpContext->ResponseLength, + "N_w_r", + 22, + &Icb->JobId, + 18, + Icb->Handle, sizeof(Icb->Handle) ); + } + + if ( NT_SUCCESS( Iosb.Status ) ) { + + Fcb = Icb->SuperType.Fcb; + + Fcb->NonPagedFcb->Attributes = 0; + Fcb->CreationDate = 0; + Fcb->LastAccessDate = 0; + Fcb->LastModifiedDate = 0; + Fcb->LastModifiedTime = 0; + + Icb->HasRemoteHandle = TRUE; + Icb->IsPrintJob = TRUE; + Icb->ActuallyPrinted = FALSE; + + SetFlag( Fcb->Flags, FCB_FLAGS_ATTRIBUTES_ARE_VALID ); + + } + + // + // Set information field assuming success. It will be ignored + // if the NCP failed. + // + + Iosb.Information = FILE_CREATED; + + if ( Iosb.Status == STATUS_UNSUCCESSFUL ) { + Iosb.Status = STATUS_OBJECT_NAME_COLLISION; + } + + + return( Iosb ); +} + +VOID +CloseFile( + PIRP_CONTEXT pIrpContext, + PICB pIcb + ) +/*++ + +Routine Description: + + This routines closes an opened file. + +Arguments: + + pIrpContext - Supplies all the information + + pIcb - A pointer to the newly created ICB. + +Return Value: + + None. + +--*/ +{ + PAGED_CODE(); + + ExchangeWithWait( + pIrpContext, + SynchronousResponseCallback, + "F-r", + NCP_CLOSE, + pIcb->Handle, 6 ); +} + |