Symlink okiem PowerShella

Postaram się zaprezentować pewne rozwiązanie, wiem może i stare, ale ostatnio byłem zmuszony go użyć w kilku kryzysowych sytuacjach. Stare dlatego, iż stało się ono popularne już w czasach wypuszczenia na rynek systemu Windows Server 2008 szczególnie w wersji Core. By nie trzymać dłużej w niepewności to mam tutaj na myśli dowiązania – zarówno twarde jak i miękkie. Dowiązania same w sobie zostały bardzo ciekawie opisane przez Paulę Januszkiewicz w artykule „Symlink, czyli MKLINK w Windows Server 2008”, więc o teorii nie będę się rozwodził. Pominę również kwestię omawiania narzędzia mklink, gdyż nie zamierzam przepisywać plików pomocy. W artykule tym nawiążę jedynie do pewnych alternatywnych metod wykorzystywania dowiązań z poziomu PowerShell’a.Na wstępie dla leniwych polecam gotowy pakiet PowerShell Community Extensions, która sama w sobie zawiera zestaw cmdletów do tworzenia „linków”. Ciekawskim jednak polecam zbadać sprawę od podszewki.

Całe rozwiązanie w głównej mierze opiera się o bibliotekę kernel32.dll, dzięki której możemy wykorzystać m.in. takie metody jak CreateHardLink czy CreateSymbolicLink.

Zacznijmy od dowiązań twardych. Jak wiemy z teorii jest to bezpośrednie wskazanie na konkretny plik. Aby jednak go stworzyć z poziomu PowerShella konieczne jest odwołanie się do biblioteki kernel32 wraz z wykorzystaniem funkcji CreateHardLink, jak np.:

$Definition = @'
[DllImport("kernel32.dll")]
	public static extern int CreateHardLink(
	string lpFileName,
	string lpExistingFileName,
	IntPtr lpSecurityAttributes);
'@

Wg dokumentacji funkcji CreateHardLink pierwszy parametr to nazwa nowego pliku będącego dowiązaniem. Drugi parametr to istniejący plik, do którego będziemy robić dowiązanie. Natomiast trzeci parametr pozostaje zarezerwowany dla systemu dlatego posiada wartość Null lub w naszym przypadku 0.

Jak widzimy sam kod pochodzi z języka C#, dlatego by wykorzystać go PowerShellu musimy zdefiniować go jako nowy typ w istniejącej sesji. Ostatecznie funkcję wywołujemy z wykorzystaniem operatora podwójnego dwukropka „::” – to wszystko 🙂

$type = Add-Type -MemberDefinition $Definition -Name WindowsAPI -PassThru
$type::CreateHardLink($PSHardLink, $OrgFile, 0)

Całość w przykładowym scenariuszu wygląda następująco:

$Definition = @'
[DllImport("kernel32.dll")]
	public static extern bool CreateHardLink(
	string lpFileName,
	string lpExistingFileName,
	IntPtr lpSecurityAttributes);
'@

$OrgFile = "P:\OrgFile.txt"
$PSHardLink = "P:\PS-HardLink.txt"
$MkLinkHardLink = "P:\MkLink-HardLink.txt"

"Hello World" | Set-Content $OrgFile
Get-Content $OrgFile | Write-Host -BackgroundColor Red

$type = Add-Type -MemberDefinition $Definition -Name WindowsAPI -PassThru
$type::CreateHardLink($PSHardLink, $OrgFile, 0)

#Test z użyciem mklink
Invoke-Command -ScriptBlock {& cmd /c "mklink /h $MkLinkHardLink $OrgFile"}

Get-Content $OrgFile | Write-Host -BackgroundColor Red
Get-Content $PSHardLink | Write-Host -BackgroundColor Red
Get-Content $MkLinkHardLink | Write-Host -BackgroundColor Red

"Hello PowerGeeks" | Set-Content $OrgFile

Get-Content $OrgFile | Write-Host -BackgroundColor Red
Get-Content $PSHardLink | Write-Host -BackgroundColor Red
Get-Content $MkLinkHardLink  | Write-Host -BackgroundColor Red

Zajmijmy się teraz dowiązaniami miekkimi. W odróżnieniu od poprzednich dowiązań, te mogą się odwoływać zarówno do pliku jak i do katalogu, a reprezentowane są w uproszczeniu jako skrót do obiektu docelowego.

Jak poprzednio importujemy bibliotekę kernel32 i definiujemy funkcję CreateSymbolicLink.

$Definition = @'
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
	public static extern int CreateSymbolicLink(
	string lpSymlinkFileName,
	string lpTargetFileName,
	uint dwFlags);
'@

Parametry mimo zmienionych nazw są zbliżone do tych z funkcji twardego dowiązania. Jedynym wyjątkiem jest tutaj ostatni parametr będący flagą definiującą czy tworzone dowiązanie dotyczy katalogu – 1, czy jednak pliku – 0.

Reszta jest analogiczna jak w poprzednim przypadku:

$Definition = @'
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
	public static extern int CreateSymbolicLink(
	string lpSymlinkFileName,
	string lpTargetFileName,
	uint dwFlags);
'@

$OrgFolder = "P:\OrgFolder"
$PSSoftLink = "P:\PS-HardLink"
$MkLinkSoftLink = "P:\MkLink-HardLink"
New-Item -ItemType Directory -Path $OrgFolder

$type = Add-Type -MemberDefinition $Definition -Name WindowsAPI -PassThru
$type::CreateSymbolicLink($PSSoftLink,$OrgFolder,1)

Invoke-Command -ScriptBlock {& cmd /c "mklink /d $MkLinkSoftLink $OrgFolder"}

Invoke-Command -ScriptBlock {& cmd /c "dir p:\ /al"}


Dla ciekawostki jeszcze. Aby utworzyć klasyczny skrót w formie pliku lnk możemy posłużyć się klasą WScript.Shell zawierającą metodę CreateShortCut. Gdzie podczas wywoływania metody wskazujemy ścieżkę do skrótu. Natomiast resztę parametrów skrótu przypisujemy odpowiednim atrybutom naszego obiektu.

$OrgFile = "c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"
$ShortCutFile = "P:\PowerShell.lnk"

$wshshell = new-object -comobject wscript.shell
$shortCut = $wshShell.CreateShortCut($ShortCutFile)
$shortCut.TargetPath = $OrgFile
$shortCut.Description = "Testowy Skrot"
$shortCut.WorkingDirectory = "P:\"
$shortCut.HotKey = "CTRL+SHIFT+P"
$shortCut.Arguments = "-version 2.0"
$shortCut.Save()


Źródła:
http://blogs.technet.com/b/plwit/archive/2009/01/14/symlink-czyli-mklink-w-windows-server-2008.aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363860(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa363866(v=vs.85).aspx

Advertisements

Skomentuj

Wprowadź swoje dane lub kliknij jedną z tych ikon, aby się zalogować:

Logo WordPress.com

Komentujesz korzystając z konta WordPress.com. Log Out / Zmień )

Zdjęcie z Twittera

Komentujesz korzystając z konta Twitter. Log Out / Zmień )

Facebook photo

Komentujesz korzystając z konta Facebook. Log Out / Zmień )

Google+ photo

Komentujesz korzystając z konta Google+. Log Out / Zmień )

Connecting to %s