mirror of https://github.com/tongzx/nt5src
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.
3131 lines
75 KiB
3131 lines
75 KiB
//
|
|
// Copyright (c) 1991 Microsoft Corporation & Maynard Electornics
|
|
//
|
|
// Module Name:
|
|
//
|
|
// backup.c
|
|
//
|
|
// Abstract:
|
|
//
|
|
// This module implements Win32 Backup APIs
|
|
//
|
|
// Author:
|
|
//
|
|
// Steve DeVos (@Maynard) 2 March, 1992 15:38:24
|
|
//
|
|
// Revision History:
|
|
|
|
#include <basedll.h>
|
|
#pragma hdrstop
|
|
|
|
#include <windows.h>
|
|
|
|
|
|
#define CWCMAX_STREAMNAME 512
|
|
#define CB_NAMELESSHEADER FIELD_OFFSET(WIN32_STREAM_ID, cStreamName)
|
|
|
|
typedef struct
|
|
{
|
|
DWORD BufferSize;
|
|
DWORD AllocSize;
|
|
BYTE *Buffer;
|
|
} BUFFER;
|
|
|
|
//
|
|
// BACKUPCONTEXT is the structure used to note the state of the backup.
|
|
//
|
|
|
|
typedef struct
|
|
{
|
|
//
|
|
// Public header describing current stream. Since this structure precedes
|
|
// a variable-length stream name, we must reserve space for that name
|
|
// following the header.
|
|
//
|
|
|
|
WIN32_STREAM_ID head;
|
|
union {
|
|
WCHAR awcName[CWCMAX_STREAMNAME];
|
|
} ex ;
|
|
|
|
LARGE_INTEGER cbSparseOffset ;
|
|
|
|
//
|
|
// Offset in the current segment of the backup stream. This includes
|
|
// the size of the above header (including variable length name).
|
|
//
|
|
|
|
LONGLONG liStreamOffset;
|
|
|
|
//
|
|
// BackupRead machine state
|
|
//
|
|
|
|
DWORD StreamIndex;
|
|
|
|
//
|
|
// Calculated size of the above header.
|
|
//
|
|
|
|
DWORD cbHeader;
|
|
|
|
//
|
|
// Handle to alternate data stream
|
|
//
|
|
|
|
HANDLE hAlternate;
|
|
|
|
//
|
|
// Buffers
|
|
//
|
|
|
|
BUFFER DataBuffer; // Data buffer
|
|
DWORD dwSparseMapSize ; // size of the sparse file map
|
|
DWORD dwSparseMapOffset ; // offset into the sparse map
|
|
BOOLEAN fSparseBlockStart ; // TRUE if start of sparse block
|
|
BOOLEAN fSparseHandAlt ; // TRUE if sparse stream is alt stream
|
|
|
|
DWORD iNameBuffer; // Offset into stream name buffer
|
|
BUFFER StreamNameBuffer; // Stream name buffer
|
|
BOOLEAN NamesReady; // TRUE if stream name buffer has data in it
|
|
|
|
BOOLEAN fStreamStart; // TRUE if start of new stream
|
|
BOOLEAN fMultiStreamType; // TRUE if stream type has > 1 stream hdr
|
|
BOOLEAN fAccessError; // TRUE if access to a stream was denied
|
|
DWORD fAttribs; // object attributes...
|
|
} BACKUPCONTEXT;
|
|
|
|
|
|
//
|
|
// BACKUPIOFRAME describes the current user BackupRead/Write request
|
|
//
|
|
|
|
typedef struct
|
|
{
|
|
BYTE *pIoBuffer;
|
|
DWORD *pcbTransferred;
|
|
DWORD cbRequest;
|
|
BOOLEAN fProcessSecurity;
|
|
} BACKUPIOFRAME;
|
|
|
|
|
|
#define CBMIN_BUFFER 1024
|
|
|
|
#define BufferOverflow(s) \
|
|
((s) == STATUS_BUFFER_OVERFLOW || (s) == STATUS_BUFFER_TOO_SMALL)
|
|
|
|
int mwStreamList[] =
|
|
{
|
|
BACKUP_SECURITY_DATA,
|
|
BACKUP_REPARSE_DATA,
|
|
BACKUP_DATA,
|
|
BACKUP_EA_DATA,
|
|
BACKUP_ALTERNATE_DATA,
|
|
BACKUP_OBJECT_ID,
|
|
BACKUP_INVALID,
|
|
};
|
|
|
|
|
|
|
|
__inline VOID *
|
|
BackupAlloc (DWORD cb)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that wraps heap allocation with tags.
|
|
|
|
Arguments:
|
|
|
|
cb - size of block to allocate
|
|
|
|
Return Value:
|
|
|
|
pointer to allocated memory or NULL
|
|
|
|
--*/
|
|
{
|
|
return RtlAllocateHeap( RtlProcessHeap( ), MAKE_TAG( BACKUP_TAG ), cb );
|
|
}
|
|
|
|
|
|
__inline VOID
|
|
BackupFree (IN VOID *pv)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that wraps heap freeing.
|
|
|
|
Arguments:
|
|
|
|
pv - memory to be freed
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
RtlFreeHeap( RtlProcessHeap( ), 0, pv );
|
|
}
|
|
|
|
|
|
BOOL
|
|
GrowBuffer (IN OUT BUFFER *Buffer, IN DWORD cbNew)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Attempt to grow the buffer in the backup context.
|
|
|
|
Arguments:
|
|
|
|
Buffer - pointer to buffer
|
|
|
|
cbNew - size of buffer to allocate
|
|
|
|
Return Value:
|
|
|
|
TRUE if buffer was successfully allocated.
|
|
|
|
--*/
|
|
{
|
|
VOID *pv;
|
|
|
|
if ( Buffer->AllocSize < cbNew ) {
|
|
pv = BackupAlloc( cbNew );
|
|
|
|
if (pv == NULL) {
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory( pv, Buffer->Buffer, Buffer->BufferSize );
|
|
|
|
BackupFree( Buffer->Buffer );
|
|
|
|
Buffer->Buffer = pv;
|
|
Buffer->AllocSize = cbNew ;
|
|
}
|
|
|
|
Buffer->BufferSize = cbNew;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
__inline VOID
|
|
FreeBuffer (IN OUT BUFFER *Buffer)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free the buffer
|
|
|
|
Arguments:
|
|
|
|
Buffer - pointer to buffer
|
|
|
|
Return Value:
|
|
|
|
Nothing
|
|
|
|
--*/
|
|
{
|
|
if (Buffer->Buffer != NULL) {
|
|
BackupFree( Buffer->Buffer );
|
|
Buffer->Buffer = NULL;
|
|
}
|
|
}
|
|
|
|
VOID ResetAccessDate( HANDLE hand )
|
|
{
|
|
|
|
LONGLONG tmp_time = -1 ;
|
|
FILETIME *time_ptr ;
|
|
|
|
time_ptr = (FILETIME *)(&tmp_time);
|
|
|
|
if (hand != INVALID_HANDLE_VALUE) {
|
|
SetFileTime( hand,
|
|
time_ptr,
|
|
time_ptr,
|
|
time_ptr ) ;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
FreeContext (IN OUT LPVOID *lpContext)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free a backup context and release all resources assigned to it.
|
|
|
|
Arguments:
|
|
|
|
lpContext - pointer to pointer backup context
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BACKUPCONTEXT *pbuc = *lpContext;
|
|
|
|
if (pbuc != INVALID_HANDLE_VALUE) {
|
|
|
|
FreeBuffer( &pbuc->DataBuffer );
|
|
FreeBuffer( &pbuc->StreamNameBuffer );
|
|
|
|
ResetAccessDate( pbuc->hAlternate ) ;
|
|
if (pbuc->hAlternate != INVALID_HANDLE_VALUE) {
|
|
|
|
CloseHandle( pbuc->hAlternate );
|
|
}
|
|
|
|
BackupFree(pbuc);
|
|
|
|
*lpContext = INVALID_HANDLE_VALUE;
|
|
}
|
|
}
|
|
|
|
|
|
BACKUPCONTEXT *
|
|
AllocContext (IN DWORD cbBuffer)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate a backup context with a buffer of a specified size
|
|
|
|
Arguments:
|
|
|
|
cbBuffer - desired length of the buffer
|
|
|
|
Return Value:
|
|
|
|
pointer to initialized backupcontext or NULL if out of memory.
|
|
|
|
--*/
|
|
{
|
|
BACKUPCONTEXT *pbuc;
|
|
|
|
pbuc = BackupAlloc( sizeof( *pbuc ));
|
|
|
|
if (pbuc != NULL) {
|
|
RtlZeroMemory( pbuc, sizeof( *pbuc ));
|
|
pbuc->fStreamStart = TRUE;
|
|
|
|
if (cbBuffer != 0 && !GrowBuffer( &pbuc->DataBuffer, cbBuffer )) {
|
|
BackupFree( pbuc );
|
|
pbuc = NULL;
|
|
}
|
|
}
|
|
|
|
if (pbuc == NULL) {
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
return(pbuc);
|
|
}
|
|
|
|
|
|
|
|
LONGLONG
|
|
ComputeRemainingSize (IN BACKUPCONTEXT *pbuc)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
(Re)Compute the number of bytes required to store the current
|
|
stream. This needs to take into account the header length as
|
|
well.
|
|
|
|
Arguments:
|
|
|
|
pbuc - backup context
|
|
|
|
Return Value:
|
|
|
|
Amount of data still remaining to transfer. Includes header
|
|
size.
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER ret_size ;
|
|
|
|
ret_size.QuadPart = pbuc->cbHeader + pbuc->head.Size.QuadPart
|
|
- pbuc->liStreamOffset;
|
|
|
|
//
|
|
// since the internally we treat the sparse buffer offset
|
|
// as part of the header and since the caller need to see it
|
|
// as part of the data, this code make the internal correction.
|
|
//
|
|
if ( pbuc->head.dwStreamId == BACKUP_SPARSE_BLOCK ) {
|
|
|
|
ret_size.QuadPart -= sizeof(LARGE_INTEGER) ;
|
|
}
|
|
|
|
return ret_size.QuadPart ;
|
|
}
|
|
|
|
|
|
DWORD
|
|
ComputeRequestSize (BACKUPCONTEXT *pbuc, DWORD cbrequest)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Given a transfer size request, return the number of
|
|
bytes remaining that can safely be returned to the
|
|
caller
|
|
|
|
Arguments:
|
|
|
|
pbuc - context of call
|
|
|
|
cbRequest - desired transfer size
|
|
|
|
Return Value:
|
|
|
|
amount of data available to return.
|
|
|
|
--*/
|
|
{
|
|
LONGLONG licbRemain;
|
|
|
|
licbRemain = ComputeRemainingSize( pbuc );
|
|
|
|
return (DWORD) min( cbrequest, licbRemain );
|
|
}
|
|
|
|
|
|
VOID
|
|
ReportTransfer(BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif, DWORD cbtransferred)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Note that a transfer has occurred and update contexts
|
|
|
|
Arguments:
|
|
|
|
pbuc - context of call
|
|
|
|
pbif - BACKUPIOFRAME of call detailing call
|
|
|
|
cbtransferred - amount successfully transferred
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
pbuc->liStreamOffset += cbtransferred;
|
|
*pbif->pcbTransferred += cbtransferred;
|
|
pbif->cbRequest -= cbtransferred;
|
|
pbif->pIoBuffer += cbtransferred;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
BackupReadBuffer (BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform a read to user buffer from the buffer in the backup
|
|
context.
|
|
|
|
Arguments:
|
|
|
|
pbuc - context of call
|
|
|
|
pbif - frame describing desired user BackupRead request
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD cbrequest;
|
|
BYTE *pb;
|
|
|
|
//
|
|
// Determine size of allowable transfer and pointer to source
|
|
// data
|
|
//
|
|
|
|
cbrequest = ComputeRequestSize( pbuc, pbif->cbRequest );
|
|
pb = &pbuc->DataBuffer.Buffer[ pbuc->liStreamOffset - pbuc->cbHeader ];
|
|
|
|
//
|
|
// Move the data to the user's buffer
|
|
//
|
|
|
|
RtlCopyMemory(pbif->pIoBuffer, pb, cbrequest);
|
|
|
|
//
|
|
// Update statistics
|
|
//
|
|
|
|
ReportTransfer(pbuc, pbif, cbrequest);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BackupReadStream (HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform a read to user buffer from the stream.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to file for transfer
|
|
|
|
pbuc - context of call
|
|
|
|
pbif - frame describing BackupRead request
|
|
|
|
Return Value:
|
|
|
|
True if transfer succeeded..
|
|
|
|
--*/
|
|
{
|
|
DWORD cbrequest;
|
|
DWORD cbtransferred;
|
|
BOOL fSuccess;
|
|
|
|
if (pbuc->fSparseBlockStart) {
|
|
|
|
PFILE_ALLOCATED_RANGE_BUFFER range_buf ;
|
|
LARGE_INTEGER licbFile ;
|
|
|
|
range_buf = (PFILE_ALLOCATED_RANGE_BUFFER)(pbuc->DataBuffer.Buffer + pbuc->dwSparseMapOffset) ;
|
|
|
|
pbuc->head.Size.QuadPart = range_buf->Length.QuadPart + sizeof(LARGE_INTEGER) ;
|
|
|
|
pbuc->head.dwStreamId = BACKUP_SPARSE_BLOCK ;
|
|
pbuc->head.dwStreamAttributes = STREAM_SPARSE_ATTRIBUTE;
|
|
|
|
pbuc->head.dwStreamNameSize = 0;
|
|
|
|
pbuc->cbHeader = CB_NAMELESSHEADER + sizeof( LARGE_INTEGER ) ;
|
|
|
|
pbuc->cbSparseOffset = range_buf->FileOffset ;
|
|
|
|
RtlCopyMemory( pbuc->head.cStreamName, &pbuc->cbSparseOffset, sizeof( LARGE_INTEGER ) ) ;
|
|
|
|
pbuc->fSparseBlockStart = FALSE;
|
|
|
|
licbFile.HighPart = 0;
|
|
|
|
licbFile.HighPart = range_buf->FileOffset.HighPart;
|
|
|
|
licbFile.LowPart = SetFilePointer( hFile,
|
|
range_buf->FileOffset.LowPart,
|
|
&licbFile.HighPart,
|
|
FILE_BEGIN );
|
|
|
|
if ( licbFile.QuadPart != range_buf->FileOffset.QuadPart ) {
|
|
pbuc->fAccessError = TRUE;
|
|
return FALSE ;
|
|
} else {
|
|
return TRUE ;
|
|
}
|
|
}
|
|
|
|
|
|
if (pbuc->liStreamOffset < pbuc->cbHeader) {
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
cbrequest = ComputeRequestSize( pbuc, pbif->cbRequest );
|
|
|
|
fSuccess = ReadFile( hFile, pbif->pIoBuffer, cbrequest, &cbtransferred, NULL );
|
|
|
|
if (cbtransferred != 0) {
|
|
|
|
ReportTransfer( pbuc, pbif, cbtransferred );
|
|
|
|
} else if (fSuccess && cbrequest != 0) {
|
|
|
|
SetLastError( ERROR_IO_DEVICE );
|
|
fSuccess = FALSE;
|
|
}
|
|
|
|
return(fSuccess);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BackupGetSparseMap (HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reads the sparse data map.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to file for transfer
|
|
|
|
pbuc - context of call
|
|
|
|
pbif - frame describing BackupRead request
|
|
|
|
Return Value:
|
|
|
|
True if transfer succeeded..
|
|
|
|
--*/
|
|
{
|
|
FILE_ALLOCATED_RANGE_BUFFER req_buf ;
|
|
PFILE_ALLOCATED_RANGE_BUFFER last_ret_buf ;
|
|
DWORD out_buf_size ;
|
|
DWORD data_size = 4096 ;
|
|
IO_STATUS_BLOCK iosb ;
|
|
LARGE_INTEGER file_size ;
|
|
NTSTATUS Status ;
|
|
BOOLEAN empty_file = FALSE ;
|
|
|
|
req_buf.FileOffset.QuadPart = 0 ;
|
|
|
|
pbuc->dwSparseMapSize = 0 ;
|
|
pbuc->dwSparseMapOffset = 0 ;
|
|
pbuc->fSparseBlockStart = FALSE ;
|
|
|
|
req_buf.Length.LowPart = GetFileSize( hFile,
|
|
&req_buf.Length.HighPart );
|
|
|
|
file_size = req_buf.Length ;
|
|
|
|
do {
|
|
if ( GrowBuffer( &pbuc->DataBuffer,
|
|
data_size ) ) {
|
|
|
|
iosb.Information = 0 ;
|
|
|
|
Status = NtFsControlFile( hFile,
|
|
NULL, // overlapped event handle
|
|
NULL, // Apc routine
|
|
NULL, // overlapped structure
|
|
&iosb,
|
|
FSCTL_QUERY_ALLOCATED_RANGES,
|
|
&req_buf,
|
|
sizeof( req_buf ),
|
|
pbuc->DataBuffer.Buffer + pbuc->dwSparseMapSize,
|
|
pbuc->DataBuffer.AllocSize - pbuc->dwSparseMapSize ) ;
|
|
|
|
out_buf_size = 0 ;
|
|
|
|
if ((Status == STATUS_BUFFER_OVERFLOW) || NT_SUCCESS( Status ) ) {
|
|
out_buf_size = (DWORD)iosb.Information ;
|
|
if ( out_buf_size == 0 ) {
|
|
empty_file = TRUE ;
|
|
}
|
|
}
|
|
|
|
if ( out_buf_size != 0 ) {
|
|
pbuc->dwSparseMapSize += out_buf_size ;
|
|
|
|
last_ret_buf =
|
|
(PFILE_ALLOCATED_RANGE_BUFFER)(pbuc->DataBuffer.Buffer +
|
|
pbuc->dwSparseMapSize -
|
|
sizeof(FILE_ALLOCATED_RANGE_BUFFER)) ;
|
|
|
|
req_buf.FileOffset = last_ret_buf->FileOffset ;
|
|
req_buf.FileOffset.QuadPart += last_ret_buf->Length.QuadPart ;
|
|
|
|
//
|
|
// if we can't fit any more in the buffer lets increase
|
|
// the size and get more data otherwise assume were done.
|
|
//
|
|
if ( pbuc->dwSparseMapSize + sizeof(FILE_ALLOCATED_RANGE_BUFFER) >
|
|
pbuc->DataBuffer.AllocSize ) {
|
|
data_size += 4096 ;
|
|
|
|
} else {
|
|
|
|
break ;
|
|
}
|
|
|
|
} else {
|
|
|
|
// reallocate for one more buffer entry
|
|
if ( out_buf_size + sizeof(FILE_ALLOCATED_RANGE_BUFFER) > data_size ) {
|
|
data_size += 4096 ;
|
|
continue ;
|
|
}
|
|
|
|
break ;
|
|
}
|
|
|
|
} else {
|
|
|
|
pbuc->dwSparseMapSize = 0 ;
|
|
break ;
|
|
|
|
}
|
|
|
|
} while ( TRUE ) ;
|
|
|
|
//
|
|
// if there are RANGE_BUFFERS and it isn't simply the whole file, then
|
|
// go into sparse read mode.
|
|
//
|
|
|
|
// hold on to your hat...
|
|
|
|
// If there are no allocated ranges and the file is NOT 0 length
|
|
// then we want to manufacture a record for the file length.
|
|
//
|
|
|
|
if ( (empty_file && ( file_size.QuadPart != 0 )) || (pbuc->dwSparseMapSize >= sizeof( FILE_ALLOCATED_RANGE_BUFFER) ) ) {
|
|
|
|
last_ret_buf = (PFILE_ALLOCATED_RANGE_BUFFER)(pbuc->DataBuffer.Buffer ) ;
|
|
|
|
if ( empty_file ||
|
|
( last_ret_buf->FileOffset.QuadPart != 0 ) ||
|
|
( last_ret_buf->Length.QuadPart != file_size.QuadPart ) ) {
|
|
|
|
|
|
// first lets add a record for the EOF marker
|
|
pbuc->dwSparseMapSize += sizeof(FILE_ALLOCATED_RANGE_BUFFER) ;
|
|
last_ret_buf =
|
|
(PFILE_ALLOCATED_RANGE_BUFFER)(pbuc->DataBuffer.Buffer +
|
|
pbuc->dwSparseMapSize -
|
|
sizeof(FILE_ALLOCATED_RANGE_BUFFER)) ;
|
|
|
|
last_ret_buf->FileOffset.QuadPart = file_size.QuadPart ;
|
|
last_ret_buf->Length.QuadPart = 0 ;
|
|
|
|
pbuc->fSparseBlockStart = TRUE ;
|
|
return TRUE ;
|
|
}
|
|
}
|
|
|
|
pbuc->dwSparseMapSize = 0 ;
|
|
return FALSE ;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BackupReadData (HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read default data for a user BackupRead request.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to file for transfer
|
|
|
|
pbuc - context of call
|
|
|
|
pbif - frame describing BackupRead request
|
|
|
|
Return Value:
|
|
|
|
True if transfer succeeded..
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER licbFile ;
|
|
|
|
//
|
|
// If the context is not initialized for this transfer,
|
|
// set up based on file size.
|
|
//
|
|
|
|
if (pbuc->fStreamStart) {
|
|
|
|
if (pbuc->fAttribs & FILE_ATTRIBUTE_ENCRYPTED) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (pbuc->fAttribs & FILE_ATTRIBUTE_DIRECTORY) {
|
|
return TRUE;
|
|
}
|
|
|
|
licbFile.LowPart = GetFileSize( hFile, &licbFile.HighPart );
|
|
|
|
if (licbFile.QuadPart == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (licbFile.LowPart == 0xffffffff && GetLastError() != NO_ERROR) {
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
pbuc->head.dwStreamId = BACKUP_DATA;
|
|
pbuc->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE;
|
|
|
|
pbuc->head.dwStreamNameSize = 0;
|
|
|
|
pbuc->cbHeader = CB_NAMELESSHEADER;
|
|
pbuc->fStreamStart = FALSE;
|
|
|
|
if ( BackupGetSparseMap( hFile, pbuc, pbif ) ) {
|
|
|
|
pbuc->head.Size.QuadPart = 0 ;
|
|
pbuc->head.dwStreamAttributes = STREAM_SPARSE_ATTRIBUTE;
|
|
|
|
} else {
|
|
|
|
pbuc->head.Size = licbFile;
|
|
|
|
licbFile.HighPart = 0;
|
|
SetFilePointer( hFile, 0, &licbFile.HighPart, FILE_BEGIN );
|
|
}
|
|
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If there's more data for us to read, then go and
|
|
// get it from the stream
|
|
//
|
|
|
|
|
|
return BackupReadStream( hFile, pbuc, pbif );
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BackupReadAlternateData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform a read to user buffer from alternate data streams.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to base file for transfer
|
|
|
|
pbuc - context of call
|
|
|
|
pbif - frame describing BackupRead request
|
|
|
|
Return Value:
|
|
|
|
True if transfer succeeded..
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If we haven't started transferring alternate data streams then
|
|
// buffer all the stream information from the file system
|
|
//
|
|
|
|
if (pbuc->fStreamStart) {
|
|
NTSTATUS Status;
|
|
FILE_STREAM_INFORMATION *pfsi;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
if (pbuc->fAttribs & FILE_ATTRIBUTE_ENCRYPTED) {
|
|
if ( !(pbuc->fAttribs & FILE_ATTRIBUTE_DIRECTORY) ) {
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop, growing the names buffer, until it is large enough to
|
|
// contain all the alternate data
|
|
//
|
|
|
|
if (!pbuc->NamesReady) {
|
|
|
|
if (!GrowBuffer( &pbuc->StreamNameBuffer, 1024 ) ) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
while (TRUE) {
|
|
//
|
|
// Resize the buffer. If we cannot grow it, then fail.
|
|
//
|
|
|
|
Status = NtQueryInformationFile(
|
|
hFile,
|
|
&iosb,
|
|
pbuc->StreamNameBuffer.Buffer,
|
|
pbuc->StreamNameBuffer.BufferSize,
|
|
FileStreamInformation);
|
|
|
|
//
|
|
// If we succeeded in reading some data, set the buffer
|
|
// up and finish initializing
|
|
//
|
|
|
|
if (NT_SUCCESS(Status) && iosb.Information != 0) {
|
|
pbuc->iNameBuffer = 0;
|
|
pbuc->NamesReady = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the error was not due to overflow, then skip
|
|
// all alternate streams
|
|
//
|
|
|
|
if (!BufferOverflow(Status)) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// simply inlarge the buffer and try again.
|
|
//
|
|
if (!GrowBuffer( &pbuc->StreamNameBuffer,
|
|
pbuc->StreamNameBuffer.BufferSize * 2)) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
pbuc->hAlternate = INVALID_HANDLE_VALUE;
|
|
pbuc->fStreamStart = FALSE;
|
|
pfsi = (FILE_STREAM_INFORMATION *) &pbuc->StreamNameBuffer.Buffer[pbuc->iNameBuffer];
|
|
|
|
//
|
|
// Skip first stream if it is the default data stream. This
|
|
// code is NTFS-specific and relies on behaviour not documented anywhere.
|
|
//
|
|
|
|
if (pfsi->StreamNameLength >= 2 * sizeof(WCHAR) &&
|
|
pfsi->StreamName[1] == ':') {
|
|
|
|
if (pfsi->NextEntryOffset == 0) {
|
|
return TRUE; // No more, do next stream type
|
|
}
|
|
|
|
pbuc->iNameBuffer += pfsi->NextEntryOffset;
|
|
|
|
}
|
|
|
|
pbuc->head.Size.LowPart = 1;
|
|
|
|
//
|
|
// If we don't have an open stream
|
|
//
|
|
|
|
} else if (pbuc->hAlternate == INVALID_HANDLE_VALUE) {
|
|
NTSTATUS Status;
|
|
PFILE_STREAM_INFORMATION pfsi;
|
|
UNICODE_STRING strName;
|
|
OBJECT_ATTRIBUTES oa;
|
|
IO_STATUS_BLOCK iosb;
|
|
DWORD reparse_flg = 0 ;
|
|
|
|
pbuc->head.Size.QuadPart = 0;
|
|
|
|
//
|
|
// Form the relative name of the stream and try to
|
|
// open it relative to the base file
|
|
//
|
|
|
|
pfsi = (FILE_STREAM_INFORMATION *) &pbuc->StreamNameBuffer.Buffer[pbuc->iNameBuffer];
|
|
|
|
strName.Length = (USHORT) pfsi->StreamNameLength;
|
|
strName.MaximumLength = strName.Length;
|
|
strName.Buffer = pfsi->StreamName;
|
|
|
|
|
|
if (pbuc->fAttribs & FILE_ATTRIBUTE_REPARSE_POINT ) {
|
|
|
|
reparse_flg = FILE_OPEN_REPARSE_POINT ;
|
|
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&oa,
|
|
&strName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hFile,
|
|
NULL);
|
|
|
|
Status = NtOpenFile(
|
|
&pbuc->hAlternate,
|
|
FILE_READ_DATA | SYNCHRONIZE,
|
|
&oa,
|
|
&iosb,
|
|
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | reparse_flg);
|
|
|
|
//
|
|
// If we did not succeed, skip this entry and set up for another stream
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
pbuc->iNameBuffer += pfsi->NextEntryOffset;
|
|
if (pfsi->NextEntryOffset != 0) {
|
|
pbuc->head.Size.LowPart = 1;
|
|
pbuc->fMultiStreamType = TRUE; // more to come
|
|
}
|
|
SetLastError( ERROR_SHARING_VIOLATION );
|
|
return FALSE;
|
|
}
|
|
|
|
// if we can't lock all records, return an error
|
|
if (!LockFile( pbuc->hAlternate, 0, 0, 0xffffffff, 0xffffffff )) {
|
|
SetLastError( ERROR_SHARING_VIOLATION );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Perform common header initialization
|
|
//
|
|
|
|
pbuc->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE;
|
|
pbuc->head.dwStreamNameSize = pfsi->StreamNameLength;
|
|
|
|
pbuc->cbHeader = CB_NAMELESSHEADER + pfsi->StreamNameLength;
|
|
|
|
RtlCopyMemory(
|
|
pbuc->head.cStreamName,
|
|
pfsi->StreamName,
|
|
pfsi->StreamNameLength);
|
|
|
|
//
|
|
// Advance to the next stream in the stream information block
|
|
//
|
|
|
|
if (pfsi->NextEntryOffset != 0) {
|
|
pbuc->iNameBuffer += pfsi->NextEntryOffset;
|
|
pbuc->fMultiStreamType = TRUE;
|
|
}
|
|
|
|
//
|
|
// If we are a data stream, set up for data stream copy
|
|
//
|
|
|
|
if (BasepIsDataAttribute( pfsi->StreamNameLength, pfsi->StreamName )) {
|
|
|
|
pbuc->head.dwStreamId = BACKUP_ALTERNATE_DATA;
|
|
|
|
|
|
if ( BackupGetSparseMap( pbuc->hAlternate, pbuc, pbif ) ) {
|
|
|
|
pbuc->head.Size.QuadPart = 0 ;
|
|
pbuc->head.dwStreamAttributes = STREAM_SPARSE_ATTRIBUTE;
|
|
|
|
} else {
|
|
|
|
pbuc->head.Size.LowPart = GetFileSize(
|
|
pbuc->hAlternate,
|
|
&pbuc->head.Size.HighPart );
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we need to return the name
|
|
//
|
|
} else if ( pbuc->liStreamOffset < pbuc->cbHeader) {
|
|
return TRUE ;
|
|
|
|
//
|
|
// If there is more data in this stream to transfer
|
|
//
|
|
|
|
} else if ( (pbuc->head.dwStreamId == BACKUP_ALTERNATE_DATA) ||
|
|
(pbuc->head.dwStreamId == BACKUP_SPARSE_BLOCK) ) {
|
|
|
|
return BackupReadStream( pbuc->hAlternate, pbuc, pbif );
|
|
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BackupReadEaData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform a read to user buffer from EA data.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to file with EAs
|
|
|
|
pbuc - context of call
|
|
|
|
pbif - frame describing BackupRead request
|
|
|
|
Return Value:
|
|
|
|
True if transfer succeeded..
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If we are just starting out on the EA data
|
|
//
|
|
|
|
if (pbuc->fStreamStart) {
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
//
|
|
// Loop trying to read all EA data into the buffer and
|
|
// resize the buffer if necessary
|
|
//
|
|
|
|
while (TRUE) {
|
|
NTSTATUS Status;
|
|
FILE_EA_INFORMATION fei;
|
|
|
|
Status = NtQueryEaFile(
|
|
hFile,
|
|
&iosb,
|
|
pbuc->DataBuffer.Buffer,
|
|
pbuc->DataBuffer.BufferSize,
|
|
FALSE,
|
|
NULL,
|
|
0,
|
|
0,
|
|
(BOOLEAN) TRUE );
|
|
|
|
//
|
|
// If we successfully read all the data, go complete
|
|
// the initialization
|
|
//
|
|
if (NT_SUCCESS( Status ) && iosb.Information != 0) {
|
|
pbuc->NamesReady = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we received a status OTHER than buffer overflow then
|
|
// skip EA's altogether
|
|
//
|
|
|
|
if (!BufferOverflow(Status)) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Get a stab at the total EA size
|
|
//
|
|
|
|
Status = NtQueryInformationFile(
|
|
hFile,
|
|
&iosb,
|
|
&fei,
|
|
sizeof(fei),
|
|
FileEaInformation);
|
|
|
|
//
|
|
// This call should never have failed (since we were able to
|
|
// QueryEaFile) above. However, if it does, skip EAs altogether
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Resize the buffer to something that seems reasonable. No guarantees
|
|
// about whether this will work or not... If we couldn't grow the buffer
|
|
// fail this call.
|
|
//
|
|
|
|
if (!GrowBuffer( &pbuc->DataBuffer, (fei.EaSize * 5) / 4)) {
|
|
pbuc->fAccessError = TRUE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set up the header for the EA stream
|
|
//
|
|
|
|
pbuc->head.dwStreamId = BACKUP_EA_DATA;
|
|
pbuc->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE;
|
|
pbuc->head.dwStreamNameSize = 0;
|
|
|
|
pbuc->cbHeader = CB_NAMELESSHEADER;
|
|
|
|
pbuc->head.Size.QuadPart = iosb.Information;
|
|
|
|
pbuc->fStreamStart = FALSE;
|
|
|
|
//
|
|
// If we have more data in the buffer to read then go
|
|
// copy it out.
|
|
//
|
|
|
|
} else if (pbuc->liStreamOffset >= pbuc->cbHeader) {
|
|
BackupReadBuffer( pbuc, pbif );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BackupReadObjectId(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform a read to user buffer from NtObject ID data.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to file with EAs
|
|
|
|
pbuc - context of call
|
|
|
|
pbif - frame describing BackupRead request
|
|
|
|
Return Value:
|
|
|
|
True if transfer succeeded..
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If we are just starting out on the Object ID data
|
|
//
|
|
|
|
if (pbuc->fStreamStart) {
|
|
IO_STATUS_BLOCK iosb;
|
|
NTSTATUS Status ;
|
|
|
|
if (!GrowBuffer( &pbuc->DataBuffer, 1024 ) ) {
|
|
pbuc->fAccessError = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
Status = NtFsControlFile( hFile,
|
|
NULL, // overlapped event handle
|
|
NULL, // Apc routine
|
|
NULL, // overlapped structure
|
|
&iosb,
|
|
FSCTL_GET_OBJECT_ID,
|
|
NULL,
|
|
0,
|
|
pbuc->DataBuffer.Buffer,
|
|
pbuc->DataBuffer.BufferSize ) ;
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return TRUE ;
|
|
}
|
|
|
|
//
|
|
// Set up the header for the Object ID stream
|
|
//
|
|
|
|
pbuc->NamesReady = TRUE;
|
|
|
|
pbuc->head.dwStreamId = BACKUP_OBJECT_ID ;
|
|
pbuc->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE;
|
|
pbuc->head.dwStreamNameSize = 0;
|
|
|
|
pbuc->cbHeader = CB_NAMELESSHEADER;
|
|
|
|
pbuc->head.Size.QuadPart = iosb.Information;
|
|
|
|
pbuc->fStreamStart = FALSE;
|
|
|
|
//
|
|
// If we have more data in the buffer to read then go
|
|
// copy it out.
|
|
//
|
|
|
|
} else if (pbuc->liStreamOffset >= pbuc->cbHeader) {
|
|
BackupReadBuffer( pbuc, pbif );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BackupReadReparseData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform a read to user buffer from Reparse tag data.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to file with EAs
|
|
|
|
pbuc - context of call
|
|
|
|
pbif - frame describing BackupRead request
|
|
|
|
Return Value:
|
|
|
|
True if transfer succeeded..
|
|
|
|
--*/
|
|
{
|
|
|
|
IO_STATUS_BLOCK iosb;
|
|
PREPARSE_DATA_BUFFER rp_buf_ptr ;
|
|
NTSTATUS Status ;
|
|
|
|
struct RP_SUMMARY {
|
|
USHORT tag ;
|
|
USHORT rp_size ;
|
|
} *rp_summary_ptr =(struct RP_SUMMARY*) &(iosb.Information) ;
|
|
|
|
|
|
//
|
|
// If the object is not a reparse then simply return
|
|
//
|
|
if ( !(pbuc->fAttribs & FILE_ATTRIBUTE_REPARSE_POINT) ) {
|
|
return TRUE ;
|
|
}
|
|
|
|
//
|
|
// If we are just starting out on the ReParse data
|
|
//
|
|
|
|
if (pbuc->fStreamStart) {
|
|
|
|
//
|
|
// Loop trying to read all EA data into the buffer and
|
|
// resize the buffer if necessary
|
|
//
|
|
|
|
// for some reason a TOO_SMALL error is not setting the information
|
|
// member of the iosb....
|
|
|
|
rp_summary_ptr->rp_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE ;
|
|
|
|
Status = NtFsControlFile( hFile,
|
|
NULL, // overlapped event handle
|
|
NULL, // Apc routine
|
|
NULL, // overlapped structure
|
|
&iosb,
|
|
FSCTL_GET_REPARSE_POINT,
|
|
NULL,
|
|
0,
|
|
pbuc->DataBuffer.Buffer,
|
|
pbuc->DataBuffer.BufferSize ) ;
|
|
|
|
|
|
if ( BufferOverflow( Status ) ) {
|
|
|
|
if ( rp_summary_ptr->rp_size == 0 ) {
|
|
rp_summary_ptr->rp_size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE ;
|
|
}
|
|
|
|
if (!GrowBuffer( &pbuc->DataBuffer,
|
|
rp_summary_ptr->rp_size ) ) {
|
|
|
|
pbuc->fAccessError = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
Status = NtFsControlFile( hFile,
|
|
NULL, // overlapped event handle
|
|
NULL, // Apc routine
|
|
NULL, // overlapped structure
|
|
&iosb,
|
|
FSCTL_GET_REPARSE_POINT,
|
|
NULL,
|
|
0,
|
|
pbuc->DataBuffer.Buffer,
|
|
pbuc->DataBuffer.BufferSize ) ;
|
|
|
|
}
|
|
|
|
//
|
|
// If we successfully read all the data, go complete
|
|
// the initialization
|
|
//
|
|
if ( !NT_SUCCESS( Status ) ) {
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
//
|
|
// Set up the header for the ReParse stream
|
|
//
|
|
|
|
rp_buf_ptr = (PREPARSE_DATA_BUFFER)(pbuc->DataBuffer.Buffer) ;
|
|
|
|
pbuc->NamesReady = TRUE;
|
|
|
|
pbuc->head.dwStreamId = BACKUP_REPARSE_DATA;
|
|
pbuc->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE;
|
|
pbuc->head.dwStreamNameSize = 0;
|
|
|
|
pbuc->cbHeader = CB_NAMELESSHEADER;
|
|
|
|
if ( IsReparseTagMicrosoft( rp_buf_ptr->ReparseTag ) ) {
|
|
pbuc->head.Size.QuadPart = rp_buf_ptr->ReparseDataLength +
|
|
FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) ;
|
|
} else {
|
|
pbuc->head.Size.QuadPart = rp_buf_ptr->ReparseDataLength +
|
|
FIELD_OFFSET(REPARSE_GUID_DATA_BUFFER, GenericReparseBuffer.DataBuffer) ;
|
|
}
|
|
|
|
pbuc->fStreamStart = FALSE;
|
|
|
|
//
|
|
// If we have more data in the buffer to read then go
|
|
// copy it out.
|
|
//
|
|
|
|
} else if (pbuc->liStreamOffset >= pbuc->cbHeader) {
|
|
BackupReadBuffer( pbuc, pbif );
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BackupReadSecurityData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
{
|
|
//
|
|
// If we are to skip security then do so.
|
|
//
|
|
|
|
if (!pbif->fProcessSecurity) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If we are just starting out on the security data
|
|
//
|
|
|
|
if (pbuc->fStreamStart) {
|
|
|
|
//
|
|
// Loop trying to read all security data into the buffer and
|
|
// resize the buffer if necessary
|
|
//
|
|
|
|
while (TRUE) {
|
|
NTSTATUS Status;
|
|
DWORD cbSecurityInfo;
|
|
|
|
RtlZeroMemory( pbuc->DataBuffer.Buffer, pbuc->DataBuffer.BufferSize );
|
|
|
|
Status = NtQuerySecurityObject(
|
|
hFile,
|
|
OWNER_SECURITY_INFORMATION |
|
|
GROUP_SECURITY_INFORMATION |
|
|
DACL_SECURITY_INFORMATION |
|
|
SACL_SECURITY_INFORMATION,
|
|
pbuc->DataBuffer.Buffer,
|
|
pbuc->DataBuffer.BufferSize,
|
|
&cbSecurityInfo );
|
|
|
|
//
|
|
// If we failed but it wasn't due to buffer overflow
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status ) && !BufferOverflow( Status )) {
|
|
|
|
//
|
|
// Try reading everything but SACL
|
|
//
|
|
|
|
Status = NtQuerySecurityObject(
|
|
hFile,
|
|
OWNER_SECURITY_INFORMATION |
|
|
GROUP_SECURITY_INFORMATION |
|
|
DACL_SECURITY_INFORMATION,
|
|
pbuc->DataBuffer.Buffer,
|
|
pbuc->DataBuffer.BufferSize,
|
|
&cbSecurityInfo );
|
|
}
|
|
|
|
//
|
|
// If we got it all, then go continue initialization
|
|
//
|
|
|
|
if (NT_SUCCESS( Status )) {
|
|
pbuc->NamesReady = TRUE;
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// If not due to overflowing buffer, skip security altogether
|
|
//
|
|
|
|
if (!BufferOverflow( Status )) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Resize the buffer to the expected size. If we fail, fail
|
|
// the entire call
|
|
//
|
|
|
|
if (!GrowBuffer( &pbuc->DataBuffer, cbSecurityInfo )) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize the stream header
|
|
//
|
|
|
|
pbuc->head.dwStreamId = BACKUP_SECURITY_DATA;
|
|
pbuc->head.dwStreamAttributes = STREAM_CONTAINS_SECURITY;
|
|
pbuc->head.dwStreamNameSize = 0;
|
|
|
|
pbuc->cbHeader = CB_NAMELESSHEADER;
|
|
|
|
pbuc->head.Size.QuadPart = RtlLengthSecurityDescriptor(pbuc->DataBuffer.Buffer);
|
|
|
|
pbuc->fStreamStart = FALSE;
|
|
|
|
//
|
|
// If there is more data in the buffer to transfer, go
|
|
// do it
|
|
//
|
|
} else if (pbuc->liStreamOffset >= pbuc->cbHeader) {
|
|
|
|
BackupReadBuffer( pbuc, pbif );
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
BackupTestRestartStream(BACKUPCONTEXT *pbuc)
|
|
{
|
|
LONGLONG licbRemain;
|
|
|
|
licbRemain = ComputeRemainingSize( pbuc );
|
|
if (licbRemain == 0) {
|
|
|
|
if ( pbuc->dwSparseMapOffset != pbuc->dwSparseMapSize ) { // only true at backup
|
|
|
|
if ( !pbuc->fSparseBlockStart ) {
|
|
pbuc->dwSparseMapOffset += sizeof ( FILE_ALLOCATED_RANGE_BUFFER ) ;
|
|
}
|
|
}
|
|
|
|
if ( pbuc->dwSparseMapOffset != pbuc->dwSparseMapSize ) { // only true at backup
|
|
pbuc->fSparseBlockStart = TRUE ;
|
|
|
|
pbuc->cbHeader = 0 ;
|
|
pbuc->liStreamOffset = 0;
|
|
|
|
} else {
|
|
if ( !pbuc->fSparseHandAlt && (pbuc->hAlternate != NULL)) {
|
|
CloseHandle(pbuc->hAlternate); // releases any locks
|
|
pbuc->hAlternate = NULL;
|
|
}
|
|
pbuc->cbHeader = 0;
|
|
pbuc->fStreamStart = TRUE;
|
|
pbuc->fSparseBlockStart = TRUE;
|
|
|
|
pbuc->liStreamOffset = 0; // for BackupWrite
|
|
|
|
if (!pbuc->fMultiStreamType) { // for BackupRead
|
|
pbuc->StreamIndex++;
|
|
pbuc->head.dwStreamId = mwStreamList[pbuc->StreamIndex] ;
|
|
pbuc->NamesReady = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
//
|
|
//
|
|
// NOTE:
|
|
// The NT File Replication Service (NTFRS) performs an MD5 checksum on the
|
|
// stream of data returned by BackupRead(). If the sequence of file information
|
|
// returned changes then two machines, one downlevel and one uplevel will
|
|
// compute different MD5 checksums for the same file data. Under certain
|
|
// conditions this will cause needless file replication. Bear this in mind
|
|
// if a change in the returned data sequence is contemplated. The sources for
|
|
// NTFRS are in \nt\private\net\svcimgs\ntrepl.
|
|
//
|
|
|
|
BOOL WINAPI
|
|
BackupRead(
|
|
HANDLE hFile,
|
|
LPBYTE lpBuffer,
|
|
DWORD nNumberOfBytesToRead,
|
|
LPDWORD lpNumberOfBytesRead,
|
|
BOOL bAbort,
|
|
BOOL bProcessSecurity,
|
|
LPVOID *lpContext)
|
|
{
|
|
BACKUPCONTEXT *pbuc;
|
|
BACKUPIOFRAME bif;
|
|
BOOL fSuccess = FALSE;
|
|
IO_STATUS_BLOCK iosb ;
|
|
|
|
pbuc = *lpContext;
|
|
bif.pIoBuffer = lpBuffer;
|
|
bif.cbRequest = nNumberOfBytesToRead;
|
|
bif.pcbTransferred = lpNumberOfBytesRead;
|
|
bif.fProcessSecurity = (BOOLEAN)bProcessSecurity;
|
|
|
|
if (bAbort) {
|
|
if (pbuc != NULL) {
|
|
ResetAccessDate( hFile ) ;
|
|
FreeContext(lpContext);
|
|
}
|
|
return TRUE;
|
|
}
|
|
*bif.pcbTransferred = 0;
|
|
|
|
if (pbuc == INVALID_HANDLE_VALUE || bif.cbRequest == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (pbuc != NULL && mwStreamList[pbuc->StreamIndex] == BACKUP_INVALID) {
|
|
ResetAccessDate( hFile ) ;
|
|
FreeContext(lpContext);
|
|
return TRUE;
|
|
}
|
|
|
|
// Allocate our Context Control Block on first call.
|
|
|
|
if (pbuc == NULL) {
|
|
pbuc = AllocContext(CBMIN_BUFFER); // Alloc initial buffer
|
|
|
|
// ok, we allocated the context, Lets initialize it.
|
|
if (pbuc != NULL) {
|
|
NTSTATUS Status ;
|
|
FILE_BASIC_INFORMATION fbi;
|
|
|
|
Status = NtQueryInformationFile(
|
|
hFile,
|
|
&iosb,
|
|
&fbi,
|
|
sizeof(fbi),
|
|
FileBasicInformation );
|
|
|
|
if ( NT_SUCCESS( Status ) ) {
|
|
pbuc->fAttribs = fbi.FileAttributes ;
|
|
} else {
|
|
BaseSetLastNTError( Status );
|
|
return FALSE ;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pbuc != NULL) {
|
|
*lpContext = pbuc;
|
|
|
|
do {
|
|
|
|
if (pbuc->fStreamStart) {
|
|
pbuc->head.Size.QuadPart = 0;
|
|
|
|
pbuc->liStreamOffset = 0;
|
|
|
|
pbuc->dwSparseMapOffset = 0;
|
|
pbuc->dwSparseMapSize = 0;
|
|
|
|
pbuc->fMultiStreamType = FALSE;
|
|
}
|
|
fSuccess = TRUE;
|
|
|
|
switch (mwStreamList[pbuc->StreamIndex]) {
|
|
case BACKUP_DATA:
|
|
fSuccess = BackupReadData(hFile, pbuc, &bif);
|
|
break;
|
|
|
|
case BACKUP_ALTERNATE_DATA:
|
|
fSuccess = BackupReadAlternateData(hFile, pbuc, &bif);
|
|
break;
|
|
|
|
case BACKUP_EA_DATA:
|
|
fSuccess = BackupReadEaData(hFile, pbuc, &bif);
|
|
break;
|
|
|
|
case BACKUP_OBJECT_ID:
|
|
fSuccess = BackupReadObjectId(hFile, pbuc, &bif);
|
|
break;
|
|
|
|
case BACKUP_REPARSE_DATA:
|
|
fSuccess = BackupReadReparseData(hFile, pbuc, &bif);
|
|
break;
|
|
|
|
case BACKUP_SECURITY_DATA:
|
|
fSuccess = BackupReadSecurityData(hFile, pbuc, &bif);
|
|
break;
|
|
|
|
default:
|
|
pbuc->StreamIndex++;
|
|
pbuc->fStreamStart = TRUE;
|
|
break;
|
|
}
|
|
|
|
// if we're in the phase of reading the header, copy the header
|
|
|
|
if (pbuc->liStreamOffset < pbuc->cbHeader) {
|
|
|
|
DWORD cbrequest;
|
|
|
|
// Send the current stream header;
|
|
|
|
cbrequest =
|
|
(ULONG)min( pbuc->cbHeader - pbuc->liStreamOffset,
|
|
bif.cbRequest);
|
|
|
|
RtlCopyMemory(
|
|
bif.pIoBuffer,
|
|
(BYTE *) &pbuc->head + pbuc->liStreamOffset,
|
|
cbrequest);
|
|
|
|
ReportTransfer(pbuc, &bif, cbrequest);
|
|
}
|
|
|
|
//
|
|
// if we are at the end of a stream then
|
|
// start at the beginning of the next stream
|
|
//
|
|
|
|
if (pbuc->liStreamOffset >= pbuc->cbHeader) {
|
|
BackupTestRestartStream(pbuc);
|
|
}
|
|
|
|
} while (fSuccess &&
|
|
mwStreamList[pbuc->StreamIndex] != BACKUP_INVALID &&
|
|
bif.cbRequest != 0);
|
|
}
|
|
|
|
if (fSuccess && *bif.pcbTransferred == 0) {
|
|
ResetAccessDate( hFile ) ;
|
|
FreeContext(lpContext);
|
|
}
|
|
|
|
return(fSuccess);
|
|
}
|
|
|
|
|
|
|
|
// 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 actually seeked is returned.
|
|
// 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 actually skiped over is returned.
|
|
//
|
|
// 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 actually seeked is returned.
|
|
|
|
BOOL WINAPI
|
|
BackupSeek(
|
|
HANDLE hFile,
|
|
DWORD dwLowBytesToSeek,
|
|
DWORD dwHighBytesToSeek,
|
|
LPDWORD lpdwLowBytesSeeked,
|
|
LPDWORD lpdwHighBytesSeeked,
|
|
LPVOID *lpContext)
|
|
{
|
|
BACKUPCONTEXT *pbuc;
|
|
LONGLONG licbRemain;
|
|
LARGE_INTEGER licbRequest;
|
|
BOOL fSuccess;
|
|
LARGE_INTEGER sparse_bytes ;
|
|
|
|
pbuc = *lpContext;
|
|
|
|
sparse_bytes.QuadPart = 0 ;
|
|
|
|
*lpdwHighBytesSeeked = 0;
|
|
*lpdwLowBytesSeeked = 0;
|
|
|
|
if (pbuc == INVALID_HANDLE_VALUE || pbuc == NULL || pbuc->fStreamStart) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (pbuc->liStreamOffset < pbuc->cbHeader) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If we made it here, we are in the middle of a stream
|
|
//
|
|
|
|
licbRemain = ComputeRemainingSize( pbuc );
|
|
|
|
licbRequest.LowPart = dwLowBytesToSeek;
|
|
licbRequest.HighPart = dwHighBytesToSeek & 0x7fffffff;
|
|
|
|
if (licbRequest.QuadPart > licbRemain) {
|
|
licbRequest.QuadPart = licbRemain;
|
|
}
|
|
fSuccess = TRUE;
|
|
|
|
switch (pbuc->head.dwStreamId) {
|
|
case BACKUP_EA_DATA:
|
|
case BACKUP_SECURITY_DATA:
|
|
case BACKUP_OBJECT_ID :
|
|
case BACKUP_REPARSE_DATA :
|
|
|
|
// assume less than 2gig of data
|
|
|
|
break;
|
|
|
|
case BACKUP_SPARSE_BLOCK :
|
|
if ( pbuc->liStreamOffset < sizeof(LARGE_INTEGER) ) {
|
|
sparse_bytes.QuadPart = ( sizeof(LARGE_INTEGER) - pbuc->liStreamOffset ) ;
|
|
if ( sparse_bytes.QuadPart < licbRequest.QuadPart ) {
|
|
licbRequest.QuadPart -= sparse_bytes.QuadPart ;
|
|
} else {
|
|
licbRequest.QuadPart = 0 ;
|
|
}
|
|
}
|
|
case BACKUP_DATA:
|
|
case BACKUP_ALTERNATE_DATA:
|
|
{
|
|
LARGE_INTEGER liCurPos;
|
|
LARGE_INTEGER liNewPos;
|
|
HANDLE hf;
|
|
|
|
// set up the correct handle to seek with
|
|
|
|
if (pbuc->head.dwStreamId == BACKUP_DATA) {
|
|
hf = hFile;
|
|
}
|
|
else {
|
|
hf = pbuc->hAlternate;
|
|
}
|
|
|
|
// first, let's get the current position
|
|
|
|
liCurPos.HighPart = 0;
|
|
liCurPos.LowPart = SetFilePointer(
|
|
hf,
|
|
0,
|
|
&liCurPos.HighPart,
|
|
FILE_CURRENT);
|
|
|
|
// Now seek the requested number of bytes
|
|
|
|
liNewPos.HighPart = licbRequest.HighPart;
|
|
liNewPos.LowPart = SetFilePointer(
|
|
hf,
|
|
licbRequest.LowPart,
|
|
&liNewPos.HighPart,
|
|
FILE_CURRENT);
|
|
|
|
// Assume that we seek the requested amount because if we do not,
|
|
// subsequent reads will fail and the caller will never be able
|
|
// to read to the next stream.
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (dwHighBytesToSeek != (DWORD) licbRequest.HighPart ||
|
|
dwLowBytesToSeek != licbRequest.LowPart) {
|
|
fSuccess = FALSE;
|
|
}
|
|
licbRequest.QuadPart += sparse_bytes.QuadPart ;
|
|
pbuc->liStreamOffset += licbRequest.QuadPart ;
|
|
|
|
*lpdwLowBytesSeeked = licbRequest.LowPart;
|
|
*lpdwHighBytesSeeked = licbRequest.HighPart;
|
|
|
|
BackupTestRestartStream(pbuc);
|
|
|
|
if (!fSuccess) {
|
|
SetLastError(ERROR_SEEK);
|
|
}
|
|
return(fSuccess);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BackupWriteHeader(BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif, DWORD cbHeader)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that fills our internal backup header
|
|
from the user's data.
|
|
|
|
Arguments:
|
|
|
|
pbuc - CONTEXT of call
|
|
|
|
pbif - IOCONTEXT of call
|
|
|
|
cbHeader - size of header to fill
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Determine how much data we can transfer into our header.
|
|
//
|
|
|
|
DWORD cbrequest =
|
|
(DWORD) min( pbif->cbRequest, cbHeader - pbuc->liStreamOffset );
|
|
|
|
//
|
|
// Copy from user buffer into header
|
|
//
|
|
|
|
|
|
if ( pbuc->liStreamOffset+cbrequest > CWCMAX_STREAMNAME + CB_NAMELESSHEADER ) {
|
|
return FALSE ;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
(CHAR *) &pbuc->head + pbuc->liStreamOffset,
|
|
pbif->pIoBuffer,
|
|
cbrequest);
|
|
|
|
//
|
|
// Update transfer statistics
|
|
//
|
|
|
|
ReportTransfer(pbuc, pbif, cbrequest);
|
|
|
|
//
|
|
// If we've filled up the header, mark the header as complete
|
|
// even though we might need more if names are present
|
|
//
|
|
|
|
if (pbuc->liStreamOffset == cbHeader) {
|
|
pbuc->cbHeader = cbHeader;
|
|
}
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
|
|
typedef enum {
|
|
BRB_FAIL,
|
|
BRB_DONE,
|
|
BRB_MORE,
|
|
} BUFFERSTATUS;
|
|
|
|
BUFFERSTATUS
|
|
BackupWriteBuffer(BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that fills our internal buffer
|
|
from the user's data.
|
|
|
|
Arguments:
|
|
|
|
pbuc - CONTEXT of call
|
|
|
|
pbif - IOCONTEXT of call
|
|
|
|
Return Value:
|
|
|
|
BRB_FAIL if an error occurred (out of memory)
|
|
|
|
BRB_DONE if buffer is full or was successfully filled
|
|
|
|
BRB_MORE if buffer is partially full
|
|
|
|
--*/
|
|
{
|
|
DWORD cbrequest;
|
|
|
|
//
|
|
// If we're starting out on the buffer, we make sure
|
|
// we have a buffer to contain all of the data since
|
|
// the Nt calls we'll use must have all the data
|
|
// present
|
|
//
|
|
|
|
if (pbuc->fStreamStart) {
|
|
pbuc->fStreamStart = FALSE;
|
|
|
|
if (pbuc->DataBuffer.BufferSize < pbuc->head.Size.QuadPart &&
|
|
!GrowBuffer( &pbuc->DataBuffer, pbuc->head.Size.LowPart )) {
|
|
|
|
return(BRB_FAIL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine how much data from the user buffer is
|
|
// needed to fill our buffer
|
|
//
|
|
|
|
cbrequest = ComputeRequestSize( pbuc, pbif->cbRequest );
|
|
|
|
//
|
|
// Fill in the next portion of the buffer
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
pbuc->DataBuffer.Buffer + pbuc->liStreamOffset - pbuc->cbHeader,
|
|
pbif->pIoBuffer,
|
|
cbrequest);
|
|
|
|
//
|
|
// Update transfer statistics
|
|
//
|
|
|
|
ReportTransfer(pbuc, pbif, cbrequest);
|
|
|
|
//
|
|
// If we've entirely filled the buffer, let our caller know
|
|
//
|
|
|
|
return ComputeRemainingSize( pbuc ) == 0 ? BRB_DONE : BRB_MORE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BackupWriteSparse(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that writes sparse block of stream data from
|
|
the user's buffer into the output handle. The BACKUPCONTEXT contains
|
|
the total length of data to be output.
|
|
|
|
Arguments:
|
|
|
|
hFile - output file handle
|
|
|
|
pbuc - CONTEXT of call
|
|
|
|
pbif - IOCONTEXT of call
|
|
|
|
Return Value:
|
|
|
|
TRUE if data was successfully written, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER licbFile ;
|
|
DWORD cbrequest;
|
|
DWORD cbtransferred;
|
|
BOOL fSuccess;
|
|
|
|
if ( pbuc->fSparseBlockStart ) {
|
|
|
|
RtlCopyMemory( &pbuc->cbSparseOffset, pbuc->head.cStreamName, sizeof( LARGE_INTEGER ) ) ;
|
|
|
|
licbFile = pbuc->cbSparseOffset;
|
|
|
|
licbFile.LowPart = SetFilePointer( pbuc->fSparseHandAlt?pbuc->hAlternate:hFile,
|
|
licbFile.LowPart,
|
|
&licbFile.HighPart,
|
|
FILE_BEGIN );
|
|
|
|
if ( licbFile.QuadPart != pbuc->cbSparseOffset.QuadPart ) {
|
|
return FALSE ;
|
|
}
|
|
|
|
if ( pbuc->head.Size.QuadPart == sizeof( LARGE_INTEGER ) ) {
|
|
SetEndOfFile(pbuc->fSparseHandAlt?pbuc->hAlternate:hFile) ;
|
|
}
|
|
pbuc->fSparseBlockStart = FALSE ;
|
|
}
|
|
|
|
//
|
|
// Determine how much data from the user buffer is
|
|
// needed to be written into the stream and perform
|
|
// the transfer.
|
|
//
|
|
|
|
cbrequest = ComputeRequestSize(pbuc, pbif->cbRequest);
|
|
|
|
fSuccess = WriteFile(
|
|
pbuc->fSparseHandAlt?pbuc->hAlternate:hFile,
|
|
pbif->pIoBuffer,
|
|
cbrequest,
|
|
&cbtransferred,
|
|
NULL);
|
|
|
|
//
|
|
// Update transfer statistics
|
|
//
|
|
|
|
ReportTransfer(pbuc, pbif, cbtransferred);
|
|
|
|
return(fSuccess);
|
|
|
|
return TRUE ;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BackupWriteStream(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that writes stream data from the user's
|
|
buffer into the output handle. The BACKUPCONTEXT contains the total
|
|
length of data to be output.
|
|
|
|
Arguments:
|
|
|
|
hFile - output file handle
|
|
|
|
pbuc - CONTEXT of call
|
|
|
|
pbif - IOCONTEXT of call
|
|
|
|
Return Value:
|
|
|
|
TRUE if data was successfully written, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
DWORD cbrequest;
|
|
DWORD cbtransferred;
|
|
BOOL fSuccess;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
|
|
if ( pbuc->fStreamStart ) {
|
|
|
|
if ( pbuc->head.dwStreamAttributes & STREAM_SPARSE_ATTRIBUTE ) {
|
|
|
|
// if it was sparse when be backed it up make is sparse again.
|
|
NtFsControlFile( hFile,
|
|
NULL, // overlapped event handle
|
|
NULL, // Apc routine
|
|
NULL, // overlapped structure
|
|
&iosb,
|
|
FSCTL_SET_SPARSE ,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0 ) ;
|
|
|
|
} else {
|
|
|
|
LARGE_INTEGER end_of_file ;
|
|
|
|
end_of_file.QuadPart = pbuc->head.Size.QuadPart ;
|
|
SetFilePointer( hFile,
|
|
end_of_file.LowPart,
|
|
&end_of_file.HighPart,
|
|
FILE_BEGIN );
|
|
|
|
SetEndOfFile(hFile) ;
|
|
|
|
end_of_file.QuadPart = 0 ;
|
|
SetFilePointer( hFile,
|
|
end_of_file.LowPart,
|
|
&end_of_file.HighPart,
|
|
FILE_BEGIN );
|
|
|
|
|
|
}
|
|
|
|
pbuc->fStreamStart = FALSE;
|
|
}
|
|
|
|
//
|
|
// Determine how much data from the user buffer is
|
|
// needed to be written into the stream and perform
|
|
// the transfer.
|
|
//
|
|
|
|
cbrequest = ComputeRequestSize(pbuc, pbif->cbRequest);
|
|
|
|
fSuccess = WriteFile(
|
|
hFile,
|
|
pbif->pIoBuffer,
|
|
cbrequest,
|
|
&cbtransferred,
|
|
NULL);
|
|
|
|
//
|
|
// Update transfer statistics
|
|
//
|
|
|
|
ReportTransfer(pbuc, pbif, cbtransferred);
|
|
|
|
return(fSuccess);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BackupWriteAlternateData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that overwrites an alternate data stream with
|
|
data from the user's buffer.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to the file itself. This is not the handle to the stream
|
|
being overwritten.
|
|
|
|
pbuc - CONTEXT of call. This contains the name of the stream.
|
|
|
|
pbif - IOCONTEXT of call
|
|
|
|
Return Value:
|
|
|
|
TRUE if data was successfully written, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If we are just starting out on this stream then attempt to
|
|
// overwrite the new stream.
|
|
//
|
|
|
|
if (pbuc->fStreamStart) {
|
|
NTSTATUS Status;
|
|
UNICODE_STRING strName;
|
|
OBJECT_ATTRIBUTES oa;
|
|
IO_STATUS_BLOCK iosb;
|
|
DWORD reparse_flg = 0 ;
|
|
|
|
strName.Length = (USHORT) pbuc->head.dwStreamNameSize;
|
|
strName.MaximumLength = strName.Length;
|
|
strName.Buffer = pbuc->head.cStreamName;
|
|
|
|
if (pbuc->hAlternate != INVALID_HANDLE_VALUE) {
|
|
CloseHandle(pbuc->hAlternate);
|
|
pbuc->hAlternate = INVALID_HANDLE_VALUE;
|
|
pbuc->fSparseHandAlt = FALSE ;
|
|
}
|
|
|
|
|
|
if (pbuc->fAttribs & FILE_ATTRIBUTE_REPARSE_POINT ) {
|
|
reparse_flg = FILE_OPEN_REPARSE_POINT ;
|
|
}
|
|
|
|
InitializeObjectAttributes(
|
|
&oa,
|
|
&strName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
hFile,
|
|
NULL);
|
|
|
|
Status = NtCreateFile(
|
|
&pbuc->hAlternate,
|
|
FILE_WRITE_DATA | SYNCHRONIZE,
|
|
&oa,
|
|
&iosb,
|
|
NULL,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE|FILE_SHARE_DELETE,
|
|
FILE_OVERWRITE_IF,
|
|
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT | reparse_flg,
|
|
NULL,
|
|
0L);
|
|
|
|
//
|
|
// If we failed, map the error, record the failure, and return failure.
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
BaseSetLastNTError( Status );
|
|
pbuc->fAccessError = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
if ( pbuc->head.dwStreamAttributes & STREAM_SPARSE_ATTRIBUTE ) {
|
|
pbuc->fSparseHandAlt = TRUE ;
|
|
|
|
// if it was sparse when be backed it up make is sparse again.
|
|
NtFsControlFile( pbuc->hAlternate,
|
|
NULL, // overlapped event handle
|
|
NULL, // Apc routine
|
|
NULL, // overlapped structure
|
|
&iosb,
|
|
FSCTL_SET_SPARSE ,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0 ) ;
|
|
}
|
|
|
|
// don't reset stream start because WriteStream will do it.
|
|
|
|
}
|
|
|
|
//
|
|
// If we have no handle for the transfer, record this failure
|
|
// and return failure.
|
|
//
|
|
|
|
if (pbuc->hAlternate == INVALID_HANDLE_VALUE) {
|
|
pbuc->fAccessError = TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Let the normal stream copy perform the transfer
|
|
//
|
|
|
|
return BackupWriteStream( pbuc->hAlternate, pbuc, pbif );
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BackupWriteEaData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that writes EA data on the file from
|
|
the user's buffer
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to output file
|
|
|
|
pbuc - CONTEXT of call
|
|
|
|
pbif - IOCONTEXT of call
|
|
|
|
Return Value:
|
|
|
|
TRUE if EA data was successfully written, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
//
|
|
// Attempt to fill up the buffer from the input.
|
|
//
|
|
|
|
switch (BackupWriteBuffer( pbuc, pbif )) {
|
|
default:
|
|
case BRB_FAIL:
|
|
return FALSE;
|
|
|
|
case BRB_MORE:
|
|
return TRUE;
|
|
|
|
case BRB_DONE:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The buffer is now completely filled with our EA data. Set the
|
|
// EA data on the file.
|
|
//
|
|
|
|
Status = NtSetEaFile(
|
|
hFile,
|
|
&iosb,
|
|
pbuc->DataBuffer.Buffer,
|
|
pbuc->head.Size.LowPart );
|
|
|
|
//
|
|
// If we failed, map the error and return failure
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
BaseSetLastNTError( Status );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BackupWriteReparseData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that writes Reparse data on the file from
|
|
the user's buffer
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to output file
|
|
|
|
pbuc - CONTEXT of call
|
|
|
|
pbif - IOCONTEXT of call
|
|
|
|
Return Value:
|
|
|
|
TRUE if EA data was successfully written, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK iosb;
|
|
DWORD *rp_tag_ptr ;
|
|
|
|
//
|
|
// Attempt to fill up the buffer from the input.
|
|
//
|
|
|
|
switch (BackupWriteBuffer( pbuc, pbif )) {
|
|
default:
|
|
case BRB_FAIL:
|
|
return FALSE;
|
|
|
|
case BRB_MORE:
|
|
return TRUE;
|
|
|
|
case BRB_DONE:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The buffer is now completely filled with our Reparse data. Set the
|
|
// Reparse data on the file.
|
|
//
|
|
|
|
|
|
rp_tag_ptr = (DWORD *)(pbuc->DataBuffer.Buffer) ;
|
|
|
|
pbuc->fAttribs |= FILE_ATTRIBUTE_REPARSE_POINT ;
|
|
|
|
|
|
Status = NtFsControlFile( hFile,
|
|
NULL, // overlapped event handle
|
|
NULL, // Apc routine
|
|
NULL, // overlapped structure
|
|
&iosb,
|
|
FSCTL_SET_REPARSE_POINT,
|
|
pbuc->DataBuffer.Buffer,
|
|
pbuc->head.Size.LowPart,
|
|
NULL,
|
|
0 ) ;
|
|
|
|
//
|
|
// If we failed, map the error and return failure
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
BaseSetLastNTError( Status );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
BackupWriteObjectId(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that writes the Object IDa on the file from
|
|
the user's buffer. Birth ids are made reborn. i.e. the volume id component
|
|
of the birth id is changed to the current volume's id, and the object id
|
|
component of the birth id is changed to the current object id.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to output file
|
|
|
|
pbuc - CONTEXT of call
|
|
|
|
pbif - IOCONTEXT of call
|
|
|
|
Return Value:
|
|
|
|
TRUE if Object ID was successfully written, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
IO_STATUS_BLOCK iosb;
|
|
NTSTATUS Status ;
|
|
FILE_FS_OBJECTID_INFORMATION fsobOID;
|
|
GUID guidZero;
|
|
|
|
//
|
|
// Attempt to fill up the buffer from the input.
|
|
//
|
|
|
|
switch (BackupWriteBuffer( pbuc, pbif )) {
|
|
default:
|
|
case BRB_FAIL:
|
|
return FALSE;
|
|
|
|
case BRB_MORE:
|
|
return TRUE;
|
|
|
|
case BRB_DONE:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Zero out the birth ID (the extended 48 bytes)
|
|
//
|
|
|
|
memset(&pbuc->DataBuffer.Buffer[sizeof(GUID)], 0, 3*sizeof(GUID));
|
|
|
|
//
|
|
// Set the ID on the file.
|
|
//
|
|
|
|
Status = NtFsControlFile( hFile,
|
|
NULL, // overlapped event handle
|
|
NULL, // Apc routine
|
|
NULL, // overlapped structure
|
|
&iosb,
|
|
FSCTL_SET_OBJECT_ID,
|
|
pbuc->DataBuffer.Buffer,
|
|
pbuc->head.Size.LowPart,
|
|
NULL,
|
|
0);
|
|
|
|
|
|
//
|
|
// Ignore errors
|
|
//
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
BaseSetLastNTError( Status );
|
|
}
|
|
|
|
return( TRUE );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL
|
|
BackupWriteSecurityData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that sets security information on the
|
|
file from data in the user's buffer.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle to output file
|
|
|
|
pbuc - CONTEXT of call
|
|
|
|
pbif - IOCONTEXT of call
|
|
|
|
Return Value:
|
|
|
|
TRUE if security was successfully written, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
SECURITY_INFORMATION si;
|
|
|
|
//
|
|
// Attempt to fill up the buffer from the input.
|
|
//
|
|
|
|
switch (BackupWriteBuffer(pbuc, pbif)) {
|
|
default:
|
|
case BRB_FAIL:
|
|
return FALSE;
|
|
|
|
case BRB_MORE:
|
|
return TRUE;
|
|
|
|
case BRB_DONE:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The buffer is now completely filled with our security data. If we
|
|
// are to ignore it, then return success
|
|
//
|
|
|
|
if (!pbif->fProcessSecurity) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Find out what security information is present so we know what to
|
|
// set.
|
|
|
|
si = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION;
|
|
|
|
if (((PISECURITY_DESCRIPTOR) pbuc->DataBuffer.Buffer)->Control & SE_DACL_PRESENT) {
|
|
si |= DACL_SECURITY_INFORMATION;
|
|
}
|
|
|
|
if (((PISECURITY_DESCRIPTOR) pbuc->DataBuffer.Buffer)->Control & SE_SACL_PRESENT) {
|
|
si |= SACL_SECURITY_INFORMATION;
|
|
}
|
|
|
|
//
|
|
// If the security descriptor has AUTO_INHERITED set, set the appropriate REQ bits.
|
|
//
|
|
if (((PISECURITY_DESCRIPTOR) pbuc->DataBuffer.Buffer)->Control & SE_DACL_AUTO_INHERITED) {
|
|
((PISECURITY_DESCRIPTOR) pbuc->DataBuffer.Buffer)->Control |= SE_DACL_AUTO_INHERIT_REQ;
|
|
}
|
|
|
|
if (((PISECURITY_DESCRIPTOR) pbuc->DataBuffer.Buffer)->Control & SE_SACL_AUTO_INHERITED) {
|
|
((PISECURITY_DESCRIPTOR) pbuc->DataBuffer.Buffer)->Control |= SE_SACL_AUTO_INHERIT_REQ;
|
|
}
|
|
|
|
Status = NtSetSecurityObject( hFile, si, pbuc->DataBuffer.Buffer );
|
|
|
|
if (!NT_SUCCESS( Status )) {
|
|
|
|
NTSTATUS Status2;
|
|
|
|
//
|
|
// 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,
|
|
pbuc->DataBuffer.Buffer );
|
|
}
|
|
|
|
if (si & DACL_SECURITY_INFORMATION) {
|
|
Status = NtSetSecurityObject(
|
|
hFile,
|
|
DACL_SECURITY_INFORMATION,
|
|
pbuc->DataBuffer.Buffer);
|
|
}
|
|
|
|
Status2 = NtSetSecurityObject(
|
|
hFile,
|
|
OWNER_SECURITY_INFORMATION |
|
|
GROUP_SECURITY_INFORMATION,
|
|
pbuc->DataBuffer.Buffer);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = Status2;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
BaseSetLastNTError(Status);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
BackupWriteLinkData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is an internal routine that establishes links based on the
|
|
user's data.
|
|
|
|
Arguments:
|
|
|
|
hFile - handle of file being restored
|
|
|
|
pbuc - CONTEXT of call
|
|
|
|
pbif - IOCONTEXT of call
|
|
|
|
Return Value:
|
|
|
|
TRUE if link was successfully established, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
FILE_LINK_INFORMATION *pfli;
|
|
WCHAR *pwc;
|
|
WCHAR *pwcSlash;
|
|
INT cbName;
|
|
INT cSlash;
|
|
WCHAR wcSave;
|
|
BOOL fSuccess;
|
|
|
|
//
|
|
// Attempt to fill up the buffer from the input.
|
|
//
|
|
|
|
switch (BackupWriteBuffer(pbuc, pbif)) {
|
|
default:
|
|
case BRB_FAIL:
|
|
return FALSE;
|
|
|
|
case BRB_MORE:
|
|
return TRUE;
|
|
|
|
case BRB_DONE:
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The buffer is now completely filled with our link data.
|
|
// Find the last component of the name.
|
|
//
|
|
|
|
cSlash = 0;
|
|
pwcSlash = NULL;
|
|
pwc = (WCHAR *) pbuc->DataBuffer.Buffer;
|
|
cbName = sizeof(WCHAR);
|
|
|
|
while (*pwc != L'\0') {
|
|
if (*pwc == L'\\') {
|
|
pwcSlash = pwc;
|
|
cSlash++;
|
|
cbName = 0;
|
|
}
|
|
pwc++;
|
|
cbName += sizeof(WCHAR);
|
|
}
|
|
|
|
pfli = BackupAlloc( sizeof(*pfli) + cbName );
|
|
|
|
if (pfli == NULL) {
|
|
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
|
return FALSE;
|
|
}
|
|
|
|
RtlCopyMemory( pfli->FileName, pwcSlash + 1, cbName );
|
|
pfli->FileNameLength = cbName - sizeof(WCHAR);
|
|
if (cSlash > 1) {
|
|
wcSave = L'\\';
|
|
}
|
|
else {
|
|
wcSave = *pwcSlash++;
|
|
}
|
|
*pwcSlash = L'\0';
|
|
|
|
//
|
|
// Open the parent of the link target
|
|
//
|
|
|
|
pfli->RootDirectory = CreateFileW(
|
|
(WCHAR *) pbuc->DataBuffer.Buffer,
|
|
GENERIC_WRITE | GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL| FILE_FLAG_BACKUP_SEMANTICS,
|
|
NULL );
|
|
|
|
*pwcSlash = wcSave;
|
|
pfli->ReplaceIfExists = TRUE;
|
|
|
|
fSuccess = TRUE;
|
|
|
|
if (pfli->RootDirectory == INVALID_HANDLE_VALUE) {
|
|
SetLastError( ERROR_FILE_NOT_FOUND );
|
|
fSuccess = FALSE;
|
|
}
|
|
else {
|
|
NTSTATUS Status;
|
|
IO_STATUS_BLOCK iosb;
|
|
|
|
Status = NtSetInformationFile(
|
|
hFile,
|
|
&iosb,
|
|
pfli,
|
|
sizeof(*pfli) + cbName,
|
|
FileLinkInformation );
|
|
|
|
CloseHandle( pfli->RootDirectory );
|
|
pfli->RootDirectory = INVALID_HANDLE_VALUE;
|
|
if (!NT_SUCCESS( Status )) {
|
|
BaseSetLastNTError( Status );
|
|
fSuccess = FALSE;
|
|
} else {
|
|
if (iosb.Information == FILE_OVERWRITTEN) {
|
|
SetLastError( ERROR_ALREADY_EXISTS );
|
|
} else {
|
|
SetLastError( 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
BackupFree( pfli );
|
|
|
|
return fSuccess;
|
|
}
|
|
|
|
|
|
|
|
// 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.
|
|
|
|
BOOL WINAPI
|
|
BackupWrite(
|
|
HANDLE hFile,
|
|
LPBYTE lpBuffer,
|
|
DWORD nNumberOfBytesToWrite,
|
|
LPDWORD lpNumberOfBytesWritten,
|
|
BOOL bAbort,
|
|
BOOL bProcessSecurity,
|
|
LPVOID *lpContext)
|
|
{
|
|
BACKUPCONTEXT *pbuc;
|
|
BACKUPIOFRAME bif;
|
|
BOOL fSuccess = FALSE;
|
|
|
|
pbuc = *lpContext;
|
|
bif.pIoBuffer = lpBuffer;
|
|
bif.cbRequest = nNumberOfBytesToWrite;
|
|
bif.pcbTransferred = lpNumberOfBytesWritten;
|
|
bif.fProcessSecurity = (BOOLEAN)bProcessSecurity;
|
|
|
|
//
|
|
// Allocate our Context Control Block on first call.
|
|
//
|
|
|
|
if (bAbort) {
|
|
if (pbuc != NULL) {
|
|
FreeContext(lpContext);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
*bif.pcbTransferred = 0;
|
|
if (pbuc == INVALID_HANDLE_VALUE) {
|
|
return TRUE;
|
|
}
|
|
|
|
// Allocate our Context Control Block on first call.
|
|
|
|
if (pbuc == NULL) {
|
|
pbuc = AllocContext(0); // No initial buffer
|
|
|
|
//
|
|
// If we have no space then return failure
|
|
//
|
|
|
|
if (pbuc == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
*lpContext = pbuc;
|
|
|
|
do {
|
|
DWORD cbrequest;
|
|
LONGLONG licbRemain;
|
|
|
|
//
|
|
// If we do not have a complete header, go
|
|
// fill it in.
|
|
//
|
|
|
|
if (pbuc->cbHeader == 0) {
|
|
|
|
pbuc->fMultiStreamType = TRUE ; //restore does not auto inc stream index.
|
|
pbuc->fStreamStart = TRUE ;
|
|
|
|
BackupWriteHeader(pbuc, &bif, CB_NAMELESSHEADER) ;
|
|
|
|
}
|
|
|
|
//
|
|
// If no more data, then exit
|
|
//
|
|
|
|
if (bif.cbRequest == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If a stream name was expected, go read it in
|
|
//
|
|
|
|
if (pbuc->cbHeader == CB_NAMELESSHEADER &&
|
|
pbuc->head.dwStreamNameSize != 0) {
|
|
|
|
if ( !BackupWriteHeader(
|
|
pbuc,
|
|
&bif,
|
|
pbuc->cbHeader + pbuc->head.dwStreamNameSize) )
|
|
{
|
|
SetLastError( ERROR_INVALID_DATA );
|
|
return FALSE ;
|
|
}
|
|
|
|
//
|
|
// If no more data then exit
|
|
//
|
|
|
|
if (bif.cbRequest == 0) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
if ( ( pbuc->cbHeader == CB_NAMELESSHEADER ) &&
|
|
( pbuc->head.dwStreamId == BACKUP_SPARSE_BLOCK ) ) {
|
|
|
|
BackupWriteHeader(
|
|
pbuc,
|
|
&bif,
|
|
pbuc->cbHeader + sizeof(LARGE_INTEGER) );
|
|
|
|
//
|
|
// If no more data then exit
|
|
//
|
|
|
|
if (bif.cbRequest == 0) {
|
|
|
|
if ( pbuc->cbHeader == CB_NAMELESSHEADER ) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine amount of data remaining in user buffer
|
|
// that can be transferred as part of this section
|
|
// of the backup stream
|
|
//
|
|
|
|
cbrequest = ComputeRequestSize(pbuc, bif.cbRequest);
|
|
|
|
//
|
|
// Determine total amount of data left in this section
|
|
// of backup stream.
|
|
//
|
|
|
|
licbRemain = ComputeRemainingSize( pbuc );
|
|
|
|
//
|
|
// If we had an error in the transfer and we're done
|
|
// doing the transfer then pretend that we successfully
|
|
// completed the section.
|
|
//
|
|
|
|
if (pbuc->fAccessError && licbRemain == 0) {
|
|
|
|
ReportTransfer(pbuc, &bif, cbrequest);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Begin or continue the transfer of data. We assume that there
|
|
// are no errors
|
|
//
|
|
|
|
pbuc->fAccessError = FALSE;
|
|
|
|
switch (pbuc->head.dwStreamId) {
|
|
|
|
case BACKUP_SPARSE_BLOCK :
|
|
fSuccess = BackupWriteSparse( hFile, pbuc, &bif ) ;
|
|
break ;
|
|
|
|
case BACKUP_DATA:
|
|
fSuccess = BackupWriteStream( hFile, pbuc, &bif );
|
|
break;
|
|
|
|
case BACKUP_ALTERNATE_DATA:
|
|
fSuccess = BackupWriteAlternateData( hFile, pbuc, &bif );
|
|
break;
|
|
|
|
case BACKUP_EA_DATA:
|
|
fSuccess = BackupWriteEaData( hFile, pbuc, &bif );
|
|
break;
|
|
|
|
case BACKUP_OBJECT_ID:
|
|
fSuccess = BackupWriteObjectId( hFile, pbuc, &bif );
|
|
break;
|
|
|
|
case BACKUP_REPARSE_DATA:
|
|
fSuccess = BackupWriteReparseData( hFile, pbuc, &bif );
|
|
break;
|
|
|
|
case BACKUP_SECURITY_DATA:
|
|
fSuccess = BackupWriteSecurityData( hFile, pbuc, &bif );
|
|
break;
|
|
|
|
case BACKUP_LINK:
|
|
fSuccess = BackupWriteLinkData( hFile, pbuc, &bif );
|
|
break;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_DATA);
|
|
fSuccess = FALSE;
|
|
break;
|
|
}
|
|
|
|
BackupTestRestartStream(pbuc);
|
|
} while (fSuccess && bif.cbRequest != 0);
|
|
|
|
if (fSuccess && *bif.pcbTransferred == 0) {
|
|
FreeContext(lpContext);
|
|
}
|
|
|
|
return(fSuccess);
|
|
}
|