2020-09-30 17:12:29 +02:00

1780 lines
67 KiB
C

/*++
Copyright (c) 1991 Microsoft Corporation & Maynard Electornics
Module Name:
backops.c
Abstract:
This module implements Win32 Backup APIs
Author:
Steve DeVos (@Maynard) 2 March, 1992 15:38:24
Revision History:
--*/
#include <basedll.h>
#define MAX_STREAM_NAME_LENG 512
#define NAMELESS_HEADER_SIZE 20
#define BKUP_DONE 0
// temp
typedef struct {
WIN32_STREAM_ID head ; // stream header describing current stream
WCHAR name_buf[ MAX_STREAM_NAME_LENG ]; // name buffer for stream header
DWORD stream_start ; // TRUE if start of new stream
DWORD multi_stream_type ;// TRUE if stream type contains more than 1 stream header
DWORD access_error ; // TRUE if access to a stream was denied
BOOL buffer_ready ; // TRUE if internal buffer is set up.
DWORD stream_index ; // index into the stream ID table
LARGE_INTEGER sub_stream_offset ;// offset in current stream
DWORD header_size ; // size of stream header
DWORD buffer_size ; // size of attached data buffer
HANDLE alt_stream_hand ; // Handle to alternate data stream
DWORD buffer_offset ; // Current offset into data buffer.
BYTE *buffer ; // pointer to allocated data buffer.
} CONTEXT_CONTROL_BLK, *CONTEXT_PTR ;
#define MIN_BUFFER_SIZE 1024
int mwStreamList[] = {
BACKUP_SECURITY_DATA,
BACKUP_DATA,
BACKUP_EA_DATA,
BACKUP_ALTERNATE_DATA,
BKUP_DONE } ;
static void CleanUpContext( LPVOID *lpContext ) ;
BOOL
WINAPI
BackupRead(
HANDLE hFile,
LPBYTE lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
BOOL bAbort,
BOOL bProcessSecurity,
LPVOID *lpContext
)
/*++
Routine Description:
Data can be Backed up from an object using BackupRead.
This API is used to read data from an object. After the
read completes, the file pointer is adjusted by the number of bytes
actually read. A return value of TRUE coupled with a bytes read of
0 indicates that end of file has been reached.
Arguments:
hFile - Supplies an open handle to a file that is to be read. The
file handle must have been created with GENERIC_READ access.
lpBuffer - Supplies the address of a buffer to receive the data read
from the file.
nNumberOfBytesToRead - Supplies the number of bytes to read from the
file.
lpNumberOfBytesRead - Returns the number of bytes read by this call.
This parameter is always set to 0 before doing any IO or error
checking.
bAbort - If TRUE, then all resources associated with the context will
be released.
bProcessSecurity - If TRUE, then the NTFS ACL data will be read.
If FALSE, then the ACL stream will be skipped.
lpContext - Points to a buffer pointer setup and maintained by
BackupRead.
Return Value:
TRUE - The operation was successul.
FALSE - The operation failed. Extended error status is available
using GetLastError.
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
CONTEXT_PTR ccb ;
LARGE_INTEGER fsize ;
DWORD nBytesToReadThisPass ;
DWORD nBytesReadThisPass ;
BOOL ret_val = FALSE ;
ccb = *lpContext ;
if ( bAbort ) {
if ( ccb != NULL ) {
CleanUpContext( lpContext ) ;
}
return TRUE ;
}
*lpNumberOfBytesRead = 0;
if ( ( ccb == INVALID_HANDLE_VALUE ) || ( nNumberOfBytesToRead == 0 ) ) {
return TRUE ;
}
if ( ( ccb != NULL ) &&
( mwStreamList[ccb->stream_index] == BKUP_DONE ) ) {
CleanUpContext( lpContext ) ;
return TRUE ;
}
//
// Allocate our Context Control Block on first call.
//
if ( ccb == NULL ) {
// allocate and set up a Context Control Block (ccb)
ccb = (CONTEXT_PTR)RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( BACKUP_TAG ),
sizeof(CONTEXT_CONTROL_BLK) ) ;
if ( ccb != NULL ) {
RtlZeroMemory( ccb, sizeof( CONTEXT_CONTROL_BLK ) ) ;
ccb->buffer = (BYTE *)RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( BACKUP_TAG ),
MIN_BUFFER_SIZE ) ;
if ( ccb->buffer == NULL ) {
CleanUpContext( lpContext ) ;
return FALSE ;
}
ccb->buffer_size = MIN_BUFFER_SIZE ;
ccb->stream_start = TRUE ;
}
}
if ( ccb != NULL ) {
do {
*lpContext = ccb ;
ret_val = TRUE ;
nBytesToReadThisPass = nNumberOfBytesToRead ;
if ( ccb->stream_start ) {
ccb->head.Size.LowPart = 0 ;
ccb->head.Size.HighPart = 0 ;
ccb->sub_stream_offset.LowPart = 0 ;
ccb->sub_stream_offset.HighPart = 0 ;
}
switch( mwStreamList[ ccb->stream_index ] ) {
case BACKUP_DATA:
ccb->multi_stream_type = FALSE ;
if ( ccb->stream_start ) {
FILE_STANDARD_INFORMATION file_info ;
RtlZeroMemory( &file_info, sizeof( file_info ) ) ;
Status = NtQueryInformationFile( hFile,
&IoStatusBlock,
&file_info,
sizeof(file_info),
FileStandardInformation ) ;
if ( !NT_SUCCESS( Status ) || file_info.Directory ) {
break ;
}
fsize.LowPart = GetFileSize( hFile, (LPDWORD)&(fsize.HighPart) ) ;
if( ( fsize.LowPart == 0 ) && ( fsize.HighPart == 0 ) ) {
break ;
}
if( ( fsize.LowPart == 0xffffffff ) && ( GetLastError() != NO_ERROR ) ) {
ret_val = FALSE ;
break;
}
ccb->head.Size = fsize ;
ccb->head.dwStreamId = mwStreamList[ ccb->stream_index ] ;
ccb->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE ;
ccb->head.dwStreamNameSize = 0 ;
ccb->header_size = NAMELESS_HEADER_SIZE ;
ccb->stream_start = FALSE ;
fsize.HighPart = 0 ;
fsize.LowPart = 0 ;
SetFilePointer( hFile, fsize.LowPart, &fsize.HighPart, FILE_BEGIN ) ;
} else if ( ( ccb->sub_stream_offset.HighPart != 0 ) ||
( ccb->sub_stream_offset.LowPart >= ccb->header_size ) ) {
fsize.HighPart = 0;
fsize.LowPart = ccb->header_size ;
fsize.QuadPart += ccb->head.Size.QuadPart ;
fsize.QuadPart -= ccb->sub_stream_offset.QuadPart ;
if ( fsize.HighPart != 0 ) {
nBytesToReadThisPass = nNumberOfBytesToRead ;
} else {
nBytesToReadThisPass = min( nNumberOfBytesToRead,
fsize.LowPart ) ;
}
Status = ReadFile(
hFile,
lpBuffer,
nBytesToReadThisPass,
&nBytesReadThisPass,
NULL ) ;
if ( nBytesReadThisPass ) {
*lpNumberOfBytesRead += nBytesReadThisPass ;
nNumberOfBytesToRead -= nBytesReadThisPass ;
fsize.LowPart = nBytesReadThisPass ;
fsize.HighPart = 0 ;
ccb->sub_stream_offset.QuadPart += fsize.QuadPart ;
lpBuffer += nBytesReadThisPass ;
}
if ( !Status ) {
ret_val = FALSE ;
} else if ( ( nBytesReadThisPass == 0 ) &&
( nBytesToReadThisPass != 0 ) ) {
ret_val = FALSE ;
SetLastError( ERROR_IO_DEVICE ) ;
}
}
break ;
case BACKUP_ALTERNATE_DATA :
// ALT_DATA is Macintosh stream data and other data streams.
Status = 0 ;
IoStatusBlock.Information = 1 ;
if ( ccb->stream_start ) {
FILE_STREAM_INFORMATION *stream_info ;
// allocate a buffer big enough to hold all the necessary data.
while( !ccb->buffer_ready ) {
if ( ccb->buffer != NULL ) {
Status = NtQueryInformationFile( hFile,
&IoStatusBlock,
ccb->buffer,
ccb->buffer_size,
FileStreamInformation ) ;
} else {
break ;
}
if ( !Status ) {
ccb->buffer_ready = TRUE ;
ccb->buffer_offset = 0 ;
} else if ( ( Status == STATUS_BUFFER_OVERFLOW ) ||
( Status == STATUS_BUFFER_TOO_SMALL ) ) {
BYTE *temp ;
ccb->buffer_size *= 2 ;
temp = (BYTE *)RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( BACKUP_TAG ),
ccb->buffer_size ) ;
RtlFreeHeap(
RtlProcessHeap(),
0,
ccb->buffer ) ;
ccb->buffer = temp ;
} else {
break ;
}
}
if ( !NT_SUCCESS( Status ) ||
(ccb->buffer == NULL) ||
(IoStatusBlock.Information == 0) ) {
// since there is no alternate streams, proceed to next stream type
ccb->multi_stream_type = FALSE ;
break ;
}
ccb->alt_stream_hand = NULL ;
ccb->stream_start = FALSE ;
stream_info = (FILE_STREAM_INFORMATION *)(&ccb->buffer[ccb->buffer_offset]) ;
if ( stream_info->StreamName[1] == ':' ) {
if ( stream_info->NextEntryOffset == 0 ) {
ccb->multi_stream_type = FALSE ;
break ;
}
ccb->buffer_offset += stream_info->NextEntryOffset ;
}
ccb->head.Size.LowPart = 1 ;
} else {
if ( ccb->alt_stream_hand == NULL ) {
OBJECT_ATTRIBUTES Obja ;
FILE_STREAM_INFORMATION *stream_info ;
UNICODE_STRING fname ;
ccb->head.Size.LowPart = 0 ;
ccb->head.Size.HighPart = 0 ;
ccb->sub_stream_offset.LowPart = 0 ;
ccb->sub_stream_offset.HighPart = 0 ;
stream_info = (FILE_STREAM_INFORMATION *)(&ccb->buffer[ccb->buffer_offset]) ;
fname.Length = (USHORT)stream_info->StreamNameLength ;
fname.MaximumLength = (USHORT)stream_info->StreamNameLength ;
fname.Buffer = stream_info->StreamName ;
InitializeObjectAttributes(
&Obja,
&fname,
OBJ_CASE_INSENSITIVE,
hFile,
NULL ) ;
Status = NtOpenFile(
&ccb->alt_stream_hand,
FILE_GENERIC_READ,
&Obja,
&IoStatusBlock,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT ) ;
if ( !NT_SUCCESS( Status ) ) {
ccb->buffer_offset += stream_info->NextEntryOffset ;
if ( stream_info->NextEntryOffset == 0 ) {
ccb->multi_stream_type = FALSE ;
} else {
ccb->head.Size.LowPart = 1 ;
}
break ;
}
ccb->head.dwStreamId = mwStreamList[ ccb->stream_index ] ;
ccb->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE ;
ccb->head.dwStreamNameSize = stream_info->StreamNameLength ;
ccb->header_size = NAMELESS_HEADER_SIZE +
stream_info->StreamNameLength ;
RtlCopyMemory( ccb->head.cStreamName, stream_info->StreamName,
stream_info->StreamNameLength ) ;
ccb->head.Size.LowPart = GetFileSize( ccb->alt_stream_hand,
(LPDWORD)(&ccb->head.Size.HighPart)) ;
if ( stream_info->NextEntryOffset == 0 ) {
ccb->multi_stream_type = FALSE ;
} else {
ccb->buffer_offset += stream_info->NextEntryOffset ;
ccb->multi_stream_type = TRUE ;
}
} else if ( ( ccb->sub_stream_offset.HighPart != 0 ) ||
( ccb->sub_stream_offset.LowPart >= ccb->header_size ) ) {
if ( ( ccb->sub_stream_offset.LowPart == ccb->header_size ) &&
( ccb->sub_stream_offset.HighPart == 0 ) ) {
/* if we cannot lock all the records then return an error */
if ( !LockFile( ccb->alt_stream_hand,
0,
0,
0xFFFFFFFF,
0xFFFFFFFF ) ) {
ret_val = FALSE ;
// ret_val = GetLastError() ;
break ;
}
}
fsize.LowPart = ccb->header_size ;
fsize.HighPart = 0 ;
fsize.QuadPart += ccb->head.Size.QuadPart ;
fsize.QuadPart -= ccb->sub_stream_offset.QuadPart ;
if ( fsize.HighPart != 0 ) {
nBytesToReadThisPass = nNumberOfBytesToRead ;
} else {
nBytesToReadThisPass = min( nNumberOfBytesToRead,
fsize.LowPart ) ;
}
Status = ReadFile(
ccb->alt_stream_hand,
lpBuffer,
nBytesToReadThisPass,
&nBytesReadThisPass,
NULL ) ;
if ( nBytesReadThisPass ) {
*lpNumberOfBytesRead += nBytesReadThisPass ;
nNumberOfBytesToRead -= nBytesReadThisPass ;
fsize.LowPart = nBytesReadThisPass ;
fsize.HighPart = 0 ;
ccb->sub_stream_offset.QuadPart += fsize.QuadPart ;
lpBuffer += nBytesReadThisPass ;
}
if ( !Status ) {
ret_val = FALSE ;
}
}
}
break ;
case BACKUP_EA_DATA:
ccb->multi_stream_type = FALSE ;
if ( ccb->stream_start ) {
Status = NtQueryEaFile( hFile,
&IoStatusBlock,
ccb->buffer,
ccb->buffer_size,
FALSE,
NULL,
0,
0,
(BOOLEAN)TRUE ) ;
if ( NT_SUCCESS( Status ) ) {
ccb->buffer_ready = TRUE ;
} else if ( Status == STATUS_NO_EAS_ON_FILE ) {
break ;
} else if ( ( Status != STATUS_BUFFER_OVERFLOW ) &&
( Status != STATUS_BUFFER_TOO_SMALL ) ) {
break ;
} else {
BYTE *temp ;
FILE_EA_INFORMATION ea_info;
Status = NtQueryInformationFile( hFile,
&IoStatusBlock,
&ea_info,
sizeof( ea_info ),
FileEaInformation ) ;
if ( !NT_SUCCESS( Status ) ) {
break ;
}
ccb->buffer_size = ((ea_info.EaSize*5)/4) ;
temp = (BYTE *)RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( BACKUP_TAG ),
ccb->buffer_size ) ;
RtlFreeHeap(
RtlProcessHeap(),
0,
ccb->buffer ) ;
ccb->buffer = temp ;
if ( ccb->buffer == NULL ) {
ccb->buffer_size = 0 ;
ccb->access_error = TRUE ;
break ;
}
}
if ( !ccb->buffer_ready ) {
fsize.LowPart = ccb->buffer_size,
Status = NtQueryEaFile( hFile,
&IoStatusBlock,
ccb->buffer,
fsize.LowPart,
FALSE,
NULL,
0,
0,
(BOOLEAN)TRUE ) ;
}
if ( !NT_SUCCESS( Status ) ) {
break ;
}
ccb->head.dwStreamId = mwStreamList[ ccb->stream_index ] ;
ccb->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE ;
ccb->head.dwStreamNameSize = 0 ;
ccb->header_size = NAMELESS_HEADER_SIZE ;
ccb->head.Size.HighPart = 0 ;
ccb->head.Size.LowPart = IoStatusBlock.Information;
ccb->stream_start = FALSE ;
} else if ( ( ccb->sub_stream_offset.HighPart != 0 ) ||
( ccb->sub_stream_offset.LowPart >= ccb->header_size ) ) {
fsize.LowPart = ccb->header_size ;
fsize.HighPart = 0 ;
fsize.QuadPart += ccb->head.Size.QuadPart ;
fsize.QuadPart -= ccb->sub_stream_offset.QuadPart ;
if ( fsize.HighPart != 0 ) {
nBytesToReadThisPass = nNumberOfBytesToRead ;
} else {
nBytesToReadThisPass = min( nNumberOfBytesToRead,
fsize.LowPart ) ;
}
fsize.LowPart = ccb->sub_stream_offset.LowPart - ccb->header_size ;
RtlCopyMemory( lpBuffer, ccb->buffer + fsize.LowPart,
nBytesToReadThisPass ) ;
fsize.LowPart = nBytesToReadThisPass ;
fsize.HighPart = 0 ;
ccb->sub_stream_offset.QuadPart += fsize.QuadPart ;
*lpNumberOfBytesRead += nBytesToReadThisPass ;
nNumberOfBytesToRead -= nBytesToReadThisPass ;
lpBuffer += nBytesToReadThisPass ;
}
break ;
case BACKUP_SECURITY_DATA:
ccb->multi_stream_type = FALSE ;
if ( !bProcessSecurity ) {
break ; // this will proced to next stream
}
if ( ccb->stream_start ) {
DWORD size_needed ;
DWORD last_error = 0;
do {
if ( ccb->buffer == NULL ) {
break ;
}
RtlZeroMemory( ccb->buffer, ccb->buffer_size ) ;
// First try to read all the security data
Status = NtQuerySecurityObject(
hFile,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION |
SACL_SECURITY_INFORMATION,
ccb->buffer,
ccb->buffer_size,
&size_needed ) ;
if ( !NT_SUCCESS( Status ) &&
( Status != STATUS_BUFFER_OVERFLOW ) &&
( Status != STATUS_BUFFER_TOO_SMALL ) ) {
// Now just try everything but SACL
Status = NtQuerySecurityObject(
hFile,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
ccb->buffer,
ccb->buffer_size,
&size_needed ) ;
}
// if ( !NT_SUCCESS( Status ) &&
// ( Status != STATUS_BUFFER_OVERFLOW ) &&
// ( Status != STATUS_BUFFER_TOO_SMALL ) ) {
// // Now just try to read the DACL
// Status = NtQuerySecurityObject(
// hFile,
// OWNER_SECURITY_INFORMATION |
// GROUP_SECURITY_INFORMATION,
// ccb->buffer,
// ccb->buffer_size,
// &size_needed ) ;
// }
if ( !NT_SUCCESS( Status ) &&
( Status != STATUS_BUFFER_OVERFLOW ) &&
( Status != STATUS_BUFFER_TOO_SMALL ) ) {
// Now Just give up.
break ;
}
if ( ccb->buffer_size < size_needed ) {
BYTE *temp ;
temp = (BYTE *)RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( BACKUP_TAG ),
size_needed ) ;
RtlFreeHeap(
RtlProcessHeap(),
0,
ccb->buffer ) ;
ccb->buffer = temp ;
ccb->buffer_size = size_needed ;
continue ;
}
} while ( !NT_SUCCESS( Status ) ) ;
if ( !NT_SUCCESS(Status) ) {
break ;
} else {
size_needed = RtlLengthSecurityDescriptor( ccb->buffer ) ;
}
ccb->head.dwStreamId = mwStreamList[ ccb->stream_index ] ;
ccb->head.dwStreamAttributes = STREAM_CONTAINS_SECURITY ;
ccb->head.dwStreamNameSize = 0 ;
ccb->header_size = NAMELESS_HEADER_SIZE ;
ccb->head.Size.LowPart = size_needed ;
ccb->head.Size.HighPart = 0 ;
ccb->stream_start = FALSE ;
} else if ( ( ccb->sub_stream_offset.HighPart != 0 ) ||
( ccb->sub_stream_offset.LowPart >= ccb->header_size ) ) {
fsize.HighPart = 0 ;
fsize.LowPart = ccb->header_size ;
fsize.QuadPart += ccb->head.Size.QuadPart ;
fsize.QuadPart -= ccb->sub_stream_offset.QuadPart ;
if ( fsize.HighPart != 0 ) {
nBytesToReadThisPass = nNumberOfBytesToRead ;
} else {
nBytesToReadThisPass = min( nNumberOfBytesToRead,
fsize.LowPart ) ;
}
fsize.LowPart = ccb->sub_stream_offset.LowPart - ccb->header_size ;
RtlCopyMemory( lpBuffer, ccb->buffer + fsize.LowPart,
nBytesToReadThisPass ) ;
fsize.LowPart = nBytesToReadThisPass ;
fsize.HighPart = 0 ;
ccb->sub_stream_offset.QuadPart += fsize.QuadPart ;
*lpNumberOfBytesRead += nBytesToReadThisPass ;
nNumberOfBytesToRead -= nBytesToReadThisPass ;
lpBuffer += nBytesToReadThisPass ;
}
break ;
default :
ccb->stream_index++ ;
ccb->stream_start ;
break ;
}
//
// if we are in the phase of reading the header
// then copy the header
//
if ( ( ccb->sub_stream_offset.LowPart < ccb->header_size ) &&
( ccb->sub_stream_offset.HighPart == 0 ) ) {
//
// Send the current stream header ;
//
nBytesToReadThisPass = min( ccb->header_size - ccb->sub_stream_offset.LowPart,
nNumberOfBytesToRead ) ;
RtlCopyMemory( lpBuffer, (BYTE *)(&ccb->head) + ccb->sub_stream_offset.LowPart,
nBytesToReadThisPass ) ;
lpBuffer += nBytesToReadThisPass ;
nNumberOfBytesToRead -= nBytesToReadThisPass ;
*lpNumberOfBytesRead += nBytesToReadThisPass ;
ccb->sub_stream_offset.LowPart += nBytesToReadThisPass ;
//
// if we are at the end of a stream then
// start at the beginning of the next stream
//
} else {
fsize.HighPart = 0 ;
fsize.LowPart = ccb->header_size ;
fsize.QuadPart += ccb->head.Size.QuadPart ;
if ( ccb->sub_stream_offset.QuadPart == fsize.QuadPart ) {
if ( ccb->alt_stream_hand != NULL ) {
UnlockFile( ccb->alt_stream_hand,
0,
0,
0xFFFFFFFF,
0xFFFFFFFF ) ;
CloseHandle( ccb->alt_stream_hand ) ;
ccb->alt_stream_hand = NULL ;
}
ccb->header_size = 0 ;
ccb->stream_start = TRUE ;
if ( !ccb->multi_stream_type ) {
ccb->stream_index ++ ;
ccb->buffer_ready = FALSE ;
}
}
}
} while ( ( ret_val == TRUE ) &&
( mwStreamList[ccb->stream_index] != BKUP_DONE ) &&
( nNumberOfBytesToRead != 0 ) ) ;
}
if ( (ret_val == TRUE) && *lpNumberOfBytesRead == 0 ) {
CleanUpContext( lpContext ) ;
}
return ret_val ;
}
BOOL
WINAPI
BackupSeek(
HANDLE hFile,
DWORD dwLowBytesToSeek,
DWORD dwHighBytesToSeek,
LPDWORD lpdwLowBytesSeeked,
LPDWORD lpdwHighBytesSeeked,
LPVOID *lpContext
)
/*++
Routine Description:
Data can be skiped during BackupRead or BackupWrite by using
BackupSeek.
This API is used to seek forward from the current position the
specified number of bytes. This function does not seek over a
stream header. The number of bytes actualy seeked is retuned.
If a caller wants to seek to the start of the next stream it can
pass 0xffffffff, 0xffffffff as the amount to seek. The number of
bytes actualy skiped over is retuned.
Arguments:
hFile - Supplies an open handle to a file that is to be read. The
file handle must have been created with GENERIC_READ or
GENERIC_WRITE access.
dwLowBytesToSeek - Specifies the low 32 bits of the number of bytes
requested to seek.
dwHighBytesToSeek - Specifies the high 32 bits of the number of bytes
requested to seek.
lpdwLowBytesSeeked - Points to the buffer where the low 32 bits of the
actual number of bytes to seek is to be placed.
lpdwHighBytesSeeked - Points to the buffer where the high 32 bits of the
actual number of bytes to seek is to be placed.
bAbort - If true, then all resources associated with the context will
be released.
lpContext - Points to a buffer pointer setup and maintained by
BackupRead.
Return Value:
TRUE - The operation successfuly seeked the requested number of bytes.
FALSE - The requested number of bytes could not be seeked. The number
of bytes actualy seeked is returned.
--*/
{
CONTEXT_PTR ccb ;
LARGE_INTEGER liObjSize ;
LARGE_INTEGER liSeeked ;
LARGE_INTEGER liToSeek ;
LARGE_INTEGER liNewPos ;
LARGE_INTEGER liCurPos ;
LARGE_INTEGER fsize ;
HANDLE strm_hand ;
BOOL ret_val ;
ccb = *lpContext ;
*lpdwHighBytesSeeked = 0 ;
*lpdwLowBytesSeeked = 0 ;
if ( (ccb == INVALID_HANDLE_VALUE) || (ccb == NULL) || ccb->stream_start ) {
return FALSE ;
}
if ( ( ccb->sub_stream_offset.LowPart < ccb->header_size ) &&
( ccb->sub_stream_offset.HighPart == 0 ) ) {
return FALSE ;
}
//
// If we made it here we are in the middle of a stream
//
liToSeek.LowPart = dwLowBytesToSeek ;
liToSeek.HighPart = dwHighBytesToSeek & 0x7FFFFFFF ;
liObjSize.LowPart = ccb->header_size ;
liObjSize.HighPart = 0 ;
liObjSize.QuadPart = ccb->sub_stream_offset.QuadPart - liObjSize.QuadPart ;
liObjSize.QuadPart = ccb->head.Size.QuadPart - liObjSize.QuadPart;
if ( liToSeek.QuadPart > liObjSize.QuadPart ) {
liToSeek = liObjSize ;
}
switch( ccb->head.dwStreamId ) {
case BACKUP_EA_DATA:
case BACKUP_SECURITY_DATA:
//
// assume less than 2gig of data
//
liToSeek.HighPart = 0 ;
ccb->buffer_offset += liToSeek.LowPart ;
liSeeked = liToSeek ;
if ( ( dwHighBytesToSeek == 0 ) &&
( liSeeked.LowPart == dwLowBytesToSeek ) ) {
ret_val = TRUE ;
} else {
ret_val = FALSE ;
}
break ;
case BACKUP_DATA:
case BACKUP_ALTERNATE_DATA :
//
// set up the correct handle to seek with.
//
if ( ccb->head.dwStreamId == BACKUP_DATA ) {
strm_hand = hFile ;
} else {
strm_hand = ccb->alt_stream_hand ;
}
//
// first lets get the current position ;
//
liCurPos.HighPart = 0 ;
liCurPos.LowPart = SetFilePointer( strm_hand, 0,
&liCurPos.HighPart,
FILE_CURRENT ) ;
//
// Now seek the requested number of bytes ;
//
liNewPos.HighPart = liToSeek.HighPart ;
liNewPos.LowPart = SetFilePointer( strm_hand,
liToSeek.LowPart,
&liNewPos.HighPart,
FILE_CURRENT ) ;
//
// Assume that we seek the requested amount because
// if we do not, subsuquent reads will fail and
// the caller will never be able to read to the next stream
//
if ( ( (DWORD)liToSeek.HighPart == dwHighBytesToSeek ) &&
( (DWORD)liToSeek.LowPart == dwLowBytesToSeek ) ) {
ret_val = TRUE ;
} else {
ret_val = FALSE ;
}
liSeeked = liToSeek ;
break ;
default :
liToSeek.LowPart = dwLowBytesToSeek ;
liToSeek.HighPart = dwHighBytesToSeek ;
ret_val = TRUE ;
liSeeked = liToSeek ;
if ( liToSeek.QuadPart > ccb->head.Size.QuadPart ) {
liSeeked = ccb->head.Size ;
}
break ;
}
*lpdwLowBytesSeeked = liSeeked.LowPart ;
*lpdwHighBytesSeeked = liSeeked.HighPart ;
ccb->sub_stream_offset.QuadPart += liSeeked.QuadPart;
fsize.HighPart = 0 ;
fsize.LowPart = ccb->header_size ;
fsize.QuadPart += ccb->head.Size.QuadPart;
if ( ccb->sub_stream_offset.QuadPart == fsize.QuadPart ) {
if ( ccb->alt_stream_hand != NULL ) {
UnlockFile( ccb->alt_stream_hand,
0,
0,
0xFFFFFFFF,
0xFFFFFFFF ) ;
CloseHandle( ccb->alt_stream_hand ) ;
ccb->alt_stream_hand = NULL ;
}
ccb->header_size = 0 ;
ccb->stream_start = TRUE ;
if ( !ccb->multi_stream_type ) {
ccb->stream_index ++ ;
ccb->buffer_ready = FALSE ;
}
}
if ( !ret_val ) {
SetLastError( ERROR_SEEK ) ;
}
return ret_val ;
}
BOOL
WINAPI
BackupWrite(
HANDLE hFile,
LPBYTE lpBuffer,
DWORD nNumberOfBytesToWrite,
LPDWORD lpNumberOfBytesWritten,
BOOL bAbort,
BOOL bProcessSecurity,
LPVOID *lpContext
)
/*++
Routine Description:
Data can be written to a file using BackupWrite.
This API is used to Restore data to an object. After the
write completes, the file pointer is adjusted by the number of bytes
actually written.
Unlike DOS, a NumberOfBytesToWrite value of zero does not truncate
or extend the file. If this function is required, SetEndOfFile
should be used.
Arguments:
hFile - Supplies an open handle to a file that is to be written. The
file handle must have been created with GENERIC_WRITE access to
the file.
lpBuffer - Supplies the address of the data that is to be written to
the file.
nNumberOfBytesToWrite - Supplies the number of bytes to write to the
file. Unlike DOS, a value of zero is interpreted a null write.
lpNumberOfBytesWritten - Returns the number of bytes written by this
call. Before doing any work or error processing, the API sets this
to zero.
bAbort - If true, then all resources associated with the context will
be released.
bProcessSecurity - If TRUE, then the NTFS ACL data will be written.
If FALSE, then the ACL stream will be ignored.
lpContext - Points to a buffer pointer setup and maintained by
BackupRead.
Return Value:
TRUE - The operation was a success.
FALSE - The operation failed. Extended error status is
available using GetLastError.
--*/
{
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
DWORD nBytesWrittenThisPass ;
CONTEXT_PTR ccb ;
DWORD temp_num_bytes ;
LARGE_INTEGER fsize ;
BOOL ret_val = FALSE ;
ccb = *lpContext ;
//
// Allocate our Context Control Block on first call.
//
if ( bAbort ) {
if ( ccb != NULL ) {
CleanUpContext( lpContext ) ;
}
return TRUE ;
}
*lpNumberOfBytesWritten = 0;
if ( ccb == INVALID_HANDLE_VALUE ) {
return TRUE ;
}
if ( ccb == NULL ) {
ccb = (CONTEXT_PTR)RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( BACKUP_TAG ),
sizeof(CONTEXT_CONTROL_BLK) ) ;
if ( ccb != NULL ) {
RtlZeroMemory( ccb, sizeof( CONTEXT_CONTROL_BLK ) ) ;
ccb->stream_start = TRUE ;
}
}
if ( ccb != NULL ) {
do {
if ( ccb->header_size == 0 ) { // we expect a stream header
//
// fill up to the stream name
//
temp_num_bytes = min( nNumberOfBytesToWrite,
NAMELESS_HEADER_SIZE -
ccb->sub_stream_offset.LowPart) ;
RtlCopyMemory( ((CHAR *)&ccb->head) + ccb->sub_stream_offset.LowPart,
lpBuffer, temp_num_bytes ) ;
*lpNumberOfBytesWritten += temp_num_bytes ;
nNumberOfBytesToWrite -= temp_num_bytes ;
ccb->sub_stream_offset.LowPart += temp_num_bytes ;
lpBuffer += temp_num_bytes ;
if ( ccb->sub_stream_offset.LowPart == NAMELESS_HEADER_SIZE ) {
ccb->header_size = ccb->sub_stream_offset.LowPart ;
}
}
*lpContext = ccb ;
if ( nNumberOfBytesToWrite == 0 ) {
return TRUE ;
}
if ( (ccb->header_size != 0 ) &&
(ccb->head.dwStreamNameSize != 0 ) &&
(ccb->header_size == NAMELESS_HEADER_SIZE) ) {
//
// now fill in the stream name if it exists
//
temp_num_bytes = min ( nNumberOfBytesToWrite,
ccb->header_size + ccb->head.dwStreamNameSize - ccb->sub_stream_offset.LowPart ) ;
RtlCopyMemory( ((CHAR *)&ccb->head) + ccb->sub_stream_offset.LowPart,
lpBuffer, temp_num_bytes ) ;
*lpNumberOfBytesWritten += temp_num_bytes ;
nNumberOfBytesToWrite -= temp_num_bytes ;
ccb->sub_stream_offset.LowPart += temp_num_bytes ;
lpBuffer += temp_num_bytes ;
if ( ccb->sub_stream_offset.LowPart ==
ccb->header_size + ccb->head.dwStreamNameSize ) {
ccb->header_size += ccb->head.dwStreamNameSize ;
} else {
return TRUE ;
}
}
fsize.LowPart = ccb->header_size ;
fsize.HighPart = 0 ;
fsize.QuadPart += ccb->head.Size.QuadPart ;
fsize.QuadPart -= ccb->sub_stream_offset.QuadPart ;
if ( fsize.HighPart != 0 ) {
temp_num_bytes = nNumberOfBytesToWrite ;
} else {
temp_num_bytes = min( fsize.LowPart,
nNumberOfBytesToWrite ) ;
}
fsize.LowPart = ccb->header_size ;
fsize.HighPart = 0 ;
fsize.QuadPart += ccb->head.Size.QuadPart ;
if ( ( ccb->access_error ) &&
(fsize.QuadPart == ccb->sub_stream_offset.QuadPart) ) {
*lpNumberOfBytesWritten += temp_num_bytes ;
nNumberOfBytesToWrite -= temp_num_bytes ;
lpBuffer += temp_num_bytes ;
fsize.LowPart = temp_num_bytes ;
fsize.HighPart = 0 ;
ccb->sub_stream_offset.QuadPart += fsize.QuadPart ;
continue ;
} else {
ccb->access_error = FALSE ;
}
switch( ccb->head.dwStreamId ) {
case BACKUP_DATA:
ccb->stream_start = FALSE ;
Status = WriteFile(
hFile,
lpBuffer,
temp_num_bytes,
&nBytesWrittenThisPass,
NULL ) ;
if ( nBytesWrittenThisPass ) {
*lpNumberOfBytesWritten += nBytesWrittenThisPass ;
nNumberOfBytesToWrite -= nBytesWrittenThisPass ;
lpBuffer += nBytesWrittenThisPass ;
fsize.LowPart = nBytesWrittenThisPass ;
fsize.HighPart = 0 ;
ccb->sub_stream_offset.QuadPart += fsize.QuadPart ;
}
ret_val = TRUE ;
if ( !Status ) {
ret_val = FALSE ;
}
break ;
case BACKUP_ALTERNATE_DATA:
if ( ccb->alt_stream_hand == NULL ) {
OBJECT_ATTRIBUTES Obja ;
UNICODE_STRING fname ;
fname.Length = (USHORT)ccb->head.dwStreamNameSize ;
fname.MaximumLength = (USHORT)ccb->head.dwStreamNameSize ;
fname.Buffer = ccb->head.cStreamName ;
InitializeObjectAttributes(
&Obja,
&fname,
OBJ_CASE_INSENSITIVE,
hFile,
NULL ) ;
Status = NtCreateFile(
&ccb->alt_stream_hand,
FILE_GENERIC_WRITE,
&Obja,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE,
FILE_OVERWRITE_IF,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT,
NULL,
0L ) ;
if ( !NT_SUCCESS( Status ) ) {
BaseSetLastNTError(Status);
ret_val = FALSE ;
ccb->access_error = TRUE ;
break ;
}
ccb->stream_start = FALSE ;
}
if ( ccb->alt_stream_hand == INVALID_HANDLE_VALUE ) {
ccb->access_error = TRUE ;
break ;
}
Status = WriteFile(
ccb->alt_stream_hand,
lpBuffer,
temp_num_bytes,
&nBytesWrittenThisPass,
NULL ) ;
if ( Status ) {
*lpNumberOfBytesWritten += nBytesWrittenThisPass ;
nNumberOfBytesToWrite -= nBytesWrittenThisPass ;
lpBuffer += nBytesWrittenThisPass ;
fsize.LowPart = nBytesWrittenThisPass ;
fsize.HighPart = 0 ;
ccb->sub_stream_offset.QuadPart += fsize.QuadPart ;
ret_val = TRUE;
} else {
ret_val = FALSE ;
}
break ;
case BACKUP_EA_DATA:
//
// allocate a buffer for EA data storage
//
if ( ccb->stream_start ) {
ccb->stream_start = FALSE ;
if ( ccb->buffer_size < ccb->head.Size.LowPart ) {
BYTE *temp ;
temp = (BYTE *)RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( BACKUP_TAG ),
ccb->head.Size.LowPart ) ;
RtlFreeHeap( RtlProcessHeap(), 0, ccb->buffer ) ;
ccb->buffer = temp ;
ccb->buffer_size = ccb->head.Size.LowPart ;
}
if ( ccb->buffer == NULL ) {
SetLastError( ERROR_NOT_ENOUGH_MEMORY ) ;
ccb->buffer_size = 0 ;
ret_val = FALSE ;
}
}
//
// now copy the EA stream into our allocated buffer
//
temp_num_bytes = min( nNumberOfBytesToWrite,
ccb->head.Size.LowPart - ccb->sub_stream_offset.LowPart + ccb->header_size ) ;
RtlCopyMemory( ccb->buffer + ccb->sub_stream_offset.LowPart - ccb->header_size,
lpBuffer, temp_num_bytes ) ;
*lpNumberOfBytesWritten += temp_num_bytes ;
nNumberOfBytesToWrite -= temp_num_bytes ;
ccb->sub_stream_offset.LowPart += temp_num_bytes ;
lpBuffer += temp_num_bytes ;
ret_val = TRUE ;
//
// once the entire stream is in our buffer we can set the
// the EA data
//
if ( ccb->sub_stream_offset.LowPart ==
ccb->head.Size.LowPart + ccb->header_size ) {
Status = NtSetEaFile( hFile,
&IoStatusBlock,
ccb->buffer,
ccb->head.Size.LowPart ) ;
if ( !NT_SUCCESS( Status ) ) {
BaseSetLastNTError(Status);
ret_val = FALSE ;
}
}
break ;
case BACKUP_SECURITY_DATA:
//
// allocate a buffer for ACL data storage
//
if ( ccb->stream_start ) {
ccb->stream_start = FALSE ;
if ( ccb->buffer_size < ccb->head.Size.LowPart - ccb->sub_stream_offset.LowPart + ccb->header_size ) {
BYTE *temp ;
temp = (BYTE *)RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( BACKUP_TAG ),
ccb->head.Size.LowPart ) ;
RtlFreeHeap( RtlProcessHeap(), 0, ccb->buffer ) ;
ccb->buffer = temp ;
ccb->buffer_size = ccb->head.Size.LowPart ;
}
if ( ccb->buffer == NULL ) {
SetLastError( ERROR_NOT_ENOUGH_MEMORY ) ;
ccb->buffer_size = 0 ;
ret_val = FALSE ;
}
}
//
// now copy the ACL stream into our allocated buffer
//
temp_num_bytes = min( nNumberOfBytesToWrite,
ccb->head.Size.LowPart - ccb->sub_stream_offset.LowPart + ccb->header_size ) ;
RtlCopyMemory( ccb->buffer + ccb->sub_stream_offset.LowPart - ccb->header_size,
lpBuffer, temp_num_bytes ) ;
*lpNumberOfBytesWritten += temp_num_bytes ;
nNumberOfBytesToWrite -= temp_num_bytes ;
ccb->sub_stream_offset.LowPart += temp_num_bytes ;
lpBuffer += temp_num_bytes ;
ret_val = TRUE ;
//
// Once the entire stream is in our buffer,
// we can set the the ACL data.
//
if ( ccb->sub_stream_offset.LowPart ==
ccb->head.Size.LowPart + ccb->header_size ) {
SECURITY_INFORMATION si;
si = OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION;
if ( ((PISECURITY_DESCRIPTOR)ccb->buffer)->Control & SE_DACL_PRESENT ) {
si |= DACL_SECURITY_INFORMATION;
}
if ( ((PISECURITY_DESCRIPTOR)ccb->buffer)->Control & SE_SACL_PRESENT ) {
si |= SACL_SECURITY_INFORMATION;
}
if ( !bProcessSecurity ) {
break ; // ignore the data we read
}
// First try to write all the security data
Status = NtSetSecurityObject(
hFile,
si,
ccb->buffer ) ;
if ( !NT_SUCCESS( Status ) ) {
NTSTATUS Stat;
// If that didn't work, the caller is
// probably not running as Backup-
// Operator, so we can't set the owner
// and group. Keep the current status
// code, and attempt to set the DACL
// and SACL while ignoring failures.
if (si & SACL_SECURITY_INFORMATION)
{
NtSetSecurityObject(
hFile,
SACL_SECURITY_INFORMATION,
ccb->buffer ) ;
}
if (si & DACL_SECURITY_INFORMATION)
{
Status = NtSetSecurityObject(
hFile,
DACL_SECURITY_INFORMATION,
ccb->buffer ) ;
}
Stat = NtSetSecurityObject(
hFile,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION,
ccb->buffer ) ;
if ( NT_SUCCESS( Status ) ) {
Status = Stat ;
}
}
if ( !NT_SUCCESS( Status ) ) {
BaseSetLastNTError(Status);
ret_val = FALSE ;
break ;
}
}
break ;
case BACKUP_LINK:
//
// allocate a buffer for LINK data storage
//
if ( ccb->stream_start ) {
ccb->stream_start = FALSE ;
if ( ccb->buffer_size < ccb->head.Size.LowPart - ccb->sub_stream_offset.LowPart + ccb->header_size ) {
BYTE *temp ;
temp = (BYTE *)RtlAllocateHeap(
RtlProcessHeap(),
MAKE_TAG( BACKUP_TAG ),
ccb->head.Size.LowPart ) ;
RtlFreeHeap( RtlProcessHeap(), 0, ccb->buffer ) ;
ccb->buffer = temp ;
ccb->buffer_size = ccb->head.Size.LowPart ;
}
if ( ccb->buffer == NULL ) {
SetLastError( ERROR_NOT_ENOUGH_MEMORY ) ;
ccb->buffer_size = 0 ;
ret_val = FALSE ;
}
}
//
// now copy the LINK stream into our allocated buffer
//
temp_num_bytes = min( nNumberOfBytesToWrite,
ccb->head.Size.LowPart - ccb->sub_stream_offset.LowPart + ccb->header_size ) ;
RtlCopyMemory( ccb->buffer + ccb->sub_stream_offset.LowPart - ccb->header_size,
lpBuffer, temp_num_bytes ) ;
*lpNumberOfBytesWritten += temp_num_bytes ;
nNumberOfBytesToWrite -= temp_num_bytes ;
ccb->sub_stream_offset.LowPart += temp_num_bytes ;
lpBuffer += temp_num_bytes ;
ret_val = TRUE ;
//
// once the entire stream is in our buffer we can set up
// the LINK
//
if ( ccb->sub_stream_offset.LowPart ==
ccb->head.Size.LowPart + ccb->header_size ) {
PFILE_LINK_INFORMATION flink_info ;
INT flink_info_length ;
WCHAR *wbuff ;
WCHAR *last_slash = NULL ;
INT fname_size = sizeof( WCHAR ) ;
INT num_slash = 0 ;
WCHAR save_char ;
wbuff = (WCHAR *)ccb->buffer ;
while( *wbuff != 0 ) {
if( *wbuff == '\\' ) {
last_slash = wbuff ;
num_slash ++ ;
fname_size = 0 ;
}
wbuff ++ ;
fname_size += sizeof(WCHAR) ;
}
flink_info = (PFILE_LINK_INFORMATION)
RtlAllocateHeap( RtlProcessHeap(),
MAKE_TAG( BACKUP_TAG ),
sizeof( FILE_LINK_INFORMATION ) +
fname_size ) ;
if ( flink_info == NULL ) {
SetLastError( ERROR_NOT_ENOUGH_MEMORY ) ;
ret_val = FALSE ;
break ;
}
if ( num_slash > 1 ) {
RtlCopyMemory( flink_info->FileName, last_slash+1, fname_size ) ;
flink_info->FileNameLength = (fname_size-sizeof(WCHAR)) ;
*last_slash = 0 ;
save_char = '\\' ;
} else {
RtlCopyMemory( flink_info->FileName, last_slash+1, fname_size ) ;
flink_info->FileNameLength = (fname_size-sizeof(WCHAR)) ;
save_char = *(last_slash++) ;
*last_slash = 0 ;
}
flink_info->RootDirectory = CreateFileW( (WCHAR *)(ccb->buffer),
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL| FILE_FLAG_BACKUP_SEMANTICS,
NULL ) ;
*last_slash = save_char ;
flink_info->ReplaceIfExists = TRUE ;
if ( flink_info->RootDirectory == INVALID_HANDLE_VALUE ) {
RtlFreeHeap( RtlProcessHeap(),
0,
flink_info ) ;
SetLastError( ERROR_FILE_NOT_FOUND ) ;
ret_val = FALSE ;
break ;
}
flink_info_length = sizeof( FILE_LINK_INFORMATION ) + fname_size ;
Status = NtSetInformationFile( hFile,
&IoStatusBlock,
flink_info,
flink_info_length,
FileLinkInformation );
CloseHandle( flink_info->RootDirectory ) ;
RtlFreeHeap( RtlProcessHeap(),
0,
flink_info ) ;
if ( !NT_SUCCESS(Status) ) {
BaseSetLastNTError(Status);
ret_val = FALSE ;
} else {
if ( IoStatusBlock.Information == FILE_OVERWRITTEN ) {
SetLastError(ERROR_ALREADY_EXISTS);
} else {
SetLastError(0);
}
}
}
break ;
default :
SetLastError( ERROR_INVALID_DATA ) ;
ret_val = FALSE ;
break ;
}
fsize.HighPart = 0 ;
fsize.LowPart = ccb->header_size ;
fsize.QuadPart += ccb->head.Size.QuadPart ;
if ( fsize.QuadPart == ccb->sub_stream_offset.QuadPart ) {
ccb->header_size = 0 ;
ccb->stream_start = TRUE ;
ccb->sub_stream_offset.LowPart = 0 ;
ccb->sub_stream_offset.HighPart = 0 ;
if ( ccb->alt_stream_hand != NULL ) {
CloseHandle( ccb->alt_stream_hand ) ;
ccb->alt_stream_hand = NULL ;
}
}
} while( ( ret_val == TRUE ) &&
( nNumberOfBytesToWrite != 0 ) ) ;
}
if ( (ret_val == TRUE) && (*lpNumberOfBytesWritten == 0) ) {
CleanUpContext( lpContext ) ;
}
return ret_val ;
}
static void
CleanUpContext(
LPVOID *lpContext )
{
CONTEXT_PTR ccb = *lpContext ;
if ( ccb == INVALID_HANDLE_VALUE ) {
return ;
}
if ( ccb->buffer != NULL ) {
RtlFreeHeap( RtlProcessHeap(), 0, ccb->buffer ) ;
}
if ( ccb->alt_stream_hand != NULL ) {
CloseHandle( ccb->alt_stream_hand ) ;
}
RtlFreeHeap( RtlProcessHeap(), 0, ccb ) ;
*lpContext = INVALID_HANDLE_VALUE ;
}