Archive: GetTempFileName bug on NT4


GetTempFileName bug on NT4
I'm using NSIS 2.0b3, and when testing on NT4 SP3, I found a problem where my plugin DLL occasionally mysteriously failed to load.

On closer inspection, it turned out to be that GetTempFileName was failing, with an 'Access Denied' status. On NT4, if GetTempFileName generates a name like "C:\TEMP\NST1.tmp", and that directory already exists, it will fail. (Regular files are simply deleted, but directories are not.)

I've worked around it by replacing the call to GetTempFileName in exec.c with this:


BOOL MyGetTempFileName(LPCTSTR path, LPCTSTR prefix, UINT unique, LPTSTR name)
{
UINT retryCount = 100;

while(retryCount--)
if(GetTempFileName(path, prefix, unique, name))
return TRUE;

return FALSE;
}

Can you please explain exactly when GetTempFileName fails? Which directory should not exist?


Ok, this is happening when trying to create the plugins directory, to extract a plugin dll. The EW_GETTEMPFILENAME opcode gets a temp filename like this:

GetTempFileName(temp_directory,"nst",0,textout))

On NT4, subsequent calls to this API will generate filenames with ascending sequence numbers, like this:

C:\TEMP\nst1.tmp
C:\TEMP\nst2.tmp
C:\TEMP\nst3.tmp
etc..

Now, say that C:\TEMP\nst3.tmp does not get deleted for some reason. (e.g. setup gets interrupted.) After a reboot, the sequence numbers will start again at 1. If GetTempFileName is called 3 times, it will try to create "C:\TEMP\nst3.tmp" again.

On later versions of Windows, if nst3.tmp exists, GetTempFileName would skip to the next sequence number, and try to create nst4.tmp. However on NT4, if the file exists, and it is a directory (which in this case it is), it will try to create nst3.tmp, and fail with an Access Denied, because it is already there. The result is that the plugin does not get extracted, and fails to load.

If you call GetTempFileName again, it will try the next sequence number, "C:\TEMP\nst4.tmp", which will succeed. So in my fix above, I just call GetTempFileName repeatedly until it works, and bail out after 100 iterations to prevent a possible hang.

Hope this clarifies it. If not, let me know :)


From MSDN:

If uUnique is zero, the function attempts to form a unique file name using the current system time. If the file already exists, the number is increased by one and the functions tests if this file already exists. This continues until a unique filename is found; the function creates a file by that name and closes it. Note that the function does not attempt to verify the uniqueness of the file name when uUnique is nonzero.
Even though, I'll check.

Yep, it seems to work as described in MSDN if it is a regular file. However, if it is a directory (which it is, in this case), and it's NT4, it doesn't work.


This is probably strongly related to the "bug" part of my current thread "MUI installer error on NT4"... in that, I already have the accumulating junk (including folders) problem, which then escalates to "execution failure" on NT4. ;)


There seems to be a bug in the GetTempFileName API. The unique file check (inside the API) does not always work. If it does not generate a unique file, the API failes.

A work-around will be added later today.

RDaneel: Can you try to add the code in the start post and check whether this solves your problem?


Joost -

I dropped the following code in, replacing the EW_GETTEMPFILENAME case with:


case EW_GETTEMPFILENAME:
{
char *textout=var0;
int n = 100;
while (n--)
if (GetTempFileName(temp_directory,"nst",0,textout))
return 0;
g_flags.exec_error++;
*textout=0;
}
return 0;


The behavior is only slightly modified - NOT fixed. When doing the install-with-reboot followed immediately by an install again (still on NT4), it now gets one complete cycle without any problems... but on the third install (so we are saying install-with-reboot ... install-with-reboot ... install-attempt -> error), we get the same error with "\\ioSpecial.ini" and the abort-retry-ignore dialog. With the original version of the temp-file code, it fails on the SECOND install.

I do still go along with the idea that this has something to do with temp files/folders... it just wasn't quite this simple. :(


A question for you and kickik: when I was looking at the project settings in VS6 prior to doing the new build (based on my BETA "3a" sources that came with that release), I noticed that the SINGLE-threaded C libraries are being used... I am not sure I would be brave enough to build this way but still spawn and execute additional threads!:eek:

I wonder if this has anything to do with that reported SMP problem where the user never followed up ("[ 676056 ] Currupted Exe's with SMP machines")? Threading problems are notorious for not showing up until you actually run on a multi-processor machine, given that most of us still develop and test on uni-processor. ;)

