Recently received an inquiry from the field about how to find out if a data on a cluster contains symlinks, and thought it was worth sharing.
Probably the simplest method to determine whether a set of data contains symbolic links is by using the ‘find’ utility from the OneFS command line interface, or from an NFS client. The following syntax will confirm that symlinks are present under a directory (in this case /ifs/test), and provide a count of the number of links the dataset contains:
# find /ifs/test -type l | wc -l
OneFS (and most Linux flavors) has the extended find command, where the –ls flag will return the link, target path and metadata for each symlink:
# find /ifs/test -type l -ls
438343 0 lrwxr-xr-x 1 root wheel 26 Jul 18 14:27 /ifs/test/slink1 -> /ifs/home1/file1
438352 0 lrwxr-xr-x 1 root wheel 26 Jul 18 14:31 /ifs/test/dir1/slink2 -> /ifs/home2/file2
453127 0 lrwxr-xr-x 1 root wheel 26 Jul 18 14:31 /ifs/test/dir2/slink3 -> /ifs/home3/file3
364228 0 lrwxr-xr-x 1 root wheel 26 Jul 18 14:27 /ifs/test/slink4 -> /ifs/home4/file4
For UNIX clients that don’t support the extended find command, the following syntax will provide similar output:
# find /ifs/test -type l -exec ls -al {} ;
lrwxr-xr-x 1 root wheel 26 Jul 18 14:27 /ifs/test/slink1 -> /ifs/home1/file1
lrwxr-xr-x 1 root wheel 26 Jul 18 14:31 /ifs/test/dir1/slink2 -> /ifs/home2/file2
lrwxr-xr-x 1 root wheel 26 Jul 18 14:31 /ifs/test/dir2/slink3 -> /ifs/home3/file3
lrwxr-xr-x 1 root wheel 26 Jul 18 14:27 /ifs/test/slink4 -> /ifs/home4/file4
The find command can easily be filtered to return just the symlink and target file. For example:
# find /ifs/symmlink -type l -exec ls -al {} ; | awk -F ‘ ‘ ‘{print $9,$10,$11}’
/ifs/test/slink1 -> /ifs/home1/file1
/ifs/test/dir1/a.txt -> /ifs/home2/file2
/ifs/test/dir2/c.txt -> /ifs/home3/file3
/ifs/test/slink4 -> /ifs/home4/file4
Symbolic linking (or soft linking) has been a feature in POSIX operating systems for decades, but was only fully added to Windows and the NFTS filesystem much later, in the Vista and SMB2 timeframe.
From the Windows command shell, the following command will show the symbolic links (or ‘reparse points’ in Windows vernacular) on an SMB share mapped to drive O:
> dir /AL /S O:
Or, using Windows PowerShell instead:
> Dir O: -Force –Recurse –ErrorAction ‘silentlycontinue’ |
Where { $_.Attributes –match “ReparsePoint”}
Symlinks are represented as follows in the ‘dir’ output:
07/14/2018 04:01 PM <SYMLINK> slink1.txt [file1.txt]
Unlike shortcut files, Windows symbolic links have no size and are not transparent to all applications. While symbolic links do directly connect to the data blocks of their target, their handling is determined by the symbolic link’s file extension. For example, a symbolic link created using:
>mklink slink1.jpg file1.doc
It’s worth noting that when accessing ‘slink1.jpg’ above, a Windows host will attempt to open the file with the default viewer for .jpg files rather than .doc files.
Windows symlinks are created via the SMB2 protocol by writing a regular file in the initial Create request, then converting it into a symlink with a ‘Set Reparse Point’ request.
Whenever a server completing and SMB2 request encounters a symlink, it returns a ‘Symbolic Link Error Response’, which includes the following:
- Target path (stored in the inode)
- The remaining section of the original file path
- Whether the target path is relative or absolute.
From here, the client is expected to formulate another request to the server using the details from this response. If there are three symlinks in the path, this process will be repeated three times as each link in the path is reached.
As with the UNIX ‘ls –l’ command, Windows ‘dir’ command will also display a symlink target. When performing the dir command, a find request of info level SMB2_FIND_ID_BOTH_DIRECTORY_INFO is used to enumerate all the files in a directory. Included with this are file attributes, which can identify a file as a reparse point.
Windows and SMB make a distinction between links to files and links to directories. Once a link is set to be of a certain type, this flag cannot change. Directory links are fully supported in OneFS 8.0 and beyond.
Other Windows link constructs include ‘widelinks’ and ‘absolute links’. Absolute links have no parallel in POSIX, and are links to specific drive letters or UNC paths. OneFS 7.2 and later supports absolute links to both UNC paths and drive letters.
A widelink is a reparse point that is constructed using a lookup table and is designed for interoperability between SMB and POSIX systems.
For instance, if the translation is:
/mnt/serverA ===> \serverA
Then the same link can point to 2 different places:
link1 -> /mnt/serverA/share1/ (NFS)
link1 -> \serverAshare1 (SMB)
Note: OneFS does not yet support widelinks.
OneFS provides the ability to disable all SMB symlink features via a registry key. This key is called SMB2Symlinks and can be accessed using the OnefsConfigSMB2Symlinks function. To enable or disable symlinks, run the lwregshell utility from the OneFS CLI as follows:
# cd /usr/likewise/bin
# ./lwregshell
> cd HKEY_THIS_MACHINEServiceslwioParametersDriversonefs
ls /* to check the current value */
set_value “SMB2Symlinks” 0 /* 0 to disable, 1 to enable */
Returning to the original question, we demonstrated how to determine if there are symlinks with a OneFS dataset. However, there is no simple way to determine whether Windows clients are actually trying to use and/or create them. That said, there are a couple of options available:
- Trace the client’s activity via the Windows “Process Monitor” tool.
- Take packet captures and analyze them with a protocol sniffer like Wireshark to find the appropriate RPCs.
For example, when selecting a linked file, a windows client creates a request which includes the name of the symbolic link. The cluster then sends a response of an error type 0x8000002D (Note: Wireshark doesn’t appear to know how to classify this error and instead designates it as a malformed packet).
Length of Response-¬
Start-¬ Reserved ⌐-Byte Count
0070 00 00 00 00 00 00 00 00 00 00│09 00│00 00│a8 00 …………….
SymLink Length¬ SymLink ErrTag¬ Reparse Tag ⌐Reparse Data Length
0080 00 00│a4 00 00 00│53 59 4d 4c│0c 00 00 a0│98 00│ ……SYML……
A-¬ B-¬ C-¬ D-¬ E¬ Flags¬ Path Buffer¬ Print Name-¬
0090 00 00│46 00│46 00│00 00│46 00│01 00 00 00│5c 00 ..F.F…F……
00a0 74 00 65 00 73 00 74 00 5c 00 64 00 6f 00 63 00 t.e.s.t..d.o.c.
00b0 73 00 4c 00 69 00 6e 00 6b 00 5c 00 57 00 69 00 s.L.i.n.k..W.i.
00c0 6e 00 4c 00 69 00 6e 00 6b 00 5c 00 74 00 65 00 n.L.i.n.k..t.e.
00d0 73 00 74 00 46 00 69 00 6c 00 65 00 2e 00 74 00 s.t.F.i.l.e…t.
Substitute Name-¬
00e0 78 00 74 00 5c 00 74 00 65 00 73 00 74 00 5c 00 x.t..t.e.s.t..
00f0 64 00 6f 00 63 00 73 00 4c 00 69 00 6e 00 6b 00 d.o.c.s.L.i.n.k.
0100 5c 00 57 00 69 00 6e 00 4c 00 69 00 6e 00 6b 00 .W.i.n.L.i.n.k.
0110 5c 00 74 00 65 00 73 00 74 00 46 00 69 00 6c 00 .t.e.s.t.F.i.l.
0120 65 00 2e 00 74 00 78 00 74 00 e…t.x.t.
SymLink Length: The length of the response excluding itself.
Symlink ErrTag: Must be set to 0x4C4D5953.
Reparse Tag: Type of link, must be set to 0xA000000C.
Reparse Data Length: The size in bytes of PathBuffer+A+B+C+D+Flags.
A: Unparsed Path Length: The length in bytes of the unparsed portion of the path remaining after the symbolic link. As the link is
at file1.txt, this is 0.
B: Substitute Name Offset: The offset in bytes from the beginning of the path buffer field at which the substitute name is located.
C: Substitute Name Length: The length in bytes of the substitute name.
D: Print Name Offset: The offset from the path buffer field at which the user friendly name is located.
E: Print Name Length.
Flags: Defines if the substitute name is relative or absolute. 0x00000001 = SYMLINK_FLAG_RELATIVE while 0 = absolute.
Path Buffer: Variable length unicode string holding the substitute and print name. For an absolute name, the format must be
??UNCservershare…, not a local evaluation. According to the rules of the error, a relative path should not start with ,
but apparently that is not true in this case.
In this example, a Windows client is searching a cluster share for a symbolic link named ‘testfile1.txt’, pointing to ‘testdocsLinkWinLinktestFile.txt’.