Scott Hanselman

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

December 18, 2004 Comment on this post [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
Hosting By
Hosted in an Azure App Service
December 18, 2004 19:27
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).
December 18, 2004 21:32
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.
December 18, 2004 23:06
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).
December 19, 2004 2:21
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?
December 19, 2004 3:28
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.

Comments are closed.

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