diff options
Diffstat (limited to 'private/utils/restore/src')
-rw-r--r-- | private/utils/restore/src/exit.c | 89 | ||||
-rw-r--r-- | private/utils/restore/src/filecopy.c | 736 | ||||
-rw-r--r-- | private/utils/restore/src/generic.c | 536 | ||||
-rw-r--r-- | private/utils/restore/src/makefile | 6 | ||||
-rw-r--r-- | private/utils/restore/src/makefile.inc | 4 | ||||
-rw-r--r-- | private/utils/restore/src/match.c | 841 | ||||
-rw-r--r-- | private/utils/restore/src/mbcs.c | 94 | ||||
-rw-r--r-- | private/utils/restore/src/misc.c | 503 | ||||
-rw-r--r-- | private/utils/restore/src/msg.c | 214 | ||||
-rw-r--r-- | private/utils/restore/src/new.c | 751 | ||||
-rw-r--r-- | private/utils/restore/src/old.c | 527 | ||||
-rw-r--r-- | private/utils/restore/src/parse.c | 787 | ||||
-rw-r--r-- | private/utils/restore/src/restore.c | 481 | ||||
-rw-r--r-- | private/utils/restore/src/restore.rc | 12 | ||||
-rw-r--r-- | private/utils/restore/src/rtmsg.mc | 191 | ||||
-rw-r--r-- | private/utils/restore/src/sources | 36 |
16 files changed, 5808 insertions, 0 deletions
diff --git a/private/utils/restore/src/exit.c b/private/utils/restore/src/exit.c new file mode 100644 index 000000000..3403c5e7b --- /dev/null +++ b/private/utils/restore/src/exit.c @@ -0,0 +1,89 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + exit.c + +Abstract: + + Exit and abort functions. + +Author: + + Ramon Juan San Andres (ramonsa) 20-Feb-1990 + + +Revision History: + + +--*/ + + +#include "restore.h" +#include <process.h> + + + +// ********************************************************************** + +void +ExitStatus ( + DWORD Status + ) +/*++ + +Routine Description: + + Exits the program with certain status code + +Arguments: + + Status - Error level with which to exit + +Return Value: + + None. + +--*/ + +{ + exit(Status); +} + + + + +// ********************************************************************** + +void +AbortTheProgram ( + CHAR *FileName, + DWORD LineNumber + ) +/*++ + +Routine Description: + + Sets the global variables that indicate an abort condition + +Arguments: + + IN FileName - Supplies the file name + IN LineNumber - Supplie the line number + +Return Value: + + None. + +--*/ +{ + + Abort = TRUE; + +#if defined (DEBUG) + DbgPrint("Program aborted in file %s, line %d\n", FileName, LineNumber); +#endif + +} diff --git a/private/utils/restore/src/filecopy.c b/private/utils/restore/src/filecopy.c new file mode 100644 index 000000000..c180ddd9f --- /dev/null +++ b/private/utils/restore/src/filecopy.c @@ -0,0 +1,736 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + filecopy.c + +Abstract: + + Copy data from one handle to another. + +Author: + + Ramon Juan San Andres (ramonsa) 20-Feb-1991 Addapted from ztools + + +Revision History: + + +--*/ + +#include "restore.h" + +// +// Reader thread stack size +// +#define READER_STACK_SIZE 1024 + +// +// We copy in small chunks +// +#define COPY_BUFFER_SIZE (1024 * 32) + + +// +// A queue Node +// +typedef struct COPY_NODE *PCOPY_NODE; +typedef struct COPY_NODE { + PCOPY_NODE Next; // Next in queue + DWORD BufferSize; + BYTE Buffer[1]; +} COPY_NODE; + + +// +// The queue structure +// +typedef struct QUEUE_STRUCTURE { + PCOPY_NODE Head; // First in queue + PCOPY_NODE Tail; // Last in queue + DWORD NumberOfNodes; // Number of nodes + HANDLE Semaphore; // Coordination semaphore + CRITICAL_SECTION CriticalSection; // Critical Section +} QUEUE_STRUCTURE, *PQUEUE_STRUCTURE; + + +// +// Global data +// +BOOL QueueInitialized = FALSE; +QUEUE_STRUCTURE Queue; +HANDLE HandleSrc; // Handle of source file +HANDLE HandleDst; // Handle of destination file +DWORD NumberOfBytes; // Number of bytes to copy +BOOL StatusCode; // Status code + + +// +// Local Prototypes +// +BOOL +UseOneThread ( + HANDLE Src, + HANDLE Dst, + DWORD Bytes + ); + +BOOL +UseTwoThreads ( + HANDLE Src, + HANDLE Dst, + DWORD Bytes + ); + +void +Reader ( + ); + +void +Writer ( + ); + +BOOL +InitializeQueue ( + void + ); + +BOOL +Enqueue ( + PCOPY_NODE Node + ); + +PCOPY_NODE +Dequeue ( + ); + +void +FlushWaiters ( + ); + +void +PrintQueue( + ); + + + +// ********************************************************************** + +BOOL +CopyData ( + HANDLE Src, + HANDLE Dst, + DWORD Bytes + ) +/*++ + +Routine Description: + + Copies data from one handle to another + +Arguments: + + IN Src - Supplies handle of source file + IN Dst - Supplies handle of destination file + IN Bytes - Supplies number of bytes to copy + +Return Value: + + TRUE if all bytes copied, FALSE otherwise + +--*/ +{ + + if (!QueueInitialized) { + InitializeQueue(); + } + + if (Bytes < COPY_BUFFER_SIZE) { + // + // The chunk size is smaller than our buffer size, so there will + // only be one read and one copy. We use one thread. + // + return UseOneThread(Src, Dst, Bytes); + + } else { + // + // Since several reads/writes will be involved, we will do the + // copy using two threads (one reader and one writer). + // + return UseTwoThreads(Src, Dst, Bytes); + } +} + + + + + +// ********************************************************************** + +BOOL +UseOneThread ( + HANDLE Src, + HANDLE Dst, + DWORD Bytes + ) +/*++ + +Routine Description: + + Copies data from one handle to another using one thread + +Arguments: + + IN Src - Supplies handle of source file + IN Dst - Supplies handle of destination file + IN Bytes - Supplies number of bytes to copy + +Return Value: + + TRUE uf all bytes copied, FALSE otherwise + +--*/ +{ + + + PBYTE Buffer; // Pointer to buffer + DWORD NumRead; // Number of bytes read + DWORD NumWrite; // Number of bytes written + BOOL StatusOk; // Status of API + + + Buffer = (PBYTE)Malloc(Bytes); + + if (!Buffer) { + // + // We could not get a buffer big enough, so we will have to split + // the request. We better use two threads to do this. + // + return UseTwoThreads(Src, Dst, Bytes); + } + + // + // Read the data + // + StatusOk = ReadFile( Src, + Buffer, + Bytes, + &NumRead, + NULL ); + + if (!StatusOk || (NumRead != Bytes)) { + + DisplayMsg( STD_OUT, REST_ERROR_READING_BACKUP ); + return FALSE; + } + + + // + // Write the chunk + // + StatusOk = WriteFile( Dst, + Buffer, + Bytes, + &NumWrite, + NULL ); + + + if (!StatusOk || (NumWrite != Bytes)) { + + return FALSE; + } + + Free(Buffer); + + return TRUE; +} + + + + + +// ********************************************************************** + +BOOL +UseTwoThreads ( + HANDLE Src, + HANDLE Dst, + DWORD Bytes + ) +/*++ + +Routine Description: + + Copies data from one handle to another using two threads + +Arguments: + + IN Src - Supplies handle of source file + IN Dst - Supplies handle of destination file + IN Bytes - Supplies number of bytes to copy + +Return Value: + + TRUE if all bytes copied, FALSE otherwise + +--*/ +{ + + + HANDLE ReaderHandle; // Handle of reader + DWORD ReaderId; // Thread Id of reader + + HandleSrc = Src; + HandleDst = Dst; + NumberOfBytes = Bytes; + StatusCode = TRUE; + + + // + // We create the reader thread + // + ReaderHandle = CreateThread( NULL, + READER_STACK_SIZE, + (LPTHREAD_START_ROUTINE)Reader, + NULL, + 0, + &ReaderId ); + + if (ReaderHandle == INVALID_HANDLE_VALUE) { + return FALSE; + } + + // + // We become the writer thread. + // + Writer(); + + // + // Dispose of the reader handle + // + CloseHandle(ReaderHandle); + + return StatusCode; +} + + + + + +// ********************************************************************** + +void +Reader ( + ) +/*++ + +Routine Description: + + Reads a chunk of data from the source handle + +Arguments: + + None + +Return Value: + + none + +--*/ +{ + + DWORD BytesLeft; // Number of bytes left to copy + PCOPY_NODE Node; // Data buffer + DWORD BufferSize; // Buffer size + DWORD NumRead; // Number of bytes read + BOOL StatusOk; + + + BytesLeft = NumberOfBytes; + + + while (BytesLeft && (StatusCode)) { + + // + // We will split the read in pieces as big as possible up to + // COPY_BUFFER_SIZE. + // + Node = NULL; + BufferSize = min(BytesLeft, COPY_BUFFER_SIZE); + + while (!Node && (BufferSize > 0)) { + // + // Try to get a buffer of size BufferSize. If we can't, then + // keep trying, halving the BufferSize each time. + // + Node = (PCOPY_NODE)Malloc(BufferSize + sizeof(COPY_NODE)); + if (!Node) { + BufferSize /= 2; + } + } + + if (!Node) { + // + // We could not allocate the buffer. + // Let the writer know, then exit + // +#if DBG==1 + OutputDebugString("RESTORE: Out of memory\n"); +#endif + StatusCode = FALSE; + FlushWaiters(); + return; + } + + // + // Read the chunk + // + StatusOk = ReadFile( HandleSrc, + Node->Buffer, + BufferSize, + &NumRead, + NULL ); + + if (!StatusOk || (NumRead != BufferSize)) { + // + // We could not read the chunk. Let the writer know, then + // exit + // +#if DBG==1 + OutputDebugString("RESTORE: Cannot read.\n"); +#endif + DisplayMsg( STD_OUT, REST_ERROR_READING_BACKUP ); + StatusCode = FALSE; + Free(Node); + FlushWaiters(); + return; + } + + // + // We got the data data. Put it in the writer queue and + // continue. + // + Node->BufferSize = BufferSize; + Enqueue(Node); + BytesLeft -= BufferSize; + } + + // + // Our job is done + // + return; +} + + + + + +// ********************************************************************** + +void +Writer ( + ) +/*++ + +Routine Description: + + Writes a chunk of data to the destination handle + +Arguments: + + None + +Return Value: + + none + +--*/ +{ + + DWORD BytesLeft; // Number of bytes left to copy + PCOPY_NODE Node; // Data buffer + DWORD BytesWritten; // Number of bytes written + + BytesLeft = NumberOfBytes; + + while (BytesLeft) { + + // + // Try to get a data buffer + // + Node = Dequeue(); + + if (Node) { + + // + // We got a valid data buffer, but will only write it + // if we have found no errors so far. + // + if (StatusCode) { + // + // Got the buffer and everything has gone fine so + // far, write the data out + // + WriteFile( HandleDst, + Node->Buffer, + Node->BufferSize, + &BytesWritten, + NULL ); + + if (BytesWritten == Node->BufferSize) { + // + // Wrote the data successfully, continue + // + BytesLeft -= Node->BufferSize; + + } else { + // + // We could not write the data. Set the status code + // to error, but keep dequeueing, so the queue + // won't be left in an inconsistent state. The reader + // will eventually stop enqueueing and we will get out. + // +#if DBG==1 + OutputDebugString("RESTORE: Cannot write file.\n"); +#endif + StatusCode = FALSE; + } + } + + // + // Free the buffer + // + Free(Node); + + } else { + + // + // The queue is empty!. Something must have gone wrong. + // The StatusCode has the error status. We only have + // to get out of here + // + break; + } + } +} + + + + + +// ********************************************************************** + +BOOL +InitializeQueue ( + void + ) +/*++ + +Routine Description: + + Initializes the copy queue + +Arguments: + + none + +Return Value: + + TRUE if initialized + +--*/ +{ + Queue.Head = Queue.Tail = NULL; + Queue.NumberOfNodes = 0; + Queue.Semaphore = CreateSemaphore(NULL, 0, 0x7FFFFFFF,NULL); + InitializeCriticalSection(&(Queue.CriticalSection)); + + return QueueInitialized = TRUE; +} + + + +// ********************************************************************** + +BOOL +Enqueue ( + PCOPY_NODE Node + ) +/*++ + +Routine Description: + + Inserts a node in the queue + +Arguments: + + Node - Supplies the node to add to queue + +Return Value: + + TRUE if enqueued + FALSE otherwise + +--*/ +{ + LONG PreviousCount; + + Node->Next = NULL; + + // + // Now insert in queue + // + EnterCriticalSection(&(Queue.CriticalSection)); + + // printf("ENQUEUE...\n"); + if (!(Queue.Tail)) { + Queue.Head = Node; + } else { + (Queue.Tail)->Next = Node; + } + Queue.Tail = Node; + + Queue.NumberOfNodes++; + + ReleaseSemaphore(Queue.Semaphore, 1, &PreviousCount); + + // PrintQueue(); + + LeaveCriticalSection(&(Queue.CriticalSection)); + + return TRUE; +} + + + + +// ********************************************************************** + +PCOPY_NODE +Dequeue ( + ) +/*++ + +Routine Description: + + Gets a node from the queue + +Arguments: + + None + +Return Value: + + Pointer to information or NULL is queue empty. + +--*/ +{ + + PCOPY_NODE Node = NULL; + + if ( StatusCode ) { + // + // Wait for a node and dequeue it + // + WaitForSingleObject(Queue.Semaphore, INFINITE); + + if ( StatusCode ) { + + EnterCriticalSection(&(Queue.CriticalSection)); + // printf("DEQUEUE...\n"); + + Node = (Queue.Head); + + if (Node) { + // + // Something in Queue, Get the first node. + // + if (!(Queue.Head = Node->Next)) { + Queue.Tail = NULL; + } + Queue.NumberOfNodes--; + } + + // PrintQueue(); + LeaveCriticalSection(&(Queue.CriticalSection)); + } + } + + return Node; +} + + + + + +// ********************************************************************** + +void +FlushWaiters ( + ) +/*++ + +Routine Description: + + Wakes up people waiting on a queue + +Arguments: + + None + +Return Value: + + None + +--*/ +{ + LONG PreviousCount; + ReleaseSemaphore(Queue.Semaphore, 1, &PreviousCount); +} + + +#if 0 + +// ********************************************************************** + +void +PrintQueue( + ) +/*++ + +Routine Description: + + Prints queue contents + +Arguments: + + IN Queue - Supplies pointer to queue + +Return Value: + + none + +--*/ +{ + PCOPY_NODE Node; + DWORD i = 0; + BOOL f = FALSE; + + Node = Queue.Head; + + printf("\tQueue contents: [%X]\n", Queue); + while (Node) { + printf("\t\t%d.- %X \n", i, (int)Node); + i++; + f = TRUE; + Node = Node->Next; + } + + if (!f) { + printf("\t\tQueue is empty. [%X]\n", Queue); + } +} + +#endif diff --git a/private/utils/restore/src/generic.c b/private/utils/restore/src/generic.c new file mode 100644 index 000000000..9dc3ea107 --- /dev/null +++ b/private/utils/restore/src/generic.c @@ -0,0 +1,536 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + generic.c + +Abstract: + + Functions that interface with the functions that deal with the + backup-disk structures. + +Author: + + Ramon Juan San Andres (ramonsa) 20-Feb-1991 + + +Revision History: + + +--*/ + + + +#include <conio.h> +#include "restore.h" + + + +// +// SPECIFIC_FUNCTIONS contains pointers to the functions that +// understand a specific backup format. +// +typedef struct SPECIFIC_FUNCTIONS { + + // + // Verify a disk format and sequence + // + DWORD (*VerifyDiskSequence) (PWORD); + + // + // Determine if a disk is the last in the backup set + // + BOOL (*IsLastBackupDisk) (void); + + // + // Get the next backed-up file + // + PFILE_INFO (*GetNextFile) (void); + + // + // Restore a file + // + BOOL (*RestoreFile) (PFILE_INFO); + +} SPECIFIC_FUNCTIONS, *PSPECIFIC_FUNCTIONS; + + + + +// +// +// We access the specific functions thru these structures: +// +static SPECIFIC_FUNCTIONS Specific_Old = { Old_VerifyDiskSequence, + Old_IsLastBackupDisk, + Old_GetNextFile, + Old_RestoreFile + }; + +static SPECIFIC_FUNCTIONS Specific_New = { New_VerifyDiskSequence, + New_IsLastBackupDisk, + New_GetNextFile, + New_RestoreFile + }; + + +// +// Pointer to the current specific function structure +// +static PSPECIFIC_FUNCTIONS Specific_Current = NULL; + + + + + +// +// Local Prototypes +// +DWORD +GetFirstBackupDisk ( + PWORD Sequence + ); + +DWORD +GetNextBackupDisk ( + PWORD Sequence + ); + + + + + + +// ********************************************************************** + +DWORD +GetABackupDisk ( + PWORD Sequence + ) +/*++ + +Routine Description: + + Mounts a backup disk and updates the disk sequence. + +Arguments: + + OUT Sequence - Provides pointer to the sequence number of the backup disk desired + +Return Value: + + DISK_OK if disk mounted + DISK_OUTOFSEQUENCE if disk is out of sequence + DISK_UNKNOWN if not a backup disk (or invalid format) + +--*/ +{ + CHAR StringBuffer[16]; // Holds string representation of Sequence + DWORD DiskType; // Type of backup disk + + // + // Ask the user to insert disk, if removable + // + if (SourceDriveType == DRIVE_REMOVABLE) { + + MakeStringNumber(StringBuffer, *Sequence, 2); + putc( '\r', STD_OUT ); + putc( '\r', STD_OUT ); + putc( '\n', STD_OUT ); + DisplayMsg(STD_ERR, REST_MSG_INSERT_SOURCE, StringBuffer, SourceSpec); + DisplayMsg(STD_ERR, REST_MSG_PRESS_ANY_KEY); + GetKey(NULL, STD_ERR, STD_ERR); + } + + if (!Specific_Current) { + // + // We don't know the format of the backup disk. + // We must determine it. + // + DiskType = GetFirstBackupDisk(Sequence); + + } else { + // + // We already accepted a previous backup disk, from now on + // we only accept disks in the same format. + // + DiskType = GetNextBackupDisk(Sequence); + } + +#if defined (DEBUG) + DbgPrint("The type of the backup disk is %X\n",DiskType); +#endif + + return DiskType; +} + + + + + +// ********************************************************************** + +DWORD +GetFirstBackupDisk ( + PWORD Sequence + ) +/*++ + +Routine Description: + + Gets the first backup disk. Determines disk format. Updates sequence. + +Arguments: + + OUT Sequence - Provides pointer to the sequence number of the backup disk desired + +Return Value: + + DISK_OK if format and sequence match, + DISK_OUTOFSEQUENCE if new format but wrong sequence + DISK_UNKNOWN if not in new format + +--*/ +{ + + DWORD DiskFormat; // Disk format + DWORD LastFormat = 0x00000000; // Format of last disk + WORD CurrentSequenceNumber = *Sequence; // Sequence of current disk + WORD LastSequenceNumber = *Sequence; // Sequence of last disk + CHAR StringBuffer[16]; // Holds Sequence in string format + + + while (TRUE) { + // + // Get the disk format of the current disk + // + DiskFormat = New_VerifyDiskSequence(&CurrentSequenceNumber); + if (DiskFormat & DISK_UNKNOWN) { + // + // Disk is not in the new format, see if it is in the old + // format + // + DiskFormat = Old_VerifyDiskSequence(&CurrentSequenceNumber); + if (DiskFormat & DISK_UNKNOWN) { + // + // Nope, this is not a backup disk + // + break; + } + } + + // + // If the disk is out of sequence we must make sure that this + // is what the user wants. + // + if (DiskFormat & DISK_OUTOFSEQUENCE) { + if (CurrentSequenceNumber == *Sequence) { + // + // The disk has the sequence number that we asked for, + // this is our guy. + // + break; + + } else if ((CurrentSequenceNumber == LastSequenceNumber) && + (DiskFormat = LastFormat)) { + // + // We verified the disk twice in a row. We accept it. + // + break; + + } else { + // + // We have an out of sequence disk, ask the user to + // confirm + // + MakeStringNumber(StringBuffer, *Sequence, 2); + DisplayMsg(STD_OUT, REST_WARNING_DISK_OUTOFSEQUENCE, StringBuffer, SourceSpec); + GetKey(NULL, STD_OUT, STD_OUT); + + LastFormat = DiskFormat; + LastSequenceNumber = CurrentSequenceNumber; + } + + } else { + + // + // The disk is correct, accept it + // + break; + } + } + + if (DiskFormat & DISK_NEW_FORMAT) { + // + // New format. Set FormatOfDisk and initialize pointer to + // specific functions. + // + Specific_Current = &Specific_New; + + } else { + // + // Old format. Set FormatOfDisk and initialize pointer to + // specific functions. + // + Specific_Current = &Specific_Old; + } + + return DiskFormat; +} + + + + +// ********************************************************************** + +DWORD +GetNextBackupDisk ( + PWORD Sequence + ) +/*++ + +Routine Description: + + Gets the next backup disk. Updates sequence. + +Arguments: + + OUT Sequence - Provides pointer to the sequence number of the backup disk desired + +Return Value: + + DISK_OK if format and sequence match, + DISK_OUTOFSEQUENCE if new format but wrong sequence + DISK_UNKNOWN if not in new format + +--*/ +{ + + DWORD DiskFormat; // Disk format + WORD CurrentSequenceNumber = *Sequence; // Current Sequence + WORD LastSequenceNumber = *Sequence; // Last sequence + CHAR StringBuffer[16]; // Holds sequence in string format + + // + // Note that at this point we know what format of disks we are + // expected to accept. + // + while (TRUE) { + // + // Verify current disk + // + DiskFormat = (*(Specific_Current->VerifyDiskSequence))(&CurrentSequenceNumber); + + // + // If the disk is out of sequence we must make sure that this + // is what the user wants. + // + if (DiskFormat & DISK_OUTOFSEQUENCE) { + if (CurrentSequenceNumber == *Sequence) { + // + // The disk has the sequence number that we asked for, + // this is our guy. + // + break; + + } else if ((CurrentSequenceNumber == LastSequenceNumber) && + (CurrentSequenceNumber > *Sequence)) { + // + // We verified the disk twice in a row. We accept it. + // + break; + + } else { + // + // We have an out of sequence disk, ask the user to + // confirm + // + MakeStringNumber(StringBuffer, *Sequence, 2); + DisplayMsg(STD_OUT, REST_WARNING_DISK_OUTOFSEQUENCE, StringBuffer, SourceSpec); + GetKey(NULL, STD_OUT, STD_OUT); + + LastSequenceNumber = CurrentSequenceNumber; + } + + } else if (DiskFormat & DISK_OK) { + // + // The disk is correct, accept it + // + *Sequence = CurrentSequenceNumber; + break; + + } else { + // + // Not a backup disk in the correct format, get out + // + break; + } + } + + return DiskFormat; +} + + + + + +// ********************************************************************** + +BOOL +IsLastBackupDisk ( + void + ) +/*++ + +Routine Description: + + Determines if the backup disk is the last one. + +Arguments: + + None + +Return Value: + + TRUE if disk is the last one, FALSE otherwise + +--*/ +{ + return (*(Specific_Current->IsLastBackupDisk))(); +} + + + + +// ********************************************************************** + +PFILE_INFO +GetNextFile ( + void + ) +/*++ + +Routine Description: + + Gets next file. + +Arguments: + + None + +Return Value: + + Pointer to file info + +--*/ +{ + return (*(Specific_Current->GetNextFile))(); +} + + + + +// ********************************************************************** + + +BOOL +RestoreFile ( + PFILE_INFO FileInfo + ) +/*++ + +Routine Description: + + Restores one file. + +Arguments: + + IN FileInfo - Supplies pointer to information structure + +Return Value: + + TRUE if file restored, FALSE otherwise + +--*/ +{ + + CHAR PathBuffer[MAX_PATH]; // For creating target directory + PCHAR PathPointer; // Pointer within pathBuffer. + + if (FileInfo->TargetExists) { + if (FileInfo->Sequence == 1) { + // + // First chunk of a file, rename original file, so we can + // recover it in case of failure. + // + Rename(FileInfo->TargetPath, FileInfo->TmpName); + } + } else { + // + // The target directory might not exist, we have to create it. + // + MakeFullPath(PathBuffer, DestinationDrive, FileInfo->Path, ""); + + PathPointer = &PathBuffer[3]; + + while (*PathPointer) { + while (*PathPointer && *PathPointer != '\\') { + PathPointer++; + } + if (*PathPointer) { + *PathPointer = '\0'; + CreateDirectory( PathBuffer, NULL ); + *PathPointer++ = '\\'; + } + } + } + + // + // Have the specific function restore the file + // + return (*(Specific_Current->RestoreFile))(FileInfo); + +} + + + + + +// ********************************************************************** + + +BOOL +RecoverFile ( + PFILE_INFO FileInfo + ) +/*++ + +Routine Description: + + Recovers a Tmp file after a backup failed. + +Arguments: + + IN FileInfo - Supplies pointer to information structure + +Return Value: + + TRUE if recovered + FALSE otherwise + +--*/ +{ + if (FileInfo->TargetExists) { + + if (Rename(FileInfo->TmpName, FileInfo->TargetPath)) { + // + // Cannot rename, we cannot recover + // + return FALSE; + } + + } + return TRUE; +} diff --git a/private/utils/restore/src/makefile b/private/utils/restore/src/makefile new file mode 100644 index 000000000..6ee4f43fa --- /dev/null +++ b/private/utils/restore/src/makefile @@ -0,0 +1,6 @@ +# +# DO NOT EDIT THIS FILE!!! Edit .\sources. if you want to add a new source +# file to this component. This file merely indirects to the real make file +# that is shared by all the components of NT OS/2 +# +!INCLUDE $(NTMAKEENV)\makefile.def diff --git a/private/utils/restore/src/makefile.inc b/private/utils/restore/src/makefile.inc new file mode 100644 index 000000000..f7cbc888c --- /dev/null +++ b/private/utils/restore/src/makefile.inc @@ -0,0 +1,4 @@ +restore.rc: rtmsg.rc msg00001.bin + +..\inc\rtmsg.h rtmsg.rc msg00001.bin: rtmsg.mc + mc -v -h ..\inc\ rtmsg.mc diff --git a/private/utils/restore/src/match.c b/private/utils/restore/src/match.c new file mode 100644 index 000000000..bda009018 --- /dev/null +++ b/private/utils/restore/src/match.c @@ -0,0 +1,841 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + misc.c + +Abstract: + + Functions for file matching. + +Author: + + Ramon Juan San Andres (ramonsa) 14-Dec-1990 + + +Revision History: + + +--*/ + + +#include "restore.h" + + + + +// +// Local Prototypes +// +void +GetFileDateAndTime ( + CHAR *FullPath, + PFATDATE Date, + PFATTIME Time + ); + +BOOL +NameMatch ( + PCHAR Pattern, + PCHAR Path + ); + +BOOL +DateAndTimeMatch ( + FATDATE BackupDate, + FATTIME BackupTime, + FATDATE CurrentDate, + FATTIME CurrentTime, + BOOL *ModifiedSinceLastBackup + ); + +BOOL +DateMatch ( + FATDATE Date + ); + +BOOL +TimeMatch ( + FATTIME Time + ); + +BOOL +IsSameDate ( + FATDATE Date1, + FATDATE Date2 + ); + +BOOL +IsSameTime ( + FATTIME Time1, + FATTIME Time2 + ); + +BOOL +IsBeforeDate( + FATDATE Date1, + FATDATE Date2 + ); + +BOOL +IsBeforeTime( + FATTIME Time1, + FATTIME Time2 + ); + + + + + +// ********************************************************************** + + +BOOL +FileMatch ( + PFILE_INFO FileInfo + ) +/*++ + +Routine Description: + + Determines if a File Information packet matches the global file + specification + +Arguments: + + IN FileInfo - Supplies the pointer to the file information structure + +Return Value: + + TRUE: File matches + FALSE: No match + +--*/ +{ + + FATDATE CurrentFileDate; // Date of target file + FATTIME CurrentFileTime; // Time of target file + DWORD Attributes; // File Attributes + BOOL ModifiedSinceLastBackup; // True if modified since last backup + DWORD PathMatch; // True if paths match + + + // + // Check paths + // + PathMatch = ComparePath(DestinationDir, FileInfo->Path); + + if ((PathMatch == COMPARE_PREFIX) && (!Flag_s)) { + // + // This is a subdirectory, but /s option not set. + // + return FALSE; + + } else if (PathMatch == COMPARE_NOMATCH) { + // + // Not same path, there's no match + // + return FALSE; + } + + + // + // File Names must match + // + if (!NameMatch(DestinationFile, FileInfo->FileName)) { + // + // Nope, no match + // + return FALSE; + } + + + // + // The name matches. Get the attributes of the file (at the + // same time, we find out if the file exists) + // + FileInfo->TargetAttributes = Attributes = GetFileAttributes(FileInfo->TargetPath); + + FileInfo->TargetExists = !(Attributes == (DWORD)-1); + + if (Flag_n) { + // + // If /n flag specified, then we must only restore the file if + // it no longer exists + // + if (Attributes == (DWORD)-1) { + // + // File does not exist, restore it + // + return TRUE; + + } else { + // + // The file exits, don't restore it + // + return FALSE; + } + } + + + if (Attributes == (DWORD)-1) { + // + // If the target does not exist, then we should restore it + // + return TRUE; + } + + // + // Get date and time of target file and see if they match + // + GetFileDateAndTime( FileInfo->TargetPath, + &CurrentFileDate, + &CurrentFileTime ); + + if (!DateAndTimeMatch( BackupDate, + BackupTime, + CurrentFileDate, + CurrentFileTime, + &ModifiedSinceLastBackup)) { + // + // Date and time does not match, don't restore this file + // + return FALSE; + } + + // + // If the /p option was specified and the file is read-only, or it has + // changed since the last backup, we prompt the user. + // + if ( Flag_p ) { + WriteNewLine = FALSE; + if ((Attributes & FILE_ATTRIBUTE_READONLY) || ModifiedSinceLastBackup) { + + BOOL ReturnValue; + + if (Attributes & FILE_ATTRIBUTE_READONLY) { + + DisplayMsg(STD_ERR, REST_WARNING_READONLY, FileInfo->TargetPath); + + } else { + + DisplayMsg(STD_ERR, REST_WARNING_FILE_CHANGED, FileInfo->TargetPath); + + } + + if ( !(ReturnValue = ( GetKey("YN", STD_OUT, STD_ERR) == 'Y'))) { + putc( '\r', STD_ERR ); + putc( '\n', STD_ERR ); + } else { + WriteNewLine = TRUE; + } + return ReturnValue; + } + } + + // + // If the /m option was specified, the file should only be restored + // if its archive bit is set. + // + if( Flag_m && !( Attributes & FILE_ATTRIBUTE_ARCHIVE ) ) { + + // the /m option is specified but the archive bit is + // not set. + // + return FALSE; + } + + // + // All tests pass, we must restore this file + // + return TRUE; +} + + + + + +// ********************************************************************** + +void +GetFileDateAndTime ( + CHAR *FullPath, + PFATDATE Date, + PFATTIME Time + ) +/*++ + +Routine Description: + + Gets the date and time of a file + + +Arguments: + + IN FullPath - Supplies pointer to file name + OUT Date - Supplies pointer to date + OUT Time - Supplies pointer to time + + +Return Value: + + none + + +--*/ +{ + HANDLE FileHandle; // File handle + FILETIME UtcFileTime; // Time of last modification (UTC) + FILETIME LocalFileTime; // Time of last modification (local) + BOOL StatusOk; + + // + // We need to obtain a handle to the file + // + FileHandle = CreateFile( FullPath, + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + + if (FileHandle == INVALID_HANDLE_VALUE) { + return; + } + + // + // Now get the date and time of last modification + // + StatusOk = GetFileTime( FileHandle, + NULL, + NULL, + &UtcFileTime ); + + // Convert the time to local time: + // + FileTimeToLocalFileTime( &UtcFileTime, &LocalFileTime ); + + + FileTimeToDosDateTime( &LocalFileTime, + (LPWORD)Date, + (LPWORD)Time); + + CloseHandle(FileHandle); +} + + + + + +// ********************************************************************** + + +DWORD +ComparePath ( + PCHAR Path1, + PCHAR Path2 + ) +/*++ + +Routine Description: + + Compares two paths + +Arguments: + + IN Path1 - Supplies pointer to first path + IN Path2 - Supplies pointer to second path + +Return Value: + + COMPARE_NOMATCH if paths don't match + COMPARE_PREFIX if Path1 is a prefix of Path2 + COMPARE_MATCH if Path1 == Path2 + + +--*/ +{ + + if ( (*Path1 == '\\') && + (*(Path1+1)=='\0') && + (*Path2 == '\\' ) ) { + + PCHAR p = Path2; +#ifdef DBCS + PCHAR q = PrevChar( Path2, Path2 + strlen(Path2) ); +#else + PCHAR q = Path2 + strlen(Path2)-1; +#endif + + while ( (p != q) && (*q != '\\')) { +#ifdef DBCS + q = PrevChar( p, q ); +#else + q--; +#endif + } + + if ( p == q ) { + return COMPARE_MATCH; + } else { + return COMPARE_PREFIX; + } + } + + + while (*Path1 && *Path2) { + if ((CHAR)(toupper(*Path1)) != (CHAR)(toupper(*Path2))) { + return COMPARE_NOMATCH; + } +#ifdef DBCS + Path1 = NextChar( Path1 ); + Path2 = NextChar( Path2 ); +#else + Path1++; Path2++; +#endif + } + + + if (*Path1) { + // + // Path1 is not a prefix of Path2 + // + return COMPARE_NOMATCH; + } + + if (*Path2) { + // + // Path1 is a prefix of Path2 + // + return COMPARE_PREFIX; + } + + // + // We have an exact match + // + return COMPARE_MATCH; + +} + + + + +// ********************************************************************** + + +BOOL +NameMatch ( + PCHAR Pattern, + PCHAR Path + ) +/*++ + +Routine Description: + + Determines if a pattern matches a file path + +Arguments: + + IN Pattern - Supplies pointer to pattern (may include wildcards) + IN Path - Supplies pointer to path + +Return Value: + + TRUE: match + FALSE: no match + +--*/ +{ + + switch (*Pattern) { + case '\0': + return *Path == '\0'; + + case '.': + if ( *Path == '\0' ) { +#ifdef DBCS + return NameMatch( NextChar(Pattern), Path ); +#else + return NameMatch( Pattern+1, Path ); +#endif + } else if ( *Path == '.' ) { +#ifdef DBCS + // Yes, both Pattern and Path point '.', we don't need NextChar(). + // But it's a good idea to always call a right function. + // + return NameMatch( NextChar(Pattern), NextChar(Path) ); +#else + return NameMatch( Pattern+1, Path+1 ); +#endif + } + return FALSE; + + case '?': +#ifdef DBCS + return ( *Path != '\0') && NameMatch( NextChar(Pattern), NextChar(Path)) ; +#else + return ( *Path != '\0') && NameMatch(Pattern+1, Path+1) ; +#endif + + case '*': +#ifdef DBCS + { + PCHAR PathTmp; + + do { + if ( NameMatch( NextChar(Pattern), Path) ) { + return TRUE; + } + PathTmp = Path; + Path = NextChar(Path); + } while ( *PathTmp ); + } +#else + do { + if (NameMatch(Pattern + 1, Path)) { + return TRUE; + } + } while (*Path++); +#endif + return FALSE; + + default: +#ifdef DBCS + if ( IsDBCSLeadByte( *Pattern )) { + if ( *Path == *Pattern && *(Path+1) == *(Pattern+1) ) { + return NameMatch( Pattern + 2, Path + 2 ); + } else { + return FALSE; + } + } else { + return ( toupper(*Path) == toupper(*Pattern)) && + NameMatch(Pattern + 1, Path + 1); + } +#else + return ( toupper(*Path) == toupper(*Pattern)) && NameMatch(Pattern + 1, Path + 1); +#endif + } +} + + + + +// ********************************************************************** + +BOOL +DateAndTimeMatch ( + FATDATE BackupDate, + FATTIME BackupTime, + FATDATE CurrentDate, + FATTIME CurrentTime, + BOOL *ModifiedSinceLastBackup + ) +/*++ + +Routine Description: + + Determines if Dates and times match + + +Arguments: + + IN BackupDate, - Supplies pointer to the backup date + IN BackupTime, - Supplies pointer to the backup time + IN CurrentDate, - Supplies pointer to the file date + IN CurrentTime, - Supplies pointer to the file time + OUT *ModifiedSinceLastBackup - Supplies pointer to boolean, which + becomes TRUE if the file has been + modified since last backup + +Return Value: + + TRUE if Date and time match + FALSE otherwise + + +--*/ +{ + + // + // First we determine if the file has been modified since the last + // backup by comparing the current date and the backup date + // + if ( (IsBeforeDate(BackupDate, CurrentDate)) || + (IsSameDate(BackupDate, CurrentDate) && IsBeforeTime(BackupTime, CurrentTime))) { + + *ModifiedSinceLastBackup = TRUE; + + } else { + + *ModifiedSinceLastBackup = FALSE; + } + + // + // Now we check the current date and time to see if they match + // the criteria set in the command line. + // + return ( DateMatch(CurrentDate) && + TimeMatch(CurrentTime) ); + +} + + + + +// ********************************************************************** + +BOOL +DateMatch ( + FATDATE Date + ) +/*++ + +Routine Description: + + Determines if date matches + + +Arguments: + + IN Date - Supplies date + +Return Value: + + TRUE if date matches + FALSE otherwise + + +--*/ +{ + BOOL Match = TRUE; + + if (Flag_z) { + // + // /z - date must be exact + // + return IsSameDate(Date, ExactDate); + } + + if (Flag_b) { + // + // /b - current date must be before or equal the specified date + // + Match &= IsBeforeDate(Date, BeforeDate) || IsSameDate(Date, BeforeDate); + } + + if (Flag_a) { + // + // /a - current date must be equal or after specified date + // + Match &= IsBeforeDate(AfterDate, Date) || IsSameDate(AfterDate, Date); + } + + return Match; + +} + + + + +// ********************************************************************** + +BOOL +TimeMatch ( + FATTIME Time + ) +/*++ + +Routine Description: + + Determines if a time matches + + +Arguments: + + IN Time - Supplies the time + +Return Value: + + TRUE if time matches + FALSE otherwise + + +--*/ +{ + BOOL Match = TRUE; + + if (Flag_Y) { + // + // /Y - times must be the same + // + return IsSameTime(Time, ExactTime); + } + + if (Flag_e) { + // + // /e - current time must be equal or earlier than given time + // + Match &= IsBeforeTime(Time, BeforeTime) || IsSameTime(Time, BeforeTime); + } + + if (Flag_L) { + // + // /L - current time must be equal or later than given time + // + Match &= IsBeforeTime(AfterTime, Time) || IsSameTime(AfterTime, Time); + } + + return Match; +} + + + + + +// ********************************************************************** + +BOOL +IsSameDate ( + FATDATE Date1, + FATDATE Date2 + ) +/*++ + +Routine Description: + + Determines if two dates are the same + + +Arguments: + + IN Date1 - Supplies First date + IN Date2 - Supplies second date + +Return Value: + + TRUE if both dates are the same + FALSE otherwise + + +--*/ +{ + + return (DATE_WORD(Date1) == DATE_WORD(Date2)); + +} + + + + +// ********************************************************************** + +BOOL +IsSameTime ( + FATTIME Time1, + FATTIME Time2 + ) +/*++ + +Routine Description: + + Determines if two times are the same + + +Arguments: + + + IN Time1 - Supplies pointer to first time + IN Time2 - Supplies pointer to second time + +Return Value: + + TRUE if both times are the same + FALSe otherwise + +--*/ +{ + + return (TIME_WORD(Time1) == TIME_WORD(Time2)); + +} + + + + + +// ********************************************************************** + +BOOL +IsBeforeDate( + FATDATE Date1, + FATDATE Date2 + ) +/*++ + +Routine Description: + + Determines if one date is before another date + + +Arguments: + + IN Date1 - Supplies pointer to first date + IN Date2 - Supplies pointer to second date + +Return Value: + + TRUE if Date1 is before Date2 + FALSE otherwise + +--*/ +{ + + return ((Date1.Year < Date2.Year) || + ((Date1.Year == Date2.Year) && + ((Date1.Month < Date2.Month) || + ((Date1.Month == Date2.Month) && (Date1.Day < Date2.Day))))); + +} + + + + + +// ********************************************************************** + +BOOL +IsBeforeTime( + FATTIME Time1, + FATTIME Time2 + ) +/*++ + +Routine Description: + + Determines if one time is before another time + + +Arguments: + + IN Time1 - Supplies pointer to first time + IN Time2 - Supplies pointer to second time + +Return Value: + + TRUE if Time1 is before Time2 + FALSE otherwise + +--*/ +{ + + return ((Time1.Hours < Time2.Hours) || + ((Time1.Hours == Time2.Hours) && + ((Time1.Minutes < Time2.Minutes) || + ((Time1.Minutes == Time2.Minutes) && (Time1.DoubleSeconds < Time2.DoubleSeconds))))); + +} + diff --git a/private/utils/restore/src/mbcs.c b/private/utils/restore/src/mbcs.c new file mode 100644 index 000000000..ad7bb1291 --- /dev/null +++ b/private/utils/restore/src/mbcs.c @@ -0,0 +1,94 @@ +#include "restore.h" + +#if defined JAPAN || defined DBCS + +/***************************************************************************\ +* NextChar() +* +* +* History: +* 04-26-93 takaok : stolen from user\client\strings.c and rename +* +\***************************************************************************/ + +LPSTR NextChar( LPCSTR lpCurrentChar) +{ + if ( IsDBCSLeadByte( *lpCurrentChar ) ) + lpCurrentChar += 2; + else if ( *lpCurrentChar ) + lpCurrentChar++; + return (LPSTR)lpCurrentChar; +} + +/***************************************************************************\ +* CharPrevA (API) +* +* Move to previous character in string, unless already at start +* +* History: +* 04-26-93 takaok : stolen from user\client\strings.c and rename +* +\***************************************************************************/ + +LPSTR PrevChar( LPCSTR lpStart, LPCSTR lpCurrentChar) +{ + LPCSTR lpCurrent = lpCurrentChar; + + if (lpStart == lpCurrent) + return (LPSTR)lpStart; + + if (--lpCurrent == lpStart) + return (LPSTR)lpStart; + + // we assume lpCurrentChar never points the second byte of double byte character + // this check makes things a little bit faster [takaok] + if ( IsDBCSLeadByte(*lpCurrent) ) + return ((LPSTR)(lpCurrent - 1)); + + do { + lpCurrent--; + if (!IsDBCSLeadByte(*lpCurrent)) { + lpCurrent++; + break; + } + } while(lpCurrent != lpStart); + + return (LPSTR)(lpCurrentChar - (((lpCurrentChar - lpCurrent) & 1) ? 1 : 2)); +} + +/***************************************************************************\ +* AppendBackSlashIfNeeded( LPSTR path, INT length ) +* +* check the last character of path, if it's not '\', append the '\'. +* +* History: +* 04-26-93 takaok : created +* +\***************************************************************************/ +BOOL AppendBackSlashIfNeeded( LPCSTR path, INT length ) +{ + PCSTR pc; + INT i; + BOOL bSlash; + +// +// check if the last character is backslash +// + bSlash = FALSE; + for ( pc = path, i = 0; *pc && i < length; i++, pc = NextChar( pc ) ) { + if ( *pc == '\\' ) + bSlash = TRUE; + else + bSlash = FALSE; + } + +// +// if the last character is not backslash, append "\\" +// + if ( ! bSlash ) { + strcat("path", "\\"); + } + return ( ! bSlash ); +} + +#endif // JAPAN || DBCS diff --git a/private/utils/restore/src/misc.c b/private/utils/restore/src/misc.c new file mode 100644 index 000000000..728d18acb --- /dev/null +++ b/private/utils/restore/src/misc.c @@ -0,0 +1,503 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + misc.c + +Abstract: + + Miscelaneous functions used by the restore utility. + +Author: + + Ramon Juan San Andres (ramonsa) 14-Dec-1990 + + +Revision History: + + +--*/ + + +#include "restore.h" + + + +// +// Template for temporary names +// +#define TMP_FILENAME "_REST%d.___" + + + + + + + +// ********************************************************************** + +void +MakeFullPath ( + CHAR *FullPath, + CHAR *Drive, + CHAR *Path, + CHAR *FileName + ) +/*++ + +Routine Description: + + makes a full path out of a drive, a directory and a filename + +Arguments: + + OUT FullPath - Supplies pointer to buffer + IN Drive - Supplies pointer to drive part + IN Path - Supplies pointer to directory path + IN FileName - Supplies pointer to file name + + +Return Value: + + none + + +--*/ +{ + CHAR *p; + + strcpy(FullPath,Drive); + strcat(FullPath, Path); +#ifdef DBCS + (VOID)AppendBackSlashIfNeeded(FullPath, strlen( FullPath ) ); +#else + p = FullPath + strlen(FullPath); + p--; + if (*p != '\\') { + strcat(FullPath, "\\"); + } +#endif + strcat(FullPath, FileName); +} + + + + + + + +// ********************************************************************** + +void +MakeTmpPath ( + CHAR *FullPath, + CHAR *Drive, + CHAR *Path, + DWORD Sequence + ) +/*++ + +Routine Description: + + makes a temporary file path + + +Arguments: + + OUT FullPath - Supplies pointer to buffer + IN Drive - Supplies pointer to drive part + IN Path - Supplies pointer to directory path + IN Sequence - Supplies sequence number + + +Return Value: + + none + + +--*/ +{ + CHAR *p; + + strcpy(FullPath,Drive); + strcat(FullPath, Path); + p = FullPath + strlen(FullPath); + p--; + if (*p != '\\') { + strcat(FullPath, "\\"); + } + p = p + strlen(p); + sprintf(p, TMP_FILENAME, Sequence); +} + + + + + + + + +// ********************************************************************** + +PCHAR +MakeStringDate( + CHAR *StringDate, + FATDATE Date + ) +/*++ + +Routine Description: + + Formats a date and puts its string representation in the given buffer + + +Arguments: + + OUT StringDate - Buffer where the date will be formated + IN Date - The date to format + +Return Value: + + Pointer to StringDate + +--*/ +{ + + MakeStringNumber(StringDate, Date.Month, 2); + StringDate[2] = '-'; + MakeStringNumber(&StringDate[3], Date.Day, 2); + StringDate[5] = '-'; + MakeStringNumber(&StringDate[6], Date.Year+1980, 4); + + return StringDate; +} + + + + +// ********************************************************************** + +PCHAR +MakeStringTime( + CHAR *StringTime, + FATTIME Time + ) +/*++ + +Routine Description: + + Formats a time and puts its string representation in the given buffer + + +Arguments: + + OUT StringTime - Buffer where the time will be formated + IN Time - The time to format + +Return Value: + + Pointer to StringDate + +--*/ +{ + + MakeStringNumber(StringTime, Time.Hours, 2); + StringTime[2] = ':'; + MakeStringNumber(&StringTime[3], Time.Minutes, 2); + StringTime[5] = ':'; + MakeStringNumber(&StringTime[6], Time.DoubleSeconds * 2, 4); + + return StringTime; +} + + + + + + +// ********************************************************************** + +PCHAR +MakeStringNumber( + CHAR *Buffer, + DWORD Number, + DWORD Width + ) +/*++ + +Routine Description: + + Formats a number and pads it with zeros + + +Arguments: + + OUT Buffer - Supplies the Buffer where the number will be formated + IN Number - Supplies the number + IN Width - Number of characters that the number must occupy + +Return Value: + + Pointer to Buffer + +--*/ + +{ + CHAR LocalBuffer[16]; + CHAR *p; + + sprintf(LocalBuffer, "%14d", Number); + + p = LocalBuffer + strlen(LocalBuffer) - 1; + + while ((p >= LocalBuffer) && (*p != ' ')) { + p--; + } + + while ((p >= LocalBuffer) && (strlen(p) <= Width)) { + *p-- = '0'; + } + + p++; + + strcpy(Buffer, p); + + return Buffer; +} + + + + +// ********************************************************************** + +BOOL +Rename ( + CHAR *OriginalFile, + CHAR *NewFile + ) +/*++ + +Routine Description: + + Renames a file, conserving attributes. Renames the file even if the + target file exists. + +Arguments: + + IN OriginalFile - Supplies name of original file + IN NewFile - Supplies name of new file + +Return Value: + + TRUE if renamed + FALSE otherwise + +--*/ +{ + DWORD Attributes; + + + // + // See if the original file exists + // + Attributes = GetFileAttributes(OriginalFile); + + if (Attributes == -1) { + // + // OriginalFile does not exist! + // + return FALSE; + } + + + // + // Set the attributes so we can rename it + // + if (!SetFileAttributes(OriginalFile, FILE_ATTRIBUTE_NORMAL)) { + // + // Cannot set the attributes + // + return FALSE; + } + + // + // Make sure that the target does not exist + // + Delete(NewFile); + + + // + // Now try to rename + // + if (!MoveFile(OriginalFile, NewFile)) { + // + // Could not rename, restore attributes + // + SetFileAttributes(OriginalFile, Attributes); + return FALSE; + } + + // + // Rename worked, set attributes of new file + // + return SetFileAttributes(NewFile, Attributes); +} + + + + + +// ********************************************************************** + +BOOL +Delete ( + CHAR *FileName + ) +/*++ + +Routine Description: + + Deletes a file, disregarding its attributes + +Arguments: + + IN FileName - Supplies name of file + +Return Value: + + TRUE if deleted + FALSE otherwise + +--*/ +{ + + + // + // Set the attributes of the file. + // + if (!SetFileAttributes(FileName, FILE_ATTRIBUTE_NORMAL)) { + // + // Cannot set the attributes. + // + return FALSE; + } + + // + // Delete the file + // + return DeleteFile(FileName); + +} + + + + + + + +#if defined (DEBUG) + + +// +// The C-runtime functions for checking the heap (_heapchk) are not +// working. The following functions just verify that we are not +// freeing memory twice, or freeing memory that we have not allocated. +// + +// +// Memory block +// +typedef struct BLOCK { + DWORD Signature; // Allocated or free + DWORD Size; // Size requested - does not include this header + BYTE Data[0]; // What we return +} BLOCK, *PBLOCK; + + +#define BLOCK_MALLOC 0xBA110CED +#define BLOCK_FREE 0xCACACACA + + +void * +DebugRealloc ( + void *Mem, + size_t Size, + CHAR * FileName, + DWORD LineNumber + ) +{ + PBLOCK b; + + if (Mem) { + + b = (PBLOCK)(((PBYTE)Mem) - sizeof(BLOCK)); + + if (b->Signature != BLOCK_MALLOC) { + DbgPrint(" ERROR: Realloc unallocated object %X ! File %s, Line %d\n", b, FileName, LineNumber); + } + + b->Signature = BLOCK_FREE; + + } else { + + b = NULL; + } + + b = realloc(b, Size + sizeof(BLOCK)); + + b->Signature = BLOCK_MALLOC; + b->Size = Size; + + return (void *)(b->Data); +} + + + +void * +DebugMalloc ( + size_t Size, + CHAR * FileName, + DWORD LineNumber + ) +{ + PBLOCK b = (PBLOCK)malloc(Size + sizeof(BLOCK)); + + if (b) { + + b->Signature = BLOCK_MALLOC; + b->Size = Size; + + return (void *)(b->Data); + } + + return NULL; + +} + + + + +void +DebugFree ( + void *Mem, + CHAR * FileName, + DWORD LineNumber + ) +{ + if (Mem) { + PBLOCK b = (PBLOCK)(((PBYTE)Mem) - sizeof(BLOCK)); + + if (b->Signature != BLOCK_MALLOC) { + DbgPrint(" ERROR: Freeing unallocated object %X ! File %s, Line %d\n", b, FileName, LineNumber); + } else { + b->Signature = BLOCK_FREE; + free(b); + } + } +} + + + +#endif diff --git a/private/utils/restore/src/msg.c b/private/utils/restore/src/msg.c new file mode 100644 index 000000000..6546b6901 --- /dev/null +++ b/private/utils/restore/src/msg.c @@ -0,0 +1,214 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + msg.c + +Abstract: + + Message displaying. + +Author: + + Ramon Juan San Andres (ramonsa) 20-Feb-1990 + + +Revision History: + + +--*/ + + +#include <conio.h> +#include "restore.h" +#include <stdarg.h> + + +#define DISPLAYBUFFER_SIZE 4096 +#define CTRL_C ((CHAR)3) + +// +// We use one buffer to display messages. Note that this means that +// no two threads should ever try to use the buffer at the same +// time. +// +static CHAR DisplayBuffer[DISPLAYBUFFER_SIZE]; + + +// ********************************************************************** + +void +Usage ( + void + ) +/*++ + +Routine Description: + + Display program usage + +Arguments: + + None + +Return Value: + + None. + +--*/ +{ + + DisplayMsg(STD_OUT, REST_MSG_USAGE, NULL); + ExitStatus(EXIT_NORMAL); +} + + + + + +// ********************************************************************** + +void +DisplayMsg ( + FILE* f, + DWORD MsgNum, + ... + ) +/*++ + +Routine Description: + + Display Message + +Arguments: + + f - Stream to which to write message + MsgNum - Message number + ... - arguments + + +Return Value: + + None. + +--*/ +{ + va_list ap; + + va_start(ap, MsgNum); + + FormatMessage( FORMAT_MESSAGE_FROM_HMODULE, + NULL, + MsgNum, + 0, + DisplayBuffer, + DISPLAYBUFFER_SIZE, + &ap ); + + fprintf(f, DisplayBuffer); + + va_end(ap); + +} + + + + + + +// ********************************************************************** + +CHAR +GetKey ( + PCHAR PossibleKeys, + FILE* StdHandleCh, + FILE* StdHandleNl + ) +/*++ + +Routine Description: + + Gets a key + +Arguments: + + IN PossibleKeys - Set of acceptable characters + IN StdHandleCh - Handle to write the response to + IN StdHandleNl - Handle to write new line to + +Return Value: + + Key pressed + +--*/ +{ + CHAR c; + CHAR Orgc; + PCHAR p; + HANDLE StdIn; + DWORD Mode; + BOOLEAN IsConsole; + + // + // Find out if stdin is a console + // + StdIn = GetStdHandle( STD_INPUT_HANDLE ); + IsConsole = GetConsoleMode( StdIn, &Mode ); + + + while (TRUE) { + + if ( IsConsole ) { + + Orgc = (CHAR)_getch(); + + // + // Some keypresses can result in the generation of two keycodes, + // eg the arrow keys. The getch() function stores the second + // keycode in the ungetch buffer. We want to discard that second + // keycode so it won't hose us the next time we want input from the + // user, but we don't want to call getch() if it might block for + // input. Thus this hack, which clears the ungetch buffer. + // + + _ungetch(1); + _getch(); + + } else { + Orgc = (CHAR)getchar(); + } + c = Orgc; + + if (c == CTRL_C ) { + ExitStatus( EXIT_USER ); + } + + if (c == (CHAR)0) { + continue; + } + + c = (CHAR)toupper(c); + + if (PossibleKeys) { + p = PossibleKeys; + while (*p) { + if (c == *p++) { + p--; + break; + } + } + if (*p) { + break; + } + } else { + break; + } + } + putc( Orgc, StdHandleCh ); + putc( '\r', StdHandleNl ); + putc( '\n', StdHandleNl ); + + return c; +} diff --git a/private/utils/restore/src/new.c b/private/utils/restore/src/new.c new file mode 100644 index 000000000..4f57fa0ff --- /dev/null +++ b/private/utils/restore/src/new.c @@ -0,0 +1,751 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + new.c + +Abstract: + + Functions dealing with new backup file format + +Author: + + Ramon Juan San Andres (ramonsa) 20-Feb-1991 + + +Revision History: + + +--*/ + + +#include "restore.h" + + +// +// A Disk in the "New" format can be either a DOS disk, an OS/2 1.1 +// disk, or an OS/2 1.2 disk. +// +typedef enum DISK_FORMAT { + FORMAT_DOS, // DOS Backup format + FORMAT_OS211, // OS/2 V1.1 Backup format + FORMAT_OS212 // OS/2 V1.2 Backup format +} DISK_FORMAT; + + +// +// Global variables +// +NEW_DISKINFO NewDiskInfo; // Disk header +BOOL DiskNew; // TRUE if this is a new disk +HANDLE ControlHandle; // Handle of control file +DISK_FORMAT DiskFormat; // Format of the backup disk +WORD NumEntries; // Number of entries in current dir + +// +// Buffers for containing directory and file records +// +BYTE DirBuffer [ sizeof( DIR_RECORD_OS212 ) ]; +BYTE FileBuffer [ sizeof( FILE_RECORD_OS212 ) ]; + +// +// The following macros are used to access the fields of the directory +// and file records of the different backup formats. +// +#define DirRecord ( (PDIR_RECORD)DirBuffer ) +#define DirRecordOs211 ( (PDIR_RECORD_OS211)DirBuffer ) +#define DirRecordOs212 ( (PDIR_RECORD_OS212)DirBuffer ) +#define FileRecord ( (PFILE_RECORD)FileBuffer ) +#define FileRecordOs211 ( (PFILE_RECORD_OS211)FileBuffer ) +#define FileRecordOs212 ( (PFILE_RECORD_OS212)FileBuffer ) + +// +// Macro for obtaining the offset of the next directory record in the +// control file. +// +#define NextDirRecord \ + ( (DiskFormat == FORMAT_DOS) ? DirRecord->NextDirRecord : \ + (DiskFormat == FORMAT_OS211) ? DirRecordOs211->NextDirRecord : \ + DirRecordOs212->NextDirRecord ) + + +// +// The names of the control and backup files. These are patched to +// reflect drive and sequence number. +// +BOOLEAN UseSubdir = FALSE; +CHAR ControlFile[] = "?:\\CONTROL.???"; +CHAR SubdirControlFile[] = "?:\\BACKUP\\CONTROL.???"; +CHAR BackupFile[] = "?:\\BACKUP.XXX"; +CHAR SubdirBackupFile[] = "?:\\BACKUP\\BACKUP.XXX"; + + + +// +// Local prototypes +// +static +BOOL +GetDirRecord ( + ); + +static +BOOL +GotOneFile ( + PFILE_INFO FileInfo + ); + + + + + + + +// ********************************************************************** + +DWORD +New_VerifyDiskSequence ( + PWORD Sequence + ) +/*++ + +Routine Description: + + Verify that a certain backup disk is mounted. + +Arguments: + + OUT Sequence - Supplies pointer to the desired sequence number. + +Return Value: + + DISK_OK if format and sequence match, + DISK_OUTOFSEQUENCE if new format but wrong sequence + DISK_UNKNOWN if not in new format + +--*/ +{ + + DWORD NumRead; // Number of bytes read + FILETIME UtcLastTime; // Time last written, UTC + FILETIME LastTime; // Time last written, local time + HANDLE FindHandle; // Handle for FindFirstFile + WIN32_FIND_DATA FindData; // Buffer for FindFirstFile + + // + // Patch the name of the control file. + // + ControlFile[0] = BackupFile[0] = *SourceSpec; + SubdirControlFile[0] = SubdirBackupFile[0] = *SourceSpec; + + ControlFile[11] = ControlFile[12] = ControlFile[13] = '?'; + SubdirControlFile[18] = SubdirControlFile[19] = SubdirControlFile[20] = '?'; + + // + // Try to find the control file + // + UseSubdir = FALSE; + FindHandle = FindFirstFile(ControlFile, &FindData); + + if (FindHandle == INVALID_HANDLE_VALUE) { + + // Did not find the control file in the root. If this + // is not a removable drive, look in the subdirectory + // BACKUP. + // + if( SourceDriveType != DRIVE_REMOVABLE && + (FindHandle = FindFirstFile(SubdirControlFile, &FindData)) != INVALID_HANDLE_VALUE ) { + + UseSubdir = TRUE; + + } else { + + // + // There is no control file, this is not a backup disk in the + // new format. + // + return DISK_UNKNOWN; + } + } + + // + // Patch the control and backup file names. + // + ControlFile[13] = BackupFile[12] = FindData.cFileName[10]; + ControlFile[12] = BackupFile[11] = FindData.cFileName[9]; + ControlFile[11] = BackupFile[10] = FindData.cFileName[8]; + + SubdirControlFile[20] = SubdirBackupFile[19] = FindData.cFileName[10]; + SubdirControlFile[19] = SubdirBackupFile[18] = FindData.cFileName[9]; + SubdirControlFile[18] = SubdirBackupFile[17] = FindData.cFileName[8]; + + FindClose(FindHandle); + + // + // Open the control file + // + ControlHandle = CreateFile( UseSubdir ? SubdirControlFile : ControlFile, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if (ControlHandle == INVALID_HANDLE_VALUE) { + // + // We should never get here, because we know the file is there! + // + return DISK_UNKNOWN; + } + + + // + // Read the header of the control file and verify it. + // + ReadFile( ControlHandle, &NewDiskInfo, sizeof(NEW_DISKINFO), &NumRead, NULL); + + if (NumRead != sizeof(NEW_DISKINFO)) { + // + // The Name might say otherwise, but this is not a valid control + // file. + // + CloseHandle(ControlHandle); + return DISK_UNKNOWN; + } + + + NewDiskInfo.Id[6] = '\0'; + if (strcmp(NewDiskInfo.Id, BACKUP_ID)) { + // + // The control file does not have the correct signature. + // + CloseHandle(ControlHandle); + return DISK_UNKNOWN; + } + + if (NewDiskInfo.SequenceNumber != (BYTE)*Sequence) { + // + // The disk is in the correct format, but has an incorrect + // sequence number. + // + CloseHandle(ControlHandle); + *Sequence = NewDiskInfo.SequenceNumber; + return (DISK_OUTOFSEQUENCE | DISK_NEW_FORMAT); + } + + // + // The disk is O.K. obtain the backup date. (We disregard the time) + // + GetFileTime( ControlHandle, + NULL, + NULL, + &UtcLastTime ); + + // Convert the file time into local time: + // + FileTimeToLocalFileTime( &UtcLastTime, &LastTime ); + + FileTimeToDosDateTime( &LastTime, + (LPWORD)&BackupDate, + (LPWORD)&BackupTime ); + + + DiskNew = TRUE; + + // + // Determine if the disk is a DOS, OS211 or OS212 backup disk + // + SetFilePointer(ControlHandle, sizeof(NEW_DISKINFO), 0, FILE_BEGIN); + + ReadFile( ControlHandle, DirRecord, sizeof(DIR_RECORD), &NumRead, NULL); + + + if ( NumRead != sizeof(DIR_RECORD)) { + + if ( NumRead >= sizeof( DIR_RECORD_OS212 ) - MAX_PATH ) { + if ( DirRecordOs212->SystemVersion == SYSTEM_VERSION_OS2_12 ) { + DiskFormat = FORMAT_OS212; + } else { + return DiskFormat = DISK_UNKNOWN; + } + } else { + return DiskFormat = DISK_UNKNOWN; + } + + } else { + + if ( DirRecordOs212->SystemVersion == SYSTEM_VERSION_OS2_12 ) { + DiskFormat = FORMAT_OS212; + } else if ( DirRecord->Length > sizeof(DIR_RECORD) ) { + DiskFormat = FORMAT_OS211; + } else if ( DirRecord->Length == sizeof(DIR_RECORD) ) { + DiskFormat = FORMAT_DOS; + } else { + return DiskFormat = DISK_UNKNOWN; + } + } + + return ( DISK_OK | DISK_NEW_FORMAT ); + +} + + + + +// ********************************************************************** + + +BOOL +New_IsLastBackupDisk ( + void + ) +/*++ + +Routine Description: + + Determines if the backup disk is the last one. + +Arguments: + + None + +Return Value: + + TRUE if disk is the last one, FALSE otherwise + +--*/ +{ + return (NewDiskInfo.Flag == LAST_BACKUP_DISK); +} + + + + + +// ********************************************************************** + +PFILE_INFO +New_GetNextFile ( + void + ) +/*++ + +Routine Description: + + Gets next file which meets the restore criteria. + +Arguments: + + None + +Return Value: + + Pointer to File info + +--*/ +{ + + PFILE_INFO FileInfo; // Pointer to FileInfo structure + + + if (DiskNew) { + // + // New disk. Read first Dir record + // + SetFilePointer(ControlHandle, sizeof(NEW_DISKINFO), 0, FILE_BEGIN); + + if ( !GetDirRecord()) { + return NULL; + } + + DiskNew = FALSE; + } + + // + // Assume that we will find a suitable file. Allocate memory + // for its information structure. This memory must be freed after + // the file has been restored. + // + FileInfo = Malloc(sizeof(FILE_INFO)); + + if (GotOneFile(FileInfo)) { + // + // Found a file, return its information + // + return FileInfo; + } else { + // + // No luck. + // + Free(FileInfo); + return NULL; + } +} + + + +// ********************************************************************** + +BOOL +GetDirRecord ( + ) +/*++ + +Routine Description: + + Gets directory record + +Arguments: + + None + +Return Value: + + TRUE if directory record read, FALSE otherwise + +--*/ +{ + DWORD NumRead; // Number of bytes read + + switch ( DiskFormat ) { + + case FORMAT_DOS: + ReadFile(ControlHandle, DirRecord, sizeof(DIR_RECORD), &NumRead, NULL); + if (NumRead != sizeof(DIR_RECORD)) { + return FALSE; + } + NumEntries = DirRecord->NumEntries; + break; + + case FORMAT_OS211: + ReadFile(ControlHandle, DirRecordOs211, sizeof(DIR_RECORD_OS211), &NumRead, NULL); + if (NumRead != sizeof(DIR_RECORD_OS211)) { + return FALSE; + } + NumEntries = DirRecordOs211->NumEntries; + break; + + case FORMAT_OS212: + ReadFile( ControlHandle, + DirRecordOs212, + sizeof(DIR_RECORD_OS212) - MAX_PATH, + &NumRead, + NULL ); + if ( NumRead != sizeof(DIR_RECORD_OS212) - MAX_PATH ) { + return FALSE; + } + ReadFile( ControlHandle, + (PBYTE)DirRecordOs212 + sizeof(DIR_RECORD_OS212) - MAX_PATH, + DirRecordOs212->Length - sizeof(DIR_RECORD_OS212) + MAX_PATH, + &NumRead, + NULL ); + if ( NumRead != DirRecordOs212->Length - sizeof(DIR_RECORD_OS212) + MAX_PATH ) { + return FALSE; + } + NumEntries = DirRecordOs212->NumEntries; + break; + + default: + return FALSE; + + } + return TRUE; +} + + + +// ********************************************************************** + +static +BOOL +GotOneFile ( + PFILE_INFO FileInfo + ) +/*++ + +Routine Description: + + Gets next file which meets the restore criteria. + +Arguments: + + OUT FileInfo - Supplies pointer to file information structure to + be filled-in. + +Return Value: + + TRUE if a suitable file was found + FALSE otherwise + +--*/ +{ + + DWORD NumRead; // Number of bytes read + + + // + // Make sure that we hava a FileInfo structure + // + if (!FileInfo) { + return FALSE; + } + + + while ( TRUE ) { + + if (NumEntries--) { + // + // There are more files under this directory + // + // Read file record + // + + switch ( DiskFormat ) { + + case FORMAT_DOS: + case FORMAT_OS211: + ReadFile(ControlHandle, FileRecord, sizeof(FILE_RECORD), &NumRead, NULL); + if (NumRead != sizeof(FILE_RECORD)) { + return FALSE; + } + // + // Initialize FileInfo structure + // + FileInfo->Path[0] = '\\'; + if ( DiskFormat == FORMAT_DOS ) { + strcpy(&(FileInfo->Path[1]), DirRecord->Path); + if (strlen(DirRecord->Path) != 0) { + strcat(FileInfo->Path, "\\"); + } + } else { + strcpy(&(FileInfo->Path[1]), DirRecordOs211->Path); + if (strlen(DirRecordOs211->Path) != 0) { + strcat(FileInfo->Path, "\\"); + } + } + memcpy(FileInfo->FileName, FileRecord->FileName, 12); + FileInfo->FileName[12] = '\0'; + FileInfo->Sequence = FileRecord->SequenceNumber; + FileInfo->Offset = FileRecord->FileOffset; + FileInfo->PartSize = FileRecord->PartSize; + FileInfo->Attr = FileRecord->Attr; + DATE_WORD(FileInfo->Date) = FileRecord->Date; + TIME_WORD(FileInfo->Time) = FileRecord->Time; + FileInfo->Flag = FILEINFO_OK; + if (FileRecord->Flag & LAST_FILECHUNK) { + FileInfo->Flag |= FILEINFO_LAST; + } + break; + + + case FORMAT_OS212: + ReadFile(ControlHandle, + FileRecordOs212, + sizeof(FILE_RECORD_OS212) - MAX_PATH, + &NumRead, + NULL); + if (NumRead != sizeof(FILE_RECORD_OS212) - MAX_PATH) { + return FALSE; + } + ReadFile(ControlHandle, + (PBYTE)FileRecordOs212 + sizeof(FILE_RECORD_OS212) - MAX_PATH, + FileRecordOs212->NameLength+1, + &NumRead, + NULL); + if (NumRead != (DWORD)(FileRecordOs212->NameLength+1)) { + return FALSE; + } + // + // Initialize FileInfo structure + // + FileInfo->Path[0] = '\\'; + memcpy( &(FileInfo->Path[1]), DirRecordOs212->Path, DirRecordOs212->PathLength ); + FileInfo->Path[ DirRecordOs212->PathLength+1] = '\0'; + if (DirRecordOs212->PathLength != 0) { + strcat(FileInfo->Path, "\\"); + } + memcpy(FileInfo->FileName, FileRecordOs212->FileName, FileRecordOs212->NameLength); + FileInfo->FileName[FileRecordOs212->NameLength] = '\0'; + FileInfo->Sequence = FileRecordOs212->SequenceNumber; + FileInfo->Offset = FileRecordOs212->FileOffset; + FileInfo->PartSize = FileRecordOs212->PartSize; + FileInfo->Attr = FileRecordOs212->Attr; + DATE_WORD(FileInfo->Date) = FileRecordOs212->Date; + TIME_WORD(FileInfo->Time) = FileRecordOs212->Time; + FileInfo->Flag = FILEINFO_OK; + if (FileRecordOs212->Flag & LAST_FILECHUNK) { + FileInfo->Flag |= FILEINFO_LAST; + } + break; + + default: + return FALSE; + + } + + MakeFullPath(FileInfo->TargetPath, DestinationDrive, FileInfo->Path, FileInfo->FileName); + + // + // If this FileInfo matches restore criteria, we got our guy. + // + if (FileMatch(FileInfo)) { + return TRUE; + } + + } else { + // + // No more files under current directory + // + if (NextDirRecord == LAST_DIR_RECORD) { + // + // No more entries in this disk + // + CloseHandle(ControlHandle); + return FALSE; + } else { + // + // Get next dir record + // + SetFilePointer(ControlHandle, NextDirRecord, 0, FILE_BEGIN); + + if ( !GetDirRecord() ) { + return FALSE; + } + } + } + } + + return FALSE; +} + + + + + +// ********************************************************************** + +BOOL +New_RestoreFile ( + PFILE_INFO FileInfo + ) +/*++ + +Routine Description: + + Restores one file. + +Arguments: + + IN FileInfo - Supplies pointer to information structure + +Return Value: + + TRUE if file restored, FALSE otherwise + +--*/ +{ + DWORD CreationDisposition; // How to open the target file + HANDLE HandleSrc; // Handle to BACKUP.XXX + HANDLE HandleDst; // Handle to target file + FILETIME UtcFileTime; // Time to set (UTC) + FILETIME LocalFileTime; // Time to set (Local) + BOOL RestoreStatus = TRUE; // Status of restore; + + + // + // Open (or create) the target file + // + if (FileInfo->Sequence == 1 ) { + // + // First chunk of the file. Create a new target file (overwritting + // the existing one. + // + CreationDisposition = CREATE_ALWAYS; + } else { + // + // Not the first chunk of the file. Apend to the existing file + // + CreationDisposition = OPEN_EXISTING; + } + + HandleDst = CreateFile( FileInfo->TargetPath, + GENERIC_WRITE, + 0, + NULL, + CreationDisposition, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + SetFilePointer( HandleDst, + 0, + 0, + FILE_END ); + + + + // + // Open the BACKUP.XXX file + // + HandleSrc = CreateFile( UseSubdir ? SubdirBackupFile : BackupFile, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + SetFilePointer( HandleSrc, + FileInfo->Offset, + 0, + FILE_BEGIN ); + + + // + // Everything set. Now copy the file + // + if (!(RestoreStatus = CopyData(HandleSrc, HandleDst, FileInfo->PartSize))) { + CloseHandle(HandleSrc); + CloseHandle(HandleDst); + return RestoreStatus; + } + + // + // If this is the last chunk of the file, the we set the + // attributes, date, etc. Note that the date and time stored + // in the FILE_INFO structure is local time, so it has to + // be converted to UTC before we can feed it to SetFileTime. + // + if (FileInfo->Flag & FILEINFO_LAST) { + + BOOL StatusOk; + + SetFileAttributes(FileInfo->TargetPath, FileInfo->Attr); + + StatusOk = DosDateTimeToFileTime( DATE_WORD(FileInfo->Date), + TIME_WORD(FileInfo->Time), + &LocalFileTime ); + + LocalFileTimeToFileTime( &LocalFileTime, &UtcFileTime ); + + StatusOk |= SetFileTime( HandleDst, + &UtcFileTime, + &UtcFileTime, + &UtcFileTime ); + + if (!StatusOk) { + return FALSE; + } else { + // FileInfo->TargetPath, + // (DWORD)FileInfo->Date.Date.Day, (DWORD)FileInfo->Date.Date.Month, (DWORD)FileInfo->Date.Date.Year, + // (DWORD)FileInfo->Time.Time.Hours, (DWORD)FileInfo->Time.Time.Minutes, (DWORD)FileInfo->Time.Time.DoubleSeconds*2)); + } + } + + CloseHandle(HandleSrc); + CloseHandle(HandleDst); + + return RestoreStatus; +} diff --git a/private/utils/restore/src/old.c b/private/utils/restore/src/old.c new file mode 100644 index 000000000..be8a7be84 --- /dev/null +++ b/private/utils/restore/src/old.c @@ -0,0 +1,527 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + old.c + +Abstract: + + Functions dealing with old format + +Author: + + Ramon Juan San Andres (ramonsa) 20-Feb-1991 + + +Revision History: + + +--*/ + +#include "restore.h" + + + + +// +// Global variables +// +CHAR BackupId[] = "?:\\BACKUPID.@@@"; // Backup disk header file +OLD_DISKINFO OldDiskInfo; // Contents of header file +CHAR FindPath[] = "?:\\*.*"; // What to look for +HANDLE FindHandle = INVALID_HANDLE_VALUE; // For FindFirst/Next +CHAR Buffer[MAX_PATH]; // Scratch buffer + + + + + +// +// Local prototypes +// +static +BOOL +GotOneFile ( + PFILE_INFO FileInfo, + LPWIN32_FIND_DATA FindBuffer + ); + + + + + + + + +// ********************************************************************** + +DWORD +Old_VerifyDiskSequence ( + PWORD Sequence + ) +/*++ + +Routine Description: + + Determine if structures are in old format. + +Arguments: + + OUT Sequence - Provides the desired sequence number + +Return Value: + + DISK_OK if format and sequence match, + DISK_OUTOFSEQUENCE if old format but wrong sequence + DISK_UNKNOWN if not in old format + +--*/ +{ + HANDLE FileHandle; + DWORD NumRead; + FILETIME LocalLastTime; + FILETIME UtcLastTime; + FATTIME FatTime; + + + // + // Try to open the BACKUPID.@@@ file + // + BackupId[0] = *SourceSpec; + + FileHandle = CreateFile( BackupId, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if (FileHandle == INVALID_HANDLE_VALUE) { + return DISK_UNKNOWN; + } + + + // + // We now try to read the entire header + // + ReadFile( FileHandle, &OldDiskInfo, sizeof(OLD_DISKINFO), &NumRead, NULL); + + if (NumRead != sizeof(OLD_DISKINFO)) { + // + // This is not a valid BackupId file + // + CloseHandle(FileHandle); + return DISK_UNKNOWN; + } + + if (OldDiskInfo.SequenceNumber != *Sequence) { + // + // Bad sequence number + // + CloseHandle(FileHandle); + *Sequence = OldDiskInfo.SequenceNumber; + return (DISK_OUTOFSEQUENCE | DISK_OLD_FORMAT); + } + + + // + // The disk is O.K. (we assume). Obtain the backup date and + // convert it to local time. + // + GetFileTime( FileHandle, + NULL, + NULL, + &UtcLastTime ); + + FileTimeToLocalFileTime( &UtcLastTime, &LocalLastTime ); + + + FileTimeToDosDateTime( &LocalLastTime, + (LPWORD)&BackupDate, + (LPWORD)&FatTime ); + + + CloseHandle(FileHandle); + + // + // If the FindHandle is valid, close it so we start afreash. + // + FindClose(FindHandle); + + return (DISK_OK | DISK_OLD_FORMAT); +} + + + + + + + +// ********************************************************************** + + +BOOL +Old_IsLastBackupDisk ( + void + ) +/*++ + +Routine Description: + + Determines if the backup disk is the last one. + +Arguments: + + None + +Return Value: + + TRUE if disk is the last one, FALSE otherwise + +--*/ +{ + + return (OldDiskInfo.Flag == LAST_SEQUENCE); + +} + + + + +// ********************************************************************** + +PFILE_INFO +Old_GetNextFile ( + void + ) +/*++ + +Routine Description: + + Gets next file which meets the restore criteria. + +Arguments: + + None + +Return Value: + + Pointer to File info + +--*/ +{ + + + PFILE_INFO FileInfo; + WIN32_FIND_DATA FindBuffer; + BOOL FoundIt; + + + + FindPath[0] = *SourceSpec; + + FileInfo = Malloc(sizeof(FILE_INFO)); + + while ( TRUE ) { + + if (FindHandle == INVALID_HANDLE_VALUE) { + // + // Get the first file + // + FindHandle = FindFirstFile( FindPath, + &FindBuffer ); + + if (FindHandle == INVALID_HANDLE_VALUE) { + // + // Nothing to restore! + // + return NULL; + } + } else { + // + // Get the next file + // + FoundIt = FindNextFile( FindHandle, + &FindBuffer ); + + if (!FoundIt) { + // + // End of disk + // + Free(FileInfo); + FindClose(FindHandle); + FindHandle = INVALID_HANDLE_VALUE; + return NULL; + } + } + + // + // We found something, see if it matches the restore criteria + // + if (GotOneFile(FileInfo, &FindBuffer)) { + return FileInfo; + } + } + + Free(FileInfo); + return NULL; +} + + + + +// ********************************************************************** + +static +BOOL +GotOneFile ( + PFILE_INFO FileInfo, + LPWIN32_FIND_DATA FindBuffer + ) + +/*++ + +Routine Description: + + Fills in a FileInfo if the file meets the match criteria + +Arguments: + + OUT FileInfo - Supplies pointer to file information + structure to be filled-in. + IN FindBuffer - Supplies a pointer to find buffer. + +Return Value: + + TRUE if a suitable file was found + FALSE otherwise + +--*/ +{ + + HANDLE FileHandle; + OLD_FILEHEADER OldFileHeader; + FILETIME LocalFileTime; // file's last write time in local time + DWORD NumRead; + PCHAR Name; + + if (!FileInfo) { + return FALSE; + } + + // + // Get name of file to open + // + strcpy(FileInfo->SourcePath, "?:\\"); + FileInfo->SourcePath[0] = *SourceSpec; + strcat(FileInfo->SourcePath, FindBuffer->cFileName); + + // + // Open the file + // + FileHandle = CreateFile( FileInfo->SourcePath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + 0, + NULL ); + + if (FileHandle == INVALID_HANDLE_VALUE) { + return FALSE; + } + + + // + // We now try to read the file header + // + ReadFile( FileHandle, &OldFileHeader, sizeof(OLD_FILEHEADER), &NumRead, NULL); + + + if (NumRead != sizeof(OLD_FILEHEADER)) { + CloseHandle(FileHandle); + return FALSE; + } + + // + // Initialize FileInfo structure + // + // + // Locate file component of name + // +#ifdef DBCS + Name = (PCHAR)&(OldFileHeader.Path[OldFileHeader.PathLen]); + Name = PrevChar( (PCHAR)&(OldFileHeader.Path[ 0 ]), Name ); + while (*Name != '\\' && Name >= (PCHAR)&(OldFileHeader.Path)) { + Name = PrevChar( (PCHAR)&(OldFileHeader.Path[0]), Name ); + } +#else + Name = (PCHAR)&(OldFileHeader.Path[OldFileHeader.PathLen-1]); + while (*Name != '\\' && Name >= (PCHAR)&(OldFileHeader.Path)) { + Name--; + } +#endif + + //if (Name != (PCHAR)&(OldFileHeader.Path)) { + // *Name = '\0'; + //} +#ifdef DBCS + Name = NextChar( Name ); +#else + Name++; +#endif + strcpy(FileInfo->FileName, Name); + *Name = '\0'; + + strcpy(FileInfo->Path, OldFileHeader.Path); + + FileTimeToLocalFileTime( &(FindBuffer->ftLastWriteTime), + &LocalFileTime ); + + FileTimeToDosDateTime( &LocalFileTime, + (LPWORD)&(FileInfo->Date), + (LPWORD)&(FileInfo->Time) ); + + FileInfo->PartSize = FindBuffer->nFileSizeLow; + MakeFullPath(FileInfo->TargetPath, DestinationDrive, FileInfo->Path, FileInfo->FileName); + + // + // If this FileInfo matches restore criteria, we got our guy. + // + if (FileMatch(FileInfo)) { + FileInfo->Sequence = OldFileHeader.SequenceNumber; + FileInfo->Flag = FILEINFO_OK; + if (OldFileHeader.Flag & LAST_SEQUENCE) { + FileInfo->Flag |= FILEINFO_LAST; + } + + return TRUE; + } +} + + + + + + +// ********************************************************************** + +BOOL +Old_RestoreFile ( + PFILE_INFO FileInfo + ) +/*++ + +Routine Description: + + Restores one file. + +Arguments: + + IN FileInfo - Supplies pointer to information structure + +Return Value: + + TRUE if file restored, FALSE otherwise + +--*/ +{ + + DWORD CreationDisposition; // How to open the target file + HANDLE HandleSrc; // Handle to BACKUP.XXX + HANDLE HandleDst; // Handle to target file + FILETIME UtcFileTime; // File time to set (UTC) + FILETIME LocalFileTime; // File time to set (local) + BOOL RestoreStatus = TRUE; // Status of restore + + // + // Open (or create) the target file + // + if (FileInfo->Sequence == 1 ) { + // + // First chunk of the file. Create a new target file (overwritting + // the existing one. + // + CreationDisposition = CREATE_ALWAYS; + } else { + // + // Not the first chunk of the file. Apend to the existing file + // + CreationDisposition = OPEN_EXISTING; + } + + HandleDst = CreateFile( FileInfo->TargetPath, + GENERIC_WRITE, + 0, + NULL, + CreationDisposition, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + + SetFilePointer( HandleDst, + 0, + 0, + FILE_END ); + + + + + // + // Open the Source file + // + HandleSrc = CreateFile( FileInfo->SourcePath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL ); + + SetFilePointer( HandleSrc, + sizeof(OLD_FILEHEADER), + 0, + FILE_BEGIN ); + + + // + // Everything set. Now copy the file + // + if (!(RestoreStatus = CopyData(HandleSrc, HandleDst, FileInfo->PartSize - sizeof(OLD_FILEHEADER)))) { + CloseHandle(HandleSrc); + CloseHandle(HandleDst); + return RestoreStatus; + } + + // + // If this is the last chunk of the file, the we set the + // attributes, time, etc. Note that the date and time stored + // in the FILE_INFO structure is local time, so it has to + // be converted to UTC before we can feed it to SetFileTime. + // + if (FileInfo->Flag & FILEINFO_LAST) { + + SetFileAttributes(FileInfo->TargetPath, FileInfo->Attr); + + DosDateTimeToFileTime( DATE_WORD(FileInfo->Date), + TIME_WORD(FileInfo->Time), + &LocalFileTime ); + + LocalFileTimeToFileTime( &LocalFileTime, &UtcFileTime ); + + SetFileTime( HandleDst, + &UtcFileTime, + &UtcFileTime, + &UtcFileTime ); + + } + + CloseHandle(HandleSrc); + CloseHandle(HandleDst); + + return RestoreStatus; +} + diff --git a/private/utils/restore/src/parse.c b/private/utils/restore/src/parse.c new file mode 100644 index 000000000..741317ea6 --- /dev/null +++ b/private/utils/restore/src/parse.c @@ -0,0 +1,787 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + parse.c + +Abstract: + + parse command line arguments. + +Author: + + Ramon Juan San Andres (ramonsa) 20-Dec-1990 + + +Revision History: + + +--*/ + + +#include <math.h> +#include <stdlib.h> +#include <sys\types.h> +#include <sys\stat.h> +#include "restore.h" + + + +// +// Prototypes +// +void +CheckForHelp ( + int argc, + CHAR **argv + ); + +void +ParseArguments ( + int NumArg, + PCHAR *ArgArray + ); + +DWORD +GetDate ( + PCHAR String, + PFATDATE Date + ); + +DWORD +GetTime ( + PCHAR String, + PFATTIME Time + ); + +DWORD +CheckDate ( + FATDATE Date + ); + +DWORD +CheckTime ( + FATTIME Time + ); + +void +VerifySourceAndDest ( + void + ); + +void +DisplayParseError ( + DWORD ParseError, + PCHAR pArg + ); + + + + +// ********************************************************************** + +void +ParseCommandLine ( + int argc, + CHAR **argv + ) +/*++ + +Routine Description: + + Parse command line arguments. + The way this routine handles arguments is weird but it + reflects the way the DOS restore utility handled them. + +Arguments: + + IN argc - Supplies the number of arguments in command line. + IN argv - Supplies array of pointers to arguments. + +Return Value: + + None. + +--*/ + +{ + PCHAR *ArgArray = NULL; // Array of arguments + int NumArg = 0; // Number of arguments + int i; + + // + // If user wants help, then display usage. + // + CheckForHelp(argc, argv); + + + // + // Form new parameter array because of the way in which + // the old restore parsed parameters. + // + for (i=0; i<argc; i++) { + + PCHAR pBegin = argv[i]; + PCHAR pEnd = &argv[i][1]; + + while (pBegin) { + + USHORT Size; + + // Note that none of restore's flags start with a digit, + // so a slash followed by a digit is not the beginning of + // an argument (but it might appear in a date). + // + while (*pEnd != '\0' && (*pEnd != '/' || isdigit(*(pEnd+1))) ) { + + pEnd++; + } + + Size = (USHORT)(pEnd - pBegin); + ArgArray = Realloc(ArgArray, (NumArg+1) * sizeof(PCHAR)); + ArgArray[NumArg] = Malloc(Size+1); + memcpy(ArgArray[NumArg], pBegin, Size); + ArgArray[NumArg][Size] = '\0'; + NumArg++; + if (*pEnd == '\0') { + + pBegin = NULL; + + } else { + + pBegin = pEnd; + pEnd++; + } + } + } + + if (NumArg == 1) { + DisplayParseError( REST_ERROR_NO_SOURCE, NULL ); + } else { + // + // Parse arguments and initialize everything. + // + ParseArguments(NumArg, ArgArray); + + // + // Free memory + // + for (i = 0; i<NumArg; i++ ) { + Free(ArgArray[i]); + } + Free(ArgArray); + + // + // Verify the source and destination specifications and + // initialize our path structures. + // + VerifySourceAndDest(); + + } +} + + + + +// ********************************************************************** + +void +CheckForHelp ( + int argc, + CHAR **argv + ) +/*++ + +Routine Description: + + Check for help flag and display Usage if flag is present. + +Arguments: + + IN argc - Supplies the number of arguments in command line. + IN argv - Supplies array of pointers to arguments. + +Return Value: + + None. + +--*/ + +{ + for (argc--, argv++; argc; argc--, argv++) { + if ((argv[0][0] == '/') + && argv[0][1] == '?' && argv[0][2] == '\0') { + Usage(); + } + } +} + + + + +// ********************************************************************** + +void +ParseArguments ( + int NumArg, + PCHAR *ArgArray + ) +/*++ + +Routine Description: + + Parse command line arguments. + +Arguments: + + IN NumArg - Supplies the number of arguments in command line. + IN ArgArray - Supplies array of pointers to arguments. + +Return Value: + + None. + +--*/ + +{ + + int i; + DWORD ParseError = 0; + + for (i=1; i<NumArg && ParseError == 0; i++) { + + PCHAR pArg = ArgArray[i]; + + if (*pArg == '/') { + + // + // A switch, verify and set. + // + if (i < 2) { + // + // First argument must be drive + // + ParseError = REST_ERROR_INVALID_DRIVE; + } else { + + switch (*(pArg+1)) { + + case 's': + case 'S': + Flag_s = TRUE; + break; + + case 'p': + case 'P': + Flag_p = TRUE; + break; + + case 'b': + case 'B': + Flag_b = TRUE; + ParseError = GetDate((pArg+2), &BeforeDate); + break; + + case 'a': + case 'A': + Flag_a = TRUE; + ParseError = GetDate((pArg+2), &AfterDate); + break; + + case 'z': + case 'Z': + Flag_z = TRUE; + ParseError = GetDate((pArg+2), &ExactDate); + break; + + case 'e': + case 'E': + Flag_e = TRUE; + ParseError = GetTime((pArg+2), &BeforeTime); + break; + + case 'l': + case 'L': + Flag_L = TRUE; + ParseError = GetTime((pArg+2), &AfterTime); + break; + + case 'y': + case 'Y': + Flag_Y = TRUE; + ParseError = GetTime((pArg+2), &ExactTime); + break; + + case 'm': + case 'M': + Flag_m = TRUE; + break; + + case 'n': + case 'N': + Flag_n = TRUE; + break; + + case 'd': + case 'D': + Flag_d = TRUE; + break; + + default: + ParseError = REST_ERROR_INVALID_SWITCH; + break; + + } + } + } else { + // + // Not a switch, Initialize the appropiate stuff + // + switch (i) { + case 1: + SourceSpec = Malloc(strlen(pArg)); + strcpy(SourceSpec, pArg); + break; + + case 2: + strcpy(DestinationSpec, pArg); + break; + + + default: + ParseError = REST_ERROR_NUMBER_OF_PARAMETERS; + break; + + } + } + } + if (ParseError) { + DisplayParseError(ParseError, ArgArray[i-1]); + } +} + + + + +// ********************************************************************** + +DWORD +GetDate ( + PCHAR String, + PFATDATE Date + ) +/*++ + +Routine Description: + + Get a date specification. + +Arguments: + + IN String - Supplies the Date in string form, first character must + be ':'. + OUT Date - Supplies pointer to DATE structure to fill. + +Return Value: + + 0 or error code. + +--*/ +{ + int NumAssigned; + DWORD Month; + DWORD Day; + DWORD Year; + + // + // BUGBUG + // + // What about day-month-year and other variations? + // + NumAssigned = sscanf(String, ":%d-%d-%d", &Month, &Day, &Year); + + if (NumAssigned != 3) { + + NumAssigned = sscanf(String, ":%d/%d/%d", &Month, &Day, &Year); + } + + if(NumAssigned != 3) { + + return REST_ERROR_INVALID_DATE; + } + + if( Year < 80 ) { + + return REST_ERROR_INVALID_DATE; + } + + if( Year < 100 ) { + + Year += 1900; + } + + Date->Year = (unsigned)Year-1980; + Date->Month = (unsigned)Month; + Date->Day = (unsigned)Day; + + return CheckDate(*Date); + +} + + + + +// ********************************************************************** + +DWORD +GetTime ( + PCHAR String, + PFATTIME Time + ) +/*++ + +Routine Description: + + Get a time specification. + +Arguments: + + IN String - Supplies the Date in string form, first character must + be ':'. + OUT Time - Supplies pointer to RT_TIME structure to fill. + +Return Value: + + 0 or error code + +--*/ +{ + + int NumAssigned; + DWORD Hours = 0; + DWORD Minutes = 0; + DWORD Seconds = 0; + + NumAssigned = sscanf(String, ":%d:%d:%d", &Hours, &Minutes, &Seconds); + + if (NumAssigned == 0) { + return REST_ERROR_INVALID_TIME; + } + + Time->Hours = (unsigned)Hours; + Time->Minutes = (unsigned)Minutes; + Time->DoubleSeconds = (unsigned)(Seconds/2); + + return CheckTime(*Time); + + +} + + + + +// ********************************************************************** + +DWORD +CheckDate ( + FATDATE Date + ) +/*++ + +Routine Description: + + Checks consistency of a date + +Arguments: + + IN Date - Supplies the date to be checked + +Return Value: + + 0 or error code + +--*/ +{ + + if (Date.Year > (2099-1980) || Date.Year < 0) { + return REST_ERROR_INVALID_DATE; + } + + if (Date.Month > 12 || Date.Month < 1) { + return REST_ERROR_INVALID_DATE; + } + + if (Date.Day > 31 || Date.Month < 1) { + return REST_ERROR_INVALID_DATE; + } + + + // + // Verify day not greater then 30 if Apr,Jun,Sep,Nov + // + if ((Date.Day>30) && + (Date.Month==4 || Date.Month==6 || Date.Month==9 || Date.Month==11)) { + return REST_ERROR_INVALID_DATE; + } + + if (Date.Month == 2) { + // + // Deal with February + // + if (Date.Day > 29) { + return REST_ERROR_INVALID_DATE; + } + + if ((Date.Year % 4) != 0) { + if (Date.Day > 28) { + return REST_ERROR_INVALID_DATE; + } + } + } + + return 0; +} + + + + +// ********************************************************************** + +DWORD +CheckTime ( + FATTIME Time + ) +/*++ + +Routine Description: + + Checks consistency of a time + +Arguments: + + IN Time - Supplies time to be checked + +Return Value: + + 0 or error code + +--*/ +{ + + if (Time.Hours > 23 || Time.Hours < 0) { + return REST_ERROR_INVALID_TIME; + + } + + if (Time.Minutes >= 60 || Time.Minutes < 0) { + return REST_ERROR_INVALID_TIME; + } + + + if (Time.DoubleSeconds >= 30 || Time.DoubleSeconds < 0) { + return REST_ERROR_INVALID_TIME; + } + + return 0; +} + + + + + +// ********************************************************************** + +void +VerifySourceAndDest ( + void + ) +/*++ + +Routine Description: + + Verifies and/or initializes source and destination specifications. + +Arguments: + + None + +Return Value: + + None + +--*/ +{ + + CHAR SrcD; + CHAR DstD; + CHAR c; + PCHAR p, q; + CHAR DriveRoot[] = "?:\\"; + CHAR ThisDirectory[MAX_PATH]; + LPSTR Last; + struct _stat StatBuffer; + + + // + // Get out current directory + // + GetCurrentDirectory(MAX_PATH, ThisDirectory); + + // + // Must specify source drive + // + if (!SourceSpec) { + DisplayParseError(REST_ERROR_NO_SOURCE, NULL); + } + // + // If DestinationSpec not present, use current directory + // + if (!DestinationSpec) { + strcpy(DestinationSpec, ThisDirectory); + } + + // + // If Destination has no drive specification, add current drive + // + //if ( ! ( *DestinationSpec == '\\' && *(DestinationSpec+1) == '\\' )) { + // + // if (*(DestinationSpec+1) != ':') { + // + // PCHAR pTmp = Malloc(MAX_PATH); + // PCHAR p; + // + // p = pTmp; + // *p++ = ThisDirectory[0]; + // *p++ = ThisDirectory[1]; + // + // strcpy(p,DestinationSpec); + // strcpy(DestinationSpec,pTmp); + // } + //} else { + // DisplayParseError( REST_ERROR_INVALID_DRIVE, DestinationSpec ); + //} + // + if ( !GetFullPathName( DestinationSpec, MAX_PATH, ThisDirectory, &Last )) { + DisplayParseError( REST_ERROR_INVALID_DRIVE, ThisDirectory ); + } + strcpy( DestinationSpec, ThisDirectory ); + + *SourceSpec = SrcD = (CHAR)toupper(*SourceSpec); + + DstD = (CHAR)toupper(*DestinationSpec); + + // + // Verify source + // + if (SrcD < 'A' || SrcD > 'Z' || *(SourceSpec+1) != ':') { + DisplayParseError(REST_ERROR_INVALID_DRIVE, SourceSpec); + } + + // + // Get type of source and target drives + // + DriveRoot[0] = SrcD; + SourceDriveType = GetDriveType(DriveRoot); + if ((SourceDriveType == 0) || (SourceDriveType == 1)) { + DisplayParseError(REST_ERROR_INVALID_DRIVE, DriveRoot); + } + + DriveRoot[0] = DstD; + TargetDriveType = GetDriveType(DriveRoot); + if ((TargetDriveType == 0) || (TargetDriveType == 1)) { + DisplayParseError(REST_ERROR_INVALID_DRIVE, DriveRoot); + } + + // + // Source must be != destination + // + if (SrcD == DstD) { + DisplayParseError(REST_ERROR_SAME_DRIVES, SourceSpec); + } + + // + // Now split the destination specification in the Drive, Dir and + // File components. e.g. + // + // D:\foo\bar.* => "D:", "\foo\", "bar.*" + // + if ( !_stat( DestinationSpec, &StatBuffer ) ) { + if (StatBuffer.st_mode & S_IFDIR ) { +#ifdef DBCS + (VOID)AppendBackSlashIfNeeded( DestinationSpec, strlen(DestinationSpec) ); +#else + if (DestinationSpec[ strlen(DestinationSpec)-1] != '\\' ) { + strcat( DestinationSpec, "\\" ); + } +#endif + strcat( DestinationSpec, "*.*" ); + } + } + + DestinationDrive[0] = DstD; + DestinationDrive[1] = ':'; + DestinationDrive[2] = '\0'; + + p = DestinationSpec + 2; +#ifdef DBCS + q = DestinationSpec + strlen(DestinationSpec); + q = PrevChar( DestinationSpec, q ); + while (p != q && *q != '\\') { + q = PrevChar( DestinationSpec, q ); + } +#else + q = DestinationSpec + strlen(DestinationSpec) - 1; + while (p != q && *q != '\\') { + q--; + } +#endif + + if ( (p == q) && ( *(p+1) == '\0' ) ) { + strcpy( DestinationDir, p ); + strcpy( DestinationFile, "*.*" ); + } else { + q++; // Off-by-one + + c = *q; + *q = '\0'; + strcpy(DestinationDir, p ); + *q = c; + strcpy( DestinationFile, q ); + } + + MakeFullPath(DestinationSpec, DestinationDrive, DestinationDir, DestinationFile); +} + + + + +// ********************************************************************** + +void +DisplayParseError ( + DWORD ParseError, + PCHAR pArg + ) +/*++ + +Routine Description: + + Displays error mesage from parser. + +Arguments: + + IN ParseError - Supplies error code + IN pArg - Supplies pointer to argument that caused the error. + +Return Value: + + None + +--*/ +{ + DisplayMsg(STD_ERR, ParseError, pArg); + + ExitStatus(EXIT_USER); +} diff --git a/private/utils/restore/src/restore.c b/private/utils/restore/src/restore.c new file mode 100644 index 000000000..f024464e9 --- /dev/null +++ b/private/utils/restore/src/restore.c @@ -0,0 +1,481 @@ +/*++ + +Copyright (c) 1990 Microsoft Corporation + +Module Name: + + restore.c + +Abstract: + + Restore utility. Restores files backed up with the DOS and OS/2 backup + utilities. + +Author: + + Ramon Juan San Andres (ramonsa) Feb-20-1990 + + +Revision History: + + +--*/ + +#include "restore.h" + +#include <fcntl.h> +#include <io.h> + + + +// ********************************************************************** + +// +// The global data is to be initialized here +// +// +// Switches set in the command line +// +BOOL Flag_s = FALSE; // Restore subdirs +BOOL Flag_p = FALSE; // Prompting +BOOL Flag_b = FALSE; // Before date +BOOL Flag_a = FALSE; // After date +BOOL Flag_z = FALSE; // Exact date +BOOL Flag_e = FALSE; // Before time +BOOL Flag_L = FALSE; // After time +BOOL Flag_Y = FALSE; // Exact time +BOOL Flag_m = FALSE; // Restore only modified files +BOOL Flag_n = FALSE; // Restore only nonexistent files +BOOL Flag_d = FALSE; // Display files on the backup disk that + // match specifications +// +// WriteNewLine is true if we must write a new line after restoring a file +// +BOOL WriteNewLine = FALSE; + +// +// Dates and times set in the command line +// +FATDATE BeforeDate = {0, 0, 0}; +FATDATE AfterDate = {0, 0, 0}; +FATDATE ExactDate = {0, 0, 0}; + +FATTIME BeforeTime = {0, 0, 0}; +FATTIME AfterTime = {0, 0, 0}; +FATTIME ExactTime = {0, 0, 0}; + + +// +// The date of the backup +// +FATDATE BackupDate = {0, 0, 0}; +FATTIME BackupTime = {0, 0, 0}; + + +// +// The source and destination specifications +// +PCHAR SourceSpec = NULL; // Source drive specification +CHAR DestinationSpec[MAX_PATH] = {'\0'}; // Full Destination spec +CHAR DestinationDrive[3] = {'\0'}; // Destination drive +CHAR DestinationDir[MAX_PATH] = {'\0'}; // Destination directory +CHAR DestinationFile[MAX_PATH] = {'\0'}; // Destination file + + +// +// Abort signals an abnormal termination condition +// +BOOL Abort = FALSE; + + +// +// Type of the source and target drives +// +DWORD SourceDriveType = 0; +DWORD TargetDriveType = 0; + + + +// ********************************************************************** + +// +// The local data +// +WORD DiskSequence = 1; // Current disk sequence +DWORD FilesFound = 0; // Number of files restored + + + + + +// ********************************************************************** + +// +// The local prototypes +// +void +RestoreAllFiles ( + ); + +BOOL +RestoreOneFile ( + ); + + + + + + +// ********************************************************************** + +void _CRTAPI1 +main ( + int argc, + CHAR **argv + ) +/*++ + +Routine Description: + + restore utility main function. + +Arguments: + + IN argc - Supplies the number of arguments in command line. + IN argv - Supplies array of pointers to arguments. + +Return Value: + + None. + +--*/ + +{ + + DWORD DiskType; // Type of backup disk + +#if defined( JAPAN ) // v-junm - 06/03/93 +// This is a hack for FormatMessage. FormatMessage checks several variables +// to determine which language message to display, and one is the TEB's +// LanguageId. Since the LanguageId in the TEB is always initialized to +// Japan's language id when a thread is created, we will change the value +// to US's language id when we are not running in JP CP. + + if ( GetConsoleOutputCP() == 932 ) + SetThreadLocale( + MAKELCID( + MAKELANGID( LANG_JAPANESE, SUBLANG_ENGLISH_US ), + SORT_DEFAULT + ) + ); + else + SetThreadLocale( + MAKELCID( + MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ), + SORT_DEFAULT + ) + ); + + +#endif // JAPAN + + // + // Don't expand line-feed to carriage-return and line-feed. + // + + _setmode(_fileno(stdout),O_BINARY); + _setmode(_fileno(stderr),O_BINARY); + + // + // Get command-line options and set the global state. + // + ParseCommandLine( argc, argv ); + + // + // Determine the format of the backup disk. + // + DiskType = GetABackupDisk( &DiskSequence ); + + // + // If this is not a backup disk, get out. + // + if (!(DiskType & DISK_OK)) { + DisplayMsg( STD_ERR, REST_ERROR_NOT_BACKUP ); + ExitStatus( EXIT_ERROR ); + } + + // + // Restore the files + // + RestoreAllFiles(); + + // + // Exit + // + if (Abort) { + ExitStatus(EXIT_ERROR); + } else if (FilesFound == 0) { + fprintf( STD_ERR , "\r\n\r\n"); + DisplayMsg(STD_ERR, REST_WARNING_NOFILES); + ExitStatus(EXIT_NOFILES); + } else { + fprintf( STD_OUT, "\r\n"); + ExitStatus(EXIT_NORMAL); + } +} + + + + + +// ********************************************************************** + +void +RestoreAllFiles ( + void + ) +/*++ + +Routine Description: + + Restore backed-up files. + +Arguments: + + None + +Return Value: + + None. + +--*/ + +{ + + BOOL AllDisksRestored = FALSE; // Termination flag + CHAR StringDate[STRING_DATE_LENGTH]; + CHAR StringBuffer[16]; + + // + // Note that at this point the first backup disk has already + // been verified. + // + + // + // Ask for target, if removable + // + if (TargetDriveType == DRIVE_REMOVABLE) { + DisplayMsg(STD_OUT, REST_MSG_INSERT_TARGET, DestinationDrive); + DisplayMsg(STD_OUT, REST_MSG_PRESS_ANY_KEY); + GetKey(NULL, STD_OUT, STD_OUT); + } + + // + // Display the backup date + // + DisplayMsg(STD_OUT, REST_MSG_FILES_WERE_BACKEDUP, MakeStringDate(StringDate, BackupDate)); + putc( '\r', STD_OUT ); + putc( '\n', STD_OUT ); + if ( Flag_d ) { + DisplayMsg(STD_OUT, REST_MSG_LISTING, SourceSpec); + } else { + DisplayMsg(STD_OUT, REST_MSG_RESTORING, SourceSpec); + } + + // + // Now do the restore, one disk at a time + // + while (!AllDisksRestored) { + + if (SourceDriveType == DRIVE_REMOVABLE) { + MakeStringNumber(StringBuffer, DiskSequence, 2); + DisplayMsg(STD_OUT, REST_MSG_DISKETTE_NUMBER, StringBuffer); + } + + // + // Restore all the restorable files on this disk. + // + while ( RestoreOneFile( ) ); + + if (IsLastBackupDisk()) { + + // + // This is the last backup disk, our job is done + // + AllDisksRestored = TRUE; + + } else { + + // + // There are more disks, get and verify the next one + // + DiskSequence++; + + if (!(GetABackupDisk(&DiskSequence) & DISK_OK)) { + DisplayMsg(STD_ERR, REST_ERROR_NOT_BACKUP); + AllDisksRestored = TRUE; + AbortProgram(); + } + } + } +} + + + + + + + +// ********************************************************************** + + +BOOL +RestoreOneFile ( + ) +/*++ + +Routine Description: + + Restores one file. + +Arguments: + + None + +Return Value: + + TRUE if one file restored, FALSE otherwise + +--*/ + +{ + PFILE_INFO FileInfo; // Pointer to information structure + DWORD Status; +static FILE_INFO LastFileInfo; // FileInfo of last chunk +static DWORD TmpSequence = 0; // Tmp sequence +static BOOL HaveLastChunk = FALSE; // have last part of file + + + while (TRUE) { + + // + // Try to get the the information about a file to restore + // + FileInfo = GetNextFile(); + + if (FileInfo) { + + // + // There is a file to restore. Determine if this is the first + // chunk of the file. + // + if (FileInfo->Sequence == 1) { + + // + // This is the first chunk of a file. Assign it a temporary + // name. + // + MakeTmpPath(FileInfo->TmpName, DestinationDrive, FileInfo->Path, TmpSequence++); + + // + // If this is not the last chunk of a file, then we save + // the FileInfo so that we can verify the next chunk. + // + if (!(FileInfo->Flag & FILEINFO_LAST)) { + + // + // This is a multi-chunk file. We save + // the FileInfo. + // + memcpy(&LastFileInfo, FileInfo, sizeof(FILE_INFO)); + HaveLastChunk = TRUE; + + } else { + + // + // Not a multi-chunk file + // + HaveLastChunk = FALSE; + } + + } else { + + // + // This is part of a multi-chunk file. We have to make + // sure that the last chunk that we copied was the + // previous chunk of this file. + // + if (!HaveLastChunk) { + + // + // We don't have a previous chunk, so we will + // just skip this file + // + Free( FileInfo ); + continue; + } + + if (!((strcmp(LastFileInfo.Path, FileInfo->Path) == 0) && + (strcmp(LastFileInfo.FileName, FileInfo->FileName) == 0) && + (LastFileInfo.Sequence == FileInfo->Sequence -1))) { + // + // ERROR!, we have to recover the file and abort + // + DisplayMsg(STD_ERR,REST_ERROR_LAST_NOTRESTORED, FileInfo->TargetPath); + RecoverFile(&LastFileInfo); + AbortProgram(); + return FALSE; + + } else { + + // + // Correct chunk. copy the FileInfo. Note that we only + // have to update the sequence number. + // + strcpy( FileInfo->TmpName, LastFileInfo.TmpName ); + LastFileInfo.Sequence = FileInfo->Sequence; + } + } + + // + // Restore it. + // + FilesFound++; + + printf("%s%s ",FileInfo->Path, FileInfo->FileName); + + if ( !Flag_d ) { + if ( !RestoreFile( FileInfo ) ) { + + DisplayMsg(STD_ERR,REST_ERROR_LAST_NOTRESTORED, FileInfo->TargetPath); + RecoverFile(&LastFileInfo); + AbortProgram(); + return FALSE; + } else if (FileInfo->TargetExists && (FileInfo->Flag & FILEINFO_LAST) ) { + // + // File restored, delete temporary file + // + DeleteFile( FileInfo->TmpName ); + } + } + + if ( Flag_p && WriteNewLine ) { + putc( '\r', STD_ERR ); + putc( '\n', STD_ERR ); + } + putc( '\r', STD_OUT ); + putc( '\n', STD_OUT ); + + Free( FileInfo ); + + return TRUE; + + } else { + + // + // No more files, return + // + return FALSE; + } + } +} diff --git a/private/utils/restore/src/restore.rc b/private/utils/restore/src/restore.rc new file mode 100644 index 000000000..1e8d24567 --- /dev/null +++ b/private/utils/restore/src/restore.rc @@ -0,0 +1,12 @@ +#include <windows.h> +#include <ntverp.h> + +#define VER_FILETYPE VFT_APP +#define VER_FILESUBTYPE VFT2_UNKNOWN +#define VER_FILEDESCRIPTION_STR "File Restore Utility" +#define VER_INTERNALNAME_STR "restore\0" +#define VER_ORIGINALFILENAME_STR "RESTORE.EXE" + +#include "common.ver" + +#include "rtmsg.rc" diff --git a/private/utils/restore/src/rtmsg.mc b/private/utils/restore/src/rtmsg.mc new file mode 100644 index 000000000..4b8eb1cf0 --- /dev/null +++ b/private/utils/restore/src/rtmsg.mc @@ -0,0 +1,191 @@ +;/*++ BUILD Version: 0001 // Increment this if a change has global effects +; +;Copyright (c) 1990 Microsoft Corporation +; +;Module Name: +; +; rtmsg.h +; +;Abstract: +; +; This file contains the message definitions for the Win32 restore +; utility. +; +;Author: +; +; Ramon Juan San Andres (ramonsa) 20-Feb-1990 +; +;Revision History: +; +;--*/ +; + +MessageId=0002 SymbolicName=REST_ERROR_SAME_DRIVES +Language=English +Source and target drives are the same +. + +MessageId=0003 SymbolicName=REST_ERROR_NUMBER_OF_PARAMETERS +Language=English +Invalid number of parameters +. + +MessageId=0005 SymbolicName=REST_ERROR_INVALID_PATH +Language=English +Invalid path +. + +MessageId=0006 SymbolicName=REST_ERROR_INVALID_DRIVE +Language=English +Invalid drive specification +. + +MessageId=0007 SymbolicName=REST_WARNING_NOFILES +Language=English +WARNING! No files were found to restore +. + +MessageId=0008 SymbolicName=REST_MSG_INSERT_SOURCE +Language=English +Insert backup diskette %1 in drive %2 +. + +MessageId=0009 SymbolicName=REST_MSG_INSERT_TARGET +Language=English +Insert restore target in drive %1 +. + +MessageId=0010 SymbolicName=REST_MSG_PRESS_ANY_KEY +Language=English +Press any key to continue . . . %0 +. + +MessageId=0011 SymbolicName=REST_WARNING_DISK_OUTOFSEQUENCE +Language=English +WARNING! Diskette is out of sequence +Replace diskette or continue if OK %0 +. + +MessageId=0012 SymbolicName=REST_ERROR_LAST_NOTRESTORED +Language=English +The last file was not restored +. + +MessageId=0013 SymbolicName=REST_MSG_FILES_WERE_BACKEDUP +Language=English +*** Files were backed up %1 *** +. + +MessageId=0014 SymbolicName=REST_ERROR_NOT_BACKUP +Language=English +Source does not contain backup files +. + +MessageId=0015 SymbolicName=REST_ERROR_NO_MEMORY +Language=English +Insufficient memory +. + +MessageId=0016 SymbolicName=REST_WARNING_READONLY +Language=English +WARNING! File %1 +Is a read only file +Replace the file (Y/N)? %0 +. + +MessageId=0017 SymbolicName=REST_ERROR_FILE_SEQUENCE +Language=English +Restore file sequence error +. + +MessageId=0018 SymbolicName=REST_ERROR_FILE_CREATION +Language=English +File creation error +. + +MessageId=0019 SymbolicName=REST_ERROR_DISK_SPACE +Language=English +Insufficient disk space +. + +MessageId=0020 SymbolicName=REST_ERROR_NOT_RESTORED +Language=English +*** Not able to restore file *** +. + +MessageId=0021 SymbolicName=REST_MSG_RESTORING +Language=English +*** Restoring files from drive %1 *** +. + +MessageId=0022 SymbolicName=REST_WARNING_FILE_CHANGED +Language=English +Warning! File %1 +was changed after it was backed up +Replace the file (Y/N)? %0 +. + +MessageId=0023 SymbolicName=REST_MSG_DISKETTE_NUMBER +Language=English +Diskette: %1 +. + +MessageId=0027 SymbolicName=REST_ERROR_INVALID_DATE +Language=English +Invalid date +. + +MessageId=0028 SymbolicName=REST_ERROR_INVALID_TIME +Language=English +Invalid time +. + +MessageId=0029 SymbolicName=REST_ERROR_NO_SOURCE +Language=English +No source drive specified +. + +MessageId=0030 SymbolicName=REST_ERROR_NO_TARGET +Language=English +No target drive specified +. + +MessageId=0031 SymbolicName=REST_ERROR_INVALID_SWITCH +Language=English +Invalid Switch - %1 +. + +MessageId=0032 SymbolicName=REST_ERROR_READING_BACKUP +Language=English +Error reading backup file. +. + +MessageId=0040 SymbolicName=REST_MSG_LISTING +Language=English +*** Listing files on drive %1 *** +. + + +MessageId=0050 SymbolicName=REST_MSG_USAGE +Language=English +Restores files that were backed up by using the DOS BACKUP command. + +RESTORE drive1: drive2:[path[filename]] [/S] [/P] [/B:date] [/A:date] [/E:time] + [/L:time] [/M] [/N] [/D] + + drive1: Specifies the drive on which the backup files are stored. + drive2:[path[filename]] + Specifies the file(s) to restore. + /S Restores files in all subdirectories in the path. + /P Prompts before restoring read-only files or files changed since + the last backup (if appropriate attributes are set). + /B Restores only files last changed on or before the specified date. + /A Restores only files changed on or after the specified date. + /E Restores only files last changed at or earlier than the specified + time. + /L Restores only files changed at or later than the specified time. + /M Restores only files changed since the last backup. + /N Restores only files that no longer exist on the destination disk. + /D Displays files on the backup disk that match specifications. + +. diff --git a/private/utils/restore/src/sources b/private/utils/restore/src/sources new file mode 100644 index 000000000..3af46c8b4 --- /dev/null +++ b/private/utils/restore/src/sources @@ -0,0 +1,36 @@ +BLDCRT=1 + +MAJORCOMP=sdktools +MINORCOMP=restore + +TARGETNAME=restore +TARGETPATH=obj +TARGETTYPE=LIBRARY + + + +INCLUDES=..\inc + + + +SOURCES= restore.rc \ + exit.c \ + filecopy.c \ + generic.c \ + match.c \ + misc.c \ + msg.c \ + new.c \ + old.c \ + parse.c \ + mbcs.c + + +C_DEFINES= -DNO_EXT_KEYS -DNT + +UMTYPE=console +UMAPPL=restore +UMLIBS=obj\*\restore.lib +UMRES=obj\*\restore.res \nt\public\sdk\lib\*\binmode.obj + +NTTARGETFILE0=..\inc\rtmsg.h |