Using NTFS Junctions in C#

The work I will blog about is based on the work that Mark Russinovich did with SysInternals. I used Mark's Junction.exe as my use case/scenario to guide the work I did and found some source at www.flexhex.com.

NTFS junction points are a type of reparse point where you can link to local directories. There are utilities out that allow you to create and delete junction points. You can find reference to them in this Microsoft Support Article: https://support.microsoft.com/kb/205524.

What I did was wrote C# code using Platform Invoke Support to call into NTFS to create, delete, and interrogate junction points. No rocket science but a challenge when you don't have access to the source code - that includes me.

In this post I will show you the layout of the reparse structures in C#. You can find references to this on the Internet, such as https://www.flexhex.com/docs/articles/hard-links.phtml, and in WinNT.h, WinIoCtrl.h, winbase.h. Much of the rest are just standard file i/o values.

To start you will need to define some maximum values.

// Our maximum reparse point name size
private const int MAX_NAME_LENGTH = 1024;
private const int MAX_PATH = 260;
// Maximum reparse buffer info size. The max user defined reparse
// data is 16KB, plus there's a header.
private const int MAX_REPARSE_SIZE = 17000;

// winnt.h
private const uint IO_REPARSE_TAG_SYMBOLIC_LINK = (0);
private const uint IO_REPARSE_TAG_MOUNT_POINT = (0xA0000003);
private const uint IO_REPARSE_TAG_HSM = (0xC0000004);
private const uint IO_REPARSE_TAG_SIS = (0x80000007);
private const uint IO_REPARSE_TAG_DFS = (0x8000000A);

private const uint REPARSE_MOUNTPOINT_HEADER_SIZE = 8;

// from WinIoCtl.h
private const uint FSCTL_SET_REPARSE_POINT = 589988;
private const uint FSCTL_GET_REPARSE_POINT = 589992;

// from winbase.h
private const int INVALID_HANDLE_VALUE = -1;
private const uint GENERIC_READ = 0x80000000;
private const uint GENERIC_WRITE = 0x40000000;
private const uint GENERIC_EXECUTE = 0x20000000;
private const uint GENERIC_ALL = 0x10000000;

//Sharing
private const uint FILE_SHARE_NONE = 0x00000000;
private const uint FILE_SHARE_READ = 0x00000001;
private const uint FILE_SHARE_WRITE = 0x00000002;
private const uint FILE_SHARE_DELETE = 0x00000004;

private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
private const uint FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400 ;

//Create
private const uint CREATE_NEW = 1;
private const uint CREATE_ALWAYS = 2;
private const uint OPEN_EXISTING = 3;
private const uint OPEN_ALWAYS = 4;
private const uint TRUNCATE_EXISTING = 5;

//Reparse
private const uint FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;

In my next post I will show you some classes you need to create and the signatures for the PInvoke calls.