Microsoft Virtualization Discussions

Unlocking files via Powershell

rlackner88
46,027 Views

Anybody been able to setup a Powershell script to unlock files? I'm thinking something that a user could run to unlock files that are locked under that user's username. We use Citrix, and sometimes a user will lose connection to a session, then not be able to get back to it -- some files are locked open in that other session, and, rather than calling the Helpdesk, etc., it would be useful for that user to unlock certain files just by running a script (e.g., a ~notes.lck file). Thanks.

1 ACCEPTED SOLUTION

mbeattie
46,027 Views

Hi,

There is no need to create a share, psfile translates "/vol/vol1/qtree1/database.mdb" as "C:\vol\vol1\qtree1\database.mdb". Here is an example in powershell:

# http://technet.microsoft.com/en-us/sysinternals/bb897552.aspx

# Note that this script relies on "psfile.exe" existing in the windows system directory

#'------------------------------------------------------------------------------

#'Initialization Section. Define Global Variables.

#'------------------------------------------------------------------------------

[String]$uncPath    = "\\testns01\test1$\databases\admin\database.mdb"

[String]$scriptPath = Split-Path($MyInvocation.MyCommand.Path)

[String]$scriptSpec = $MyInvocation.MyCommand.Definition

[String]$scriptName = (Get-Item $scriptSpec).Name

[String]$systemPath = [Environment]::SystemDirectory

[String]$exeSpec    = "$systemPath\cmd.exe"

[String]$exeName    = "psfile.exe"

