/* * EXTRACT.C borrowed from TWEX\wextract.c * * Has the CAB extraction capabilty for Code Downloader; uses FDI.LIB */ #include #include #include #include #include "cdl.h" #ifdef unix #include "unixfile.h" #endif /* unix */ // // single theaded access to the FDI lib static BOOL fCritCreated = FALSE; CRITICAL_SECTION g_mxsFDI; /* * W i n 3 2 O p e n ( ) * * Routine: Win32Open() * * Purpose: Translate a C-Runtime _open() call into appropriate Win32 * CreateFile() * * Returns: Handle to file on success * INVALID_HANDLE_VALUE on failure * * * BUGBUG: Doesn't fully implement C-Runtime _open() capability but it * BUGBUG: currently supports all callbacks that FDI will give us */ HANDLE Win32Open(char *pszFile, int oflag, int pmode ) { HANDLE FileHandle = INVALID_HANDLE_VALUE; BOOL fExists = FALSE; DWORD fAccess; DWORD fCreate; ASSERT( pszFile ); // BUGBUG: No Append Mode Support if (oflag & _O_APPEND) return( INVALID_HANDLE_VALUE ); // Set Read-Write Access if ((oflag & _O_RDWR) || (oflag & _O_WRONLY)) fAccess = GENERIC_WRITE; else fAccess = GENERIC_READ; // Set Create Flags if (oflag & _O_CREAT) { if (oflag & _O_EXCL) fCreate = CREATE_NEW; else if (oflag & _O_TRUNC) fCreate = CREATE_ALWAYS; else fCreate = OPEN_ALWAYS; } else { if (oflag & _O_TRUNC) fCreate = TRUNCATE_EXISTING; else fCreate = OPEN_EXISTING; } #ifdef unix UnixEnsureDir(pszFile); #endif /* unix */ //BUGBUG: seterrormode to no crit errors and then catch sharing violations // and access denied // Call Win32 FileHandle = CreateFile( pszFile, fAccess, FILE_SHARE_READ, NULL, fCreate, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE ); if (FileHandle == INVALID_HANDLE_VALUE && SetFileAttributes(pszFile, FILE_ATTRIBUTE_NORMAL)) FileHandle = CreateFile( pszFile, fAccess, FILE_SHARE_READ, NULL, fCreate, FILE_ATTRIBUTE_NORMAL, INVALID_HANDLE_VALUE ); return( FileHandle ); } /* * O p e n F u n c ( ) * * Routine: OpenFunc() * * Purpose: Open File Callback from FDI * * Returns: File Handle (small integer index into file table) * -1 on failure * */ int FAR DIAMONDAPI openfunc(char FAR *pszFile, int oflag, int pmode ) { int rc; HANDLE hf; ASSERT( pszFile ); //BUGBUG Spill File Support for Quantum? if ((*pszFile == '*') && (*(pszFile+1) != 'M')) { DEBUGTRAP("Spill File Support for Quantum Not Supported"); } hf = Win32Open(pszFile, oflag, pmode ); if (hf != INVALID_HANDLE_VALUE) { // SUNDOWN: typecast problem rc = PtrToLong(hf); } else { rc = -1; } return( rc ); } /* * R E A D F U N C ( ) * * Routine: readfunc() * * Purpose: FDI read() callback * */ UINT FAR DIAMONDAPI readfunc(INT_PTR hf, void FAR *pv, UINT cb) { int rc; ASSERT( pv ); if (! ReadFile((HANDLE)hf, pv, cb, (DWORD *) &cb, NULL)) rc = -1; else rc = cb; return( rc ); } /* * W r i t e F u n c ( ) * * Routine: WriteFunc() * * Purpose: FDI Write() callback * */ UINT FAR DIAMONDAPI writefunc(INT_PTR hf, void FAR *pv, UINT cb) { int rc; ASSERT( pv ); if (! WriteFile((HANDLE)hf, pv, cb, (DWORD *) &cb, NULL)) rc = -1; else rc = cb; // BUGBUG: implement OnProgress notification return( rc ); } /* * C l o s e F u n c ( ) * * Routine: CloseFunc() * * Purpose: FDI Close File Callback * */ int FAR DIAMONDAPI closefunc( INT_PTR hf ) { int rc; if (CloseHandle( (HANDLE)hf )) { rc = 0; } else { rc = -1; } return( rc ); } /* * S e e k F u n c ( ) * * Routine: seekfunc() * * Purpose: FDI Seek Callback */ long FAR DIAMONDAPI seekfunc( INT_PTR hf, long dist, int seektype ) { long rc; DWORD W32seektype; switch (seektype) { case SEEK_SET: W32seektype = FILE_BEGIN; break; case SEEK_CUR: W32seektype = FILE_CURRENT; break; case SEEK_END: W32seektype = FILE_END; break; } rc = SetFilePointer((HANDLE)hf, dist, NULL, W32seektype); if (rc == 0xffffffff) rc = -1; return( rc ); } /* * A d j u s t F i l e T i m e ( ) * * Routine: AdjustFileTime() * * Purpose: Change the time info for a file */ BOOL AdjustFileTime(INT_PTR hf, USHORT date, USHORT time ) { FILETIME ft; FILETIME ftUTC; if (! DosDateTimeToFileTime( date, time, &ft )) return( FALSE ); if (! LocalFileTimeToFileTime(&ft, &ftUTC)) return( FALSE ); if (! SetFileTime((HANDLE)hf,&ftUTC,&ftUTC,&ftUTC)) return( FALSE ); return( TRUE ); } /* * A t t r 3 2 F r o m A t t r F A T ( ) * * Translate FAT attributes to Win32 Attributes */ DWORD Attr32FromAttrFAT(WORD attrMSDOS) { //** Quick out for normal file special case if (attrMSDOS == _A_NORMAL) { return FILE_ATTRIBUTE_NORMAL; } //** Otherwise, mask off read-only, hidden, system, and archive bits // NOTE: These bits are in the same places in MS-DOS and Win32! // return attrMSDOS & ~(_A_RDONLY | _A_HIDDEN | _A_SYSTEM | _A_ARCH); } /* * A l l o c F u n c ( ) * * FDI Memory Allocation Callback */ FNALLOC(allocfunc) { void *pv; pv = (void *) CoTaskMemAlloc( cb ); DEBUGMSG("%d = ALLOC( %d )", (DWORD_PTR) pv, cb ); return( pv ); } /* * F r e e F u n c ( ) * * FDI Memory Deallocation Callback * XXX Return Value? */ FNFREE(freefunc) { ASSERT(pv); DEBUGMSG("FREE( %d )", pv ); CoTaskMemFree( pv ); } /* * D O G E T N E X T C A B ( ) * * Routine: doGetNextCab() * * Purpose: Get Next Cabinet in chain * * Returns: -1 * * BUGBUG: CLEANUP: STUB THIS OUT * BUGBUG: STUBBED OUT IN WEXTRACT - CHAINED CABINETS NOT SUPPORTED */ FNFDINOTIFY(doGetNextCab) { return( -1 ); } /*** updateCabinetInfo - update history of cabinets seen * * Entry: * psess - Session * pfdin - FDI info structurue * * Exit: * Returns 0; */ int updateCabinetInfo(PSESSION psess, PFDINOTIFICATION pfdin) { ASSERT(psess); // Don't need any of this! //** Save cabinet info //lstrcpy(psess->acab.achCabPath ,pfdin->psz3); //lstrcpy(psess->acab.achCabFilename ,pfdin->psz1); //lstrcpy(psess->acab.achDiskName ,pfdin->psz2); //psess->acab.setID = pfdin->setID; //psess->acab.iCabinet = pfdin->iCabinet; return 0; } /* * A P P E N D P A T H S E P A R A T O R ( ) * * Routine: appendPathSeparator() * * Purpose: Append a path separator only if necessary * * Returns: TRUE - Path Separator Added * FALSE No Path Separator added */ BOOL appendPathSeparator(char *pszPathEnd) { //** Add path separator if necessary if ((*pszPathEnd != '\0') && // Path is not empty (*pszPathEnd != chPATH_SEP1) && // Not a path separator (*pszPathEnd != chPATH_SEP2) && // Not a path separator (*pszPathEnd != chDRIVE_SEP) ) { // Not a drive separator #ifdef unix *(++pszPathEnd) = chPATH_SEP2; // Add Unix path separator #else *(++pszPathEnd) = chPATH_SEP1; // Add path separator #endif /* !unix */ *(++pszPathEnd) = '\0'; // Terminate path return TRUE; // Account for path separator } //** No separator added return FALSE; } /* * C A T D I R A N D F I L E ( ) * * Routine: catDirAndFile() * * Purpose: Concatenate a directory with a filename! * */ BOOL catDirAndFile( char *pszResult, int cbResult, char *pszDir, char *pszFile ) { int cch = 0; //** Handle directory if (!cbResult) return FALSE; pszResult[0] = '\0'; // No filespec, yet if (pszDir) cch = lstrlen(pszDir); // Get length of dir if (cch != 0) { // Have to concatenate path cbResult -= cch; // Account for dir if (cbResult <= 1) { //this check also accounts for the next one byte that could be added. return FALSE; } lstrcpy(pszResult,pszDir); // Copy destination dir to buffer //** Add path separator if necessary, adjust remaining size cbResult -= appendPathSeparator(&(pszResult[cch-1])); if (cbResult <= 0) { return FALSE; } } //** Append file name, using default if primary one not supplied if (*pszFile == '\0') { return( FALSE ); } cbResult -= lstrlen(pszFile); // Update remaining size if (cbResult <= 0) { return FALSE; } lstrcat(pszResult,pszFile); // Append file name //** Success return TRUE; } /* * IsExtracted * * Look for pszName in psess->pFileList and see if extracted * * Returns: * Success: TRUE, failure: FALSE */ static BOOL IsExtracted( PSESSION ps, LPCSTR pszName) { PFNAME CurName = ps->pFileList; ASSERT(pszName); ASSERT(CurName); // atleast one file needed // search for filename in list of files in this CAB do { if (lstrcmpi(pszName, CurName->pszFilename) == 0) { if (CurName->status == SFNAME_EXTRACTED) return TRUE; else return FALSE; } } while (CurName = CurName->pNextName); ASSERT(TRUE); // if here not found in list! return FALSE; } /* * NeedFile * * search for pszName in psess->pFilesToExtract (list of PFNAMEs) * Returns: * TRUE - need file, extract it * FALSE - don't need file, skip it * */ static BOOL NeedFile( PSESSION ps, LPCSTR pszName) { PFNAME CurName; ASSERT(pszName); if (IsExtracted(ps, pszName) ) return FALSE; if ( ps->flags & SESSION_FLAG_EXTRACT_ALL ) return TRUE; // search for filename in list of files needed for (CurName = ps->pFilesToExtract; CurName; CurName = CurName->pNextName){ ASSERT(CurName->pszFilename); if (lstrcmpi(CurName->pszFilename, pszName) == 0) return TRUE; } return FALSE; } /* * MarkExtracted * * Look for pszName in psess->pFileList and mark status = status_passed_in * really can be use to mark status as anything else as well (not just * extracted.) * * Returns: * Success: TRUE, failure: FALSE */ static BOOL MarkExtracted( PSESSION ps, LPCSTR pszName , DWORD status) { PFNAME CurName = ps->pFileList; ASSERT(pszName); ASSERT(CurName); // atleast one file needed // search for filename in list of files in this CAB do { if (lstrcmpi(pszName, CurName->pszFilename) == 0) { CurName->status = status; return TRUE; } } while (CurName = CurName->pNextName); ASSERT(TRUE); // if here not found in list! return FALSE; } /* * A d d F i l e ( ) * * Add a file to the list of files we have in the CAB file * * Singly linked list - items added at front */ static BOOL AddFile( PSESSION ps, LPCSTR pszName , long cb) { PFNAME NewName; ASSERT(pszName); if (!(ps->flags & SESSION_FLAG_ENUMERATE)) return TRUE; // Allocate Node NewName = (PFNAME) CoTaskMemAlloc(sizeof(FNAME) ); if (NewName == NULL) { DEBUGMSG("AddFile(): Memory Allocation of structure failed"); return( FALSE ); } // Allocate String Space NewName->pszFilename = (LPSTR) CoTaskMemAlloc(lstrlen(pszName) + 1); if (NewName->pszFilename == NULL) { DEBUGMSG("AddFile(): Memory Allocation of name failed"); CoTaskMemFree(NewName); return( FALSE ); } NewName->status = SFNAME_INIT; // Copy Filename lstrcpy( (char *)NewName->pszFilename, pszName ); // Link into list NewName->pNextName = ps->pFileList; ps->pFileList = NewName; ps->cFiles++; ps->cbCabSize += cb; return( TRUE ); } /* * f d i N o t i f y E x t r a c t() * * Routine: fdiNotifyExtract() * * Purpose: Principle FDI Callback in file extraction * * */ FNFDINOTIFY(fdiNotifyExtract) { int fh; PSESSION psess; psess = (PSESSION) pfdin->pv; switch (fdint) { case fdintCABINET_INFO: return updateCabinetInfo(psess,pfdin); case fdintCOPY_FILE: // BUGBUG: implement OnProgress? #ifdef unix UnixifyFileName(pfdin->psz1); #endif /* unix */ if (!catDirAndFile(psess->achFile, // Buffer for output filespec sizeof(psess->achFile), // Size of output buffer psess->achLocation, // Output directory pfdin->psz1)) { return -1; // Abort with error; } // always add the file (enumeration) if (! AddFile(psess, pfdin->psz1, pfdin->cb)) return( -1 ); // check if this is the file we are looking for if any if (!NeedFile(psess, pfdin->psz1)) return( 0 ); if (StrStrA(pfdin->psz1, "\\")) { // cab contains dir struct for this file // create struct on dest dir as well. char *pBaseFileName = NULL; char szDir[MAX_PATH]; LPSTR pchStart; LPSTR pchSlash = NULL; lstrcpy(szDir, psess->achFile); pchStart = szDir + lstrlen(psess->achLocation) + 1; while (*pchStart && (pchSlash = StrStrA(pchStart, "\\"))) { *pchSlash = '\0'; // don't care if this fails. may even already exist! CreateDirectory(szDir, NULL); *pchSlash = '\\'; pchStart = pchSlash +1; } } //** Do overwrite processing fh = openfunc( psess->achFile, _O_BINARY | _O_TRUNC | _O_RDWR | _O_CREAT, 0 ); return(fh); // -1 if error on open case fdintCLOSE_FILE_INFO: if (!catDirAndFile(psess->achFile, // Buffer for output filespec sizeof(psess->achFile), // Size of output buffer psess->achLocation, // Output directory pfdin->psz1)) { return -1; // Abort with error; } if (! AdjustFileTime( pfdin->hf, pfdin->date, pfdin->time )) { return( -1 ); } closefunc( (int) pfdin->hf ); if (! SetFileAttributes(psess->achFile, Attr32FromAttrFAT(pfdin->attribs))) return( -1 ); MarkExtracted(psess, pfdin->psz1, SFNAME_EXTRACTED); return(TRUE); case fdintPARTIAL_FILE: return( 0 ); case fdintNEXT_CABINET: return doGetNextCab(fdint,pfdin); default: DEBUGTRAP("fdiNotifyExtract(): Unknown Callback Type"); break; } return( 0 ); } #ifdef DEBUG /* * V E R I F Y C A B I N E T ( ) * * Routine: VerifyCabinet() * * Purpose: Check that cabinet is properly formed * * Returns: TRUE - Cabinet OK * FALSE - Cabinet invalid */ BOOL VerifyCabinet( PSESSION psess, LPCSTR lpCabName ) { HFDI hfdi; ERF erf; FDICABINETINFO cabinfo; INT fh; hfdi = FDICreate(allocfunc,freefunc,openfunc,readfunc,writefunc,closefunc,seekfunc,cpu80386,&erf); if (hfdi == NULL) { DEBUGMSG("VerifyCabinet(): FDICreate() Failed"); //BUGBUG Error Handling? return( FALSE ); } fh = openfunc((char FAR *)lpCabName, _O_BINARY | _O_RDONLY, 0 ); if (fh == -1) { DEBUGMSG("VerifyCabinet(): Open of Memory File Failed"); return( FALSE ); } if (FDIIsCabinet(hfdi, fh, &cabinfo ) == FALSE) { DEBUGMSG("VerifyCabinet(): FDIIsCabinet() Returned FALSE"); return( FALSE ); } if (cabinfo.cbCabinet != (long) psess->cbCabSize) { DEBUGMSG("VerifyCabinet(): cabinfo.cbCabinet != cbCabSize"); return( FALSE ); } if (cabinfo.hasprev || cabinfo.hasnext) { DEBUGMSG("VerifyCabinet(): Cabinet is chained"); return( FALSE ); } if (closefunc( fh ) == -1) { DEBUGMSG("VerifyCabinet(): Closefunc() Failed"); return( FALSE ); } if (FDIDestroy(hfdi) == FALSE) { DEBUGMSG("VerifyCabinet(): FDIDestroy() Failed"); return( FALSE ); } return( TRUE ); } #endif /* DEBUG */ /* * E X T R A C T ( ) * * Routine: Extract() * * Parameters: * * PSESSION ps = session information tied to this extract session * * IN params * ps->pFilesToExtract = linked list of PFNAMEs that point to * upper case filenames that need extraction * * ps->flags SESSION_FLAG_ENUMERATE = whether need to enumerate * files in CAB (ie. create a pFileList * ps->flags SESSION_FLAG_EXTRACTALL = all * * OUT params * ps->pFileList = global alloced list of files in CAB * caller needs to call DeleteExtractedFiles * to free memory and temp files * * * LPCSTR lpCabName = name of cab file * * * Returns: * S_OK: sucesss * * */ HRESULT Extract(PSESSION ps, LPCSTR lpCabName ) { HFDI hfdi; BOOL fExtractResult = FALSE; HRESULT hr = S_OK; if (ps->flags & SESSION_FLAG_EXTRACTED_ALL) { // already extracted all files in this CAB // nothing to do! return S_OK; } memset(&(ps->erf), 0, sizeof(ERF)); if (ps->flags & SESSION_FLAG_ENUMERATE) { ps->cbCabSize = 0; } // don't enumerate when pFileList already pre-populated ASSERT( (!(ps->flags & SESSION_FLAG_ENUMERATE)) || (!ps->pFileList)); { HRESULT hrOut = NOERROR; if (fCritCreated == FALSE) { fCritCreated = TRUE; InitializeCriticalSection(&g_mxsFDI); } EnterCriticalSection(&g_mxsFDI); // Extract the files hfdi = FDICreate(allocfunc,freefunc,openfunc,readfunc,writefunc,closefunc,seekfunc,cpu80386, &(ps->erf)); if (hfdi == NULL) { // Error value will be retrieved from ps->erf hrOut = STG_E_UNKNOWN; goto done; } fExtractResult = FDICopy(hfdi, (char FAR *)lpCabName, "", 0, fdiNotifyExtract, NULL, (void *) ps ); if (FDIDestroy(hfdi) == FALSE) { // Error value will be retrieved from ps->erf hrOut = STG_E_UNKNOWN; } done: LeaveCriticalSection(&g_mxsFDI); // leave now if this failed! if (hrOut != NOERROR) { return hrOut; } } if (fExtractResult && (!ps->erf.fError)) return S_OK; hr = HRESULT_FROM_WIN32(GetLastError()); if (SUCCEEDED(hr)) { // not a win32 failure but a cabinet failure // convert CABINET failure to disk full or STG_E_UNKNOWN. // On win95 writefile failing with disk full is not // setting the last error correctly if (ps->erf.fError && (ps->erf.erfOper == FDIERROR_TARGET_FILE)) hr = HRESULT_FROM_WIN32(ERROR_DISK_FULL); else hr = STG_E_UNKNOWN; } return hr; } /* * D E L E T E E X T R A C T E D F I L E S ( ) * * Routine: DeleteExtractedFiles() * * Purpose: Delete the files that were extracted * into the temporary directory * FREE all the memory in pFileList * make pFileList = NULL. * * Paramaters: * psess - Pointer to Session Structure containing * all state about this extraction session * * Returns: None */ VOID DeleteExtractedFiles(PSESSION psess) { PFNAME rover = psess->pFileList; PFNAME roverprev; char szBuf[MAX_PATH]; ASSERT(psess); DEBUGMSG("Deleting Extracted Files"); while (rover != NULL) { // skip if this is not a tmp file if ( rover->status == SFNAME_EXTRACTED) { // Get full filename if (catDirAndFile(szBuf, MAX_PATH, psess->achLocation, rover->pszFilename)) { if (SetFileAttributes(szBuf, FILE_ATTRIBUTE_NORMAL)) DeleteFile(szBuf); } } CoTaskMemFree(rover->pszFilename); roverprev = rover; // save for free'ing current rover below rover = rover->pNextName; CoTaskMemFree(roverprev); } psess->pFileList = NULL; // prevent use after deletion! }