Browse Source

lib/fs: Try to remove read only Windows files (fixes #3744) (#8650)

This happens when folders contain a custom icon.

Co-authored-by: Alexandre Alves <[email protected]>
Jakob Borg 2 years ago
parent
commit
a29605750d
4 changed files with 46 additions and 8 deletions
  1. 0 8
      lib/fs/basicfs.go
  2. 8 0
      lib/fs/basicfs_unix.go
  3. 15 0
      lib/fs/basicfs_windows.go
  4. 23 0
      lib/fs/basicfs_windows_test.go

+ 0 - 8
lib/fs/basicfs.go

@@ -178,14 +178,6 @@ func (f *BasicFilesystem) Lstat(name string) (FileInfo, error) {
 	return basicFileInfo{fi}, err
 }
 
-func (f *BasicFilesystem) Remove(name string) error {
-	name, err := f.rooted(name)
-	if err != nil {
-		return err
-	}
-	return os.Remove(name)
-}
-
 func (f *BasicFilesystem) RemoveAll(name string) error {
 	name, err := f.rooted(name)
 	if err != nil {

+ 8 - 0
lib/fs/basicfs_unix.go

@@ -74,6 +74,14 @@ func (f *BasicFilesystem) Lchown(name, uid, gid string) error {
 	return os.Lchown(name, nuid, ngid)
 }
 
+func (f *BasicFilesystem) Remove(name string) error {
+	name, err := f.rooted(name)
+	if err != nil {
+		return err
+	}
+	return os.Remove(name)
+}
+
 // unrootedChecked returns the path relative to the folder root (same as
 // unrooted) or an error if the given path is not a subpath and handles the
 // special case when the given path is the folder root without a trailing

+ 15 - 0
lib/fs/basicfs_windows.go

@@ -190,6 +190,21 @@ func (f *BasicFilesystem) Lchown(name, uid, gid string) error {
 	return windows.SetSecurityInfo(hdl, windows.SE_FILE_OBJECT, si, (*windows.SID)(ownerSID), (*windows.SID)(groupSID), nil, nil)
 }
 
+func (f *BasicFilesystem) Remove(name string) error {
+	name, err := f.rooted(name)
+	if err != nil {
+		return err
+	}
+	err = os.Remove(name)
+	if os.IsPermission(err) {
+		// Try to remove the read-only attribute and try again
+		if os.Chmod(name, 0600) == nil {
+			err = os.Remove(name)
+		}
+	}
+	return err
+}
+
 // unrootedChecked returns the path relative to the folder root (same as
 // unrooted) or an error if the given path is not a subpath and handles the
 // special case when the given path is the folder root without a trailing

+ 23 - 0
lib/fs/basicfs_windows_test.go

@@ -13,6 +13,7 @@ import (
 	"os"
 	"path/filepath"
 	"strings"
+	"syscall"
 	"testing"
 )
 
@@ -192,3 +193,25 @@ func TestGetFinalPath(t *testing.T) {
 		}
 	}
 }
+
+func TestRemoveWindowsDirIcon(t *testing.T) {
+	//Try to delete a folder with a custom icon with os.Remove (simulated by the readonly file attribute)
+
+	fs, dir := setup(t)
+	relativePath := "folder_with_icon"
+	path := filepath.Join(dir, relativePath)
+
+	if err := os.Mkdir(path, os.ModeDir); err != nil {
+		t.Fatal(err)
+	}
+	ptr, err := syscall.UTF16PtrFromString(path)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if err := syscall.SetFileAttributes(ptr, uint32(syscall.FILE_ATTRIBUTE_DIRECTORY+syscall.FILE_ATTRIBUTE_READONLY)); err != nil {
+		t.Fatal(err)
+	}
+	if err := fs.Remove(relativePath); err != nil {
+		t.Fatal(err)
+	}
+}