Symbolic Links, Junctions, Hard Links
Hi, I have created a set of functions to deal with symbolic links, junctions and hard links. These functions are able to create them, delete them and identify them. Treat it as an alpha, because it has been tested only on latest version of XP and 7 (32-bit) and outside of that, there are probably more bugs. It may also not work on Ansi version of NSIS. Let me know if you find any bugs. First save this to "Junction.nsh":
; miscThen you can use these functions:
>!define CreateParentFolder "!insertmacro CreateParentFolder"
>!macro CreateParentFolder Path
Push$1
${GetParent} "${Path}" $1
CreateDirectory "$1"
Pop $1
>!macroend
>; info
>!define IsLink "!insertmacro IsLink"
>!define IsSoftLink "!insertmacro IsSoftLink"
>!define IsHardLink "!insertmacro IsHardLink"
>Function IsSoftLink
Exch$0
${GetFileAttributes} "$0" "REPARSE_POINT" $0
${If} $0 != "1"
StrCpy $0 "0"
${EndIf}
Exch $0
FunctionEnd
>!macro IsSoftLink Path outVar
Push "${Path}"
Call IsSoftLink
Pop${outVar}
!macroend
>Function IsHardLink
Exch$1
System::Call "kernel32::CreateFileW(w `$1`, i 0x40000000, i 0, i 0, i 3, i 0, i 0) i .r0"
${If} $0 = "-1"
StrCpy $0 "0"
goto is_hard_link_end
${EndIf}
System::Call "*(&i256 0) i. r1"
System::Call "kernel32::GetFileInformationByHandle(i r0, i r1) i .s"
System::Call "kernel32::CloseHandle(i r0) i.r0"
Pop $0
${If} $0 == "0"
goto is_hard_link_end
${EndIf}
System::Call "*$1(&i40 0, &i4 .r0)"
${If} $0 != "0"
IntOp $0 $0 - 1
${EndIf}
is_hard_link_end:
Pop $1
FunctionEnd
>!macro IsHardLink Path outVar
Push$0
Push "${Path}"
Call IsHardLink
StrCpy${outVar} $0
Pop$0
>!macroend
>!macro IsLink Path outVar
${IsSoftLink} "${Path}" ${outVar}
${If} ${outVar} == 0
${IsHardLink} "${Path}" ${outVar}
${EndIf}
!macroend
>; files
>!define CreateHardLink "!insertmacro CreateHardLink"
>!define CreateSymbolicLinkFile "!insertmacro CreateSymbolicLinkFile"
>!define CreateLinkFile "!insertmacro CreateLinkFile"
>!define DeleteLinkFile "!insertmacro DeleteLinkFile"
>!macro CreateSymbolicLinkFile Junction Target outVar
${CreateParentFolder} "${Junction}"
System::Call "kernel32::CreateSymbolicLinkW(w `${Junction}`, w `${Target}`, i 0) i .s"
Pop ${outVar}
${If} ${outVar} == "error"
StrCpy ${outVar} "0"
${EndIf}
!macroend
>!macro CreateHardLink Junction Target outVar
${CreateParentFolder} "${Junction}"
System::Call "kernel32::CreateHardLinkW(w `${Junction}`, w `${Target}`, i 0) i .s"
Pop ${outVar}
!macroend
>!macro CreateLinkFile Junction Target outVar
${CreateSymbolicLinkFile} "${Junction}" "${Target}" ${outVar}
${If} ${outVar} == 0
${CreateHardLink} "${Junction}" "${Target}" ${outVar}
${EndIf}
!macroend
>!macro DeleteLinkFile Path outVar
${IsLink} "${Path}" ${outVar}
${If} ${outVar} != 0
SetFileAttributes "${Path}" "NORMAL"
System::Call "kernel32::DeleteFileW(w `${Path}`) i.s"
Pop ${outVar}
${EndIf}
!macroend
>; folders
>!define CreateJunction "!insertmacro CreateJunction"
>!define CreateSymbolicLinkFolder "!insertmacro CreateSymbolicLinkFolder"
>!define CreateLinkFolder "!insertmacro CreateLinkFolder"
>!define DeleteLinkFolder "!insertmacro DeleteLinkFolder"
>Function CreateJunction
Exch$4
Exch
Exch$5
Push$1
Push$2
Push$3
Push$6
CreateDirectory "$5"
System::Call "kernel32::CreateFileW(w `$5`, i 0x40000000, i 0, i 0, i 3, i 0x02200000, i 0) i .r6"
${If} $0 = "-1"
StrCpy $0 "0"
RMDir "$5"
goto create_junction_end
${EndIf}
CreateDirectory "$4" ; Windows XP requires that the destination exists
StrCpy$4 "\??\$4"
StrLen $0 $4
IntOp$0 $0 * 2
IntOp$1 $0 + 2
IntOp$2 $1 + 10
IntOp$3 $1 + 18
System::Call "*(i 0xA0000003, &i4 $2, &i2 0, &i2 $0, &i2 $1, &i2 0, &w$1 `$4`, &i2 0)i.r2"
System::Call "kernel32::DeviceIoControl(i r6, i 0x900A4, i r2, i r3, i 0, i 0, *i r4r4, i 0) i.r0"
System::Call "kernel32::CloseHandle(i r6) i.r1"
${If} $0 == "0"
RMDir "$5"
${EndIf}
create_junction_end:
Pop $6
Pop$3
Pop$2
Pop$1
Pop$5
Pop$4
FunctionEnd
>!macro CreateJunction Junction Target outVar
Push$0
Push "${Junction}"
Push "${Target}"
Call CreateJunction
StrCpy${outVar} $0
Pop$0
>!macroend
>!macro CreateSymbolicLinkFolder Junction Target outVar
${CreateParentFolder} "${Junction}"
System::Call "kernel32::CreateSymbolicLinkW(w `${Junction}`, w `${Target}`, i 1) i .s"
Pop ${outVar}
${If} ${outVar} == "error"
StrCpy ${outVar} "0"
${EndIf}
!macroend
>!macro CreateLinkFolder Junction Target outVar
${CreateSymbolicLinkFolder} "${Junction}" "${Target}" ${outVar}
${If} ${outVar} == 0
${CreateJunction} "${Junction}" "${Target}" ${outVar}
${EndIf}
!macroend
>!macro DeleteLinkFolder Path outVar
${IsSoftLink} "${Path}" ${outVar}
${If} ${outVar} != 0
SetFileAttributes "${Path}" "NORMAL"
System::Call "kernel32::RemoveDirectoryW(w `${Path}`) i.s"
Pop ${outVar}
${EndIf}
!macroend
>
All of the above returns "0" on fail and anything else (usually 1, but not always) on success."Junction.nsh"
IsSoftLink} "${Path}" ${outVar}
>; checks if target is hard link
>${IsHardLink} "${Path}" ${outVar}
;checks if target is a soft link (symbolic link or junction)
${
;checks if target is either a hard link or a soft link
>${IsLink} "${Path}" ${outVar}
;creates a hard link (file only, must be on the same volume, and target must exist)
${CreateHardLink} "${Junction}" "${Target}" ${outVar}
;creates a symbolic link for a file (Vista+, target doesn't need to exist and can be anywhere)
${CreateSymbolicLinkFile} "${Junction}" "${Target}" ${outVar}
; tries to create a symbolic link first and when it fails, then it tries to create a hard link (files only)
${CreateLinkFile} "${Junction}" "${Target}" ${outVar}
; creates a symbolic link for a folder (Vista+, target doesn't need to exist)
${CreateSymbolicLinkFolder} "${Junction}" "${Target}" ${outVar}
;creates a junction (folders only, path must be absolute and target should exist)
${CreateJunction} "${Junction}" "${Target}" ${outVar}
;tries to create symbolic link first and when it fails, then it tries to create a junctions (directories only)
${CreateLinkFolder} "${Junction}" "${Target}" ${outVar}
;checks if a folder is a junction or a symbolic link, and if it is, it deletes it
>${DeleteLinkFolder} "${Path}" ${outVar}
;checks if a file is a symbolic link or a hard link, and if it is, it deletes it
>${DeleteLinkFile} "${Path}" ${outVar}
Some basic differences between these three guys are:
all 3 of them:
- must be located on a NTFS volume (but not necessarily their target)
- system must be Windows 2000 or newer
Hard links:
- only for files
- both the link and the target must be on the same volume and must exist
- path must be absolute
- it is indistinguishable from the original file and doesn't really act like a link, but rather like another copy of the target file (except that when you edit one, both get changed; but you need to delete both to get that file actually deleted)
Junctions:
- only for folders
- in XP and older, when they are deleted in the explorer, the target gets wiped out as well (see wikipedia entry on junctions)
- path must be absolute, target can be anywhere
- on Win XP SP 4, creation fails if the target doesn't exist, on Win 7, it gets created (didn't try anywhere else)
Symbolic Links:
- only Vista and newer
- supports both files and folders
- target can be anywhere and doesn't need to exist
- path can be relative or absolute
more info:
http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
http://msdn.microsoft.com/en-us/libr...(v=vs.85).aspx
http://en.wikipedia.org/wiki/NTFS_junction_point
http://en.wikipedia.org/wiki/NTFS_symbolic_link