Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove-Item -Force cannot delete hidden file on Samba #15022

Closed
sba923 opened this issue Mar 14, 2021 · 32 comments
Closed

Remove-Item -Force cannot delete hidden file on Samba #15022

sba923 opened this issue Mar 14, 2021 · 32 comments
Labels
Area-FileSystem-Provider specific to the FileSystem provider Issue-Bug Issue has been identified as a bug in the product Resolution-No Activity Issue has had no activity for 6 months or more Up-for-Grabs Up-for-grabs issues are not high priorities, and may be opportunities for external contributors WG-Cmdlets-Management cmdlets in the Microsoft.PowerShell.Management module WG-Engine-Providers built-in PowerShell providers such as FileSystem, Certificates, Registry, etc.

Comments

@sba923
Copy link
Contributor

sba923 commented Mar 14, 2021

Steps to reproduce

Attempting to delete a hidden file that is stored on a Samba file share (Samba 4.11.6-Ubuntu on Ubuntu 20.04.2 LTS) fails, whereas CMD can delete the file

PowerShell:


PS❯ gci -force  '\\pnjnas\public\FileHistory\LATITUDE7400\Data\C\Users\sto\OneDrive\.NET assembly dependency (2020_09_20 08_01_14 UTC).docx'

    Directory:  S:\MM\catalogs

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
---h-        2016-01-26     09:46    12.67KB .NET assembly dependency (2020_09_20 08_01_14 UTC).docx


PS❯ remove-item '\\pnjnas\public\FileHistory\LATITUDE7400\Data\C\Users\sto\OneDrive\.NET assembly dependency (2020_09_20 08_01_14 UTC).docx'
Remove-Item: You do not have sufficient access rights to perform this operation or the item is hidden, system, or read only.


PS❯ remove-item -force '\\pnjnas\public\FileHistory\LATITUDE7400\Data\C\Users\sto\OneDrive\.NET assembly dependency (2020_09_20 08_01_14 UTC).docx'
Remove-Item: The handle is invalid. : '\\pnjnas\public\FileHistory\LATITUDE7400\Data\C\Users\sto\OneDrive\.NET assembly dependency (2020_09_20 08_01_14 UTC).docx'

CMD:

C:\Users\<REDACTED>>dir /ah "\\pnjnas\public\FileHistory\LATITUDE7400\Data\C\Users\sto\OneDrive\.NET assembly dependency (2020_09_20 08_01_14 UTC).docx
 Volume in drive \\pnjnas\public is Public
 Volume Serial Number is 4664-C5A0

 Directory of \\pnjnas\public\FileHistory\LATITUDE7400\Data\C\Users\sto\OneDrive

2016-01-26  09:46            12,979 .NET assembly dependency (2020_09_20 08_01_14 UTC).docx
               1 File(s)         12,979 bytes
               0 Dir(s)  1,390,021,279,744 bytes free

C:\Users\<REDACTED>>del /ah "\\pnjnas\public\FileHistory\LATITUDE7400\Data\C\Users\sto\OneDrive\.NET assembly dependency (2020_09_20 08_01_14 UTC).docx

C:\Users\<REDACTED>>dir /ah "\\pnjnas\public\FileHistory\LATITUDE7400\Data\C\Users\sto\OneDrive\.NET assembly dependency (2020_09_20 08_01_14 UTC).docx
 Volume in drive \\pnjnas\public is Public
 Volume Serial Number is 4664-C5A0

 Directory of \\pnjnas\public\FileHistory\LATITUDE7400\Data\C\Users\sto\OneDrive

File Not Found

Expected behavior

Remove-Item -Force should delete the file

Actual behavior

Remove-Item -Force fails with an "invalid handle" error.

Environment data

> $psversiontable

Name                           Value
----                           -----
PSVersion                      7.1.3
PSEdition                      Core
GitCommitId                    7.1.3
OS                             Microsoft Windows 10.0.19042
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0
@sba923 sba923 added the Needs-Triage The issue is new and needs to be triaged by a work group. label Mar 14, 2021
@iSazonov
Copy link
Collaborator

It seems you are trying to remove OneDrive file which is a Windows reparse point - I guess Samba does not support this.

@sba923
Copy link
Contributor Author

sba923 commented Mar 14, 2021

No I'm not. It's just a coincidence that the Samba-hosted file I was trying to remove happened to be an old backup copy of some file within my OneDrive tree. Sorry for the confusion.

And CMD was able to remove the file.

@iSazonov
Copy link
Collaborator

