|
|
#include "verpriv.h"
#include <lzexpand.h>
#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<dwDstNum; ++dwDstTrans) { for (dwSrcTrans=0; ; ++dwSrcTrans) { if (dwSrcTrans >= 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); }
|