0

Before you downvote the question, you need to know that I have spent some time on this and I am somewhat confused. I have looked through several answers but almost all of them have comments saying this is a good solution or will not work in a lot of cases.

The problem, ultimately, is that the program closes after excepting the error.

Examples below.

C# Test if user has write access to a folder

Some Code:

public static void CcnDirSearch(string sDir) // This is the directory I am passing c:\\ to here
{
   try
   {
     foreach (string file in Directory.EnumerateFiles(sDir, "*.*", SearchOption.AllDirectories)) // File is supposed to hold the individual file...
     {
       bool isSystem = ((File.GetAttributes(file) & FileAttributes.System) == FileAttributes.System);

          if (HasFolderWritePermission(file) == true  && isSystem == false && file != @"c:\$Recycle.Bin\S-1-5-18")
          {
             Console.WriteLine(file);
             using (var stream = File.OpenRead(file))
             {
              // I am checking file here  with my own functions.
             }
          }

      }
    }
    catch (UnauthorizedAccessException ex)
    {
        //User cannot access directory
        Console.WriteLine("I AM NOT CONTINUING " + ex.Message);         
    }
    catch (System.Exception excpt)
    {
       // Console.WriteLine("I AM AN ERROR!!!\n");
        Console.WriteLine(excpt.Message);            
    }
}

The Error I am receiving is

I AM NOT CONTINUING Access to the path 'c:\$Recycle.Bin\S-1-5-18' is denied.

Then my program exists.

And yes I have looked this up and all of the examples thus far seem to not cover this completely.

Lewis86
  • 511
  • 6
  • 15
LUser
  • 1,127
  • 4
  • 23
  • 39
  • 1
    Side note: `if (HasFolderWritePermission(file) && !isSystem && file != @"c:\$Recycle.Bin\S-1-5-18")` is more readable – Dmitry Bychenko Sep 11 '18 at 09:34
  • 3
    Have you stepped through the code and pinpointed where exactly the error is thrown? Might well be it already fails on your enumerate or getting the attributes. – AsheraH Sep 11 '18 at 09:34
  • yes I have.... and why the downvote? it fails at Directory.EnumerateFiles when it gets to the Recycle bin directory. – LUser Sep 11 '18 at 10:40
  • The answer provided there is awful and doesn't solve anyone's problem. – LUser Sep 11 '18 at 10:59
  • @ApertureSecurity then please provide some more detail. What is it that you want to do? Search all folders *but* the recycle bin? Search all folders *including* the recycle bin? If you can elaborate on what you want to achieve, I can give a better answer – Thomas Flinkow Sep 11 '18 at 11:02
  • My code is supposed to listing all dirs in c:\\ and ignoring files that would throw aaccess denied warning. Looking at the file properties the recycle bin is owned by system which is why (I think ) I cannot enumerate all files. – LUser Sep 11 '18 at 11:04
  • @ApertureSecurity good to know. Please give me about 5-10 minutes to update my answer. If everything works as planned, I can provide you some code that does exactly that task without the encountering the same problem :) – Thomas Flinkow Sep 11 '18 at 11:12
  • @ApertureSecurity please see my edited answer, and please see whether the code works for you. – Thomas Flinkow Sep 11 '18 at 11:34

3 Answers3

4

Continuing execution after catching an exception

The problem ultimately is that the program closes after excepting the error.

So your real problem is that the program exits after the catch-block?

Well, the reason your problem does not continue execution if an exception is caught is that you don't have any code after the last catch-block, so the program exits because there is nothing more to do.

See this simplified example of your code

public static void Main(string[] args)
{
    try
    {
        throw new UnauthorizedAccessException("Cannot access this path.");
    }
    catch (UnauthorizedAccessException e)
    {
        Console.WriteLine($"I am not continuing. Reason: {e.Message}.");
    }

    // Important, otherwise we would simply exit.
    Console.WriteLine("Or look, I am in fact continuing :)");     }

which prints

I am not continuing. Reason: Cannot access this path.

Or look, I am in fact continuing :)

so, in order for your program to not exit after the catch-block(s) and continue execution, you need to put some code after them.


Getting all files in a folder and its subfolders

As the OP has explained in a comment, the real real problem is they want to iterate over every file in a particular folder and its subfolders, but don't want to stop if they are not authorized to access a file or folder.

Therefore, I came up with the following method

public static IEnumerable<string> GetAllAccessibleFilesIn(string rootDirectory, string searchPattern = "*.*")
{
    List<string> files = new List<string>();

    try
    {
        files.AddRange(Directory.GetFiles(rootDirectory, searchPattern, SearchOption.TopDirectoryOnly));

        foreach (string directory in Directory.GetDirectories(rootDirectory))
        {
            files.AddRange(GetAllAccessibleFilesIn(directory, searchPattern));
        }
    }
    catch (UnauthorizedAccessException)
    {
        // Don't do anything if we cannot access a file.
    }

    return files;
}

