windows-nt/Source/XPSP1/NT/shell/osshell/version/instfile.c
2020-09-26 16:20:57 +08:00

477 lines
15 KiB
C

#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);
}