@sba923 I'd prefer to have an clean repro steps. Can you please create simple test folder tree with PowerShell cmdlets? What results do you see?

@iSazonov iSazonov added WG-Cmdlets-Management cmdlets in the Microsoft.PowerShell.Management module WG-Engine-Providers built-in PowerShell providers such as FileSystem, Certificates, Registry, etc. labels Mar 15, 2021
@sba923
Copy link
Contributor Author

sba923 commented Mar 15, 2021

Will work on that. Stay tuned.

@sba923
Copy link
Contributor Author

sba923 commented Mar 15, 2021

Here's the test script:

$unixpath = '/data/Public/foo.txt'
$uncpath = '\\pnjnas\Public\foo.txt'

ssh -n sto@pnjnas "rm -f $unixpath"

if (Test-Path -LiteralPath $uncpath)
{
    Write-Host -ForegroundColor Red ("Failed to remove test file")
    Exit(1)
}

"foo" | out-file $uncpath

attrib -a $uncpath
attrib +h $uncpath

ssh -n sto@pnjnas "chmod 544 $unixpath"

ssh -n sto@pnjnas "ls -ld $unixpath"

Get-ChildItem -Force -LiteralPath $uncpath

Remove-Item -LiteralPath $uncpath

Remove-Item -Force -LiteralPath $uncpath

And the outcome:


PS❯ S:\powershell\test\Remove-SambaHidden544.ps1
-r-xr--r-- 1 sto sto 5 Mar 15 08:48 /data/Public/foo.txt

    Directory:  C:\Users\<REDACTED>

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
---h-        2021-03-15     08:48        5   foo.txt
Remove-Item: S:\powershell\test\Remove-SambaHidden544.ps1:23
Line |
  23 |  Remove-Item -LiteralPath $uncpath
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | You do not have sufficient access rights to perform this operation or the item is hidden, system, or
     | read only.

Remove-Item: S:\powershell\test\Remove-SambaHidden544.ps1:25
Line |
  25 |  Remove-Item -Force -LiteralPath $uncpath
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The handle is invalid. : '\\pnjnas\Public\foo.txt'

@iSazonov
Copy link
Collaborator

iSazonov commented Mar 15, 2021

You should use -Force switch to remove hidden file objects.
Also in OP you used a file name started with dot.

@sba923
Copy link
Contributor Author

sba923 commented Mar 15, 2021

That's exactly what I'm doing, and that triggers the "invalid handle" error.

And that is the case even if the filename doesn't start with "."

@iSazonov
Copy link
Collaborator

Thanks, I see.
Please try .Net methods to remove the file:

$a=[io.fileinfo]::new('\\pnjnas\Public\foo.txt')
$a.Delete()

@sba923
Copy link
Contributor Author

sba923 commented Mar 15, 2021

This works:

$unixpath = '/data/Public/foo.txt'
$uncpath = '\\pnjnas\Public\foo.txt'

ssh -n sto@pnjnas "rm -f $unixpath"

if (Test-Path -LiteralPath $uncpath)
{
    Write-Host -ForegroundColor Red ("Failed to remove test file")
    Exit(1)
}

"foo" | out-file $uncpath

attrib -a $uncpath
attrib +h $uncpath

ssh -n sto@pnjnas "chmod 544 $unixpath"

ssh -n sto@pnjnas "ls -ld $unixpath"

Get-ChildItem -Force -LiteralPath $uncpath

Remove-Item -LiteralPath $uncpath

Remove-Item -Force -LiteralPath $uncpath

$a=[io.fileinfo]::new($uncpath)
$a.Delete()

if (Test-Path -LiteralPath $uncpath)
{
    Write-Host -ForegroundColor Red ("Failed to remove test file")
    Exit(1)
}
else {
    Write-Host -ForegroundColor Green ("Test file removal succeeded")
}


PS❯ S:\powershell\test\Remove-SambaHidden544.ps1
-r-xr--r-- 1 sto sto 5 Mar 15 13:40 /data/Public/foo.txt

    Directory:  Microsoft.PowerShell.Core\FileSystem::\\pnjnas\Musique\Twonky\Playlists

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
---h-        2021-03-15     13:40        5   foo.txt
Remove-Item: S:\powershell\test\Remove-SambaHidden544.ps1:23
Line |
  23 |  Remove-Item -LiteralPath $uncpath
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | You do not have sufficient access rights to perform this operation or the item is hidden, system, or
     | read only.

