/* * Implementation of VERSION.DLL - File Installer routines * * Copyright 1996,1997 Marcus Meissner * Copyright 1997 David Cuthbert */ #include #include #include #include "windef.h" #include "winver.h" #include "wine/winestring.h" #include "winerror.h" #include "heap.h" #include "lzexpand.h" #include "xmalloc.h" #include "debugtools.h" DEFAULT_DEBUG_CHANNEL(ver); /****************************************************************************** * * void ver_dstring( * char const * prologue, * char const * teststring, * char const * epilogue ) * * This function will print via dprintf[_]ver to stddeb the prologue string, * followed by the address of teststring and the string it contains if * teststring is non-null or "(null)" otherwise, and then the epilogue * string followed by a new line. * * Revision history * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) * Original implementation as dprintf[_]ver_string * 05-Jul-1997 Dave Cuthbert (dacut@ece.cmu.edu) * Fixed problem that caused bug with tools/make_debug -- renaming * this function should fix the problem. * 15-Feb-1998 Dimitrie Paun (dimi@cs.toronto.edu) * Modified it to make it print the message using only one * dprintf[_]ver call. * *****************************************************************************/ static void ver_dstring( char const * prologue, char const * teststring, char const * epilogue ) { TRACE("%s %p (\"%s\") %s\n", prologue, (void const *) teststring, teststring ? teststring : "(null)", epilogue); } /****************************************************************************** * * int testFileExistence( * char const * path, * char const * file ) * * Tests whether a given path/file combination exists. If the file does * not exist, the return value is zero. If it does exist, the return * value is non-zero. * * Revision history * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) * Original implementation * *****************************************************************************/ static int testFileExistence( char const * path, char const * file ) { char filename[1024]; int filenamelen; OFSTRUCT fileinfo; int retval; fileinfo.cBytes = sizeof(OFSTRUCT); strcpy(filename, path); filenamelen = strlen(filename); /* Add a trailing \ if necessary */ if(filenamelen) { if(filename[filenamelen - 1] != '\\') strcat(filename, "\\"); } else /* specify the current directory */ strcpy(filename, ".\\"); /* Create the full pathname */ strcat(filename, file); if(OpenFile(filename, &fileinfo, OF_EXIST) == HFILE_ERROR) retval = 0; else retval = 1; return retval; } /****************************************************************************** * * int testFileExclusiveExistence( * char const * path, * char const * file ) * * Tests whether a given path/file combination exists and ensures that no * other programs have handles to the given file. If the file does not * exist or is open, the return value is zero. If it does exist, the * return value is non-zero. * * Revision history * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) * Original implementation * *****************************************************************************/ static int testFileExclusiveExistence( char const * path, char const * file ) { char filename[1024]; int filenamelen; OFSTRUCT fileinfo; int retval; fileinfo.cBytes = sizeof(OFSTRUCT); strcpy(filename, path); filenamelen = strlen(filename); /* Add a trailing \ if necessary */ if(filenamelen) { if(filename[filenamelen - 1] != '\\') strcat(filename, "\\"); } else /* specify the current directory */ strcpy(filename, ".\\"); /* Create the full pathname */ strcat(filename, file); if(OpenFile(filename, &fileinfo, OF_EXIST | OF_SHARE_EXCLUSIVE) == HFILE_ERROR) retval = 0; else retval = 1; return retval; } /***************************************************************************** * * VerFindFile() [VER.8] * Determines where to install a file based on whether it locates another * version of the file in the system. The values VerFindFile returns are * used in a subsequent call to the VerInstallFile function. * * Revision history: * 30-May-1997 Dave Cuthbert (dacut@ece.cmu.edu) * Reimplementation of VerFindFile from original stub. * ****************************************************************************/ /* VerFindFile32A [VERSION.5] */ DWORD WINAPI VerFindFileA( UINT flags, LPCSTR lpszFilename, LPCSTR lpszWinDir, LPCSTR lpszAppDir, LPSTR lpszCurDir, UINT *lpuCurDirLen, LPSTR lpszDestDir, UINT *lpuDestDirLen ) { DWORD retval; char curDir[256]; char destDir[256]; unsigned int curDirSizeReq; unsigned int destDirSizeReq; retval = 0; /* Print out debugging information */ TRACE("called with parameters:\n" "\tflags = %x", flags); if(flags & VFFF_ISSHAREDFILE) TRACE(" (VFFF_ISSHAREDFILE)\n"); else TRACE("\n"); ver_dstring("\tlpszFilename = ", lpszFilename, ""); ver_dstring("\tlpszWinDir = ", lpszWinDir, ""); ver_dstring("\tlpszAppDir = ", lpszAppDir, ""); TRACE("\tlpszCurDir = %p\n", lpszCurDir); if(lpuCurDirLen) TRACE("\tlpuCurDirLen = %p (%u)\n", lpuCurDirLen, *lpuCurDirLen); else TRACE("\tlpuCurDirLen = (null)\n"); TRACE("\tlpszDestDir = %p\n", lpszDestDir); if(lpuDestDirLen) TRACE("\tlpuDestDirLen = %p (%u)\n", lpuDestDirLen, *lpuDestDirLen); /* Figure out where the file should go; shared files default to the system directory */ strcpy(curDir, ""); strcpy(destDir, ""); if(flags & VFFF_ISSHAREDFILE) { GetSystemDirectoryA(destDir, 256); /* Were we given a filename? If so, try to find the file. */ if(lpszFilename) { if(testFileExistence(destDir, lpszFilename)) { strcpy(curDir, destDir); if(!testFileExclusiveExistence(destDir, lpszFilename)) retval |= VFF_FILEINUSE; } else if(lpszAppDir && testFileExistence(lpszAppDir, lpszFilename)) { strcpy(curDir, lpszAppDir); retval |= VFF_CURNEDEST; if(!testFileExclusiveExistence(lpszAppDir, lpszFilename)) retval |= VFF_FILEINUSE; } } } else if(!(flags & VFFF_ISSHAREDFILE)) { /* not a shared file */ if(lpszAppDir) { char systemDir[256]; GetSystemDirectoryA(systemDir, 256); strcpy(destDir, lpszAppDir); if(lpszFilename) { if(testFileExistence(lpszAppDir, lpszFilename)) { strcpy(curDir, lpszAppDir); if(!testFileExclusiveExistence(lpszAppDir, lpszFilename)) retval |= VFF_FILEINUSE; } else if(testFileExistence(systemDir, lpszFilename)) { strcpy(curDir, systemDir); retval |= VFF_CURNEDEST; if(!testFileExclusiveExistence(systemDir, lpszFilename)) retval |= VFF_FILEINUSE; } } } } curDirSizeReq = strlen(curDir) + 1; destDirSizeReq = strlen(destDir) + 1; /* Make sure that the pointers to the size of the buffers are valid; if not, do NOTHING with that buffer. If that pointer is valid, then make sure that the buffer pointer is valid, too! */ if(lpuDestDirLen && lpszDestDir) { if(*lpuDestDirLen < destDirSizeReq) { retval |= VFF_BUFFTOOSMALL; if (*lpuDestDirLen) { lstrcpynA(lpszDestDir, destDir, *lpuDestDirLen); } } else strcpy(lpszDestDir, destDir); *lpuDestDirLen = destDirSizeReq; } if(lpuCurDirLen && lpszCurDir) { if(*lpuCurDirLen < curDirSizeReq) { retval |= VFF_BUFFTOOSMALL; if (*lpuCurDirLen) { lstrcpynA(lpszCurDir, curDir, *lpuCurDirLen); } } else strcpy(lpszCurDir, curDir); *lpuCurDirLen = curDirSizeReq; } TRACE("ret = %lu (%s%s%s)\n", retval, (retval & VFF_CURNEDEST) ? "VFF_CURNEDEST " : "", (retval & VFF_FILEINUSE) ? "VFF_FILEINUSE " : "", (retval & VFF_BUFFTOOSMALL) ? "VFF_BUFFTOOSMALL " : ""); ver_dstring("\t(Exit) lpszCurDir = ", lpszCurDir, ""); if(lpuCurDirLen) TRACE("\t(Exit) lpuCurDirLen = %p (%u)\n", lpuCurDirLen, *lpuCurDirLen); else TRACE("\t(Exit) lpuCurDirLen = (null)\n"); ver_dstring("\t(Exit) lpszDestDir = ", lpszDestDir, ""); if(lpuDestDirLen) TRACE("\t(Exit) lpuDestDirLen = %p (%u)\n", lpuDestDirLen, *lpuDestDirLen); return retval; } /* VerFindFile32W [VERSION.6] */ DWORD WINAPI VerFindFileW( UINT flags,LPCWSTR filename,LPCWSTR windir,LPCWSTR appdir, LPWSTR curdir,UINT *pcurdirlen,LPWSTR destdir,UINT *pdestdirlen ) { UINT curdirlen, destdirlen; LPSTR wfn,wwd,wad,wdd,wcd; DWORD ret; wfn = HEAP_strdupWtoA( GetProcessHeap(), 0, filename ); wwd = HEAP_strdupWtoA( GetProcessHeap(), 0, windir ); wad = HEAP_strdupWtoA( GetProcessHeap(), 0, appdir ); wcd = HeapAlloc( GetProcessHeap(), 0, *pcurdirlen ); wdd = HeapAlloc( GetProcessHeap(), 0, *pdestdirlen ); ret = VerFindFileA(flags,wfn,wwd,wad,wcd,&curdirlen,wdd,&destdirlen); lstrcpynAtoW(curdir,wcd,*pcurdirlen); lstrcpynAtoW(destdir,wdd,*pdestdirlen); *pcurdirlen = strlen(wcd); *pdestdirlen = strlen(wdd); HeapFree( GetProcessHeap(), 0, wfn ); HeapFree( GetProcessHeap(), 0, wwd ); HeapFree( GetProcessHeap(), 0, wad ); HeapFree( GetProcessHeap(), 0, wcd ); HeapFree( GetProcessHeap(), 0, wdd ); return ret; } static LPBYTE _fetch_versioninfo(LPSTR fn,VS_FIXEDFILEINFO **vffi) { DWORD alloclen; LPBYTE buf; DWORD ret; alloclen = 1000; buf= xmalloc(alloclen); while (1) { ret = GetFileVersionInfoA(fn,0,alloclen,buf); if (!ret) { free(buf); return 0; } if (alloclen<*(WORD*)buf) { free(buf); alloclen = *(WORD*)buf; buf = xmalloc(alloclen); } else { *vffi = (VS_FIXEDFILEINFO*)(buf+0x14); if ((*vffi)->dwSignature == 0x004f0049) /* hack to detect unicode */ *vffi = (VS_FIXEDFILEINFO*)(buf+0x28); if ((*vffi)->dwSignature != VS_FFI_SIGNATURE) WARN("Bad VS_FIXEDFILEINFO signature 0x%08lx\n",(*vffi)->dwSignature); return buf; } } } static DWORD _error2vif(DWORD error) { switch (error) { case ERROR_ACCESS_DENIED: return VIF_ACCESSVIOLATION; case ERROR_SHARING_VIOLATION: return VIF_SHARINGVIOLATION; default: return 0; } } /****************************************************************************** * VerInstallFile32A [VERSION.7] */ DWORD WINAPI VerInstallFileA( UINT flags,LPCSTR srcfilename,LPCSTR destfilename,LPCSTR srcdir, LPCSTR destdir,LPCSTR curdir,LPSTR tmpfile,UINT *tmpfilelen ) { LPCSTR pdest; char destfn[260],tmpfn[260],srcfn[260]; HFILE hfsrc,hfdst; DWORD attr,ret,xret,tmplast; LPBYTE buf1,buf2; OFSTRUCT ofs; TRACE("(%x,%s,%s,%s,%s,%s,%p,%d)\n", flags,srcfilename,destfilename,srcdir,destdir,curdir,tmpfile,*tmpfilelen ); xret = 0; sprintf(srcfn,"%s\\%s",srcdir,srcfilename); if (!destdir || !*destdir) pdest = srcdir; else pdest = destdir; sprintf(destfn,"%s\\%s",pdest,destfilename); hfsrc=LZOpenFileA(srcfn,&ofs,OF_READ); if (hfsrc==HFILE_ERROR) return VIF_CANNOTREADSRC; sprintf(tmpfn,"%s\\%s",pdest,destfilename); tmplast=strlen(pdest)+1; attr = GetFileAttributesA(tmpfn); if (attr!=-1) { if (attr & FILE_ATTRIBUTE_READONLY) { LZClose(hfsrc); return VIF_WRITEPROT; } /* FIXME: check if file currently in use and return VIF_FILEINUSE */ } attr = -1; if (flags & VIFF_FORCEINSTALL) { if (tmpfile[0]) { sprintf(tmpfn,"%s\\%s",pdest,tmpfile); tmplast = strlen(pdest)+1; attr = GetFileAttributesA(tmpfn); /* if it exists, it has been copied by the call before. * we jump over the copy part... */ } } if (attr == -1) { char *s; GetTempFileNameA(pdest,"ver",0,tmpfn); /* should not fail ... */ s=strrchr(tmpfn,'\\'); if (s) tmplast = s-tmpfn; else tmplast = 0; hfdst = OpenFile(tmpfn,&ofs,OF_CREATE); if (hfdst == HFILE_ERROR) { LZClose(hfsrc); return VIF_CANNOTCREATE; /* | translated dos error */ } ret = LZCopy(hfsrc,hfdst); _lclose(hfdst); if (((long) ret) < 0) { /* translate LZ errors into VIF_xxx */ switch (ret) { case LZERROR_BADINHANDLE: case LZERROR_READ: case LZERROR_BADVALUE: case LZERROR_UNKNOWNALG: ret = VIF_CANNOTREADSRC; break; case LZERROR_BADOUTHANDLE: case LZERROR_WRITE: ret = VIF_OUTOFMEMORY; /* FIXME: correct? */ break; case LZERROR_GLOBALLOC: case LZERROR_GLOBLOCK: ret = VIF_OUTOFSPACE; break; default: /* unknown error, should not happen */ ret = 0; break; } if (ret) { LZClose(hfsrc); return ret; } } } xret = 0; if (!(flags & VIFF_FORCEINSTALL)) { VS_FIXEDFILEINFO *destvffi,*tmpvffi; buf1 = _fetch_versioninfo(destfn,&destvffi); if (buf1) { buf2 = _fetch_versioninfo(tmpfn,&tmpvffi); if (buf2) { char *tbuf1,*tbuf2; UINT len1,len2; len1=len2=40; /* compare file versions */ if ((destvffi->dwFileVersionMS > tmpvffi->dwFileVersionMS)|| ((destvffi->dwFileVersionMS==tmpvffi->dwFileVersionMS)&& (destvffi->dwFileVersionLS > tmpvffi->dwFileVersionLS) ) ) xret |= VIF_MISMATCH|VIF_SRCOLD; /* compare filetypes and filesubtypes */ if ((destvffi->dwFileType!=tmpvffi->dwFileType) || (destvffi->dwFileSubtype!=tmpvffi->dwFileSubtype) ) xret |= VIF_MISMATCH|VIF_DIFFTYPE; if (VerQueryValueA(buf1,"\\VarFileInfo\\Translation",(LPVOID*)&tbuf1,&len1) && VerQueryValueA(buf2,"\\VarFileInfo\\Translation",(LPVOID*)&tbuf2,&len2) ) { /* irgendwas mit tbuf1 und tbuf2 machen * generiert DIFFLANG|MISMATCH */ } free(buf2); } else xret=VIF_MISMATCH|VIF_SRCOLD; free(buf1); } } if (xret) { if (*tmpfilelen