3

Before closing my question, please see what I checked and doesn't work at the end of this question


Question

I've been trying all sorts of SO answers and all of them brings me a an exception, besides many of them sounding overcomplicated for something that should be very simple.

How can I check if I have permission to enumerate a folder's content before doing it? (I don't want I try/catch because I'm traversing the file system and multiple try/catch affects performance on C#)

Whenever I try the following:

//where dir is a DirectoryInfo instance
foreach (FileInfo file in dir.EnumerateFiles())

I get this exception:

An unhandled exception of type 'System.UnauthorizedAccessException' occurred in mscorlib.dll
Additional information: O acesso ao caminho 'e:\$RECYCLE.BIN\S-1-5-18' foi negado.
translation:"Access to the path 'e:\$RECYCLE.BIN\S-1-5-18' was denied"


What doesn't work:

1:

When I do this, the code enters the if and throws the same exception when it hits the enumeration.

PermissionSet permissions = new PermissionSet(System.Security.Permissions.PermissionState.None);
permissions.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, dir.FullName));

if (permissions.IsSubsetOf(AppDomain.CurrentDomain.PermissionSet))

2:

The following raises an exception:

DirectorySecurity security = dir.GetAccessControl(AccessControlSections.All); 

Exception:

An unhandled exception of type 'System.Security.AccessControl.PrivilegeNotHeldException' occurred in mscorlib.dll
Additional information: O processo não possui o privilégio 'SeSecurityPrivilege' necessário para esta operação.
translation: The process doesn't have the 'SeSecurityPrivilege' privilege necessary for this operation.

And with:

    DirectorySecurity security = dir.GetAccessControl(AccessControlSections.None); 

Exception:

An unhandled exception of type 'System.UnauthorizedAccessException' occurred in mscorlib.dll
Additional information: Tentativa de execução de uma operação não autorizada.
translation: Attempting to execute an unauthorized operation

3:

Tried:

DirectorySecurity security = Directory.GetAccessControl(dir.FullName);

Same exception as above.

