1404 lines
42 KiB
C
1404 lines
42 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
move.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the routine to rename or copy a file. This
|
||
routine is used by the routines SrvSmbRenameFile,
|
||
SrvSmbRenameFileExtended, and SrvSmbCopyFile.
|
||
|
||
Author:
|
||
|
||
David Treadwell (davidtr) 22-Jan-1990
|
||
|
||
Revision History:
|
||
|
||
--*/
|
||
|
||
#include "precomp.h"
|
||
#include "move.tmh"
|
||
#pragma hdrstop
|
||
|
||
#define BugCheckFileId SRV_FILE_MOVE
|
||
|
||
NTSTATUS
|
||
DoCopy (
|
||
IN PWORK_CONTEXT WorkContext,
|
||
IN PUNICODE_STRING Source,
|
||
IN HANDLE SourceHandle,
|
||
IN PUNICODE_STRING Target,
|
||
IN PSHARE TargetShare,
|
||
IN USHORT SmbOpenFunction,
|
||
IN PUSHORT SmbFlags
|
||
);
|
||
|
||
NTSTATUS
|
||
DoRename (
|
||
IN PWORK_CONTEXT WorkContext,
|
||
IN PUNICODE_STRING Source,
|
||
IN HANDLE SourceHandle,
|
||
IN PUNICODE_STRING Target,
|
||
IN PSHARE TargetShare,
|
||
IN USHORT SmbOpenFunction,
|
||
IN PUSHORT SmbFlags,
|
||
IN BOOLEAN FailIfTargetIsDirectory,
|
||
IN USHORT InformationLevel,
|
||
IN ULONG ClusterCount
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text( PAGE, SrvMoveFile )
|
||
#pragma alloc_text( PAGE, DoCopy )
|
||
#pragma alloc_text( PAGE, DoRename )
|
||
#endif
|
||
|
||
|
||
NTSTATUS
|
||
SrvMoveFile(
|
||
IN PWORK_CONTEXT WorkContext,
|
||
IN PSHARE TargetShare,
|
||
IN USHORT SmbOpenFunction,
|
||
IN OUT PUSHORT SmbFlags,
|
||
IN USHORT SmbSearchAttributes,
|
||
IN BOOLEAN FailIfTargetIsDirectory,
|
||
IN USHORT InformationLevel,
|
||
IN ULONG ClusterCount,
|
||
IN PUNICODE_STRING Source,
|
||
IN OUT PUNICODE_STRING Target
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine moves a file, which may be a copy or a rename.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - a pointer to the work context block for the operation. The
|
||
Session, TreeConnect, and RequestHeader fields are used.
|
||
|
||
TargetShare - a pointer to the share on which the target should
|
||
be. The RootDirectoryHandle field is used to do relative opens.
|
||
|
||
SmbOpenFunction - the "OpenFunction" field of the request SMB. This
|
||
parameter is used to determine what should be done if the target
|
||
file does or does not exist.
|
||
|
||
SmbFlags - a pointer to the "Flags" field of the request SMB. This
|
||
parameter is used to determine whether we know that the target
|
||
is supposed to be a file or directory. In addition, if this has
|
||
no information about the target, it is set to reflect whether
|
||
the target was a directory or file. This is useful when doing
|
||
multiple renames or copies as a result of wildcards--move a*.* b
|
||
might call this routine many times, and if b is a directory,
|
||
this routine will set this parameter appropiately such that if
|
||
does not have to reopen the directory for each move.
|
||
|
||
SmbSearchAttributes - the search attributes specified in the request
|
||
SMB. The attributes on the source file are checked against
|
||
these to make sure that the move can be done.
|
||
|
||
FailIfTargetIsDirectory - if TRUE and the target already exists as
|
||
a directory, fail the operation. Otherwise, rename the file
|
||
into the directory.
|
||
|
||
InformationLevel - Move/Rename/CopyOnWrite/Link/MoveCluster
|
||
|
||
ClusterCount - MoveCluster count
|
||
|
||
Source - a pointer to a string describing the name of the source file
|
||
relative to the share directory in which it is located.
|
||
|
||
Target - a pathname to the target file. This may contain directory
|
||
information--it should be the raw information from the SMB,
|
||
unadulterated by the SMB processing routine except for
|
||
canonicalization. This name may end in a directory name, in
|
||
which case the source name is used as the filename.
|
||
|
||
Return Value:
|
||
|
||
Status.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
HANDLE sourceHandle;
|
||
BOOLEAN isCompatibilityOpen;
|
||
PMFCB mfcb;
|
||
PNONPAGED_MFCB nonpagedMfcb;
|
||
PLFCB lfcb;
|
||
|
||
OBJECT_ATTRIBUTES sourceObjectAttributes;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
ULONG sourceAccess = 0;
|
||
BOOLEAN isNtRename;
|
||
ULONG hashValue;
|
||
|
||
PSESSION session;
|
||
PSHARE sourceShare;
|
||
|
||
PSRV_LOCK mfcbLock;
|
||
|
||
PAGED_CODE( );
|
||
|
||
IF_SMB_DEBUG(FILE_CONTROL2) SrvPrint0( "SrvMoveFile entered.\n" );
|
||
|
||
//
|
||
// Set handles and pointers to NULL so we know how to clean up on
|
||
// exit.
|
||
//
|
||
|
||
sourceHandle = NULL;
|
||
isCompatibilityOpen = FALSE;
|
||
lfcb = NULL;
|
||
//mfcb = NULL; // not really necessary--SrvFindMfcb sets it correctly
|
||
|
||
//
|
||
// Set up the block pointers that will be needed.
|
||
//
|
||
|
||
session = WorkContext->Session;
|
||
sourceShare = WorkContext->TreeConnect->Share;
|
||
|
||
isNtRename = (BOOLEAN)(WorkContext->RequestHeader->Command == SMB_COM_NT_RENAME);
|
||
|
||
//
|
||
// See if we already have this file open in compatibility mode. If
|
||
// we do, and this session owns it, then we must use that open
|
||
// handle and, if this is a rename, close all the handles when we
|
||
// are done.
|
||
//
|
||
// *** SrvFindMfcb references the MFCB--remember to dereference it.
|
||
//
|
||
|
||
if ( (WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE) ||
|
||
WorkContext->Session->UsingUppercasePaths ) {
|
||
mfcb = SrvFindMfcb( Source, TRUE, &mfcbLock, &hashValue, WorkContext );
|
||
} else {
|
||
mfcb = SrvFindMfcb( Source, FALSE, &mfcbLock, &hashValue, WorkContext );
|
||
}
|
||
|
||
if ( mfcb != NULL ) {
|
||
nonpagedMfcb = mfcb->NonpagedMfcb;
|
||
ACQUIRE_LOCK( &nonpagedMfcb->Lock );
|
||
}
|
||
|
||
if( mfcbLock ) {
|
||
RELEASE_LOCK( mfcbLock );
|
||
}
|
||
|
||
if ( mfcb == NULL || !mfcb->CompatibilityOpen ) {
|
||
|
||
//
|
||
// Either the file wasn't opened by the server or it was not
|
||
// a compatibility/FCB open, so open it here.
|
||
//
|
||
// Release the open lock--we don't need it any more.
|
||
//
|
||
|
||
if ( mfcb != NULL ) {
|
||
RELEASE_LOCK( &nonpagedMfcb->Lock );
|
||
}
|
||
|
||
//
|
||
// Use DELETE access for a rename, and the appropriate copy access
|
||
// for Copy/Link/Move/MoveCluster.
|
||
//
|
||
|
||
switch (InformationLevel) {
|
||
case SMB_NT_RENAME_RENAME_FILE:
|
||
sourceAccess = DELETE;
|
||
break;
|
||
|
||
case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
|
||
sourceAccess = SRV_COPY_TARGET_ACCESS & ~(WRITE_DAC | WRITE_OWNER);
|
||
break;
|
||
|
||
case SMB_NT_RENAME_SET_LINK_INFO:
|
||
case SMB_NT_RENAME_MOVE_FILE:
|
||
sourceAccess = SRV_COPY_SOURCE_ACCESS;
|
||
break;
|
||
|
||
default:
|
||
ASSERT(FALSE);
|
||
}
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&sourceObjectAttributes,
|
||
Source,
|
||
(WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
|
||
session->UsingUppercasePaths) ? OBJ_CASE_INSENSITIVE : 0L,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
IF_SMB_DEBUG(FILE_CONTROL2) {
|
||
SrvPrint1( "Opening source: %wZ\n",
|
||
sourceObjectAttributes.ObjectName );
|
||
}
|
||
|
||
//
|
||
// Open the source file. We allow read access for other processes.
|
||
//
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
|
||
|
||
#ifdef SLMDBG
|
||
if ( SrvIsSlmStatus( Target ) ) {
|
||
sourceAccess |= GENERIC_READ;
|
||
}
|
||
#endif
|
||
//
|
||
// !!! Currently we can't specify complete if oplocked, because
|
||
// this won't break a batch oplock. Unfortunately this also
|
||
// means that we can't timeout the open (if the oplock break
|
||
// takes too long) and fail this SMB gracefully.
|
||
//
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&sourceHandle,
|
||
sourceAccess | SYNCHRONIZE, // DesiredAccess
|
||
&sourceObjectAttributes,
|
||
&ioStatusBlock,
|
||
NULL, // AllocationSize
|
||
0, // FileAttributes
|
||
FILE_SHARE_READ, // ShareAccess
|
||
FILE_OPEN, // Disposition
|
||
FILE_SYNCHRONOUS_IO_NONALERT // CreateOptions
|
||
| FILE_OPEN_REPARSE_POINT,
|
||
NULL, // EaBuffer
|
||
0, // EaLength
|
||
CreateFileTypeNone, // File type
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK, // Options
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
|
||
if( status == STATUS_INVALID_PARAMETER ) {
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&sourceHandle,
|
||
sourceAccess | SYNCHRONIZE, // DesiredAccess
|
||
&sourceObjectAttributes,
|
||
&ioStatusBlock,
|
||
NULL, // AllocationSize
|
||
0, // FileAttributes
|
||
FILE_SHARE_READ, // ShareAccess
|
||
FILE_OPEN, // Disposition
|
||
FILE_SYNCHRONOUS_IO_NONALERT, // CreateOptions
|
||
NULL, // EaBuffer
|
||
0, // EaLength
|
||
CreateFileTypeNone, // File type
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK, // Options
|
||
WorkContext->TreeConnect->Share
|
||
);
|
||
}
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
|
||
SRVDBG_CLAIM_HANDLE( sourceHandle, "MOV", 4, 0 );
|
||
|
||
} else if ( status == STATUS_ACCESS_DENIED ) {
|
||
|
||
//
|
||
// If the user didn't have this permission, update the statistics
|
||
// database.
|
||
//
|
||
SrvStatistics.AccessPermissionErrors++;
|
||
}
|
||
|
||
ASSERT( status != STATUS_OPLOCK_BREAK_IN_PROGRESS );
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "SrvMoveFile: SrvIoCreateFile failed (source): %X\n",
|
||
status );
|
||
}
|
||
|
||
goto exit;
|
||
}
|
||
|
||
IF_SMB_DEBUG(FILE_CONTROL2) {
|
||
SrvPrint1( "SrvIoCreateFile succeeded (source), handle = 0x%p\n",
|
||
sourceHandle );
|
||
}
|
||
|
||
SrvStatistics.TotalFilesOpened++;
|
||
|
||
} else {
|
||
|
||
//
|
||
// The file was opened by the server in compatibility mode or as
|
||
// an FCB open.
|
||
//
|
||
|
||
lfcb = CONTAINING_RECORD( mfcb->LfcbList.Blink, LFCB, MfcbListEntry );
|
||
|
||
//
|
||
// Make sure that the session which sent this request is the
|
||
// same as the one which has the file open.
|
||
//
|
||
|
||
if ( lfcb->Session != session ) {
|
||
|
||
//
|
||
// A different session has the file open in compatibility
|
||
// mode, so reject the request.
|
||
//
|
||
|
||
status = STATUS_ACCESS_DENIED;
|
||
RELEASE_LOCK( &nonpagedMfcb->Lock );
|
||
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// Set isCompatibilityOpen so that we'll know on exit to close
|
||
// all the open instances of this file.
|
||
//
|
||
|
||
isCompatibilityOpen = TRUE;
|
||
|
||
sourceHandle = lfcb->FileHandle;
|
||
sourceAccess = lfcb->GrantedAccess;
|
||
|
||
}
|
||
|
||
//
|
||
// Make sure that the search attributes jive with the attributes
|
||
// on the file.
|
||
//
|
||
|
||
status = SrvCheckSearchAttributesForHandle( sourceHandle, SmbSearchAttributes );
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
goto exit;
|
||
}
|
||
|
||
//
|
||
// If the target has length 0, then it is the share root, which must
|
||
// be a directory. If the target is supposed to be a file, fail,
|
||
// otherwise indicate that the target is a directory.
|
||
//
|
||
|
||
if ( Target->Length == 0 ) {
|
||
|
||
if ( *SmbFlags & SMB_TARGET_IS_FILE ) {
|
||
status = STATUS_INVALID_PARAMETER;
|
||
goto exit;
|
||
}
|
||
|
||
*SmbFlags |= SMB_TARGET_IS_DIRECTORY;
|
||
}
|
||
|
||
//
|
||
// We now have the source file open. Call the appropriate routine
|
||
// to rename or copy the file.
|
||
//
|
||
|
||
if (InformationLevel != SMB_NT_RENAME_MOVE_FILE) {
|
||
|
||
#ifdef SLMDBG
|
||
if (InformationLevel == SMB_NT_RENAME_RENAME_FILE &&
|
||
SrvIsSlmStatus( Source ) || SrvIsSlmStatus( Target ) ) {
|
||
|
||
ULONG offset;
|
||
|
||
status = SrvValidateSlmStatus(
|
||
sourceHandle,
|
||
WorkContext,
|
||
&offset
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
SrvReportCorruptSlmStatus(
|
||
Source,
|
||
status,
|
||
offset,
|
||
SLMDBG_RENAME,
|
||
WorkContext->Session
|
||
);
|
||
SrvDisallowSlmAccess(
|
||
Source,
|
||
WorkContext->TreeConnect->Share->RootDirectoryHandle
|
||
);
|
||
status = STATUS_DISK_CORRUPT_ERROR;
|
||
goto exit;
|
||
}
|
||
|
||
}
|
||
#endif
|
||
|
||
status = DoRename(
|
||
WorkContext,
|
||
Source,
|
||
sourceHandle,
|
||
Target,
|
||
TargetShare,
|
||
SmbOpenFunction,
|
||
SmbFlags,
|
||
FailIfTargetIsDirectory,
|
||
InformationLevel,
|
||
ClusterCount
|
||
);
|
||
|
||
} else {
|
||
|
||
FILE_BASIC_INFORMATION fileBasicInformation;
|
||
|
||
//
|
||
// Check whether this is a tree copy request. If so, allow it only if
|
||
// this is a single file copy operation.
|
||
//
|
||
|
||
if ( (*SmbFlags & SMB_COPY_TREE) != 0 ) {
|
||
|
||
//
|
||
// Get the attributes on the file.
|
||
//
|
||
|
||
status = SrvQueryBasicAndStandardInformation(
|
||
sourceHandle,
|
||
NULL,
|
||
&fileBasicInformation,
|
||
NULL
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_UNEXPECTED,
|
||
"SrvMoveFile: NtQueryInformationFile (basic "
|
||
"information) returned %X",
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
SrvLogServiceFailure( SRV_SVC_NT_QUERY_INFO_FILE, status );
|
||
goto exit;
|
||
}
|
||
|
||
if ( ( fileBasicInformation.FileAttributes &
|
||
FILE_ATTRIBUTE_DIRECTORY ) != 0 ) {
|
||
|
||
//
|
||
// Fail this copy.
|
||
//
|
||
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"Tree copy not implemented.",
|
||
NULL,
|
||
NULL
|
||
);
|
||
status = STATUS_NOT_IMPLEMENTED;
|
||
goto exit;
|
||
}
|
||
|
||
}
|
||
|
||
status = DoCopy(
|
||
WorkContext,
|
||
Source,
|
||
sourceHandle,
|
||
Target,
|
||
TargetShare,
|
||
SmbOpenFunction,
|
||
SmbFlags
|
||
);
|
||
}
|
||
|
||
exit:
|
||
|
||
if ( sourceHandle != NULL && !isCompatibilityOpen ) {
|
||
SRVDBG_RELEASE_HANDLE( sourceHandle, "MOV", 9, 0 );
|
||
SrvNtClose( sourceHandle, TRUE );
|
||
} else if (isCompatibilityOpen &&
|
||
InformationLevel == SMB_NT_RENAME_RENAME_FILE) {
|
||
SrvCloseRfcbsOnLfcb( lfcb );
|
||
}
|
||
|
||
//
|
||
// If the file is open in compatibility mode, then we have held the
|
||
// MFCB lock all along. Release it now.
|
||
//
|
||
|
||
if ( isCompatibilityOpen ) {
|
||
RELEASE_LOCK( &nonpagedMfcb->Lock );
|
||
}
|
||
|
||
if ( mfcb != NULL ) {
|
||
SrvDereferenceMfcb( mfcb );
|
||
}
|
||
|
||
return status;
|
||
|
||
} // SrvMoveFile
|
||
|
||
|
||
NTSTATUS
|
||
DoCopy (
|
||
IN PWORK_CONTEXT WorkContext,
|
||
IN PUNICODE_STRING Source,
|
||
IN HANDLE SourceHandle,
|
||
IN PUNICODE_STRING Target,
|
||
IN PSHARE TargetShare,
|
||
IN USHORT SmbOpenFunction,
|
||
IN PUSHORT SmbFlags
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets up for a call to SrvCopyFile. It opens the target,
|
||
determining, if necessary, whether the target is a file or directory.
|
||
If this information is unknown, it writes it into the SmbFlags
|
||
location.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - a pointer to the work context block for the operation.
|
||
The session pointer is used, and the block itself is used for
|
||
an impersonation.
|
||
|
||
Source - the name of the source file relative to its share.
|
||
|
||
SourceHandle - the handle to the source file.
|
||
|
||
Target - the name of the target file relative to its share.
|
||
|
||
TargetShare - the share of the target file. The RootDirectoryHandle
|
||
field is used for a relative rename.
|
||
|
||
SmbOpenFunction - describes whether we are allowed to overwrite an
|
||
existing file, or we should append to existing files.
|
||
|
||
SmbFlags - can tell if the target is a file, directory, or unknown.
|
||
This routine writes the true information into the location if
|
||
it is unknown.
|
||
|
||
Return Value:
|
||
|
||
Status.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
ULONG createDisposition;
|
||
UNICODE_STRING sourceBaseName;
|
||
BOOLEAN create;
|
||
|
||
HANDLE targetHandle = NULL;
|
||
OBJECT_ATTRIBUTES targetObjectAttributes;
|
||
UNICODE_STRING targetName;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Set the buffer field of targetName to NULL so that we'll know
|
||
// if we have to deallocate it at the end.
|
||
//
|
||
|
||
targetName.Buffer = NULL;
|
||
|
||
//
|
||
// Open the target file. If we know that it is a directory, generate
|
||
// the full file name. Otherwise, open the target as a file.
|
||
//
|
||
|
||
SrvInitializeObjectAttributes_U(
|
||
&targetObjectAttributes,
|
||
Target,
|
||
(WorkContext->RequestHeader->Flags & SMB_FLAGS_CASE_INSENSITIVE ||
|
||
WorkContext->Session->UsingUppercasePaths) ? OBJ_CASE_INSENSITIVE : 0L,
|
||
NULL,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Determine the create disposition from the open function.
|
||
//
|
||
|
||
create = SmbOfunCreate( SmbOpenFunction );
|
||
|
||
if ( SmbOfunTruncate( SmbOpenFunction ) ) {
|
||
createDisposition = create ? FILE_OVERWRITE_IF : FILE_OVERWRITE;
|
||
} else if ( SmbOfunAppend( SmbOpenFunction ) ) {
|
||
createDisposition = create ? FILE_OPEN_IF : FILE_OPEN;
|
||
} else {
|
||
createDisposition = FILE_CREATE;
|
||
}
|
||
|
||
//
|
||
// If we know that the target is a directory, generate the real target
|
||
// name.
|
||
//
|
||
|
||
if ( *SmbFlags & SMB_TARGET_IS_DIRECTORY ) {
|
||
|
||
SrvGetBaseFileName( Source, &sourceBaseName );
|
||
|
||
SrvAllocateAndBuildPathName(
|
||
Target,
|
||
&sourceBaseName,
|
||
NULL,
|
||
&targetName
|
||
);
|
||
|
||
if ( targetName.Buffer == NULL ) {
|
||
status = STATUS_INSUFF_SERVER_RESOURCES;
|
||
goto copy_done;
|
||
}
|
||
|
||
targetObjectAttributes.ObjectName = &targetName;
|
||
}
|
||
|
||
IF_SMB_DEBUG(FILE_CONTROL2) {
|
||
SrvPrint1( "Opening target: %wZ\n", targetObjectAttributes.ObjectName );
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
|
||
|
||
//
|
||
// !!! Currently we can't specify complete if oplocked, because
|
||
// this won't break a batch oplock. Unfortunately this also
|
||
// means that we can't timeout the open (if the oplock break
|
||
// takes too long) and fail this SMB gracefully.
|
||
//
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&targetHandle,
|
||
SRV_COPY_TARGET_ACCESS | SYNCHRONIZE, // DesiredAccess
|
||
&targetObjectAttributes,
|
||
&ioStatusBlock,
|
||
NULL, // AllocationSize
|
||
0, // FileAttributes
|
||
FILE_SHARE_READ, // ShareAccess
|
||
createDisposition,
|
||
FILE_NON_DIRECTORY_FILE | // CreateOptions
|
||
FILE_OPEN_REPARSE_POINT |
|
||
FILE_SYNCHRONOUS_IO_NONALERT,
|
||
// | FILE_COMPLETE_IF_OPLOCKED,
|
||
NULL, // EaBuffer
|
||
0, // EaLength
|
||
CreateFileTypeNone, // File type
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK, // Options
|
||
TargetShare
|
||
);
|
||
|
||
if( status == STATUS_INVALID_PARAMETER ) {
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&targetHandle,
|
||
SRV_COPY_TARGET_ACCESS | SYNCHRONIZE, // DesiredAccess
|
||
&targetObjectAttributes,
|
||
&ioStatusBlock,
|
||
NULL, // AllocationSize
|
||
0, // FileAttributes
|
||
FILE_SHARE_READ, // ShareAccess
|
||
createDisposition,
|
||
FILE_NON_DIRECTORY_FILE | // CreateOptions
|
||
FILE_SYNCHRONOUS_IO_NONALERT,
|
||
// | FILE_COMPLETE_IF_OPLOCKED,
|
||
NULL, // EaBuffer
|
||
0, // EaLength
|
||
CreateFileTypeNone, // File type
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK, // Options
|
||
TargetShare
|
||
);
|
||
}
|
||
|
||
|
||
//
|
||
// If the open failed because the target is a directory, and we didn't
|
||
// know that it was supposed to be a file, then concatenate the
|
||
// source base name to the target and retry the open.
|
||
//
|
||
// !!! NOT THE CORRECT STATUS CODE. It should be something like
|
||
// STATUS_FILE_IS_DIRECTORY.
|
||
|
||
if ( status == STATUS_INVALID_PARAMETER &&
|
||
!( *SmbFlags & SMB_TARGET_IS_FILE ) &&
|
||
!( *SmbFlags & SMB_TARGET_IS_DIRECTORY ) ) {
|
||
|
||
//
|
||
// Set the flags so that future calls to this routine will do
|
||
// the right thing first time around.
|
||
//
|
||
|
||
*SmbFlags |= SMB_TARGET_IS_DIRECTORY;
|
||
|
||
SrvGetBaseFileName( Source, &sourceBaseName );
|
||
|
||
SrvAllocateAndBuildPathName(
|
||
Target,
|
||
&sourceBaseName,
|
||
NULL,
|
||
&targetName
|
||
);
|
||
|
||
if ( targetName.Buffer == NULL ) {
|
||
status = STATUS_INSUFF_SERVER_RESOURCES;
|
||
goto copy_done;
|
||
}
|
||
|
||
targetObjectAttributes.ObjectName = &targetName;
|
||
|
||
IF_SMB_DEBUG(FILE_CONTROL2) {
|
||
SrvPrint1( "Opening target: %wZ\n", targetObjectAttributes.ObjectName );
|
||
}
|
||
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpenAttempts );
|
||
INCREMENT_DEBUG_STAT( SrvDbgStatistics.TotalOpensForPathOperations );
|
||
|
||
//
|
||
// !!! Currently we can't specify complete if oplocked, because
|
||
// this won't break a batch oplock. Unfortunately this also
|
||
// means that we can't timeout the open (if the oplock break
|
||
// takes too long) and fail this SMB gracefully.
|
||
//
|
||
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&targetHandle,
|
||
SRV_COPY_TARGET_ACCESS | SYNCHRONIZE, // DesiredAccess
|
||
&targetObjectAttributes,
|
||
&ioStatusBlock,
|
||
NULL, // AllocationSize
|
||
0, // FileAttributes
|
||
FILE_SHARE_READ, // ShareAccess
|
||
createDisposition,
|
||
FILE_NON_DIRECTORY_FILE | // CreateOptions
|
||
FILE_OPEN_REPARSE_POINT |
|
||
FILE_SYNCHRONOUS_IO_NONALERT,
|
||
NULL, // EaBuffer
|
||
0, // EaLength
|
||
CreateFileTypeNone, // File Type
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK, // Options
|
||
TargetShare
|
||
);
|
||
|
||
if( status == STATUS_INVALID_PARAMETER ) {
|
||
status = SrvIoCreateFile(
|
||
WorkContext,
|
||
&targetHandle,
|
||
SRV_COPY_TARGET_ACCESS | SYNCHRONIZE, // DesiredAccess
|
||
&targetObjectAttributes,
|
||
&ioStatusBlock,
|
||
NULL, // AllocationSize
|
||
0, // FileAttributes
|
||
FILE_SHARE_READ, // ShareAccess
|
||
createDisposition,
|
||
FILE_NON_DIRECTORY_FILE | // CreateOptions
|
||
FILE_SYNCHRONOUS_IO_NONALERT,
|
||
NULL, // EaBuffer
|
||
0, // EaLength
|
||
CreateFileTypeNone, // File Type
|
||
NULL, // ExtraCreateParameters
|
||
IO_FORCE_ACCESS_CHECK, // Options
|
||
TargetShare
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
if ( targetHandle != NULL ) {
|
||
SRVDBG_CLAIM_HANDLE( targetHandle, "CPY", 5, 0 );
|
||
}
|
||
|
||
//
|
||
// Is the target is a directory, and the copy move is append if exists,
|
||
// create if the file does not exist, fail the request. We must do
|
||
// this, because we have no way of knowing whether the original request
|
||
// expects us append to the file, or truncate it.
|
||
//
|
||
|
||
if ( (*SmbFlags & SMB_TARGET_IS_DIRECTORY) &&
|
||
((SmbOpenFunction & SMB_OFUN_OPEN_MASK) == SMB_OFUN_OPEN_OPEN) &&
|
||
((SmbOpenFunction & SMB_OFUN_CREATE_MASK) == SMB_OFUN_CREATE_CREATE)) {
|
||
|
||
status = STATUS_OS2_CANNOT_COPY;
|
||
goto copy_done;
|
||
|
||
}
|
||
|
||
//
|
||
// If the user didn't have this permission, update the statistics
|
||
// database.
|
||
//
|
||
|
||
if ( status == STATUS_ACCESS_DENIED ) {
|
||
SrvStatistics.AccessPermissionErrors++;
|
||
}
|
||
|
||
ASSERT( status != STATUS_OPLOCK_BREAK_IN_PROGRESS );
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "Unable to open target: %X\n", status );
|
||
}
|
||
|
||
goto copy_done;
|
||
}
|
||
|
||
SrvStatistics.TotalFilesOpened++;
|
||
|
||
//
|
||
// Copy the source to the target handle just opened.
|
||
//
|
||
|
||
status = SrvCopyFile(
|
||
SourceHandle,
|
||
targetHandle,
|
||
SmbOpenFunction,
|
||
*SmbFlags,
|
||
(ULONG)ioStatusBlock.Information // TargetOpenAction
|
||
);
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "SrvCopyFile failed, status = %X\n", status );
|
||
}
|
||
}
|
||
|
||
copy_done:
|
||
|
||
if ( targetName.Buffer != NULL ) {
|
||
FREE_HEAP( targetName.Buffer );
|
||
}
|
||
|
||
if ( targetHandle != NULL ) {
|
||
SRVDBG_RELEASE_HANDLE( targetHandle, "CPY", 10, 0 );
|
||
SrvNtClose( targetHandle, TRUE );
|
||
}
|
||
|
||
return status;
|
||
|
||
} // DoCopy
|
||
|
||
|
||
NTSTATUS
|
||
DoRename (
|
||
IN PWORK_CONTEXT WorkContext,
|
||
IN PUNICODE_STRING Source,
|
||
IN HANDLE SourceHandle,
|
||
IN PUNICODE_STRING Target,
|
||
IN PSHARE TargetShare,
|
||
IN USHORT SmbOpenFunction,
|
||
IN OUT PUSHORT SmbFlags,
|
||
IN BOOLEAN FailIfTargetIsDirectory,
|
||
IN USHORT InformationLevel,
|
||
IN ULONG ClusterCount
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine does the actual rename of an open file. The target may
|
||
be a file or directory, but is bound by the constraints of SmbFlags.
|
||
If SmbFlags does not indicate what the target is, then it is first
|
||
assumed to be a file; if this fails, then the rename if performed
|
||
again with the target as the original target string plus the source
|
||
base name.
|
||
|
||
*** If the source and target are on different volumes, then this
|
||
routine will fail. We could make this work by doing a copy
|
||
then delete, but this seems to be of limited usefulness and
|
||
possibly incorrect due to the fact that a big file would take
|
||
a long time, something the user would not expect.
|
||
|
||
Arguments:
|
||
|
||
WorkContext - a pointer to the work context block for this operation
|
||
used for an impersonation.
|
||
|
||
Source - the name of the source file relative to its share.
|
||
|
||
SourceHandle - the handle to the source file.
|
||
|
||
Target - the name of the target file relative to its share.
|
||
|
||
TargetShare - the share of the target file. The RootDirectoryHandle
|
||
field is used for a relative rename.
|
||
|
||
SmbOpenFunction - describes whether we are allowed to overwrite an
|
||
existing file.
|
||
|
||
SmbFlags - can tell if the target is a file, directory, or unknown.
|
||
This routine writes the true information into the location if
|
||
it is unknown.
|
||
|
||
FailIfTargetIsDirectory - if TRUE and the target already exists as
|
||
a directory, fail the operation. Otherwise, rename the file
|
||
into the directory.
|
||
|
||
InformationLevel - Rename/CopyOnWrite/Link/MoveCluster
|
||
|
||
ClusterCount - MoveCluster count
|
||
|
||
Return Value:
|
||
|
||
Status.
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
IO_STATUS_BLOCK ioStatusBlock;
|
||
PFILE_RENAME_INFORMATION fileRenameInformation;
|
||
ULONG renameBlockSize;
|
||
USHORT NtInformationLevel;
|
||
UNICODE_STRING sourceBaseName;
|
||
UNICODE_STRING targetBaseName;
|
||
PWCH s, es;
|
||
|
||
PAGED_CODE( );
|
||
|
||
//
|
||
// Allocate enough heap to hold a FILE_RENAME_INFORMATION block and
|
||
// the target file name. Allocate enough extra to hold the source
|
||
// name in case the target turns out to be a directory and we have
|
||
// to concatenate the source and target.
|
||
//
|
||
|
||
renameBlockSize = sizeof(FILE_RENAME_INFORMATION) + Target->Length +
|
||
Source->Length;
|
||
|
||
fileRenameInformation = ALLOCATE_HEAP_COLD(
|
||
renameBlockSize,
|
||
BlockTypeDataBuffer
|
||
);
|
||
|
||
if ( fileRenameInformation == NULL ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint0( "SrvMoveFile: Unable to allocate heap.\n" );
|
||
}
|
||
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
|
||
}
|
||
|
||
//
|
||
// Get the Share root handle.
|
||
//
|
||
|
||
status = SrvGetShareRootHandle( TargetShare );
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "DoRename: SrvGetShareRootHandle failed. %X\n", status );
|
||
}
|
||
|
||
FREE_HEAP( fileRenameInformation );
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// Set up the rename block.
|
||
//
|
||
|
||
if (InformationLevel == SMB_NT_RENAME_MOVE_CLUSTER_INFO) {
|
||
((FILE_MOVE_CLUSTER_INFORMATION *)fileRenameInformation)->ClusterCount =
|
||
ClusterCount;
|
||
} else {
|
||
fileRenameInformation->ReplaceIfExists =
|
||
SmbOfunTruncate( SmbOpenFunction );
|
||
}
|
||
|
||
fileRenameInformation->RootDirectory = TargetShare->RootDirectoryHandle;
|
||
|
||
//
|
||
// If the target file has wildcards, expand name.
|
||
//
|
||
|
||
if ( FsRtlDoesNameContainWildCards( Target ) ) {
|
||
|
||
ULONG tempUlong;
|
||
UNICODE_STRING newTargetBaseName;
|
||
|
||
if (InformationLevel != SMB_NT_RENAME_RENAME_FILE) {
|
||
return(STATUS_OBJECT_PATH_SYNTAX_BAD);
|
||
}
|
||
|
||
//
|
||
// Get source and target filenames. The target filename is to be
|
||
// used as a template for wildcard expansion.
|
||
//
|
||
|
||
SrvGetBaseFileName( Source, &sourceBaseName );
|
||
SrvGetBaseFileName( Target, &targetBaseName );
|
||
|
||
tempUlong = sourceBaseName.Length + targetBaseName.Length;
|
||
newTargetBaseName.Length = (USHORT)tempUlong;
|
||
newTargetBaseName.MaximumLength = (USHORT)tempUlong;
|
||
newTargetBaseName.Buffer = ALLOCATE_NONPAGED_POOL(
|
||
tempUlong,
|
||
BlockTypeDataBuffer
|
||
);
|
||
|
||
if ( newTargetBaseName.Buffer == NULL ) {
|
||
|
||
INTERNAL_ERROR(
|
||
ERROR_LEVEL_EXPECTED,
|
||
"DoRename: Unable to allocate %d bytes from nonpaged pool.\n",
|
||
tempUlong,
|
||
NULL
|
||
);
|
||
|
||
//
|
||
// Release the share root handle if device is removable
|
||
//
|
||
|
||
SrvReleaseShareRootHandle( TargetShare );
|
||
|
||
FREE_HEAP( fileRenameInformation );
|
||
return STATUS_INSUFF_SERVER_RESOURCES;
|
||
|
||
}
|
||
|
||
//
|
||
// Get expanded filename
|
||
//
|
||
|
||
status = SrvWildcardRename(
|
||
&targetBaseName,
|
||
&sourceBaseName,
|
||
&newTargetBaseName
|
||
);
|
||
|
||
if ( !NT_SUCCESS( status ) ) {
|
||
|
||
//
|
||
// Release the share root handle if device is removable
|
||
//
|
||
|
||
SrvReleaseShareRootHandle( TargetShare );
|
||
|
||
DEALLOCATE_NONPAGED_POOL( newTargetBaseName.Buffer );
|
||
FREE_HEAP( fileRenameInformation );
|
||
return STATUS_OBJECT_NAME_INVALID;
|
||
|
||
}
|
||
|
||
//
|
||
// tempUlong is equal to the directory path without this filename
|
||
// but including the last delimeter.
|
||
//
|
||
|
||
tempUlong = Target->Length - targetBaseName.Length;
|
||
|
||
//
|
||
// Copy the directory path (including the delimeter.
|
||
//
|
||
|
||
RtlCopyMemory(
|
||
fileRenameInformation->FileName,
|
||
Target->Buffer,
|
||
tempUlong
|
||
);
|
||
|
||
s = (PWCH) ((PCHAR)fileRenameInformation->FileName + tempUlong);
|
||
|
||
//
|
||
// Copy the expanded file name
|
||
//
|
||
|
||
RtlCopyMemory(
|
||
s,
|
||
newTargetBaseName.Buffer,
|
||
newTargetBaseName.Length
|
||
);
|
||
|
||
|
||
fileRenameInformation->FileNameLength = tempUlong +
|
||
newTargetBaseName.Length;
|
||
|
||
DEALLOCATE_NONPAGED_POOL( newTargetBaseName.Buffer );
|
||
|
||
} else {
|
||
|
||
fileRenameInformation->FileNameLength = Target->Length;
|
||
|
||
RtlCopyMemory(
|
||
fileRenameInformation->FileName,
|
||
Target->Buffer,
|
||
Target->Length
|
||
);
|
||
|
||
// Check if we can do a fast rename if they are in the same path (which is usually the case)
|
||
SrvGetBaseFileName( Source, &sourceBaseName );
|
||
SrvGetBaseFileName( Target, &targetBaseName );
|
||
|
||
|
||
if ((Source->Length - sourceBaseName.Length) == (Target->Length - targetBaseName.Length)) {
|
||
ULONG i;
|
||
PWCH sptr,tptr;
|
||
|
||
i = Source->Length - sourceBaseName.Length;
|
||
i=i>>1;
|
||
|
||
sptr = &Source->Buffer[i-1];
|
||
tptr = &Target->Buffer[i-1];
|
||
|
||
while ( i > 0) {
|
||
if (*sptr-- != *tptr--) {
|
||
goto no_match;
|
||
}
|
||
i--;
|
||
}
|
||
|
||
// If the names matched, we're set for a quick rename (where the directory is not needed,
|
||
// since they are in the same path)
|
||
fileRenameInformation->RootDirectory = NULL;
|
||
|
||
fileRenameInformation->FileNameLength = targetBaseName.Length;
|
||
|
||
RtlCopyMemory(
|
||
fileRenameInformation->FileName,
|
||
targetBaseName.Buffer,
|
||
targetBaseName.Length
|
||
);
|
||
}
|
||
}
|
||
|
||
no_match:
|
||
|
||
//
|
||
// If we know that the target is a directory, then concatenate the
|
||
// source base name to the end of the target name.
|
||
//
|
||
|
||
if ( *SmbFlags & SMB_TARGET_IS_DIRECTORY ) {
|
||
|
||
SrvGetBaseFileName( Source, &sourceBaseName );
|
||
|
||
s = (PWCH)((PCHAR)fileRenameInformation->FileName +
|
||
fileRenameInformation->FileNameLength);
|
||
|
||
//
|
||
// Only add in a directory separator if the target had some path
|
||
// information. This avoids having a new name like "\NAME", which
|
||
// is illegal with a relative rename (there should be no
|
||
// leading backslash).
|
||
//
|
||
|
||
if ( Target->Length > 0 ) {
|
||
*s++ = DIRECTORY_SEPARATOR_CHAR;
|
||
}
|
||
|
||
RtlCopyMemory( s, sourceBaseName.Buffer, sourceBaseName.Length );
|
||
|
||
fileRenameInformation->FileNameLength +=
|
||
sizeof(WCHAR) + sourceBaseName.Length;
|
||
}
|
||
|
||
//
|
||
// Call NtSetInformationFile to actually rename the file.
|
||
//
|
||
|
||
IF_SMB_DEBUG(FILE_CONTROL2) {
|
||
UNICODE_STRING name;
|
||
name.Length = (USHORT)fileRenameInformation->FileNameLength;
|
||
name.Buffer = fileRenameInformation->FileName;
|
||
SrvPrint2( "Renaming %wZ to %wZ\n", Source, &name );
|
||
}
|
||
switch (InformationLevel) {
|
||
case SMB_NT_RENAME_RENAME_FILE:
|
||
NtInformationLevel = FileRenameInformation;
|
||
|
||
//
|
||
// If we are renaming a substream, we do not supply
|
||
// fileRenameInformation->RootDirectory
|
||
//
|
||
es = fileRenameInformation->FileName +
|
||
fileRenameInformation->FileNameLength / sizeof( WCHAR );
|
||
|
||
for( s = fileRenameInformation->FileName; s < es; s++ ) {
|
||
if( *s == L':' ) {
|
||
fileRenameInformation->RootDirectory = 0;
|
||
break;
|
||
}
|
||
}
|
||
break;
|
||
|
||
case SMB_NT_RENAME_MOVE_CLUSTER_INFO:
|
||
NtInformationLevel = FileMoveClusterInformation;
|
||
break;
|
||
|
||
case SMB_NT_RENAME_SET_LINK_INFO:
|
||
NtInformationLevel = FileLinkInformation;
|
||
break;
|
||
|
||
default:
|
||
ASSERT(FALSE);
|
||
}
|
||
|
||
status = IMPERSONATE( WorkContext );
|
||
|
||
if( NT_SUCCESS( status ) ) {
|
||
status = NtSetInformationFile(
|
||
SourceHandle,
|
||
&ioStatusBlock,
|
||
fileRenameInformation,
|
||
renameBlockSize,
|
||
NtInformationLevel
|
||
);
|
||
|
||
//
|
||
// If the media was changed and we can come up with a new share root handle,
|
||
// then we should retry the operation
|
||
//
|
||
if( SrvRetryDueToDismount( TargetShare, status ) ) {
|
||
|
||
fileRenameInformation->RootDirectory = TargetShare->RootDirectoryHandle;
|
||
|
||
status = NtSetInformationFile(
|
||
SourceHandle,
|
||
&ioStatusBlock,
|
||
fileRenameInformation,
|
||
renameBlockSize,
|
||
NtInformationLevel
|
||
);
|
||
}
|
||
|
||
REVERT( );
|
||
}
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
status = ioStatusBlock.Status;
|
||
SrvRemoveCachedDirectoryName( WorkContext, Source );
|
||
}
|
||
|
||
//
|
||
// If the status was STATUS_OBJECT_NAME_COLLISION then the target
|
||
// already existed as a directory. Unless the target name was
|
||
// supposed to indicate a file or we have already tried used the
|
||
// source name, retry by concatenating the source base name to the
|
||
// target.
|
||
//
|
||
|
||
if ( status == STATUS_OBJECT_NAME_COLLISION &&
|
||
!FailIfTargetIsDirectory &&
|
||
!( *SmbFlags & SMB_TARGET_IS_FILE ) &&
|
||
!( *SmbFlags & SMB_TARGET_IS_DIRECTORY ) ) {
|
||
|
||
IF_SMB_DEBUG(FILE_CONTROL2) {
|
||
SrvPrint0( "Retrying rename with source name.\n" );
|
||
}
|
||
|
||
//
|
||
// Set the flags so that future calls to this routine will do
|
||
// the right thing first time around.
|
||
//
|
||
|
||
*SmbFlags |= SMB_TARGET_IS_DIRECTORY;
|
||
|
||
//
|
||
// Generate the new target name.
|
||
//
|
||
|
||
SrvGetBaseFileName( Source, &sourceBaseName );
|
||
|
||
s = (PWCH)((PCHAR)fileRenameInformation->FileName +
|
||
fileRenameInformation->FileNameLength);
|
||
|
||
*s++ = DIRECTORY_SEPARATOR_CHAR;
|
||
|
||
RtlCopyMemory( s, sourceBaseName.Buffer, sourceBaseName.Length );
|
||
|
||
fileRenameInformation->FileNameLength +=
|
||
sizeof(WCHAR) + sourceBaseName.Length;
|
||
|
||
//
|
||
// Do the rename again. If it fails this time, too bad.
|
||
//
|
||
// *** Note that it may fail because the source and target
|
||
// exist on different volumes. This could potentially
|
||
// cause confusion for DOS clients in the presence of
|
||
// links.
|
||
|
||
IF_SMB_DEBUG(FILE_CONTROL2) {
|
||
UNICODE_STRING name;
|
||
name.Length = (USHORT)fileRenameInformation->FileNameLength;
|
||
name.Buffer = fileRenameInformation->FileName;
|
||
SrvPrint2( "Renaming %wZ to %wZ\n", Source, &name );
|
||
}
|
||
|
||
status = IMPERSONATE( WorkContext );
|
||
|
||
if( NT_SUCCESS( status ) ) {
|
||
status = NtSetInformationFile(
|
||
SourceHandle,
|
||
&ioStatusBlock,
|
||
fileRenameInformation,
|
||
renameBlockSize,
|
||
NtInformationLevel
|
||
);
|
||
|
||
//
|
||
// If the media was changed and we can come up with a new share root handle,
|
||
// then we should retry the operation
|
||
//
|
||
if( SrvRetryDueToDismount( TargetShare, status ) ) {
|
||
|
||
fileRenameInformation->RootDirectory = TargetShare->RootDirectoryHandle;
|
||
|
||
status = NtSetInformationFile(
|
||
SourceHandle,
|
||
&ioStatusBlock,
|
||
fileRenameInformation,
|
||
renameBlockSize,
|
||
NtInformationLevel
|
||
);
|
||
}
|
||
|
||
REVERT( );
|
||
}
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
status = ioStatusBlock.Status;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Release the share root handle if device is removable
|
||
//
|
||
|
||
SrvReleaseShareRootHandle( TargetShare );
|
||
|
||
FREE_HEAP( fileRenameInformation );
|
||
|
||
if ( !NT_SUCCESS(status) ) {
|
||
|
||
IF_DEBUG(ERRORS) {
|
||
SrvPrint1( "DoRename: NtSetInformationFile failed, status = %X\n",
|
||
status );
|
||
}
|
||
}
|
||
|
||
return status;
|
||
|
||
} // DoRename
|