Ignore folder C# (System.UnauthorizedAccessException) - c#

A quick question. I'm executing this code:
listBox1.Items.AddRange( Directory.GetDirectories("C:\\Users\\", "*" ,SearchOption.AllDirectories));
It list all directories and subdirectories in C:\Users\ (yes I know, it maybe blow up my pc)
Anyways, I am getting this error (System.UnauthorizedAccessException)
This error comes from the special folders "C:\Users\All Users\" and "C:\Users\USER\AppData\"
How can I ignore this folders to program keeping listing all dir and subd without Exceptions?

Unfortunately it's not possible to filter out all directories without the required permissions. You need to implement your own recursive function to deal with the problem by catching the UnauthorizedAccessException. Since there could be many exceptions the way is not very fast but reliable like explained in this question:
[...] permissions (even file existence) are volatile — they can change at any time [...]
Here is my possible solution:
public static void GetDirectories(string path, Action<string> foundDirectory)
{
string[] dirs;
try
{
dirs = Directory.GetDirectories(path, "*", SearchOption.TopDirectoryOnly);
}
catch (UnauthorizedAccessException)
{
//Ignore a directory if an unauthorized access occured
return;
}
foreach (string dir in dirs)
{
foundDirectory(dir);
//Recursive call to get all subdirectories
GetDirectories(dir, foundDirectory);
}
}
Simply call the function like
List<string> allDirectories = new List<string>();
GetDirectories(#"C:\Users\", d => allDirectories.Add(d));

Related

C# File Permissions

I am currently writing a program in C# that will copy all user profile files to an external device (in this case, my home server).
When my code iterates through my files and folders, it throws an UnauthorizedAccessException.
I have Googled this and searched StackOverflow, but I am unable to find a clear solution that doesn't involve terminating my process. The idea is that it should copy the files and folders that have read permissions.
I had this when I first started, but I easily fixed it by limiting what directories I would backup (though I would prefer a full backup).
Here is my code:
FileInfo f = new FileInfo(_configuration.Destination);
if (!f.Directory.Exists)
{
f.Directory.Create();
}
string[] backupDirectories = new string[]
{
"Desktop",
"Documents",
"Downloads",
"Favorites",
"Links",
"Pictures",
"Saved Games",
"Searches",
"Videos",
".git",
".android",
".IdealC15",
".nuget",
".oracle_jre_usage",
".vs",
"Contacts"
};
foreach (string dirPath in backupDirectories)
{
DirectoryInfo dirInfo = new DirectoryInfo(_path + "\\" + dirPath);
if (dirInfo.Exists)
{
foreach (string dirP in Directory.GetDirectories(dirInfo.FullName, "*", SearchOption.AllDirectories))
{
DirectoryInfo dirI = new DirectoryInfo(dirP);
if (dirI.Exists)
{
string dir = dirP.Replace(_path, _configuration.Destination);
try
{
Directory.CreateDirectory(dir);
textBox2.Invoke((MethodInvoker) delegate
{
textBox2.AppendText("Create Directory: " + dir + Environment.NewLine);
});
} catch (Exception e)
{
textBox2.Invoke((MethodInvoker) delegate
{
textBox2.AppendText("Could NOT Create Directory: " + dir + Environment.NewLine);
});
continue;
}
foreach (FileInfo theFile in dirInfo.GetFiles("*", SearchOption.AllDirectories))
{
string newPath = theFile.FullName;
string file = newPath.Replace(_path, _configuration.Destination);
try
{
File.Copy(newPath, file, true);
textBox2.Invoke((MethodInvoker) delegate
{
textBox2.AppendText("Create File: " + file + Environment.NewLine);
});
} catch (Exception ex)
{
textBox2.Invoke((MethodInvoker) delegate
{
textBox2.AppendText("Could NOT Create File: " + file + Environment.NewLine);
});
}
}
}
}
}
}
I apologise if the code is messy, but I will describe sort of what it is doing. The first bit checks if the backup folder exists on the external drive.
The second part says what folders I need to backup (if you're able to fix this and make it backup all directories with permissions, please help me in doing so).
The first loop starts the iteration for each of the backupDirectories. The second loop starts the iteration for each of the directories in the backup directory. The third loop starts the iteration for each of the folders in the backup directory.
The exception is thrown at Directory.GetDirectories(dirInfo.FullName, "*", SearchOption.AllDirectories), and it is trying to access C:\Users\MyName\Documents\My Music. Attempting to access it in explorer does give me a permissions error, though it isn't listed in explorer when I try going to "Documents" (I am in Windows 10 Pro).
As I recommended, since the Operating System authority is higher than the application, it is likely that you cannot do more than what the Operating System would allow you to do (that is to access or not to access certain folder).
Thus, folders' accessibility is best solved in the Operating System level.
But you could still do two things in the program level to minimize the damage when you search for the items.
To use Directory.AccessControl to know the access level of a directory before you do any query on it. This is not so easy, and there are elaborated answers about this here and also here.
To minimize the damage made by unauthorized access issues by using SearchOption.TopDirectoryOnly instead of SearchOption.AllDirectories, combined with recursive search for all the accessible directories. This is how you can code it
public static List<string> GetAllAccessibleDirectories(string path, string searchPattern) {
List<string> dirPathList = new List<string>();
try {
List<string> childDirPathList = Directory.GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly).ToList(); //use TopDirectoryOnly
if (childDirPathList == null || childDirPathList.Count <= 0) //this directory has no child
return null;
foreach (string childDirPath in childDirPathList) { //foreach child directory, do recursive search
dirPathList.Add(childDirPath); //add the path
List<string> grandChildDirPath = GetAllAccessibleDirectories(childDirPath, searchPattern);
if (grandChildDirPath != null && grandChildDirPath.Count > 0) //this child directory has children and nothing has gone wrong
dirPathList.AddRange(grandChildDirPath.ToArray()); //add the grandchildren to the list
}
return dirPathList; //return the whole list found at this level
} catch {
return null; //something has gone wrong, return null
}
}
The function above minimize the damage caused by the unauthorized access only to the sub-directories which have the issue. All other accessible directories can be returned.

Access to the path is denied when using Directory.GetFiles(...) [duplicate]

This question already has answers here:
Ignore folders/files when Directory.GetFiles() is denied access
(8 answers)
UnauthorizedAccessException cannot resolve Directory.GetFiles failure [duplicate]
(6 answers)
Closed 2 years ago.
I'm running the code below and getting exception below. Am I forced to put this function in try catch or is there other way to get all directories recursively?
I could write my own recursive function to get files and directory. But I wonder if there is a better way.
// get all files in folder and sub-folders
var d = Directory.GetFiles(#"C:\", "*", SearchOption.AllDirectories);
// get all sub-directories
var dirs = Directory.GetDirectories(#"C:\", "*", SearchOption.AllDirectories);
"Access to the path 'C:\Documents and Settings\' is denied."
If you want to continue with the next folder after a fail, then yea; you'll have to do it yourself. I would recommend a Stack<T> (depth first) or Queue<T> (bredth first) rather than recursion, and an iterator block (yield return); then you avoid both stack-overflow and memory usage issues.
Example:
public static IEnumerable<string> GetFiles(string root, string searchPattern)
{
Stack<string> pending = new Stack<string>();
pending.Push(root);
while (pending.Count != 0)
{
var path = pending.Pop();
string[] next = null;
try
{
next = Directory.GetFiles(path, searchPattern);
}
catch { }
if(next != null && next.Length != 0)
foreach (var file in next) yield return file;
try
{
next = Directory.GetDirectories(path);
foreach (var subdir in next) pending.Push(subdir);
}
catch { }
}
}
You can set the program so you can only run as administrator.
In Visual Studio:
Right click on the Project -> Properties -> Security -> Enable ClickOnce Security Settings
After you clicked it, a file will be created under the Project's properties folder called app.manifest once this is created, you can uncheck the Enable ClickOnce Security Settings option
Open that file and change this line :
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
to:
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
This will make the program require administrator privileges, and it will guarantee you have access to that folder.
It has already been pointed out that you need to do it yourself so I thought I'd share my solution which avoids collections along the way. It should be noted that this will ignore all errors not just AccessDenied. To change that you can just make the catch blocks more specific.
IEnumerable<string> GetFiles(string folder, string filter, bool recursive)
{
string [] found = null;
try
{
found = Directory.GetFiles(folder, filter);
}
catch { }
if (found!=null)
foreach (var x in found)
yield return x;
if (recursive)
{
found = null;
try
{
found = Directory.GetDirectories(folder);
}
catch { }
if (found != null)
foreach (var x in found)
foreach (var y in GetFiles(x, filter, recursive))
yield return y;
}
}
Well, you either avoid the directories for which you don't have permissions, or you don't but then respond gracefully when access is denied.
If you choose the first option, you will need to make sure that you know what directories they are, and also that the permissions for the thread's identity do not change. This is tricky and prone to error; I wouldn't recommend it for a production-quality system.
The second option looks more appropriate. Use a try/catch block and skip any "forbidden" directories.
You can achieve this by using EnumerationOptions for the third argument. This class provides a property called IgnoreInaccessible which toggles whether an exception will be thrown if an inaccessbile file/folder is encountered.
Other properties related to searching are available too, see: EnumerationOptions Class (System.IO)
Example:
var options = new EnumerationOptions()
{
IgnoreInaccessible = true
};
var files = Directory.GetFiles("mypath", "*.*", options);
foreach (var file in files)
{
// File related activities
}
Note: IgnoreAccessible is set to true by default, but I've included it in the example above for visibility purposes.
I know this question is somewhat old, but I had this same problem today and I found the following article that explains a 'folder recursion' solution in detail.
The article acknowledges the flaws of the GetDirectories() method... :
Unfortunately, this [using the GetDirectories() method] has problems. Key amongst these is that some of
the folders that you attempt to read could be configured so that the
current user may not access them. Rather than ignoring folders to
which you have restricted access, the method throws an
UnauthorizedAccessException. However, we can circumvent this problem
by creating our own recursive folder search code.
... and then introduces the solution in detail:
http://www.blackwasp.co.uk/FolderRecursion.aspx
This recursive method will return list of all files that are accessible in the folder.
static List<string> getFilesInDir(string dirPath)
{
List<string> retVal = new List<string>();
try
{
retVal = IO.Directory.GetFiles(dirPath, "*.*", IO.SearchOption.TopDirectoryOnly).ToList();
foreach (IO.DirectoryInfo d in new IO.DirectoryInfo(dirPath).GetDirectories("*", IO.SearchOption.TopDirectoryOnly))
{
retVal.AddRange(getFilesInDir(d.FullName));
}
}
catch (Exception ex)
{
//Console.WriteLine(dirPath);
}
return retVal;
}

C#: How to check if I can read and/or delete a directory

I loop through a bunch of directories recursively. Some of them (like D:\$RECYCLE.BIN\S-1-5-20) give me a System.UnauthorizedAccessException. I suppose that I can just catch it and move on, but I would rather figure that out in advance.
So, when I have a DirectoryInfo object. How can I see if I am allowed to GetDirectories() and possibly Delete() it?
If you intend to delete it, try to delete it and then proceed (handling the exception as appropriate).
If you perform a check-and-then-delete-if-should-be-able-to-delete there is the chance of a race condition on the filesystem, however slight. This applies to most all file/directory access operations. Most filesystem operations are designed to be atomic and moving this logic into user code conflicts this atomicity and one would still need to handle a possible exception being raised.
I built following code. Please, see if it helps:
//using System.IO;
//using System.Security.AccessControl;
//using System.Security.Principal;
string[] directories = Directory.GetDirectories(
Path.Combine(Environment.CurrentDirectory, #"..\.."),
"*", SearchOption.AllDirectories);
foreach (string directory in directories)
{
DirectoryInfo info = new DirectoryInfo(directory);
DirectorySecurity security = info.GetAccessControl();
Console.WriteLine(info.FullName);
foreach (FileSystemAccessRule rule in
security.GetAccessRules(true, true, typeof(NTAccount)))
{
Console.WriteLine("\tIdentityReference = {0}", rule.IdentityReference);
Console.WriteLine("\tInheritanceFlags = {0}", rule.InheritanceFlags );
Console.WriteLine("\tPropagationFlags = {0}", rule.PropagationFlags );
Console.WriteLine("\tAccessControlType = {0}", rule.AccessControlType);
Console.WriteLine("\tFileSystemRights = {0}", rule.FileSystemRights );
Console.WriteLine();
}
}
Result:
D:\Projects\ConsoleApplication1\bin
IdentityReference = BUILTIN\Administrators
InheritanceFlags = ContainerInherit, ObjectInherit
PropagationFlags = None
AccessControlType = Allow
FileSystemRights = FullControl
Note that IdentityReference and FileSystemRights properties; probably you should test your current ACL against them before trying to delete a directory.
I believe you will need to write your own GetDirectories() method; that recursivly gets the ones inside of it.
This Microsoft Article has a good article on how to do it, with a bit of work you can clean it up to use Generic Lists and make it fit your solution.
Simply put, System.IO.Directory.GetDirectories() will fail every time it gets one of those exceptions.
Code roughly like this (copied from above) should get you started
List<String> directories = new List<String>();
void DirSearch(string sDir)
{
try
{
foreach (string d in Directory.GetDirectories(sDir))
{
//foreach (string f in Directory.GetFiles(d, txtFile.Text))
//{
//
//}
// use this to search for files recursivly.
directories.Add(d);
DirSearch(d);
}
}
catch (System.Exception excpt)
{
Console.WriteLine(excpt.Message);
}
}
Once you have your list of directories, you can then perform operations on them, with some mods the above method should ensure you have read permissions on anything in the list.

DirectoryNotFoundException when calling Directory.GetDirectories on Environment.SpecialFolder.Favorites due to Domain Folder Redirection

I have some C# code that tries to get the Favorites for the currently logged in user. The code is part of a Taskbar Toolbar that gets loaded into the Windows Explorer process. I have a user who is using Windows Vista with UAC enabled on a domain that either has Roaming Profiles or Folder Redirection setup and enabled. When calling Directory.GetDirectories on the Favorites path, it throws "System.IO.DirectoryNotFoundException: Could not find a part of the path 'C:\Users\\Favorites\". Other users on other domains that do not have Roaming Profiles or Folder Redirection setup do not have this issue.
The user also reported that copying the path from the failed logs into the run prompt fails to load the path, but if they navigate to the path directly using explorer and then copy and paste that path into the run prompt, it works. He sent me both paths and they are exactly identical which doesn't make any sense at all.
My theory is that this is caused by the Folder Redirection where that path is actually pointing to a share on the server but the redirection is failing when trying to access the subdirectories (of the directoryInfo returned from Directory.GetDirectories). The initial directory works but all subdirectories of the initial directory fail to redirect correctly.
Has anyone come across a situation like this and/or know a workaround to gain proper access to redirected folders?
private void GetFavorites()
{
try
{
System.IO.DirectoryInfo dirInfo = new System.IO.DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Favorites));
AddFavorites(dirInfo);
}
catch
{
}
}
private void AddFavorites(DirectoryInfo dirInfo)
{
foreach (System.IO.FileInfo fileInfo in dirInfo.GetFiles("*.url"))
{
//string alias = fileInfo.Name.Replace(".url", "");
if (!ItemsBookmarks.ContainsKey(fileInfo.Name))
ItemsBookmarks.Add(fileInfo.Name, fileInfo.Name);
}
foreach (System.IO.FileInfo fileInfo in dirInfo.GetFiles("*.lnk"))
{
if (!ItemsBookmarks.ContainsKey(fileInfo.Name))
ItemsBookmarks.Add(fileInfo.Name, fileInfo.Name);
}
foreach (System.IO.DirectoryInfo objDir in dirInfo.GetDirectories())
{
AddFavorites(objDir);
}
}
Thanks,
John
I believe the problem you are experiencing is related to Reparse Points.
See: http://msdn.microsoft.com/en-us/library/bb513869.aspx
See: What is the best way to check for reparse point in .net (c#)?
The problem can be avoided by using the following syntax:
private void AddFavorites(string dirPath)
{
try
{
foreach (string fileName in Directory.GetFiles(dirPath, "*.*", SearchOption.TopDirectoryOnly))
{
//string alias = fileInfo.Name.Replace(".url", "");
if (!ItemsBookmarks.ContainsKey(fileInfo.Name))
{
ItemsBookmarks.Add(fileName);
}
}
foreach (string subDirName in Directory.GetDirectories(dirPath, "*.*", SearchOption.TopDirectoryOnly))
{
AddFavorites(objDir);
}
}
catch
{
//error getting files or subdirs... permissions issue?
//throw
}
}

How do I get a directory size (files in the directory) in C#?

I want to be able to get the size of one of the local directories using C#. I'm trying to avoid the following (pseudo like code), although in the worst case scenario I will have to settle for this:
int GetSize(Directory)
{
int Size = 0;
foreach ( File in Directory )
{
FileInfo fInfo of File;
Size += fInfo.Size;
}
foreach ( SubDirectory in Directory )
{
Size += GetSize(SubDirectory);
}
return Size;
}
Basically, is there a Walk() available somewhere so that I can walk through the directory tree? Which would save the recursion of going through each sub-directory.
A very succinct way to get a folder size in .net 4.0 is below. It still suffers from the limitation of having to traverse all files recursively, but it doesn't load a potentially huge array of filenames, and it's only two lines of code. Make sure to use the namespaces System.IO and System.Linq.
private static long GetDirectorySize(string folderPath)
{
DirectoryInfo di = new DirectoryInfo(folderPath);
return di.EnumerateFiles("*.*", SearchOption.AllDirectories).Sum(fi => fi.Length);
}
If you use Directory.GetFiles you can do a recursive seach (using SearchOption.AllDirectories), but this is a bit flaky anyway (especially if you don't have access to one of the sub-directories) - and might involve a huge single array coming back (warning klaxon...).
I'd be happy with the recursion approach unless I could show (via profiling) a bottleneck; and then I'd probably switch to (single-level) Directory.GetFiles, using a Queue<string> to emulate recursion.
Note that .NET 4.0 introduces some enumerator-based file/directory listing methods which save on the big arrays.
Here my .NET 4.0 approach
public static long GetFileSizeSumFromDirectory(string searchDirectory)
{
var files = Directory.EnumerateFiles(searchDirectory);
// get the sizeof all files in the current directory
var currentSize = (from file in files let fileInfo = new FileInfo(file) select fileInfo.Length).Sum();
var directories = Directory.EnumerateDirectories(searchDirectory);
// get the size of all files in all subdirectories
var subDirSize = (from directory in directories select GetFileSizeSumFromDirectory(directory)).Sum();
return currentSize + subDirSize;
}
Or even nicer:
// get IEnumerable from all files in the current dir and all sub dirs
var files = Directory.EnumerateFiles(searchDirectory,"*",SearchOption.AllDirectories);
// get the size of all files
long sum = (from file in files let fileInfo = new FileInfo(file) select fileInfo .Length).Sum();
As Gabriel pointed out this will fail if you have a restricted directory under the searchDirectory!
You could hide your recursion behind an extension method (to avoid the issues Marc has highlighted with the GetFiles() method):
public static class UserExtension
{
public static IEnumerable<FileInfo> Walk(this DirectoryInfo directory)
{
foreach(FileInfo file in directory.GetFiles())
{
yield return file;
}
foreach(DirectoryInfo subDirectory in directory.GetDirectories())
{
foreach(FileInfo file in subDirectory.Walk())
{
yield return file;
}
}
}
}
(You probably want to add some exception handling to this for protected folders etc.)
Then:
using static UserExtension;
long totalSize = 0L;
var startFolder = new DirectoryInfo("<path to folder>");
// iteration
foreach(FileInfo file in startFolder.Walk())
{
totalSize += file.Length;
}
// linq
totalSize = di.Walk().Sum(s => s.Length);
Basically the same code, but maybe a little neater...
First, forgive my poor english ;o)
I had a problem that took me to this page : enumerate files of a directory and his subdirectories without blocking on an UnauthorizedAccessException, and, like the new method of .Net 4 DirectoryInfo.Enumerate..., get the first result before the end of the entire query.
With the help of various examples found here and there on the web, I finally write this method :
public static IEnumerable<FileInfo> EnumerateFiles_Recursive(this DirectoryInfo directory, string searchPattern, SearchOption searchOption, Func<DirectoryInfo, Exception, bool> handleExceptionAccess)
{
Queue<DirectoryInfo> subDirectories = new Queue<DirectoryInfo>();
IEnumerable<FileSystemInfo> entries = null;
// Try to get an enumerator on fileSystemInfos of directory
try
{
entries = directory.EnumerateFileSystemInfos(searchPattern, SearchOption.TopDirectoryOnly);
}
catch (Exception e)
{
// If there's a callback delegate and this delegate return true, we don't throw the exception
if (handleExceptionAccess == null || !handleExceptionAccess(directory, e))
throw;
// If the exception wasn't throw, we make entries reference an empty collection
entries = EmptyFileSystemInfos;
}
// Yield return file entries of the directory and enqueue the subdirectories
foreach (FileSystemInfo entrie in entries)
{
if (entrie is FileInfo)
yield return (FileInfo)entrie;
else if (entrie is DirectoryInfo)
subDirectories.Enqueue((DirectoryInfo)entrie);
}
// If recursive search, we make recursive call on the method to yield return entries of the subdirectories.
if (searchOption == SearchOption.AllDirectories)
{
DirectoryInfo subDir = null;
while (subDirectories.Count > 0)
{
subDir = subDirectories.Dequeue();
foreach (FileInfo file in subDir.EnumerateFiles_Recursive(searchPattern, searchOption, handleExceptionAccess))
{
yield return file;
}
}
}
else
subDirectories.Clear();
}
I use a Queue and a recursive method to keep traditional order (content of directory and then content of first subdirectory and his own subdirectories and then content of the second...). The parameter "handleExceptionAccess" is just a function call when an exception is thrown with a directory; the function must return true to indicate that the exception must be ignored.
With this methode, you can write :
DirectoryInfo dir = new DirectoryInfo("c:\\temp");
long size = dir.EnumerateFiles_Recursive("*", SearchOption.AllDirectories, (d, ex) => true).Sum(f => f.Length);
And here we are : all exception when trying to enumerate a directory will be ignore !
Hope this help
Lionel
PS : for a reason I can't explain, my method is more quick than the framework 4 one...
PPS : you can get my test solutions with source for those methods : here TestDirEnumerate. I write EnumerateFiles_Recursive, EnumerateFiles_NonRecursive (use a queue to avoid recursion) and EnumerateFiles_NonRecursive_TraditionalOrder (use a stack of queue to avoid recursion and keep traditional order). Keep those 3 methods has no interest, I write them only for test the best one. I think to keep only the last one.
I also wrote the equivalent for EnumerateFileSystemInfos and EnumerateDirectories.
Have a look at this post:
http://social.msdn.microsoft.com/forums/en-US/vbgeneral/thread/eed54ebe-facd-4305-b64b-9dbdc65df04e
Basically there is no clean .NET way, but there is a quite straightforward COM approach so if you're happy with using COM interop and being tied to Windows, this could work for you.
the solution is already here https://stackoverflow.com/a/12665904/1498669
as in the duplicate How do I Get Folder Size in C#? shown -> you can do this also in c#
first, add the COM reference "Microsoft Scripting Runtime" to your project and use:
var fso = new Scripting.FileSystemObject();
var folder = fso.GetFolder(#"C:\Windows");
double sizeInBytes = folder.Size;
// cleanup COM
System.Runtime.InteropServices.Marshal.ReleaseComObject(folder);
System.Runtime.InteropServices.Marshal.ReleaseComObject(fso);
remember to cleanup the COM references
I've been looking some time ago for a function like the one you ask for and from what I've found on the Internet and in MSDN forums, there is no such function.
The recursive way is the only I found to obtain the size of a Folder considering all the files and subfolders that contains.
You should make it easy on yourself. Make a method and passthrough the location of the directory.
private static long GetDirectorySize(string location) {
return new DirectoryInfo(location).GetFiles("*.*", SearchOption.AllDirectories).Sum(file => file.Length);
}
-G

Categories

Resources