Remove-Item: S:\powershell\test\Remove-SambaHidden544.ps1:25
Line |
  25 |  Remove-Item -Force -LiteralPath $uncpath
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The handle is invalid. : '\\pnjnas\Public\foo.txt'

Test file removal succeeded

@iSazonov
Copy link
Collaborator

Thanks! Since .Net API works I believe it is PowerShell bug.

@iSazonov iSazonov added Issue-Bug Issue has been identified as a bug in the product OS-Linux labels Mar 15, 2021
@sba923
Copy link
Contributor Author

sba923 commented Mar 15, 2021

You're very welcome.

Note: this is not linked to PowerShell running on Linux!

image

@iSazonov
Copy link
Collaborator

Note: this is not linked to PowerShell running on Linux!

Yes, I checked the code works well for Windows shares. So the label is only about Samba context.

@sba923
Copy link
Contributor Author

sba923 commented Mar 15, 2021

Fine.

Don't hesitate to involve me in testing (candidate) fixes!

@jborean93
Copy link
Collaborator

The issue here is the chmod 544 is setting permissions for the Samba user to just read and execute without any write access. For a delete operation this is fine as you just need to make sure you open the file with that request right. When you do

$a=[io.fileinfo]::new($uncpath)
$a.Delete()

# Or just
[IO.File]::Delete($uncpath)

It will open the file with the Delete access mask which should still be fine.

When you do Remove-Item ... it is actually opening the file with read and write access to the attributes and setting the attributes to Normal. I assume this is because -Force is meant to force the deletion that fails when the ReadOnly flag is set.

image

Because the samba user does not have write access it is failing to open the handle to that file and thus fails to delete the attributes. The fact that it's a compound request is probably the reason why the error back is invalid handle rather than access denied because the last request failed with that.

For fixing this problem I see 2 ways this could be done

  • Ignore this particular error if it occurs and just try the delete anyway, make removing the ReadOnly flag a best effort and use the actual deletion request what is reported on
  • Try to delete the file first and then unset the attribute if that failed with access denied

Personally I think the first option is the way to go, it doesn't add/remove any extra steps than what is currently done and if the user is truly unable to delete the file then the error back from the deletion request will still reflect that. The 2nd option might be quicker in normal cases but it could add an extra step if it does need to remove the attribute (and it's not needed).

@iSazonov
Copy link
Collaborator

iSazonov commented Mar 16, 2021

The issue here is the chmod 544 is setting permissions for the Samba user to just read and execute without any write access.

Could you please share a screenshot with how Windows maps this to Windows permissions? I'd want to see Security tab for the Samba file. This could helps to emulate the behavior on Windows shares/local disk and to prepare more unified fix.

Personally I think the first option is the way to go

I guess PowerShell has long way to the delete call - there is a globbing code, specific provider code - there may be file open operations due to the versatility of this code and it may not be easy to fix.
For example, this should works for Remove-Item -Force -Path \\server\share\folder\*\file*.txt

@jborean93
Copy link
Collaborator

jborean93 commented Mar 16, 2021

So I have 2 files in a share

[root@083f251e5344 app]# ls -al /srv/samba/share/
total 0
drwxr-xr-x. 1 smbuser smbgroup 34 Mar 16 04:43 .
drwxr-xr-x. 1 root    root     54 Mar 16 04:41 ..
-r-xr--r--. 1 smbuser smbuser   0 Mar 16 04:42 foo.txt
-rwxr--r--. 1 smbuser smbuser   0 Mar 16 04:43 normal.txt

For foo.txt (chmod 544) here are the permissions in the Windows GUI

image

For normal.txt (chmod 744) here are the permissions in the Windows GUI

image

So this confirms that the Unix permissions affect the permissions that Samba uses over SMB. It makes sense as well because if the Samba user is unable to write to the file then it is unable to write data (like attributes) to it. You can even replicate this outside of PowerShell and get the same Invalid handle error message back by trying to change the attributes after removing write access on the remote side.

If you are wanting to emulate the fix just make sure that the user does not have the Write Attributes access mask because that is the access this particular operation is requesting and failing to request. You can see in the Wireshark screenshot that it's simply requesting WA/RA/Synchronize and WA is what is actually used in the SetInfo request with that handle.

there may be file open operations due to the versatility of this code and it may not be easy to fix.

