windows-nt/Source/XPSP1/NT/sdktools/winobj/wfsys.c
2020-09-26 16:20:57 +08:00

709 lines
23 KiB
C

/****************************************************************************/
/* */
/* WFSYS.C - */
/* */
/* Routines for Making Bootable Floppies */
/* */
/****************************************************************************/
#include "winfile.h"
#include "winnet.h"
#include "lfn.h"
#include "wfcopy.h"
#define CSYSFILES 3 /* Three system files are to be copied */
#define SYSFILENAMELEN 16 /* Example "A:\????????.???\0" */
#define SEEK_END 2 /* Used by _llseek() */
/* Error Codes. NOTE: Don't change this order! */
#define NOERROR 0 /* No error */
#define NOMEMORY 1 /* Insufficient memory */
#define NOSRCFILEBIOS 2 /* BIOS is missing */
#define NOSRCFILEDOS 3 /* DOS is missing */
#define NOSRCFILECMD 4 /* Command.Com is missing */
#define COPYFILEBIOS 5 /* Error in copying BIOS */
#define COPYFILEDOS 6 /* Error in copying DOS */
#define COPYFILECMD 7 /* Error in copying Command.com */
#define INVALIDBOOTSEC 8
#define INVALIDDSTDRIVE 9
#define DSTDISKERROR 10
#define NOTSYSABLE 11 /* First N clusters are NOT empty */
#define NOTSYSABLE1 12 /* First 2 entries in ROOT are not sys files */
#define NOTSYSABLE2 13 /* First N clusters are not allocated to SYS files */
#define NODISKSPACE 14 /* There is not sufficient disk space */
#define BUFFSIZE 8192
#define SECTORSIZE 512
LONG SysFileSize[CSYSFILES];
CHAR BIOSfile[SYSFILENAMELEN];
CHAR DOSfile[SYSFILENAMELEN];
CHAR COMMANDfile[130]; /* Command.com can have a full path name in COMSPEC= */
CHAR *SysFileNamePtr[CSYSFILES]; /* Ptrs to source file names */
/* SysNameTable contains the names of System files; First for PCDOS, the
* second set for MSDOS.
*/
CHAR *SysNameTable[2][3] = {
{"IBMBIO.COM", "IBMDOS.COM", "COMMAND.COM"},
{"IO.SYS", "MSDOS.SYS", "COMMAND.COM"}
};
BOOL
IsSYSable(
WORD iSrceDrive,
WORD iDestDrive,
CHAR DestFileNames[][SYSFILENAMELEN], /* NOTE: 2-dimensional array */
LPSTR lpFileBuff
);
/*--------------------------------------------------------------------------*/
/* */
/* SameFilenames() - */
/* */
/*--------------------------------------------------------------------------*/
/* This checks whether the two filenames are the same or not.
* The problem lies in the fact that lpDirFileName points to the
* filename as it appears in a directory (filename padded with blanks
* up to eight characters and then followed by extension). But
* szFileName is an ASCII string with no embedded blanks and has a
* dot that seperates the extension from file name.
*/
BOOL
SameFilenames(
LPSTR lpDirFileName,
LPSTR szFileName
)
{
INT i;
CHAR c1;
CHAR c2;
/* lpDirFileName definitely has 11 characters (8+3). Nothing more!
* Nothing less!
*/
for (i=0; i < 11; i++) {
c1 = *lpDirFileName++;
c2 = *szFileName++;
if (c2 == '.') {
/* Skip all the blanks at the end of the filename */
while (c1 == ' ' && i < 11) {
c1 = *lpDirFileName++;
i++;
}
c2 = *szFileName++;
}
if (c1 != c2)
break;
}
return (i != 11);
}
/*--------------------------------------------------------------------------*/
/* */
/* HasSystemFiles() - */
/* */
/*--------------------------------------------------------------------------*/
/* See if the specified disk has IBMBIO.COM and IBMDOS.COM (or IO.SYS and
* MSDOS.SYS). If so, store their sizes in SysFileSize[].
*/
BOOL
APIENTRY
HasSystemFiles(
WORD iDrive
)
{
INT i;
HFILE fh;
DPB DPB;
BOOL rc;
CHAR ch;
LPSTR lpStr;
LPSTR lpFileBuff;
OFSTRUCT OFInfo;
HANDLE hFileBuff;
/* Initialise the source filename pointers */
SysFileNamePtr[0] = &BIOSfile[0];
SysFileNamePtr[1] = &DOSfile[0];
SysFileNamePtr[2] = &COMMANDfile[0];
hFileBuff = NULL;
lpFileBuff = NULL;
/* Acertain the presence of BIOS/DOS/COMMAND and grab their sizes.
* First we will try IBMBIO.COM. If it does not exist, then we will try
* IO.SYS. It it also does not exist, then it is an error.
*/
/* Get the DPB */
if (GetDPB(iDrive, &DPB) != NOERROR)
goto HSFError;
/* Check if the iDrive has standard sector size; If it doesn't then report
* error; (We can allocate a bigger buffer and proceed at this point, but
* int25 to read an abosolute sector may not work in pmodes, because they
* assume standard sector sizes;)
* Fix for Bug #10632 --SANKAR-- 03-21-90
*/
if (HIWORD(GetClusterInfo(iDrive)) > SECTORSIZE)
goto HSFError;
/* Allocate enough memory to read the first cluster of root dir. */
if (!(hFileBuff = LocalAlloc(LHND, (DWORD)SECTORSIZE)))
goto HSFError;
if (!(lpFileBuff = LocalLock(hFileBuff)))
goto HSFError;
/* Read the first cluster of the root directory. */
if (MyInt25(iDrive, lpFileBuff, 1, DPB.dir_sector))
goto HSFError;
/* Let us start with the first set of system files. */
for (i=0; i <= CSYSFILES-1; i++) {
lstrcpy((LPSTR)SysFileNamePtr[i], "C:\\");
lstrcat((LPSTR)SysFileNamePtr[i], SysNameTable[0][i]);
*SysFileNamePtr[i] = (BYTE)('A'+iDrive);
}
/* Get the command.com from the COMSPEC= environment variable */
lpStr = MGetDOSEnvironment();
/* Find the COMSPEC variable. */
while (*lpStr != TEXT('\0')) {
if (lstrlen(lpStr) > 8) {
ch = lpStr[7];
lpStr[7] = TEXT('\0');
if (lstrcmpi(lpStr, (LPSTR)"COMSPEC") == 0) {
lpStr[7] = ch;
break;
}
}
lpStr += lstrlen(lpStr)+1;
}
/* If no COMSPEC then things are really roached... */
if (*lpStr == TEXT('\0'))
goto HSFError;
/* The environment variable is COMSPEC; Look for '=' char */
while (*lpStr != '=')
lpStr = AnsiNext(lpStr);
/* Copy the command.com with the full pathname */
lstrcpy((LPSTR)SysFileNamePtr[2], lpStr);
/* Check if the IBMBIO.COM and IBMDOS.COM exist. */
if (SameFilenames(lpFileBuff, (LPSTR)(SysFileNamePtr[0]+3)) ||
SameFilenames(lpFileBuff+sizeof(DIRTYPE), (LPSTR)(SysFileNamePtr[1]+3))) {
/* Check if at least IO.SYS and MSDOS.SYS exist. */
lstrcpy((LPSTR)(SysFileNamePtr[0]+3), SysNameTable[1][0]);
lstrcpy((LPSTR)(SysFileNamePtr[1]+3), SysNameTable[1][1]);
if (SameFilenames(lpFileBuff, (SysFileNamePtr[0]+3)) ||
SameFilenames(lpFileBuff+sizeof(DIRTYPE), (SysFileNamePtr[1]+3)))
goto HSFError;
}
/* Check if COMMAND.COM exists in the source drive. */
if ((fh = MOpenFile((LPSTR)SysFileNamePtr[2], (LPOFSTRUCT)&OFInfo, OF_READ)) == -1)
goto HSFError;
/* Get the file sizes. */
SysFileSize[0] = ((LPDIRTYPE)lpFileBuff)->size;
SysFileSize[1] = ((LPDIRTYPE)(lpFileBuff+sizeof(DIRTYPE)))->size;
SysFileSize[2] = M_llseek(fh, 0L, SEEK_END);
M_lclose(fh);
rc = TRUE;
goto HSFExit;
HSFError:
rc = FALSE;
HSFExit:
if (lpFileBuff)
LocalUnlock(hFileBuff);
if (hFileBuff)
LocalFree(hFileBuff);
MFreeDOSEnvironment(lpStr);
return (rc);
}
/*--------------------------------------------------------------------------*/
/* */
/* CalcFreeSpace() - */
/* */
/*--------------------------------------------------------------------------*/
/* Given an array of filenames and the number of files, this function
* calculates the freespace that would be created if those files are deleted.
*
* NOTE: This function returns TOTAL free space, (i.e) the summation of
* already existing free space and the space occupied by those files.
*/
INT
CalcFreeSpace(
CHAR DestFiles[][SYSFILENAMELEN],
INT cFiles,
INT cbCluster,
WORD wFreeClusters,
WORD wReqdClusters
)
{
INT i;
HFILE fh;
LONG lFileSize;
OFSTRUCT OFInfo;
ENTER("CalcFreeSpace");
/* Find out the space already occupied by SYS files, if any. */
for (i=0; i < cFiles; i++) {
fh = MOpenFile(&DestFiles[i][0], &OFInfo, OF_READ);
if (fh != (HFILE)-1) {
/* Get the file size */
lFileSize = M_llseek(fh, 0L, SEEK_END);
if (lFileSize != -1L)
wFreeClusters += LOWORD((lFileSize + cbCluster - 1)/cbCluster);
M_lclose(fh);
if (wFreeClusters >= wReqdClusters)
return (wFreeClusters);
}
}
LEAVE("CalcFreeSpace");
return (wFreeClusters);
}
/*--------------------------------------------------------------------------*/
/* */
/* CheckDiskSpace() - */
/* */
/*--------------------------------------------------------------------------*/
BOOL
CheckDiskSpace(
WORD iDestDrive,
INT cbCluster, /* Bytes/Cluster of dest drive */
CHAR DestFileNames[][SYSFILENAMELEN], /* NOTE: 2-dimensional array */
BOOL bDifferentSysFiles,
CHAR DestSysFiles[][SYSFILENAMELEN]
)
{
INT i;
INT wFreeClusters;
INT wReqdClusters;
/* Compute the number of clusters required. */
wReqdClusters = 0;
for (i=0; i < CSYSFILES; i++)
wReqdClusters += LOWORD((SysFileSize[i] + cbCluster - 1) / cbCluster);
/* Calculate the free disk space in clusters in the destination disk */
wFreeClusters = LOWORD(GetFreeDiskSpace(iDestDrive) / cbCluster);
if (wFreeClusters >= wReqdClusters)
/* We have enough space. */
return (TRUE);
wFreeClusters = CalcFreeSpace(DestFileNames, CSYSFILES, cbCluster, (WORD)wFreeClusters, (WORD)wReqdClusters);
if (wFreeClusters >= wReqdClusters)
return (TRUE);
/* Check if the sys files in the dest disk are different. */
if (bDifferentSysFiles) {
wFreeClusters = CalcFreeSpace(DestSysFiles, 2, cbCluster, (WORD)wFreeClusters, (WORD)wReqdClusters);
if (wFreeClusters >= wReqdClusters)
return (TRUE);
}
/* Insufficient disk space even if we delete the sys files. */
return (FALSE);
}
/*--------------------------------------------------------------------------*/
/* */
/* IsSYSable() - */
/* */
/*--------------------------------------------------------------------------*/
/* The requirements for the destination disk to be sysable are either:
*
* 1) first two directory entries are empty
* 2) the first N clusters free where N = ceil (size IBMBIO/secPerClus)
* 3) there is enough room on the disk for IBMBIO/IBMDOS/COMMAND
*
* - or -
*
* 1) the first two directory entries are IBMBIO.COM and IBMDOS.COM
* or IO.SYS and MSDOS.SYS
* 2) the first N clusters are alloced to these files where N is defines above.
* 3) there is enough room on the disk for IBMBIO/IBMDOS/COMMAND after
* deleting the IBMBIO/IBMDOS/COMMAND on the disk.
*
* IMPORTANT NOTE:
* DestFileNames[][] contain the names of the sys files that would be
* created on the Destination diskette;
* DestSysFiles[][] contain the names of the sys files already
* present in the destination diskette, if any; Please Note that
* these two sets of filenames need not be the same, because you can
* install MSDOS on to a diskette that already has PCDOS and
* vice-versa.
*/
BOOL
IsSYSable(
WORD iSrceDrive,
WORD iDestDrive,
CHAR DestFileNames[][SYSFILENAMELEN], /* NOTE: 2-dimensional array */
LPSTR lpFileBuff
)
{
#ifdef LATER
INT i;
DPB DPB;
WORD clusTmp1, clusTmp2;
WORD clusBIOS, clusDOS;
INT cBytesPerCluster;
INT cBIOSsizeInClusters;
BOOL bDifferentDestFiles = FALSE;
CHAR chVolLabel[11]; /* This is NOT null terminated */
DWORD dwSerialNo;
CHAR DestSysFiles[2][SYSFILENAMELEN];
INT cContigClusters;
DWORD dwClusterInfo;
CHAR szTemp[SYSFILENAMELEN];
/* Grab DPB for destination. */
if (GetDPB(iDestDrive, &DPB))
return (FALSE);
/* Has the user aborted? */
if (WFQueryAbort())
return (FALSE);
/* Get bytes per cluster for destination. */
dwClusterInfo = GetClusterInfo(iDestDrive);
/* Bytes per cluster = sectors per cluster * size of a sector */
cBytesPerCluster = LOWORD(dwClusterInfo) * HIWORD(dwClusterInfo);
if (!cBytesPerCluster)
return (FALSE);
/* Has the user aborted? */
if (WFQueryAbort())
return (FALSE);
/* Convert size of BIOS into full clusters */
cBIOSsizeInClusters = LOWORD((SysFileSize[0] + cBytesPerCluster - 1) / cBytesPerCluster);
/* Number of clusters required to be contiguous depends on DOS versions.
* DOS 3.2 and below expect all clusters of BIOS to be contiguos.
* But 3.3 and above expect only the first stub loader (<2K) to be contiguous.
*/
cContigClusters = (GetDOSVersion() > 0x314) ?
((2048 + cBytesPerCluster - 1)/cBytesPerCluster) :
cBIOSsizeInClusters;
/* Grab first sector of destination root directory */
if (MyInt25(iDestDrive, lpFileBuff, 1, DPB.dir_sector))
return (FALSE);
/* Has the user aborted? */
if (WFQueryAbort())
return (FALSE);
/* Are the first two directory entries empty? */
if ((lpFileBuff[0] == 0 || (BYTE)lpFileBuff[0] == 0xE5) &&
(lpFileBuff[sizeof(DIRTYPE)] == 0 || (BYTE)lpFileBuff[sizeof(DIRTYPE)] == 0xE5)) {
/* Any of first N (= BIOS size) clusters not empty? */
for (i=0; i < cContigClusters; i++) {
/* Has the user aborted? */
if (WFQueryAbort())
return (FALSE);
}
} else {
/* Are the first two directory entries NOT BIOS/DOS? */
for (i=0; i < 2; i++) {
if ((!SameFilenames(lpFileBuff, SysNameTable[i][0])) ||
(!SameFilenames(lpFileBuff+sizeof(DIRTYPE), SysNameTable[i][1]))) {
/* Check if the destination files are the same as the source files */
if (lstrcmpi(&DestFileNames[0][3], SysNameTable[i][0])) {
/* No! Delete the other set of filenames. */
DestSysFiles[0][0] = DestSysFiles[1][0] = (BYTE)('A'+iDestDrive);
lstrcpy(&DestSysFiles[0][1], ":\\");
lstrcpy(&DestSysFiles[0][3], SysNameTable[i][0]);
lstrcpy(&DestSysFiles[1][1], ":\\");
lstrcpy(&DestSysFiles[1][3], SysNameTable[i][1]);
bDifferentDestFiles = TRUE;
}
break;
}
}
/* Did we find a match? */
if (i == 2)
/* Nope, the 2 entries are occupied by non-system files. */
return (FALSE);
/* Any of first N clusters NOT allocated to BIOS/DOS? */
clusBIOS = ((LPDIRTYPE)lpFileBuff)->first;
clusDOS = ((LPDIRTYPE)(lpFileBuff + sizeof(DIRTYPE)))->first;
/* Do it the hard way, for each cluster 2..N+2 see if it is in the chain.
*/
for (i=0; i < cContigClusters; i++) {
clusTmp1 = clusBIOS;
clusTmp2 = clusDOS;
/* Check if cluster #i+2 is allocated to either of these files. */
while (TRUE) {
if (i+2 == (INT)clusTmp1 || i+2 == (INT)clusTmp2)
break;
// if (clusTmp1 != -1)
if (clusTmp1 < 0xFFF0)
clusTmp1 = 0;
// if (clusTmp2 != -1)
if (clusTmp2 < 0xFFF0)
clusTmp2 = 0;
// if (clusTmp1 == -1 && clusTmp2 == -1)
if (clusTmp1 >= 0xFFF0 && clusTmp2 >= 0xFFF0)
return FALSE;
/* Did the user abort? */
if (WFQueryAbort())
return FALSE;
}
}
}
/* Let us check if there is enough space on the dest disk. */
if (CheckDiskSpace(iDestDrive, cBytesPerCluster, DestFileNames, bDifferentDestFiles, DestSysFiles) == FALSE)
return (FALSE);
/* Has the user aborted? */
if (WFQueryAbort())
return (FALSE);
/* Get the Present Volume label and preserve it. */
GetVolumeLabel(iDestDrive, (LPSTR)chVolLabel, FALSE);
/*** NOTE: chVolLabel remains in OEM characters! ***/
/* Get the serial no if any and preserve it. */
dwSerialNo = ReadSerialNumber(iDestDrive, lpFileBuff);
/* Copy and adjust boot sector from source to destination */
if (WriteBootSector(iSrceDrive, iDestDrive, NULL, lpFileBuff) != NOERROR)
return (FALSE);
/* Restore the old volume label and serial number in the boot rec. */
if (ModifyVolLabelInBootSec(iDestDrive, (LPSTR)chVolLabel, dwSerialNo, lpFileBuff))
return (FALSE);
/* Delete destination BIOS/DOS/COMMAND. */
for (i=0; i < CSYSFILES; i++) {
AnsiToOem(DestFileNames[i], szTemp);
SetFileAttributes(szTemp, 0);
DosDelete(szTemp);
if ((bDifferentDestFiles) && (i < 2)) {
SetFileAttributes(szTemp, 0);
DosDelete(szTemp);
}
/* Has the user aborted? */
if (WFQueryAbort())
return (FALSE);
}
/* Reset the DPB_next_free field of the DPB to 2, sothat when IBMBIO.COM is
* copied into this disk, the clusters will get allocated starting from 2.
*/
ModifyDPB(iDestDrive);
#endif // LATER
return (TRUE);
}
/*--------------------------------------------------------------------------*/
/* */
/* MakeSystemDiskette() - */
/* */
/*--------------------------------------------------------------------------*/
/* This routine is intended to mimic the functions of the SYS command
* under MSDOS: to transfer a version of the operating system from a source
* disk to a destination such that the destination will be bootable.
*
* The requirements of the source disk is that it contain:
*
* 1) a command processor (COMMAND.COM)
* 2) a default set of device drivers (IBMBIO.COM)
* 3) an operating system (IBMDOS.COM)
* 4) a boot sector appropriate to the device drivers
*
* The requirements for the destination disk are either:
*
* 1) first two directory entries are empty
* 2) the first N clusters free where N = ceil (size IBMBIO/secPerClus)
* 3) there is enough room on the disk for IBMBIO/IBMDOS/COMMAND
*
* - or -
*
* 1) the first two directory entries are IBMBIO.COM and IBMDOS.COM
* or IO.SYS and MSDOS.SYS
* 2) the first N clusters are alloced to these files where N is defined
* above
* 3) there is enough room on the disk for IBMBIO/IBMDOS/COMMAND after
* deleting the IBMBIO/IBMDOS/COMMAND on the disk.
*
* Inputs:
* iDestDrive 0-based drive number of formatted drive
* for destination.
* bEmptyFloppy : TRUE if the floppy is empty; Useful when
* the floppy is just formatted; No need to check if
* it is Sysable;
* Returns: 0 Successful transferral of boot sector and files
* <> 0 error code.
*/
BOOL
APIENTRY
MakeSystemDiskette(
WORD iDestDrive,
BOOL bEmptyFloppy
)
{
INT i;
HANDLE hFileBuff; /* Buffer to read in file contents etc., */
LPSTR lpFileBuff;
CHAR DestFileName[CSYSFILES][SYSFILENAMELEN];
CHAR szTemp1[SYSFILENAMELEN];
CHAR szTemp2[SYSFILENAMELEN];
WORD nSource;
nSource = (WORD)GetBootDisk();
if (!HasSystemFiles(nSource)) {
LoadString(hAppInstance, IDS_SYSDISKNOFILES, szMessage, sizeof(szMessage));
MessageBox(hdlgProgress, szMessage, szTitle, MB_OK | MB_ICONSTOP);
bUserAbort = TRUE;
return FALSE;
}
if (iDestDrive == nSource) {
LoadString(hAppInstance, IDS_SYSDISKSAMEDRIVE, szMessage, sizeof(szMessage));
MessageBox(hdlgProgress, szMessage, szTitle, MB_OK | MB_ICONSTOP);
bUserAbort = TRUE;
return FALSE;
}
/* Initialize variables for cleanup. */
hFileBuff = NULL;
lpFileBuff = NULL;
/* Flush the DOS buffers. */
DiskReset();
if (!(hFileBuff = LocalAlloc(LHND, (DWORD)BUFFSIZE)))
return (1);
lpFileBuff = LocalLock(hFileBuff);
for (i=0; i < (CSYSFILES - 1); i++) {
/* Create the destination file names */
lstrcpy((LPSTR)&DestFileName[i][0], (LPSTR)SysFileNamePtr[i]);
DestFileName[i][0] = (BYTE)('A' + iDestDrive);
}
/* Copy just the Command.COM without any path name */
lstrcpy((LPSTR)DestFileName[2], "X:\\");
lstrcat((LPSTR)DestFileName[2], (LPSTR)SysNameTable[0][2]);
DestFileName[2][0] = (BYTE)('A' + iDestDrive);
/* Check if it is an empty floppy; If so, there is no need to check if it
* is 'SYSable'. It is bound to be 'Sysable'. So, skip all the checks and
* go ahead with copying the sys files.
*/
if (!bEmptyFloppy) {
/* Check if the Destination floppy is SYS-able */
if (!IsSYSable(nSource, iDestDrive, DestFileName, lpFileBuff))
goto MSDErrExit;
/* Did the user abort? */
if (WFQueryAbort())
goto MSDErrExit;
}
/* Copy files */
bCopyReport = FALSE;
DisableFSC();
for (i=0; i < CSYSFILES; i++) {
/* Copy all files except command.com with sys attributes */
AnsiToOem(SysFileNamePtr[i], szTemp1);
AnsiToOem(DestFileName[i], szTemp2);
/* Make sure the destination file is deleted first */
SetFileAttributes(szTemp2, ATTR_ALL);
WFRemove(szTemp2);
// copy code preserves the attributes
if (FileCopy(szTemp1, szTemp2))
goto MSDErrExit2;
if (WFQueryAbort())
goto MSDErrExit2;
}
if (EndCopy()) // empty the copy queue
goto MSDErrExit2;
EnableFSC();
/* Normal Exit. */
LocalUnlock(hFileBuff);
LocalFree(hFileBuff);
return FALSE; // success
MSDErrExit2:
EnableFSC();
MSDErrExit:
CopyAbort(); // Purge any copy commands in copy queue
LocalUnlock(hFileBuff);
LocalFree(hFileBuff);
return TRUE; // failure
}