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

1570 lines
35 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
#ifdef _CAIRO_
#define BACKUP_OLEINFO
#endif
#ifdef BACKUP_OLEINFO
#include <windows.h>
#include <iofs.h>
#endif
#define CWCMAX_STREAMNAME 512
#define CB_NAMELESSHEADER FIELD_OFFSET(WIN32_STREAM_ID, cStreamName)
#define QuadAlign(cb) \
(((cb) + sizeof(LARGE_INTEGER) - 1) & ~(sizeof(LARGE_INTEGER) - 1))
typedef struct
{
WIN32_STREAM_ID head; // stream hdr describing current stream
WCHAR awcName[CWCMAX_STREAMNAME]; // stream header name buffer
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
BOOLEAN fBufferReady; // TRUE if internal buffer is set up.
DWORD StreamIndex; // index into the stream ID table
LARGE_INTEGER liStreamOffset; // offset in current stream
DWORD cbHeader; // size of stream header
DWORD cbBuffer; // size of attached data buffer
HANDLE hAlternate; // Handle to alternate data stream
DWORD iBuffer; // Current offset into data buffer.
BYTE *pBuffer; // pointer to allocated data buffer.
} BACKUPCONTEXT;
typedef struct
{
BYTE *pIoBuffer;
DWORD *pcbTransfered;
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_DATA,
BACKUP_EA_DATA,
BACKUP_ALTERNATE_DATA,
#ifdef BACKUP_OLEINFO
BACKUP_PROPERTY_DATA,
#endif // BACKUP_OLEINFO
BACKUP_INVALID,
};
VOID *
BackupAlloc(DWORD cb)
{
return(RtlAllocateHeap(RtlProcessHeap(), MAKE_TAG( BACKUP_TAG ), cb));
}
__inline VOID
BackupFree(VOID *pv)
{
RtlFreeHeap(RtlProcessHeap(), 0, pv);
}
BOOL
GrowBuffer(BACKUPCONTEXT *pbuc, DWORD cbNew)
{
VOID *pv;
pv = BackupAlloc(cbNew);
if (pv == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return(FALSE);
}
if (pbuc->pBuffer != NULL) {
BackupFree(pbuc->pBuffer);
}
pbuc->pBuffer = pv;
pbuc->cbBuffer = cbNew;
return(TRUE);
}
VOID
FreeContext(LPVOID *lpContext)
{
BACKUPCONTEXT *pbuc = *lpContext;
if (pbuc != INVALID_HANDLE_VALUE) {
if (pbuc->pBuffer != NULL) {
BackupFree(pbuc->pBuffer);
}
if (pbuc->hAlternate != NULL) {
CloseHandle(pbuc->hAlternate); // releases any locks
}
BackupFree(pbuc);
*lpContext = INVALID_HANDLE_VALUE;
}
}
BACKUPCONTEXT *
AllocContext(DWORD cbBuffer)
{
BACKUPCONTEXT *pbuc;
pbuc = BackupAlloc(sizeof(*pbuc));
if (pbuc != NULL) {
RtlZeroMemory(pbuc, sizeof(*pbuc));
pbuc->fStreamStart = TRUE;
if (cbBuffer != 0 && !GrowBuffer(pbuc, cbBuffer)) {
BackupFree(pbuc);
pbuc = NULL;
}
}
if (pbuc == NULL) {
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
return(pbuc);
}
VOID
ComputeRemainingSize(BACKUPCONTEXT *pbuc, LARGE_INTEGER *plicbRemain)
{
plicbRemain->QuadPart = pbuc->cbHeader +
pbuc->head.Size.QuadPart -
pbuc->liStreamOffset.QuadPart;
}
DWORD
ComputeRequestSize(BACKUPCONTEXT *pbuc, DWORD cbrequest)
{
LARGE_INTEGER licbRemain;
ComputeRemainingSize(pbuc, &licbRemain);
if (licbRemain.HighPart == 0 && cbrequest > licbRemain.LowPart) {
cbrequest = licbRemain.LowPart;
}
return(cbrequest);
}
VOID
ReportTransfer(BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif, DWORD cbtransfered)
{
LARGE_INTEGER licbTransfered;
licbTransfered.HighPart = 0;
licbTransfered.LowPart = cbtransfered;
pbuc->liStreamOffset.QuadPart += licbTransfered.QuadPart;
*pbif->pcbTransfered += cbtransfered;
pbif->cbRequest -= cbtransfered;
pbif->pIoBuffer += cbtransfered;
}
VOID
BackupReadBuffer(BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
DWORD cbrequest;
BYTE *pb;
cbrequest = ComputeRequestSize(pbuc, pbif->cbRequest);
pb = &pbuc->pBuffer[pbuc->liStreamOffset.LowPart - pbuc->cbHeader];
RtlCopyMemory(pbif->pIoBuffer, pb, cbrequest);
ReportTransfer(pbuc, pbif, cbrequest);
}
BOOL
BackupReadStream(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
DWORD cbrequest;
DWORD cbtransfered;
BOOL fSuccess;
cbrequest = ComputeRequestSize(pbuc, pbif->cbRequest);
fSuccess = ReadFile(hFile, pbif->pIoBuffer, cbrequest, &cbtransfered, NULL);
if (cbtransfered != 0) {
ReportTransfer(pbuc, pbif, cbtransfered);
}
else if (fSuccess && cbrequest != 0) {
SetLastError(ERROR_IO_DEVICE);
fSuccess = FALSE;
}
return(fSuccess);
}
BOOL
BackupReadData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
NTSTATUS Status;
IO_STATUS_BLOCK iosb;
if (pbuc->fStreamStart) {
LARGE_INTEGER licbFile;
FILE_STANDARD_INFORMATION fsi;
RtlZeroMemory(&fsi, sizeof(fsi));
Status = NtQueryInformationFile(
hFile,
&iosb,
&fsi,
sizeof(fsi),
FileStandardInformation);
if (!NT_SUCCESS(Status) || fsi.Directory) {
return(TRUE);
}
licbFile.LowPart = GetFileSize(hFile, &licbFile.HighPart);
if (licbFile.LowPart == 0 && licbFile.HighPart == 0) {
return(TRUE);
}
if (licbFile.LowPart == 0xffffffff && GetLastError() != NO_ERROR) {
return(FALSE);
}
pbuc->head.Size = licbFile;
pbuc->head.dwStreamId = mwStreamList[pbuc->StreamIndex];
pbuc->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE;
pbuc->head.dwStreamNameSize = 0;
pbuc->cbHeader = CB_NAMELESSHEADER;
pbuc->fStreamStart = FALSE;
licbFile.HighPart = 0;
SetFilePointer(hFile, 0, &licbFile.HighPart, FILE_BEGIN);
return(TRUE);
}
if (pbuc->liStreamOffset.HighPart != 0 ||
pbuc->liStreamOffset.LowPart >= pbuc->cbHeader) {
return(BackupReadStream(hFile, pbuc, pbif));
}
return(TRUE);
}
BOOL
BackupReadAlternateData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
// ALT_DATA is Macintosh stream data & other data streams.
if (pbuc->fStreamStart) {
NTSTATUS Status;
FILE_STREAM_INFORMATION *pfsi;
IO_STATUS_BLOCK iosb;
// allocate a buffer big enough to hold all the necessary data.
if (!pbuc->fBufferReady) {
while (TRUE) {
Status = NtQueryInformationFile(
hFile,
&iosb,
pbuc->pBuffer,
pbuc->cbBuffer,
FileStreamInformation);
if (NT_SUCCESS(Status) && iosb.Information != 0) {
pbuc->iBuffer = 0;
pbuc->fBufferReady = TRUE;
break;
}
if (!BufferOverflow(Status)) {
return(TRUE); // No alt. streams, do next stream type
}
if (!GrowBuffer(pbuc, pbuc->cbBuffer * 2)) {
return(FALSE); // No memory
}
// else grow succeeded
}
}
pbuc->hAlternate = NULL;
pbuc->fStreamStart = FALSE;
pfsi = (FILE_STREAM_INFORMATION *) &pbuc->pBuffer[pbuc->iBuffer];
// Check StreamName for default data stream and skip if found
// Checking StreamNameLength for <= 1 character is OFS specific!
// Checking StreamName[1] for a colon is NTFS specific!
if (pfsi->StreamNameLength <= sizeof(WCHAR) ||
pfsi->StreamName[1] == ':') {
if (pfsi->NextEntryOffset == 0) {
return(TRUE); // No more, do next stream type
}
pbuc->iBuffer += pfsi->NextEntryOffset;
}
pbuc->head.Size.LowPart = 1;
}
else if (pbuc->hAlternate == NULL) {
NTSTATUS Status;
FILE_STREAM_INFORMATION *pfsi;
UNICODE_STRING strName;
OBJECT_ATTRIBUTES oa;
IO_STATUS_BLOCK iosb;
pbuc->head.Size.LowPart = 0;
pbuc->head.Size.HighPart = 0;
pfsi = (FILE_STREAM_INFORMATION *) &pbuc->pBuffer[pbuc->iBuffer];
strName.Length = (USHORT) pfsi->StreamNameLength;
strName.MaximumLength = strName.Length;
strName.Buffer = pfsi->StreamName;
InitializeObjectAttributes(
&oa,
&strName,
OBJ_CASE_INSENSITIVE,
hFile,
NULL);
Status = NtOpenFile(
&pbuc->hAlternate,
FILE_GENERIC_READ,
&oa,
&iosb,
FILE_SHARE_READ|FILE_SHARE_WRITE,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_OPEN_FOR_BACKUP_INTENT);
if (!NT_SUCCESS(Status)) {
pbuc->iBuffer += pfsi->NextEntryOffset;
if (pfsi->NextEntryOffset != 0) {
pbuc->head.Size.LowPart = 1;
pbuc->fMultiStreamType = TRUE; // more to come
}
return(TRUE);
}
pbuc->head.dwStreamId = mwStreamList[pbuc->StreamIndex];
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);
pbuc->head.Size.LowPart = GetFileSize(
pbuc->hAlternate,
&pbuc->head.Size.HighPart);
if (pfsi->NextEntryOffset != 0) {
pbuc->iBuffer += pfsi->NextEntryOffset;
pbuc->fMultiStreamType = TRUE; // more to come after this one
}
}
else if (pbuc->liStreamOffset.HighPart != 0 ||
pbuc->liStreamOffset.LowPart >= pbuc->cbHeader) {
if (pbuc->liStreamOffset.LowPart == pbuc->cbHeader &&
pbuc->liStreamOffset.HighPart == 0) {
// if we can't lock all records, return an error
if (!LockFile(pbuc->hAlternate, 0, 0, 0xffffffff, 0xffffffff)) {
return(FALSE);
}
}
return(BackupReadStream(pbuc->hAlternate, pbuc, pbif));
}
return(TRUE);
}
BOOL
BackupReadEaData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
if (pbuc->fStreamStart) {
IO_STATUS_BLOCK iosb;
while (TRUE) {
NTSTATUS Status;
FILE_EA_INFORMATION fei;
Status = NtQueryEaFile(
hFile,
&iosb,
pbuc->pBuffer,
pbuc->cbBuffer,
FALSE,
NULL,
0,
0,
(BOOLEAN) TRUE);
if (NT_SUCCESS(Status) && iosb.Information != 0) {
pbuc->fBufferReady = TRUE;
break;
}
if (!BufferOverflow(Status)) {
return(TRUE); // No Eas, do next stream type
}
Status = NtQueryInformationFile(
hFile,
&iosb,
&fei,
sizeof(fei),
FileEaInformation);
if (!NT_SUCCESS(Status)) {
return(TRUE); // No Eas, do next stream type
}
if (!GrowBuffer(pbuc, (fei.EaSize * 5) / 4)) {
pbuc->fAccessError = TRUE;
return(FALSE); // No memory
}
// else grow succeeded
}
pbuc->head.dwStreamId = mwStreamList[pbuc->StreamIndex];
pbuc->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE;
pbuc->head.dwStreamNameSize = 0;
pbuc->cbHeader = CB_NAMELESSHEADER;
pbuc->head.Size.HighPart = 0;
pbuc->head.Size.LowPart = iosb.Information;
pbuc->fStreamStart = FALSE;
}
else if (pbuc->liStreamOffset.HighPart != 0 ||
pbuc->liStreamOffset.LowPart >= pbuc->cbHeader) {
BackupReadBuffer(pbuc, pbif);
}
return(TRUE);
}
BOOL
BackupReadSecurityData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
if (!pbif->fProcessSecurity) {
return(TRUE);
}
if (pbuc->fStreamStart) {
while (TRUE) {
NTSTATUS Status;
DWORD cbSecurityInfo;
// First try to read all the security data
RtlZeroMemory(pbuc->pBuffer, pbuc->cbBuffer);
Status = NtQuerySecurityObject(
hFile,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION |
SACL_SECURITY_INFORMATION,
pbuc->pBuffer,
pbuc->cbBuffer,
&cbSecurityInfo);
if (!NT_SUCCESS(Status) && !BufferOverflow(Status)) {
// Now just try everything but SACL
Status = NtQuerySecurityObject(
hFile,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION |
DACL_SECURITY_INFORMATION,
pbuc->pBuffer,
pbuc->cbBuffer,
&cbSecurityInfo);
}
#if 0
if (!NT_SUCCESS(Status) && !BufferOverflow(Status)) {
// Now just try to read the DACL
Status = NtQuerySecurityObject(
hFile,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION,
pbuc->pBuffer,
pbuc->cbBuffer,
&cbSecurityInfo);
}
#endif
if (NT_SUCCESS(Status)) {
pbuc->fBufferReady = TRUE;
break;
}
if (!BufferOverflow(Status)) {
return(TRUE); // No Security info, do next stream type
}
if (!GrowBuffer(pbuc, cbSecurityInfo)) {
return(FALSE); // No memory
}
// else grow succeeded
}
pbuc->head.dwStreamId = mwStreamList[pbuc->StreamIndex];
pbuc->head.dwStreamAttributes = STREAM_CONTAINS_SECURITY;
pbuc->head.dwStreamNameSize = 0;
pbuc->cbHeader = CB_NAMELESSHEADER;
pbuc->head.Size.LowPart = RtlLengthSecurityDescriptor(pbuc->pBuffer);
pbuc->head.Size.HighPart = 0;
pbuc->fStreamStart = FALSE;
}
else if (pbuc->liStreamOffset.HighPart != 0 ||
pbuc->liStreamOffset.LowPart >= pbuc->cbHeader) {
BackupReadBuffer(pbuc, pbif);
}
return(TRUE);
}
#ifdef BACKUP_OLEINFO
BOOL
BackupReadOleData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
if (pbuc->fStreamStart) {
IO_STATUS_BLOCK iosb;
NTSTATUS Status;
FILE_OLE_INFORMATION *pfoi = (FILE_OLE_INFORMATION *) pbuc->pBuffer;
Status = NtQueryInformationFile(
hFile,
&iosb,
pfoi,
sizeof(*pfoi),
FileOleInformation);
if (!NT_SUCCESS(Status)) {
if (Status == STATUS_INVALID_PARAMETER)
{
return(TRUE); // Downlevel filesystem - unsupported infolevel
}
BaseSetLastNTError(Status);
return(FALSE);
}
pbuc->fBufferReady = TRUE;
pbuc->head.dwStreamId = mwStreamList[pbuc->StreamIndex];
pbuc->head.dwStreamAttributes = STREAM_NORMAL_ATTRIBUTE;
pbuc->head.dwStreamNameSize = 0;
pbuc->cbHeader = CB_NAMELESSHEADER;
pbuc->head.Size.HighPart = 0;
pbuc->head.Size.LowPart = sizeof(*pfoi);
pbuc->fStreamStart = FALSE;
}
else if (pbuc->liStreamOffset.HighPart != 0 ||
pbuc->liStreamOffset.LowPart >= pbuc->cbHeader) {
BackupReadBuffer(pbuc, pbif);
}
return(TRUE);
}
#endif // BACKUP_OLEINFO
VOID
BackupTestRestartStream(BACKUPCONTEXT *pbuc)
{
LARGE_INTEGER licbRemain;
ComputeRemainingSize(pbuc, &licbRemain);
if (licbRemain.HighPart == 0 && licbRemain.LowPart == 0) {
if (pbuc->hAlternate != NULL) {
CloseHandle(pbuc->hAlternate); // releases any locks
pbuc->hAlternate = NULL;
}
pbuc->cbHeader = 0;
pbuc->fStreamStart = TRUE;
pbuc->liStreamOffset.LowPart = 0; // for BackupWrite
pbuc->liStreamOffset.HighPart = 0;
if (!pbuc->fMultiStreamType) { // for BackupRead
pbuc->StreamIndex++;
pbuc->fBufferReady = 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.
BOOL WINAPI
BackupRead(
HANDLE hFile,
LPBYTE lpBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
BOOL bAbort,
BOOL bProcessSecurity,
LPVOID *lpContext)
{
BACKUPCONTEXT *pbuc;
BACKUPIOFRAME bif;
BOOL fSuccess = FALSE;
pbuc = *lpContext;
bif.pIoBuffer = lpBuffer;
bif.cbRequest = nNumberOfBytesToRead;
bif.pcbTransfered = lpNumberOfBytesRead;
bif.fProcessSecurity = bProcessSecurity;
if (bAbort) {
if (pbuc != NULL) {
FreeContext(lpContext);
}
return(TRUE);
}
*bif.pcbTransfered = 0;
if (pbuc == INVALID_HANDLE_VALUE || bif.cbRequest == 0) {
return(TRUE);
}
if (pbuc != NULL && mwStreamList[pbuc->StreamIndex] == BACKUP_INVALID) {
FreeContext(lpContext);
return(TRUE);
}
// Allocate our Context Control Block on first call.
if (pbuc == NULL) {
pbuc = AllocContext(CBMIN_BUFFER); // Alloc initial buffer
}
if (pbuc != NULL) {
*lpContext = pbuc;
do {
if (pbuc->fStreamStart) {
pbuc->head.Size.LowPart = 0;
pbuc->head.Size.HighPart = 0;
pbuc->liStreamOffset.LowPart = 0;
pbuc->liStreamOffset.HighPart = 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_SECURITY_DATA:
fSuccess = BackupReadSecurityData(hFile, pbuc, &bif);
break;
#ifdef BACKUP_OLEINFO
case BACKUP_PROPERTY_DATA:
fSuccess = BackupReadOleData(hFile, pbuc, &bif);
break;
#endif // BACKUP_OLEINFO
default:
pbuc->StreamIndex++;
pbuc->fStreamStart = TRUE;
break;
}
// if we're in the phase of reading the header, copy the header
if (pbuc->liStreamOffset.LowPart < pbuc->cbHeader &&
pbuc->liStreamOffset.HighPart == 0) {
DWORD cbrequest;
// Send the current stream header;
cbrequest = min(
pbuc->cbHeader - pbuc->liStreamOffset.LowPart,
bif.cbRequest);
RtlCopyMemory(
bif.pIoBuffer,
(BYTE *) &pbuc->head + pbuc->liStreamOffset.LowPart,
cbrequest);
ReportTransfer(pbuc, &bif, cbrequest);
}
//
// if we are at the end of a stream then
// start at the beginning of the next stream
//
else {
BackupTestRestartStream(pbuc);
}
} while (fSuccess &&
mwStreamList[pbuc->StreamIndex] != BACKUP_INVALID &&
bif.cbRequest != 0);
}
if (fSuccess && *bif.pcbTransfered == 0) {
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;
LARGE_INTEGER licbRemain;
LARGE_INTEGER licbRequest;
BOOL fSuccess;
pbuc = *lpContext;
*lpdwHighBytesSeeked = 0;
*lpdwLowBytesSeeked = 0;
if (pbuc == INVALID_HANDLE_VALUE || pbuc == NULL || pbuc->fStreamStart) {
return(FALSE);
}
if (pbuc->liStreamOffset.LowPart < pbuc->cbHeader &&
pbuc->liStreamOffset.HighPart == 0) {
return(FALSE);
}
//
// If we made it here, we are in the middle of a stream
//
ComputeRemainingSize(pbuc, &licbRemain);
licbRequest.LowPart = dwLowBytesToSeek;
licbRequest.HighPart = dwHighBytesToSeek & 0x7fffffff;
if (licbRequest.QuadPart > licbRemain.QuadPart) {
licbRequest = licbRemain;
}
fSuccess = TRUE;
switch (pbuc->head.dwStreamId) {
case BACKUP_EA_DATA:
case BACKUP_SECURITY_DATA:
// assume less than 2gig of data
pbuc->iBuffer += licbRequest.LowPart;
break;
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;
}
pbuc->liStreamOffset.QuadPart += licbRequest.QuadPart;
*lpdwLowBytesSeeked = licbRequest.LowPart;
*lpdwHighBytesSeeked = licbRequest.HighPart;
BackupTestRestartStream(pbuc);
if (!fSuccess) {
SetLastError(ERROR_SEEK);
}
return(fSuccess);
}
VOID
BackupWriteHeader(BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif, DWORD cbHeader)
{
DWORD cbrequest;
cbrequest = min(pbif->cbRequest, cbHeader - pbuc->liStreamOffset.LowPart);
RtlCopyMemory(
(CHAR *) &pbuc->head + pbuc->liStreamOffset.LowPart,
pbif->pIoBuffer,
cbrequest);
ReportTransfer(pbuc, pbif, cbrequest);
if (pbuc->liStreamOffset.LowPart == cbHeader) {
pbuc->cbHeader = cbHeader;
}
}
#define BRB_FAIL 0
#define BRB_DONE 1
#define BRB_MORE 2
INT
BackupWriteBuffer(BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
DWORD cbrequest;
// allocate a buffer for data storage
if (pbuc->fStreamStart) {
pbuc->fStreamStart = FALSE;
if (pbuc->cbBuffer < pbuc->head.Size.LowPart &&
!GrowBuffer(pbuc, pbuc->head.Size.LowPart)) {
return(BRB_FAIL);
}
}
// Copy the stream into our allocated buffer
cbrequest = min(pbif->cbRequest,
pbuc->head.Size.LowPart -
pbuc->liStreamOffset.LowPart + pbuc->cbHeader);
RtlCopyMemory(
pbuc->pBuffer + pbuc->liStreamOffset.LowPart - pbuc->cbHeader,
pbif->pIoBuffer,
cbrequest);
ReportTransfer(pbuc, pbif, cbrequest);
// Tell caller if the entire stream is in our buffer
if (pbuc->liStreamOffset.LowPart ==
pbuc->head.Size.LowPart + pbuc->cbHeader) {
return(BRB_DONE);
}
return(BRB_MORE);
}
BOOL
BackupWriteStream(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
DWORD cbrequest;
DWORD cbtransfered;
BOOL fSuccess;
cbrequest = ComputeRequestSize(pbuc, pbif->cbRequest);
fSuccess = WriteFile(
hFile,
pbif->pIoBuffer,
cbrequest,
&cbtransfered,
NULL);
if (cbtransfered != 0) {
ReportTransfer(pbuc, pbif, cbtransfered);
}
return(fSuccess);
}
BOOL
BackupWriteAlternateData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
if (pbuc->fStreamStart) {
NTSTATUS Status;
UNICODE_STRING strName;
OBJECT_ATTRIBUTES oa;
IO_STATUS_BLOCK iosb;
strName.Length = (USHORT) pbuc->head.dwStreamNameSize;
strName.MaximumLength = strName.Length;
strName.Buffer = pbuc->head.cStreamName;
InitializeObjectAttributes(
&oa,
&strName,
OBJ_CASE_INSENSITIVE,
hFile,
NULL);
Status = NtCreateFile(
&pbuc->hAlternate,
FILE_GENERIC_WRITE,
&oa,
&iosb,
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);
pbuc->fAccessError = TRUE;
return(FALSE);
}
pbuc->fStreamStart = FALSE;
}
if (pbuc->hAlternate == INVALID_HANDLE_VALUE) {
pbuc->fAccessError = TRUE;
return(FALSE);
}
return(BackupWriteStream(pbuc->hAlternate, pbuc, pbif));
}
BOOL
BackupWriteEaData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
NTSTATUS Status;
IO_STATUS_BLOCK iosb;
switch (BackupWriteBuffer(pbuc, pbif)) {
default:
case BRB_FAIL:
return(FALSE);
case BRB_MORE:
return(TRUE);
case BRB_DONE:
break;
}
// once the entire stream is in our buffer, we can set the EA data
Status = NtSetEaFile(
hFile,
&iosb,
pbuc->pBuffer,
pbuc->head.Size.LowPart);
if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
return(FALSE);
}
return(TRUE);
}
BOOL
BackupWriteSecurityData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
NTSTATUS Status;
SECURITY_INFORMATION si;
switch (BackupWriteBuffer(pbuc, pbif)) {
default:
case BRB_FAIL:
return(FALSE);
case BRB_MORE:
return(TRUE);
case BRB_DONE:
if (!pbif->fProcessSecurity) {
return(TRUE); // ignore the data we read
}
break;
}
// Once the entire stream is in our buffer, we can set the ACL data.
// First try to write all the security data that's there.
si = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION;
if (((PISECURITY_DESCRIPTOR) pbuc->pBuffer)->Control & SE_DACL_PRESENT) {
si |= DACL_SECURITY_INFORMATION;
}
if (((PISECURITY_DESCRIPTOR) pbuc->pBuffer)->Control & SE_SACL_PRESENT) {
si |= SACL_SECURITY_INFORMATION;
}
Status = NtSetSecurityObject(hFile, si, pbuc->pBuffer);
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->pBuffer);
}
if (si & DACL_SECURITY_INFORMATION)
{
Status = NtSetSecurityObject(
hFile,
DACL_SECURITY_INFORMATION,
pbuc->pBuffer);
}
Status2 = NtSetSecurityObject(
hFile,
OWNER_SECURITY_INFORMATION |
GROUP_SECURITY_INFORMATION,
pbuc->pBuffer);
if (NT_SUCCESS(Status)) {
Status = Status2;
}
}
if (!NT_SUCCESS(Status)) {
BaseSetLastNTError(Status);
return(FALSE);
}
return(TRUE);
}
BOOL
BackupWriteLinkData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
FILE_LINK_INFORMATION *pfli;
WCHAR *pwc;
WCHAR *pwcSlash;
INT cbName;
INT cSlash;
WCHAR wcSave;
BOOL fSuccess;
switch (BackupWriteBuffer(pbuc, pbif)) {
default:
case BRB_FAIL:
return(FALSE);
case BRB_MORE:
return(TRUE);
case BRB_DONE:
break;
}
// once the entire stream is in our buffer, we can set up the LINK
cSlash = 0;
pwcSlash = NULL;
pwc = (WCHAR *) pbuc->pBuffer;
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';
pfli->RootDirectory = CreateFileW(
(WCHAR *) pbuc->pBuffer,
GENERIC_WRITE | GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
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);
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);
}
#ifdef BACKUP_OLEINFO
BOOL
BackupWriteOleData(HANDLE hFile, BACKUPCONTEXT *pbuc, BACKUPIOFRAME *pbif)
{
NTSTATUS Status;
IO_STATUS_BLOCK iosb;
BOOLEAN fRetried = FALSE;
switch (BackupWriteBuffer(pbuc, pbif)) {
default:
case BRB_FAIL:
return(FALSE);
case BRB_MORE:
return(TRUE);
case BRB_DONE:
break;
}
if (pbuc->cbBuffer < sizeof(FILE_OLE_INFORMATION) ||
pbuc->head.Size.HighPart != 0 ||
pbuc->head.Size.LowPart != sizeof(FILE_OLE_INFORMATION))
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// once all the data are in our buffer, we can set the OLE info
retry:
Status = NtSetInformationFile(
hFile,
&iosb,
pbuc->pBuffer,
sizeof(FILE_OLE_INFORMATION),
FileOleInformation);
if (!NT_SUCCESS(Status)) {
// If some other object on the volume has the same object id, generate
// a related object id, and try again. Loop as long as it takes.
if (Status == STATUS_DUPLICATE_OBJECTID)
{
OBJECTID oidNew;
RtlGenerateRelatedObjectId(
&((FILE_OLE_INFORMATION *) pbuc->pBuffer)->
ObjectIdInformation.ObjectId,
&oidNew);
((FILE_OLE_INFORMATION *) pbuc->pBuffer)->
ObjectIdInformation.ObjectId = oidNew;
goto retry;
}
// If the target object already has a different ObjectId,
// attempt to delete the existing ObjectId and go try again.
if (Status == STATUS_OBJECTID_EXISTS && !fRetried)
{
FILE_OBJECTID_INFORMATION foi;
RtlZeroMemory(&foi, sizeof(foi));
Status = NtSetInformationFile(
hFile,
&iosb,
&foi,
sizeof(foi),
FileObjectIdInformation);
if (NT_SUCCESS(Status)) {
fRetried = TRUE;
goto retry;
}
}
BaseSetLastNTError(Status);
return(FALSE);
}
return(TRUE);
}
#endif // BACKUP_OLEINFO
// 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.pcbTransfered = lpNumberOfBytesWritten;
bif.fProcessSecurity = bProcessSecurity;
//
// Allocate our Context Control Block on first call.
//
if (bAbort) {
if (pbuc != NULL) {
FreeContext(lpContext);
}
return(TRUE);
}
*bif.pcbTransfered = 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 (pbuc != NULL) {
*lpContext = pbuc;
do {
DWORD cbrequest;
LARGE_INTEGER licbRemain;
if (pbuc->cbHeader == 0) { // we expect a stream header
// fill up to the stream name
BackupWriteHeader(pbuc, &bif, CB_NAMELESSHEADER);
}
if (bif.cbRequest == 0) {
return(TRUE);
}
if (pbuc->cbHeader == CB_NAMELESSHEADER &&
pbuc->head.dwStreamNameSize != 0) {
// now fill in the stream name if it exists
BackupWriteHeader(
pbuc,
&bif,
pbuc->cbHeader + pbuc->head.dwStreamNameSize);
if (bif.cbRequest == 0) {
return(TRUE);
}
}
cbrequest = ComputeRequestSize(pbuc, bif.cbRequest);
ComputeRemainingSize(pbuc, &licbRemain);
if (pbuc->fAccessError &&
licbRemain.HighPart == 0 &&
licbRemain.LowPart == 0) {
ReportTransfer(pbuc, &bif, cbrequest);
continue;
}
pbuc->fAccessError = FALSE;
switch (pbuc->head.dwStreamId) {
case BACKUP_DATA:
pbuc->fStreamStart = FALSE;
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_SECURITY_DATA:
fSuccess = BackupWriteSecurityData(hFile, pbuc, &bif);
break;
case BACKUP_LINK:
fSuccess = BackupWriteLinkData(hFile, pbuc, &bif);
break;
#ifdef BACKUP_OLEINFO
case BACKUP_PROPERTY_DATA:
fSuccess = BackupWriteOleData(hFile, pbuc, &bif);
break;
#endif // BACKUP_OLEINFO
default:
SetLastError(ERROR_INVALID_DATA);
fSuccess = FALSE;
break;
}
BackupTestRestartStream(pbuc);
} while (fSuccess && bif.cbRequest != 0);
}
if (fSuccess && *bif.pcbTransfered == 0) {
FreeContext(lpContext);
}
return(fSuccess);
}