Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

3648 lines
107 KiB

/*++
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
);
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;
BOOLEAN fNDSLookupFirst = FALSE;
USHORT iBufferIndex = 0;
DWORD dwSlashCount = 0;
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;
SECURITY_SUBJECT_CONTEXT SubjectContext;
PAGED_CODE();
//
// Get the current IRP stack location
//
Irp = IrpContext->pOriginalIrp;
IrpSp = IoGetCurrentIrpStackLocation( Irp );
//
// tommye - MS bug 30091 / MCS 262 - added some safety nets around those pointers
// containing pointers so we don't bugcheck in the debug code.
//
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 );
if (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 );
if (IrpSp->Parameters.Create.SecurityContext) {
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;
//
// Reference the credentials before doing the create so
// we are guaranteed not to lose them. This call will
// create a credential shell if none exists. This keeps
// our reference counting consistent. We track the
// credentials pointer in the irp context specific data.
//
Iosb.Status = ExCreateReferenceCredentials( IrpContext, &Server );
if ( !NT_SUCCESS( Iosb.Status ) ) {
try_return( Iosb.Status );
}
}
if (PreferNDSBrowsing) {
//
// Attempt to open \\TREE
//
Iosb.Status = NdsCreateTreeScb( IrpContext,
&Scb, // dest scb
&Server, // tree we want
&UserName,
&Password,
DeferredLogon,
DeleteOnClose );
if ( NT_SUCCESS( Iosb.Status ) ) {
OpenedTreeHandle = TRUE;
}
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 \\server
//
Iosb.Status = CreateScb(
&Scb,
IrpContext,
&Server,
NULL,
&UserName,
&Password,
DeferredLogon,
DeleteOnClose );
}
}else{
//
// 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 ) ) {
OpenedTreeHandle = TRUE;
}
}
}
// if( IsTerminalServer() ) clause below has been shifted down as we are more
// likely to be opening a tree or server than a pserver.
// so we need to check there first.
if (IsTerminalServer()) {
/*
* This is an attempt to get GUEST to work for printman.
* I.E. If you have no connection, try the guest
* connection.
*/
if ( ( !NT_SUCCESS(Iosb.Status) ) &&
( Iosb.Status == STATUS_NO_SUCH_USER ) &&
( !CreateTreeConnection ) &&
( !DeferredLogon ) ) {
DebugTrace( -1, Dbg, " Attempting default GUEST logon for %wZ\n", &Server );
Iosb.Status = CreateScb(
&Scb,
IrpContext,
&Server,
NULL,
&Guest.UserName,
&Guest.PassWord,
DeferredLogon,
DeleteOnClose );
}
}
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;
//
// If this was an extended create, associate this handle
// with its extended credentials so that we can cleanup
// when all the handles are closed.
//
if ( IrpContext->Specific.Create.fExCredentialCreate ) {
ASSERT( IrpContext->Specific.Create.pExCredentials != NULL );
Icb->pContext = IrpContext->Specific.Create.pExCredentials;
Icb->IsExCredentialHandle = TRUE;
}
} 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;
//
// Check to see if this is an NDS object, if so set the flag to check NDS first.
// The only way a DOT can be in the Volume name is if it is an NDS Object,
// between the third and fourth slashes.
//
fNDSLookupFirst = FALSE;
for (iBufferIndex=0; iBufferIndex < (USHORT)(Volume.Length/sizeof(WCHAR)); iBufferIndex++ ) {
if (Volume.Buffer[iBufferIndex] == L'\\')
dwSlashCount++;
if (dwSlashCount > 3) {
fNDSLookupFirst = FALSE;
break;
}
if (Volume.Buffer[iBufferIndex] == L'.') {
fNDSLookupFirst = TRUE;
break;
}
}
if (fNDSLookupFirst) {
IrpContext->Specific.Create.NdsCreate = TRUE;
IrpContext->Specific.Create.NeedNdsData = TRUE;
Iosb.Status = NdsCreateTreeScb( IrpContext,
&Scb,
&Server,
&UserName,
&Password,
DeferredLogon,
DeleteOnClose );
if ( Iosb.Status == STATUS_REMOTE_NOT_LISTENING ||
Iosb.Status == STATUS_BAD_NETWORK_PATH ||
Iosb.Status == STATUS_UNSUCCESSFUL ) {
//
// Not found, do a bindery lookup
//
IrpContext->Specific.Create.NdsCreate = FALSE;
IrpContext->Specific.Create.NeedNdsData = FALSE;
Iosb.Status = CreateScb(
&Scb,
IrpContext,
&Server,
NULL,
&UserName,
&Password,
DeferredLogon,
DeleteOnClose );
}
}else {
//
// Object appears to be bindery, check there first.
//
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 is horrable, 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 ) && ( IrpContext->pNpScb != NULL )) {
NwDereferenceScb( IrpContext->pNpScb );
}
if ( DefaultServer.Buffer != NULL ) {
FREE_POOL( DefaultServer.Buffer );
}
if ( ( IrpContext->Specific.Create.fExCredentialCreate ) &&
( IrpContext->Specific.Create.pExCredentials ) &&
( !NT_SUCCESS( Iosb.Status ) ) ) {
ExCreateDereferenceCredentials(
IrpContext,
IrpContext->Specific.Create.pExCredentials
);
}
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) &&
(EaBuffer->EaValueLength >= sizeof(ULONG))) {
*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 = FALSE;
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;
if ( NonPagedDcb ) {
Dcb = NonPagedDcb->Fcb;
}
//
// If there is a related file object then this is a relative open
// and it better be a DCB.
//
if ( !Dcb || (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 );
// in rare cases, NwFindFcb might return NULL instead of throwing an exception
// Raid # 432500
if (Fcb == NULL) {
DebugTrace(0, Dbg, "NwFindFcb returned NULL in CreateRemoteFile\n", 0);
Iosb.Status = STATUS_INVALID_PARAMETER;
try_return( Iosb );
}
//
// Check the share access for this file. The share access
// is updated if access is granted.
//
if (!IsTerminalServer() ||
!FlagOn( Vcb->Flags, VCB_FLAG_PRINT_QUEUE )) {
if ( Fcb->IcbCount > 0 ) {
NwAcquireSharedFcb( Fcb->NonPagedFcb, TRUE );
Iosb.Status = IoCheckShareAccess(
DesiredAccess,
ShareAccess,
FileObject,
&Fcb->ShareAccess,
TRUE );
NwReleaseFcb( Fcb->NonPagedFcb );
if ( !NT_SUCCESS( Iosb.Status ) ) {
try_return( Iosb );
}
} else {
NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
IoSetShareAccess(
DesiredAccess,
ShareAccess,
FileObject,
&Fcb->ShareAccess );
NwReleaseFcb( Fcb->NonPagedFcb );
}
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(IrpContext->pNpScb, (ULONG_PTR)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 ) {
NwAcquireExclusiveFcb( Fcb->NonPagedFcb, TRUE );
IoRemoveShareAccess( FileObject, &Fcb->ShareAccess );
NwReleaseFcb( Fcb->NonPagedFcb );
}
//
// Failed to create
//
if ( Icb != NULL ) {
if ( Icb->Pid != 0 ) {
NwUnmapPid(IrpContext->pNpScb, Icb->Pid, NULL );
}
//
// dfergus 19 Apr 2001 #330484
//
NwDeleteIcb( NULL, Icb );
// added to fix 330484
IrpContext->Icb = NULL;
}
//
// 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 = (USHORT) (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 = (USHORT) (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 = (USHORT) ((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 );
// in rare cases, NwFindFcb might return NULL instead of throwing an exception
// Raid # 432500
if (Fcb == NULL) {
DebugTrace(0, Dbg, "NwFindFcb returned NULL in OpenRenameTarget\n", 0);
Iosb.Status = STATUS_INVALID_PARAMETER;
try_return( Iosb );
}
//
// 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" ;
ULONG PrintOptions;
PLOGON Logon;
PUNICODE_STRING BannerName;
PAGED_CODE();
BannerName = &IrpContext->pScb->UserName;
NwAcquireExclusiveRcb( &NwRcb, TRUE );
Logon = FindUser( &IrpContext->pScb->UserUid, TRUE );
if ( Logon == NULL ) {
PrintOptions = NwPrintOptions;
} else {
PrintOptions = Logon->NwPrintOptions;
/*
* If user name is GUEST, use the validated user name
*/
if ((BannerName->Length == 0 ) ||
(RtlCompareUnicodeString( BannerName, &Guest.UserName, TRUE ) == 0 )) {
BannerName = &Logon->UserName;
}
}
NwReleaseRcb( &NwRcb );
//
// 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
PrintOptions, // Control Flags
0x3C, // Maximum lines
0x84, // Maximum characters
22, // Skip bytes
BannerName, 13, // 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 );
}