windows-nt/Source/XPSP1/NT/shell/osshell/control/mmsys/copy.c

888 lines
22 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*
* copy.c - Copy routine for WinDosSetup
* Todd Laney
*
* Modification History:
*
* 6/03/91 Vlads Change copy process to incorporate new Install API
*
* 3/24/89 Toddla Wrote it
*
*
* notes:
* we now use the LZCopy stuff for compression
* we now set the crit error handler ourselves so CHECKFLOPPY is
* NOT defined
*/
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <mmsystem.h>
#include "drivers.h"
#include "sulib.h"
//#include <ver.h>
#define MAX_COPY_ATTEMPTS 15
/*
* Maximum number of install disks we support
*/
#define MAX_DISKS 100
/*
* Flags for VerInstallFile
*/
#define FORCEABLE_FLAGS (VIF_MISMATCH + VIF_SRCOLD + VIF_DIFFLANG + VIF_DIFFTYPE + VIF_DIFFCODEPG )
/**********************************************************************
*
* Local function prototypes.
*
**********************************************************************/
// Retrieve disk path for logical disk
BOOL GetDiskPath(LPTSTR Disk, LPTSTR szPath);
// Convert VIF_... to ERROR... return codes
UINT ConvertFlagToValue(DWORD dwFlags);
// Do the work of trying to copy a file
LONG TryCopy(LPTSTR szSrc, // Full source file path
LPTSTR szLogSrc, // Logical source name
LPTSTR szDestPath,// Destination path
FPFNCOPY fpfnCopy); // Callback routine
#ifdef CHECK_FLOPPY
BOOL NEAR IsDiskInDrive(int iDisk);
#endif
// GLOBAL VARIABLES
// directory where windows will be setup to
TCHAR szSetupPath[MAX_PATH];
// directory where the root of the setup disks are!
TCHAR szDiskPath[MAX_PATH];
// Name of driver being copied (or oemsetup.inf)
TCHAR szDrv[120];
/*
* global vars used by DosCopy
*/
static LPTSTR lpBuf = NULL; // copy buffer
static int iBuf = 0; // usage count
static UINT nBufSize;
BOOL bRetry = FALSE;
BOOL bQueryExist;
extern BOOL bCopyEvenIfOlder; // From DRIVERS.C
BOOL DefCopyCallback(int msg, DWORD_PTR n, LPTSTR szFile)
{
return FC_IGNORE;
}
/* UINT FileCopy (szSource, szDir, fpfnCopy, UINT fCopy)
*
* This function will copy a group of files to a single destination
*
* ENTRY:
*
* szSourc : pointer to a SETUP.INF section
* szDir : pointer to a string containing the target DIR
* fpfnCopy : callback function used to notify called of copy status
* fCopy : flags
*
* FC_SECTION - szSource is a section name
* FC_LIST - szSource is a pointer to a char **foo;
* FC_LISTTYPE - szSource is a pointer to a char *foo[];
* FC_FILE - szSource is a file name.
* FC_QUALIFIED - szSource is a fully qualified file name.
* FC_DEST_QUALIFIED - szDir is fully qualified. Don't expand this.
* FC_CALLBACK_WITH_VER - call back if file exists and report version information.
*
* NOTES:
* if szSource points to a string of the form '#name' the section
* named by 'name' will be used as the source files
*
* the first field of each line in the secion is used as the name of the
* source file. A file name has the following form:
*
* #:name
*
* # - Disk number containing file 1-9,A-Z
* name - name of the file, may be a wild card expression
*
* Format for copy status function
*
* BOOL FAR PASCAL CopyStatus(int msg, int n, LPSTR szFile)
*
* msg:
* COPY_ERROR error occured while copying file(s)
* n is the DOS error number
* szFile is the file that got the error
* return: TRUE ok, FALSE abort copy
*
* COPY_STATUS Called each time a new file is copied
* n is the percent done
* szFile is the file being copied
* return: TRUE ok, FALSE abort copy
*
* COPY_INSERTDISK Please tell the user to insert a disk
* n is the disk needed ('1' - '9')
* return: TRUE try again, FALSE abort copy
*
* COPY_QUERYCOPY Should this file be copied?
* n line index in SETUP.INF section (0 based)
* szFile is the line from section
* return: TRUE copy it, FALSE dont copy
*
* COPY_START Sent before any files are copied
*
* COPY_END Sent after all files have been copied
* n is dos error if copy failed
*
* COPY_EXISTS Sent if the FC_CALL_ON_EXIST bit was set
* and the file exists at the destination
* given for the filecopy.
*
*
* EXIT: returns TRUE if successful, FALSE if failure.
*
*/
UINT FileCopy (LPTSTR szSource, LPTSTR szDir, FPFNCOPY fpfnCopy, UINT fCopy)
{
int err = ERROR_SUCCESS; // Return code from this routine
TCHAR szPath[MAX_PATH];
TCHAR szLogSrc[MAX_PATH];
TCHAR szSrc[MAX_PATH];
LPTSTR pFileBegin; // First file
LPTSTR * List; // Handle lists of files
LPTSTR * ListHead;
int nDisk; // The disk we're on
int cntFiles = 0; // How many files we've got to do
if (fpfnCopy == NULL) {
fpfnCopy = DefCopyCallback;
}
if (!szSource || !*szSource || !szDir || !*szDir) {
return ERROR_FILE_NOT_FOUND;
}
/*
* fix up the drive in the destination
*/
if ( fCopy & FC_DEST_QUALIFIED ) {
lstrcpy(szPath, szDir);
fCopy &= ~FC_DEST_QUALIFIED;
} else {
ExpandFileName(szDir, szPath);
}
if (szSource[0] == TEXT('#') && fCopy == FC_FILE) {
fCopy = FC_SECTION;
++szSource;
}
switch (fCopy) {
case FC_SECTION:
{
szSource = infFindSection(NULL,szSource);
/*
* We are called even when the section doesn't exist
*/
if (szSource == NULL) {
return ERROR_SUCCESS;
}
fCopy = FC_LIST;
}
// fall through to FC_LIST
case FC_LIST:
pFileBegin = szSource;
cntFiles = infLineCount(szSource);
break;
case FC_LISTTYPE:
ListHead = List = (LPTSTR far *)szSource;
pFileBegin = *ListHead;
while ( *List++ ) // Count files to be copied.
++cntFiles;
break;
case FC_FILE:
case FC_QUALIFIED:
default:
pFileBegin = szSource;
cntFiles = 1;
}
/*
* walk all files in the list and call TryCopy ....
*
* NOTES:
* we must walk file list sorted by disk number.
* we should use the disk that is currently inserted.
* we should do a find first/find next on the files????
* we need to check for errors.
* we need to ask the user to insert disk in drive.
*
*/
(*fpfnCopy)(COPY_START,0,NULL);
/*
* Go through all possible disks: 1 to 100 and A to Z (26)
*/
for (nDisk = 1;
err == ERROR_SUCCESS && (cntFiles > 0) &&
(nDisk <= MAX_DISKS + 'Z' - 'A' + 1);
nDisk++)
{
TCHAR Disk[10]; // Maximum string is "100:"
LPTSTR pFile;
int FileNumber; // Which file in the list we're on
// (to pass to callback)
pFile = pFileBegin; // Start at first file
List = ListHead; // Handled chained lists
FileNumber = 0; // Informational for callback - gives
// which file in list we're on
/*
* Work out the string representing our disk letter
*/
if (nDisk > MAX_DISKS) {
Disk[0] = TEXT('A') + nDisk - MAX_DISKS - 1;
Disk[1] = TEXT('\0');
} else {
_itow(nDisk, Disk, 10);
}
wcscat(Disk, TEXT(":"));
for (;
err == ERROR_SUCCESS && pFile;
FileNumber++,
pFile = fCopy == FC_LISTTYPE ? *(++List) :
fCopy == FC_LIST ? infNextLine(pFile) :
NULL)
{
/*
* We have to reset high bit of first byte because it could be set
* by translating service in OEM setup to show that file name was
* mapped
*/
*pFile = toascii(*pFile);
/*
* should we copy this file?
* copy the files in disk order.
*/
if (_wcsnicmp(pFile, Disk, wcslen(Disk)) == 0 || // File has disk
// number and we're
// on that disk
RemoveDiskId(pFile) == pFile &&
nDisk == 1 && *pFile || // First disk and
// no disk number
fCopy == FC_QUALIFIED) { // Fully qualified
/*
* done with a file. decrement count.
*/
cntFiles--;
lstrcpy(szDrv, RemoveDiskId(pFile));
switch ((*fpfnCopy)(COPY_QUERYCOPY, FileNumber, pFile))
{
case CopyCurrent: // Skip
continue;
case CopyNeither:
err = ERROR_FILE_EXISTS; // File already exists
case CopyNew:
break;
default:
break;
}
/*
* Pick up bad return code from switch
*/
if (err != ERROR_SUCCESS) {
break;
}
/*
* now we convert logical dest into a physical
* (unless FC_QUALIFIED)
*/
infParseField(pFile, 1, szLogSrc); // logical source
if ( fCopy != FC_QUALIFIED ) {
ExpandFileName(szLogSrc, szSrc); // full physical source
} else {
lstrcpy(szSrc,szLogSrc);
}
/*
* Attempt copy
*/
err = TryCopy(szSrc, // Qualified Source file
szLogSrc, // Logical source file name (with disk #)
szPath, // Path for directory to install in
fpfnCopy); // Copy callback function
/*
* If failed to find file try the windows directory
*/
if (err != ERROR_SUCCESS) {
break;
}
} /* End if dor if DoCopy */
}
}
(*fpfnCopy)(COPY_END,err,NULL);
return err;
}
/**********************************************************************
*
* TryCopy
*
* Copy a single file from source to destination using the VerInstallFile
* API - interpreting the return code as :
*
* ERROR_SUCCESS - OK
* Other - failure type
*
**********************************************************************/
LONG TryCopy(LPTSTR szSrc, // Full expanded source file path
LPTSTR szLogSrc, // Logical source name
LPTSTR szDestPath, // Destination path
FPFNCOPY fpfnCopy) // Callback routine
{
DWORD wTmpLen;
DWORD dwRetFlags;
TCHAR szTempFile[MAX_PATH];
TCHAR szErrFile[MAX_PATH];
TCHAR DriversPath[MAX_PATH];
BOOL bRetVal; // Return code from callback
LPTSTR szFile;
TCHAR szSrcPath[MAX_PATH];
int iAttemptCount;
WORD wVerFlags;
LONG err;
/*
* Fix up destination if file is a kernel driver
*/
if (IsFileKernelDriver(szSrc) && szDestPath)
{
wcscpy(DriversPath, szDestPath);
wcscat(DriversPath, TEXT("\\drivers"));
szDestPath = DriversPath;
}
/*
* Create file name from current string
*/
szFile = FileName(szSrc);
lstrcpy(szSrcPath, szSrc);
StripPathName(szSrcPath);
for(iAttemptCount = 0, wVerFlags = 0 ;
iAttemptCount <= MAX_COPY_ATTEMPTS;
iAttemptCount++) {
HCURSOR hcurPrev; // Saved cursor state
// Central operation - attempt to install file szFile in directory
// pointed by szPath from directory pointed by szSrc
// If operation will fail but with possibility to force install
// in last parameter buffer we will have temporary file name ==>
// therefore we can avoid excessive copying.
// NOTE: now szFile consists of only file name and other buffers
// only path names.
wTmpLen = MAX_PATH;
hcurPrev = SetCursor(LoadCursor(NULL,IDC_WAIT));
dwRetFlags = VerInstallFile(wVerFlags,
(LPTSTR) szFile,
(LPTSTR) szFile,
(LPTSTR) szSrcPath,
(LPTSTR) szDestPath,
(LPTSTR) szDestPath,
(LPTSTR) szTempFile,
(LPDWORD) &wTmpLen);
SetCursor(hcurPrev);
/*
* Operation failed if at least one bit of return flags is non-zero
* That is unusual but defined so in Version API.
*/
if ( !dwRetFlags )
return ERROR_SUCCESS; // If no errors - goto next file
/*
* If flag MISMATCH is set - install can be forced and we have
* temporary file in destination subdirectory
*/
if ( dwRetFlags & VIF_MISMATCH ) {
if ( (dwRetFlags & VIF_SRCOLD) && (!bCopyEvenIfOlder) ) {
/*
* If we need not call back with question - automatically
* force install with same parameters.
* michaele, *only* if src file is *newer* than dst file
*/
DeleteFile(szTempFile);
return ERROR_SUCCESS;
}
/*
* If we need not call back with question - automatically
* force install with same parameters.
*/
wVerFlags |= VIFF_FORCEINSTALL;
iAttemptCount--; // Make sure we get another go.
continue;
} /* End if MISMATCH */
/*
* If real error occured - call back with error file info
* In all dialogs we use our error codes - so I will convert
* flags returned from Ver API to ours.
*/
err = ConvertFlagToValue(dwRetFlags);
/*
* If source path or file is nor readable - try to change disk
*/
if ( dwRetFlags & VIF_CANNOTREADSRC )
{
/*
* Now new path in szSrc so I deleted logic for creating it
*/
if (RemoveDiskId(szLogSrc) == szLogSrc)
/*
* if disk # not provided, default to 1
*/
bRetVal = (*fpfnCopy)(COPY_INSERTDISK, (DWORD_PTR)"1", szSrcPath);
else
bRetVal = (*fpfnCopy)(COPY_INSERTDISK, (DWORD_PTR)szLogSrc, szSrcPath);
switch (bRetVal)
{
case FC_RETRY:
continue; // and try again...
case FC_ABORT:
return ERROR_FILE_NOT_FOUND;
case FC_IGNORE:
break;
}
}
ExpandFileName(szLogSrc, szErrFile);
#if WINDOWSDIR
if (!*bWindowsDir &&
err != FC_ERROR_LOADED_DRIVER &&
err != ERROR_DISK_FULL)
{
GetWindowsDirectory(szPath, MAX_PATH);
*bWindowsDir = TRUE;
continue;
}
#endif // WINDOWSDIR
switch ((*fpfnCopy)(COPY_ERROR, err, szErrFile)) {
case FC_IGNORE:
return ERROR_SUCCESS;
case FC_RETRY:
break;
case FC_ABORT:
return ERROR_FILE_NOT_FOUND;
}
} // End of attempts
return err;
}
/* BOOL GetDiskPath(Disk, szPath)
*
* This function will retrive the full path name for a logical disk
*
* The code reads the [disks] section of SETUP.INF and looks for
* n = path where n is the disk char. NOTE the disk '0' defaults to
* the root windows directory.
*
* ENTRY:
*
* cDisk : what disk to find 0-9,A-Z
* szPath : buffer to hold disk path
*
* Returns :
* TRUE if a disk path was found
* FALSE if there was no disk specified (ie no ':'
*
*/
BOOL GetDiskPath(LPTSTR Disk, LPTSTR szPath)
{
TCHAR ach[MAX_PATH];
TCHAR szBuf[MAX_PATH];
int i;
/*
* Check to see if there is actually a disk id.
* If not return FALSE
*/
if (RemoveDiskId(Disk) == Disk) {
return FALSE;
}
/*
* Create our copy of the disk id
*/
for (i = 0; Disk[i] != TEXT(':'); i++) {
ach[i] = Disk[i];
}
ach[i] = TEXT('\0');
/*
* Zero disk letter means windows setup directory
*/
if (_wcsicmp(ach, TEXT("0")) == 0) {
/*
* return the windows setup directory
*/
lstrcpy(szPath,szSetupPath);
return TRUE;
}
/*
* now look in the [disks] section for a full path name
*
* This is a pretty bogus concept and is not supported
* in win 32 style disks section [Source Media Descriptions]
*/
if ( !infGetProfileString(NULL,DISK_SECT,ach,szPath) &&
!infGetProfileString(NULL,OEMDISK_SECT,ach,szPath)) {
lstrcpy(szPath, szDiskPath);
} else {
infParseField(szPath,1,szPath);
/*
* is the path relative? is so prepend the szDiskPath
*/
if (szPath[0] == TEXT('.') || szPath[0] == TEXT('\0')) {
lstrcpy(szBuf,szDiskPath);
catpath(szBuf,szPath);
lstrcpy(szPath,szBuf);
}
}
return TRUE;
}
/* BOOL FAR PASCAL ExpandFileName(LPSTR szFile, LPTSTR szPath)
*
* This function will retrive the full path name for a file
* it will expand, logical disk letters to pyshical ones
* will use current disk and directory if non specifed.
*
* if the drive specifed is 0-9, it will expand the drive into a
* full pathname using GetDiskPath()
*
* IE 0:system ==> c:windows\system
* 1:foo.txt a:\foo.txt
*
* ENTRY:
*
* szFile : File name to expand
* szPath : buffer to hold full file name
*
*/
BOOL ExpandFileName(LPTSTR szFile, LPTSTR szPath)
{
TCHAR szBuf[MAX_PATH*2];
if (GetDiskPath(szFile, szBuf)) {
lstrcpy(szPath,szBuf);
if (szFile[2])
catpath(szPath,szFile + 2);
} else {
lstrcpy(szPath,szFile);
}
return TRUE;
}
void catpath(LPTSTR path, LPTSTR sz)
{
//
// Remove any drive letters from the directory to append
//
sz = RemoveDiskId(sz);
//
// Remove any current directories ".\" from directory to append
//
while (sz[0] == TEXT('.') && SLASH(sz[1]))
sz += 2;
//
// Dont append a NULL string or a single "."
//
if (*sz && ! (sz[0] == TEXT('.') && sz[1] == 0))
{
// Add a slash separator if necessary.
if ((! SLASH(path[lstrlen(path) - 1])) && // slash at end of path
((path[lstrlen(path) - 1]) != TEXT(':')) && // colon at end of path
(! SLASH(sz[0]))) // slash at beginning of file
lstrcat(path, CHSEPSTR);
lstrcat(path, sz);
}
}
/*
* Return a pointer to the file name part of a string
*/
LPTSTR FileName(LPTSTR szPath)
{
LPTSTR sz;
for (sz=szPath; *sz; sz++)
;
for (; sz>=szPath && !SLASH(*sz) && *sz!=TEXT(':'); sz--)
;
return ++sz;
}
/*
* Return the portion of a file name following the disk (ie anything
* before the colon).
* If there is no colon just return a pointer to the original string
*/
LPTSTR RemoveDiskId(LPTSTR szPath)
{
LPTSTR sz;
for (sz = szPath; *sz; sz++) {
if (*sz == TEXT(':')) {
return sz + 1;
}
}
return szPath;
}
LPTSTR StripPathName(LPTSTR szPath)
{
LPTSTR sz;
sz = FileName(szPath);
if (sz > szPath+1 && SLASH(sz[-1]) && sz[-2] != TEXT(':'))
sz--;
*sz = 0;
return szPath;
}
/*
* See if a file is a kernel driver. Unfortunately the VersionInfo APIs
* don't seem coded up to take care of this at the moment so we just check
* to see if the file extension is ".SYS"
*/
BOOL IsFileKernelDriver(LPTSTR szPath)
{
TCHAR drive[MAX_PATH];
TCHAR dir[MAX_PATH];
TCHAR fname[MAX_PATH];
TCHAR ext[MAX_PATH];
lsplitpath(szPath, drive, dir, fname, ext);
return !_wcsicmp(ext, TEXT(".sys"));
}
/**************************************************************************
*
* This function converts returned flags from Ver API to the numerical
* error codes used in SETUP.
*
***************************************************************************/
UINT ConvertFlagToValue(DWORD dwFlags)
{
if ( ! dwFlags )
return(NO_ERROR);
if ( dwFlags & VIF_CANNOTREADSRC )
return(ERROR_FILE_NOT_FOUND);
if ( dwFlags & VIF_OUTOFMEMORY )
return(ERROR_OUTOFMEMORY);
if ( dwFlags & VIF_ACCESSVIOLATION )
return(ERROR_ACCESS_DENIED);
if ( dwFlags & VIF_SHARINGVIOLATION )
return(ERROR_SHARING_VIOLATION);
if ( dwFlags & VIF_FILEINUSE)
return(FC_ERROR_LOADED_DRIVER);
return(ERROR_CANNOT_COPY); // General error
}
#ifdef CHECK_FLOPPY
/*--------------------------------------------------------------------------
IsValidDiskette() -
--------------------------------------------------------------------------*/
#define CBSECTORSIZE 512
#define INT13_READ 2
BOOL IsValidDiskette(int iDrive)
{
TCHAR buf[CBSECTORSIZE];
iDrive |= 0x0020; // make lower case
iDrive -= 'a'; // A = 0, B = 1, etc. for BIOS stuff
return MyReadWriteSector(buf, INT13_READ, iDrive, 0, 0, 1);
}
/* BOOL IsDiskInDrive(char cDisk)
*
* Is the specifed disk in the drive
*
* ENTRY:
*
* cDisk : what disk required to be in the drive (logical)
*
* return TRUE if the specifed disk is in the drive
* FALSE if the wrong disk is in the drive or disk error
*
*/
BOOL IsDiskInDrive(int iDisk)
{
if ((iDisk >= 'A' && iDisk <= 'Z') ||
(iDisk >= 'a' && iDisk <= 'z'))
{
if (DosRemoveable(iDisk))
{
if (!IsValidDiskette(iDisk))
return FALSE;
}
return TRUE;
}
return TRUE; // for non drive letters assume a path
// and thus always in.
}
#endif