I don't think the proposed fix changes anything about that. All I'm proposing is to not make that 1 operation a failure but rather a at best operation. If it truly cannot delete the file because it does not have access (or it's in use) then the subsequent delete call will still fail and report that error back to the user.

For example, this should works for Remove-Item -Force -Path \server\share\folder*\file*.txt

In this case it does not, the problem lies with how the FileSystem provider tries to delete a file when -Force is present (it must be present here because the file is hidden). All this logic is contained in RemoveFileSystemItem with the offending code in particular being

if (force)
{
fileSystemInfo.Attributes &= ~(FileAttributes.Hidden | FileAttributes.ReadOnly | FileAttributes.System);
attributeRecoveryRequired = true;
}
.

We can see that if -Force is set it always removes the ReadOnly | Hidden | System access mask before it moves onto the actual delete. By ignoring the failure when setting the attributes here we would actually have a successful deletion. Even if removing the attributes failed because the user didn't have permissions that permission error would be raised in the subsequent Delete call. Going a step further it looks like we've already gotten the attributes of the file just above this. We could make this call optional depending on whether it has the ReadOnly flag. AFAIK there are no complications or extra steps required to delete a file that is Hidden or System so I'm not sure why those are being cleared out here. If a file had ReadOnly and the user did not have WA access then not even Windows will delete the file, there's nothing that PowerShell can do here for that particular use case.

@iSazonov
Copy link
Collaborator

So I have 2 files in a share

Can you confirm the files is hidden? Can you share info for non-hidden files?

@jborean93
Copy link
Collaborator

I can confirm, I set it myself. The fact it is hidden has no bearing over the permissions. It’s only to expose this bug that happens when using -Force and the user does not have write attributes access to the file.

@iSazonov
Copy link
Collaborator

AFAIK there are no complications or extra steps required to delete a file that is Hidden or System so I'm not sure why those are being cleared out here.

It is exclusive PowerShell smart feature :-) By design. We should keep this.

So the proposed fix is to put the "if (force)" to "try-catch" - I am inclined to accept this.

@sba923
Copy link
Contributor Author

sba923 commented Mar 16, 2021

I confirm that if the file is not hidden, Remove-Item -Force still fails with the "invalid handle" error:

$unixpath = '/data/Public/foo.txt'
$uncpath = '\\pnjnas\Public\foo.txt'

ssh -n sto@pnjnas "rm -f $unixpath"

if (Test-Path -LiteralPath $uncpath)
{
    Write-Host -ForegroundColor Red ("Failed to remove test file")
    Exit(1)
}

"foo" | out-file $uncpath

# attrib -a $uncpath
# attrib +h $uncpath

ssh -n sto@pnjnas "chmod 544 $unixpath"

ssh -n sto@pnjnas "ls -ld $unixpath"

Get-ChildItem -Force -LiteralPath $uncpath

# Remove-Item -LiteralPath $uncpath

Remove-Item -Force -LiteralPath $uncpath

# $a=[io.fileinfo]::new($uncpath)
# $a.Delete()

if (Test-Path -LiteralPath $uncpath)
{
    Write-Host -ForegroundColor Red ("Failed to remove test file")
    Exit(1)
}
else {
    Write-Host -ForegroundColor Green ("Test file removal succeeded")
}


PS❯ S:\powershell\test\Remove-SambaHidden544.ps1
-r-xr--r-- 1 sto sto 5 Mar 16 09:41 /data/Public/foo.txt

    Directory:  Microsoft.PowerShell.Core\FileSystem::\\pnjnas\Musique\Twonky\Playlists

Mode                LastWriteTime     Length Name
----                -------------     ------ ----
-a---        2021-03-16     09:41        5   foo.txt
Remove-Item: S:\powershell\test\Remove-SambaHidden544.ps1:25
Line |
  25 |  Remove-Item -Force -LiteralPath $uncpath
     |  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     | The handle is invalid. : '\\pnjnas\Public\foo.txt'

Failed to remove test file

@sba923
Copy link
Contributor Author

sba923 commented Mar 16, 2021

For fixing this problem I see 2 ways this could be done

  • Ignore this particular error if it occurs and just try the delete anyway, make removing the ReadOnly flag a best effort and use the actual deletion request what is reported on
  • Try to delete the file first and then unset the attribute if that failed with access denied

Personally I think the first option is the way to go, it doesn't add/remove any extra steps than what is currently done and if the user is truly unable to delete the file then the error back from the deletion request will still reflect that. The 2nd option might be quicker in normal cases but it could add an extra step if it does need to remove the attribute (and it's not needed).

We need to make sure that in no circumstances the operation could fail but have the side effect of modifying the file's attributes.

@jborean93
Copy link
Collaborator

