SYSK 162: P/Invoke Required. Or Is It?


This is one of those examples when not being able to see the publication timestamp can lead you to make false conclusions…  So, “trust but verify”…


 


Say, you want to secure your application and disallow users from opening network files (or think of your own cases where you may need to call GetDriveType API, which determines whether a disk drive is a removable, fixed, CD-ROM, RAM disk, or network drive).  All web articles/posts I came across (as of July 21, 2006) indicated that you have to do P/Invoke as follows:


 


const int DRIVE_UNKNOWN = 0;


const int DRIVE_NO_ROOT_DIR = 1;


const int DRIVE_REMOVABLE = 2;


const int DRIVE_FIXED = 3;


const int DRIVE_REMOTE = 4;


const int DRIVE_CDROM = 5;


const int DRIVE_RAMDISK = 6;


 


[System.Runtime.InteropServices.DllImport(“KERNEL32”)]


public static extern int GetDriveType(string s);


 


public bool IsLocalDrive(string driveRoot)


{


    int driveType = GetDriveType(driveRoot);


 


    if (driveType == DRIVE_UNKNOWN)


        throw new ApplicationException(string.Format(“Unable to determine drive type for {0}”, driveRoot));


    else if (driveType == DRIVE_NO_ROOT_DIR)


        throw new ApplicationException(string.Format(“Invalid argument {0}”, driveRoot));


 


    return (driveType != DRIVE_REMOTE);


}


 


However, .NET 2.0 has DriveInfo support.  So, you can do same using .NET native code:


 


public bool IsLocalDrive(string path)


{


    bool result = false;


 


    System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(path);


    System.IO.DriveInfo[] drives = System.IO.DriveInfo.GetDrives();


    foreach (System.IO.DriveInfo driveInfo in drives)


    {


        if (string.Compare(driveInfo.RootDirectory.FullName, di.Root.FullName, true) == 0)


        {


            if (driveInfo.DriveType == System.IO.DriveType.Unknown)


                throw new ApplicationException(string.Format(“Unable to determine drive type for {0}”, path));


            else if (driveInfo.DriveType == System.IO.DriveType.NoRootDirectory)


                throw new ApplicationException(string.Format(“Invalid argument {0}”, path));


 


            result = (driveInfo.DriveType != System.IO.DriveType.Network);


 


            break;


        }


    }


 


    return result;


}


 

Comments (3)

  1. Peter Ritchie says:

    The benefit to using DriveInfo over PInvoke is that you don’t have to elevate your assembly’s permission to include UnmanagedCode permissions; which may not be possible in all policies.

    It also ensures the user has PathDiscovery permission, where the IsLocaDrive sample code does not.

  2. Gabe says:

    This is a nice function, but it is not good for security. A volume mount point could exist, allowing a path that looks like a hard drive actually point to a removable or network drive. For example, if you make C:CDROM point to a CD-ROM drive, you will look at C: and determine that it’s safe because it’s a local disk.

  3. irenake says:

    Excellent point.  One should also iterate through every folder (e.g. c:Folder1Folder2 would go through 3 checks — c:Folder1Folder2, c:Folder1 and c:) and check whether it’s a reparse point or not…  Since a junction point described by Gabe is just a reparse point, this kind of check will catch it.  To do that, just check File/Folder attribute ReparsePoint.  

Skip to main content