views:

142

answers:

5

Hello,

I am doing a file server migration and I'm writing a small C# app to help me map the user permissions so we can put them in user groups.

I'm currently using

Directory.GetAccessControl(path);

However it fails when it get to this 263 char file path.

Invalid name.
Parameter name: name

I get the same error when I use DirectoryInfo.GetAccessControl();

Is there a work around or alternative to this method?

Thanks!

A: 

If it's an arbitrary limit in the library, then you could try using the 8 character names for the directories. To work out what these names are, run dir with the /X option:


C:\>dir /x

29/12/2009  23:33              PROGRA~1     Program Files
23/02/2010  21:26              PROGRA~2     Program Files (x86
05/12/2009  20:57                           Users
02/02/2010  09:23                           Windows

The short names are those with the tildes in. Try passing these to the function to reduce the string length. No guaranteees this will work, mind.

Paul Ruane
+1  A: 

You should process your directory tree recursively using DirectoryInfo - doing so you will avoid passing the full path.

Filburt
I tried, the error comes when I execute the call DirectoryInfo.GetAccessControl()
Biff MaGriff
It's weird, maybe I should decompile the System.IO.dll to see why/how it does this.
Biff MaGriff
A: 

One alternative is to use subst. From a command prompt, you can execute

subst X: "D:\really really really\long path\that you can shorten"

Then perform your operations on the X: drive and the whole beginning section won't count against your 260-char limit.

Gabe
This is closest to the solution I used :)
Biff MaGriff
+2  A: 

Prefix the path with "\?\" to specify an 'extended-length path". I haven't been able to test if Directory.GetAccessControl()` will work with extended-length paths, but it's worth a try:

from http://msdn.microsoft.com/en-us/library/aa365247.aspx:

Maximum Path Length Limitation

In the Windows API (with some exceptions discussed in the following paragraphs), the maximum length for a path is MAX_PATH, which is defined as 260 characters. A local path is structured in the following order: drive letter, colon, backslash, name components separated by backslashes, and a terminating null character. For example, the maximum path on drive D is "D:\<some 256-character path string><NUL>" where "<NUL>" represents the invisible terminating null character for the current system codepage. (The characters < > are used here for visual clarity and cannot be part of a valid path string.)

Note File I/O functions in the Windows API convert "/" to "\" as part of converting the name to an NT-style name, except when using the "\\?\" prefix as detailed in the following sections.

The Windows API has many functions that also have Unicode versions to permit an extended-length path for a maximum total path length of 32,767 characters. This type of path is composed of components separated by backslashes, each up to the value returned in the lpMaximumComponentLength parameter of the GetVolumeInformation function (this value is commonly 255 characters). To specify an extended-length path, use the "\\?\" prefix. For example, "\\?\D:\<very long path>". (The characters < > are used here for visual clarity and cannot be part of a valid path string.)

Michael Burr
Hmm, can't seem to get it to work :( Thanks for the idea though.
Biff MaGriff
A: 

Using the library I mentioned above this does the trick nicely. I suppose I should have grabbed more mapped drive letters as needed but my max dir length was only 300 chars long.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security;
using System.Security.AccessControl;
using aejw.Network;

namespace SecurityScanner
{
    class Program
    {
        static void Main(string[] args)
        {
            string path = @"\\mynetworkdir";
            DirectoryInfo di = new DirectoryInfo(path);
            List<DirSec> dirs = new List<DirSec>();
            RecordSecurityData(di, dirs, path, path);

            //Grouping up my users
            List<List<DirSec>> groups = new List<List<DirSec>>();
            foreach (DirSec d in dirs)
            {
                bool IsNew = true;
                foreach (List<DirSec> group in groups)
                {
                    if (d.IsSameUserList(group[0]))
                    {
                        group.Add(d);
                        IsNew = false;
                        break;
                    }
                }
                if (IsNew)
                {
                    List<DirSec> newGroup = new List<DirSec>();
                    newGroup.Add(d);
                    groups.Add(newGroup);
                }
            }

            //Outputting my potential user groups
            StringBuilder sb = new StringBuilder();
            foreach (List<DirSec> group in groups)
            {
                foreach (DirSec d in group)
                {
                    sb.AppendLine(d.DirectoryName);
                }
                foreach (string s in group[0].UserList)
                {
                    sb.AppendLine("\t" + s);
                }
                sb.AppendLine();
            }
            File.WriteAllText(@"c:\security.txt", sb.ToString());
        }

        public static void RecordSecurityData(DirectoryInfo di, List<DirSec> dirs, string path, string fullPath)
        {
            DirSec me = new DirSec(fullPath);
            DirectorySecurity ds;
            NetworkDrive nd = null;
            if(path.Length <= 248)
                ds = Directory.GetAccessControl(path);
            else
            {
                nd = new NetworkDrive();
                nd.LocalDrive = "X:";
                nd.ShareName = path;
                nd.MapDrive();
                path = @"X:\";
                di = new DirectoryInfo(path);
                ds = Directory.GetAccessControl(path);
            }
            foreach (AuthorizationRule ar in ds.GetAccessRules(true, true, typeof(System.Security.Principal.NTAccount)))
            {
                me.AddUser(ar.IdentityReference.Value);
            }
            dirs.Add(me);
            foreach (DirectoryInfo child in di.GetDirectories())
            {
                RecordSecurityData(child, dirs, path + @"\" + child.Name, fullPath + @"\" + child.Name);
            }
            if (nd != null)
                nd.UnMapDrive();
        }

        public struct DirSec
        {
            public string DirectoryName;
            public List<string> UserList;

            public DirSec(string directoryName)
            {
                DirectoryName = directoryName;
                UserList = new List<string>();
            }

            public void AddUser(string UserName)
            {
                UserList.Add(UserName);
            }

            public bool IsSameUserList(DirSec other)
            {
                bool isSame = false;
                if (this.UserList.Count == other.UserList.Count)
                {
                    isSame = true;
                    foreach (string myUser in this.UserList)
                    {
                        if (!other.UserList.Contains(myUser))
                        {
                            isSame = false;
                            break;
                        }
                    }
                }
                return isSame;
            }
        }
    }
}
Biff MaGriff