Totally understand it’s a feature of PowerShell :) My main point is that the code is unilaterally removing all 3 attributes (ReadOnly, System, Hidden) in this case but really it’s only ReadOnly that needs to be removed. It could even be smarter and only remove the attribute if it’s not currently set to reduce the number of operations that need to occur.

I still think a try/catch is also good for this situation but both options will fix this problem.

@jborean93
Copy link
Collaborator

We need to make sure that in no circumstances the operation could fail but have the side effect of modifying the file's attributes.

That’s already handled in the current code. If the attributes were changed but the delete failed then the attributes are reset back.

@iSazonov
Copy link
Collaborator

Totally understand it’s a feature of PowerShell :) My main point is that the code is unilaterally removing all 3 attributes (ReadOnly, System, Hidden) in this case but really it’s only ReadOnly that needs to be removed. It could even be smarter and only remove the attribute if it’s not currently set to reduce the number of operations that need to occur.

I still think a try/catch is also good for this situation but both options will fix this problem.

I see your point. Only concern is about how this attributes are mapped to Unix attributes by Samba (I'd want to get a confirmation that .Net API can remove files with the attributes). If there are no issues due to the mapping I think we can accept the fix with the two changes since the code has good test coverage.

@jborean93
Copy link
Collaborator

Only concern is about how this attributes are mapped to Unix attributes by Samba

From my understanding of Samba it can store the attributes Hidden, ReadOnly, System, and Archive in 2 different ways.

  • When store dos attributes is yes (default since 4.9
    • The attributes are stored in an extended attribute which is like special metadata on a file
  • When store dos attributes = no and the relevant map <attribute> is set to yes
    • Uses the mode of the file to determine if the 4 attributes are set
    • Hidden - world executable (001)
    • System - group executable (010)
    • Archive - owner executable (100)
    • ReadOnly - owner write (200) (there are slight variants here but doesn't matter as much)

In my case I'm testing against a Samba setup that has store dos attributes = yes, potentially @sba923 might be using the owner write bit hence the chmod example.

I can confirm that when using store dos attributes = yes then .NET is able to delete the file when Hidden, System, Archive is set. It only fails when ReadOnly is set which aligns with how I understand Windows works. Even the documentation for DeleteFile states

To delete a read-only file, first you must remove the read-only attribute.

There are no other mentions for the other attributes and considering SMB is a Microsoft protocol it would make sense that Samba replicates the same behaviour.

I also tested store dos attributes = no with the relevant map <attribute> = yes options enabled and it has the same behaviour.

@iSazonov
Copy link
Collaborator

@jborean93 Thanks for your research! I think we can offer the community fix.

@sba923
Copy link
Contributor Author

sba923 commented Mar 16, 2021

FWIW I'm running Samba version 4.11.6-Ubuntu and the config file contains nothing but a commented-out store dos attributes = yes

@iSazonov iSazonov added the Up-for-Grabs Up-for-grabs issues are not high priorities, and may be opportunities for external contributors label Mar 16, 2021
@joeyaiello joeyaiello removed Needs-Triage The issue is new and needs to be triaged by a work group. OS-Linux labels Mar 17, 2021
@sba923
Copy link
Contributor Author

sba923 commented Sep 17, 2021

Note that I also get Remove-Item: The handle is invalid upon a Remove-Item -LiteralPath foo -Verbose -Force, for a non-hidden file that is just not writable for the user used for the connection to the Samba share, i.e.:

-rwxrw-r-- 1 otheruser otheruser 21215 Jul 25  2010 foo

What's next towards a solution to this issue?

@iSazonov iSazonov added the Area-FileSystem-Provider specific to the FileSystem provider label Oct 9, 2021
Copy link
Contributor

This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

2 similar comments
Copy link
Contributor

This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

Copy link
Contributor

This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.

@microsoft-github-policy-service microsoft-github-policy-service bot added Resolution-No Activity Issue has had no activity for 6 months or more labels Nov 16, 2023
Copy link
Contributor

This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-FileSystem-Provider specific to the FileSystem provider Issue-Bug Issue has been identified as a bug in the product Resolution-No Activity Issue has had no activity for 6 months or more Up-for-Grabs Up-for-grabs issues are not high priorities, and may be opportunities for external contributors WG-Cmdlets-Management cmdlets in the Microsoft.PowerShell.Management module WG-Engine-Providers built-in PowerShell providers such as FileSystem, Certificates, Registry, etc.
Projects
None yet
Development

No branches or pull requests

4 participants