Scott Hanselman

Brute force check to ensure access to the TEMP directory in order to use the XmlSerializer

December 18, '04 Comments [5] Posted in ASP.NET | Web Services | XmlSerializer
Sponsored By

If you want to use the XmlSerilializer, you'll (ASPNET user) need write access to the Windows/Temp folder. Otherwise you may see this as the temporary assembly fails to be saved:

File or assembly name zmp0husw.dll, or one of its dependencies, was
not found.
Description: An unhandled exception occurred during the execution of
the current web request. Please review the stack trace for more
information about the error and where it originated in the code.

Exception Details: System.IO.FileNotFoundException: File or assembly
name zmp0husw.dll, or one of its dependencies, was not found.

Internally, there are Security Demands, to see if you have the "right" from a Code Access Security point of view, but noone actually CHECKS to see if you have the ACL rights.

So, here's a brute force way to find out, once per AppDomain, to check if you have access. I reflectored into XmlSerializer to find out what they were doing to find our what path to write to. They were using GetTempPath from kernel32.dll, so we could PInvoke as well. (Update: However, Kevin Dente points out that Path.GetTempPath() will do the PInvoke for you. I was mirroring XmlSerializer's code, but as long as we do the same thing in essense, we're OK. Edits below, line numbers changed. Thanks Kevin!)

    1 public sealed class SomeUtilityThingieClass
2 {
3 const string ErrorMessage = "We neede write access to: {0}";
6
7 private static bool tempFileAccess = false;
8 private static object tempFileAccessLock = new object();
9
10 public static bool EnsureTempFileAccess()
11 {
12 if (tempFileAccess == true)
13 {
14 return true;
15 }
16
17 if(tempFileAccess == false)
18 {
19 lock(tempFileAccessLock)
20 {
21 if(tempFileAccess == false)
22 {
29 string tempFile = Path.Combine(Path.GetTempPath(),"WriteTest.txt");
30 try
31 {
32 using(StreamWriter file = File.CreateText(tempFile))
33 {
34 file.Write("This is a test to see if we can write
to this TEMP folder and consequently make XmlSerializer
assemblies without trouble.");
35 }
36 }
37 catch(System.IO.IOException ex)
38 {
39 throw new System.IO.IOException(
string.Format(ErrorMessage,tempFullPath),ex);
40 }
41 catch(UnauthorizedAccessException ex)
42 {
43 throw new UnauthorizedAccessException(
string.Format(ErrorMessage,tempFullPath),ex);
44 }
45
46 if (File.Exists(tempFile))
47 {
48 File.Delete(tempFile);
49 }
50 tempFileAccess = true;
51 }
52 }
53 }
54 return tempFileAccess;
55 }
56 }

Once the write has worked once, we catch the success in a static and return that. If multiple threads get in here together, only one will make it past the lock. The others will wait. After we learn of success or failure from the first thread, the threads that were waiting for the lock will check the (now change) tempFileAccess boolean, and find it changed. The file write will happen only once per AppDomain. Rather than calling "throw;" or not catching the exceptions at all, I add a little extra polite message and wrap and throw. They won't know the line number that went wrong, but they WILL get a nice message.

The most interesting stuff to the beginner is the classic check/lock/doublecheck threadsafety. Note also that we have an explicit object tempFileAccessLock that exists ONLY for the purposes of locking.

 

 

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by SherWeb
Saturday, 18 December 2004 15:27:57 UTC
You don't actually have to P/Invoke to get the temp path. Path has a static property called "TempPath" that gives it to you. In fact, path also has a methd called GetTempFileName, which you also may be able to use to simplify the code a bit (although I'm not sure what exception it throws if you don't have permissions to the temp folder).
Saturday, 18 December 2004 17:32:13 UTC
I'm looking at the reflectored code for Path...I can see that there is Path.GetTempPath which PInvokes, as I did. So, I definitely don't need to PInvoke. I was copying what the Serializer did internally.

However, I don't see a Path static property on System.IO.Path...? I think you meant (and were right) about GetTempPath(), though.
Scott Hanselman
Saturday, 18 December 2004 19:06:12 UTC
Oops, sorry, spaced out there. You're right, it's a static GetTempPath() method, not a TempPath property (though it seems to me it would make more sense as a property - but whatever).
Saturday, 18 December 2004 22:21:56 UTC
Where I work, "windows\temp" is locked out by security policy for EVERYONE. I don't agree with this, but have no say. 5000+ people in company. I don't understand a need to access this directory anyways. One should use the user's "temp" directory in most every case.

Are you saying (here) that you need to run independently of any user account?
Stephan Hodges
Saturday, 18 December 2004 23:28:02 UTC
Steve,

No, what I'm saying is that:

1. there is a temporary assembly created when one reflects over an object with the XmlSerializer.
2. that assembly is saved in a directory that is one of 4 places (see MSDN) but likely c:\windows\temp.
3. the ASPNET account has no User Profile (You can't login as ASPNET on the desktop), so it will be %WINDIR%\temp, and write access is needed
4. the error you get if the running account doesn't have access is obscure at best.
5. This code is a per-appdomain check to see if one has access.
Scott Hanselman
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.