Daniel Möller
  • 84,878
  • 18
  • 192
  • 214
  • [Ignore folders/files when Directory.GetFiles() is denied access](https://stackoverflow.com/q/172544/8967612) – 41686d6564 stands w. Palestine Feb 12 '20 at 18:21
  • 1
    If you are only interested to skip system folders then you can use a DirectoryInfo and check its Attributes property – Steve Feb 12 '20 at 18:31
  • I tried this, it seemed it would work, but later a directory with only the attribute `Directory` appeared with the same problem. Would a double check work? First check for `System`, then check for the permissions with one of the answers? – Daniel Möller Feb 12 '20 at 18:32
  • 1
    Probably a manual permissions change? I would skip everything with System and Hidden attributes. However a try/catch will be needed in any case. At least you would limit the exceptions raised when enumerating folders. – Steve Feb 12 '20 at 18:37
  • @Steve `However a try/catch will be needed in any case.` where do you put that inline the way the `foreach` has been written? `FileInfo file in dir.EnumerateFiles()` doing this, you've already tried accessing the file, no help here. An option is to filter the ones out with a `where`... – Trevor Feb 12 '20 at 18:39
  • @DanielMöller try this static function `private static bool HasAccessToFolderFile(string path) { try { FileAttributes attributes = File.GetAttributes(path); if (attributes.HasFlag(FileAttributes.ReadOnly) || attributes.HasFlag(FileAttributes.Hidden) || attributes.HasFlag(FileAttributes.System)) { return false; } else { return true; } } catch (Exception ex) { return false; } }` – Trevor Feb 12 '20 at 19:30
  • Then chance your loop to: `foreach (FileInfo file in dir.EnumerateFiles().Where(d => HasAccessToFolderFile(d.FullName)))` If you can't access the file, you can't access the folder... – Trevor Feb 12 '20 at 19:31

2 Answers2

1

This seems like a good question to post my SafeWalk class that gathers together the hints from multiple answers here. It is able to traverse C:\ and D:\ on my box without causing a problem.

public static class SafeWalk {
    [Flags]
    public enum ReturnOptions {
        ReturnFiles = 1, ReturnDirectories = 2, ReturnBoth = 3
    }

    const string AllFiles = "*.*";

    // File and Directory Name Tree Walkers

    public static IEnumerable<string> SafeEnumerateFileAndDirNames(this DirectoryInfo di, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        di.FullName.SafeEnumerateFileAndDirNames(AllFiles, AllFiles, searchOpt, ReturnOptions.ReturnBoth);

    public static IEnumerable<string> SafeEnumerateFileAndDirNames(this DirectoryInfo di, string filePattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        di.FullName.SafeEnumerateFileAndDirNames(filePattern, AllFiles, searchOpt, ReturnOptions.ReturnBoth);

    public static IEnumerable<string> SafeEnumerateFileAndDirNames(this DirectoryInfo di, string filePattern, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        di.FullName.SafeEnumerateFileAndDirNames(filePattern, dirPattern, searchOpt, ReturnOptions.ReturnBoth);

    public static IEnumerable<string> SafeEnumerateFileAndDirNames(this string path, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.SafeEnumerateFileAndDirNames(AllFiles, AllFiles, searchOpt, ReturnOptions.ReturnBoth);

    public static IEnumerable<string> SafeEnumerateFileAndDirNames(this string path, string filePattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.SafeEnumerateFileAndDirNames(filePattern, AllFiles, searchOpt, ReturnOptions.ReturnBoth);

    public static IEnumerable<string> SafeEnumerateFileAndDirNames(this string path, string filePattern, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly, ReturnOptions returnOpt = ReturnOptions.ReturnBoth) {
        var searchQueue = new Queue<string>() { path };

        while (searchQueue.Count > 0) {
            var cdn = searchQueue.Dequeue();

            IEnumerable<string> cdiFiles = null;
            if (returnOpt.HasFlag(ReturnOptions.ReturnFiles)) {
                try {
                    cdiFiles = Directory.EnumerateFiles(cdn, filePattern, SearchOption.TopDirectoryOnly);
                }
                catch (Exception) {
                }

                if (cdiFiles != null)
                    foreach (var filename in cdiFiles)
                        yield return filename;
            }

            if ((!returnOpt.HasFlag(ReturnOptions.ReturnFiles) || cdiFiles != null) && (returnOpt.HasFlag(ReturnOptions.ReturnDirectories) || searchOpt == SearchOption.AllDirectories)) { // skip if file enumeration failed
                IEnumerable<string> cdiDirs = null;
                try {
                    cdiDirs = Directory.EnumerateDirectories(cdn, dirPattern, SearchOption.TopDirectoryOnly);
                }
                catch (Exception) {
                }

                if (cdiDirs != null) {
                    foreach (var dirname in cdiDirs) {
                        if (searchOpt == SearchOption.AllDirectories)
                            searchQueue.Enqueue(dirname);

                        if (returnOpt.HasFlag(ReturnOptions.ReturnDirectories))
                            yield return dirname;
                    }
                }
            }
        }
    }

    public static IEnumerable<string> SafeEnumerateFileNames(this string path, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.SafeEnumerateFileAndDirNames(AllFiles, AllFiles, searchOpt, ReturnOptions.ReturnFiles);

    public static IEnumerable<string> SafeEnumerateFileNames(this string path, string filePattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.SafeEnumerateFileAndDirNames(filePattern, AllFiles, searchOpt, ReturnOptions.ReturnFiles);

    public static IEnumerable<string> SafeEnumerateFileNames(this string path, string filePattern, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.SafeEnumerateFileAndDirNames(filePattern, dirPattern, searchOpt, ReturnOptions.ReturnFiles);

    public static IEnumerable<string> SafeEnumerateFileNames(this DirectoryInfo path, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.FullName.SafeEnumerateFileAndDirNames(AllFiles, AllFiles, searchOpt, ReturnOptions.ReturnFiles);

    public static IEnumerable<string> SafeEnumerateFileNames(this DirectoryInfo path, string filePattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.FullName.SafeEnumerateFileAndDirNames(filePattern, AllFiles, searchOpt, ReturnOptions.ReturnFiles);

    public static IEnumerable<string> SafeEnumerateFileNames(this DirectoryInfo path, string filePattern, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.FullName.SafeEnumerateFileAndDirNames(filePattern, dirPattern, searchOpt, ReturnOptions.ReturnFiles);

    public static IEnumerable<string> SafeEnumerateDirectoryNames(this string path, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.SafeEnumerateFileAndDirNames(AllFiles, AllFiles, searchOpt, ReturnOptions.ReturnDirectories);

    public static IEnumerable<string> SafeEnumerateDirectoryNames(this string path, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.SafeEnumerateFileAndDirNames(AllFiles, dirPattern, searchOpt, ReturnOptions.ReturnDirectories);

    public static IEnumerable<string> SafeEnumerateDirectoryNames(this DirectoryInfo path, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.FullName.SafeEnumerateFileAndDirNames(AllFiles, AllFiles, searchOpt, ReturnOptions.ReturnDirectories);

    public static IEnumerable<string> SafeEnumerateDirectoryNames(this DirectoryInfo path, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        path.FullName.SafeEnumerateFileAndDirNames(AllFiles, dirPattern, searchOpt, ReturnOptions.ReturnDirectories);

    // File and Directory Info Tree Walkers

    public static IEnumerable<FileSystemInfo> SafeEnumerateFileSystemInfos(this string path, SearchOption searchOpt = SearchOption.TopDirectoryOnly, ReturnOptions returnOpt = ReturnOptions.ReturnBoth) =>
        new DirectoryInfo(path).SafeEnumerateFileSystemInfos(AllFiles, AllFiles, searchOpt, returnOpt);

    public static IEnumerable<FileSystemInfo> SafeEnumerateFileSystemInfos(this string path, string searchPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly, ReturnOptions returnOpt = ReturnOptions.ReturnBoth) =>
        new DirectoryInfo(path).SafeEnumerateFileSystemInfos(searchPattern, searchPattern, searchOpt, returnOpt);

    public static IEnumerable<FileSystemInfo> SafeEnumerateFileSystemInfos(this string path, string filePattern, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly, ReturnOptions returnOpt = ReturnOptions.ReturnBoth) =>
        new DirectoryInfo(path).SafeEnumerateFileSystemInfos(filePattern, dirPattern, searchOpt, returnOpt);

    public static IEnumerable<FileSystemInfo> SafeEnumerateFileSystemInfos(this DirectoryInfo di, SearchOption searchOpt = SearchOption.TopDirectoryOnly, ReturnOptions returnOpt = ReturnOptions.ReturnBoth) =>
        di.SafeEnumerateFileSystemInfos(AllFiles, AllFiles, searchOpt, returnOpt);

    public static IEnumerable<FileSystemInfo> SafeEnumerateFileSystemInfos(this DirectoryInfo di, string searchPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly, ReturnOptions returnOpt = ReturnOptions.ReturnBoth) =>
        di.SafeEnumerateFileSystemInfos(searchPattern, searchPattern, searchOpt, returnOpt);

    public static IEnumerable<FileSystemInfo> SafeEnumerateFileSystemInfos(this DirectoryInfo di, string filePattern, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly, ReturnOptions returnOpt = ReturnOptions.ReturnBoth) {
        var searchQueue = new Queue<DirectoryInfo>();
        searchQueue.Enqueue(di);
        while (searchQueue.Count > 0) {
            var cdi = searchQueue.Dequeue();

            IEnumerable<string> cdiFiles = null;
            if (returnOpt.HasFlag(ReturnOptions.ReturnFiles)) {
                try {
                    cdiFiles = Directory.EnumerateFiles(cdi.FullName, filePattern, SearchOption.TopDirectoryOnly);
                }
                catch (Exception) {
                }

                if (cdiFiles != null) {
                    var cfis = new ConcurrentBag<FileInfo>();
                    cdiFiles.AsParallel()
                            .ForAll(f => {
                                try {
                                    cfis.Add(new FileInfo(f));
                                }
                                catch (Exception) {
                                }
                            });

                    foreach (var fi in cfis)
                        yield return fi;
                }
            }

            if ((!returnOpt.HasFlag(ReturnOptions.ReturnFiles) || cdiFiles != null) && (returnOpt.HasFlag(ReturnOptions.ReturnDirectories) || searchOpt.HasFlag(SearchOption.AllDirectories))) { // skip if file enumeration failed
                IEnumerable<string> cdiDirs = null;
                try {
                    cdiDirs = Directory.EnumerateDirectories(cdi.FullName, dirPattern, SearchOption.TopDirectoryOnly);
                }
                catch (Exception) {
                }

                if (cdiDirs != null) {
                    var cdis = new ConcurrentBag<DirectoryInfo>();
                    cdiDirs.AsParallel()
                           .ForAll(d => {
                               try {
                                   cdis.Add(new DirectoryInfo(d));
                               }
                               catch (Exception) {
                               }
                           });
                    foreach (var rdi in cdis) {
                        if (returnOpt.HasFlag(ReturnOptions.ReturnDirectories))
                            yield return rdi;
                        if (searchOpt == SearchOption.AllDirectories)
                            searchQueue.Enqueue(rdi);
                    }
                }
            }
        }
    }

    public static IEnumerable<FileInfo> SafeEnumerateFileInfos(this string path, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        new DirectoryInfo(path).SafeEnumerateFileSystemInfos(AllFiles, AllFiles, searchOpt, ReturnOptions.ReturnFiles).Cast<FileInfo>();

    public static IEnumerable<FileInfo> SafeEnumerateFileInfos(this string path, string filePattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        new DirectoryInfo(path).SafeEnumerateFileSystemInfos(filePattern, AllFiles, searchOpt, ReturnOptions.ReturnFiles).Cast<FileInfo>();

    public static IEnumerable<FileInfo> SafeEnumerateFileInfos(this string path, string filePattern, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        new DirectoryInfo(path).SafeEnumerateFileSystemInfos(filePattern, dirPattern, searchOpt, ReturnOptions.ReturnFiles).Cast<FileInfo>();

    public static IEnumerable<FileInfo> SafeEnumerateFileInfos(this DirectoryInfo di, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        di.SafeEnumerateFileSystemInfos(AllFiles, AllFiles, searchOpt, ReturnOptions.ReturnFiles).Cast<FileInfo>();

    public static IEnumerable<FileInfo> SafeEnumerateFileInfos(this DirectoryInfo di, string filePattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        di.SafeEnumerateFileSystemInfos(filePattern, AllFiles, searchOpt, ReturnOptions.ReturnFiles).Cast<FileInfo>();

    public static IEnumerable<FileInfo> SafeEnumerateFileInfos(this DirectoryInfo di, string filePattern, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        di.SafeEnumerateFileSystemInfos(filePattern, dirPattern, searchOpt, ReturnOptions.ReturnFiles).Cast<FileInfo>();

    public static IEnumerable<DirectoryInfo> SafeEnumerateDirectoryInfos(this string path, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        new DirectoryInfo(path).SafeEnumerateFileSystemInfos(AllFiles, AllFiles, searchOpt, ReturnOptions.ReturnDirectories).Cast<DirectoryInfo>();

    public static IEnumerable<DirectoryInfo> SafeEnumerateDirectoryInfos(this string path, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        new DirectoryInfo(path).SafeEnumerateFileSystemInfos(AllFiles, dirPattern, searchOpt, ReturnOptions.ReturnDirectories).Cast<DirectoryInfo>();

    public static IEnumerable<DirectoryInfo> SafeEnumerateDirectoryInfos(this DirectoryInfo di, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        di.SafeEnumerateFileSystemInfos(AllFiles, AllFiles, searchOpt, ReturnOptions.ReturnDirectories).Cast<DirectoryInfo>();

    public static IEnumerable<DirectoryInfo> SafeEnumerateDirectoryInfos(this DirectoryInfo di, string dirPattern, SearchOption searchOpt = SearchOption.TopDirectoryOnly) =>
        di.SafeEnumerateFileSystemInfos(AllFiles, dirPattern, searchOpt, ReturnOptions.ReturnDirectories).Cast<DirectoryInfo>();
}
NetMage
  • 26,163
  • 3
  • 34
  • 55
-1

How can I check if I have permission to enumerate a folder's content before doing it?

You can not. Or at least not reliable enough for it to mater. Disk access is a thing you can just try all over again. Just because it worked once, does not imply it will continue to work. I mean what would you do if someone pulled out the Disk between this read and the last? Or if the folder rights were changed? There is just too much outside your control that you have to expect.

Disk errors belong to the class of Exogenous Exceptions. And those you can only handle, never avoid.

As for proper exception handling, I have two articles. The first one I already linked. The 2nd one is this: https://www.codeproject.com/Articles/9538/Exception-Handling-Best-Practices-in-NET

Christopher
  • 9,634
  • 2
  • 17
  • 31
  • Not my downvote, but those events you mention are so rare I don't think it's worthy to care for them in my case. If the user pulls the disk, well, sorry, user. – Daniel Möller Feb 12 '20 at 18:19
  • 1
    @DanielMöller I literally just dealt with a Windows where the Disk Driver seemed to break 5 minutes after start. And windows did not handle it gracefully. It kinda just "locked up". – Christopher Feb 23 '20 at 13:59