1

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).

SysVoid
  • 584
  • 5
  • 18
  • Try giving the Network Service permissions to the that folder. Right-click the user folder and go to security. – Kami Jan 09 '16 at 13:49
  • Currently my account, `Administrators`, and `SYSTEM` have nothing but full access to my directory in `C:\Users`. Not sure what to do about `My Music`. Also note that `My Music` is separate from `Music`. – SysVoid Jan 09 '16 at 13:53
  • If your code throws exception because it is inaccessible, I don't think there is anything much you can do, since code authority is not higher than the Operating System. But two things you can do on the code side: (1) Check `Directory.GetAccessControl()` per child directory check to see if you have an access to a Directory before hand (this option is rather hard though) (2) Use `SearchOption.TopDirectoryOnly` instead of `SearchOption.AllDirectories`, combined with **recursive** search for all the accessible directories to minimize the access issues – Ian Jan 09 '16 at 13:57
  • @Ian could you make an answer with an example? – SysVoid Jan 09 '16 at 13:59
  • @SysVoid There you go. I present my solution with explanation, links, and samples as you have requested. – Ian Jan 09 '16 at 14:11
  • You can't use GetDirectories and get subdirectories. You must create a recursive function the gets one directory at a time and include a exception handler inside any loops so you don't exit out of application the first time you get an access error. Been doing this for lots of years. If you don't understand my answer I will give a good example. – jdweng Jan 09 '16 at 14:11

1 Answers1

1

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.

  1. 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.

  2. 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.

Community
  • 1
  • 1
Ian
  • 30,182
  • 19
  • 69
  • 107
  • Sorry if I am reading this wrong, but surely in my situation, where `Documents\My Music` is breaking it, this will stop the process and make it return null? Once it hits `Directory.GetDirectories(path, searchPattern, SearchOption.TopDirectoryOnly).ToList();` in `My Documents`, it will see `My Music`, and therefore stop indexing the rest of the folders in `Documents`? – SysVoid Jan 09 '16 at 14:43
  • Yes, but since this is recursive, thus as long as you have parent path, and if the parent path has other children, it will not affect the accessing of the other children because the `null` is handled in the previous call in the recursive call. – Ian Jan 09 '16 at 14:47
  • @SysVoid Great. Glad to hear that. =) the important thing here is that you want to minimize your error to as small number of folders as possible by recursive search. – Ian Jan 09 '16 at 15:03
  • Just as a note, I also had to modify how I was getting the files and make it top directory only. – SysVoid Jan 09 '16 at 15:13
  • I see, it also should not be a problem with the recursive calls, I think. – Ian Jan 09 '16 at 15:16
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/100242/discussion-between-sysvoid-and-ian). – SysVoid Jan 09 '16 at 15:41