which, when used like

IEnumerable<string> allFiles = GetAllAccessibleFilesIn(@"C:\");

yields every accessible file in either C:\ or any of its subfolders.

Community
  • 1
  • 1
Thomas Flinkow
  • 4,845
  • 5
  • 29
  • 65
  • This works. I had to add a foreach (var file in files) { //do something with file } return files; Thank you – LUser Sep 11 '18 at 12:12
  • @ApertureSecurity great to hear that. However, I recommend that you do not add the `foreach(var file in files) { /* do something with file */ }` in the `GetAllAccessibleFilesIn` method, but rather do it like this: `foreach(var file in GetAllAccessibleFilesIn(@"C:\")) { /* do something with file */ }`... – Thomas Flinkow Sep 11 '18 at 12:14
  • 1
    you're right . I did this and it's fine. There was a small typo you are still listing GetAllFilesIn in the function . I think you meant for GetAllAccessibleFilesIn . – LUser Sep 11 '18 at 12:47
  • 1
    @ApertureSecurity thank you for pointing that out. Fixed the typo. Great to hear that it works for you, thanks for making this the accepted answer. – Thomas Flinkow Sep 11 '18 at 12:48
0

This is a bit long to add as a comment. Your issue probably lies in Directory.EnumerateFiles()

Here is a link which can help you:

DirectoryInfo.EnumerateFiles(...) causes UnauthorizedAccessException (and other exceptions)

Also, it would be great to add Pathtoolongexception

And have checks for Directory.Exists etc..

You can probably have a boolean method like below:

bool CanAccessDirectory(string sDir)
{
if (!Directory.Exists(sDir)) return false;

try {
IEnumerable files = Directory.EnumerateFiles(sDir, "*.*",         SearchOption.AllDirectories);
}
catch(UnauthorizedException exc) 
{ return false; } 

catch(PathtooLongException ex)
{ return false; }

// we reach here
return true;
}
Gauravsa
  • 6,330
  • 2
  • 21
  • 30
0

There are so many ways that file access can go wrong. Having checks in your code doesn't help as files can be deleted and permissions can be changed between your check and the file read.

I've included the correct checks to demonstrate how to check access control. Do note that you send a file to a directory check.

public static bool HasFileReadPermission(string fullPathToFile)
{
    var accessControlList = File.GetAccessControl(fullPathToFile);
    if (accessControlList == null)
        return false;

    var accessRules = accessControlList.GetAccessRules(true, true,
        typeof(System.Security.Principal.SecurityIdentifier));

    bool allow = false, deny = false;
    foreach (FileSystemAccessRule rule in accessRules)
    {
        if ((FileSystemRights.Read & rule.FileSystemRights) == 0)
            continue;

        if (rule.AccessControlType == AccessControlType.Allow)
            allow = true;
        if (rule.AccessControlType == AccessControlType.Deny)
            deny = true;

    }

    return allow || !deny;
}

public static bool HasDirectoryReadPermission(string directory)
{
    var accessControlList = Directory.GetAccessControl(directory);
    if (accessControlList == null)
        return false;

    var accessRules = accessControlList.GetAccessRules(true, true,
        typeof(System.Security.Principal.SecurityIdentifier));

    bool allow = false, deny = false;
    foreach (FileSystemAccessRule rule in accessRules)
    {
        if ((FileSystemRights.Read & rule.FileSystemRights) == 0)
            continue;

        if (rule.AccessControlType == AccessControlType.Allow)
            allow = true;
        if (rule.AccessControlType == AccessControlType.Deny)
            deny = true;

    }

    return allow || !deny;
}

public static IEnumerable<string> GetAllAccessibleFilesIn(string rootDirectory, string searchPattern = "*.*")
{
    foreach (string directoryAndFileName in Directory.EnumerateFiles(rootDirectory, "*.*", SearchOption.AllDirectories))
    {
        var directory = Path.GetDirectoryName(directoryAndFileName);

        // Start by checking the directory
        if (!HasDirectoryReadPermission(directory))
            continue;

        var isSystem = (File.GetAttributes(directoryAndFileName) & FileAttributes.System) != 0;

        try
        {
            // Skip files that are system files or inaccessible
            if (!HasFileReadPermission(directoryAndFileName) || isSystem)
                continue;

            Console.WriteLine(directoryAndFileName);
            using (var stream = File.OpenRead(directoryAndFileName))
            {
                // I am checking file here  with my own functions.
            }
        }

        catch (UnauthorizedAccessException ex)
        {
            // Things can change, guard against errors
            Console.WriteLine("I AM NOT CONTINUING " + ex.Message);
        }
        catch (IOException ex)
        {
            // Other IO (file operation) exceptions
            // don't catch "Exception" as that can hide non-related errors.
            Console.WriteLine(ex.Message);
        }

    }
}
jgauffin
  • 99,844
  • 45
  • 235
  • 372