Sunday, July 4, 2010

An API for working with Windows symbolic links

What sort of an API would we like to have in a library for working with Windows symbolic links? I don't think it has to be terribly complicated. We could probably get by with 3 basic functions:


  1. NTFS_makelink(name, target) -- create a new symbolic link by the given name, pointing at the indicated target

  2. NTFS_islink(name) -- return 1 if there is a symbolic link by the given name, return 0 otherwise

  3. NTFS_readlink(name) -- return the target that is pointed to by the symbolic link by the given name



NTFS_makelink looks pretty straightforward: we just have to call CreateSymbolicLink. One bit of complexity is that we have to figure out if the link target is a directory, and pass the SYMBOLIC_LINK_FLAG_DIRECTORY flag if it is. Why do you suppose this flags argument exists? Couldn't the Windows API have figured that out for us, instead of making us figure it out ourselves?

NTFS_islink is slightly more complicated, but still not too hard: we call GetFileAttributes, then check the FILE_ATTRIBUTE_REPARSE_POINT flag to see if we have a reparse point. If we do, then we call FindFirstFile to get the WIN32_FIND_DATA for the file, and look in the dwReserved0 member of that structure for the value IO_REPARSE_TAG_MOUNT_POINT.

NTFS_readlink could be implemented in either of two ways:

  1. We could use DeviceIoControl to read the FSCTL_GET_REPARSE_POINT data, then look at the returned value to see what the target is.

  2. Or, we could use Raymond Chen's technique, and open the symbolic link, allowing the file system to automatically follow the reference and locate the target, then call GetFinalPathNameByHandle to figure out what file we got to.



Overall, this leaves us with an API looking something like:

// makes a symbolic link by the given name, pointing at the given target.
//
// returns 0 if all went well, <0 if there was a problem (call GetLastError()
// to find out the details of the error)
extern int NTFS_makelink(const char *name, const char *target);

// Checks the given name to see if it is a symbolic link.
//
// Returns 1 if it is a link, 0 if it is not, <0 if there was a problem
// (call GetLastError() to find out the details of the problem)
extern int NTFS_islink(const char *name);

// Fills in the provided buffer with the target of the given symbolic link.
//
// Copies the target into the buffer and null-terminates it, unless
// the target is longer than bufSize, in which case it is truncated
// and NOT null-terminated. So you probably want to pass an allocated
// buffer of size MAX_PATH to this function.
//
// Returns 0 if all went well, <0 if there was a problem (call GetLastError()
// to find out the details of the error)
extern int NTFS_readlink(const char *name, const char *targetBuf, int bufSize);


The next step, which will have to wait until next week sometime when I get access to my Windows 7 development machine, is to try writing this code, and seeing what it does :)

1 comment:

  1. Would Chen's technique work if the link pointed to a file that does not exist?

    ReplyDelete