diff --git a/go.mod b/go.mod index 19881b0e81a3ea3964fd19e02a7b04aa71ff6471..cc0252c8123b9a69fff481f15fca0597d0b3de46 100644 --- a/go.mod +++ b/go.mod @@ -13,13 +13,13 @@ require ( github.com/go-ldap/ldap/v3 v3.4.8 github.com/go-sql-driver/mysql v1.7.1 github.com/go-webauthn/webauthn v0.10.2 + github.com/gofrs/flock v0.12.1 github.com/google/go-cmp v0.6.0 github.com/lib/pq v1.10.9 github.com/mattn/go-sqlite3 v1.14.23 github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 github.com/pquerna/otp v1.4.0 github.com/prometheus/client_golang v1.20.3 - github.com/theckman/go-flock v0.8.1 go.opentelemetry.io/otel v1.10.0 go.opentelemetry.io/otel/trace v1.10.0 golang.org/x/sync v0.8.0 diff --git a/go.sum b/go.sum index b60dca5b31dcf7a4266d8fd988073015eb8fdc5f..d41ff7b87c4f39a43622e70ea13a3be8036e49fa 100644 --- a/go.sum +++ b/go.sum @@ -784,7 +784,6 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/theckman/go-flock v0.8.1 h1:kTixuOsFBOtGYSTLRLWK6GOs1hk/8OD11sR1pDd0dl4= github.com/theckman/go-flock v0.8.1/go.mod h1:kjuth3y9VJ2aNlkNEO99G/8lp9fMIKaGyBmh84IBheM= github.com/tj/assert v0.0.0-20171129193455-018094318fb0/go.mod h1:mZ9/Rh9oLWpLLDRpvE+3b7gP/C2YyLFYxNmcLnPTMe0= github.com/tj/go-elastic v0.0.0-20171221160941-36157cbbebc2/go.mod h1:WjeM0Oo1eNAjXGDx2yma7uG2XoyRZTq1uv3M/o7imD0= diff --git a/lineproto/unix.go b/lineproto/unix.go index 3a6378b492619900939412a51e2fd3b9e662523d..d67ce7846fed2a3f68c2856a0eb360b3ac5bb9a3 100644 --- a/lineproto/unix.go +++ b/lineproto/unix.go @@ -5,7 +5,7 @@ import ( "net" "os" - "github.com/theckman/go-flock" + "github.com/gofrs/flock" ) type socketListener struct { diff --git a/vendor/github.com/theckman/go-flock/.gitignore b/vendor/github.com/gofrs/flock/.gitignore similarity index 100% rename from vendor/github.com/theckman/go-flock/.gitignore rename to vendor/github.com/gofrs/flock/.gitignore diff --git a/vendor/github.com/gofrs/flock/.golangci.yml b/vendor/github.com/gofrs/flock/.golangci.yml new file mode 100644 index 0000000000000000000000000000000000000000..3ad88a38fcd0f1cb2f011819a4739a74d60e6d97 --- /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/theckman/go-flock/LICENSE b/vendor/github.com/gofrs/flock/LICENSE similarity index 97% rename from vendor/github.com/theckman/go-flock/LICENSE rename to vendor/github.com/gofrs/flock/LICENSE index 8b8ff36fe4269955d9c5487e8c9dcb939b9ee135..7de525bf027ae25743e0418e7a6315c1f48f142c 100644 --- a/vendor/github.com/theckman/go-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 0000000000000000000000000000000000000000..65c139d68c089e767da15ee48befe216088e0186 --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..f7ca0dd9c2417fc83dc3304db7c74f5afbfa5aa1 --- /dev/null +++ b/vendor/github.com/gofrs/flock/README.md @@ -0,0 +1,45 @@ +# flock + +[](https://pkg.go.dev/github.com/gofrs/flock) +[](https://github.com/gofrs/flock/blob/main/LICENSE) +[](https://goreportcard.com/report/github.com/gofrs/flock) + +`flock` implements a thread-safe file lock. + +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 +import "github.com/gofrs/flock" + +fileLock := flock.New("/var/lock/go-lock.lock") + +locked, err := fileLock.TryLock() + +if err != nil { + // handle locking error +} + +if locked { + // do work + fileLock.Unlock() +} +``` + +For more detailed usage information take a look at the package API docs on +[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 0000000000000000000000000000000000000000..01419bd592afd34db61c51b03c9afaa9ee367ddd --- /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/build.sh b/vendor/github.com/gofrs/flock/build.sh new file mode 100644 index 0000000000000000000000000000000000000000..60f7809f06578bc91b9a8ab47df02c8fd6024514 --- /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/theckman/go-flock/flock.go b/vendor/github.com/gofrs/flock/flock.go similarity index 68% rename from vendor/github.com/theckman/go-flock/flock.go rename to vendor/github.com/gofrs/flock/flock.go index 95c784ca504bd1e089fd2e14462ea17a6fe4656c..ff942b228a66a7a4bbba671333bbe7cb1571beea 100644 --- a/vendor/github.com/theckman/go-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_others.go b/vendor/github.com/gofrs/flock/flock_others.go new file mode 100644 index 0000000000000000000000000000000000000000..18b14f1bd7a54690857e5e4a8f6c7e7405297aaf --- /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 new file mode 100644 index 0000000000000000000000000000000000000000..cf8919c7addb5a100124bb585dc0c645415645b4 --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_unix.go @@ -0,0 +1,210 @@ +// 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 darwin || dragonfly || freebsd || illumos || linux || netbsd || openbsd + +package flock + +import ( + "errors" + "os" + + "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. +// +// 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, 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. +// +// 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, unix.LOCK_SH) +} + +func (f *Flock) lock(locked *bool, flag int) 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 := unix.Flock(int(f.fh.Fd()), flag) + if err != nil { + shouldRetry, reopenErr := f.reopenFDOnError(err) + if reopenErr != nil { + return reopenErr + } + + if !shouldRetry { + return err + } + + 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. +// +// 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. +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 + } + + // Mark the file as unlocked. + err := unix.Flock(int(f.fh.Fd()), unix.LOCK_UN) + if err != nil { + return err + } + + 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. +// +// 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, 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. +// +// 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, unix.LOCK_SH) +} + +func (f *Flock) try(locked *bool, flag int) (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() + } + + var retried bool +retry: + err := unix.Flock(int(f.fh.Fd()), flag|unix.LOCK_NB) + + switch { + case errors.Is(err, unix.EWOULDBLOCK): + return false, nil + case err == nil: + *locked = true + return true, nil + } + + if !retried { + shouldRetry, reopenErr := f.reopenFDOnError(err) + if reopenErr != nil { + return false, reopenErr + } else if shouldRetry { + retried = true + goto 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(). +// > https://github.com/util-linux/util-linux/blob/198e920aa24743ef6ace4e07cf6237de527f9261/sys-utils/flock.c#L374-L390 +func (f *Flock) reopenFDOnError(err error) (bool, error) { + if !errors.Is(err, unix.EIO) && !errors.Is(err, unix.EBADF) { + return false, 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 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 0000000000000000000000000000000000000000..ea007b47d9af5f5ef76c96e4decca1267ed50d6f --- /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_windows.go b/vendor/github.com/gofrs/flock/flock_windows.go new file mode 100644 index 0000000000000000000000000000000000000000..dfd31e15f509ce7c0661ba8ed86b8390055eb918 --- /dev/null +++ b/vendor/github.com/gofrs/flock/flock_windows.go @@ -0,0 +1,158 @@ +// 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 ( + "errors" + + "golang.org/x/sys/windows" +) + +// 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. +// +// 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, 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. +// +// 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) +} + +func (f *Flock) lock(locked *bool, flag uint32) 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 := 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. +// +// 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() + + // 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 + 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.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. +// +// 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, 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. +// +// 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) +} + +func (f *Flock) try(locked *bool, flag uint32) (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() + } + + 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, err + } + + *locked = true + + return true, nil +} diff --git a/vendor/github.com/theckman/go-flock/.travis.yml b/vendor/github.com/theckman/go-flock/.travis.yml deleted file mode 100644 index b16d040fa89ebc904cbbc98afc8a8bcaf855507e..0000000000000000000000000000000000000000 --- a/vendor/github.com/theckman/go-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/theckman/go-flock/README.md b/vendor/github.com/theckman/go-flock/README.md deleted file mode 100644 index 71ce63692e8e3ef433719aea060aa4dbfbe53ffb..0000000000000000000000000000000000000000 --- a/vendor/github.com/theckman/go-flock/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# flock -[](https://travis-ci.org/gofrs/flock) -[](https://godoc.org/github.com/gofrs/flock) -[](https://github.com/gofrs/flock/blob/master/LICENSE) -[](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. - -## License -`flock` is released under the BSD 3-Clause License. See the `LICENSE` file for more details. - -## 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+. - -## Installation -``` -go get -u github.com/gofrs/flock -``` - -## Usage -```Go -import "github.com/gofrs/flock" - -fileLock := flock.New("/var/lock/go-lock.lock") - -locked, err := fileLock.TryLock() - -if err != nil { - // handle locking error -} - -if locked { - // do work - fileLock.Unlock() -} -``` - -For more detailed usage information take a look at the package API docs on -[GoDoc](https://godoc.org/github.com/gofrs/flock). diff --git a/vendor/github.com/theckman/go-flock/appveyor.yml b/vendor/github.com/theckman/go-flock/appveyor.yml deleted file mode 100644 index 909b4bf7cb4e21ca70c7cb9869066e8f4b6d719c..0000000000000000000000000000000000000000 --- a/vendor/github.com/theckman/go-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/theckman/go-flock/flock_aix.go b/vendor/github.com/theckman/go-flock/flock_aix.go deleted file mode 100644 index 7277c1b6b2655cdc7eb5dfa59666ed0ebafd7b98..0000000000000000000000000000000000000000 --- a/vendor/github.com/theckman/go-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/theckman/go-flock/flock_unix.go b/vendor/github.com/theckman/go-flock/flock_unix.go deleted file mode 100644 index c315a3e29084fd02b0da3513f50edec2cdaf8326..0000000000000000000000000000000000000000 --- a/vendor/github.com/theckman/go-flock/flock_unix.go +++ /dev/null @@ -1,197 +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 !aix,!windows - -package flock - -import ( - "os" - "syscall" -) - -// 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, syscall.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. -// -// 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) -} - -func (f *Flock) lock(locked *bool, flag int) 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 := syscall.Flock(int(f.fh.Fd()), flag); err != nil { - shouldRetry, reopenErr := f.reopenFDOnError(err) - if reopenErr != nil { - return reopenErr - } - - if !shouldRetry { - return err - } - - if err = syscall.Flock(int(f.fh.Fd()), flag); 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. -// -// 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. -// -// 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 (!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 { - return err - } - - f.fh.Close() - - f.l = false - f.r = false - f.fh = nil - - 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. -// -// 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) -} - -// 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, syscall.LOCK_SH) -} - -func (f *Flock) try(locked *bool, flag int) (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() - } - - var retried bool -retry: - err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB) - - switch err { - case syscall.EWOULDBLOCK: - return false, nil - case nil: - *locked = true - return true, nil - } - if !retried { - if shouldRetry, reopenErr := f.reopenFDOnError(err); reopenErr != nil { - return false, reopenErr - } else if shouldRetry { - retried = true - goto 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(). -func (f *Flock) reopenFDOnError(err error) (bool, error) { - if err != syscall.EIO && err != syscall.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 - } - } - - return false, nil -} diff --git a/vendor/github.com/theckman/go-flock/flock_winapi.go b/vendor/github.com/theckman/go-flock/flock_winapi.go deleted file mode 100644 index fe405a255ae5893637fe2b445c49090ae3c8f008..0000000000000000000000000000000000000000 --- a/vendor/github.com/theckman/go-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/theckman/go-flock/flock_windows.go b/vendor/github.com/theckman/go-flock/flock_windows.go deleted file mode 100644 index ddb534ccef097fa17fd211ddc9ba55a8313f1bb4..0000000000000000000000000000000000000000 --- a/vendor/github.com/theckman/go-flock/flock_windows.go +++ /dev/null @@ -1,142 +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. - -package flock - -import ( - "syscall" -) - -// 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 - -// 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. -func (f *Flock) Lock() error { - return f.lock(&f.l, winLockfileExclusiveLock) -} - -// 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. -func (f *Flock) RLock() error { - return f.lock(&f.r, winLockfileSharedLock) -} - -func (f *Flock) lock(locked *bool, flag uint32) 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 _, errNo := lockFileEx(syscall.Handle(f.fh.Fd()), flag, 0, 1, 0, &syscall.Overlapped{}); errNo > 0 { - return errNo - } - - *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. -// -// 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() - - // 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 _, errNo := unlockFileEx(syscall.Handle(f.fh.Fd()), 0, 1, 0, &syscall.Overlapped{}); errNo > 0 { - return errNo - } - - f.fh.Close() - - f.l = false - f.r = false - f.fh = nil - - 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. -// -// 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) -} - -// 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. -func (f *Flock) TryRLock() (bool, error) { - return f.try(&f.r, winLockfileSharedLock) -} - -func (f *Flock) try(locked *bool, flag uint32) (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() - } - - _, 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 { - return false, nil - } - - return false, errNo - } - - *locked = true - - return true, nil -} diff --git a/vendor/modules.txt b/vendor/modules.txt index e9faa3ece05883b776b0a52a5b14e690012ec1ae..81ba21f9780d6da9240b17df0d1521dfab52de15 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -73,6 +73,9 @@ github.com/go-webauthn/webauthn/webauthn # github.com/go-webauthn/x v0.1.9 ## explicit; go 1.21 github.com/go-webauthn/x/revoke +# github.com/gofrs/flock v0.12.1 +## explicit; go 1.21.0 +github.com/gofrs/flock # github.com/golang-jwt/jwt/v5 v5.2.1 ## explicit; go 1.18 github.com/golang-jwt/jwt/v5 @@ -145,9 +148,6 @@ github.com/prometheus/common/model github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util -# github.com/theckman/go-flock v0.8.1 -## explicit -github.com/theckman/go-flock # github.com/x448/float16 v0.8.4 ## explicit; go 1.11 github.com/x448/float16