[Array]$elements    = $uncPath -Split [regex]::Escape("\")

[String]$controller = $elements[2]

[String]$shareName  = $elements[3]

[String]$fileName   = $elements[$elements.Length -1]

#'------------------------------------------------------------------------------

#'construct the folder path between the share name and file name.

#'------------------------------------------------------------------------------

Write-Host "The Script ""$scriptName"" Started Processing."

Write-Host "Processing file ""$uncPath"""

[String]$folderPath = "/"

For($i=4; $i -lt ($elements.Count -1); $i++){

   [String]$folderPath = $folderPath + $elements[$i] + "/"

}

#'------------------------------------------------------------------------------

#'Ensure psfile.exe exists in the scripts system directory.

#'------------------------------------------------------------------------------

If(-Not(Test-Path -Path "$systemPath\$exeName")){

   Write-Host "The file ""$systemPath\$exeName"" does not exist"

   Break;

}

#'------------------------------------------------------------------------------

#'Enumerate the mount point of the share and file

#'------------------------------------------------------------------------------

Import-Module DataOnTap

Connect-NaController -Name $controller | Out-Null

[String]$mountPoint = Get-NaCifsShare | Where-Object {$_.ShareName -eq $shareName} | Select-Object -ExpandProperty MountPoint

[String]$mountPath  = "$mountPoint$folderPath$fileName"

$file               = Get-NaLockStatus | Where-Object {$_.Mode -eq "Oplock-Excl" -And $_.Path -eq $mountPath}

[String]$filePath   = $file.Path

[String]$owner      = $file.Owner

Write-Host "Enumerated the mount point for CIFS share ""$shareName"" as ""$mountPoint"""

Write-Host "Enumerated CIFS Locked file as ""$filePath"" locked by ""$owner"""

#'------------------------------------------------------------------------------

#'Replace the foward slash with back slash characters to build the local file path.

#'------------------------------------------------------------------------------

[String]$fileSpec = "C:" + ($filePath -Replace ([regex]::Escape("/"),"\"))

[String]$command  = "$exeName \\$controller ""$fileSpec"" -c"

Start-Process $exeSpec -ArgumentList " /c $command"

Write-Host "Executed Command: $command"

Write-Host "The Script ""$scriptName"" Completed Successfully."

Here is an example of the script output:

PS C:\Scripts\PowerShell\Projects\CloseLockedFile> .\CloseLockedFile.ps1

The Script "test.ps1" Started Processing.

Processing file "\\testns01\test1$\databases\admin\database.mdb"

Enumerated the mount point for CIFS share "test1$" as "/vol/vol1/qtree1"

Enumerated CIFS Locked file as "/vol/vol1/qtree1/Databases/Admin/database.mdb" locked by "administrator"

Executed Command: psfile.exe \\testns01 "C:\vol\vol1\qtree1\Databases\Admin\database.mdb" -c

The Script "CloseLockedFile.ps1" Completed Successfully.

PS C:\Scripts\PowerShell\Projects\CloseLockedFile>

Hope that helps

Cheers Matt

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

View solution in original post

6 REPLIES 6

mbeattie
46,027 Views

Hey,

Actually I'm working on a solution to do this right...there appears to be a cmdlet to unlock NFS locks but no CIFS??? If you want to provide standard users with the ability to do this then you will probably need to have a service account run the script as a scheduled task or use an orchestration tool like NetApp WFA product to build a workflow that accepts the UNC path of the file to close. At a first glance it will probably require a few cmdlet's to translate the UNC path to mount point and find out who has the file locked

Get-NaLockStatus [[-Protocol] <String>] [[-FileName] <String>]

Get-NaCifsShare [[-Share] <String>] [-Controller <NaController>] [<CommonParameters>]

$command = “lock break -p cifs -f /vol/vol1/qtree1/file.mdb”

Invoke-NaSsh $command

Watch this space...I'll post back when i've had time to develop something

Cheers Matt

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

rlackner88
46,027 Views

Sounds great! I'll check out NetApp WFA and will keep an eye on this space.

Would the service account need to have root / administrator access on the filer? That's another, related issue that I've been trying to solve -- provide the permission to unlock files, but not provide full admin. This would be especially important if running a script, but your "scheduled task" idea sounds good -- just allow users the ability to start the task, which itself runs under admin credentials.

Thanks!

mbeattie
46,027 Views

Hey...

So it appears that there is a known problem with the powershell toolkit when you execute the "invoke-nassh" cmdlet using the "lock break -f" command. The ZAPI command on the controller prompts for confirmation and there is not currently a workaround for this issue with the "invoke-nassh" cmdlet...however you can use a combination of Get-NaCifsShare and Get-NaLockStatus with the psfile.exe utility from system internals to close the file. Here is the syntax (assumes it is executed with an account with admin privellages on the filer...i think this may be delegated to a member of Power Users)

C:\>psfile.exe \\testns01 C:\vol\vol1\qtree1\database.mdb

psfile v1.02 - psfile
Copyright ® 2001 Mark Russinovich
Sysinternals

Files opened remotely on testns01 matching C:\vol\vol1\qtree1\database.mdb:

[207] C:\vol\vol1\qtree1\database.mdb
    User:   administrator
    Locks:  0
    Access: Read Write

C:\>psfile \\testns01 C:\vol\vol1\qtree1\database.mdb -c

psfile v1.02 - psfile
Copyright ® 2001 Mark Russinovich
Sysinternals

Closed file C:\vol\vol1\qtree1\database.mdb on testns01.

Keep an eye out and i'll post something when i've had time to convert it into a powershell script.

Matt

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

rlackner88
46,027 Views

Thanks.

How'd you share c:\ on the filer, so that the path c:\vol\vol1\qtree1\database.mdb would work when accessed from a Windows machine?

mbeattie
46,028 Views

Hi,

There is no need to create a share, psfile translates "/vol/vol1/qtree1/database.mdb" as "C:\vol\vol1\qtree1\database.mdb". Here is an example in powershell:

# http://technet.microsoft.com/en-us/sysinternals/bb897552.aspx

# Note that this script relies on "psfile.exe" existing in the windows system directory

#'------------------------------------------------------------------------------

#'Initialization Section. Define Global Variables.

#'------------------------------------------------------------------------------

[String]$uncPath    = "\\testns01\test1$\databases\admin\database.mdb"

[String]$scriptPath = Split-Path($MyInvocation.MyCommand.Path)

[String]$scriptSpec = $MyInvocation.MyCommand.Definition

[String]$scriptName = (Get-Item $scriptSpec).Name

[String]$systemPath = [Environment]::SystemDirectory

[String]$exeSpec    = "$systemPath\cmd.exe"

[String]$exeName    = "psfile.exe"

[Array]$elements    = $uncPath -Split [regex]::Escape("\")

[String]$controller = $elements[2]

[String]$shareName  = $elements[3]

[String]$fileName   = $elements[$elements.Length -1]

#'------------------------------------------------------------------------------

#'construct the folder path between the share name and file name.

#'------------------------------------------------------------------------------

Write-Host "The Script ""$scriptName"" Started Processing."

Write-Host "Processing file ""$uncPath"""

[String]$folderPath = "/"

For($i=4; $i -lt ($elements.Count -1); $i++){

   [String]$folderPath = $folderPath + $elements[$i] + "/"

}

#'------------------------------------------------------------------------------

#'Ensure psfile.exe exists in the scripts system directory.

#'------------------------------------------------------------------------------

If(-Not(Test-Path -Path "$systemPath\$exeName")){

   Write-Host "The file ""$systemPath\$exeName"" does not exist"

   Break;

}

#'------------------------------------------------------------------------------

#'Enumerate the mount point of the share and file

#'------------------------------------------------------------------------------

Import-Module DataOnTap

Connect-NaController -Name $controller | Out-Null

[String]$mountPoint = Get-NaCifsShare | Where-Object {$_.ShareName -eq $shareName} | Select-Object -ExpandProperty MountPoint

[String]$mountPath  = "$mountPoint$folderPath$fileName"

$file               = Get-NaLockStatus | Where-Object {$_.Mode -eq "Oplock-Excl" -And $_.Path -eq $mountPath}

[String]$filePath   = $file.Path

[String]$owner      = $file.Owner

Write-Host "Enumerated the mount point for CIFS share ""$shareName"" as ""$mountPoint"""

Write-Host "Enumerated CIFS Locked file as ""$filePath"" locked by ""$owner"""

#'------------------------------------------------------------------------------

#'Replace the foward slash with back slash characters to build the local file path.

#'------------------------------------------------------------------------------

[String]$fileSpec = "C:" + ($filePath -Replace ([regex]::Escape("/"),"\"))

[String]$command  = "$exeName \\$controller ""$fileSpec"" -c"

Start-Process $exeSpec -ArgumentList " /c $command"

Write-Host "Executed Command: $command"

Write-Host "The Script ""$scriptName"" Completed Successfully."

Here is an example of the script output:

PS C:\Scripts\PowerShell\Projects\CloseLockedFile> .\CloseLockedFile.ps1

The Script "test.ps1" Started Processing.

Processing file "\\testns01\test1$\databases\admin\database.mdb"

Enumerated the mount point for CIFS share "test1$" as "/vol/vol1/qtree1"

Enumerated CIFS Locked file as "/vol/vol1/qtree1/Databases/Admin/database.mdb" locked by "administrator"

Executed Command: psfile.exe \\testns01 "C:\vol\vol1\qtree1\Databases\Admin\database.mdb" -c

The Script "CloseLockedFile.ps1" Completed Successfully.

PS C:\Scripts\PowerShell\Projects\CloseLockedFile>

Hope that helps

Cheers Matt

If this post resolved your issue, help others by selecting ACCEPT AS SOLUTION or adding a KUDO.

rlackner88
46,027 Views

It might help if I used the correct path.... Email had been upgraded recently, and I forgot that the path to the ~notes.lck file is different. After using the correct path, I was able to close the files with psfile as my non-admin user. I confirmed, though, that at least Power Users privileges on the filer are required. Rather than making all users part of the Power Users group on the filer, I'll likely create a local user on the filer specifically for the purpose of running my psfile script.

Thanks for all the help, mbeattie!

Public