Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

2161 lines
60 KiB

/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
dllcopy.c
Abstract:
This module implements the OS/2 V2.0 DosCopy API
Author:
Therese Stowell (thereses) 21-Jan-1990
ported from cruiser file doscall1\doscopy.c
Revision History:
--*/
#define INCL_OS2V20_ERRORS
#define INCL_OS2V20_FILESYS
#include "os2dll.h"
#include "dllcopy.h"
APIRET
CopyTree(
IN PSTRING SourcePath,
IN HANDLE ParentSourceHandle,
IN PSTRING TargetPath,
IN HANDLE ParentTargetHandle,
IN PVOID IoBuffer,
IN ULONG IoBufferSize,
IN PVOID FindBuffer,
IN ULONG SourceType,
IN ULONG TargetType,
IN ULONG CopyOption
);
APIRET
Od2CopyFile(
IN PSTRING SourcePath,
IN HANDLE ParentSourceHandle,
IN PSTRING TargetPath,
IN HANDLE ParentTargetHandle,
IN PVOID IoBuffer,
IN ULONG IoBufferSize,
IN ULONG SourceType,
IN ULONG TargetType,
IN ULONG CopyOption,
IN BOOLEAN TreeCopy
);
BOOLEAN
CheckNeedEa(
PFILE_FULL_EA_INFORMATION EaList
);
APIRET
CreateTargetFromNul(
IN PSTRING TargetPath,
IN HANDLE ParentTargetHandle,
IN ULONG TargetType,
IN ULONG CopyOption
);
APIRET
GetCopyFileType(
IN PSTRING FileName,
IN OUT PULONG CopyFileType,
IN BOOLEAN Target
)
/*++
Routine Description:
This routine figures out what type a file is for copy purposes.
Arguments:
FileName - file to get type of
CopyFileType - on input, the type returned by Canonicalize
on output, the type of file (COT_DIRECTORY, COT_FILE, etc)
Target - whether the FileName is the target
Return Value:
ERROR_FILE_NOT_FOUND - the source file doesn't exist or the target
file parent dir doesn't exist.
--*/
{
NTSTATUS Status;
HANDLE ObjHandle;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatus;
USHORT FileType;
BOOLEAN Directory;
APIRET RetCode;
USHORT DeviceAttributes;
UNICODE_STRING FileName_U;
//
// if the type returned by Canonicalize is device or
// named pipe, we know that's the right type of the file and we return
// immediately. otherwise, we need to open the file to see if it is
// a device or named pipe that Canonicalize didn't recognize or if
// it's a directory.
//
if (*CopyFileType & FILE_TYPE_DEV) {
*CopyFileType = COT_DEVICE;
return NO_ERROR;
} else if (*CopyFileType & FILE_TYPE_NMPIPE) {
*CopyFileType = COT_OTHER;
return NO_ERROR;
}
else if (*CopyFileType & FILE_TYPE_PIPE) {
*CopyFileType = COT_OTHER;
return NO_ERROR;
}
else if (*CopyFileType & FILE_TYPE_MAILSLOT) {
*CopyFileType = COT_OTHER;
return NO_ERROR;
}
//
// UNICODE conversion -
//
RetCode = Od2MBStringToUnicodeString(
&FileName_U,
FileName,
TRUE);
if (RetCode)
{
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("GetCopyFileType: no memory for Unicode Conversion\n");
}
#endif
return RetCode;
}
InitializeObjectAttributes(&Obja,
&FileName_U,
OBJ_CASE_INSENSITIVE,
NULL,
NULL);
do {
Status = NtOpenFile(&ObjHandle,
Target ?
// (SYNCHRONIZE | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA) :
(SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA) :
(SYNCHRONIZE | FILE_READ_ATTRIBUTES | FILE_READ_DATA),
&Obja,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT
);
} while (RetryCreateOpen(Status, &Obja));
RtlFreeUnicodeString (&FileName_U);
if (!NT_SUCCESS(Status)) {
if (!Target)
return Or2MapNtStatusToOs2Error(Status, ERROR_FILE_NOT_FOUND);
else {
//
// if parent exists, Status should be STATUS_OBJECT_NAME_NOT_FOUND
//
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
*CopyFileType = COT_PARENT;
}
//
// if the parent doesn't exist, we assume that the target is a
// named pipe
//
else {
*CopyFileType = COT_OTHER;
}
return NO_ERROR;
}
}
RetCode = MapFileType(ObjHandle,&Directory,&FileType,&DeviceAttributes);
NtClose(ObjHandle);
if (RetCode) {
return RetCode;
}
if (Directory) {
*CopyFileType = COT_DIRECTORY;
}
else if (FileType == FHT_DISKFILE) {
*CopyFileType = COT_FILE;
}
else if (FileType == FHT_CHRDEV) {
*CopyFileType = COT_DEVICE;
}
return NO_ERROR;
}
APIRET
DosCopy(
IN PSZ OldFileName,
IN PSZ NewFileName,
IN ULONG CopyOption
)
/*++
Routine Description:
This routine copies a file.
Arguments:
OldFileName - The ASCIIZ path name of the source file,
subdirectory, or character device. Wildcards are not
allowed.
NewFileName - The ASCIIZ path name of the target file,
subdirectory, or character device. Wildcards are not
allowed.
CopyOption - A bit map that defines how the DOSCOPY function will be done.
The bit field mapping is shown as follows:
fsOpMode 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
bits R R R R R R R R R R R R R F A E
E Existing Target File Disposition
A Append/Replace Mode
F EA bit; fail copy/discard EA's
R Reserved and must be set to zero.
Return Value:
TBS
--*/
{
APIRET RetCode;
STRING SourceCanonicalName;
STRING TargetCanonicalName;
ULONG SourceType;
ULONG TargetType;
ULONG SourceFlags;
ULONG TargetFlags;
ULONG IoBufferSize;
PVOID IoBuffer;
struct FIND_BUFFER {
FILE_DIRECTORY_INFORMATION DirInfo;
CHAR Name[CCHMAXPATH-1];
} FindBuffer;
PSZ LastComponent, NewTarget;
ULONG TargetPathLen;
BOOLEAN fAppendSlash;
NTSTATUS Status;
//
// Validate opmode
//
if (CopyOption & ~DCPY_ALL) {
return(ERROR_INVALID_PARAMETER);
}
if (RetCode = Od2Canonicalize( OldFileName,
CANONICALIZE_FILE_DEV_OR_PIPE,
&SourceCanonicalName,
NULL,
&SourceFlags,
&SourceType) != NO_ERROR) {
return RetCode;
}
//
// Special handling of <boot-drive>:\config.sys
// opening this file is mapped to the OS/2 SS config.sys
//
if (Od2FileIsConfigSys(&SourceCanonicalName, OPEN_ACCESS_READONLY, &Status))
{
if (!NT_SUCCESS(Status))
{
// failed to init for config.sys
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
return Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED);
}
SourceFlags = 0;
SourceType = FILE_TYPE_FILE;
}
if (SourceFlags & CANONICALIZE_META_CHARS_FOUND) {
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
return ERROR_FILE_NOT_FOUND;
}
if (SourceType & FILE_TYPE_PSDEV) {
//
// NUL device - continue, in order to create the target file
// anyhow, like os/2 does.
//
/*
if (!strcmp(SourceCanonicalName.Buffer, "@0")){
//
// NUL device - just return OK
//
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
return(NO_ERROR);
}
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
return ERROR_ACCESS_DENIED;
*/
if (strcmp(SourceCanonicalName.Buffer, "@0")){
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
return ERROR_ACCESS_DENIED;
}
}
//
// Determine type of source object. We already know if
// the source is a device or named pipe because Canonicalize
// determines it. It is possible that the source is a device or
// named pipe without it being detected by Canonicalize. We also need
// to know whether it's a file or directory. So we open it.
//
if (strcmp(SourceCanonicalName.Buffer, "@0")) {
RetCode = GetCopyFileType(&SourceCanonicalName,
&SourceType,
FALSE);
if ((RetCode == ERROR_FILE_NOT_FOUND) && (SourceType == FILE_TYPE_FILE)) {
RetCode = ERROR_PATH_NOT_FOUND;
}
if (RetCode) {
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
return RetCode;
}
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("oldfilename is %s\n",SourceCanonicalName.Buffer);
DbgPrint("Object type: Source - %u\n", SourceType);
}
#endif
//
// canonicalize destination name
//
if (RetCode = Od2Canonicalize(NewFileName,
CANONICALIZE_FILE_DEV_OR_PIPE,
&TargetCanonicalName,
NULL,
&TargetFlags,
&TargetType) != NO_ERROR) {
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
return RetCode;
}
//
// Special handling of <boot-drive>:\config.sys
// opening this file is mapped to the OS/2 SS config.sys
//
if (Od2FileIsConfigSys(&TargetCanonicalName, OPEN_ACCESS_READWRITE, &Status))
{
if (!NT_SUCCESS(Status))
{
// failed to init for config.sys
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
RtlFreeHeap(Od2Heap,0,TargetCanonicalName.Buffer);
return Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED);
}
TargetFlags = 0;
TargetType = FILE_TYPE_FILE;
}
if (TargetFlags & CANONICALIZE_META_CHARS_FOUND) {
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
RtlFreeHeap(Od2Heap,0,TargetCanonicalName.Buffer);
return ERROR_FILE_NOT_FOUND;
}
if (TargetType & FILE_TYPE_PSDEV) {
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
if (!strcmp(TargetCanonicalName.Buffer, "@0")){
//
// NUL device - just return OK
//
RtlFreeHeap(Od2Heap,0,TargetCanonicalName.Buffer);
return(NO_ERROR);
}
RtlFreeHeap(Od2Heap,0,TargetCanonicalName.Buffer);
return ERROR_ACCESS_DENIED;
}
//
// Determine type of target object. We already know if
// the target is a device or named pipe because Canonicalize
// determines it. It is possible that the target is a device or
// named pipe without it being detected by Canonicalize. We also need
// to know whether it's a file or directory. So we open it.
//
RetCode = GetCopyFileType(&TargetCanonicalName,
&TargetType,
TRUE);
if (RetCode) {
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
RtlFreeHeap(Od2Heap,0,TargetCanonicalName.Buffer);
return RetCode;
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("newfilename is %s\n",TargetCanonicalName.Buffer);
DbgPrint("Object type: Target - %u\n", TargetType);
}
#endif
//
// Perform validation
//
// Error if the source is a directory and the target exists
// but is not a directory.
//
if (SourceType == COT_DIRECTORY
&& TargetType != COT_DIRECTORY
&& TargetType != COT_PARENT) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("ERROR: Source is a directory, target is not.\n") ;
}
#endif
RetCode = ERROR_DIRECTORY;
goto DosCopyAbort_2 ;
}
//
// Error if target is a directory and source is a device;
// e.g. we don't want to create X:\DIR\LPT1.
//
if (SourceType == COT_DEVICE
&& TargetType == COT_DIRECTORY) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("ERROR: Source is a device, target is a directory.\n") ;
}
#endif
RetCode = ERROR_DIRECTORY ;
goto DosCopyAbort_2 ;
}
//
// Error if the target is the same directory as the source or is a
// subdirectory of it.
//
// NOTE: If the source and target refer to the same file, the error
// will occur when trying to open the target for writing after
// the source has been opened deny-write.
//
// WARNING: This test will not catch the scenario where one directory
// can be referred to with two distinct fully-qualified
// pathnames, e.g. \\SERVER\SHARE\DIR and S:\DIR.
//
TargetPathLen = TargetCanonicalName.Length;
if (SourceType == COT_DIRECTORY
&& SourceCanonicalName.Length <= (USHORT)TargetPathLen
&& (strncmp(SourceCanonicalName.Buffer,
TargetCanonicalName.Buffer,
(ULONG)(SourceCanonicalName.Length)) == (int) 0)
&& (SourceCanonicalName.Length == (USHORT)TargetPathLen
|| (TargetCanonicalName.Buffer[SourceCanonicalName.Length] == '\\')
|| (SourceFlags & CANONICALIZE_IS_ROOT_DIRECTORY)!=0)) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("ERROR: Source and target are the same, or target is a subdirectory of the source. \n");
}
#endif
RetCode = ERROR_DIRECTORY ;
goto DosCopyAbort_2 ;
}
//
// Now we're getting close to the real work...
//
// Allocate as big a buffer as possible for EAs and file I/O
//
IoBufferSize = MAX_ALIGNED_EA_LIST_SIZE;
IoBuffer = 0;
Status = NtAllocateVirtualMemory(NtCurrentProcess(),
&IoBuffer,
0,
&IoBufferSize,
MEM_COMMIT,
PAGE_READWRITE
);
if (!NT_SUCCESS(Status)) {
RetCode = ERROR_NOT_ENOUGH_MEMORY;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("ERROR: Could not allocate %u bytes of memory for I/O.\n",
MAX_ALIGNED_EA_LIST_SIZE) ;
}
#endif
goto DosCopyAbort_2 ;
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Allocated %u bytes of memory for I/O.\n", IoBufferSize) ;
}
#endif
//
// If the source is a file and the target is a directory, we need to
// take the filename portion of the source pathname and append it to
// the target pathname, e.g. X:\DIRA\FILE.EXT to Y:\DIRB
//
if ((SourceType == COT_FILE || SourceType == COT_OTHER)
&& TargetType == COT_DIRECTORY) {
//
// Determine filename portion of the source, to be appended to
// the target.
//
LastComponent = FindLastComponent(SourceCanonicalName.Buffer);
//
// A slash will be have to appended if the target is not the root
// directory of a drive.
//
fAppendSlash =(BOOLEAN)((TargetFlags & CANONICALIZE_IS_ROOT_DIRECTORY) == 0);
//
// Check to see if new name will fit in buffer;
// if not, return error
//
if (TargetPathLen+strlen(LastComponent)+fAppendSlash > CCHMAXPATH-1) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("ERROR: Real target pathname is too long:\n\tTarget path=%s\n\tTarget file=%s\n",
TargetCanonicalName.Buffer,LastComponent) ;
}
#endif
RetCode = ERROR_FILENAME_EXCED_RANGE ;
goto DosCopyAbort_3 ;
}
//
// allocate a new heap buffer for the target and copy old target in.
//
NewTarget = RtlAllocateHeap(Od2Heap,0,TargetPathLen+strlen(LastComponent)+fAppendSlash+1);
if (NewTarget == NULL) {
#if DBG
KdPrint(( "OS2: DosCopy, no memory in Od2Heap\n" ));
ASSERT(FALSE);
#endif
RetCode = ERROR_NOT_ENOUGH_MEMORY;
goto DosCopyAbort_3 ;
}
strncpy(NewTarget,TargetCanonicalName.Buffer,TargetPathLen);
//
// Finally, do the append operation
//
if (fAppendSlash) NewTarget[TargetPathLen++] = '\\' ;
strcpy(NewTarget + TargetPathLen,LastComponent) ;
RtlFreeHeap(Od2Heap,0,TargetCanonicalName.Buffer);
RtlInitString(&TargetCanonicalName,NewTarget);
//
// Modify the target type
//
TargetType = COT_FILE ;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("New target pathname: %s\n",TargetCanonicalName.Buffer ) ;
}
#endif
}
//
// NOW LET'S DO THE REAL WORK!
//
if (SourceType == COT_DIRECTORY) {
//
// TREE COPY
//
// Append mode should be DCPY_EXISTING in tree copy in case there is a
// file existing in both the source and target directories.
//
// CopyOption &= ~DCPY_APPEND ;
CopyOption |= DCPY_EXISTING ;
//
// Change target type from parent to directory
//
if (TargetType == COT_PARENT)
TargetType = COT_DIRECTORY ;
RetCode = CopyTree(&SourceCanonicalName,
(HANDLE) NULL,
&TargetCanonicalName,
(HANDLE) NULL,
IoBuffer,
IoBufferSize,
(PVOID) &FindBuffer,
SourceType,
TargetType,
CopyOption) ;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Top-level copy_tree(...) returns %u\n", RetCode) ;
}
#endif
} else {
//
// FILE COPY
//
// Change target type from parent to file
//
if (TargetType == COT_PARENT)
TargetType = COT_FILE ;
//
// if Source is NUL, do not copy, just create target file
//
if (!strcmp(SourceCanonicalName.Buffer, "@0")){
RetCode = CreateTargetFromNul(&TargetCanonicalName,
(HANDLE) NULL,
TargetType,
CopyOption);
}
else {
RetCode = Od2CopyFile(&SourceCanonicalName,
(HANDLE) NULL,
&TargetCanonicalName,
(HANDLE) NULL,
IoBuffer,
IoBufferSize,
SourceType,
TargetType,
CopyOption,
FALSE) ;
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Top-level copy_file(...) returns %u\n", RetCode) ;
}
#endif
}
DosCopyAbort_3:
//
// Free the buffer for file I/O.
//
Status = NtFreeVirtualMemory(NtCurrentProcess(),
&IoBuffer,
&IoBufferSize,
MEM_RELEASE
);
DosCopyAbort_2:
//
// Free the name buffers
//
RtlFreeHeap(Od2Heap,0,SourceCanonicalName.Buffer);
RtlFreeHeap(Od2Heap,0,TargetCanonicalName.Buffer);
//
// Return the error to the caller.
//
return(RetCode);
}
APIRET
DosICopy(
IN PSZ OldFileName,
IN PSZ NewFileName,
IN ULONG CopyOption
)
/*++
Routine Description:
This routine copies a file by calling DosCopy.
Arguments:
OldFileName - The ASCIIZ path name of the source file,
subdirectory, or character device. Wildcards are not
allowed.
NewFileName - The ASCIIZ path name of the target file,
subdirectory, or character device. Wildcards are not
allowed.
CopyOption - A bit map that defines how the DOSCOPY function will be done.
The bit field mapping is shown as follows:
fsOpMode 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
bits R R R R R R R R R R R R R F A E
E Existing Target File Disposition
A Append/Replace Mode
F EA bit; fail copy/discard EA's
R Reserved and must be set to zero.
Return Value:
TBS
--*/
{
return(DosCopy(OldFileName,NewFileName,CopyOption));
}
APIRET
CopyTree(
IN PSTRING SourcePath,
IN HANDLE ParentSourceHandle,
IN PSTRING TargetPath,
IN HANDLE ParentTargetHandle,
IN PVOID IoBuffer,
IN ULONG IoBufferSize,
IN PVOID FindBuffer,
IN ULONG SourceType,
IN ULONG TargetType,
IN ULONG CopyOption
)
/*++
Routine Description:
This routine recursively copies a directory tree. it does the
following:
create destination directory, if it doesn't exist
for each entry in source directory
if (directory)
CopyTree
else
CopyFile
Arguments:
SourcePath - name of source file. if a parent handle is passed in,
this contains only the last component in the path.
ParentSourceHandle - handle to parent directory of source file
TargetPath - name of target file. if a parent handle is passed in,
this contains only the last component in the path.
ParentTargetHandle - handle to parent directory of target file
IoBuffer - buffer to use for setting EAs and copying data.
IoBufferSize - size of buffer. it's large enough to hold all the EAs
for a file.
FindBuffer - buffer to use for getting entries in source directory. it's
large enough to contain one entry.
SourceType - whether source is file, dir, device, named pipe
TargetType - whether target is file, dir, device, named pipe
CopyOption - append, replace, fail if eas not supported
Return Value:
TBS.
--*/
{
APIRET RetCode;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatus;
HANDLE SourceHandle;
HANDLE TargetHandle;
STRING ChildSourceName;
STRING ChildTargetName;
STRING FileName;
UNICODE_STRING FileName_U;
LARGE_INTEGER AllocationSize;
FILE_EA_INFORMATION EaInfo;
FILE_BASIC_INFORMATION BasicInfo;
NTSTATUS Status;
PFILE_DIRECTORY_INFORMATION DirEntry;
UNICODE_STRING SourcePath_U, TargetPath_U;
//
// Initialize return value
//
RetCode = NO_ERROR;
//
// Create the target, if it doesn't exist, with the source's extended
// attributes.
//
// to do this:
// query the source directory's EAs
// NtCreateFile(CREATE_DIRECTORY, open or create, EAs)
// if the createfile fails because EAs aren't supported
// NtCreateFile(no EAs)
//
//
// open the source
//
//
// UNICODE conversion -
//
RetCode = Od2MBStringToUnicodeString(
&SourcePath_U,
SourcePath,
TRUE);
if (RetCode)
{
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("CopyTree: no memory for Unicode Conversion\n");
}
#endif
return RetCode;
}
InitializeObjectAttributes(&Obja,
&SourcePath_U,
OBJ_CASE_INSENSITIVE,
ParentSourceHandle,
NULL);
do {
Status = NtOpenFile(&SourceHandle,
FILE_LIST_DIRECTORY |
FILE_READ_EA |
FILE_READ_ATTRIBUTES |
SYNCHRONIZE,
&Obja,
&IoStatus,
FILE_SHARE_READ,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
);
} while (RetryCreateOpen(Status, &Obja));
RtlFreeUnicodeString (&SourcePath_U);
if (!(NT_SUCCESS(Status))) {
// RetCode = ERROR_PATH_NOT_FOUND;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("CopyTree: Error at NtOpen %lx\n", Status);
}
#endif
return Or2MapNtStatusToOs2Error(Status, ERROR_PATH_NOT_FOUND);
}
//
// since we can't do FILE_OPEN_IF with directories, we first try to open
// the directory. if that fails, we create it.
//
//
// UNICODE conversion -
//
RetCode = Od2MBStringToUnicodeString(
&TargetPath_U,
TargetPath,
TRUE);
if (RetCode)
{
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("CopyTree: no memory for Unicode Conversion\n");
}
#endif
goto CopyTreeAbort_2 ;
}
InitializeObjectAttributes(&Obja,
&TargetPath_U,
OBJ_CASE_INSENSITIVE,
ParentTargetHandle,
NULL);
do {
Status = NtOpenFile(&TargetHandle,
SYNCHRONIZE,
&Obja,
&IoStatus,
FILE_SHARE_READ,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT
);
} while (RetryCreateOpen(Status, &Obja));
if (!(NT_SUCCESS(Status))) {
if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
RetCode = ERROR_FILE_NOT_FOUND;
goto CopyTreeAbort_2 ;
}
else {
do {
Status = NtQueryInformationFile(SourceHandle,
&IoStatus,
&EaInfo,
sizeof (EaInfo),
FileEaInformation);
} while (RetryIO(Status, SourceHandle));
if (!(NT_SUCCESS(Status))) {
RetCode = ERROR_FILE_NOT_FOUND;
goto CopyTreeAbort_2 ;
}
if (EaInfo.EaSize > MAX_ALIGNED_EA_LIST_SIZE)
ASSERT(FALSE);
do {
Status = NtQueryInformationFile(SourceHandle,
&IoStatus,
&BasicInfo,
sizeof (BasicInfo),
FileBasicInformation);
} while (RetryIO(Status, SourceHandle));
if (!(NT_SUCCESS(Status))) {
RetCode = ERROR_FILE_NOT_FOUND;
goto CopyTreeAbort_2 ;
}
do {
Status = NtQueryEaFile(SourceHandle,
&IoStatus,
IoBuffer,
IoBufferSize,
FALSE,
(PVOID) NULL,
0,
(PULONG) NULL,
TRUE
);
} while (RetryIO(Status, SourceHandle));
if (!(NT_SUCCESS(Status)) && Status != STATUS_NO_EAS_ON_FILE) {
RetCode = ERROR_FILE_NOT_FOUND;
goto CopyTreeAbort_2 ;
}
AllocationSize = RtlConvertUlongToLargeInteger(0);
do {
Status = NtCreateFile(&TargetHandle,
SYNCHRONIZE,
&Obja,
&IoStatus,
&AllocationSize,
BasicInfo.FileAttributes,
FILE_SHARE_READ,
FILE_CREATE,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
IoBuffer,
EaInfo.EaSize
);
} while (RetryCreateOpen(Status, &Obja));
if (!(NT_SUCCESS(Status))) {
//
// Make sure the destination supports EAs. If not, fail the copy
// according to the F bit of the FsOpMode parameter and the presence
// of need EAs in the source.
//
if (Status == STATUS_EAS_NOT_SUPPORTED) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Target filesystem does not support EA's\n");
}
#endif
if(CheckNeedEa((PFILE_FULL_EA_INFORMATION) IoBuffer)) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("...and source dir has need EAs; aborting!\n");
}
#endif
RetCode = ERROR_NEED_EAS_FOUND;
goto CopyTreeAbort_2;
}
if(CopyOption & DCPY_EA_FAIL_COPY) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("...and EA_fail_copy bit is set; aborting!\n");
}
#endif
RetCode = ERROR_EAS_NOT_SUPPORTED;
goto CopyTreeAbort_2;
} else {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("...so source EA's will be discarded.\n");
}
#endif
do {
Status = NtCreateFile(&TargetHandle,
SYNCHRONIZE,
&Obja,
&IoStatus,
&AllocationSize,
BasicInfo.FileAttributes,
FILE_SHARE_READ,
FILE_OPEN_IF,
FILE_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
} while (RetryCreateOpen(Status, &Obja));
if (!(NT_SUCCESS(Status))) {
RetCode = Or2MapNtStatusToOs2Error(Status, ERROR_FILE_NOT_FOUND);
goto CopyTreeAbort_2;
}
}
}
else {
RetCode = Or2MapNtStatusToOs2Error(Status, ERROR_FILE_NOT_FOUND);
goto CopyTreeAbort_2;
}
}
}
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Target directory %s opened/created.\n",TargetPath->Buffer);
}
#endif
for (Status = NtQueryDirectoryFile(SourceHandle,
NULL,
NULL,
NULL,
&IoStatus,
FindBuffer,
sizeof (FILE_DIRECTORY_INFORMATION) + CCHMAXPATH - 1,
FileDirectoryInformation,
TRUE,
NULL,
TRUE);
NT_SUCCESS(Status);
Status = NtQueryDirectoryFile(SourceHandle,
NULL,
NULL,
NULL,
&IoStatus,
FindBuffer,
sizeof (FILE_DIRECTORY_INFORMATION) + CCHMAXPATH - 1,
FileDirectoryInformation,
TRUE,
NULL,
FALSE)) {
//
// Skip "." and ".." entries
//
DirEntry = (PFILE_DIRECTORY_INFORMATION) FindBuffer;
//
// What we get back from Nt is Unicode
//
DirEntry->FileName[(DirEntry->FileNameLength)/2] = 0;
RtlInitUnicodeString (&FileName_U,
DirEntry->FileName);
//
// Convert it to Ansi
//
Status = Od2UnicodeStringToMBString(
&FileName,
&FileName_U,
TRUE);
ASSERT(NT_SUCCESS(Status));
if (!NT_SUCCESS(Status)){
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("CopyTree: no memory for Unicode Conversion\n");
}
#endif
RetCode = Or2MapNtStatusToOs2Error(Status, ERROR_NOT_ENOUGH_MEMORY);
goto CopyTreeAbort_2;
}
if (!strcmp(FileName.Buffer,CURRENT_DIRECTORY) ||
!strcmp(FileName.Buffer,PARENT_DIRECTORY))
continue ;
ChildTargetName.Buffer = ChildSourceName.Buffer = FileName.Buffer;
ChildTargetName.Length = ChildSourceName.Length = FileName.Length;
ChildTargetName.MaximumLength = ChildSourceName.MaximumLength =
FileName.MaximumLength + (USHORT)1;
//
// Now make the recursive call. the reason we don't pass the source's
// attributes to CopyTree or CopyFile is we can't guarantee that
// the file/dir won't be deleted and recreated in the middle because
// we don't have an open handle to the object.
//
if (DirEntry->FileAttributes & FILE_DIRECTORY) {
//
// TREE COPY
//
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("About to call copy_tree():\n\tSource=%s\n\tTarget=%s\n",
ChildSourceName.Buffer,ChildTargetName.Buffer) ;
}
#endif
RetCode = CopyTree(&ChildSourceName,
SourceHandle,
&ChildTargetName,
TargetHandle,
IoBuffer,
IoBufferSize,
FindBuffer,
SourceType,
TargetType,
CopyOption) ;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Recursive CopyTree(...) returns %u\n", RetCode) ;
}
#endif
} else {
//
// FILE COPY
//
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("About to call copy_file():\n\tSource=%s\n\tTarget=%s\n",
ChildSourceName.Buffer,ChildTargetName.Buffer) ;
}
#endif
RetCode = Od2CopyFile(&ChildSourceName,
SourceHandle,
&ChildTargetName,
TargetHandle,
IoBuffer,
IoBufferSize,
SourceType,
TargetType,
CopyOption,
TRUE) ;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Recursive copy_file(...) returns %u\n", RetCode) ;
}
#endif
}
if (RetCode)
goto CopyTreeAbort_3 ;
} // End of NtQueryDirectory loop
if (!(NT_SUCCESS(Status)) && (Status != STATUS_NO_MORE_FILES)) {
RetCode = Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED);
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("ERROR: DosFindFirst/Next returns %u\n", RetCode) ;
}
#endif
goto CopyTreeAbort_3 ;
}
else
RetCode = NO_ERROR;
CopyTreeAbort_3:
NtClose(TargetHandle);
CopyTreeAbort_2:
NtClose(SourceHandle);
RtlFreeUnicodeString (&TargetPath_U);
//
// Return the error to the caller.
//
return(RetCode) ;
}
// Notice: the function below takes only raw file EA data, not an FEA2 list
NTSTATUS
Od2SizeFEA2Data(
PFEA2 pFEA2,
PULONG pSize
)
{
ULONG size = 0;
PFEA2 ptr = pFEA2;
try
{
while (ptr->oNextEntryOffset != 0)
ptr = (PFEA2)((PBYTE)ptr + ptr->oNextEntryOffset);
if (ptr->cbName != 0) // Just to protect against empty/bad EA list
{
size = (ULONG)ptr - (ULONG)pFEA2;
size += FIELD_OFFSET(FEA2, szName)
+ ptr->cbValue
+ ptr->cbName + 1;
size = (size + 3) & ~0x3; // align to ULONG
}
else if (ptr != pFEA2)
{
// This is wrong: we have a non-empty EA list and the last entry
// has a .cbName of 0
return STATUS_EA_LIST_INCONSISTENT;
}
} except( EXCEPTION_EXECUTE_HANDLER )
{
return STATUS_EA_LIST_INCONSISTENT;
}
*pSize = size;
return STATUS_SUCCESS;
}
APIRET
Od2CopyFile(
IN PSTRING SourcePath,
IN HANDLE ParentSourceHandle,
IN PSTRING TargetPath,
IN HANDLE ParentTargetHandle,
IN PVOID IoBuffer,
IN ULONG IoBufferSize,
IN ULONG SourceType,
IN ULONG TargetType,
IN ULONG CopyOption,
IN BOOLEAN TreeCopy
)
/*++
Routine Description:
This routine copies or appends from the source file to the target file.
it does the following:
open source
read sources EAs, attributes, size
create/open target using source's info
read data from source and write it to target
Arguments:
SourcePath - name of source file. if a parent handle is passed in,
this contains only the last component in the path.
ParentSourceHandle - handle to parent directory of source file
TargetPath - name of target file. if a parent handle is passed in,
this contains only the last component in the path.
ParentTargetHandle - handle to parent directory of target file
IoBuffer - buffer to use for setting EAs and copying data.
IoBufferSize - size of buffer. it's large enough to hold all the EAs
for a file.
SourceType - whether source is file, dir, device, named pipe
TargetType - whether target is file, dir, device, named pipe
CopyOption - append, replace, fail if eas not supported
TreeCopy - whether this is a tree copy
Return Value:
TBS.
--*/
{
APIRET RetCode;
NTSTATUS Status;
NTSTATUS ReadStatus = STATUS_SUCCESS;
NTSTATUS WriteStatus = STATUS_SUCCESS;
USHORT fsCopyFileFlags = 0 ;
HANDLE SourceHandle;
HANDLE TargetHandle;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatus;
FILE_EA_INFORMATION EaInfo;
FILE_BASIC_INFORMATION SourceBasicInfo;
FILE_BASIC_INFORMATION TargetBasicInfo;
FILE_STANDARD_INFORMATION StandardInfo;
FILE_POSITION_INFORMATION PositionInfo;
FILE_DISPOSITION_INFORMATION DispositionInfo;
FILE_ALLOCATION_INFORMATION AllocationInfo;
FILE_END_OF_FILE_INFORMATION EndOfFileInfo;
LARGE_INTEGER FileOffset;
ULONG CreateDisposition;
ULONG ActionTaken;
ULONG Key;
UNICODE_STRING SourcePath_U, TargetPath_U;
//
// Set TargetPath_U Length to zero, so we know what to do
// at Abort_2:
//
TargetPath_U.Length = 0;
//
// Determine if the source and target are regular files
//
fsCopyFileFlags |= CFF_SOURCE_IS_FILE *
(SourceType != COT_DEVICE
&& SourceType != COT_OTHER) ;
fsCopyFileFlags |= CFF_TARGET_IS_FILE *
(TargetType != COT_DEVICE
&& TargetType != COT_OTHER) ;
//
// based on the append and replace flags, initialize CreateDisposition.
//
if (fsCopyFileFlags & CFF_TARGET_IS_FILE) {
if (CopyOption & DCPY_APPEND) {
CreateDisposition = FILE_OPEN_IF;
}
else if (CopyOption & DCPY_EXISTING) {
CreateDisposition = FILE_OVERWRITE_IF;
}
else {
CreateDisposition = FILE_CREATE;
}
}
//
// If the target is a device or named pipe, just open it.
// NOTE: Since the target cannot be a device in a tree copy, there's
// no danger here of these flags being reset when copy_file()
// is called by copy_tree()
//
else {
CopyOption &= ~(DCPY_EXISTING | DCPY_APPEND);
CreateDisposition = FILE_OPEN;
}
//
// open the source
//
//
// UNICODE conversion -
//
RetCode = Od2MBStringToUnicodeString(
&SourcePath_U,
SourcePath,
TRUE);
if (RetCode)
{
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("CopyFile: no memory for Unicode Conversion\n");
}
#endif
return RetCode;
}
InitializeObjectAttributes(&Obja,
&SourcePath_U,
OBJ_CASE_INSENSITIVE,
ParentSourceHandle,
NULL);
do {
Status = NtOpenFile(&SourceHandle,
FILE_READ_DATA |
FILE_READ_EA |
FILE_READ_ATTRIBUTES |
SYNCHRONIZE,
&Obja,
&IoStatus,
FILE_SHARE_READ,
FILE_SYNCHRONOUS_IO_NONALERT
);
} while (RetryCreateOpen(Status, &Obja));
RtlFreeUnicodeString (&SourcePath_U);
if (!NT_SUCCESS(Status)) {
RetCode = ERROR_PATH_NOT_FOUND;
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("ERROR: NtOpenFile(%s, ...) returns %X\n",
SourcePath,Status) ;
}
#endif
goto CopyFileAbort_1 ;
}
//
// Get EAs, attributes, and filesize.
//
do {
Status = NtQueryInformationFile(SourceHandle,
&IoStatus,
&EaInfo,
sizeof (EaInfo),
FileEaInformation);
} while (RetryIO(Status, SourceHandle));
if (!(NT_SUCCESS(Status))) {
RetCode = ERROR_FILE_NOT_FOUND;
goto CopyFileAbort_2 ;
}
if (EaInfo.EaSize > MAX_ALIGNED_EA_LIST_SIZE)
ASSERT(FALSE);
do {
Status = NtQueryInformationFile(SourceHandle,
&IoStatus,
&StandardInfo,
sizeof (StandardInfo),
FileStandardInformation);
} while (RetryIO(Status, SourceHandle));
if (!(NT_SUCCESS(Status))) {
RetCode = ERROR_FILE_NOT_FOUND;
goto CopyFileAbort_2 ;
}
do {
Status = NtQueryInformationFile(SourceHandle,
&IoStatus,
&SourceBasicInfo,
sizeof (SourceBasicInfo),
FileBasicInformation);
} while (RetryIO(Status, SourceHandle));
if (!(NT_SUCCESS(Status))) {
RetCode = ERROR_FILE_NOT_FOUND;
goto CopyFileAbort_2 ;
}
do {
Status = NtQueryEaFile(SourceHandle,
&IoStatus,
IoBuffer,
IoBufferSize,
FALSE,
(PVOID) NULL,
0,
(PULONG) NULL,
TRUE
);
} while (RetryIO(Status, SourceHandle));
if (!(NT_SUCCESS(Status)) && Status != STATUS_NO_EAS_ON_FILE) {
RetCode = ERROR_FILE_NOT_FOUND;
goto CopyFileAbort_2 ;
}
//
// create/open target
//
//
// UNICODE conversion -
//
RetCode = Od2MBStringToUnicodeString(
&TargetPath_U,
TargetPath,
TRUE);
if (RetCode)
{
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("CopyFile: no memory for Unicode Conversion\n");
}
#endif
return RetCode;
}
InitializeObjectAttributes(&Obja,
&TargetPath_U,
OBJ_CASE_INSENSITIVE,
ParentTargetHandle,
NULL);
do {
// BUGBUG - Oct. 11 92: Workaround for LAN bug which returns a size of
// 4 for EAs of files w/o EAs (local yields 0). NtCreateFile
// failed with such an EA size (even though the .CbList of
// IoBuffer is 0).
if (EaInfo.EaSize <= 4) // 4 and below means no EAs
Status = NtCreateFile(&TargetHandle,
FILE_WRITE_ATTRIBUTES | DELETE | FILE_READ_ATTRIBUTES |
FILE_WRITE_DATA | FILE_ADD_SUBDIRECTORY | SYNCHRONIZE,
&Obja,
&IoStatus,
&StandardInfo.EndOfFile,
SourceBasicInfo.FileAttributes,
FILE_SHARE_READ,
CreateDisposition,
FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
else
{
ULONG sz;
// BUGBUG - Oct. 11 92: Workaround for NT bug where NtQueryInformationFile
// returns a wrong size for the file EA's
Status = Od2SizeFEA2Data(IoBuffer, &sz);
if (Status == STATUS_SUCCESS)
Status = NtCreateFile(&TargetHandle,
FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | FILE_READ_ATTRIBUTES |
FILE_WRITE_DATA | FILE_ADD_SUBDIRECTORY | SYNCHRONIZE,
&Obja,
&IoStatus,
&StandardInfo.EndOfFile,
SourceBasicInfo.FileAttributes,
FILE_SHARE_READ,
CreateDisposition,
FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT,
IoBuffer,
sz
);
}
} while (RetryCreateOpen(Status, &Obja));
if (!(NT_SUCCESS(Status))) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtCreateFile for target returned %lx\n", Status) ;
}
#endif
//
// Make sure the destination supports EAs. If not, fail the copy
// according to the F bit of the FsOpMode parameter and the presence
// of need EAs in the source.
//
if (Status == STATUS_EAS_NOT_SUPPORTED) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("Target filesystem does not support EA's\n");
}
#endif
if(CheckNeedEa((PFILE_FULL_EA_INFORMATION) IoBuffer)) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("...and source dir has need EAs; aborting!\n");
}
#endif
RetCode = ERROR_NEED_EAS_FOUND;
goto CopyFileAbort_2;
}
if(CopyOption & DCPY_EA_FAIL_COPY) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("...and EA_fail_copy bit is set; aborting!\n");
}
#endif
RetCode = ERROR_EAS_NOT_SUPPORTED;
goto CopyFileAbort_2;
}
else {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("...so source EA's will be discarded.\n");
}
#endif
do {
Status = NtCreateFile(&TargetHandle,
FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES | DELETE | FILE_READ_ATTRIBUTES |
FILE_WRITE_DATA | FILE_ADD_SUBDIRECTORY | SYNCHRONIZE,
&Obja,
&IoStatus,
&StandardInfo.EndOfFile,
SourceBasicInfo.FileAttributes,
FILE_SHARE_READ,
CreateDisposition | FILE_SYNCHRONOUS_IO_NONALERT,
FILE_SEQUENTIAL_ONLY,
NULL,
0
);
} while (RetryCreateOpen(Status, &Obja));
if (!(NT_SUCCESS(Status))) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtCreateFile for target returned %X\n", Status) ;
}
#endif
RetCode = Or2MapNtStatusToOs2Error(Status, ERROR_FILE_NOT_FOUND);
goto CopyFileAbort_2;
}
}
}
else {
//
// if this is a tree copy, E=0 and A=0, and the open failed because
// the file existed, we return no_error.
//
if (Status == STATUS_ACCESS_DENIED && TreeCopy &&
!(CopyOption & (DCPY_APPEND | DCPY_EXISTING))) {
RetCode = NO_ERROR;
}
else {
if (Status == STATUS_OBJECT_NAME_COLLISION) {
RetCode = ERROR_ACCESS_DENIED;
}
else {
RetCode = Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED);
}
}
goto CopyFileAbort_2;
}
}
//
// if file existed (action == opened) and replace and append bits are off,
// we fail the operation.
//
ActionTaken = IoStatus.Information;
if ((ActionTaken == FILE_OPENED) &&
(!(CopyOption & (DCPY_EXISTING | DCPY_APPEND)))) {
RetCode = ERROR_ACCESS_DENIED;
goto CopyFileAbort_3;
}
//
// If target was opened and we're in append mode, save the target's
// original size and time. then set target file pointer to the end of
// the file.
//
if ((IoStatus.Information == FILE_OPENED) && (CopyOption & DCPY_APPEND)) {
do {
Status = NtQueryInformationFile(TargetHandle,
&IoStatus,
&TargetBasicInfo,
sizeof (TargetBasicInfo),
FileBasicInformation);
} while (RetryIO(Status, TargetHandle));
if (!(NT_SUCCESS(Status))) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtQueryInformationFile(BasicInfo) for target returned %X\n", Status) ;
}
#endif
RetCode = ERROR_FILE_NOT_FOUND;
goto CopyFileAbort_3 ;
}
do {
Status = NtQueryInformationFile(TargetHandle,
&IoStatus,
&StandardInfo,
sizeof (StandardInfo),
FileStandardInformation);
} while (RetryIO(Status, TargetHandle));
if (!(NT_SUCCESS(Status))) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtQueryInformationFile(StandardInfo) for target returned %X\n", Status) ;
}
#endif
RetCode = ERROR_FILE_NOT_FOUND;
goto CopyFileAbort_3 ;
}
PositionInfo.CurrentByteOffset = StandardInfo.EndOfFile;
do {
Status = NtSetInformationFile(TargetHandle,
&IoStatus,
&PositionInfo,
sizeof (PositionInfo),
FilePositionInformation);
} while (RetryIO(Status, TargetHandle));
if (!(NT_SUCCESS(Status))) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtSetInformationFile(PositionInfo) for target returned %X\n", Status) ;
}
#endif
RetCode = Or2MapNtStatusToOs2Error(Status, ERROR_FILE_NOT_FOUND);
goto CopyFileAbort_3;
}
}
//
// copy data
// BUGBUG when devices and named pipes are added, this loop will have
// to change.
//
Key = (ULONG) Od2Process->Pib.ProcessId;
FileOffset = RtlConvertLongToLargeInteger(FILE_USE_FILE_POINTER_POSITION);
while (TRUE) {
do {
ReadStatus = NtReadFile(SourceHandle,
(HANDLE) NULL,
(PIO_APC_ROUTINE) NULL,
(PVOID) NULL,
&IoStatus,
IoBuffer,
IoBufferSize,
&FileOffset,
&Key
);
} while (RetryIO(Status, SourceHandle));
if ((!NT_SUCCESS(ReadStatus)) && (ReadStatus != STATUS_END_OF_FILE)) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtReadFile returned %X\n", ReadStatus) ;
}
#endif
RetCode = Or2MapNtStatusToOs2Error(ReadStatus, ERROR_ACCESS_DENIED);
goto CopyFileAbort_5;
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtReadFile: Read %u bytes from source file\n", IoStatus.Information) ;
}
#endif
if (ReadStatus == STATUS_END_OF_FILE) {
RetCode = NO_ERROR;
break;
}
do {
WriteStatus = NtWriteFile(TargetHandle,
(HANDLE) NULL,
(PIO_APC_ROUTINE) NULL,
(PVOID) NULL,
&IoStatus,
IoBuffer,
IoStatus.Information,
&FileOffset,
&Key
);
} while (RetryIO(Status, TargetHandle));
if (!NT_SUCCESS(WriteStatus)) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtWriteFile returned %X\n", WriteStatus) ;
}
#endif
RetCode = Or2MapNtStatusToOs2Error(WriteStatus, ERROR_ACCESS_DENIED);
break;
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtWriteFile: wrote %u bytes to target source file\n", IoStatus.Information) ;
}
#endif
}
#if DBG
IF_OD2_DEBUG( FILESYS ) {
if (ReadStatus) DbgPrint("ERROR: NtReadFile(...) returns %X\n", ReadStatus) ;
if (WriteStatus) DbgPrint("ERROR: NtWriteFile(...) returns %X\n", WriteStatus) ;
}
#endif
if (RetCode)
goto CopyFileAbort_5 ;
//
// if target was created or replaced, set its time to that of the source.
//
if ((ActionTaken == FILE_CREATED) || (ActionTaken == FILE_SUPERSEDED)) {
do {
Status = NtSetInformationFile(TargetHandle,
&IoStatus,
&SourceBasicInfo,
sizeof (SourceBasicInfo),
FileBasicInformation);
} while (RetryIO(Status, TargetHandle));
if (!NT_SUCCESS(Status)) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("NtSetInformationFile(BasicInfo) for target returned %X\n", Status) ;
}
#endif
RetCode = Or2MapNtStatusToOs2Error(Status, ERROR_ACCESS_DENIED);
goto CopyFileAbort_5;
}
}
CopyFileAbort_5:
//
// With append mode, resize target file and reset its dates on error
//
if (RetCode && (CopyOption & DCPY_APPEND)) {
//
// Ignore errors, since we can't do any recovery
//
EndOfFileInfo.EndOfFile = StandardInfo.EndOfFile;
do {
Status = NtSetInformationFile(TargetHandle,
&IoStatus,
&EndOfFileInfo,
sizeof (EndOfFileInfo),
FileEndOfFileInformation
);
} while (RetryIO(Status, TargetHandle));
AllocationInfo.AllocationSize = StandardInfo.EndOfFile;
do {
Status = NtSetInformationFile(TargetHandle,
&IoStatus,
&AllocationInfo,
sizeof (AllocationInfo),
FileAllocationInformation
);
} while (RetryIO(Status, TargetHandle));
do {
Status = NtSetInformationFile(TargetHandle,
&IoStatus,
&TargetBasicInfo,
sizeof (TargetBasicInfo),
FileBasicInformation
);
} while (RetryIO(Status, TargetHandle));
}
CopyFileAbort_3:
//
// With create/replace mode: delete target file on error
//
if (RetCode) {
if ((ActionTaken == FILE_CREATED) || (ActionTaken == FILE_SUPERSEDED)) {
//
// Don't check for error, since we can't do any recovery
//
DispositionInfo.DeleteFile = TRUE;
do {
Status = NtSetInformationFile(TargetHandle,
&IoStatus,
&DispositionInfo,
sizeof (DispositionInfo),
FileDispositionInformation);
} while (RetryIO(Status, TargetHandle));
}
}
//
// Close the target file
//
NtClose(TargetHandle) ;
CopyFileAbort_2:
//
// Close the source file
//
// Free Unicode string
//
if (TargetPath_U.Length > 0)
RtlFreeUnicodeString (&TargetPath_U);
NtClose(SourceHandle);
CopyFileAbort_1:
//
// Return the error to the caller.
//
return(RetCode) ;
}
BOOLEAN
CheckNeedEa(
PFILE_FULL_EA_INFORMATION EaList
)
/*++
Routine Description:
This routine looks for a NEED EA in an EA list
Arguments:
EaList - NT format EA list
Return Value:
FALSE if EA list has no NEED EAs.
TRUE if any of the EAs in the list are NEED EAs.
--*/
{
while (TRUE) {
if(EaList->Flags & FILE_NEED_EA) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("This EA has needea set; returning 1\n");
}
#endif
return(TRUE);
}
if (EaList->NextEntryOffset == 0L) {
break;
}
EaList = (PFILE_FULL_EA_INFORMATION) ((CHAR *) EaList + EaList->NextEntryOffset);
}
return FALSE;
}
APIRET
CreateTargetFromNul(
IN PSTRING TargetPath,
IN HANDLE ParentTargetHandle,
IN ULONG TargetType,
IN ULONG CopyOption
)
/*++
Routine Description:
This routine create/open a target file.
Arguments:
TargetPath - name of target file. if a parent handle is passed in,
this contains only the last component in the path.
ParentTargetHandle - handle to parent directory of target file
TargetType - whether target is file, dir, device, named pipe
CopyOption - append, replace, fail if eas not supported
Return Value:
TBS.
--*/
{
APIRET RetCode;
NTSTATUS Status;
USHORT fsCopyFileFlags = 0 ;
HANDLE TargetHandle;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatus;
ULONG CreateDisposition;
UNICODE_STRING TargetPath_U;
fsCopyFileFlags |= CFF_TARGET_IS_FILE *
(TargetType != COT_DEVICE
&& TargetType != COT_OTHER) ;
//
// based on the append and replace flags, initialize CreateDisposition.
//
if (fsCopyFileFlags & CFF_TARGET_IS_FILE) {
if (CopyOption & DCPY_APPEND) {
CreateDisposition = FILE_OPEN_IF;
}
else if (CopyOption & DCPY_EXISTING) {
CreateDisposition = FILE_OVERWRITE_IF;
}
else {
CreateDisposition = FILE_CREATE;
}
}
//
// If the target is a device or named pipe, just open it.
// NOTE: Since the target cannot be a device in a tree copy, there's
// no danger here of these flags being reset when copy_file()
// is called by copy_tree()
//
else {
CopyOption &= ~(DCPY_EXISTING | DCPY_APPEND);
CreateDisposition = FILE_OPEN;
}
//
// create/open target
//
//
// UNICODE conversion -
//
RetCode = Od2MBStringToUnicodeString(
&TargetPath_U,
TargetPath,
TRUE);
if (RetCode)
{
#if DBG
IF_OD2_DEBUG( FILESYS )
{
DbgPrint("CreateTargetFromNul: no memory for Unicode Conversion\n");
}
#endif
return RetCode;
}
InitializeObjectAttributes(&Obja,
&TargetPath_U,
OBJ_CASE_INSENSITIVE,
ParentTargetHandle,
NULL);
do {
Status = NtCreateFile(&TargetHandle,
FILE_WRITE_ATTRIBUTES | DELETE | FILE_READ_ATTRIBUTES |
FILE_WRITE_DATA | FILE_ADD_SUBDIRECTORY | SYNCHRONIZE,
&Obja,
&IoStatus,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
CreateDisposition,
FILE_SEQUENTIAL_ONLY | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
} while (RetryCreateOpen(Status, &Obja));
if (!(NT_SUCCESS(Status))) {
#if DBG
IF_OD2_DEBUG( FILESYS ) {
DbgPrint("CreateTargetFromNul: NtCreateFile for target returned %lx\n", Status) ;
}
#endif
}
//
// Close the target file
//
NtClose(TargetHandle) ;
//
// Free Unicode string
//
if (TargetPath_U.Length > 0)
RtlFreeUnicodeString (&TargetPath_U);
//
// Return the error to the caller.
//
return(RetCode) ;
}