1 #Region "Microsoft.VisualBasic::3657912b74e191d47455cc60f5792cc7, Microsoft.VisualBasic.Core\Extensions\IO\SymLinker\SymbolicLink.vb"
2
3     ' Author:
4     
5     '       asuka (amethyst.asuka@gcmodeller.org)
6     '       xie (genetics@smrucc.org)
7     '       xieguigang (xie.guigang@live.com)
8     
9     ' Copyright (c) 2018 GPL3 Licensed
10     
11     
12     ' GNU GENERAL PUBLIC LICENSE (GPL3)
13     
14     
15     ' This program is free software: you can redistribute it and/or modify
16     ' it under the terms of the GNU General Public License as published by
17     ' the Free Software Foundation, either version 3 of the License, or
18     ' (at your option) any later version.
19     
20     ' This program is distributed in the hope that it will be useful,
21     ' but WITHOUT ANY WARRANTY; without even the implied warranty of
22     ' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23     ' GNU General Public License for more details.
24     
25     ' You should have received a copy of the GNU General Public License
26     ' along with this program. If not, see <http://www.gnu.org/licenses/>.
27
28
29
30     ' /********************************************************************************/
31
32     ' Summaries:
33
34     '     Module SymbolicLink
35     
36     '         Function: CreateFile, CreateSymbolicLink, DeviceIoControl, Exists, getFileHandle
37     '                   GetTarget
38     
39     '         Sub: CreateDirectoryLink, CreateFileLink
40     '         Structure SymbolicLinkReparseData
41     
42     
43     
44     
45     
46     
47     ' /********************************************************************************/
48
49 #End Region
50
51 Imports Microsoft.Win32.SafeHandles
52 Imports System.IO
53 Imports System.Runtime.InteropServices
54 Imports System.Text
55
56 Namespace FileIO.SymLinker
57
58     ''' <summary>
59     ''' 
60     ''' </summary>
61     ''' <remarks>https://github.com/amd989/Symlinker</remarks>
62     Public Module SymbolicLink
63
64         Private Const genericReadAccess As UInteger = &H80000000UI
65
66         Private Const fileFlagsForOpenReparsePointAndBackupSemantics As UInteger = &H2200000
67
68         Private Const ioctlCommandGetReparsePoint As Integer = &H900A8
69
70         Private Const openExisting As UInteger = &H3
71
72         Private Const pathNotAReparsePointError As UInteger = &H80071126UI
73
74         ''' <summary>
75         ''' Read, Write, Delete
76         ''' </summary>
77         Private Const shareModeAll As UInteger = &H7
78
79         ''' <summary>
80         ''' 
81         ''' </summary>
82         Private Const symLinkTag As UInteger = &HA000000CUI
83
84         Private Const targetIsAFile As Integer = 0
85
86         Private Const targetIsADirectory As Integer = 1
87
88         <DllImport("kernel32.dll"SetLastError:=True)>
89         Private Function CreateFile(lpFileName As String, dwDesiredAccess As UInteger, dwShareMode As UInteger, lpSecurityAttributes As IntPtr, dwCreationDisposition As UInteger, dwFlagsAndAttributes As UInteger,
90             hTemplateFile As IntPtr) As SafeFileHandle
91         End Function
92
93         <DllImport("kernel32.dll"SetLastError:=True)>
94         Private Function CreateSymbolicLink(lpSymlinkFileName As String, lpTargetFileName As String, dwFlags As IntegerAs Boolean
95         End Function
96
97         <DllImport("kernel32.dll"CharSet:=CharSet.Auto, SetLastError:=True)>
98         Private Function DeviceIoControl(hDevice As IntPtr, dwIoControlCode As UInteger, lpInBuffer As IntPtr, nInBufferSize As Integer, lpOutBuffer As IntPtr, nOutBufferSize As Integer,
99             ByRef lpBytesReturned As Integer, lpOverlapped As IntPtr) As Boolean
100         End Function
101
102         <StructLayout(LayoutKind.Sequential)>
103         Public Structure SymbolicLinkReparseData
104             Not certain about this!
105             Private Const maxUnicodePathLength As Integer = 260 * 2
106
107             Public ReparseTag As UInteger
108             Public ReparseDataLength As UShort
109             Public Reserved As UShort
110             Public SubstituteNameOffset As UShort
111             Public SubstituteNameLength As UShort
112             Public PrintNameOffset As UShort
113             Public PrintNameLength As UShort
114             Public Flags As UInteger
115
116             <MarshalAs(UnmanagedType.ByValArray, SizeConst:=maxUnicodePathLength)>
117             Public PathBuffer As Byte()
118         End Structure
119
120         Public Sub CreateDirectoryLink(linkPath As String, targetPath As String)
121             If Not CreateSymbolicLink(linkPath, targetPath, targetIsADirectory) OrElse Marshal.GetLastWin32Error() <> 0 Then
122                 Try
123                     Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error())
124                 Catch exception As COMException
125                     Throw New IOException(exception.Message, exception)
126                 End Try
127             End If
128         End Sub
129
130         Public Sub CreateFileLink(linkPath As String, targetPath As String)
131             If Not CreateSymbolicLink(linkPath, targetPath, targetIsAFile) Then
132                 Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error())
133             End If
134         End Sub
135
136         Public Function Exists(path As StringAs Boolean
137             If Not Directory.Exists(path) AndAlso Not File.Exists(path) Then
138                 Return False
139             End If
140             Dim target As String = GetTarget(path)
141             Return target IsNot Nothing
142         End Function
143
144         Private Function getFileHandle(path As StringAs SafeFileHandle
145             Return CreateFile(path, genericReadAccess, shareModeAll, IntPtr.Zero, openExisting, fileFlagsForOpenReparsePointAndBackupSemantics,
146                 IntPtr.Zero)
147         End Function
148
149         Public Function GetTarget(path As StringAs String
150             Dim reparseDataBuffer As SymbolicLinkReparseData
151
152             Using fileHandle As SafeFileHandle = getFileHandle(path)
153                 If fileHandle.IsInvalid Then
154                     Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error())
155                 End If
156
157                 Dim outBufferSize As Integer = Marshal.SizeOf(GetType(SymbolicLinkReparseData))
158                 Dim outBuffer As IntPtr = IntPtr.Zero
159                 Try
160                     outBuffer = Marshal.AllocHGlobal(outBufferSize)
161                     Dim bytesReturned As Integer
162                     Dim success As Boolean = DeviceIoControl(fileHandle.DangerousGetHandle(), ioctlCommandGetReparsePoint, IntPtr.Zero, 0, outBuffer, outBufferSize,
163                         bytesReturned, IntPtr.Zero)
164
165                     fileHandle.Close()
166
167                     If Not success Then
168                         If CUInt(Marshal.GetHRForLastWin32Error()) = pathNotAReparsePointError Then
169                             Return Nothing
170                         End If
171                         Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error())
172                     End If
173
174                     reparseDataBuffer = CType(Marshal.PtrToStructure(outBuffer, GetType(SymbolicLinkReparseData)), SymbolicLinkReparseData)
175                 Finally
176                     Marshal.FreeHGlobal(outBuffer)
177                 End Try
178             End Using
179             If reparseDataBuffer.ReparseTag <> symLinkTag Then
180                 Return Nothing
181             End If
182
183             Dim target As String = Encoding.Unicode.GetString(reparseDataBuffer.PathBuffer, reparseDataBuffer.PrintNameOffset, reparseDataBuffer.PrintNameLength)
184
185             Return target
186         End Function
187     End Module
188 End Namespace