Currently I have a program that searches a user set directory and sub-directories for music files and adds them to a collection. However if one of the directories it comes accross is protected then the program falls over. I wanted to know how I can check if the user has access to the directory before trying to search it to avoid this problem.
Below is the code I'm using for the search, it currently contains a basic work around for "System Volume Information" but as there is a possibility that there may be other protected directories I wanted to change this to include them.
public void SearchForMusic()
{
//Searches selected directory and its sub directories for music files and adds their path to ObservableCollection<string> MusicFound
foreach (string ext in extentions)
{
foreach (string song in Directory.GetFiles(SearchDirectory, ext))
{
musicFound.Add(song);
}
foreach (string directory in Directory.GetDirectories(SearchDirectory))
{
if (directory.Contains("System Volume Information"))
{
}
else
{
foreach (string song in Directory.GetFiles(directory, ext))
{
musicFound.Add(song);
}
foreach (string subDirectory in Directory.GetDirectories(directory))
{
foreach (string subSong in Directory.GetFiles(subDirectory, ext))
{
musicFound.Add(subSong);
}
}
}
}
}
}
Many thanks :)
By far the easiest way to be sure that you have access to a file system object is to attempt to access it. If it fails with an Access Denied error, then you don't have access. Just detect that error condition and proceed with the next item in the search.
In other words, delegate checking access to the system which is, after all, the ultimate arbiter of access rights.
You can check this question by replacing the Write with Read permissions. Also, wrap your code in a try catch block and if the exception is thrown, you can assume (or properly check the exception type to be sure) that the directory cannot be traversed.
Related
you very helpful people,
I am currently trying to go through an array of "permissions" run a block of code to check if there have been any changes made and then add the changes as a log on our site.
I currently have the following array (this is a very slimmed down version of the array in question):
string[] permissions = {"CanLogOn",
"CanAddConsignments",
"CanAddUsers",
"CanListBrokers",
"CanViewBrokers",
"CanAddBrokers",
"CanModifyBrokers",
"CanPutAwayWaste"};
This array is saved in the same file just above this snippet of code:
if (dbSecurityGroup.PermissionList != model.PermissionList)
{
foreach (string permission in permissions)
{
if (dbSecurityGroup.PermissionList.permission != model.PermissionList.permission)
{
model.PermissionList.Logs.Add(model.PermissionList.Log(Logger.LogType.Information, permission + " was edited by " + Authentication.AuthenticationManager.LoggedOnUser.Name));
};
};
}
As you can see we have a lot of permissions that are always being added too so we need this to be scalable and check if any of the permissions have changed.
The following code will work as intended but is for a single permission, we are just trying to find a more efficient way of producing this code for many permissions:
if (dbSecurityGroup.PermissionList != model.PermissionList)
{
if (dbSecurityGroup.PermissionList.CanLogOn != model.PermissionList.CanLogOn)
{
model.PermissionList.Logs.Add(model.PermissionList.Log(Logger.LogType.Information, "Can Log On was edited by " + Authentication.AuthenticationManager.LoggedOnUser.Name));
};
}
Any help is more than welcome. (if this method can be used we will also need a RegEx to put a space in between all of the capitals when producing the logs, to change "CanLogOn" to "Can Log On")
Using .NET Core, C#, Linux
I've searched around a bit and can't seem to find anything. Maybe it's not possible and I need a different approach?
Can someone kindly point me in the direction of how I can go about getting the directory listing for a given path for a specific username?
I am running a web application service as Root but need to check and return directories and files for a given username (no password is available) - to report directories and files that a given username has read-access to.
Say for example "/opt/mydata/" and in there I will have a number of directories that I will manually create and set the permissions for each user group. I.e. "/opt/mydata/user_1_readable" will be returned when I do a directory listing for user1 (because this user is in the respective permissions group, or is the owner, or it is set for everyone to read) but will not be returned for user2 (this user is not in the correct group).
Essentially, I want to "impersonate" or in Linux, do the equivalent of "sudo su user1" and report what directories/files are readable within "/opt/mydata/" for a given user.
I can get the directory listing and files fine running as Root. What I can't do / don't know how to is getting the directory listing for a specific user. The examples I found and tried are all Windows Identity and Windows Security specific.
E.g. I found this example but it seemed to apply to "Mono" which I am not running, but essentially I really want to do something along the lines of this:
// Impersonate a user
using (WindowsIdentity newId = new WindowsIdentity("user1"))
using (WindowsImpersonationContext impersonatedUser = newId.Impersonate())
{
var content = _fileProvider.GetDirectoryContents(uri);
}
Is there some third party library or some other way please?
Resource:
Change current Linux user in a C# application running with Mono?
If you look at this issue on .net core repository, Proposal: Expose POSIX functions , it looks like it won't be implemented in .net core, but only in Mono.Posix.NETStandard.
The library is compatible with .net core 2.0, and it shouldn't be too hard to implement this yourself.
You could try something like this with the package to filter which files the user can read.
public UserHasReadPermission(string username, string file)
{
var user = new UnixUserInfo(username);
var file = new UnixFileInfo(file);
// Everyone has read permission
if (file.FileAccessPermissions.HasFlag(FileAccessPermissions.OtherRead))
return true;
// User owns the file and has read permission
if (file.OwnerUser == user && file.FileAccessPermissions.HasFlag(FileAccessPermissions.UserRead))
return true;
// User group owns the file and has read permission
if (file.OwnerGroup == user.Group && file.FileAccessPermissions.HasFlag(FileAccessPermissions.GroupRead))
return true;
return false;
}
Perhaps you want to read the /etc/passwd file to get users' directories?
Once you have that, you can then get all subdirs inside the folders:
List<string> AllFiles = new List<string>();
void ParsePath(string path)
{
string[] SubDirs = Directory.GetDirectories(path);
AllFiles.AddRange(SubDirs);
AllFiles.AddRange(Directory.GetFiles(path));
foreach (string subdir in SubDirs)
ParsePath(subdir);
}
Update: I be glad to drop the C# requirement, and just see any program that can list all the files running as Admin or System, my question is has anyone seen such a thing?
There are numerous methods of enumerating files in a directory, but all suffer the same problems:
"The specified path, file name, or both are too long. The fully qualified file name must be less than 260 characters, and the directory name must be less than 248 characters."
"Access to the path 'C:\Users\All Users\Application Data' is denied"
etc.
Even running under admin, single user machine, it seems impossible to list all the files without encountering exceptions\errors.
Is it really an impossible task just to get list of all the files under windows? Has anyone ever been able to obtain the complete list of all files on their machine using C# or any other method?
This link from MS with the title "Enumerate Directories and Files" , does not show how to Enumerate Directories and Files, it only show a subset of what that will not throw : DirectoryNotFoundException, UnauthorizedAccessException, PathTooLongException,
Update : Here is sample code to run over C and attempt to enumerate all the files and errors. Even when running this as admin there are folders that not only can be access, but I even can't change their ownership to Admin! for example : "C:\Windows\CSC"
just have look at "Errors {0}.csv" log file to see how many places are inaccessible to admin.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
class Program
{
static System.IO.StreamWriter logfile;
static System.IO.StreamWriter errorfile;
static void Main(string[] args)
{
string directory = #"C:\";
logfile = new System.IO.StreamWriter(string.Format(#"E:\Files {0}.csv", DateTime.Now.ToString("yyyyMMddHHmm")));
errorfile = new System.IO.StreamWriter(string.Format(#"E:\Errors {0}.csv", DateTime.Now.ToString("yyyyMMddHHmm")));
TraverseTree(directory, OnGotFileInfo, OnGotException);
logfile.Close();
errorfile.Close();
}
public static void OnGotFileInfo(System.IO.FileInfo fileInfo)
{
logfile.WriteLine("{0},{1},", fileInfo.FullName, fileInfo.Length.ToString("N0"));
}
public static void OnGotException(Exception ex, string info)
{
errorfile.WriteLine("{0},{1}", ex.Message, info);
}
public static void TraverseTree(string root, Action<System.IO.FileInfo> fileAction, Action<Exception, string> errorAction)
{
// Data structure to hold names of subfolders to be
// examined for files.
Stack<string> dirs = new Stack<string>(20);
if (!System.IO.Directory.Exists(root))
{
throw new ArgumentException();
}
dirs.Push(root);
while (dirs.Count > 0)
{
string currentDir = dirs.Pop();
string[] subDirs;
try
{
subDirs = System.IO.Directory.GetDirectories(currentDir);
}
// An UnauthorizedAccessException exception will be thrown if we do not have
// discovery permission on a folder or file. It may or may not be acceptable
// to ignore the exception and continue enumerating the remaining files and
// folders. It is also possible (but unlikely) that a DirectoryNotFound exception
// will be raised. This will happen if currentDir has been deleted by
// another application or thread after our call to Directory.Exists. The
// choice of which exceptions to catch depends entirely on the specific task
// you are intending to perform and also on how much you know with certainty
// about the systems on which this code will run.
catch (System.Exception e)
{
errorAction(e, currentDir);
continue;
}
string[] files = null;
try
{
files = System.IO.Directory.GetFiles(currentDir);
}
catch (System.Exception e)
{
errorAction(e, currentDir);
continue;
}
// Perform the required action on each file here.
// Modify this block to perform your required task.
foreach (string file in files)
{
try
{
// Perform whatever action is required in your scenario.
System.IO.FileInfo fi = new System.IO.FileInfo(file);
fileAction(fi);
}
catch (System.Exception e)
{
// If file was deleted by a separate application
// or thread since the call to TraverseTree()
// then just continue.
errorAction(e ,file);
continue;
}
}
// Push the subdirectories onto the stack for traversal.
// This could also be done before handing the files.
foreach (string str in subDirs)
dirs.Push(str);
}
}
}
Yes, it is at very least hard to enumerate all files without exceptions.
Several set of issues here:
some path (long ones- PathTooLongException) are not supported by CLR
security restrictions on folders/files
junctions/hard links that introduce duplicates (and in theory cycles to case StackOverflow in recursive iteration).
basic sharing violation restrictions (if you try to read files).
For PathTooLongException: I think you'll need to deal with PInvoke of corresponding Win32 functions. All path related methods in CLR are restricted to 256 characters long.
Security restrictions - you may be able to enumerate everything if you run under system (not sure) or with backup permissions, but any other account is guaranteed to not being able to access all files on system configured by default.
Instead of getting exceptions you can PInvoke native versions and handle error codes instead. You may be able to decrease number of exceptions on going into directories by checking ACL on the directly first.
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.
I'm trying to create an application which scans a drive. The tricky part though, is that my drive contains a set of folders that have folders within folders and contain documents. I'm trying to scan the drive, take a "snapshot" of all documents & folders and dump into a .txt file.
The first time i run this app, the output will be a text file with all the folders & files.
The second time i run this application, it will take the 2 text files (the one produced from the 2nd time i run the app and the .txt file from the 1st time i have run the app) and compare them...reporting what has been moved/overridden/deleted.
Does anybody have any code for this? I'm a newbie at this C# stuff and any help would be greatly appreciated.
Thanks in advance.
One thing that we learned in the 80's was that if it's really tempting to use recursion for file system walking, but the moment you do that, someone will make a file system with nesting levels that will cause your stack to overflow. It's far better to use heap-based walking of the file system.
Here is a class I knocked together which does just that. It's not super pretty, but it does the job quite well:
using System;
using System.IO;
using System.Collections.Generic;
namespace DirectoryWalker
{
public class DirectoryWalker : IEnumerable<string>
{
private string _seedPath;
Func<string, bool> _directoryFilter, _fileFilter;
public DirectoryWalker(string seedPath) : this(seedPath, null, null)
{
}
public DirectoryWalker(string seedPath, Func<string, bool> directoryFilter, Func<string, bool> fileFilter)
{
if (seedPath == null)
throw new ArgumentNullException(seedPath);
_seedPath = seedPath;
_directoryFilter = directoryFilter;
_fileFilter = fileFilter;
}
public IEnumerator<string> GetEnumerator()
{
Queue<string> directories = new Queue<string>();
directories.Enqueue(_seedPath);
Queue<string> files = new Queue<string>();
while (files.Count > 0 || directories.Count > 0)
{
if (files.Count > 0)
{
yield return files.Dequeue();
}
if (directories.Count > 0)
{
string dir = directories.Dequeue();
string[] newDirectories = Directory.GetDirectories(dir);
string[] newFiles = Directory.GetFiles(dir);
foreach (string path in newDirectories)
{
if (_directoryFilter == null || _directoryFilter(path))
directories.Enqueue(path);
}
foreach (string path in newFiles)
{
if (_fileFilter == null || _fileFilter(path))
files.Enqueue(path);
}
}
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
Typical usage is this:
DirectoryWalker walker = new DirectoryWalker(#"C:\pathToSource\src", null, (x => x.EndsWith(".cs")));
foreach (string s in walker)
{
Console.WriteLine(s);
}
Which recursively lists all files that end in ".cs"
A better approach than your text file comparisons would be to use the FileSystemWatcher Class.
Listens to the file system change notifications and raises events when a directory, or file in a directory, changes.
You could log the changes and then generate your reports as needed from that log.
you can easily utilize the DirectoryInfo/FileInfo classes for this.
Basically instantiate an instance of the DirectoryInfo class, pointing towards the c:\ folder. Then using it's objects walk the folder structure.
http://msdn.microsoft.com/en-us/library/system.io.directoryinfo.aspx has code that could quite easily be translated.
Now, the other part of your question is insanity. You can find the differences between the two files relatively easily, but translating that into what has been moved/deleted/etc will take the some fairly advanced logic structures. After all, if I have two files, both named myfile.dat, and one is found at c:\foo and the other at c:\notfoo, how would the one at c:\notfoo be reported if I deleted the one at c:\foo? Another example, is if I have a file myfile2.dat and copy it from c:\bar to c:\notbar is that considered a move? What happens if I copy it on Tuesday, and then on Thursday I delete c:\bar\myfile2.dat--is that a move or a delete? And would the answer change if I ran the program on every Monday as opposed to daily?
There's a whole host of questions, and their corresponding logic structures which you'd need to think of amd code for in order to build that functionality, and even then, it would not be 100% correct, because it's not paging the file system as changes occur--there will always exist the possibility of a scenario that did not get reported correctly in your logic due to timing, logic structure, process time, when the app runs, or just due to the sheer perversity of computers.
Additionally, the processing time would grow exponentially with the size of your drive. After all, you'd need to check every file against every other file to determine it's state as opposed to its previous state. I'd hate to have to run this against my 600+GB drive at home, let alone the 40TB drives I have on servers at work.