Difference between "Network path not found" and "Access denied" in C# - c#

In my C# .NET 2.0 application I'm accessing network paths, and I'd like to be able to tell the difference between paths that don't exist, and paths which do exist but for which I don't have access rights. I tried doing the following:
try
{
string[] contents = Directory.GetFileSystemEntries( path );
}
catch( Exception e )
{
if( e is DirectoryNotFoundException )
MessageBox.Show( "Path not found" );
else
MessageBox.Show( "Access denied" );
}
This works fine for local paths, but for network paths, the exception is always System.IO.IOException, regardless of the reason for the error. The exception Message field shows a different message depending on whether the path exists or not, so clearly the information is available at some point, but I can't get to it. Is there a way to differentiate between "path not found" and "access denied" for network paths?
Edit: So, in case anyone else wants the quick solution to this, as suggested by henrik, and incorporating peSHIr's advice, here is what you can do:
try
{
// Issue this call just to find out whether the path exists
// We don't care about the result
string[] contents = Directory.GetFileSystemEntries( path );
// If we get this far then the path exists.
}
catch( IOException e )
{
uint error = (uint)Marshal.GetHRForException( e );
if( error == (uint)0x80070041 ) // ERROR_NETWORK_ACCESS_DENIED
{
// The poor deluded user doesn't have access rights
this.SuperProprietaryTechniqueForGettingAccessRights();
}
else
{
// Hah! The idiot user specified a path that doesn't exist!
// Chastise them severely, like all good GUI should.
MessageBox.Show( "NO! BAD USER!" );
}
}
catch
{
// Swallow all other types of exception - we only made the call
// to find out whether the path exists.
}

First, I would not catch (Exception), but do something like this:
try {
string[] contents = Directory.GetFileSystemEntries(path);
}
catch(DirectoryNotFoundException)
{
MessageBox.Show("Path not found");
}
catch(IOException)
{
MessageBox.Show("Could not access path");
}
But the real question is: why would you actually need to know the difference, if all you do is show a message box to the user with a generic error message?

Also, unfortunately, the path may exist and you don't have access, and the result is that the path appears non-existent to your account. Many companies modify the special privs on a folder to remove "intermediary" folders from visibility, but they are still present.
For example, consider the path \srv\test1\test2\test3\
If you modify the advanced security for the folder named "test2" above, you can prevent users from navigating into it. Attempts to open the folder will result in a not found exception, and viewing the parent folder "test1" will not show the folder as present. However, if you provide the full path above and have sufficient privileges on the folder named "test3" then the folder will open.
Just something to consider, for all intents and purposes the folder doesn't exist for the user, however, if you offer the ability for a user to specify a full path then you should keep the above in mind (e.g. allow users to reach paths by full name, because it's possible they lack privs to view/list/read/see an intermediary, and the error returned doesn't indicate that it's a security restriction, so preventing access to the full path because of an intermediary appeared non-existent coudl be considered bugged app logic.)
Hope that helps.

You can call Marshal.GetHRForException to get more detailed information as in:
if (Marshal.GetHRForException(e) == unchecked((int)0x800704cf)) // ERROR_NETWORK_UNREACHABLE
See WinError.h for error codes.

Related

How to save data to network path on .NET?

I'm really struggling with saving data to my local network NAS (a Synology DS214 if that matters).
I need to store some files in my network folders after creating them in another part of my program, but I haven't been able to handle the authentication/permissions properly.
My code atm is this:
WrapperImpersonationContext WIContext =
new WrapperImpersonationContext("\\\\DiskStation", "admin", "admin");
try
{
WIContext.Enter();
// code to select the final path simplified.
string fileName = "file.txt";
string originalPath = Environment.GetFolderPath(
Environment.SpecialFolder.MyDocuments);
originalPath= Path.Combine(new string[] {originalPath, fileName});
string finalPath = "\\\\DiskStation\\Virtual\\DestFolder";
if (!Directory.Exists(finalPath))
{
// This goes well for whatever reason
Directory.CreateDirectory(finalPath);
}
finalPath = Path.Combine(new string[] {finalPath, fileName});
// This fails for wrong username/password
File.Move(originalPath, finalPath);
} catch (Exception ex)
{
// Exception showing simplified here
MessageBox.Show(ex.ToString());
throw;
} finally
{
WIContext.Leave();
}
The code used for the WrapperImpersonationContext I found here:
WindowsImpersonationContext made easy
As written in my code when I try to move the file I get an UnauthorizedAccessException: Access to the path is denied. I also tried to create a new file in the network folder with the same results.
While looking at the Michiel Vanotegem's code linked above, I discovered that I get an authentication error calling the LogonUser function (error code 1326 that gets me a Win32Exception (0x80004005): The user name or password is incorrect).
I tried to use the WNetUseConnection function looking at this and this pages but while I get no error from the function (after substituting it in the Michiel code), when I try to move the file I get the same UnauthorizedAccessException: Access to the path is denied.
I also tried to fiddle with the domain passed to the Impersonation Wrapper but I couldn't seem to make it work. I feel like I'm missing something... Can someone kindly point me to the right direction or help me with this issue?
Ty all who contributes in advance.
Edit 15/12/2017 11:52: I discovered that if I try to rerun the LogonUser function immediately after the first error I get a different exception (error 87 Win32Exception (0x80004005): The parameter is incorrect)
I followed on #LennartStoop suggestion, so I enclosed my code in a using block instead of a try finally using the code I borrowed from this answer:
using (NetworkConnection netConn =
new NetworkConnection("\\\\DiskStation", new NetworkCredential("admin", "admin")))
{
// My code here
}
Using this I've been able to establish a connection the network folder and perform all the IO operation I needed so ty very much for the tip Lennart :)

