/****************************************************************************/ /* */ /* 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 = NULL; LPSTR lpFileBuff = NULL; OFSTRUCT OFInfo; HANDLE hFileBuff = NULL; /* Initialise the source filename pointers */ SysFileNamePtr[0] = &BIOSfile[0]; SysFileNamePtr[1] = &DOSfile[0]; SysFileNamePtr[2] = &COMMANDfile[0]; /* 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 (_stricmp(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); if (lpStr) 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 }