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

1850 lines
44 KiB

/*++
Copyright (c) 1992 Microsoft Corporation
Module Name:
dncopy.c
Abstract:
File copy routines for DOS-hosted NT Setup program.
Author:
Ted Miller (tedm) 1-April-1992
Revision History:
4.0 Stephane Plante (t-stepl) 11-Dec-95
Upgraded for SUR Release
--*/
#include "winnt.h"
#include <dos.h>
#include <fcntl.h>
#include <share.h>
#include <string.h>
#include <direct.h>
#include <stdio.h>
#include <ctype.h>
//
// Define size of buffer we initially try to allocate for file copies
// and the size we use if the initial allocation attempt fails.
//
#define COPY_BUFFER_SIZE1 (65536-512) // 64K - 512
#define COPY_BUFFER_SIZE2 (24*1024) // 24K
#define COPY_BUFFER_SIZE3 (8*1024) // 8K
typedef struct _DIRECTORY_NODE {
struct _DIRECTORY_NODE *Next;
PCHAR Directory; // never starts or ends with \.
PCHAR Symbol;
} DIRECTORY_NODE, *PDIRECTORY_NODE;
PDIRECTORY_NODE DirectoryList;
PSCREEN CopyingScreen;
BOOLEAN UsingGauge = FALSE;
//
// Total number of files to be copied
//
unsigned TotalFileCount;
//
// Buffer used for file copying and verifying,
// and the size of the buffer.
//
PVOID CopyBuffer;
unsigned CopyBufferSize;
VOID
DnpCreateDirectoryList(
IN PCHAR SectionName,
OUT PDIRECTORY_NODE *ListHead
);
VOID
DnpCreateDirectories(
IN PCHAR TargetRootDir
);
VOID
DnpCreateOneDirectory(
IN PCHAR Directory
);
BOOLEAN
DnpOpenSourceFile(
IN PCHAR Filename,
OUT int *Handle,
OUT unsigned *Attribs
);
ULONG
DnpIterateCopyList(
IN BOOLEAN ValidationPass,
IN PCHAR SectionName,
IN PCHAR DestinationRoot,
IN BOOLEAN UseDestRoot,
IN BOOLEAN Verify,
IN unsigned ClusterSize OPTIONAL
);
ULONG
DnpIterateCopyListSection(
IN BOOLEAN ValidationPass,
IN PCHAR SectionName,
IN PCHAR DestinationRoot,
IN BOOLEAN UseDestRoot,
IN BOOLEAN Verify,
IN unsigned ClusterSize OPTIONAL
);
ULONG
DnpCopyOneFile(
IN PCHAR SourceName,
IN PCHAR DestName,
IN BOOLEAN Verify,
IN BOOLEAN PreserveAttribs
);
BOOLEAN
DnpDoCopyOneFile(
IN int SrcHandle,
IN int DstHandle,
IN PCHAR Filename,
IN BOOLEAN Verify,
OUT PBOOLEAN Verified,
OUT PULONG BytesWritten
);
PCHAR
DnpLookUpDirectory(
IN PCHAR RootDirectory,
IN PDIRECTORY_NODE DirList,
IN PCHAR Symbol
);
VOID
DnpInfSyntaxError(
IN PCHAR Section
);
VOID
DnpFreeDirectoryList(
IN OUT PDIRECTORY_NODE *List
);
VOID
DnpFormatSpaceErrMsg(
IN ULONG NtSpaceReq,
IN ULONG CSpaceReq
);
ULONG
DnpIterateOptionalDirs(
IN BOOLEAN ValidationPass,
IN BOOLEAN Verify,
IN unsigned ClusterSize OPTIONAL
);
ULONG
DnpDoIterateOptionalDir(
IN BOOLEAN ValidationPass,
IN PCHAR SourceDir,
IN PCHAR DestDir,
IN BOOLEAN Verify,
IN unsigned ClusterSize OPTIONAL
);
VOID
DnCopyFiles(
VOID
)
/*++
Routine Description:
Top-level file copy entry point. Creates all directories listed in
the [Directories] section of the inf. Copies all files listed in the
[Files] section of the inf file from the source to the target (which
becomes the local source).
Arguments:
None.
Return Value:
None.
--*/
{
PCHAR LocalSourceRoot;
struct diskfree_t DiskFree;
unsigned ClusterSize;
ULONG SizeOccupied;
ULONG FileCount;
PCHAR UdfFileName = WINNT_UNIQUENESS_DB;
PCHAR UdfPath;
//
// Do not change this without changing text setup as well
// (SpPtDetermineRegionSpace()).
//
PCHAR SizeFile = "\\size.sif";
PCHAR Lines[3] = { "[Data]\n","Size = xxxxxxxxxxxxxx\n",NULL };
DnClearClientArea();
DnDisplayScreen(CopyingScreen = &DnsWaitCopying);
DnWriteStatusText(NULL);
//
// Create the linked list of directories.
//
DnpCreateDirectoryList(DnfDirectories,&DirectoryList);
//
// Generate the full root path of the local source
//
LocalSourceRoot = MALLOC(sizeof(LOCAL_SOURCE_DIRECTORY) + strlen(x86DirName) + strlen(SizeFile),TRUE);
LocalSourceRoot[0] = DngTargetDriveLetter;
LocalSourceRoot[1] = ':';
strcpy(LocalSourceRoot+2,LocalSourceDirName);
DnpCreateOneDirectory(LocalSourceRoot);
if(UniquenessDatabaseFile) {
UdfPath = MALLOC(strlen(LocalSourceRoot) + strlen(UdfFileName) + 2, TRUE);
strcpy(UdfPath,LocalSourceRoot);
strcat(UdfPath,"\\");
strcat(UdfPath,UdfFileName);
DnpCopyOneFile(UniquenessDatabaseFile,UdfPath,FALSE,TRUE);
FREE(UdfPath);
}
strcat(LocalSourceRoot,x86DirName);
//
// Determine the cluster size on the drive.
//
_dos_getdiskfree(toupper(DngTargetDriveLetter)-'A'+1,&DiskFree);
ClusterSize = DiskFree.sectors_per_cluster * DiskFree.bytes_per_sector;
//
// Pass over the copy list and check syntax.
//
DnpIterateCopyList(TRUE,DnfFiles,LocalSourceRoot,FALSE,FALSE,0);
FileCount = DnpIterateOptionalDirs(TRUE,FALSE,0);
//
// Create the target directories
//
DnpCreateDirectories(LocalSourceRoot);
//
// Pass over the copy list again and actually perform the copy.
//
UsingGauge = TRUE;
SizeOccupied = DnpIterateCopyList(FALSE,DnfFiles,LocalSourceRoot,FALSE,FALSE,ClusterSize);
SizeOccupied += DnpIterateOptionalDirs(FALSE,FALSE,ClusterSize);
//
// Free the copy buffer.
//
if(CopyBuffer) {
FREE(CopyBuffer);
CopyBuffer = NULL;
}
//
// Free the directory node list
//
DnpFreeDirectoryList(&DirectoryList);
//
// Make an approximate calculation of the amount of disk space taken up
// by the local source directory itself, assuming 32 bytes per dirent.
// Also account for the small ini file that we'll put in the local source
// directory, to tell text setup how much space the local source takes up.
// We don't account for clusters in the directory -- we base this on sector
// counts only.
//
SizeOccupied += DiskFree.bytes_per_sector
* (((FileCount + 1) / (DiskFree.bytes_per_sector / 32)) + 1);
//
// Create a small ini file listing the size occupied by the local source.
// Account for the ini file in the size.
//
strcpy(LocalSourceRoot+2,LocalSourceDirName);
strcat(LocalSourceRoot,SizeFile);
sprintf(Lines[1],"Size = %lu\n",SizeOccupied + ClusterSize);
DnWriteSmallIniFile(LocalSourceRoot,Lines,NULL);
FREE(LocalSourceRoot);
}
VOID
DnCopyFloppyFiles(
IN PCHAR SectionName,
IN PCHAR TargetRoot
)
/*++
Routine Description:
Top-level entry point to copy files to the setup floppy or hard-disk
boot root when this routine is called. Copies all files listed in the
[FloppyFiles.x] sections of the inf file from the source to TargetRoot.
Arguments:
SectionName - supplies the name of the section in the inf file
that contains the list of files to be copied.
TargetRoot - supplies the target path without trailing \.
Return Value:
None.
--*/
{
DnClearClientArea();
DnDisplayScreen(CopyingScreen = (DngFloppyless ? &DnsWaitCopying : &DnsWaitCopyFlop));
DnWriteStatusText(NULL);
//
// Create the linked list of directories.
//
DnpCreateDirectoryList(DnfDirectories,&DirectoryList);
//
// Copy the files.
//
DnpIterateCopyList(TRUE ,SectionName,TargetRoot,TRUE,FALSE,0);
DnpIterateCopyList(FALSE,SectionName,TargetRoot,TRUE,DngFloppyVerify,0);
//
// Free the copy buffer.
//
if(CopyBuffer) {
FREE(CopyBuffer);
CopyBuffer = NULL;
}
//
// Free the directory node list
//
DnpFreeDirectoryList(&DirectoryList);
}
VOID
DnpCreateDirectoryList(
IN PCHAR SectionName,
OUT PDIRECTORY_NODE *ListHead
)
/*++
Routine Description:
Examine a section in the INF file, whose lines are to be in the form
key = directory and create a linked list describing the key/directory
pairs found therein.
If the directory field is empty, it is assumed to be the root.
Arguments:
SectionName - supplies name of section
ListHead - receives pointer to head of linked list
Return Value:
None. Does not return if syntax error in the inf file section.
--*/
{
unsigned LineIndex,len;
PDIRECTORY_NODE DirNode,PreviousNode;
PCHAR Key;
PCHAR Dir;
LineIndex = 0;
PreviousNode = NULL;
while(Key = DnGetKeyName(DngInfHandle,SectionName,LineIndex)) {
Dir = DnGetSectionKeyIndex(DngInfHandle,SectionName,Key,0);
if(Dir == NULL) {
Dir = ""; // use the root if not specified
}
//
// Skip leading backslashes
//
while(*Dir == '\\') {
Dir++;
}
//
// Clip off trailing backslashes if present
//
while((len = strlen(Dir)) && (Dir[len-1] == '\\')) {
Dir[len-1] = '\0';
}
DirNode = MALLOC(sizeof(DIRECTORY_NODE),TRUE);
DirNode->Next = NULL;
DirNode->Directory = Dir;
DirNode->Symbol = Key;
if(PreviousNode) {
PreviousNode->Next = DirNode;
} else {
*ListHead = DirNode;
}
PreviousNode = DirNode;
LineIndex++;
}
}
VOID
DnpCreateDirectories(
IN PCHAR TargetRootDir
)
/*++
Routine Description:
Create the local source directory, and run down the DirectoryList and
create directories listed therein relative to the given root dir.
Arguments:
TargetRootDir - supplies the name of root directory of the target
Return Value:
None.
--*/
{
PDIRECTORY_NODE DirNode;
CHAR TargetDirTemp[128];
DnpCreateOneDirectory(TargetRootDir);
for(DirNode = DirectoryList; DirNode; DirNode = DirNode->Next) {
//
// No need to create the root
//
if(*DirNode->Directory) {
strcpy(TargetDirTemp,TargetRootDir);
strcat(TargetDirTemp,"\\");
strcat(TargetDirTemp,DirNode->Directory);
DnpCreateOneDirectory(TargetDirTemp);
}
}
}
VOID
DnpCreateOneDirectory(
IN PCHAR Directory
)
/*++
Routine Description:
Create a single directory if it does not already exist.
Arguments:
Directory - directory to create
Return Value:
None. Does not return if directory cannot be created.
--*/
{
struct find_t FindBuf;
int Status;
//
// First, see if there's a file out there that matches the name.
//
Status = _dos_findfirst( Directory,
_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_SUBDIR,
&FindBuf
);
if(Status) {
//
// file could not be matched so we should be able to create the dir.
//
if(mkdir(Directory)) {
DnFatalError(&DnsCantCreateDir,Directory);
}
} else {
//
// file matched. If it's a dir, we're OK. Otherwise we can't
// create the dir, a fatal error.
//
if(FindBuf.attrib & _A_SUBDIR) {
return;
} else {
DnFatalError(&DnsCantCreateDir,Directory);
}
}
}
ULONG
DnpIterateCopyList(
IN BOOLEAN ValidationPass,
IN PCHAR SectionName,
IN PCHAR DestinationRoot,
IN BOOLEAN UseDestRoot,
IN BOOLEAN Verify,
IN unsigned ClusterSize OPTIONAL
)
/*++
Routine Description:
Run through the NtTreeFiles and BootFiles sections, validating their
syntactic correctness and copying files if directed to do so.
Arguments:
ValidationPass - If TRUE, then do not actually copy the files.
If FALSE, copy the files as they are iterated.
SectionName - name of section coptaining the list of files
DestinationRoot- supplies the root of the destination, to which all
directories are relative.
UseDestRoot - if TRUE, ignore the directory symbol and copy each file to
the DestinationRoot directory. Otherwise append the directory
implied by the directory symbol for a file to the DestinationRoot.
Verify - if TRUE and this is not a validation pass, files will be
verified after they have been copied by rereading them from the
copy source and comparing with the local version that was just
copied.
ClusterSize - if specified, supplies the number of bytes in a cluster
on the destination. If ValidationPass is FALSE, files will be sized as
they are copied, and the return value of this function will be
the total size occupied on the target by the files that are copied
there.
Return Value:
If ValidationPass is TRUE, then the return value is the number of files
that will be copied.
If ClusterSize was specfied and ValidationPass is FALSE,
the return value is the total space occupied on the target drive
by the files that were copied. Otherwise the return value is undefined.
Does not return if a syntax error in encountered in the INF file.
--*/
{
ULONG rc;
if(ValidationPass) {
TotalFileCount = 0;
} else {
if(UsingGauge) {
DnInitGauge(TotalFileCount,CopyingScreen);
}
}
rc = DnpIterateCopyListSection(
ValidationPass,
SectionName,
DestinationRoot,
UseDestRoot,
Verify,
ClusterSize
);
return(rc);
}
ULONG
DnpIterateOptionalDirs(
IN BOOLEAN ValidationPass,
IN BOOLEAN Verify,
IN unsigned ClusterSize OPTIONAL
)
/*++
Routine Description:
Runs down all optional dir components and add them to the copy
list
Arguments:
ValidationPass - If TRUE, then do not actually copy the files.
If FALSE, copy the files as they are iterated.
Verify - if TRUE and this is not a validation pass, files will be
verified after they have been copied by rereading them from the
copy source and comparing with the local version that was just
copied.
ClusterSize - if specified, supplies the number of bytes in a cluster
on the destination. If ValidationPass is FALSE, files will be sized as
they are copied, and the return value of this function will be
the total size occupied on the target by the files that are copied
there.
Return Value:
If ValidationPass is TRUE, then the return value is the number of files
that will be copied.
If ClusterSize was specfied and ValidationPass is FALSE,
the return value is the total space occupied on the target drive
by the files that were copied. Otherwise the return value is undefined.
--*/
{
PCHAR Ptr;
PCHAR SourceDir;
PCHAR DestDir;
ULONG rc;
unsigned u;
BOOLEAN OemOptDirCreated = FALSE;
struct find_t FindData;
BOOLEAN OemSysDirExists;
for (rc=0,u=0; u < OptionalDirCount; u++ ) {
//
// For each directory build in the list build up the
// full path name to both the source and destination
// directory, then start our recursive copy engine
//
//
// Source Dir Allocation
// We want the base dir + '\'
// + oem optional dir root + '\'
// + optional dir name + '\'
// + 8.3 name + '\0'
//
SourceDir = MALLOC( strlen(DngSourceRootPath) +
strlen(OemOptionalDirectory) +
strlen(OptionalDirs[u]) + 16, TRUE );
strcpy(SourceDir,DngSourceRootPath);
strcat(SourceDir,"\\");
if (OptionalDirFlags[u] & OPTDIR_OEMOPT) {
strcat(SourceDir,OemOptionalDirectory);
strcat(SourceDir,"\\");
}
strcat(SourceDir,OptionalDirs[u]);
if (OptionalDirFlags[u] & OPTDIR_OEMSYS) {
//
// Remember whether or not $OEM$ exists on the source
//
if (_dos_findfirst(SourceDir,_A_HIDDEN|_A_SYSTEM|_A_SUBDIR, &FindData) ) {
OemSysDirExists = FALSE;
} else {
OemSysDirExists = TRUE;
}
}
strcat(SourceDir,"\\");
//
// Dest Dir Allocation
// This depends if the 'SourceOnly' flag is set with the directory
// If it is, then we want to copy it $win_nt$.~ls\i386\<dir> otherwise
// we want to stick into $win_nt$.~ls\<dir>
//
if (OptionalDirFlags[u] & OPTDIR_TEMPONLY) {
//
// Dest Dir is '<x>:' + LocalSourceDirName + x86dir + '\' +
// optional dir name + '\' + 8.3 name + '\0'
//
DestDir = MALLOC(strlen(LocalSourceDirName) +
strlen(x86DirName) +strlen(OptionalDirs[u]) + 17, TRUE);
DestDir[0] = DngTargetDriveLetter;
DestDir[1] = ':';
strcpy(DestDir+2,LocalSourceDirName);
strcat(DestDir,x86DirName);
} else if (OptionalDirFlags[u] & OPTDIR_OEMOPT) {
//
// Dest Dir is '<x>:' + LocalSourceDirName + '\' +
// $OEMOPT$ + '\' +
// optional dir name + '\' + 8.3 name + '\0'
//
DestDir = MALLOC(strlen(LocalSourceDirName) +
strlen(OemOptionalDirectory) +
strlen(OptionalDirs[u]) + 18, TRUE);
DestDir[0] = DngTargetDriveLetter;
DestDir[1] = ':';
strcpy(DestDir+2,LocalSourceDirName);
strcat(DestDir,"\\");
strcat(DestDir,OemOptionalDirectory);
if (!ValidationPass && !OemOptDirCreated) {
DnpCreateOneDirectory(DestDir);
OemOptDirCreated = TRUE;
}
} else if (OptionalDirFlags[u] & OPTDIR_OEMSYS) {
//
// Dest Dir is '<x>:' + '\' + '$' + '\' + 8.3 name + '\0'
//
// Note that on winnt case the directory $OEM$ goes to
// <drive letter>\$ directory. This is to avoid hitting the
// DOS limit of 64 characters on a path, that is more likely to
// happen if we put $OEM$ under \$win_nt$.~ls
//
DestDir = MALLOC(strlen( WINNT_OEM_DEST_DIR ) + 17, TRUE);
DestDir[0] = DngTargetDriveLetter;
DestDir[1] = ':';
DestDir[2] = '\0';
} else {
//
// Dest Dir is '<x>:' + LocalSourceDirName + '\' +
// optional dir name + '\' + 8.3 name + '\0'
//
DestDir = MALLOC(strlen(LocalSourceDirName) +
strlen(OptionalDirs[u]) + 17, TRUE);
DestDir[0] = DngTargetDriveLetter;
DestDir[1] = ':';
strcpy(DestDir+2,LocalSourceDirName);
}
//
// We need a trailing backslash at this point
//
strcat(DestDir,"\\");
//
// Keep a pointer to the place we the optional dir part of
// the string begins
//
Ptr = DestDir + strlen(DestDir);
//
// Add the optional dir name
//
if (OptionalDirFlags[u] & OPTDIR_OEMSYS) {
strcat(DestDir,WINNT_OEM_DEST_DIR);
} else {
strcat(DestDir,OptionalDirs[u]);
}
if (!ValidationPass) {
//
// Create the Directory now
//
while (*Ptr != '\0') {
//
// If the current pointer is a backslash then we need to
// create this subcomponent of the optional dir
//
if (*Ptr == '\\') {
//
// Replace the char with a terminator for now
//
*Ptr = '\0';
//
// Create the subdirectory
//
DnpCreateOneDirectory(DestDir);
//
// Restore the seperator
//
*Ptr = '\\';
}
Ptr++;
}
//
// Create the last component in the optional dir path
//
DnpCreateOneDirectory(DestDir);
}
//
// Concate the trailing backslash now
//
strcat(DestDir,"\\");
//
// If the the optional directory is $OEM$ and it doesn't exist on
// source, then assume that it exists, but it is empty
//
if ( !(OptionalDirFlags[u] & OPTDIR_OEMSYS) ||
OemSysDirExists ) {
//
// Call our recursive tree copy function
//
rc += DnpDoIterateOptionalDir(
ValidationPass,
SourceDir,
DestDir,
Verify,
ClusterSize);
}
//
// Free the allocated buffers
//
FREE(DestDir);
FREE(SourceDir);
} // for
//
// return our result code if we aren't a validation pass, otherwise
// return the total number of files to copy
//
return (ValidationPass ? (ULONG) TotalFileCount : rc);
}
ULONG
DnpDoIterateOptionalDir(
IN BOOLEAN ValidationPass,
IN PCHAR SourceDir,
IN PCHAR DestDir,
IN BOOLEAN Verify,
IN unsigned ClusterSize OPTIONAL
)
{
ULONG TotalSize = 0;
ULONG BytesWritten = 0;
ULONG rc = 0;
PCHAR SourceEnd;
PCHAR DestEnd;
struct find_t FindData;
//
// Remember where the last '\' in each of the two paths is.
// Note: that we assume that all of the dir paths have a
// terminating '\' when it is passed to us.
//
SourceEnd = SourceDir + strlen(SourceDir);
DestEnd = DestDir + strlen(DestDir);
//
// Set the WildCard search string
//
strcpy(SourceEnd,"*.*");
//
// Do the initial search
//
if (_dos_findfirst(SourceDir,_A_HIDDEN|_A_SYSTEM|_A_SUBDIR, &FindData) ) {
//
// We couldn't find anything -- return 0
//
return (0);
}
do {
//
// Form the source and dest dirs strings
//
strcpy(SourceEnd,FindData.name);
strcpy(DestEnd,FindData.name);
//
// Check to see if the entry is a subdir. Recurse into it
// unless it is '.' or '..'
//
if (FindData.attrib & _A_SUBDIR) {
PCHAR NewSource;
PCHAR NewDest;
//
// Check to see if the name is '.' or '..'
//
if (!strcmp(FindData.name,".") || !strcmp(FindData.name,"..")) {
//
// Ignore these two cases
//
continue;
}
//
// Create the new buffers for the source and dest dir names
//
NewSource = MALLOC( strlen(SourceDir) + 14, TRUE);
strcpy(NewSource,SourceDir);
strcat(NewSource,"\\");
NewDest = MALLOC( strlen(DestDir) + 14, TRUE);
strcpy(NewDest,DestDir);
if (!ValidationPass) {
//
// Create the directory
//
DnpCreateOneDirectory(NewDest);
}
//
// Trailing BackSlash
//
strcat(NewDest,"\\");
//
// Recursive call to ourselves
//
BytesWritten = DnpDoIterateOptionalDir(
ValidationPass,
NewSource,
NewDest,
Verify,
ClusterSize);
if (!ValidationPass) {
//
// We don't care about the other case since the
// function is recursive and modifies a global
// value
//
rc += BytesWritten;
}
//
// Free all of the allocated buffers
//
FREE(NewSource);
FREE(NewDest);
//
// Continue Processing
//
continue;
} // if ...
//
// Mainline case
//
if(ValidationPass) {
TotalFileCount++;
} else {
BytesWritten = DnpCopyOneFile(SourceDir,DestDir,Verify,TRUE);
//
// Figure out how much space was taken up by the file on the target.
//
if(ClusterSize) {
TotalSize += BytesWritten;
if(BytesWritten % ClusterSize) {
TotalSize += (ULONG)ClusterSize - (BytesWritten % ClusterSize);
}
}
if(UsingGauge) {
DnTickGauge();
}
}
} while ( !_dos_findnext(&FindData) );
DnSetCopyStatusText(DntEmptyString,NULL);
rc = (ValidationPass ? (ULONG)TotalFileCount : (rc + TotalSize));
return (rc);
}
ULONG
DnpIterateCopyListSection(
IN BOOLEAN ValidationPass,
IN PCHAR SectionName,
IN PCHAR DestinationRoot,
IN BOOLEAN UseDestRoot,
IN BOOLEAN Verify,
IN unsigned ClusterSize OPTIONAL
)
/*++
Routine Description:
Run down a particular section in the INF file making sure it is
syntactically correct and copying files if directed to do so.
Arguments:
ValidationPass - If TRUE, then do not actually copy the files.
If FALSE, copy the files as they are iterated.
SectionName - supplies name of section in inf file to run down.
UseDestRoot - if TRUE, ignore the directory symbol and copy each file to
the DestinationRoot directory. Otherwise append the directory
implied by the directory symbol for a file to the DestinationRoot.
Verify - if TRUE and this is not a validation pass, files will be
verified after they have been copied by rereading them from the
copy source and comparing with the local version that was just
copied.
ClusterSize - if specified, supplies the number of bytes in a cluster
on the destination. If ValidationPass is FALSE, files will be sized as
they are copied, and the return value of this function will be
the total size occupied on the target by the files that are copied
there.
Return Value:
If ValidationPass is TRUE, then the return value is the number of files
that will be copied.
If ClusterSize was specfied and ValidationPass is FALSE,
the return value is the total space occupied on the target drive
by the files that were copied. Otherwise the return value is undefined.
Does not return if a syntax error in encountered in the INF file.
--*/
{
unsigned LineIndex;
PCHAR DirSym,FileName,RenameName;
PCHAR ActualDestPath;
PCHAR ActualSourcePath;
PCHAR FullSourceName,FullDestName;
ULONG TotalSize;
ULONG BytesWritten;
TotalSize = 0;
LineIndex = 0;
while(DirSym = DnGetSectionLineIndex(DngInfHandle,SectionName,LineIndex,0)) {
FileName = DnGetSectionLineIndex(DngInfHandle,SectionName,LineIndex,1);
RenameName = DnGetSectionLineIndex( DngInfHandle,SectionName,LineIndex,2);
//
// Make sure the filename was specified.
//
if(!FileName) {
DnpInfSyntaxError(SectionName);
}
//
// If no rename name was specified, use the file name.
//
if(!RenameName || *RenameName == 0) {
RenameName = FileName;
}
//
// Get destination path
//
if(UseDestRoot) {
ActualDestPath = DnDupString(DestinationRoot);
if(ActualDestPath == NULL) {
DnFatalError(&DnsOutOfMemory);
}
} else {
ActualDestPath = DnpLookUpDirectory( DestinationRoot,
DirectoryList,
DirSym
);
}
if(ActualDestPath == NULL) {
DnpInfSyntaxError(SectionName);
}
//
// Get source path
//
ActualSourcePath = DnpLookUpDirectory( DngSourceRootPath,
DirectoryList,
DirSym
);
if(ActualSourcePath == NULL) {
DnpInfSyntaxError(SectionName);
}
FullSourceName = MALLOC(strlen(ActualSourcePath) + strlen(FileName) + 2,TRUE);
FullDestName = MALLOC(strlen(ActualDestPath) + strlen(RenameName) + 2,TRUE);
strcpy(FullSourceName,ActualSourcePath);
strcat(FullSourceName,"\\");
strcat(FullSourceName,FileName);
strcpy(FullDestName,ActualDestPath);
strcat(FullDestName,"\\");
strcat(FullDestName,RenameName);
FREE(ActualDestPath);
FREE(ActualSourcePath);
if(ValidationPass) {
TotalFileCount++;
} else {
BytesWritten = DnpCopyOneFile(FullSourceName,FullDestName,Verify,FALSE);
//
// Figure out how much space was taken up by the file on the target.
//
if(ClusterSize) {
TotalSize += BytesWritten;
if(BytesWritten % ClusterSize) {
TotalSize += (ULONG)ClusterSize - (BytesWritten % ClusterSize);
}
}
if(UsingGauge) {
DnTickGauge();
}
}
FREE(FullSourceName);
FREE(FullDestName);
LineIndex++;
}
DnSetCopyStatusText(DntEmptyString,NULL);
return(ValidationPass ? (ULONG)TotalFileCount : TotalSize);
}
PCHAR
DnpLookUpDirectory(
IN PCHAR RootDirectory,
IN PDIRECTORY_NODE DirList,
IN PCHAR Symbol
)
/*++
Routine Description:
Match a symbol to an actual directory. Scans a give list of symbol/
directory pairs and if a match is found constructs a fully qualified
pathname that never ends in '\'.
Arguments:
RootDirectory - supplies the beginning of the path spec, to be prepended
to the directory that matches the given Symbol.
DirList - supplies pointer to head of linked list of dir/symbol pairs.
Symbol - Symbol to match.
Return Value:
NULL if a match was not found. Otherwise a pointer to a buffer holding
a full pathspec. The caller must free this buffer.
--*/
{
PCHAR PathSpec;
while(DirList) {
if(!stricmp(DirList->Symbol,Symbol)) {
PathSpec = MALLOC( strlen(RootDirectory)
+ strlen(DirList->Directory)
+ 2,
TRUE
);
strcpy(PathSpec,RootDirectory);
if(*DirList->Directory) {
strcat(PathSpec,"\\");
strcat(PathSpec,DirList->Directory);
}
return(PathSpec);
}
DirList = DirList->Next;
}
return(NULL);
}
VOID
DnpInfSyntaxError(
IN PCHAR Section
)
/*++
Routine Description:
Print an error message about a syntax error in the given section and
terminate.
Arguments:
SectionName - supplies name of section containing bad syntax.
Return Value:
None. Does not return.
--*/
{
CHAR MsgLine1[128];
sprintf(MsgLine1,DnsBadInfSection.Strings[BAD_SECTION_LINE],Section);
DnsBadInfSection.Strings[BAD_SECTION_LINE] = MsgLine1;
DnFatalError(&DnsBadInfSection);
}
ULONG
DnpCopyOneFile(
IN PCHAR SourceName,
IN PCHAR DestName,
IN BOOLEAN Verify,
IN BOOLEAN PreserveAttribs
)
/*++
Routine Description:
Copies a single file.
Arguments:
SourceName - supplies fully qualified name of source file
DestName - supplies fully qualified name of destination file
Verify - if TRUE, the file will be verified after it has been copied.
Return Value:
None. May not return if an error occurs during the copy.
--*/
{
int SrcHandle,DstHandle;
BOOLEAN Err,Retry;
PCHAR FilenamePart;
BOOLEAN Verified;
ULONG BytesWritten = 0;
unsigned attribs;
FilenamePart = strrchr(SourceName,'\\') + 1;
do {
DnSetCopyStatusText(DntCopying,FilenamePart);
Err = TRUE;
_LOG(("Copy %s to %s: ",SourceName,DestName));
if(DnpOpenSourceFile(SourceName,&SrcHandle,&attribs)) {
_dos_setfileattr(DestName,_A_NORMAL);
if(!_dos_creat(DestName,_A_NORMAL,&DstHandle)) {
if(DnpDoCopyOneFile(SrcHandle,DstHandle,FilenamePart,Verify,&Verified,&BytesWritten)) {
_LOG(("success\n"));
Err = FALSE;
}
_dos_close(DstHandle);
} else {
_LOG(("unable to create target\n"));
}
_dos_close(SrcHandle);
} else {
_LOG(("unable to open source file\n"));
}
if(PreserveAttribs && (attribs & (_A_HIDDEN | _A_RDONLY | _A_SYSTEM)) && !Err) {
_dos_setfileattr(DestName,attribs);
}
if(Err) {
Retry = DnCopyError(FilenamePart,&DnsCopyError,COPYERR_LINE);
if(UsingGauge) {
DnDrawGauge(CopyingScreen);
} else {
DnClearClientArea();
DnDisplayScreen(CopyingScreen);
}
DnWriteStatusText(NULL);
} else if(Verify && !Verified) {
Retry = DnCopyError(FilenamePart,&DnsVerifyError,VERIFYERR_LINE);
if(UsingGauge) {
DnDrawGauge(CopyingScreen);
} else {
DnClearClientArea();
DnDisplayScreen(CopyingScreen);
}
DnWriteStatusText(NULL);
Err = TRUE;
}
} while(Err && Retry);
return(BytesWritten);
}
BOOLEAN
DnpDoCopyOneFile(
IN int SrcHandle,
IN int DstHandle,
IN PCHAR Filename,
IN BOOLEAN Verify,
OUT PBOOLEAN Verified,
OUT PULONG BytesWritten
)
/*++
Routine Description:
Perform the actual copy of a single file.
Arguments:
SrcHandle - supplies the DOS file handle for the open source file.
DstHandle - supplies the DOS file handle for the open target file.
Filename - supplies the base filename of the file being copied.
This is used in the status bar at the bottom of the screen.
Verify - if TRUE, the copied file will be verified against the
original copy.
Verified - if Verify is TRUE and the copy succeeds, this value will
receive a flag indicating whether the file verification
determined that the file was copied correctly.
BytesWritten - Receives the number of bytes written to
the target file (ie, the file size).
Return Value:
TRUE if the copy succeeded, FALSE if it failed for any reason.
If TRUE and Verify is TRUE, the caller should also check the value
returned in the Verified variable.
--*/
{
unsigned BytesRead,bytesWritten;
BOOLEAN TimestampValid;
unsigned Date,Time;
PUCHAR VerifyBuffer;
//
// Assume verification will succeed. If the file is not copied correctly,
// this value will become irrelevent.
//
if(Verified) {
*Verified = TRUE;
}
//
// If the copy buffer is not already allocated, attempt to allocate it.
// The first two attempts can fail because we have a fallback size to try.
// If the third attempt fails, bail.
//
if((CopyBuffer == NULL)
&&((CopyBuffer = MALLOC(CopyBufferSize = COPY_BUFFER_SIZE1,FALSE)) == NULL)
&&((CopyBuffer = MALLOC(CopyBufferSize = COPY_BUFFER_SIZE2,FALSE)) == NULL)) {
CopyBuffer = MALLOC(CopyBufferSize = COPY_BUFFER_SIZE3,TRUE);
}
//
// Obtain the timestamp from the source file.
//
TimestampValid = (BOOLEAN)(_dos_getftime(SrcHandle,&Date,&Time) == 0);
//
// read and write chunks of the file.
//
*BytesWritten = 0L;
do {
if(_dos_read(SrcHandle,CopyBuffer,CopyBufferSize,&BytesRead)) {
_LOG(("read error\n"));
return(FALSE);
}
if(BytesRead) {
if(_dos_write(DstHandle,CopyBuffer,BytesRead,&bytesWritten)
|| (BytesRead != bytesWritten))
{
_LOG(("write error\n"));
return(FALSE);
}
*BytesWritten += bytesWritten;
}
} while(BytesRead == CopyBufferSize);
//
// Perserve the original timestamp.
//
if(TimestampValid) {
_dos_setftime(DstHandle,Date,Time);
}
if(Verify) {
union REGS RegsIn,RegsOut;
DnSetCopyStatusText(DntVerifying,Filename);
*Verified = FALSE; // assume failure
//
// Rewind the files.
//
RegsIn.x.ax = 0x4200; // seek to offset from start of file
RegsIn.x.bx = SrcHandle;
RegsIn.x.cx = 0; // offset = 0
RegsIn.x.dx = 0;
intdos(&RegsIn,&RegsOut);
if(RegsOut.x.cflag) {
goto x1;
}
RegsIn.x.bx = DstHandle;
intdos(&RegsIn,&RegsOut);
if(RegsOut.x.cflag) {
goto x1;
}
//
// Files are rewound. Start the verification process.
// Use half the buffer for reading the copy and the other half
// to read the original.
//
VerifyBuffer = (PUCHAR)CopyBuffer + (CopyBufferSize/2);
do {
if(_dos_read(SrcHandle,CopyBuffer,CopyBufferSize/2,&BytesRead)) {
goto x1;
}
if(_dos_read(DstHandle,VerifyBuffer,CopyBufferSize/2,&bytesWritten)) {
goto x1;
}
if(BytesRead != bytesWritten) {
goto x1;
}
if(memcmp(CopyBuffer,VerifyBuffer,BytesRead)) {
goto x1;
}
} while(BytesRead == CopyBufferSize/2);
*Verified = TRUE;
}
x1:
return(TRUE);
}
VOID
DnpFreeDirectoryList(
IN OUT PDIRECTORY_NODE *List
)
/*++
Routine Description:
Free a linked list of directory nodes and place NULL in the
head pointer.
Arguments:
List - supplies pointer to list head pointer; receives NULL.
Return Value:
None.
--*/
{
PDIRECTORY_NODE n,p = *List;
while(p) {
n = p->Next;
FREE(p);
p = n;
}
*List = NULL;
}
VOID
DnDetermineSpaceRequirements(
OUT PULONG RequiredSpace
)
/*++
Routine Description:
Read space requirements from the inf file. The 'space requirement' is the
amount of free disk space required to be on a drive before we will see it
as a valid potential local source.
Arguments:
RequiredSpace - receives the number of bytes of free space on a drive
for it to be a valid local source.
Return Value:
None.
--*/
{
PCHAR RequiredSpaceStr = DnGetSectionKeyIndex( DngInfHandle,
DnfSpaceRequirements,
DnkNtDrive,
0
);
if(!RequiredSpaceStr || !sscanf(RequiredSpaceStr,"%lu",RequiredSpace)) {
DnpInfSyntaxError(DnfSpaceRequirements);
}
}
PCHAR
DnpGenerateCompressedName(
IN PCHAR Filename
)
/*++
Routine Description:
Given a filename, generate the compressed form of the name.
The compressed form is generated as follows:
Look backwards for a dot. If there is no dot, append "._" to the name.
If there is a dot followed by 0, 1, or 2 charcaters, append "_".
Otherwise assume there is a 3-character extension and replace the
third character after the dot with "_".
Arguments:
Filename - supplies filename whose compressed form is desired.
Return Value:
Pointer to buffer containing nul-terminated compressed-form filename.
The caller must free this buffer via FREE().
--*/
{
PCHAR CompressedName,p,q;
//
// The maximum length of the compressed filename is the length of the
// original name plus 2 (for ._).
//
CompressedName = MALLOC(strlen(Filename)+3,TRUE);
strcpy(CompressedName,Filename);
p = strrchr(CompressedName,'.');
q = strrchr(CompressedName,'\\');
if(q < p) {
//
// If there are 0, 1, or 2 characters after the dot, just append
// the underscore. p points to the dot so include that in the length.
//
if(strlen(p) < 4) {
strcat(CompressedName,"_");
} else {
//
// Assume there are 3 characters in the extension. So replace
// the final one with an underscore.
//
p[3] = '_';
}
} else {
//
// No dot, just add ._.
//
strcat(CompressedName,"._");
}
return(CompressedName);
}
BOOLEAN
DnpOpenSourceFile(
IN PCHAR Filename,
OUT int *Handle,
OUT unsigned *Attribs
)
/*++
Routine Description:
Open a file by name or by compressed name. If the previous call to
this function found the compressed name, then try to open the compressed
name first. Otherwise try to open the uncompressed name first.
Arguments:
Filename - supplies full path of file to open. This should be the
uncompressed form of the filename.
Handle - If successful, receives the id for the opened file.
Attribs - if successful receives dos file attributes.
Return Value:
TRUE if the file was opened successfully.
FALSE if not.
--*/
{
static BOOLEAN TryCompressedFirst = FALSE;
PCHAR CompressedName;
PCHAR names[2];
int OrdCompressed,OrdUncompressed;
int i;
BOOLEAN rc;
//
// Generate compressed name.
//
CompressedName = DnpGenerateCompressedName(Filename);
//
// Figure out which name to try to use first. If the last successful
// call to this routine opened the file using the compressed name, then
// try to open the compressed name first. Otherwise try to open the
// uncompressed name first.
//
if(TryCompressedFirst) {
OrdCompressed = 0;
OrdUncompressed = 1;
} else {
OrdCompressed = 1;
OrdUncompressed = 0;
}
names[OrdUncompressed] = Filename;
names[OrdCompressed] = CompressedName;
for(i=0, rc=FALSE; (i<2) && !rc; i++) {
if(!_dos_open(names[i],O_RDONLY|SH_DENYWR,Handle)) {
_dos_getfileattr(names[i],Attribs);
TryCompressedFirst = (BOOLEAN)(i == OrdCompressed);
rc = TRUE;
}
}
FREE(CompressedName);
return(rc);
}
VOID
DnCopyFilesInSection(
IN PCHAR SectionName,
IN PCHAR SourcePath,
IN PCHAR TargetPath
)
{
unsigned line;
PCHAR FileName;
PCHAR TargName;
PCHAR p,q;
DnClearClientArea();
DnWriteStatusText(NULL);
line = 0;
while(FileName = DnGetSectionLineIndex(DngInfHandle,SectionName,line++,0)) {
TargName = DnGetSectionLineIndex(DngInfHandle,SectionName,line-1,1);
if(!TargName) {
TargName = FileName;
}
p = MALLOC(strlen(SourcePath) + strlen(FileName) + 2,TRUE);
q = MALLOC(strlen(TargetPath) + strlen(TargName) + 2,TRUE);
sprintf(p,"%s\\%s",SourcePath,FileName);
sprintf(q,"%s\\%s",TargetPath,TargName);
DnpCopyOneFile(p,q,FALSE,FALSE);
FREE(p);
FREE(q);
}
}
VOID
DnCopyOemBootFiles(
PCHAR TargetPath
)
{
PCHAR SourcePath;
PCHAR FileName;
unsigned Count;
PCHAR p,q;
DnClearClientArea();
DnWriteStatusText(NULL);
SourcePath = MALLOC( strlen( DngSourceRootPath ) + 1 +
strlen( WINNT_OEM_TEXTMODE_DIR ) + 1, TRUE );
strcpy( SourcePath, DngSourceRootPath );
strcat( SourcePath, "\\" );
strcat( SourcePath, WINNT_OEM_TEXTMODE_DIR );
for( Count = 0; Count < OemBootFilesCount; Count++ ) {
FileName = OemBootFiles[Count];
p = MALLOC(strlen(SourcePath) + strlen(FileName) + 2,TRUE);
q = MALLOC(strlen(TargetPath) + strlen(FileName) + 2,TRUE);
sprintf(p,"%s\\%s",SourcePath,FileName);
sprintf(q,"%s\\%s",TargetPath,FileName);
DnpCopyOneFile(p,q,FALSE,FALSE);
FREE(p);
FREE(q);
}
}