I have a simple function that deletes every file and folder in a target directory. I want to blacklist some directories so that this delete function will not perform the deletion on any file or folder inside any of these directories. Also, it should't delete sub-directories in this directory.
I used a list to represent the blacklisted directories:
private string[] blacklistedFolders =
{
"C:/Program Files",
"C:/Program Files (x86)",
"C:/ProgramData",
"C:/Users",
"C:/Windows"
};
The code that deletes items in a directory:
bool IsBacklisted(string targetDir)
{
for (int i = 0; i < blacklistedFolders.Length; i++)
{
string bList = blacklistedFolders[i];
//Check if the targetDir contains the target directory
if (bList.Contains(targetDir))
return true;
}
return false;
}
public void EmptyDirectory(string targetDir)
{
//Exit if path is blacklisted
if (IsBacklisted(targetDir))
return;
DirectoryInfo dInfo = new DirectoryInfo(targetDir);
foreach (FileInfo fileInfo in dInfo.GetFiles())
{
fileInfo.Delete();
}
foreach (DirectoryInfo dirInfo in dInfo.GetDirectories())
{
dirInfo.Delete(true);
}
}
The problem is that I'am worried that this blacklisted part of the code might fail resulting to the files and folders from the blacklisted directory being deleted. Maybe the blacklist check may not match on different platforms due to the presence of "/" or "\" at the end or middle of the path or other scenarios I may have missed.
Is there a built-in System.IO function or a better way to do this check other that the String.Contains function I am currently using to check for blacklisted directories?
Related
I wish to get list of all the folders/directories that has a particular file in it. How do I do this using C# code.
Eg: Consider I have 20 folders of which 7 of them have a file named "abc.txt". I wish to know all folders that has the file "abc.txt".
I know that we can do this by looking thru all the folders in the path and for each check if the File.Exists(filename); But I wish to know if there is any other way of doing the same rather than looping through all the folder (which may me little time consuming in the case when there are many folders).
Thanks
-Nayan
I would use the method EnumerateFiles of the Directory class with a search pattern and the SearchOption to include AllDirectories. This will return all files (full filename including directory) that match the pattern.
Using the Path class you get the directory of the file.
string rootDirectory = //your root directory;
var foundFiles = Directory.EnumerateFiles(rootDirectory , "abc.txt", SearchOption.AllDirectories);
foreach (var file in foundFiles){
Console.WriteLine(System.IO.Path.GetDirectoryName(file));
}
EnumerateFiles is only available since .NET Framework 4. If you are working with an older version of the .NET Framework then you could use GetFiles of the Directory class.
Update (see comment from PLB):
The code above will fail if the access to a directory in denied. In this case you will need to search each directory one after one to handle exceptions.
public static void SearchFilesRecursivAndPrintOut(string root, string pattern)
{
//Console.WriteLine(root);
try
{
var childDireactory = Directory.EnumerateDirectories(root);
var files = Directory.EnumerateFiles(root, pattern);
foreach (var file in files)
{
Console.WriteLine(System.IO.Path.GetDirectoryName(file));
}
foreach (var dir in childDireactory)
{
SearchRecursiv(dir, pattern);
}
}
catch (Exception exception)
{
Console.WriteLine(exception);
}
}
The following shows how to narrow down your search by specific criteria (i.e. include only DLLs that contain "Microsoft", "IBM" or "nHibernate" in its name).
var filez = Directory.EnumerateFiles(#"c:\MLBWRT", "*.dll", SearchOption.AllDirectories)
.Where(
s => s.ToLower().Contains("microsoft")
&& s.ToLower().Contains("ibm")
&& s.ToLower().Contains("nhibernate"));
string[] allFiles = filez.ToArray<string>();
for (int i = 0; i < allFiles.Length; i++) {
FileInfo fInfo = new FileInfo(allFiles[i]);
Console.WriteLine(fInfo.Name);
}
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.
How would you get a list of all sub directories and their sub directories (until there are none left) from a root directory.
My current code uses a FolderBrowserDialog (WPF does not have one so I have to take it from winforms), and from there attempts to transverse directories. So far I can only make it transverse 1 level deep, as in
If the music directory is setup as
\rock\metallica\ride the lightning
\rock\aerosmith\
\rock\something\
and the user chooses \rock\ it would only grab files from aerosmith, something, and metallica, and not the sub directory of metallica, ride the lightning.
private void btnAddTestFile_Click(object sender, RoutedEventArgs e)
{
string[] files;
FolderBrowserDialog dlg = new FolderBrowserDialog();
DialogResult result = dlg.ShowDialog();
if (result.ToString() == "OK")
{
InitializePropertyValues();
files = System.IO.Directory.GetFiles(dlg.SelectedPath, "*.mp3");
foreach (string file in files)
{
lstvTrackList.Items.Add(new TrackList{ ID = lstvTrackList.Items.Count.ToString(), TrackName = Regex.Match(file, #".*\\(.*).mp3").Groups[1].Value, TrackPath = file });
}
System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(dlg.SelectedPath);
foreach (System.IO.DirectoryInfo d in di.GetDirectories())
{
files = System.IO.Directory.GetFiles(d.FullName, "*.mp3");
foreach (string file in files)
{
lstvTrackList.Items.Add(new TrackList{ ID = lstvTrackList.Items.Count.ToString(), TrackName = Regex.Match(file, #".*\\(.*).mp3").Groups[1].Value, TrackPath = file });
}
}
}
}
Basically what I have attempted to do was first add all the loose files from the root directory, then get a list of directories inside the root directory and add those files.
I think I could figure it out if I knew how to crawl all sub directories starting from a root directory, but I don't quite understand how I would achieve that.
Does anyone have any hints, tips, or sample code that allows you to specify a root directory, then crawl every directory (keeping a string array) until no more sub directories are found, so I can grab files from each directory?
To get all the sub directories you have to pass the SearchOption as the third argument, Which will return all the sub-directories as well.
Directory.GetFiles("","",SearchOption.AllDirectories);
SearchOption
SearchOption.AllDirectories: Includes the current directory and all its subdirectories in a search operation. This option includes reparse points such as mounted drives and symbolic links in the search.
SearchOption.TopDirectoryOnly: Includes only the current directory in
a search operation.
I have a simple method that counts the size of the directory and all files within it. Here is the code:
using System;
using System.IO;
namespace MyProject.Libs
{
public sealed class DirectorySize
{
public static long GetDirectorySize(DirectoryInfo dir)
{
long total = 0;
FileInfo[] fileInfos = dir.GetFiles();
foreach (FileInfo fileInfo in fileInfos)
{
total += fileInfo.Length;
}
DirectoryInfo[] dirInfos = dir.GetDirectories();
foreach (DirectoryInfo dirInfo in dirInfos)
{
total += DirectorySize.GetDirectorySize(dirInfo);
}
return total;
}
}
}
When I use it on drive c:\ I get "UnauthorizedAccessException" with a message "Access to the path 'C:\Documents and Settings' is denied." That is:
DirectoryInfo di = new DirectoryInfo(Path.GetPathRoot(Environment.SystemDirectory));
long ds = DirectorySize.GetDirectorySize(di);
Tried to run Visual Studio as Administrator. All the same. Why?
Your code fails on C:\Documents and Settings which is now a junction point that points to C:\Users. You could check that with the FileAttributes.ReparsePoint of the directory.
Here's the modified code with additional exception handling(for other dirs which you are not authorized):
public sealed class DirectorySize
{
public static long GetDirectorySize(DirectoryInfo dir)
{
long total = 0;
FileAttributes attributes = File.GetAttributes(dir.FullName);
if (!((attributes & FileAttributes.ReparsePoint) == FileAttributes.ReparsePoint))
{
try{
FileInfo[] fileInfos = dir.GetFiles();
foreach (FileInfo fileInfo in fileInfos)
{
total += fileInfo.Length;
}
DirectoryInfo[] dirInfos = dir.GetDirectories();
foreach (DirectoryInfo dirInfo in dirInfos)
{
total += DirectorySize.GetDirectorySize(dirInfo);
}
} catch (UnauthorizedAccessException)
{
// log this?
}
}
return total;
}
}
Junction Points (Windows)
In Windows Vista and Windows Server 2008, the default locations for
user data and system data have changed. For example, user data that
was previously stored in the %SystemDrive%\Documents and Settings
directory is now stored in the %SystemDrive%\Users directory. For
backward compatibility, the old locations have junction points that
point to the new locations. For example, C:\Documents and Settings is
now a junction point that points to C:\Users. Backup applications must
be capable of backing up and restoring junction points. These junction
points can be identified as follows: They have the
FILE_ATTRIBUTE_REPARSE_POINT, FILE_ATTRIBUTE_HIDDEN, and
FILE_ATTRIBUTE_SYSTEM file attributes set. They also have their access
control lists (ACLs) set to deny read access to everyone. Applications
that call out a specific path can traverse these junction points if
they have the required permissions. However, attempts to enumerate the
contents of the junction points will result in failures.
There are files and folders you don't have permissions to access, even as administrator. The best you can do is add some exception handling and hope the information you return is reasonably complete.
There are some folders where even the administrator has no permission to visit them. This most of the time happens with translated systems. For example the "program files" folder would be called "programme" in german. You have no access to "programme" within the code but to "program files"
I am writing a tool that will allow me to go though a fairly large list of Directories and Sub-directories. I would like it to delete a folder if there it is empty. I can delete folders and sub folders that are empty with this code:
string dir = textBox1.Text;
string[] folders = System.IO.Directory.GetDirectories(dir, "*.*", System.IO.SearchOption.AllDirectories);
foreach (var directory in folders)
{
if (System.IO.Directory.GetFiles(directory).Length == 0 && System.IO.Directory.GetDirectories(directory).Length == 0)
{
System.IO.StreamWriter Dfile = new System.IO.StreamWriter(newpath, true);
System.IO.Directory.Delete(directory);
}
}
My question is how to have the code go though and check the folders after each delete because once it deletes a folder it could make the parent folder empty and should then should be deleted. Once the code does not find any folders or sub-folders that are empty it would exit.
Write a depth-first recursive function. As you complete each recursive call, check the current folder to see if it is empty. If it is, then delete it.
Something like this (pseudocode)
DeleteEmptyFolders(path)
{
foreach Folder f in Path
{
DeleteEmptyFolders(f);
if (f is empty)
{
Delete(f);
}
}
}
You can do this recursively like this (not tested):
void DeleteFolder(string folder) {
string[] folders = System.IO.Directory.GetDirectories(folder, "*.*", System.IO.SearchOption.AllDirectories);
foreach (var directory in folders)
{
DeleteFolder(directory);
}
//delete this folder if empty
}
Here's an idea (this isn't tested)
while ( true )
{
DirectoryInfo parent = Directory.GetParent(current.FullName);
if ( parent.GetFiles().Length == 0 && parent.GetDirectories().Length == 0 )
{
current = parent;
current.Delete();
}
else
{
break;
}
}
This walks up the tree of the current directory to delete any parent directories that are empty.
By the look of it your are trying to delete any folder which does not contain a file. I believe this would do the trick for you. I tested it with a small folder set that went 5 levels deep and a single file in a couple of locations. All folders which did not have files were deleted. All files were left intact. Small tweak to a snippet found... Thanks Matt Smith.
Cannot delete directory with Directory.Delete(path, true)
string[] dirs = System.IO.Directory.GetDirectories(Directory.GetCurrentDirectory(), "*.*",SearchOption.AllDirectories);
foreach (string d in dirs)
{
if (System.IO.Directory.Exists(d)) {
if (System.IO.Directory.GetFiles(d, "*.*", SearchOption.AllDirectories).Length == 0)
{
DeleteDirectory(d);
}
}
}
public static void DeleteDirectory(string target_dir)
{
string[] dirs = Directory.GetDirectories(target_dir);
foreach (string dir in dirs)
{
DeleteDirectory(dir);
}
Directory.Delete(target_dir, false);
}
this will delete all empty (sub)folders in a given directory
https://stackoverflow.com/a/16688997/2408998