#include "verpriv.h" #include #include "diamondd.h" #include "mydiam.h" #ifdef __cplusplus extern "C" { #endif /* ** Prototypes */ // For the time being, private APIS exported INT LZCreateFileW( LPWSTR, DWORD, DWORD, DWORD, LPWSTR); VOID LZCloseFile( INT); #ifdef __cplusplus } #endif BOOL FileInUse(LPWSTR lpszFilePath, LPWSTR lpszFileName); DWORD MakeFileName(LPWSTR lpDst, LPWSTR lpDir, LPWSTR lpFile, int cchDst); typedef struct tagVS_VERSION { WORD wTotLen; WORD wValLen; WORD wType; WCHAR szSig[16]; VS_FIXEDFILEINFO vInfo; } VS_VERSION; typedef struct tagLANGANDCP { WORD wLanguage; WORD wCodePage; } LANGANDCP; WCHAR szTrans[] = TEXT("\\VarFileInfo\\Translation"); WCHAR szTempHdr[] = TEXT("temp."); /* The routines in here will find a file on a path, and an environment * variable. The constants _MAX_PATH and need to be defined * by the including module, plus the constant WINDOWS should * be defined if this is to be used in Windows, so that lstrcmpi * and lstrlen will not be compiled */ VOID Ver3IToA(LPWSTR lpwStr, int n) { int nShift; WCHAR cTemp; for (nShift=8; nShift>=0; nShift-=4, ++lpwStr) { if ((cTemp = (WCHAR)((n>>nShift)&0x000f)) >= 10) *lpwStr = (WCHAR)('A' + cTemp - 10); else *lpwStr = (WCHAR)('0' + cTemp ); } *lpwStr = 0; } /* Convert a DOS error into an error flag */ DWORD FileErrFlag(int err) { switch (err) { case 0x05: return (VIF_ACCESSVIOLATION); case 0x20: return (VIF_SHARINGVIOLATION); default: return (0); } } /* Create the given file with default flags; global nFileErr will * receive any DOS error; returns -1 on error, otherwise the DOS * file handle. */ HANDLE VerCreateFile(LPWSTR lpszFile) { HANDLE hFile; hFile = CreateFile(lpszFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); return (hFile); } VOID VerClose(HANDLE hW32File) { CloseHandle(hW32File); } #define MyAlloc(x) ((WCHAR *)LocalAlloc(LMEM_FIXED, x)) #define MyFree(x) LocalFree((HANDLE)(x)) LPVOID MyGetFileVersionInfo(LPWSTR lpFileName) { WCHAR *pInfo; WORD wLen = 2048; TryAgain: if (!(pInfo=MyAlloc(wLen))) goto Error1; if (!GetFileVersionInfo(lpFileName, 0L, wLen, pInfo)) goto Error2; if (wLen < *(WORD *)pInfo) { wLen = *(WORD *)pInfo; MyFree(pInfo); goto TryAgain; } return (pInfo); Error2: MyFree(pInfo); Error1: return (NULL); } HINSTANCE hLz32; DWORD cLz32Load; typedef INT (APIENTRY *tLZInit)( INT ); typedef INT (APIENTRY *tLZOpenFileW)(LPWSTR, LPOFSTRUCT, WORD ); typedef INT (APIENTRY *tLZCreateFileW)(LPWSTR, DWORD, DWORD, DWORD, LPWSTR); typedef VOID (APIENTRY *tLZClose)( INT ); typedef VOID (APIENTRY *tLZCloseFile)( INT ); typedef LONG (APIENTRY *tLZCopy)( INT, INT ); tLZInit pLZInit; tLZOpenFileW pLZOpenFileW; tLZCreateFileW pLZCreateFileW; tLZClose pLZClose; tLZCloseFile pLZCloseFile; tLZCopy pLZCopy; DWORD APIENTRY VerInstallFileW( DWORD wFlags, LPWSTR lpszSrcFileName, LPWSTR lpszDstFileName, LPWSTR lpszSrcDir, LPWSTR lpszDstDir, LPWSTR lpszCurDir, LPWSTR lpszTmpFile, PUINT puTmpFileLen ) { WCHAR szSrcFile[_MAX_PATH]; WCHAR szDstFile[_MAX_PATH]; WCHAR szCurFile[_MAX_PATH]; DWORD dwRetVal = 0L, dwSrcAttr; WORD wDirLen; LONG lCopy; HANDLE hW32Out; int i, fIn, fDosOut; WCHAR szCompFile[_MAX_PATH]; CHAR szOemFile[_MAX_PATH]; int iOemString; BOOL bDefaultCharUsed; BOOL DiamondFile; if (!cLz32Load) { hLz32 = LoadLibraryW(L"LZ32.DLL"); if (!hLz32) { return (VIF_CANNOTLOADLZ32); } pLZOpenFileW = (tLZOpenFileW) GetProcAddress(hLz32, "LZOpenFileW"); pLZCreateFileW = (tLZCreateFileW) GetProcAddress(hLz32, "LZCreateFileW"); pLZInit = (tLZInit) GetProcAddress(hLz32, "LZInit"); pLZCopy = (tLZCopy) GetProcAddress(hLz32, "LZCopy"); pLZClose = (tLZClose) GetProcAddress(hLz32, "LZClose"); pLZCloseFile = (tLZCloseFile) GetProcAddress(hLz32, "LZCloseFile"); if (!(pLZOpenFileW && pLZInit && pLZCopy && pLZClose && pLZCreateFileW && pLZCloseFile)) { FreeLibrary(hLz32); return (VIF_CANNOTLOADLZ32); } if (InterlockedExchangeAdd(&cLz32Load, 1) != 0) { // Multiple threads are attempting to LoadLib // Free one here. FreeLibrary(hLz32); } } LogData("inst", __LINE__, (DWORD)puTmpFileLen); /* LZ Open the source for reading */ MakeFileName(szSrcFile, lpszSrcDir, lpszSrcFileName, ARRAYSIZE(szSrcFile)); dwRetVal = InitDiamond(); if (dwRetVal) { return (dwRetVal); } if((fIn=pLZCreateFileW(szSrcFile, GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING, szCompFile)) < 0) { dwRetVal |= VIF_CANNOTREADSRC; goto doReturn; } LogData("inst", __LINE__, (DWORD)puTmpFileLen); /* * now we don't have any unicode interface for Diamond API. we need to convert * it to OEM charset string. we will use the converted string instead of of.szPathName. */ DiamondFile = FALSE; iOemString = WideCharToMultiByte( CP_OEMCP, // Oem Codepage 0, // no option flag szCompFile, // Unicode string -1, // should be NULL terminated szOemFile, // Oem string ARRAYSIZE(szOemFile),// Oem string buffer size NULL, // use nls default char &bDefaultCharUsed ); if( ( iOemString != 0 ) && // should succeed conversion. ( iOemString <= OFS_MAXPATHNAME ) && // should be <= 128 for OpenFile() API. ( bDefaultCharUsed == FALSE ) // the def. char should not be contain. ) { DiamondFile = IsDiamondFile(szOemFile); } if (DiamondFile) { pLZCloseFile(fIn); } LogData("inst", __LINE__, (DWORD)puTmpFileLen); /* If the dest file exists and is read only, return immediately; * the calling app must change the attributes before continuing * In the library version, we assume the file is in use if it exists * and we are in a VM; check all other possible errors and then return, * so the calling app can override our determination of "in use" * on a second call, along with all other problems */ wDirLen = (WORD)MakeFileName(szDstFile, lpszDstDir, lpszDstFileName, ARRAYSIZE(szDstFile)); lstrcpyn(szSrcFile, szDstFile, ARRAYSIZE(szSrcFile)); if (!HIWORD(dwSrcAttr=GetFileAttributes(szSrcFile))) { LogData("inst", __LINE__, (DWORD)puTmpFileLen); if (LOWORD(dwSrcAttr)&0x01) { LogData("inst", __LINE__, (DWORD)puTmpFileLen); dwRetVal |= VIF_WRITEPROT; goto doCloseSrc; } } /* If this is a force install and there is a temp file name from a * previous call to this function, use that as the temp file name */ LogData("inst", __LINE__, (DWORD)puTmpFileLen); if ((wFlags&VIFF_FORCEINSTALL) && *lpszTmpFile) { LogData("inst", __LINE__, (DWORD)puTmpFileLen); LogData("fnam", (DWORD)lpszDstDir, (DWORD)lpszTmpFile); MakeFileName(szSrcFile, lpszDstDir, lpszTmpFile, ARRAYSIZE(szSrcFile)); LogData("inst", __LINE__, (DWORD)puTmpFileLen); LogData("srcf", (DWORD)szSrcFile, *(LPDWORD)szSrcFile); if (!HIWORD(GetFileAttributes(szSrcFile))) { LogData("inst", __LINE__, (DWORD)puTmpFileLen); goto doCheckDstFile; } } /* Determine a file name that is not in use; try names of the form: * temp.nnn where nnn is a three digit hex number. If we get to * 0xfff, we have a serious file system problem. Create the file. */ LogData("inst", __LINE__, (DWORD)puTmpFileLen); lstrcpy(szSrcFile+wDirLen, szTempHdr); for (i=0; ; ++i) { Ver3IToA(szSrcFile+wDirLen+lstrlen(szTempHdr), i); LogData("inst", __LINE__, (DWORD)puTmpFileLen); if (HIWORD(GetFileAttributes(szSrcFile))) break; if (i > 0xfff) { dwRetVal |= VIF_CANNOTCREATE; goto doCloseSrc; } } /* Copy the file, and fill in appropriate errors */ LogData("inst", __LINE__, (DWORD)puTmpFileLen); if (DiamondFile) { LZINFO lzi; lCopy = ExpandDiamondFile(szOemFile, szSrcFile, FALSE, &lzi); LogData("inst", __LINE__, (DWORD)puTmpFileLen); } else { if ((hW32Out=VerCreateFile(szSrcFile)) == INVALID_HANDLE_VALUE) { dwRetVal |= VIF_CANNOTCREATE | FileErrFlag(GetLastError()); goto doCloseSrc; } LogData("inst", __LINE__, (DWORD)puTmpFileLen); fDosOut = pLZInit((INT)((DWORD_PTR)hW32Out)); lCopy = pLZCopy(fIn, fDosOut); pLZClose(fDosOut); } LogData("inst", __LINE__, (DWORD)puTmpFileLen); switch (lCopy) { case LZERROR_BADINHANDLE: case LZERROR_READ: case LZERROR_BADVALUE: case LZERROR_UNKNOWNALG: dwRetVal |= VIF_CANNOTREADSRC; goto doDelTempFile; case LZERROR_BADOUTHANDLE: case LZERROR_WRITE: dwRetVal |= VIF_OUTOFSPACE; goto doDelTempFile; case LZERROR_GLOBALLOC: case LZERROR_GLOBLOCK: dwRetVal |= VIF_OUTOFMEMORY; goto doDelTempFile; default: break; } /* If the destination exists, check the versions of the two files, * and only copy if the src file is at least as new as the dst, and * they are the same type and in the same language and codepage */ doCheckDstFile: if (!HIWORD(dwSrcAttr)) { VS_VERSION *pSrcVer, *pDstVer; LANGANDCP *lpSrcTrans, *lpDstTrans; DWORD dwSrcNum, dwDstNum; DWORD dwSrcTrans, dwDstTrans; LogData("inst", __LINE__, (DWORD)puTmpFileLen); if (!(wFlags & VIFF_FORCEINSTALL) && (pDstVer=MyGetFileVersionInfo(szDstFile))) { LogData("inst", __LINE__, (DWORD)puTmpFileLen); if (!(pSrcVer=MyGetFileVersionInfo(szSrcFile))) { dwRetVal |= VIF_MISMATCH | VIF_SRCOLD; } else { LogData("inst", __LINE__, (DWORD)puTmpFileLen); if (pDstVer->vInfo.dwFileVersionMS>pSrcVer->vInfo.dwFileVersionMS || (pDstVer->vInfo.dwFileVersionMS==pSrcVer->vInfo.dwFileVersionMS && pDstVer->vInfo.dwFileVersionLS>pSrcVer->vInfo.dwFileVersionLS)) dwRetVal |= VIF_MISMATCH | VIF_SRCOLD; if (pDstVer->vInfo.dwFileType!=pSrcVer->vInfo.dwFileType || pDstVer->vInfo.dwFileSubtype!=pSrcVer->vInfo.dwFileSubtype) dwRetVal |= VIF_MISMATCH | VIF_DIFFTYPE; if (VerQueryValueW(pDstVer, szTrans, (LPVOID)&lpDstTrans, &dwDstNum) && VerQueryValueW(pSrcVer, szTrans, (LPVOID)&lpSrcTrans, &dwSrcNum)) { dwDstNum /= sizeof(DWORD); dwSrcNum /= sizeof(DWORD); for (dwDstTrans=0; dwDstTrans= dwSrcNum) { dwRetVal |= VIF_MISMATCH | VIF_DIFFLANG; break; } if (lpDstTrans[dwDstTrans].wLanguage == lpSrcTrans[dwSrcTrans].wLanguage) { /* OK if dst is CP0 and src is not UNICODE */ if (lpDstTrans[dwDstTrans].wCodePage==0 && lpSrcTrans[dwSrcTrans].wCodePage!=1200) break; if (lpDstTrans[dwDstTrans].wCodePage == lpSrcTrans[dwSrcTrans].wCodePage) break; } } } } LogData("inst", __LINE__, (DWORD)puTmpFileLen); MyFree(pSrcVer); } LogData("inst", __LINE__, (DWORD)puTmpFileLen); MyFree(pDstVer); } /* If there were no errors, delete the currently existing file */ LogData("inst", __LINE__, (DWORD)puTmpFileLen); if (FileInUse(szDstFile, lpszDstFileName)) { LogData("inst", __LINE__, (DWORD)puTmpFileLen); dwRetVal |= VIF_FILEINUSE; } if (!dwRetVal && !DeleteFile(szDstFile)) { dwRetVal |= VIF_CANNOTDELETE | FileErrFlag(GetLastError()); goto doDelTempFile; } } /* If there were no errors, rename the temp file (any existing file * should have been deleted by now). Otherwise, if we created a valid * temp file, then pass along the temp file name. */ LogData("inst", __LINE__, (DWORD)puTmpFileLen); if (dwRetVal) { DWORD wTemp; if (*puTmpFileLen > (wTemp=lstrlen(szSrcFile+wDirLen))) { lstrcpy(lpszTmpFile, szSrcFile+wDirLen); dwRetVal |= VIF_TEMPFILE; } else { dwRetVal |= VIF_BUFFTOOSMALL; DeleteFile(szSrcFile); } *puTmpFileLen = wTemp + 1; } else { /* Delete the currently existing file; this gets done before renaming * the temp file in case someone has tried to mess us up with a weird * directory name that would allow us to delete the newly installed * file. */ if (!(wFlags&VIFF_DONTDELETEOLD) && lpszCurDir && *lpszCurDir && lstrcmpi(lpszCurDir, lpszDstDir)) { MakeFileName(szCurFile, lpszCurDir, lpszDstFileName, ARRAYSIZE(szCurFile)); if (!HIWORD(GetFileAttributes(szCurFile)) && (FileInUse(szCurFile, lpszDstFileName) || !DeleteFile(szCurFile))) dwRetVal |= VIF_CANNOTDELETECUR | FileErrFlag(GetLastError()); } if (!MoveFile(szSrcFile, szDstFile)) { dwRetVal |= VIF_CANNOTRENAME | FileErrFlag(GetLastError()); doDelTempFile: DeleteFile(szSrcFile); } } doCloseSrc: if (!DiamondFile) { pLZCloseFile(fIn); } doReturn: LogData("inst", __LINE__, (DWORD)puTmpFileLen); TermDiamond(); return (dwRetVal); }