From c97c816133b42257d0bcf1ee4bd178bb2a7a2b9e Mon Sep 17 00:00:00 2001 From: Taras Madan Date: Tue, 10 Sep 2024 12:16:33 +0200 Subject: vendor: update --- vendor/github.com/gofrs/flock/.golangci.yml | 114 +++++++ vendor/github.com/gofrs/flock/.travis.yml | 10 - vendor/github.com/gofrs/flock/LICENSE | 1 + vendor/github.com/gofrs/flock/Makefile | 15 + vendor/github.com/gofrs/flock/README.md | 32 +- vendor/github.com/gofrs/flock/SECURITY.md | 21 ++ vendor/github.com/gofrs/flock/appveyor.yml | 25 -- vendor/github.com/gofrs/flock/build.sh | 18 + vendor/github.com/gofrs/flock/flock.go | 110 ++++-- vendor/github.com/gofrs/flock/flock_aix.go | 281 ---------------- vendor/github.com/gofrs/flock/flock_others.go | 40 +++ vendor/github.com/gofrs/flock/flock_unix.go | 179 +++++----- vendor/github.com/gofrs/flock/flock_unix_fcntl.go | 393 ++++++++++++++++++++++ vendor/github.com/gofrs/flock/flock_winapi.go | 76 ----- vendor/github.com/gofrs/flock/flock_windows.go | 122 ++++--- 15 files changed, 871 insertions(+), 566 deletions(-) create mode 100644 vendor/github.com/gofrs/flock/.golangci.yml delete mode 100644 vendor/github.com/gofrs/flock/.travis.yml create mode 100644 vendor/github.com/gofrs/flock/Makefile create mode 100644 vendor/github.com/gofrs/flock/SECURITY.md delete mode 100644 vendor/github.com/gofrs/flock/appveyor.yml create mode 100644 vendor/github.com/gofrs/flock/build.sh delete mode 100644 vendor/github.com/gofrs/flock/flock_aix.go create mode 100644 vendor/github.com/gofrs/flock/flock_others.go create mode 100644 vendor/github.com/gofrs/flock/flock_unix_fcntl.go delete mode 100644 vendor/github.com/gofrs/flock/flock_winapi.go (limited to 'vendor/github.com/gofrs') diff --git a/vendor/github.com/gofrs/flock/.golangci.yml b/vendor/github.com/gofrs/flock/.golangci.yml new file mode 100644 index 000000000..3ad88a38f --- /dev/null +++ b/vendor/github.com/gofrs/flock/.golangci.yml @@ -0,0 +1,114 @@ +run: + timeout: 10m + +linters: + enable: + - asasalint + - bidichk + - dogsled + - dupword + - durationcheck + - err113 + - errname + - errorlint + - fatcontext + - forbidigo + - gocheckcompilerdirectives + - gochecknoinits + - gocritic + - godot + - godox + - gofumpt + - goheader + - goimports + - gomoddirectives + - goprintffuncname + - gosec + - inamedparam + - interfacebloat + - ireturn + - mirror + - misspell + - nolintlint + - revive + - stylecheck + - tenv + - testifylint + - thelper + - unconvert + - unparam + - usestdlibvars + - whitespace + +linters-settings: + misspell: + locale: US + godox: + keywords: + - FIXME + goheader: + template: |- + Copyright 2015 Tim Heckman. All rights reserved. + Copyright 2018-{{ YEAR }} The Gofrs. All rights reserved. + Use of this source code is governed by the BSD 3-Clause + license that can be found in the LICENSE file. + gofumpt: + extra-rules: true + gocritic: + enabled-tags: + - diagnostic + - style + - performance + disabled-checks: + - paramTypeCombine # already handle by gofumpt.extra-rules + - whyNoLint # already handle by nonolint + - unnamedResult + - hugeParam + - sloppyReassign + - rangeValCopy + - octalLiteral + - ptrToRefParam + - appendAssign + - ruleguard + - httpNoBody + - exposedSyncMutex + + revive: + rules: + - name: struct-tag + - name: blank-imports + - name: context-as-argument + - name: context-keys-type + - name: dot-imports + - name: error-return + - name: error-strings + - name: error-naming + - name: exported + - name: if-return + - name: increment-decrement + - name: var-naming + - name: var-declaration + - name: package-comments + - name: range + - name: receiver-naming + - name: time-naming + - name: unexported-return + - name: indent-error-flow + - name: errorf + - name: empty-block + - name: superfluous-else + - name: unused-parameter + - name: unreachable-code + - name: redefines-builtin-id + +issues: + exclude-use-default: true + max-issues-per-linter: 0 + max-same-issues: 0 + +output: + show-stats: true + sort-results: true + sort-order: + - linter + - file diff --git a/vendor/github.com/gofrs/flock/.travis.yml b/vendor/github.com/gofrs/flock/.travis.yml deleted file mode 100644 index b16d040fa..000000000 --- a/vendor/github.com/gofrs/flock/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: go -go: - - 1.14.x - - 1.15.x -script: go test -v -check.vv -race ./... -sudo: false -notifications: - email: - on_success: never - on_failure: always diff --git a/vendor/github.com/gofrs/flock/LICENSE b/vendor/github.com/gofrs/flock/LICENSE index 8b8ff36fe..7de525bf0 100644 --- a/vendor/github.com/gofrs/flock/LICENSE +++ b/vendor/github.com/gofrs/flock/LICENSE @@ -1,3 +1,4 @@ +Copyright (c) 2018-2024, The Gofrs Copyright (c) 2015-2020, Tim Heckman All rights reserved. diff --git a/vendor/github.com/gofrs/flock/Makefile b/vendor/github.com/gofrs/flock/Makefile new file mode 100644 index 000000000..65c139d68 --- /dev/null +++ b/vendor/github.com/gofrs/flock/Makefile @@ -0,0 +1,15 @@ +.PHONY: lint test test_race build_cross_os + +default: lint test build_cross_os + +test: + go test -v -cover ./... + +test_race: + CGO_ENABLED=1 go test -v -race ./... + +lint: + golangci-lint run + +build_cross_os: + ./build.sh diff --git a/vendor/github.com/gofrs/flock/README.md b/vendor/github.com/gofrs/flock/README.md index 71ce63692..f7ca0dd9c 100644 --- a/vendor/github.com/gofrs/flock/README.md +++ b/vendor/github.com/gofrs/flock/README.md @@ -1,26 +1,22 @@ # flock -[![TravisCI Build Status](https://img.shields.io/travis/gofrs/flock/master.svg?style=flat)](https://travis-ci.org/gofrs/flock) -[![GoDoc](https://img.shields.io/badge/godoc-flock-blue.svg?style=flat)](https://godoc.org/github.com/gofrs/flock) -[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/master/LICENSE) -[![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/flock)](https://goreportcard.com/report/github.com/gofrs/flock) -`flock` implements a thread-safe sync.Locker interface for file locking. It also -includes a non-blocking TryLock() function to allow locking without blocking execution. +[![Go Reference](https://pkg.go.dev/badge/github.com/gofrs/flock.svg)](https://pkg.go.dev/github.com/gofrs/flock) +[![License](https://img.shields.io/badge/license-BSD_3--Clause-brightgreen.svg?style=flat)](https://github.com/gofrs/flock/blob/main/LICENSE) +[![Go Report Card](https://goreportcard.com/badge/github.com/gofrs/flock)](https://goreportcard.com/report/github.com/gofrs/flock) -## License -`flock` is released under the BSD 3-Clause License. See the `LICENSE` file for more details. +`flock` implements a thread-safe file lock. -## Go Compatibility -This package makes use of the `context` package that was introduced in Go 1.7. As such, this -package has an implicit dependency on Go 1.7+. +It also includes a non-blocking `TryLock()` function to allow locking without blocking execution. ## Installation -``` + +```bash go get -u github.com/gofrs/flock ``` ## Usage -```Go + +```go import "github.com/gofrs/flock" fileLock := flock.New("/var/lock/go-lock.lock") @@ -38,4 +34,12 @@ if locked { ``` For more detailed usage information take a look at the package API docs on -[GoDoc](https://godoc.org/github.com/gofrs/flock). +[GoDoc](https://pkg.go.dev/github.com/gofrs/flock). + +## License + +`flock` is released under the BSD 3-Clause License. See the [`LICENSE`](./LICENSE) file for more details. + +## Project History + +This project was originally `github.com/theckman/go-flock`, it was transferred to Gofrs by the original author [Tim Heckman ](https://github.com/theckman). diff --git a/vendor/github.com/gofrs/flock/SECURITY.md b/vendor/github.com/gofrs/flock/SECURITY.md new file mode 100644 index 000000000..01419bd59 --- /dev/null +++ b/vendor/github.com/gofrs/flock/SECURITY.md @@ -0,0 +1,21 @@ +# Security Policy + +## Supported Versions + +We support the latest version of this library. +We do not guarantee support of previous versions. + +If a defect is reported, it will generally be fixed on the latest version (provided it exists) irrespective of whether it was introduced in a prior version. + +## Reporting a Vulnerability + +To report a potential security vulnerability, please create a [security advisory](https://github.com/gofrs/flock/security/advisories/new). + +For us to respond to your report most effectively, please include any of the following: + +- Steps to reproduce or a proof-of-concept +- Any relevant information, including the versions used + +## Security Scorecard + +This project submits security [results](https://scorecard.dev/viewer/?uri=github.com/gofrs/flock) to the [OpenSSF Scorecard](https://securityscorecards.dev/). diff --git a/vendor/github.com/gofrs/flock/appveyor.yml b/vendor/github.com/gofrs/flock/appveyor.yml deleted file mode 100644 index 909b4bf7c..000000000 --- a/vendor/github.com/gofrs/flock/appveyor.yml +++ /dev/null @@ -1,25 +0,0 @@ -version: '{build}' - -build: false -deploy: false - -clone_folder: 'c:\gopath\src\github.com\gofrs\flock' - -environment: - GOPATH: 'c:\gopath' - GOVERSION: '1.15' - -init: - - git config --global core.autocrlf input - -install: - - rmdir c:\go /s /q - - appveyor DownloadFile https://storage.googleapis.com/golang/go%GOVERSION%.windows-amd64.msi - - msiexec /i go%GOVERSION%.windows-amd64.msi /q - - set Path=c:\go\bin;c:\gopath\bin;%Path% - - go version - - go env - -test_script: - - go get -t ./... - - go test -race -v ./... diff --git a/vendor/github.com/gofrs/flock/build.sh b/vendor/github.com/gofrs/flock/build.sh new file mode 100644 index 000000000..60f7809f0 --- /dev/null +++ b/vendor/github.com/gofrs/flock/build.sh @@ -0,0 +1,18 @@ +#!/bin/bash -e + +# Not supported by flock: +# - plan9/* +# - js/wasm +# - wasp1/wasm + +for row in $(go tool dist list -json | jq -r '.[] | @base64'); do + _jq() { + echo ${row} | base64 --decode | jq -r ${1} + } + + GOOS=$(_jq '.GOOS') + GOARCH=$(_jq '.GOARCH') + + echo "$GOOS/$GOARCH" + GOOS=$GOOS GOARCH=$GOARCH go build +done diff --git a/vendor/github.com/gofrs/flock/flock.go b/vendor/github.com/gofrs/flock/flock.go index 95c784ca5..ff942b228 100644 --- a/vendor/github.com/gofrs/flock/flock.go +++ b/vendor/github.com/gofrs/flock/flock.go @@ -1,4 +1,5 @@ // Copyright 2015 Tim Heckman. All rights reserved. +// Copyright 2018-2024 The Gofrs. All rights reserved. // Use of this source code is governed by the BSD 3-Clause // license that can be found in the LICENSE file. @@ -18,12 +19,29 @@ package flock import ( "context" + "io/fs" "os" "runtime" "sync" "time" ) +type Option func(f *Flock) + +// SetFlag sets the flag used to create/open the file. +func SetFlag(flag int) Option { + return func(f *Flock) { + f.flag = flag + } +} + +// SetPermissions sets the OS permissions to set on the file. +func SetPermissions(perm fs.FileMode) Option { + return func(f *Flock) { + f.perm = perm + } +} + // Flock is the struct type to handle file locking. All fields are unexported, // with access to some of the fields provided by getter methods (Path() and Locked()). type Flock struct { @@ -32,12 +50,37 @@ type Flock struct { fh *os.File l bool r bool + + // flag is the flag used to create/open the file. + flag int + // perm is the OS permissions to set on the file. + perm fs.FileMode } // New returns a new instance of *Flock. The only parameter // it takes is the path to the desired lockfile. -func New(path string) *Flock { - return &Flock{path: path} +func New(path string, opts ...Option) *Flock { + // create it if it doesn't exist, and open the file read-only. + flags := os.O_CREATE + switch runtime.GOOS { + case "aix", "solaris", "illumos": + // AIX cannot preform write-lock (i.e. exclusive) on a read-only file. + flags |= os.O_RDWR + default: + flags |= os.O_RDONLY + } + + f := &Flock{ + path: path, + flag: flags, + perm: fs.FileMode(0o600), + } + + for _, opt := range opts { + opt(f) + } + + return f } // NewFlock returns a new instance of *Flock. The only parameter @@ -67,6 +110,7 @@ func (f *Flock) Path() string { func (f *Flock) Locked() bool { f.m.RLock() defer f.m.RUnlock() + return f.l } @@ -76,6 +120,7 @@ func (f *Flock) Locked() bool { func (f *Flock) RLocked() bool { f.m.RLock() defer f.m.RUnlock() + return f.r } @@ -83,16 +128,18 @@ func (f *Flock) String() string { return f.path } -// TryLockContext repeatedly tries to take an exclusive lock until one of the -// conditions is met: TryLock succeeds, TryLock fails with error, or Context -// Done channel is closed. +// TryLockContext repeatedly tries to take an exclusive lock until one of the conditions is met: +// - TryLock succeeds +// - TryLock fails with error +// - Context Done channel is closed. func (f *Flock) TryLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) { return tryCtx(ctx, f.TryLock, retryDelay) } -// TryRLockContext repeatedly tries to take a shared lock until one of the -// conditions is met: TryRLock succeeds, TryRLock fails with error, or Context -// Done channel is closed. +// TryRLockContext repeatedly tries to take a shared lock until one of the conditions is met: +// - TryRLock succeeds +// - TryRLock fails with error +// - Context Done channel is closed. func (f *Flock) TryRLockContext(ctx context.Context, retryDelay time.Duration) (bool, error) { return tryCtx(ctx, f.TryRLock, retryDelay) } @@ -101,10 +148,12 @@ func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Durati if ctx.Err() != nil { return false, ctx.Err() } + for { if ok, err := fn(); ok || err != nil { return ok, err } + select { case <-ctx.Done(): return false, ctx.Err() @@ -114,31 +163,44 @@ func tryCtx(ctx context.Context, fn func() (bool, error), retryDelay time.Durati } } -func (f *Flock) setFh() error { +func (f *Flock) setFh(flag int) error { // open a new os.File instance - // create it if it doesn't exist, and open the file read-only. - flags := os.O_CREATE - if runtime.GOOS == "aix" { - // AIX cannot preform write-lock (ie exclusive) on a - // read-only file. - flags |= os.O_RDWR - } else { - flags |= os.O_RDONLY - } - fh, err := os.OpenFile(f.path, flags, os.FileMode(0600)) + fh, err := os.OpenFile(f.path, flag, f.perm) if err != nil { return err } - // set the filehandle on the struct + // set the file handle on the struct f.fh = fh + return nil } -// ensure the file handle is closed if no lock is held +// resetFh resets file handle: +// - tries to close the file (ignore errors) +// - sets fh to nil. +func (f *Flock) resetFh() { + if f.fh == nil { + return + } + + _ = f.fh.Close() + + f.fh = nil +} + +// ensure the file handle is closed if no lock is held. func (f *Flock) ensureFhState() { - if !f.l && !f.r && f.fh != nil { - f.fh.Close() - f.fh = nil + if f.l || f.r || f.fh == nil { + return } + + f.resetFh() +} + +func (f *Flock) reset() { + f.l = false + f.r = false + + f.resetFh() } diff --git a/vendor/github.com/gofrs/flock/flock_aix.go b/vendor/github.com/gofrs/flock/flock_aix.go deleted file mode 100644 index 7277c1b6b..000000000 --- a/vendor/github.com/gofrs/flock/flock_aix.go +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright 2019 Tim Heckman. All rights reserved. Use of this source code is -// governed by the BSD 3-Clause license that can be found in the LICENSE file. - -// Copyright 2018 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This code implements the filelock API using POSIX 'fcntl' locks, which attach -// to an (inode, process) pair rather than a file descriptor. To avoid unlocking -// files prematurely when the same file is opened through different descriptors, -// we allow only one read-lock at a time. -// -// This code is adapted from the Go package: -// cmd/go/internal/lockedfile/internal/filelock - -//+build aix - -package flock - -import ( - "errors" - "io" - "os" - "sync" - "syscall" - - "golang.org/x/sys/unix" -) - -type lockType int16 - -const ( - readLock lockType = unix.F_RDLCK - writeLock lockType = unix.F_WRLCK -) - -type cmdType int - -const ( - tryLock cmdType = unix.F_SETLK - waitLock cmdType = unix.F_SETLKW -) - -type inode = uint64 - -type inodeLock struct { - owner *Flock - queue []<-chan *Flock -} - -var ( - mu sync.Mutex - inodes = map[*Flock]inode{} - locks = map[inode]inodeLock{} -) - -// Lock is a blocking call to try and take an exclusive file lock. It will wait -// until it is able to obtain the exclusive file lock. It's recommended that -// TryLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. -// -// If we are already exclusive-locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. -// -// If the *Flock has a shared lock (RLock), this may transparently replace the -// shared lock with an exclusive lock on some UNIX-like operating systems. Be -// careful when using exclusive locks in conjunction with shared locks -// (RLock()), because calling Unlock() may accidentally release the exclusive -// lock that was once a shared lock. -func (f *Flock) Lock() error { - return f.lock(&f.l, writeLock) -} - -// RLock is a blocking call to try and take a shared file lock. It will wait -// until it is able to obtain the shared file lock. It's recommended that -// TryRLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. -// -// If we are already shared-locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. -func (f *Flock) RLock() error { - return f.lock(&f.r, readLock) -} - -func (f *Flock) lock(locked *bool, flag lockType) error { - f.m.Lock() - defer f.m.Unlock() - - if *locked { - return nil - } - - if f.fh == nil { - if err := f.setFh(); err != nil { - return err - } - defer f.ensureFhState() - } - - if _, err := f.doLock(waitLock, flag, true); err != nil { - return err - } - - *locked = true - return nil -} - -func (f *Flock) doLock(cmd cmdType, lt lockType, blocking bool) (bool, error) { - // POSIX locks apply per inode and process, and the lock for an inode is - // released when *any* descriptor for that inode is closed. So we need to - // synchronize access to each inode internally, and must serialize lock and - // unlock calls that refer to the same inode through different descriptors. - fi, err := f.fh.Stat() - if err != nil { - return false, err - } - ino := inode(fi.Sys().(*syscall.Stat_t).Ino) - - mu.Lock() - if i, dup := inodes[f]; dup && i != ino { - mu.Unlock() - return false, &os.PathError{ - Path: f.Path(), - Err: errors.New("inode for file changed since last Lock or RLock"), - } - } - - inodes[f] = ino - - var wait chan *Flock - l := locks[ino] - if l.owner == f { - // This file already owns the lock, but the call may change its lock type. - } else if l.owner == nil { - // No owner: it's ours now. - l.owner = f - } else if !blocking { - // Already owned: cannot take the lock. - mu.Unlock() - return false, nil - } else { - // Already owned: add a channel to wait on. - wait = make(chan *Flock) - l.queue = append(l.queue, wait) - } - locks[ino] = l - mu.Unlock() - - if wait != nil { - wait <- f - } - - err = setlkw(f.fh.Fd(), cmd, lt) - - if err != nil { - f.doUnlock() - if cmd == tryLock && err == unix.EACCES { - return false, nil - } - return false, err - } - - return true, nil -} - -func (f *Flock) Unlock() error { - f.m.Lock() - defer f.m.Unlock() - - // if we aren't locked or if the lockfile instance is nil - // just return a nil error because we are unlocked - if (!f.l && !f.r) || f.fh == nil { - return nil - } - - if err := f.doUnlock(); err != nil { - return err - } - - f.fh.Close() - - f.l = false - f.r = false - f.fh = nil - - return nil -} - -func (f *Flock) doUnlock() (err error) { - var owner *Flock - mu.Lock() - ino, ok := inodes[f] - if ok { - owner = locks[ino].owner - } - mu.Unlock() - - if owner == f { - err = setlkw(f.fh.Fd(), waitLock, unix.F_UNLCK) - } - - mu.Lock() - l := locks[ino] - if len(l.queue) == 0 { - // No waiters: remove the map entry. - delete(locks, ino) - } else { - // The first waiter is sending us their file now. - // Receive it and update the queue. - l.owner = <-l.queue[0] - l.queue = l.queue[1:] - locks[ino] = l - } - delete(inodes, f) - mu.Unlock() - - return err -} - -// TryLock is the preferred function for taking an exclusive file lock. This -// function takes an RW-mutex lock before it tries to lock the file, so there is -// the possibility that this function may block for a short time if another -// goroutine is trying to take any action. -// -// The actual file lock is non-blocking. If we are unable to get the exclusive -// file lock, the function will return false instead of waiting for the lock. If -// we get the lock, we also set the *Flock instance as being exclusive-locked. -func (f *Flock) TryLock() (bool, error) { - return f.try(&f.l, writeLock) -} - -// TryRLock is the preferred function for taking a shared file lock. This -// function takes an RW-mutex lock before it tries to lock the file, so there is -// the possibility that this function may block for a short time if another -// goroutine is trying to take any action. -// -// The actual file lock is non-blocking. If we are unable to get the shared file -// lock, the function will return false instead of waiting for the lock. If we -// get the lock, we also set the *Flock instance as being share-locked. -func (f *Flock) TryRLock() (bool, error) { - return f.try(&f.r, readLock) -} - -func (f *Flock) try(locked *bool, flag lockType) (bool, error) { - f.m.Lock() - defer f.m.Unlock() - - if *locked { - return true, nil - } - - if f.fh == nil { - if err := f.setFh(); err != nil { - return false, err - } - defer f.ensureFhState() - } - - haslock, err := f.doLock(tryLock, flag, false) - if err != nil { - return false, err - } - - *locked = haslock - return haslock, nil -} - -// setlkw calls FcntlFlock with cmd for the entire file indicated by fd. -func setlkw(fd uintptr, cmd cmdType, lt lockType) error { - for { - err := unix.FcntlFlock(fd, int(cmd), &unix.Flock_t{ - Type: int16(lt), - Whence: io.SeekStart, - Start: 0, - Len: 0, // All bytes. - }) - if err != unix.EINTR { - return err - } - } -} diff --git a/vendor/github.com/gofrs/flock/flock_others.go b/vendor/github.com/gofrs/flock/flock_others.go new file mode 100644 index 000000000..18b14f1bd --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_others.go @@ -0,0 +1,40 @@ +//go:build (!unix && !windows) || plan9 + +package flock + +import ( + "errors" + "io/fs" +) + +func (f *Flock) Lock() error { + return &fs.PathError{ + Op: "Lock", + Path: f.Path(), + Err: errors.ErrUnsupported, + } +} + +func (f *Flock) RLock() error { + return &fs.PathError{ + Op: "RLock", + Path: f.Path(), + Err: errors.ErrUnsupported, + } +} + +func (f *Flock) Unlock() error { + return &fs.PathError{ + Op: "Unlock", + Path: f.Path(), + Err: errors.ErrUnsupported, + } +} + +func (f *Flock) TryLock() (bool, error) { + return false, f.Lock() +} + +func (f *Flock) TryRLock() (bool, error) { + return false, f.RLock() +} diff --git a/vendor/github.com/gofrs/flock/flock_unix.go b/vendor/github.com/gofrs/flock/flock_unix.go index c315a3e29..cf8919c7a 100644 --- a/vendor/github.com/gofrs/flock/flock_unix.go +++ b/vendor/github.com/gofrs/flock/flock_unix.go @@ -1,42 +1,44 @@ // Copyright 2015 Tim Heckman. All rights reserved. +// Copyright 2018-2024 The Gofrs. All rights reserved. // Use of this source code is governed by the BSD 3-Clause // license that can be found in the LICENSE file. -// +build !aix,!windows +//go:build darwin || dragonfly || freebsd || illumos || linux || netbsd || openbsd package flock import ( + "errors" "os" - "syscall" + + "golang.org/x/sys/unix" ) -// Lock is a blocking call to try and take an exclusive file lock. It will wait -// until it is able to obtain the exclusive file lock. It's recommended that -// TryLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. +// Lock is a blocking call to try and take an exclusive file lock. +// It will wait until it is able to obtain the exclusive file lock. +// It's recommended that TryLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. // -// If we are already exclusive-locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. +// If we are already exclusive-locked, +// this function short-circuits and returns immediately assuming it can take the mutex lock. // -// If the *Flock has a shared lock (RLock), this may transparently replace the -// shared lock with an exclusive lock on some UNIX-like operating systems. Be -// careful when using exclusive locks in conjunction with shared locks -// (RLock()), because calling Unlock() may accidentally release the exclusive -// lock that was once a shared lock. +// If the *Flock has a shared lock (RLock), +// this may transparently replace the shared lock with an exclusive lock on some UNIX-like operating systems. +// Be careful when using exclusive locks in conjunction with shared locks (RLock()), +// because calling Unlock() may accidentally release the exclusive lock that was once a shared lock. func (f *Flock) Lock() error { - return f.lock(&f.l, syscall.LOCK_EX) + return f.lock(&f.l, unix.LOCK_EX) } -// RLock is a blocking call to try and take a shared file lock. It will wait -// until it is able to obtain the shared file lock. It's recommended that -// TryRLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. +// RLock is a blocking call to try and take a shared file lock. +// It will wait until it is able to obtain the shared file lock. +// It's recommended that TryRLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. // -// If we are already shared-locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. +// If we are already shared-locked, +// this function short-circuits and returns immediately assuming it can take the mutex lock. func (f *Flock) RLock() error { - return f.lock(&f.r, syscall.LOCK_SH) + return f.lock(&f.r, unix.LOCK_SH) } func (f *Flock) lock(locked *bool, flag int) error { @@ -48,13 +50,15 @@ func (f *Flock) lock(locked *bool, flag int) error { } if f.fh == nil { - if err := f.setFh(); err != nil { + if err := f.setFh(f.flag); err != nil { return err } + defer f.ensureFhState() } - if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil { + err := unix.Flock(int(f.fh.Fd()), flag) + if err != nil { shouldRetry, reopenErr := f.reopenFDOnError(err) if reopenErr != nil { return reopenErr @@ -64,71 +68,74 @@ func (f *Flock) lock(locked *bool, flag int) error { return err } - if err = syscall.Flock(int(f.fh.Fd()), flag); err != nil { + err = unix.Flock(int(f.fh.Fd()), flag) + if err != nil { return err } } *locked = true + return nil } -// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so -// while it is running the Locked() and RLocked() functions will be blocked. +// Unlock is a function to unlock the file. +// This file takes a RW-mutex lock, +// so while it is running the Locked() and RLocked() functions will be blocked. // -// This function short-circuits if we are unlocked already. If not, it calls -// syscall.LOCK_UN on the file and closes the file descriptor. It does not -// remove the file from disk. It's up to your application to do. +// This function short-circuits if we are unlocked already. +// If not, it calls unix.LOCK_UN on the file and closes the file descriptor. +// It does not remove the file from disk. It's up to your application to do. // -// Please note, if your shared lock became an exclusive lock this may -// unintentionally drop the exclusive lock if called by the consumer that -// believes they have a shared lock. Please see Lock() for more details. +// Please note, +// if your shared lock became an exclusive lock, +// this may unintentionally drop the exclusive lock if called by the consumer that believes they have a shared lock. +// Please see Lock() for more details. func (f *Flock) Unlock() error { f.m.Lock() defer f.m.Unlock() - // if we aren't locked or if the lockfile instance is nil - // just return a nil error because we are unlocked + // If we aren't locked or if the lockfile instance is nil + // just return a nil error because we are unlocked. if (!f.l && !f.r) || f.fh == nil { return nil } - // mark the file as unlocked - if err := syscall.Flock(int(f.fh.Fd()), syscall.LOCK_UN); err != nil { + // Mark the file as unlocked. + err := unix.Flock(int(f.fh.Fd()), unix.LOCK_UN) + if err != nil { return err } - f.fh.Close() - - f.l = false - f.r = false - f.fh = nil + f.reset() return nil } -// TryLock is the preferred function for taking an exclusive file lock. This -// function takes an RW-mutex lock before it tries to lock the file, so there is -// the possibility that this function may block for a short time if another -// goroutine is trying to take any action. +// TryLock is the preferred function for taking an exclusive file lock. +// This function takes an RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time +// if another goroutine is trying to take any action. // -// The actual file lock is non-blocking. If we are unable to get the exclusive -// file lock, the function will return false instead of waiting for the lock. If -// we get the lock, we also set the *Flock instance as being exclusive-locked. +// The actual file lock is non-blocking. +// If we are unable to get the exclusive file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being exclusive-locked. func (f *Flock) TryLock() (bool, error) { - return f.try(&f.l, syscall.LOCK_EX) + return f.try(&f.l, unix.LOCK_EX) } -// TryRLock is the preferred function for taking a shared file lock. This -// function takes an RW-mutex lock before it tries to lock the file, so there is -// the possibility that this function may block for a short time if another -// goroutine is trying to take any action. +// TryRLock is the preferred function for taking a shared file lock. +// This function takes an RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time +// if another goroutine is trying to take any action. // -// The actual file lock is non-blocking. If we are unable to get the shared file -// lock, the function will return false instead of waiting for the lock. If we -// get the lock, we also set the *Flock instance as being share-locked. +// The actual file lock is non-blocking. +// If we are unable to get the shared file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being share-locked. func (f *Flock) TryRLock() (bool, error) { - return f.try(&f.r, syscall.LOCK_SH) + return f.try(&f.r, unix.LOCK_SH) } func (f *Flock) try(locked *bool, flag int) (bool, error) { @@ -140,25 +147,28 @@ func (f *Flock) try(locked *bool, flag int) (bool, error) { } if f.fh == nil { - if err := f.setFh(); err != nil { + if err := f.setFh(f.flag); err != nil { return false, err } + defer f.ensureFhState() } var retried bool retry: - err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB) + err := unix.Flock(int(f.fh.Fd()), flag|unix.LOCK_NB) - switch err { - case syscall.EWOULDBLOCK: + switch { + case errors.Is(err, unix.EWOULDBLOCK): return false, nil - case nil: + case err == nil: *locked = true return true, nil } + if !retried { - if shouldRetry, reopenErr := f.reopenFDOnError(err); reopenErr != nil { + shouldRetry, reopenErr := f.reopenFDOnError(err) + if reopenErr != nil { return false, reopenErr } else if shouldRetry { retried = true @@ -169,29 +179,32 @@ retry: return false, err } -// reopenFDOnError determines whether we should reopen the file handle -// in readwrite mode and try again. This comes from util-linux/sys-utils/flock.c: -// Since Linux 3.4 (commit 55725513) -// Probably NFSv4 where flock() is emulated by fcntl(). +// reopenFDOnError determines whether we should reopen the file handle in readwrite mode and try again. +// This comes from `util-linux/sys-utils/flock.c`: +// > Since Linux 3.4 (commit 55725513) +// > Probably NFSv4 where flock() is emulated by fcntl(). +// > https://github.com/util-linux/util-linux/blob/198e920aa24743ef6ace4e07cf6237de527f9261/sys-utils/flock.c#L374-L390 func (f *Flock) reopenFDOnError(err error) (bool, error) { - if err != syscall.EIO && err != syscall.EBADF { + if !errors.Is(err, unix.EIO) && !errors.Is(err, unix.EBADF) { return false, nil } - if st, err := f.fh.Stat(); err == nil { - // if the file is able to be read and written - if st.Mode()&0600 == 0600 { - f.fh.Close() - f.fh = nil - - // reopen in read-write mode and set the filehandle - fh, err := os.OpenFile(f.path, os.O_CREATE|os.O_RDWR, os.FileMode(0600)) - if err != nil { - return false, err - } - f.fh = fh - return true, nil - } + + st, err := f.fh.Stat() + if err != nil { + return false, nil + } + + if st.Mode()&f.perm != f.perm { + return false, nil + } + + f.resetFh() + + // reopen in read-write mode and set the file handle + err = f.setFh(f.flag | os.O_RDWR) + if err != nil { + return false, err } - return false, nil + return true, nil } diff --git a/vendor/github.com/gofrs/flock/flock_unix_fcntl.go b/vendor/github.com/gofrs/flock/flock_unix_fcntl.go new file mode 100644 index 000000000..ea007b47d --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_unix_fcntl.go @@ -0,0 +1,393 @@ +// Copyright 2015 Tim Heckman. All rights reserved. +// Copyright 2018-2024 The Gofrs. All rights reserved. +// Use of this source code is governed by the BSD 3-Clause +// license that can be found in the LICENSE file. + +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This code implements the filelock API using POSIX 'fcntl' locks, +// which attach to an (inode, process) pair rather than a file descriptor. +// To avoid unlocking files prematurely when the same file is opened through different descriptors, +// we allow only one read-lock at a time. +// +// This code is adapted from the Go package (go.22): +// https://github.com/golang/go/blob/release-branch.go1.22/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go + +//go:build aix || (solaris && !illumos) + +package flock + +import ( + "errors" + "io" + "io/fs" + "math/rand" + "sync" + "syscall" + "time" + + "golang.org/x/sys/unix" +) + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L28 +type lockType int16 + +// String returns the name of the function corresponding to lt +// (Lock, RLock, or Unlock). +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock.go#L67 +func (lt lockType) String() string { + switch lt { + case readLock: + return "RLock" + case writeLock: + return "Lock" + default: + return "Unlock" + } +} + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L30-L33 +const ( + readLock lockType = unix.F_RDLCK + writeLock lockType = unix.F_WRLCK +) + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L35 +type inode = uint64 + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L37-L40 +type inodeLock struct { + owner *Flock + queue []<-chan *Flock +} + +type cmdType int + +const ( + tryLock cmdType = unix.F_SETLK + waitLock cmdType = unix.F_SETLKW +) + +var ( + mu sync.Mutex + inodes = map[*Flock]inode{} + locks = map[inode]inodeLock{} +) + +// Lock is a blocking call to try and take an exclusive file lock. +// It will wait until it is able to obtain the exclusive file lock. +// It's recommended that TryLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already exclusive-locked, this function short-circuits and +// returns immediately assuming it can take the mutex lock. +// +// If the *Flock has a shared lock (RLock), +// this may transparently replace the shared lock with an exclusive lock on some UNIX-like operating systems. +// Be careful when using exclusive locks in conjunction with shared locks (RLock()), +// because calling Unlock() may accidentally release the exclusive lock that was once a shared lock. +func (f *Flock) Lock() error { + return f.lock(&f.l, writeLock) +} + +// RLock is a blocking call to try and take a shared file lock. +// It will wait until it is able to obtain the shared file lock. +// It's recommended that TryRLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. +// +// If we are already shared-locked, this function short-circuits and +// returns immediately assuming it can take the mutex lock. +func (f *Flock) RLock() error { + return f.lock(&f.r, readLock) +} + +func (f *Flock) lock(locked *bool, flag lockType) error { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return nil + } + + if f.fh == nil { + if err := f.setFh(f.flag); err != nil { + return err + } + + defer f.ensureFhState() + } + + _, err := f.doLock(waitLock, flag, true) + if err != nil { + return err + } + + *locked = true + + return nil +} + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L48 +func (f *Flock) doLock(cmd cmdType, lt lockType, blocking bool) (bool, error) { + // POSIX locks apply per inode and process, + // and the lock for an inode is released when *any* descriptor for that inode is closed. + // So we need to synchronize access to each inode internally, + // and must serialize lock and unlock calls that refer to the same inode through different descriptors. + fi, err := f.fh.Stat() + if err != nil { + return false, err + } + + // Note(ldez): don't replace `syscall.Stat_t` by `unix.Stat_t` because `FileInfo.Sys()` returns `syscall.Stat_t` + ino := fi.Sys().(*syscall.Stat_t).Ino + + mu.Lock() + + if i, dup := inodes[f]; dup && i != ino { + mu.Unlock() + return false, &fs.PathError{ + Op: lt.String(), + Path: f.Path(), + Err: errors.New("inode for file changed since last Lock or RLock"), + } + } + + inodes[f] = ino + + var wait chan *Flock + + l := locks[ino] + + switch { + case l.owner == f: + // This file already owns the lock, but the call may change its lock type. + case l.owner == nil: + // No owner: it's ours now. + l.owner = f + + case !blocking: + // Already owned: cannot take the lock. + mu.Unlock() + return false, nil + + default: + // Already owned: add a channel to wait on. + wait = make(chan *Flock) + l.queue = append(l.queue, wait) + } + + locks[ino] = l + + mu.Unlock() + + if wait != nil { + wait <- f + } + + // Spurious EDEADLK errors arise on platforms that compute deadlock graphs at + // the process, rather than thread, level. Consider processes P and Q, with + // threads P.1, P.2, and Q.3. The following trace is NOT a deadlock, but will be + // reported as a deadlock on systems that consider only process granularity: + // + // P.1 locks file A. + // Q.3 locks file B. + // Q.3 blocks on file A. + // P.2 blocks on file B. (This is erroneously reported as a deadlock.) + // P.1 unlocks file A. + // Q.3 unblocks and locks file A. + // Q.3 unlocks files A and B. + // P.2 unblocks and locks file B. + // P.2 unlocks file B. + // + // These spurious errors were observed in practice on AIX and Solaris in + // cmd/go: see https://golang.org/issue/32817. + // + // We work around this bug by treating EDEADLK as always spurious. If there + // really is a lock-ordering bug between the interacting processes, it will + // become a livelock instead, but that's not appreciably worse than if we had + // a proper flock implementation (which generally does not even attempt to + // diagnose deadlocks). + // + // In the above example, that changes the trace to: + // + // P.1 locks file A. + // Q.3 locks file B. + // Q.3 blocks on file A. + // P.2 spuriously fails to lock file B and goes to sleep. + // P.1 unlocks file A. + // Q.3 unblocks and locks file A. + // Q.3 unlocks files A and B. + // P.2 wakes up and locks file B. + // P.2 unlocks file B. + // + // We know that the retry loop will not introduce a *spurious* livelock + // because, according to the POSIX specification, EDEADLK is only to be + // returned when “the lock is blocked by a lock from another process”. + // If that process is blocked on some lock that we are holding, then the + // resulting livelock is due to a real deadlock (and would manifest as such + // when using, for example, the flock implementation of this package). + // If the other process is *not* blocked on some other lock that we are + // holding, then it will eventually release the requested lock. + + nextSleep := 1 * time.Millisecond + const maxSleep = 500 * time.Millisecond + for { + err = setlkw(f.fh.Fd(), cmd, lt) + if !errors.Is(err, unix.EDEADLK) { + break + } + + time.Sleep(nextSleep) + + nextSleep += nextSleep + if nextSleep > maxSleep { + nextSleep = maxSleep + } + // Apply 10% jitter to avoid synchronizing collisions when we finally unblock. + nextSleep += time.Duration((0.1*rand.Float64() - 0.05) * float64(nextSleep)) + } + + if err != nil { + f.doUnlock() + + if cmd == tryLock && errors.Is(err, unix.EACCES) { + return false, nil + } + + return false, &fs.PathError{ + Op: lt.String(), + Path: f.Path(), + Err: err, + } + } + + return true, nil +} + +func (f *Flock) Unlock() error { + f.m.Lock() + defer f.m.Unlock() + + // If we aren't locked or if the lockfile instance is nil + // just return a nil error because we are unlocked. + if (!f.l && !f.r) || f.fh == nil { + return nil + } + + if err := f.doUnlock(); err != nil { + return err + } + + f.reset() + + return nil +} + +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L163 +func (f *Flock) doUnlock() (err error) { + var owner *Flock + + mu.Lock() + + ino, ok := inodes[f] + if ok { + owner = locks[ino].owner + } + + mu.Unlock() + + if owner == f { + err = setlkw(f.fh.Fd(), waitLock, unix.F_UNLCK) + } + + mu.Lock() + + l := locks[ino] + + if len(l.queue) == 0 { + // No waiters: remove the map entry. + delete(locks, ino) + } else { + // The first waiter is sending us their file now. + // Receive it and update the queue. + l.owner = <-l.queue[0] + l.queue = l.queue[1:] + locks[ino] = l + } + + delete(inodes, f) + + mu.Unlock() + + return err +} + +// TryLock is the preferred function for taking an exclusive file lock. +// This function takes an RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time +// if another goroutine is trying to take any action. +// +// The actual file lock is non-blocking. +// If we are unable to get the exclusive file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being exclusive-locked. +func (f *Flock) TryLock() (bool, error) { + return f.try(&f.l, writeLock) +} + +// TryRLock is the preferred function for taking a shared file lock. +// This function takes an RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time +// if another goroutine is trying to take any action. +// +// The actual file lock is non-blocking. +// If we are unable to get the shared file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being share-locked. +func (f *Flock) TryRLock() (bool, error) { + return f.try(&f.r, readLock) +} + +func (f *Flock) try(locked *bool, flag lockType) (bool, error) { + f.m.Lock() + defer f.m.Unlock() + + if *locked { + return true, nil + } + + if f.fh == nil { + if err := f.setFh(f.flag); err != nil { + return false, err + } + + defer f.ensureFhState() + } + + hasLock, err := f.doLock(tryLock, flag, false) + if err != nil { + return false, err + } + + *locked = hasLock + + return hasLock, nil +} + +// setlkw calls FcntlFlock with cmd for the entire file indicated by fd. +// https://github.com/golang/go/blob/09aeb6e33ab426eff4676a3baf694d5a3019e9fc/src/cmd/go/internal/lockedfile/internal/filelock/filelock_fcntl.go#L198 +func setlkw(fd uintptr, cmd cmdType, lt lockType) error { + for { + err := unix.FcntlFlock(fd, int(cmd), &unix.Flock_t{ + Type: int16(lt), + Whence: io.SeekStart, + Start: 0, + Len: 0, // All bytes. + }) + if !errors.Is(err, unix.EINTR) { + return err + } + } +} diff --git a/vendor/github.com/gofrs/flock/flock_winapi.go b/vendor/github.com/gofrs/flock/flock_winapi.go deleted file mode 100644 index fe405a255..000000000 --- a/vendor/github.com/gofrs/flock/flock_winapi.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2015 Tim Heckman. All rights reserved. -// Use of this source code is governed by the BSD 3-Clause -// license that can be found in the LICENSE file. - -// +build windows - -package flock - -import ( - "syscall" - "unsafe" -) - -var ( - kernel32, _ = syscall.LoadLibrary("kernel32.dll") - procLockFileEx, _ = syscall.GetProcAddress(kernel32, "LockFileEx") - procUnlockFileEx, _ = syscall.GetProcAddress(kernel32, "UnlockFileEx") -) - -const ( - winLockfileFailImmediately = 0x00000001 - winLockfileExclusiveLock = 0x00000002 - winLockfileSharedLock = 0x00000000 -) - -// Use of 0x00000000 for the shared lock is a guess based on some the MS Windows -// `LockFileEX` docs, which document the `LOCKFILE_EXCLUSIVE_LOCK` flag as: -// -// > The function requests an exclusive lock. Otherwise, it requests a shared -// > lock. -// -// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx - -func lockFileEx(handle syscall.Handle, flags uint32, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) { - r1, _, errNo := syscall.Syscall6( - uintptr(procLockFileEx), - 6, - uintptr(handle), - uintptr(flags), - uintptr(reserved), - uintptr(numberOfBytesToLockLow), - uintptr(numberOfBytesToLockHigh), - uintptr(unsafe.Pointer(offset))) - - if r1 != 1 { - if errNo == 0 { - return false, syscall.EINVAL - } - - return false, errNo - } - - return true, 0 -} - -func unlockFileEx(handle syscall.Handle, reserved uint32, numberOfBytesToLockLow uint32, numberOfBytesToLockHigh uint32, offset *syscall.Overlapped) (bool, syscall.Errno) { - r1, _, errNo := syscall.Syscall6( - uintptr(procUnlockFileEx), - 5, - uintptr(handle), - uintptr(reserved), - uintptr(numberOfBytesToLockLow), - uintptr(numberOfBytesToLockHigh), - uintptr(unsafe.Pointer(offset)), - 0) - - if r1 != 1 { - if errNo == 0 { - return false, syscall.EINVAL - } - - return false, errNo - } - - return true, 0 -} diff --git a/vendor/github.com/gofrs/flock/flock_windows.go b/vendor/github.com/gofrs/flock/flock_windows.go index ddb534cce..dfd31e15f 100644 --- a/vendor/github.com/gofrs/flock/flock_windows.go +++ b/vendor/github.com/gofrs/flock/flock_windows.go @@ -1,35 +1,48 @@ // Copyright 2015 Tim Heckman. All rights reserved. +// Copyright 2018-2024 The Gofrs. All rights reserved. // Use of this source code is governed by the BSD 3-Clause // license that can be found in the LICENSE file. +//go:build windows + package flock import ( - "syscall" + "errors" + + "golang.org/x/sys/windows" ) -// ErrorLockViolation is the error code returned from the Windows syscall when a -// lock would block and you ask to fail immediately. -const ErrorLockViolation syscall.Errno = 0x21 // 33 +// Use of 0x00000000 for the shared lock is a guess based on some the MS Windows `LockFileEX` docs, +// which document the `LOCKFILE_EXCLUSIVE_LOCK` flag as: +// +// > The function requests an exclusive lock. Otherwise, it requests a shared lock. +// +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx +const winLockfileSharedLock = 0x00000000 + +// ErrorLockViolation is the error code returned from the Windows syscall when a lock would block, +// and you ask to fail immediately. +const ErrorLockViolation windows.Errno = 0x21 // 33 -// Lock is a blocking call to try and take an exclusive file lock. It will wait -// until it is able to obtain the exclusive file lock. It's recommended that -// TryLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. +// Lock is a blocking call to try and take an exclusive file lock. +// It will wait until it is able to obtain the exclusive file lock. +// It's recommended that TryLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. // -// If we are already locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. +// If we are already locked, this function short-circuits and +// returns immediately assuming it can take the mutex lock. func (f *Flock) Lock() error { - return f.lock(&f.l, winLockfileExclusiveLock) + return f.lock(&f.l, windows.LOCKFILE_EXCLUSIVE_LOCK) } -// RLock is a blocking call to try and take a shared file lock. It will wait -// until it is able to obtain the shared file lock. It's recommended that -// TryRLock() be used over this function. This function may block the ability to -// query the current Locked() or RLocked() status due to a RW-mutex lock. +// RLock is a blocking call to try and take a shared file lock. +// It will wait until it is able to obtain the shared file lock. +// It's recommended that TryRLock() be used over this function. +// This function may block the ability to query the current Locked() or RLocked() status due to a RW-mutex lock. // -// If we are already locked, this function short-circuits and returns -// immediately assuming it can take the mutex lock. +// If we are already locked, this function short-circuits and +// returns immediately assuming it can take the mutex lock. func (f *Flock) RLock() error { return f.lock(&f.r, winLockfileSharedLock) } @@ -43,26 +56,31 @@ func (f *Flock) lock(locked *bool, flag uint32) error { } if f.fh == nil { - if err := f.setFh(); err != nil { + if err := f.setFh(f.flag); err != nil { return err } + defer f.ensureFhState() } - if _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}); errNo > 0 { - return errNo + err := windows.LockFileEx(windows.Handle(f.fh.Fd()), flag, 0, 1, 0, &windows.Overlapped{}) + if err != nil && !errors.Is(err, windows.Errno(0)) { + return err } *locked = true + return nil } -// Unlock is a function to unlock the file. This file takes a RW-mutex lock, so -// while it is running the Locked() and RLocked() functions will be blocked. +// Unlock is a function to unlock the file. +// This file takes a RW-mutex lock, +// so while it is running the Locked() and RLocked() functions will be blocked. // -// This function short-circuits if we are unlocked already. If not, it calls -// UnlockFileEx() on the file and closes the file descriptor. It does not remove -// the file from disk. It's up to your application to do. +// This function short-circuits if we are unlocked already. +// If not, it calls UnlockFileEx() on the file and closes the file descriptor. +// It does not remove the file from disk. +// It's up to your application to do. func (f *Flock) Unlock() error { f.m.Lock() defer f.m.Unlock() @@ -74,39 +92,37 @@ func (f *Flock) Unlock() error { } // mark the file as unlocked - if _, errNo := unlockFileEx(syscall.Handle(f.fh.Fd()), 0, 1, 0, &syscall.Overlapped{}); errNo > 0 { - return errNo + err := windows.UnlockFileEx(windows.Handle(f.fh.Fd()), 0, 1, 0, &windows.Overlapped{}) + if err != nil && !errors.Is(err, windows.Errno(0)) { + return err } - f.fh.Close() - - f.l = false - f.r = false - f.fh = nil + f.reset() return nil } -// TryLock is the preferred function for taking an exclusive file lock. This -// function does take a RW-mutex lock before it tries to lock the file, so there -// is the possibility that this function may block for a short time if another -// goroutine is trying to take any action. +// TryLock is the preferred function for taking an exclusive file lock. +// This function does take a RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time +// if another goroutine is trying to take any action. // -// The actual file lock is non-blocking. If we are unable to get the exclusive -// file lock, the function will return false instead of waiting for the lock. If -// we get the lock, we also set the *Flock instance as being exclusive-locked. +// The actual file lock is non-blocking. +// If we are unable to get the exclusive file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being exclusive-locked. func (f *Flock) TryLock() (bool, error) { - return f.try(&f.l, winLockfileExclusiveLock) + return f.try(&f.l, windows.LOCKFILE_EXCLUSIVE_LOCK) } -// TryRLock is the preferred function for taking a shared file lock. This -// function does take a RW-mutex lock before it tries to lock the file, so there -// is the possibility that this function may block for a short time if another -// goroutine is trying to take any action. +// TryRLock is the preferred function for taking a shared file lock. +// This function does take a RW-mutex lock before it tries to lock the file, +// so there is the possibility that this function may block for a short time if another goroutine is trying to take any action. // -// The actual file lock is non-blocking. If we are unable to get the shared file -// lock, the function will return false instead of waiting for the lock. If we -// get the lock, we also set the *Flock instance as being shared-locked. +// The actual file lock is non-blocking. +// If we are unable to get the shared file lock, +// the function will return false instead of waiting for the lock. +// If we get the lock, we also set the *Flock instance as being shared-locked. func (f *Flock) TryRLock() (bool, error) { return f.try(&f.r, winLockfileSharedLock) } @@ -120,20 +136,20 @@ func (f *Flock) try(locked *bool, flag uint32) (bool, error) { } if f.fh == nil { - if err := f.setFh(); err != nil { + if err := f.setFh(f.flag); err != nil { return false, err } + defer f.ensureFhState() } - _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag|winLockfileFailImmediately, 0, 1, 0, &syscall.Overlapped{}) - - if errNo > 0 { - if errNo == ErrorLockViolation || errNo == syscall.ERROR_IO_PENDING { + err := windows.LockFileEx(windows.Handle(f.fh.Fd()), flag|windows.LOCKFILE_FAIL_IMMEDIATELY, 0, 1, 0, &windows.Overlapped{}) + if err != nil && !errors.Is(err, windows.Errno(0)) { + if errors.Is(err, ErrorLockViolation) || errors.Is(err, windows.ERROR_IO_PENDING) { return false, nil } - return false, errNo + return false, err } *locked = true -- cgit mrf-deployment