Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 1 addition & 19 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ jobs:
test:
strategy:
matrix:
go-version: [1.18.x, oldstable, stable]
go-version: [oldstable, stable]
platform: [ubuntu-22.04, ubuntu-24.04, windows-2022, windows-2025, macos-15, macos-26]
runs-on: ${{ matrix.platform }}
timeout-minutes: 10 # guardrails timeout for the whole job
Expand All @@ -44,24 +44,6 @@ jobs:
# We don't need to run golangci-lint here yet, but
# there's no way to avoid it, so run it on one module.
working-directory: ./mountinfo
- name: Set PACKAGES env
if: ${{ matrix.go-version == '1.18.x' }}
run: |
# Check if the module supports this version of Go.
go_version="$(go env GOVERSION)"
go_version="${go_version#go}"

packages=""
for p in */; do
[ -f "$p/go.mod" ] || continue
if ! (cd "$p" && go list -m -f "{{if gt .GoVersion \"$go_version\"}}ko{{end}}" | grep -q ko); then
packages+="${p%/} "
else
echo "::notice::SKIP: github.com/moby/sys/${p%/} requires a more recent version of Go"
fi
done

echo "PACKAGES=${packages}" >> "$GITHUB_ENV"
- name: go mod tidy
run: |
make foreach CMD="go mod tidy"
Expand Down
4 changes: 2 additions & 2 deletions sequential/go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module github.com/moby/sys/sequential

go 1.18
go 1.24.0

require golang.org/x/sys v0.1.0
require golang.org/x/sys v0.37.0
4 changes: 2 additions & 2 deletions sequential/go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
103 changes: 16 additions & 87 deletions sequential/sequential_windows.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//go:build windows

package sequential

import (
Expand All @@ -6,108 +8,36 @@ import (
"strconv"
"sync"
"time"
"unsafe"

"golang.org/x/sys/windows"
)

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

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

// OpenFile is a copy of [os.OpenFile], modified to use sequential file access.
//
// It uses [windows.FILE_FLAG_SEQUENTIAL_SCAN] rather than [windows.FILE_ATTRIBUTE_NORMAL]
// as implemented in golang. Refer to the [Win32 API documentation] for details
// on sequential file access.
// It uses the Windows sequential scan file flag. Refer to the [Win32 API
// documentation] for details on sequential file access.
//
// [Win32 API documentation]: https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
func OpenFile(name string, flag int, _ os.FileMode) (*os.File, error) {
return openFileSequential(name, flag)
}

func openFileSequential(name string, flag int) (file *os.File, err error) {
if name == "" {
return nil, &os.PathError{Op: "open", Path: name, Err: windows.ERROR_FILE_NOT_FOUND}
}
r, e := openSequential(name, flag|windows.O_CLOEXEC)
if e != nil {
return nil, &os.PathError{Op: "open", Path: name, Err: e}
}
return os.NewFile(uintptr(r), name), nil
}

func makeInheritSa() *windows.SecurityAttributes {
var sa windows.SecurityAttributes
sa.Length = uint32(unsafe.Sizeof(sa))
sa.InheritHandle = 1
return &sa
}

func openSequential(path string, mode int) (fd windows.Handle, err error) {
if len(path) == 0 {
return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND
}
pathp, err := windows.UTF16PtrFromString(path)
if err != nil {
return windows.InvalidHandle, err
}
var access uint32
switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) {
case windows.O_RDONLY:
access = windows.GENERIC_READ
case windows.O_WRONLY:
access = windows.GENERIC_WRITE
case windows.O_RDWR:
access = windows.GENERIC_READ | windows.GENERIC_WRITE
}
if mode&windows.O_CREAT != 0 {
access |= windows.GENERIC_WRITE
}
if mode&windows.O_APPEND != 0 {
access &^= windows.GENERIC_WRITE
access |= windows.FILE_APPEND_DATA
}
sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE)
var sa *windows.SecurityAttributes
if mode&windows.O_CLOEXEC == 0 {
sa = makeInheritSa()
}
var createmode uint32
switch {
case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL):
createmode = windows.CREATE_NEW
case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC):
createmode = windows.CREATE_ALWAYS
case mode&windows.O_CREAT == windows.O_CREAT:
createmode = windows.OPEN_ALWAYS
case mode&windows.O_TRUNC == windows.O_TRUNC:
createmode = windows.TRUNCATE_EXISTING
default:
createmode = windows.OPEN_EXISTING
}
// Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang.
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, windows.FILE_FLAG_SEQUENTIAL_SCAN, 0)
return h, e
func OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) {
return openFileSequential(name, flag, perm)
}

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

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