Let me know if I can run any more tests for you, but be aware of one thing - the makensis.exe that I built is ~3KB larger than the "release" BETA 3 version! I run VS6 with SP5 AND the latest Platform SDK, so something about this environment is causing the difference, even though I only changed the above source and no project settings.

The size difference is not a problem. Maybe it's the Processor Pack or something like that.

Can you please add a messagebox in .onInit and check the contents of $PLUGINSDIR (add the InitPlugsinsDir command first)?


RDaneel, can you post your ICQ/MSN # so we can try fix this problem?


Or drop by IRC.

Processor Pack:
http://msdn.microsoft.com/vstudio/do...ck/default.asp

NSIS doesn't use runtime libraries only Windows API functions.


I have uploaded a compiled version of makensis.exe with some new fixes. It removes the pluginsdir when rebooting and GetTempFileName will be called in a loop.

Download it using NSIS Update and post the results :) Please try it with some folders left in your temp dir too.


Joost/kichik -

I don't have ICQ/MSN/AIM/etc IDs! :D

However, just for you guys I have installed a copy of Trillian, and would actually go ahead and communicate through one of these mediums - my take on it is that IRC requires the least "registration" on my part, but any relatively specific tips/suggestions would be appreciated. :)

kicjik - thanks for the "Processor Pack" link... I had never noticed that before (I had sort of wondered how one got "support" for these instructions without using an Intel development product).

Joost - I did the NSIS Update you requested (the "latest, clean" option) and get the following error when I try to build an installer:

Error: could not resolve label "t_init" in function "un.Initialize_____Plugins"

Remember, I was starting with a BETA "3a" installation except for the "looping" change from yesterday in exec.c - which appears to have been incorporated, as you said... what else should I be doing?


You don't need to register to use IRC, just download a client like mIRC and you can chat with us.

Do other example scripts work fine? Can you attach the script?


ChatZilla (installed with my Mozilla 1.3) seems to work just fine - talking to the bot SuperPimp...

Anyway, things look ugly regarding that NSIS build-installer-time error I reported in my last post... my script compiles fine with the released Beta 3 version, but with today's CVS version, it does NOT. What is more disturbing (to me, anyway) is that relatively minor random changes in the script cause the error to change:

could not resolve label "t_init" in function "un.Initialize_____Plugins"

changes to messed-up forms of other labels in the either MY script or your [MUI] scripts. Really. :weird:

What is happening is that AFTER the Uninstall section is processed, NSIS then spits out

Processed 1 file, writing output:
Adding plug-ins initializing function... setting uninstall mode to true
Done!


immediately before a complaint about some script label... I think that the error above is about the label "reboot_init" from the MUI scripts. I can make the error shift to complaining about a label in one of my macros being instantiated in an uninstaller function, ALSO with characters dropped from the front. :(

To answer your question, other simpler scripts work fine - your examples or some test scripts of mine. I will attach my script since you ask, but it is 353 lines and refers [of course] to an EXE and a DLL of mine. ;)


Hmmm... that didn't seem to attach anything - I will try again. :)


I think I found the problem... well, not what causes it, but I have it down to a single script statement that causes NSIS to mess up its compilation:

File /r "d:\Help"

where "d:\Help" is a folder that I expect to be recursively included in the installer, and then expanded to "$OUTDIR\Help". This works fine (compilation and execution) with Beta "3a", but with the current CVS Beta 4 causes the spurious

Error: could not resolve label "xxx" in function "Initialize_____Plugins"

error where the xxx is dependent on the rest of the script - which is suggestive of memory/pointer/length corruption. :(

I have included a simplified version of my script showing the failure - comment out the "File /r ..." line and all is well, try to compile with it in, and...


Thanks. I'll have a look at it later today. Please stay at the IRC channel, so we can chat when I'm back.


Ummm... sure. But do remember that I am in a GMT-8 time zone, which means that it is 2am right now, and I was planning on a bit of sleep (although I don't mind eating breakfast at my computer)... :D

All of this means that I will be back (and on IRC) around 1730-1800 GMT.


Think I fixed them all in the latest CVS version. Please check and report.

It always amuses me how bullet proof to uninitialized memory problems my computer is and how bad Java can screw one's initializing skills ;)