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.
 
 
 
 
 
 

1044 lines
25 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:
--*/
#include "precomp.h"
#pragma hdrstop
#include "msg.h"
typedef struct _DIRECTORY_NODE {
struct _DIRECTORY_NODE *Next;
PTSTR Directory; // never starts or ends with \.
PTSTR Symbol;
} DIRECTORY_NODE, *PDIRECTORY_NODE;
#define RETRY_COUNT 2 // retry copy twice if copy fails.
//
// NOTE:
//
// This code must be multi-thread safe, as more than one thread
// may be running through it at any one time.
//
PDIRECTORY_NODE DirectoryList;
BOOL
DnpCreateDirectories(
IN PTSTR TargetRootDir,
IN HWND hdlg
);
DWORD
DnpLocateSourceFile(
IN PTSTR Filename,
OUT PWIN32_FIND_DATA FindData,
OUT PTSTR *ActualFilename
);
PTSTR
DnpLookUpDirectory(
IN PTSTR Symbol
);
BOOL
DnCreateLocalSourceDirectories(
IN HWND hdlg
)
{
BOOL b;
b = DnpCreateOneDirectory(LocalSourcePath,hdlg)
&& DnpCreateDirectories(LocalSourceSubPath,hdlg);
return(b);
}
DWORD
ThreadCopyLocalSourceFiles(
IN PVOID ThreadParameter
)
/*++
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:
ThreadParameter - supplies the handle of a dialog box that will
receive window messages relating to copy progress.
Return Value:
FALSE if an error occurred copying a file and the user elected to
exit setup, or it an error occurred creating directories,
or a syntax error appeared in the inf file.
TRUE otherwise.
--*/
{
DWORD ClusterSize,SectorSize;
DWORD SizeOccupied;
TCHAR Buffer[MAX_PATH];
DWORD FileCount;
HWND hdlg;
BOOL rc;
//
// Do not change this without changing text setup as well
// (SpPtDetermineRegionSpace()).
//
CHAR SizeLine[64];
PCHAR Lines[3] = { "[Data]",SizeLine,NULL };
hdlg = (HWND)ThreadParameter;
try {
FileCount = DnSearchINFSection(InfHandle,INF_FILES);
//
// Determine the sector and cluster size on the drive.
//
GetDriveSectorInfo(LocalSourceDrive,&SectorSize,&ClusterSize);
//
// Pass over the copy list and actually perform the copies.
//
SizeOccupied = CopySectionOfFilesToCopy(
hdlg,
INF_FILES,
LocalSourceSubPath,
ClusterSize,
TRUE
);
//
// Assume failure.
//
rc = FALSE;
if(SizeOccupied != (DWORD)(-1)) {
//
// 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 += SectorSize
* (((FileCount + 1) / (SectorSize / 32)) + 1);
//
// Create a small ini file listing the size occupied by the local source.
// Account for the ini file in the size.
//
lstrcpy(Buffer,LocalSourcePath);
//
// Do not change this without changing text setup as well
// (SpPtDetermineRegionSpace()).
//
DnConcatenatePaths(Buffer,TEXT("\\size.sif"),MAX_PATH);
wsprintfA(Lines[1],"Size = %lu\n",SizeOccupied + ClusterSize);
DnWriteSmallIniFile(Buffer,Lines,NULL);
//
// BUGBUG should really check return code from DnWriteSmallIniFile.
// The ini file being written tells text setup how large the local sources
// are on disk. Not critical if absent.
//
rc = TRUE;
}
PostMessage(hdlg, WMX_ALL_FILES_COPIED, rc, 0);
} except(EXCEPTION_EXECUTE_HANDLER) {
MessageBoxFromMessage(
hdlg,
MSG_GENERIC_EXCEPTION,
AppTitleStringId,
MB_ICONSTOP | MB_OK | MB_APPLMODAL | MB_SETFOREGROUND,
GetExceptionCode()
);
rc = FALSE;
PostMessage(hdlg, WMX_ALL_FILES_COPIED, rc, 0);
}
ExitThread(rc);
return(rc); // avoid compiler warning
}
VOID
DnCreateDirectoryList(
IN PTSTR SectionName
)
/*++
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
Return Value:
None. Does not return if syntax error in the inf file section.
--*/
{
unsigned LineIndex,len;
PDIRECTORY_NODE DirNode,PreviousNode;
PTSTR Key;
PTSTR Dir;
LineIndex = 0;
PreviousNode = NULL;
while(Key = DnGetKeyName(InfHandle,SectionName,LineIndex)) {
Dir = DnGetSectionKeyIndex(InfHandle,SectionName,Key,0);
if(Dir == NULL) {
Dir = TEXT(""); // use the root if not specified
}
//
// Skip leading backslashes
//
while(*Dir == TEXT('\\')) {
Dir++;
}
//
// Clip off trailing backslashes if present
//
while((len = lstrlen(Dir)) && (Dir[len-1] == TEXT('\\'))) {
Dir[len-1] = 0;
}
DirNode = MALLOC(sizeof(DIRECTORY_NODE));
DirNode->Next = NULL;
DirNode->Directory = Dir;
DirNode->Symbol = Key;
if(PreviousNode) {
PreviousNode->Next = DirNode;
} else {
DirectoryList = DirNode;
}
PreviousNode = DirNode;
LineIndex++;
}
}
BOOL
DnpCreateDirectories(
IN PTSTR TargetRootDir,
IN HWND hdlg
)
/*++
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:
Boolean value indicating whether the directories were created
successfully.
--*/
{
PDIRECTORY_NODE DirNode;
TCHAR TargetDirTemp[MAX_PATH];
if(!DnpCreateOneDirectory(TargetRootDir,hdlg)) {
return(FALSE);
}
for(DirNode = DirectoryList; DirNode; DirNode = DirNode->Next) {
//
// No need to create the root
//
if(*DirNode->Directory) {
lstrcpy(TargetDirTemp,TargetRootDir);
DnConcatenatePaths(TargetDirTemp,DirNode->Directory,MAX_PATH);
if(!DnpCreateOneDirectory(TargetDirTemp,hdlg)) {
return(FALSE);
}
}
}
return(TRUE);
}
BOOL
DnpCreateOneDirectory(
IN PTSTR Directory,
IN HWND hdlg
)
/*++
Routine Description:
Create a single directory if it does not already exist.
Arguments:
Directory - directory to create
Return Value:
Boolean value indicating whether the directory was created.
--*/
{
WIN32_FIND_DATA FindData;
HANDLE FindHandle;
BOOL b;
//
// First, see if there's a file out there that matches the name.
//
FindHandle = FindFirstFile(Directory,&FindData);
if(FindHandle == INVALID_HANDLE_VALUE) {
//
// Directory doesn't seem to be there, so we should be able
// to create the directory.
//
b = CreateDirectory(Directory,NULL);
} else {
//
// File matched. If it's a dir, we're OK. Otherwise we can't
// create the dir, a fatal error.
//
b = ((FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
FindClose(FindHandle);
}
if(!b) {
MessageBoxFromMessage(
hdlg,
MSG_COULDNT_CREATE_DIRECTORY,
IDS_ERROR,
MB_OK | MB_ICONSTOP,
Directory
);
}
return(b);
}
BOOL
VerifySectionOfFilesToCopy(
IN PTSTR SectionName,
OUT PDWORD ErrorLine,
OUT PDWORD ErrorValue
)
{
DWORD LineIndex;
DWORD LineCount;
PTSTR DirSym,FileName;
LineCount = DnSearchINFSection(InfHandle,SectionName);
if((LONG)LineCount <= 0) {
// section missing or empty -- indicate bad value on line 0, value 0
*ErrorLine = 0;
*ErrorValue = 0;
return(FALSE);
}
for(LineIndex=0; LineIndex<LineCount; LineIndex++) {
*ErrorLine = LineIndex;
DirSym = DnGetSectionLineIndex(InfHandle,SectionName,LineIndex,0);
if(!DirSym) {
*ErrorValue = 0;
return(FALSE);
}
FileName = DnGetSectionLineIndex(InfHandle,SectionName,LineIndex,1);
if(!FileName) {
*ErrorValue = 1;
return(FALSE);
}
if(!DnpLookUpDirectory(DirSym)) {
*ErrorValue = 0;
return(FALSE);
}
}
return(TRUE);
}
DWORD
CopySectionOfFilesToCopy(
IN HWND hdlg,
IN PTSTR SectionName,
IN PTSTR DestinationRoot,
IN DWORD ClusterSize, OPTIONAL
IN BOOL TickGauge
)
/*++
Routine Description:
Run down a particular section in the INF file copying files listed
therein.
Note that the section is assumed to be free of errors because
the caller should have previously called VerifySectionOfFilesToCopy().
Arguments:
SectionName - supplies name of section in inf file to run down.
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 ClusterSize was specfied the return value is the total space
occupied on the target drive by the files that were copied.
If the return value is -1, then an error occurred copying a file
and the user elected to exit setup.
--*/
{
DWORD LineIndex;
PTSTR DirSym,FileName,RenameName;
PTSTR Directory;
TCHAR SourceFilename[MAX_PATH];
TCHAR TargetFilename[MAX_PATH];
DWORD TotalSize;
DWORD BytesWritten;
DWORD FileCount;
DWORD FilesCopied;
DWORD d;
DWORD ec;
MSG msg;
BOOL Boost;
UINT SourceIndex[MAX_SOURCES];
static UINT SrcNo;
TotalSize = 0;
LineIndex = 0;
//
// Count the number of lines in the section.
//
FileCount = DnSearchINFSection(InfHandle,SectionName);
//
// Prime the pump with the first filename and initialize the copy gauge.
//
FileName = DnGetSectionLineIndex(InfHandle,SectionName,LineIndex,1);
if(TickGauge) {
PostMessage(hdlg,WMX_NTH_FILE_COPIED,0,(LPARAM)TEXT(""));
}
for(LineIndex = 0; LineIndex < FileCount; ) {
DirSym = DnGetSectionLineIndex(InfHandle,SectionName,LineIndex,0);
Directory = DnpLookUpDirectory(DirSym);
//
// If no rename name was specified, use the file name.
//
RenameName = DnGetSectionLineIndex(InfHandle,SectionName,LineIndex,2);
if(!RenameName || *RenameName == 0) {
RenameName = FileName;
}
if(TickGauge) {
EnqueueFileForCopy(
DestinationRoot, // first part of target pathname
Directory, // relative dir for source and target
FileName, // name of file on source
RenameName
);
LineIndex++;
FileName = DnGetSectionLineIndex(InfHandle,SectionName,LineIndex,1);
} else {
//
// Form full paths. For secondary files (ie, files getting copied
// to boot floppies/local boot dir) we want to pick one of the
// sources and not just use the same source over and over.
//
for(d=0; d<SourceCount; d++) {
SourceIndex[d] = (SrcNo+d)%SourceCount;
}
SrcNo = (SrcNo + 1) % SourceCount;
do {
for(d=0; d<SourceCount; d++) {
if(bCancelled) {
return((DWORD)(-1));
}
_lstrcpyn(SourceFilename,Sources[SourceIndex[d]],MAX_PATH);
DnConcatenatePaths(SourceFilename,Directory,MAX_PATH);
DnConcatenatePaths(SourceFilename,FileName,MAX_PATH);
_lstrcpyn(TargetFilename,DestinationRoot,MAX_PATH);
DnConcatenatePaths(TargetFilename,Directory,MAX_PATH);
DnConcatenatePaths(TargetFilename,RenameName,MAX_PATH);
BytesWritten = DnCopyOneFile(hdlg,SourceFilename,TargetFilename,&ec);
if(BytesWritten != (DWORD)(-1)) {
ec = NO_ERROR; // causes break out of while loop
break; // break out of for loop
}
//
// Got an error copying. If this is that last source we can try
// then tell the user about the error. Otherwise let the loop
// continue and we'll try the next source.
//
if(d == (SourceCount-1)) {
switch(DnFileCopyError(hdlg,SourceFilename,TargetFilename,ec)) {
case COPYERR_EXIT:
PostMessage(hdlg,WM_COMMAND,IDCANCEL,0);
return((DWORD)(-1));
case COPYERR_SKIP:
ec = NO_ERROR; // causes break out of while loop
d = SourceCount; // causes break out of for loop
BytesWritten = 0;
break;
case COPYERR_RETRY:
//
// Set vars to force starting over at source 0.
//
ec = NO_ERROR+1; // forces us to stay within while loop
d = (DWORD)(-1); // forces for loop to start over
break;
}
}
}
} while(ec != NO_ERROR);
//
// Figure out how much space was taken up by the file on the target.
//
if(ClusterSize) {
TotalSize += BytesWritten;
if(BytesWritten % ClusterSize) {
TotalSize += ClusterSize - (BytesWritten % ClusterSize);
}
}
LineIndex++;
FileName = DnGetSectionLineIndex(InfHandle,SectionName,LineIndex,1);
}
}
if(TickGauge) {
FileCount += OptionalDirsFileCount;
StartMultiSourcedCopy();
FilesCopied = 0;
while(FilesCopied != FileCount) {
while(PeekMessage(&msg,NULL,WMX_MULTICOPY,WMX_MULTICOPY,PM_REMOVE)) {
if(ClusterSize) {
BytesWritten = msg.lParam;
TotalSize += BytesWritten;
if(BytesWritten % ClusterSize) {
TotalSize += ClusterSize - (BytesWritten % ClusterSize);
}
}
FilesCopied++;
PostMessage(
hdlg,
WMX_NTH_FILE_COPIED,
MAKELONG(FilesCopied,FileCount),
(LPARAM)NULL
);
if(FilesCopied == FileCount) {
break;
}
}
if(FilesCopied != FileCount) {
d = MsgWaitForMultipleObjects(1,&StopCopyingEvent,FALSE,INFINITE,QS_POSTMESSAGE);
if(d == WAIT_OBJECT_0) {
//
// The stop copying event was signalled.
//
return((DWORD)(-1));
}
}
}
//
// This shuts down the copy threads.
//
SetEvent(StopCopyingEvent);
}
return(TotalSize);
}
PTSTR
DnpLookUpDirectory(
IN PTSTR Symbol
)
/*++
Routine Description:
Match a symbol to an actual directory. Scans a given list of
symbol/directory pairs.
Arguments:
Symbol - supplies the symbol for the directory to be looked up.
Return Value:
Pointer to the actual directory if the symbol is found.
The caller should not modify this buffer in any way.
NULL if not found.
--*/
{
PTSTR PathSpec;
PDIRECTORY_NODE DirList;
for(DirList=DirectoryList; DirList; DirList=DirList->Next) {
if(!lstrcmpi(DirList->Symbol,Symbol)) {
return(DirList->Directory);
}
}
return(NULL);
}
DWORD
DnCopyOneFile(
IN HWND hdlg,
IN PTSTR SourceName,
IN PTSTR DestName,
OUT PDWORD ErrorCode OPTIONAL
)
/*++
Routine Description:
Copies a single file.
Arguments:
SourceName - supplies fully qualified name of source file
DestName - supplies fully qualified name of destination file
Return Value:
Number of bytes copied (0 if file is skipped).
If an error occurs, and the user elects to exit setup,
the return value is -1.
--*/
{
BOOL Retry;
DWORD FileSize;
DWORD ec;
WIN32_FIND_DATA FindData;
BOOL b;
UINT RetryCount;
PTSTR ActualName = NULL;
//
// Try to ensure that the target file is overwritable if it exists.
//
SetFileAttributes(DestName,FILE_ATTRIBUTE_NORMAL);
while(1) {
RetryCount = 0;
do {
ec = DnpLocateSourceFile(SourceName,&FindData,&ActualName);
} while((ec != NO_ERROR) && (RetryCount++ < RETRY_COUNT));
if(ec == NO_ERROR) {
//
// Copy the file.
//
RetryCount = 0;
do {
b = CopyFile(ActualName ? ActualName : SourceName,DestName,FALSE);
} while(!b && (RetryCount++ < RETRY_COUNT));
if(b) {
if(ActualName) {
FREE(ActualName);
}
return(FindData.nFileSizeLow);
} else {
ec = GetLastError();
}
}
if(ActualName) {
FREE(ActualName);
}
if(ErrorCode) {
*ErrorCode = ec;
return((DWORD)(-1));
} else {
//
// Send out to copy error routine for processing.
// That routine can tell us to exit, retry, or skip the file.
//
ec = DnFileCopyError(hdlg,SourceName,DestName,ec);
switch(ec) {
case COPYERR_EXIT:
PostMessage(hdlg, WM_COMMAND, IDCANCEL, 0);
return((DWORD)(-1));
case COPYERR_SKIP:
return(0);
case COPYERR_RETRY:
break;
}
}
}
}
VOID
DnFreeDirectoryList(
VOID
)
/*++
Routine Description:
Free a linked list of directory nodes and place NULL in the
head pointer.
Arguments:
None.
Return Value:
None.
--*/
{
PDIRECTORY_NODE n,p = DirectoryList;
while(p) {
n = p->Next;
FREE(p);
p = n;
}
DirectoryList = NULL;
}
PTSTR
DnpGenerateCompressedName(
IN PTSTR 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().
--*/
{
PTSTR CompressedName,p,q;
//
// The maximum length of the compressed filename is the length of the
// original name plus 2 (for ._).
//
CompressedName = MALLOC((lstrlen(Filename)+3)*sizeof(TCHAR));
lstrcpy(CompressedName,Filename);
p = StringRevChar(CompressedName,TEXT('.'));
q = StringRevChar(CompressedName,TEXT('\\'));
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(lstrlen(p) < 4) {
lstrcat(CompressedName,TEXT("_"));
} else {
//
// Assume there are 3 characters in the extension. So replace
// the final one with an underscore.
//
p[3] = TEXT('_');
}
} else {
//
// No dot, just add ._.
//
lstrcat(CompressedName,TEXT("._"));
}
return(CompressedName);
}
DWORD
DnpLocateSourceFile(
IN PTSTR Filename,
OUT PWIN32_FIND_DATA FindData,
OUT PTSTR *ActualFilename
)
/*++
Routine Description:
Determine whether a source file exists and is accessible, and if so,
which name (compressed form vs. uncompressed form) is used. Whether we
look for the compressed or uncompressed name first depends on what form
was found the last time this routine succeeded.
Arguments:
Filename - supplies full path of file to open. This should be the
uncompressed form of the filename.
FindData - if the routine succeeds this receives Win32 Find data
for the file.
ActualFilename - if the filename is in compressed form, receives the name
of the file. Otherwise receives NULL. Not valid unless this routine
returns TRUE.
Return Value:
NO_ERROR if the file was located.
Other Win32 error code if not.
--*/
{
BOOL TryCompressedFirst;
HANDLE FindHandle;
PTSTR FirstName,SecondName;
DWORD ec;
TryCompressedFirst = (BOOL)TlsGetValue(TlsIndex);
if(TryCompressedFirst) {
FirstName = DnpGenerateCompressedName(Filename);
SecondName = Filename;
} else {
FirstName = Filename;
}
FindHandle = FindFirstFile(FirstName,FindData);
if(FindHandle != INVALID_HANDLE_VALUE) {
FindClose(FindHandle);
*ActualFilename = TryCompressedFirst ? FirstName : NULL;
TlsSetValue(TlsIndex,(PVOID)(FirstName != Filename));
return(NO_ERROR);
}
if(!TryCompressedFirst) {
SecondName = DnpGenerateCompressedName(Filename);
}
FindHandle = FindFirstFile(SecondName,FindData);
if(FindHandle == INVALID_HANDLE_VALUE) {
ec = GetLastError();
} else {
FindClose(FindHandle);
if(TryCompressedFirst) {
*ActualFilename = NULL;
FREE(FirstName);
} else {
*ActualFilename = SecondName;
}
TlsSetValue(TlsIndex,(PVOID)(SecondName != Filename));
return(NO_ERROR);
}
//
// Use if statement instead of ?: because FREE is a macro
// that involves taking the address of its operand.
//
if(FirstName == Filename) {
FREE(SecondName);
} else {
FREE(FirstName);
}
return(ec);
}
BOOL
DnCopyFilesInSection(
IN HWND hdlg,
IN PTSTR Section,
IN PTSTR SourceDir,
IN PTSTR TargetDir,
IN BOOL ForceNoComp
)
{
DWORD line = 0;
PTSTR Filename,p,q;
PTSTR Targname;
DWORD d;
while(Filename = DnGetSectionLineIndex(InfHandle,Section,line++,0)) {
Targname = DnGetSectionLineIndex(InfHandle,Section,line-1,1);
if(!Targname) {
Targname = Filename;
}
p = MALLOC((lstrlen(SourceDir)+lstrlen(Filename)+2)*sizeof(TCHAR));
q = MALLOC((lstrlen(TargetDir)+lstrlen(Targname)+2)*sizeof(TCHAR));
wsprintf(p,TEXT("%s\\%s"),SourceDir,Filename);
wsprintf(q,TEXT("%s\\%s"),TargetDir,Targname);
d = DnCopyOneFile(hdlg,p,q,NULL);
if((d != -1) && ForceNoComp) {
//
// then ensure this file isn't using NTFS compression
//
ForceFileNoCompress(q);
}
FREE(p);
FREE(q);
if(d == (DWORD)(-1)) {
return(FALSE);
}
}
return(TRUE);
}