Skip to content

Commit 51a742c

Browse files
committed
sequential: use os.OpenFile with O_FILE_FLAG_SEQUENTIAL_SCAN on Go 1.26+
Go 1.26 adds support for passing Windows file flags via os.OpenFile, eliminating the need to call windows.CreateFile directly. Split the implementation into build-tagged files: go1.26+ uses os.OpenFile with the new O_FILE_FLAG_SEQUENTIAL_SCAN open flag, while older versions retain the manual CreateFile approach. Also bump golang.org/x/sys to v0.37.0 (provides the new constant) and update the minimum Go version to 1.24. Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
1 parent 469a746 commit 51a742c

5 files changed

Lines changed: 109 additions & 91 deletions

File tree

sequential/go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module github.com/moby/sys/sequential
22

3-
go 1.18
3+
go 1.24.0
44

5-
require golang.org/x/sys v0.1.0
5+
require golang.org/x/sys v0.37.0

sequential/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
2-
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1+
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
2+
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=

sequential/sequential_windows.go

Lines changed: 16 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build windows
2+
13
package sequential
24

35
import (
@@ -6,108 +8,36 @@ import (
68
"strconv"
79
"sync"
810
"time"
9-
"unsafe"
10-
11-
"golang.org/x/sys/windows"
1211
)
1312

1413
// Create is a copy of [os.Create], modified to use sequential file access.
1514
//
16-
// It uses [windows.FILE_FLAG_SEQUENTIAL_SCAN] rather than [windows.FILE_ATTRIBUTE_NORMAL]
17-
// as implemented in golang. Refer to the [Win32 API documentation] for details
18-
// on sequential file access.
15+
// It uses the Windows sequential scan file flag. Refer to the [Win32 API
16+
// documentation] for details on sequential file access.
1917
//
2018
// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
2119
func Create(name string) (*os.File, error) {
22-
return openFileSequential(name, windows.O_RDWR|windows.O_CREAT|windows.O_TRUNC)
20+
return OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666)
2321
}
2422

2523
// Open is a copy of [os.Open], modified to use sequential file access.
2624
//
27-
// It uses [windows.FILE_FLAG_SEQUENTIAL_SCAN] rather than [windows.FILE_ATTRIBUTE_NORMAL]
28-
// as implemented in golang. Refer to the [Win32 API documentation] for details
29-
// on sequential file access.
25+
// It uses the Windows sequential scan file flag. Refer to the [Win32 API
26+
// documentation] for details on sequential file access.
3027
//
3128
// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
3229
func Open(name string) (*os.File, error) {
33-
return openFileSequential(name, windows.O_RDONLY)
30+
return OpenFile(name, os.O_RDONLY, 0)
3431
}
3532

3633
// OpenFile is a copy of [os.OpenFile], modified to use sequential file access.
3734
//
38-
// It uses [windows.FILE_FLAG_SEQUENTIAL_SCAN] rather than [windows.FILE_ATTRIBUTE_NORMAL]
39-
// as implemented in golang. Refer to the [Win32 API documentation] for details
40-
// on sequential file access.
35+
// It uses the Windows sequential scan file flag. Refer to the [Win32 API
36+
// documentation] for details on sequential file access.
4137
//
4238
// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
43-
func OpenFile(name string, flag int, _ os.FileMode) (*os.File, error) {
44-
return openFileSequential(name, flag)
45-
}
46-
47-
func openFileSequential(name string, flag int) (file *os.File, err error) {
48-
if name == "" {
49-
return nil, &os.PathError{Op: "open", Path: name, Err: windows.ERROR_FILE_NOT_FOUND}
50-
}
51-
r, e := openSequential(name, flag|windows.O_CLOEXEC)
52-
if e != nil {
53-
return nil, &os.PathError{Op: "open", Path: name, Err: e}
54-
}
55-
return os.NewFile(uintptr(r), name), nil
56-
}
57-
58-
func makeInheritSa() *windows.SecurityAttributes {
59-
var sa windows.SecurityAttributes
60-
sa.Length = uint32(unsafe.Sizeof(sa))
61-
sa.InheritHandle = 1
62-
return &sa
63-
}
64-
65-
func openSequential(path string, mode int) (fd windows.Handle, err error) {
66-
if len(path) == 0 {
67-
return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND
68-
}
69-
pathp, err := windows.UTF16PtrFromString(path)
70-
if err != nil {
71-
return windows.InvalidHandle, err
72-
}
73-
var access uint32
74-
switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) {
75-
case windows.O_RDONLY:
76-
access = windows.GENERIC_READ
77-
case windows.O_WRONLY:
78-
access = windows.GENERIC_WRITE
79-
case windows.O_RDWR:
80-
access = windows.GENERIC_READ | windows.GENERIC_WRITE
81-
}
82-
if mode&windows.O_CREAT != 0 {
83-
access |= windows.GENERIC_WRITE
84-
}
85-
if mode&windows.O_APPEND != 0 {
86-
access &^= windows.GENERIC_WRITE
87-
access |= windows.FILE_APPEND_DATA
88-
}
89-
sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE)
90-
var sa *windows.SecurityAttributes
91-
if mode&windows.O_CLOEXEC == 0 {
92-
sa = makeInheritSa()
93-
}
94-
var createmode uint32
95-
switch {
96-
case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL):
97-
createmode = windows.CREATE_NEW
98-
case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC):
99-
createmode = windows.CREATE_ALWAYS
100-
case mode&windows.O_CREAT == windows.O_CREAT:
101-
createmode = windows.OPEN_ALWAYS
102-
case mode&windows.O_TRUNC == windows.O_TRUNC:
103-
createmode = windows.TRUNCATE_EXISTING
104-
default:
105-
createmode = windows.OPEN_EXISTING
106-
}
107-
// Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang.
108-
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
109-
h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, windows.FILE_FLAG_SEQUENTIAL_SCAN, 0)
110-
return h, e
39+
func OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) {
40+
return openFileSequential(name, flag, perm)
11141
}
11242