nconflict := 0
for i := 0; i < 10000; i++ {
for range 10000 {
name := filepath.Join(dir, prefix+nextSuffix())
f, err = openFileSequential(name, windows.O_RDWR|windows.O_CREAT|windows.O_EXCL)
f, err = OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600)
if os.IsExist(err) {
if nconflict++; nconflict > 10 {
randmu.Lock()
Expand Down
13 changes: 13 additions & 0 deletions sequential/sequential_windows_go126.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//go:build windows && go1.26

package sequential

import (
"os"

"golang.org/x/sys/windows"
)

func openFileSequential(name string, flag int, perm os.FileMode) (*os.File, error) {
return os.OpenFile(name, flag|windows.O_FILE_FLAG_SEQUENTIAL_SCAN, perm)
}
76 changes: 76 additions & 0 deletions sequential/sequential_windows_pre126.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
//go:build windows && !go1.26

package sequential

import (
"os"
"unsafe"

"golang.org/x/sys/windows"
)

func openFileSequential(name string, flag int, _ os.FileMode) (file *os.File, err error) {
if name == "" {
return nil, &os.PathError{Op: "open", Path: name, Err: windows.ERROR_FILE_NOT_FOUND}
}
r, e := openSequential(name, flag|windows.O_CLOEXEC)
if e != nil {
return nil, &os.PathError{Op: "open", Path: name, Err: e}
}
return os.NewFile(uintptr(r), name), nil
}

func makeInheritSa() *windows.SecurityAttributes {
var sa windows.SecurityAttributes
sa.Length = uint32(unsafe.Sizeof(sa))
sa.InheritHandle = 1
return &sa
}

func openSequential(path string, mode int) (fd windows.Handle, err error) {
if len(path) == 0 {
return windows.InvalidHandle, windows.ERROR_FILE_NOT_FOUND
}
pathp, err := windows.UTF16PtrFromString(path)
if err != nil {
return windows.InvalidHandle, err
}
var access uint32
switch mode & (windows.O_RDONLY | windows.O_WRONLY | windows.O_RDWR) {
case windows.O_RDONLY:
access = windows.GENERIC_READ
case windows.O_WRONLY:
access = windows.GENERIC_WRITE
case windows.O_RDWR:
access = windows.GENERIC_READ | windows.GENERIC_WRITE
}
if mode&windows.O_CREAT != 0 {
access |= windows.GENERIC_WRITE
}
if mode&windows.O_APPEND != 0 {
access &^= windows.GENERIC_WRITE
access |= windows.FILE_APPEND_DATA
}
sharemode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE)
var sa *windows.SecurityAttributes
if mode&windows.O_CLOEXEC == 0 {
sa = makeInheritSa()
}
var createmode uint32
switch {
case mode&(windows.O_CREAT|windows.O_EXCL) == (windows.O_CREAT | windows.O_EXCL):
createmode = windows.CREATE_NEW
case mode&(windows.O_CREAT|windows.O_TRUNC) == (windows.O_CREAT | windows.O_TRUNC):
createmode = windows.CREATE_ALWAYS
case mode&windows.O_CREAT == windows.O_CREAT:
createmode = windows.OPEN_ALWAYS
case mode&windows.O_TRUNC == windows.O_TRUNC:
createmode = windows.TRUNCATE_EXISTING
default:
createmode = windows.OPEN_EXISTING
}
// Use FILE_FLAG_SEQUENTIAL_SCAN rather than FILE_ATTRIBUTE_NORMAL as implemented in golang.
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea#FILE_FLAG_SEQUENTIAL_SCAN
h, e := windows.CreateFile(pathp, access, sharemode, sa, createmode, windows.FILE_FLAG_SEQUENTIAL_SCAN, 0)
return h, e
}
Loading