mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-08-18 05:06:58 +00:00
test: cover delete-on-close on the windows mount (#10561)
* test: cover delete-on-close on the windows mount Windows software creates temporaries with FILE_FLAG_DELETE_ON_CLOSE and never deletes them explicitly. The conformance suite showed a file outliving its last handle — an aborted test left its file behind and every later test hit a name collision — but nothing in this suite asks for the flag, because os offers no way to. Skips where the flag is unavailable rather than passing quietly. * test: fail delete-on-close on a real error instead of skipping Skipping on any error meant a refused flag looked the same as a platform that cannot ask for it, so the test could pass by never running. It now skips only on that one sentinel and reports everything else. Also stops printing a nil error when the file is still there after its last handle closed, and checks the closes it was discarding.
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
package winfsp
|
||||
|
||||
import "errors"
|
||||
|
||||
// errWindowsOnly marks the platforms with no way to ask for delete-on-close,
|
||||
// so the test can tell that apart from the flag being refused.
|
||||
var errWindowsOnly = errors.New("delete-on-close is a windows flag")
|
||||
@@ -0,0 +1,9 @@
|
||||
//go:build !windows
|
||||
|
||||
package winfsp
|
||||
|
||||
type handle uintptr
|
||||
|
||||
func createDeleteOnClose(path string) (handle, error) { return 0, errWindowsOnly }
|
||||
|
||||
func closeHandle(h handle) error { return errWindowsOnly }
|
||||
@@ -0,0 +1,24 @@
|
||||
package winfsp
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
// createDeleteOnClose opens a new file that Windows removes when the last
|
||||
// handle to it closes. Installers and editors use this for temporaries, and
|
||||
// os has no way to ask for it.
|
||||
func createDeleteOnClose(path string) (windows.Handle, error) {
|
||||
p, err := windows.UTF16PtrFromString(path)
|
||||
if err != nil {
|
||||
return windows.InvalidHandle, err
|
||||
}
|
||||
return windows.CreateFile(
|
||||
p,
|
||||
windows.GENERIC_READ|windows.GENERIC_WRITE,
|
||||
windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE,
|
||||
nil,
|
||||
windows.CREATE_NEW,
|
||||
windows.FILE_ATTRIBUTE_NORMAL|windows.FILE_FLAG_DELETE_ON_CLOSE,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
func closeHandle(h windows.Handle) error { return windows.CloseHandle(h) }
|
||||
@@ -2,6 +2,7 @@ package winfsp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -461,3 +462,41 @@ func TestStatfsIsSelfConsistent(t *testing.T) {
|
||||
t.Fatalf("total free (%d) exceeds total (%d)", totalFree, total)
|
||||
}
|
||||
}
|
||||
|
||||
// TestDeleteOnClose covers the pattern Windows software uses for temporaries:
|
||||
// the file goes away when the last handle closes, with no explicit delete. A
|
||||
// filesystem that ignores the flag leaves the name behind, and the next
|
||||
// program to create it gets a collision.
|
||||
func TestDeleteOnClose(t *testing.T) {
|
||||
dir := testRoot(t)
|
||||
path := filepath.Join(dir, "ephemeral.tmp")
|
||||
|
||||
h, err := createDeleteOnClose(path)
|
||||
if err != nil {
|
||||
if errors.Is(err, errWindowsOnly) {
|
||||
t.Skip("delete-on-close needs the Win32 create call")
|
||||
}
|
||||
t.Fatalf("open with delete-on-close: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(path); err != nil {
|
||||
closeHandle(h)
|
||||
t.Fatalf("file is not visible while its handle is open: %v", err)
|
||||
}
|
||||
if err := closeHandle(h); err != nil {
|
||||
t.Fatalf("close: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
t.Fatal("file outlived its last handle")
|
||||
} else if !os.IsNotExist(err) {
|
||||
t.Fatalf("stat after close: %v", err)
|
||||
}
|
||||
// The name has to be free again, which is what the conformance suite
|
||||
// tripped over when an aborted test left its file behind.
|
||||
h, err = createDeleteOnClose(path)
|
||||
if err != nil {
|
||||
t.Fatalf("recreating the same name failed: %v", err)
|
||||
}
|
||||
if err := closeHandle(h); err != nil {
|
||||
t.Fatalf("close after recreate: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user