Directory.Exist : identify types of errors

I use Directory.Exist to test a directory, but I can not distinguish types of errors.
I would like to distinguish if the directory doesn't exist or access is not allowed. I saw this link C# Test if user has write access to a folder but it's quite long and complicated. There is not an easier way?
You can use the Exist method to check whether the directory exists or not. Like this
if(Directory.Exists(directory)) {
// directory exists
try
{
// and is accessible by user...
File.GetAccessControl(filePath);
return true;
}
catch (UnauthorizedAccessException)
{
// but is unable to be accessed...
return false;
}
} else {
// directory doesn't exist, so no file accessible.
}
This is a bit easy and understand code for you. Each of the method is having its own commentary for you.

How to check if path is valid without using try-catch?

I want to check if a folder exists and if not then create it. But I don't know if the path supplied will even valid. When the path is not valid the following happens.
string path = "this is an invalid path";
if (!Directory.Exists(path))
Directory.CreateDirectory(path); //Exception thrown here
If you supply an invalid path, it will throw a DirectoryNotFoundException exception.
How can I stop this exception from occurring? I don't want to use a try-catch. I want to detect that this exception will occur even before the exception happens.
Use Directory.Exists method to check if a folder exists
if(Directory.Exists(path))
{
//Directory exists
}
else
{
// doesn't exist
}
Remember to include System.IO;
The explanation for the failure of your code is that the path is invalid. The documentation says:
DirectoryNotFoundException
The specified path is invalid (for example, it is on an
unmapped drive).
Trying to predict in advance whether or not a directory can be created is a devil of a job. You'd need to account for security, OS name rules and limits, file system name rules and limits, and whether or not drives are mapped. And probably lots more concerns. I would not contemplate re-implementing what the system provides for free.
In any case, whilst you can call Directory.Exists, you do still need to allow for an exception being thrown. If the file system changes between the call to Directory.Exists and the subsequent call to Directory.CreateDirectory, then an exception will be raised. For example, if another process creates the directory that you are trying to create. Granted, this is a rather unlikely event, but it's perfectly possible.
In summary, the best option, by a distance, is to catch the exception. As the well known saying goes, it's better to ask for forgiveness than to ask for permission.
Why don't you want to catch (specific) Exceptions? It is considered as a good practice... anyway, these are my solutions without try/catch:
solution:
string path = "C:\test";
if (!Directory.Exists(path) && path.IndexOfAny(Path.GetInvalidFileNameChars()) == -1)
{
Directory.CreateDirectory(path);
}
solution:
string path = "C:\test";
var canCreate = true;
foreach (var c in path.Where(Path.GetInvalidFileNameChars().Contains))
{
canCreate = false;
}
if (canCreate && !Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
solution:
if (path.Any(c => Path.GetInvalidFileNameChars().Contains(c)) && !Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
Please, be aware that this code can still fail... e.g. think about SecurityExeption (do you have the credentionals to create a directory there?!?
Also be aware there is still a (little) chance that the directory has been created (by another process/thread/...) after your test with Exists() but before your call of CreateDirectory(). These are two calls and they are not atomic (together) when querying/modifying the file system.
You can also use Path.GetInvalidFileNameChars() to check whether the directory name supplied is valid for a new directory name:
// Directory.Exists will return false because path is not even valid
var path = "1:1 comparison?";
var firstBad = Path.GetInvalidFileNameChars().Cast<char?>()
.FirstOrDefault(c => path.Contains(c.Value));
if (firstBad != null)
Console.WriteLine("Char '{0}' is invalid for a directory name", firstBad.Value);
try this
string path = "this is an invalid path";
if (Path.IsPathRooted(path)
{
Directory.CreateDirectory(path);
}
I think the simplest way to test if a path is valid and the file exists without raising an exception in C# is to call the unmanaged function PathFileExists directly in shlwapi.dll.
[DllImport("shlwapi.dll", EntryPoint = "PathFileExistsW", SetLastError = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool PathFileExists([MarshalAs(UnmanagedType.LPTStr)]string pszPath);
Note that as David writes in his answer, you might nevertheless get an exception when you try to create the directory.
You can use a single if-else statement
if(!Directory.Exists(path))
Directory.CreateDirectory(path)
else
//Show user a custom error message

What is the best practice for ensuring permissions are correct during the launch of an ASP.NET application

I have an ASP.NET application which requires write access on the App_Data subfolder. The MSI used to deploy the application tries to set the permissions correctly, but in spite of this, it seems the permissions are sometimes wrong. Most of the application works fine without this permission. I would prefer that the application fails to start if the permissions are wrong.
What is the best practice for ensuring that the necessary permissions are correct for the IIS user context? Ideally I want to display some simple instructions for fixing whatever is wrong. And I want the message to appear in as many incorrect configurations as possible.
The following describes what I've tried so far, until I realised there's a probably a better or standard way.
I tried putting this in Application_Start()
protected void Application_Start(Object sender, EventArgs e)
{
// Assert permissions on writeable folders are correct
var permissionsChecker = new AppDataPermissionsChecker();
permissionsChecker.AssertFolderIsWriteable(
HttpContext.Current.Server.MapPath("~/App_Data"));
// remainder of Application_Start()...
}
where AppDataPermissionsChecker is defined as follows:
public class AppDataPermissionsChecker
{
private bool CanWriteAccessToFolder(string folderPath)
{
try
{
// Attempt to get a list of security permissions from the folder.
// This will raise an exception if the path is read only or do not have access to view the permissions.
DirectorySecurity directorySecurity = Directory.GetAccessControl(folderPath);
return true;
}
catch (UnauthorizedAccessException)
{
return false;
}
}
public void AssertFolderIsWriteable(string folderPath)
{
if (!Directory.Exists(folderPath))
throw new Exception(String.Format("The {0} folder does not exist.", folderPath));
if (!CanWriteAccessToFolder(folderPath))
throw new Exception(String.Format("The ASPNET user does not have "
+ "access to the {0} folder. Please ensure the ASPNET user has "
+ "read/write/delete access on the folder. See 'The App_Data folder' "
+ "here: http://msdn.microsoft.com/en-us/library/06t2w7da.aspx'",
folderPath));
}
}
I thought this would throw an ugly exception if the rights are incorrect (which is better than nothing), but in some situations I just get an HTTP Error 503.
I found this implementation of a diagnostics page which does exactly what I was looking for (and more besides).

How do I check whether File.Delete() will succeed without trying it, in C#?

In C#, System.IO.File.Delete(filePath) will either delete the specified file, or raise an exception. If the current user doesn't have permission to delete the file, it'll raise an UnauthorizedAccessException.
Is there some way that I can tell ahead of time whether the delete is likely to throw an UnauthorizedAccessException or not (i.e. query the ACL to see whether the current thread's identity has permission to delete the specified file?)
I'm basically looking to do:
if (FileIsDeletableByCurrentUser(filePath)) {
/* remove supporting database records, etc. here */
File.Delete(filePath);
}
but I have no idea how to implement FileIsDeletableByCurrentUser().
The problem with implementing FileIsDeletableByCurrentUser is that it's not possible to do so. The reason is the file system is a constantly changing item.
In between any check you make to the file system and the next operation any number of events can and will happen. Including ...
Permissions on the file could change
The file could be deleted
The file could be locked by another user / process
The USB key the file is on could be removed
The best function you could write would most aptly be named FileWasDeletableByCurrentUser.
Have you tried System.IO.File.GetAccessControl(filename) it should return a FileSecurity with information about the permissions for that file.
Strictly speaking, an UnauthorizedAccessException means that the path is a directory, so you can use a System.IO.Path.GetFileName(path) type command and catch the argument exception.
But if you want a more holistic solution, use System.IO.File.GetAccessControl as mentioned by Dale Halliwell
As stated above.
Lookup the file permissions and compare with the user who is running the application.
You could always use this aproach as well
bool deletemyfile()
{
try
{
...delete my file
return true;
}
catch
{
return false;
}
}
if it returns false you know it failed if it returns true then.. it worked and file is gone. Not sure what you're after exactly but this was the best I could think of
Of course you can check ReadOnly flags using System.IO and probably ACL security on the file combined with the current user too, but like Mehrdad writes in his comment it would never be full-proof in all cases. So you would need exception handling for the exceptional case at all times (even if its just a top-level catch-all, that logs/shows an "unexpected problem" and kills your application).
You should get the access control list (ACL) of that file.
But this doesn't necessarily mean you could actually delete it because the readonly flag could still be set or another program has locked the file.
Seems like it would be easier to do things in the order of:
Get whatever information you need about the file in order to do the other parts (delete database data, etc)
Try to delete the file
If you successfully delete the file, then carry out the rest of the "cleanup" work. If you don't successfully delete it, return/handle the exception, etc.

Categories

Resources