t.Parallel()
name := filepath.Join(t.TempDir(), "test.txt")
- wname, err := syscall.UTF16PtrFromString(name)
- if err != nil {
- t.Fatal(err)
- }
- h, err := syscall.CreateFile(wname, syscall.GENERIC_ALL, 0, nil, syscall.CREATE_NEW, syscall.FILE_ATTRIBUTE_NORMAL|syscall.FILE_FLAG_OVERLAPPED, 0)
+ f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|windows.O_FILE_FLAG_OVERLAPPED, 0666)
if err != nil {
t.Fatal(err)
}
- f := os.NewFile(uintptr(h), name)
defer f.Close()
data := []byte("test")
}
func newFileOverlapped(t testing.TB, name string, overlapped bool) *os.File {
- namep, err := syscall.UTF16PtrFromString(name)
- if err != nil {
- t.Fatal(err)
- }
- flags := syscall.FILE_ATTRIBUTE_NORMAL
+ flags := os.O_RDWR | os.O_CREATE
if overlapped {
- flags |= syscall.FILE_FLAG_OVERLAPPED
+ flags |= windows.O_FILE_FLAG_OVERLAPPED
}
- h, err := syscall.CreateFile(namep,
- syscall.GENERIC_READ|syscall.GENERIC_WRITE,
- syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_READ,
- nil, syscall.OPEN_ALWAYS, uint32(flags), 0)
+ f, err := os.OpenFile(name, flags, 0666)
if err != nil {
t.Fatal(err)
}
- f := os.NewFile(uintptr(h), name)
t.Cleanup(func() {
if err := f.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
t.Fatal(err)
// Create the read handle.
flags := windows.PIPE_ACCESS_DUPLEX
if overlapped {
- flags |= syscall.FILE_FLAG_OVERLAPPED
+ flags |= windows.O_FILE_FLAG_OVERLAPPED
}
typ := windows.PIPE_TYPE_BYTE | windows.PIPE_READMODE_BYTE
if message {
// See https://go.dev/issues/74951.
t.Parallel()
name := `\\.\` + filepath.VolumeName(t.TempDir())
- namep, err := syscall.UTF16PtrFromString(name)
- if err != nil {
- t.Fatal(err)
- }
- h, err := syscall.CreateFile(namep,
- syscall.GENERIC_READ|syscall.GENERIC_WRITE,
- syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_READ,
- nil, syscall.OPEN_ALWAYS, syscall.FILE_FLAG_OVERLAPPED, 0)
+ f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE|windows.O_FILE_FLAG_OVERLAPPED, 0666)
if err != nil {
if errors.Is(err, syscall.ERROR_ACCESS_DENIED) {
t.Skip("skipping test: access denied")
}
t.Fatal(err)
}
- f := os.NewFile(uintptr(h), name)
defer f.Close()
var buf [0]byte
}
}
}
+
+func TestOpenFileFlags(t *testing.T) {
+ t.Parallel()
+
+ // The only way to retrieve some of the flags passed in CreateFile
+ // is using NtQueryInformationFile, which returns the file flags
+ // NT equivalent. Note that FILE_SYNCHRONOUS_IO_NONALERT is always
+ // set when FILE_FLAG_OVERLAPPED is not passed.
+ // The flags that can't be retrieved using NtQueryInformationFile won't
+ // be tested in here, but we at least know that the logic to handle them is correct.
+ tests := []struct {
+ flag uint32
+ wantMode uint32
+ }{
+ {0, windows.FILE_SYNCHRONOUS_IO_NONALERT},
+ {windows.O_FILE_FLAG_OVERLAPPED, 0},
+ {windows.O_FILE_FLAG_NO_BUFFERING, windows.FILE_NO_INTERMEDIATE_BUFFERING | windows.FILE_SYNCHRONOUS_IO_NONALERT},
+ {windows.O_FILE_FLAG_NO_BUFFERING | windows.O_FILE_FLAG_OVERLAPPED, windows.FILE_NO_INTERMEDIATE_BUFFERING},
+ {windows.O_FILE_FLAG_SEQUENTIAL_SCAN, windows.FILE_SEQUENTIAL_ONLY | windows.FILE_SYNCHRONOUS_IO_NONALERT},
+ {windows.O_FILE_FLAG_WRITE_THROUGH, windows.FILE_WRITE_THROUGH | windows.FILE_SYNCHRONOUS_IO_NONALERT},
+ }
+ for i, tt := range tests {
+ t.Run(strconv.Itoa(i), func(t *testing.T) {
+ t.Parallel()
+ f, err := os.OpenFile(filepath.Join(t.TempDir(), "test.txt"), syscall.O_RDWR|syscall.O_CREAT|int(tt.flag), 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ var info windows.FILE_MODE_INFORMATION
+ if err := windows.NtQueryInformationFile(syscall.Handle(f.Fd()), &windows.IO_STATUS_BLOCK{},
+ unsafe.Pointer(&info), uint32(unsafe.Sizeof(info)), windows.FileModeInformation); err != nil {
+ t.Fatal(err)
+ }
+ if info.Mode != tt.wantMode {
+ t.Errorf("file mode = 0x%x; want 0x%x", info.Mode, tt.wantMode)
+ }
+ })
+ }
+}
+
+func TestOpenFileDeleteOnClose(t *testing.T) {
+ t.Parallel()
+ name := filepath.Join(t.TempDir(), "test.txt")
+ f, err := os.OpenFile(name, syscall.O_RDWR|syscall.O_CREAT|windows.O_FILE_FLAG_DELETE_ON_CLOSE, 0666)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := f.Close(); err != nil {
+ t.Fatal(err)
+ }
+ // The file should be deleted after closing.
+ if _, err := os.Stat(name); !errors.Is(err, os.ErrNotExist) {
+ t.Errorf("expected file to be deleted, got %v", err)
+ }
+}
+
+func TestOpenFileFlagInvalid(t *testing.T) {
+ t.Parallel()
+ // invalidFileFlag is the only value in the file flag range that is not supported,
+ // as it is not defined in the Windows API.
+ const invalidFileFlag = 0x00400000
+ f, err := os.OpenFile(filepath.Join(t.TempDir(), "test.txt"), syscall.O_RDWR|syscall.O_CREAT|invalidFileFlag, 0666)
+ if !errors.Is(err, os.ErrInvalid) {
+ t.Fatalf("expected os.ErrInvalid, got %v", err)
+ }
+ f.Close()
+}
15: "terminated",
}
+const fileFlagsMask = 0xFFF00000
+
+const validFileFlagsMask = FILE_FLAG_OPEN_REPARSE_POINT |
+ FILE_FLAG_BACKUP_SEMANTICS |
+ FILE_FLAG_OVERLAPPED |
+ _FILE_FLAG_OPEN_NO_RECALL |
+ _FILE_FLAG_SESSION_AWARE |
+ _FILE_FLAG_POSIX_SEMANTICS |
+ _FILE_FLAG_DELETE_ON_CLOSE |
+ _FILE_FLAG_SEQUENTIAL_SCAN |
+ _FILE_FLAG_NO_BUFFERING |
+ _FILE_FLAG_RANDOM_ACCESS |
+ _FILE_FLAG_WRITE_THROUGH
+
const (
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
OPEN_ALWAYS = 4
TRUNCATE_EXISTING = 5
+ // The following flags are supported by [Open]
+ // and exported in [golang.org/x/sys/windows].
+ _FILE_FLAG_OPEN_NO_RECALL = 0x00100000
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
+ _FILE_FLAG_SESSION_AWARE = 0x00800000
+ _FILE_FLAG_POSIX_SEMANTICS = 0x01000000
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
+ _FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
+ _FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000
+ _FILE_FLAG_RANDOM_ACCESS = 0x10000000
+ _FILE_FLAG_NO_BUFFERING = 0x20000000
FILE_FLAG_OVERLAPPED = 0x40000000
+ _FILE_FLAG_WRITE_THROUGH = 0x80000000
HANDLE_FLAG_INHERIT = 0x00000001
STARTF_USESTDHANDLES = 0x00000100