diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4ae18209..656731a7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -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 @@ -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" diff --git a/sequential/go.mod b/sequential/go.mod index 2403a3d0..e0beae6d 100644 --- a/sequential/go.mod +++ b/sequential/go.mod @@ -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 diff --git a/sequential/go.sum b/sequential/go.sum index b69ea857..ff185ac0 100644 --- a/sequential/go.sum +++ b/sequential/go.sum @@ -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= diff --git a/sequential/sequential_windows.go b/sequential/sequential_windows.go index 3500ecc6..3dd18560 100644 --- a/sequential/sequential_windows.go +++ b/sequential/sequential_windows.go @@ -1,3 +1,5 @@ +//go:build windows + package sequential import ( @@ -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 @@ -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) { @@ -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() diff --git a/sequential/sequential_windows_go126.go b/sequential/sequential_windows_go126.go new file mode 100644 index 00000000..926c4201 --- /dev/null +++ b/sequential/sequential_windows_go126.go @@ -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) +} diff --git a/sequential/sequential_windows_pre126.go b/sequential/sequential_windows_pre126.go new file mode 100644 index 00000000..fa8129cf --- /dev/null +++ b/sequential/sequential_windows_pre126.go @@ -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 +}