11343
// Helpers for CreateTemp
@@ -134,9 +64,8 @@ func nextSuffix() string {
13464

13565
// CreateTemp is a copy of [os.CreateTemp], modified to use sequential file access.
13666
//
137-
// It uses [windows.FILE_FLAG_SEQUENTIAL_SCAN] rather than [windows.FILE_ATTRIBUTE_NORMAL]
138-
// as implemented in golang. Refer to the [Win32 API documentation] for details
139-
// on sequential file access.
67+
// It uses the Windows sequential scan file flag. Refer to the [Win32 API
68+
// documentation] for details on sequential file access.
14069
//
14170
// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
14271
func CreateTemp(dir, prefix string) (f *os.File, err error) {
@@ -145,9 +74,9 @@ func CreateTemp(dir, prefix string) (f *os.File, err error) {
14574
}
14675

14776
nconflict := 0
148-
for i := 0; i < 10000; i++ {
77+
for range 10000 {
14978
name := filepath.Join(dir, prefix+nextSuffix())
150-
f, err = openFileSequential(name, windows.O_RDWR|windows.O_CREAT|windows.O_EXCL)
79+
f, err = OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600)
15180
if os.IsExist(err) {
15281
if nconflict++; nconflict > 10 {
15382
randmu.Lock()
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//go:build windows && go1.26
2+
3+
package sequential
4+
5+
import (
6+
"os"
7+
8+
"golang.org/x/sys/windows"
9+
)
10+
11+
func openFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) {
12+
return os.OpenFile(name, flag|windows.O_FILE_FLAG_SEQUENTIAL_SCAN, perm)
13+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//go:build windows && !go1.26
2+
3+
package sequential
4+
5+
import (
6+
"os"
7+
"unsafe"
8+
9+
"golang.org/x/sys/windows"
10+
)
11+
12+
func openFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) {
13+
if name == "" {
14+
return nil, &os.PathError{Op: "open", Path: name, Err: windows.ERROR_FILE_NOT_FOUND}
15+
}
16+
r, e := openSequential(name, flag|windows.O_CLOEXEC)
17+
if e != nil {
18+
return nil, &os.PathError{Op: "open", Path: name, Err: e}
19+
}
20+
return os.NewFile(uintptr(r), name), nil
21+
}
22+
23+
func makeInheritSa() *windows.SecurityAttributes {
24+
var sa windows.SecurityAttributes
25+
sa.Length = uint32(unsafe.Sizeof(sa))
26+
sa.InheritHandle = 1
27+
return &sa
28+
}
29+
30+
func openSequential(path string, mode int) (fd windows.Handle, err error) {
31+
if len(path) == 0 {
32+
return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND
33+
}
34+
pathp, err := windows.UTF16PtrFromString(path)
35+
if err != nil {
36+
return windows.InvalidHandle, err
37+
}
38+
var access uint32
39+
switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) {
40+
case windows.O_RDONLY:
41+
access = windows.GENERIC_READ
42+
case windows.O_WRONLY:
43+
access = windows.GENERIC_WRITE
44+
case windows.O_RDWR:
45+
access = windows.GENERIC_READ | windows.GENERIC_WRITE
46+
}
47+
if mode&windows.O_CREAT != 0 {
48+
access |= windows.GENERIC_WRITE
49+
}
50+
if mode&windows.O_APPEND != 0 {
51+
access &^= windows.GENERIC_WRITE
52+
access |= windows.FILE_APPEND_DATA
53+
}
54+
sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE)
55+
var sa *windows.SecurityAttributes
56+
if mode&windows.O_CLOEXEC == 0 {
57+
sa = makeInheritSa()
58+
}
59+
var createmode uint32
60+
switch {
61+
case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL):
62+
createmode = windows.CREATE_NEW
63+
case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC):
64+
createmode = windows.CREATE_ALWAYS
65+
case mode&windows.O_CREAT == windows.O_CREAT:
66+
createmode = windows.OPEN_ALWAYS
67+
case mode&windows.O_TRUNC == windows.O_TRUNC:
68+
createmode = windows.TRUNCATE_EXISTING
69+
default:
70+
createmode = windows.OPEN_EXISTING
71+
}
72+
// Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang.
73+
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
74+
h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, windows.FILE_FLAG_SEQUENTIAL_SCAN, 0)
75+
return h, e
76+
}

0 commit comments

Comments
 (0)