summaryrefslogtreecommitdiffstats
path: root/private/utils/restore/src
diff options
context:
space:
mode:
Diffstat (limited to 'private/utils/restore/src')
-rw-r--r--private/utils/restore/src/exit.c89
-rw-r--r--private/utils/restore/src/filecopy.c736
-rw-r--r--private/utils/restore/src/generic.c536
-rw-r--r--private/utils/restore/src/makefile6
-rw-r--r--private/utils/restore/src/makefile.inc4
-rw-r--r--private/utils/restore/src/match.c841
-rw-r--r--private/utils/restore/src/mbcs.c94
-rw-r--r--private/utils/restore/src/misc.c503
-rw-r--r--private/utils/restore/src/msg.c214
-rw-r--r--private/utils/restore/src/new.c751
-rw-r--r--private/utils/restore/src/old.c527
-rw-r--r--private/utils/restore/src/parse.c787
-rw-r--r--private/utils/restore/src/restore.c481
-rw-r--r--private/utils/restore/src/restore.rc12
-rw-r--r--private/utils/restore/src/rtmsg.mc191
-rw-r--r--private/utils/restore/src/sources36
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