Detecting a Symbolic Link

Started by td, January 23, 2018, 03:04:45 PM

Previous topic - Next topic

td

A user recently asked about how a WinBatch script could detect a symbolic link.  Happened to stumble across this lightly documented technique on  MSFT's website.   Don't know how much if any interest there is but thought I would share it anyway.  Error handling is not included for brevity.

Code (winbatch) Select
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; IsSymLink
;;
;; Determines if a file or directory is
;; a sybolic link.
;;   _strFile - full path-name of file or
;;              directory.
;;
;; Returns true if input is a symbolic link.
#DefineFunction IsSymLink(_strFile)
   WIN32_FIND_DATA = $"
   DWORD::dwFileAttributes;
   DWORD::ftCreationTimeHigh;
   DWORD::ftCreationTimeLow;
   DWORD::ftLastAccessTimeHigh;
   DWORD::ftLastAccessTimeLow;
   DWORD::ftLastWriteTimeHigh;
   DWORD::ftLastWriteTimeLow;
   DWORD::nFileSizeHigh;
   DWORD::nFileSizeLow;
   DWORD::dwReserved0;
   DWORD::dwReserved1;
   CHAR::cFileName[260];
   CHAR::cAlternateFileName[14];
   $"
   FILE_ATTRIBUTE_REPARSE_POINT = 1024
   IO_REPARSE_TAG_SYMLINK = 2684354572 ; = -1610612724
   pFindData = DllStructAlloc(WIN32_FIND_DATA)

   hSearch = DllCall('kernel32.dll', long:'FindFirstFileA', lpstr:_strFile, lpstruct:pFindData)
   if hSearch > -1
      DllCall('kernel32.dll', long:'FindClose', long:hSearch)
      FileAttribute = DllStructPeek(pFindData, 'dwFileAttributes')
      Reserved0      = DllStructPeek(pFindData, 'dwReserved0')
      DllStructFree(pFindData)

      return FileAttribute&FILE_ATTRIBUTE_REPARSE_POINT && Reserved0==IO_REPARSE_TAG_SYMLINK
   Endif

   return 0
#EndFunction


; Test
strFile = 'c:\temp\Link.txt'

bResult = IsSymLink( strFile )
if bResult then strText = 'is a symbolic link'
else strText = ' is NOT a symbolic link'
Message(strFile, strText)
   
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

ChuckC

That looks to essentially be correct.  The standard attributes bit mask contains the FILE_ATTRIBUTE_REPARSE_POINT flag bit if the directory or file is actually a reparse point of some type.  What I found thru some testing across a variety of versions of Windows and various NAS devices is that ferreting out the Reparse Point ID Tag value from the Reserved0 field in the structure wasn't always a 100% reliable method.

To avoid any issues of that type, there is a further call that you can make to DeviceIoControl() with an open handle to a directory or file, using the control code FSCTL_GET_REPARSE_POINT [need to have the handle open with read extended attributes access], which will return a REPARSE_DATA_BUFFER structure [declared in the Windows DDK, not the Platform SDK], which reliably contains the Reparse Point ID Tag as well as all of the other data requires to fully decode reparse point so that the target directory/file can be identified for a Junction, Directory Symbolic Link, File Symbolic Link, Volume Mount Point and DFS Link, as well as target data for other less commonly used types of reparse points [SIS stubs, HSM stubs, DeDup stubs, etc...] that may or may not be at least partially decoded into something meaningful.

td

I am aware of the  DeviceIoControl() approach but your feedback is appreciated.   In use, the method above has proven to be very reliable with respect to symbolic links on local system NTFS file systems on newer versions of Windows for both file and directory symbolic links.     
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade