diff --git a/.travis.yml b/.travis.yml index c545358..38cf1eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: language: go go: - - 1.11.x + - 1.13.x env: global: @@ -22,7 +22,6 @@ script: cache: directories: - - $GOPATH/src/gx - $GOPATH/pkg/mod - $HOME/.cache/go-build diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile deleted file mode 100644 index e0be167..0000000 --- a/ci/Jenkinsfile +++ /dev/null @@ -1 +0,0 @@ -golang() \ No newline at end of file diff --git a/example_test.go b/example_test.go new file mode 100644 index 0000000..da1db46 --- /dev/null +++ b/example_test.go @@ -0,0 +1,19 @@ +package fslock_test + +import ( + "errors" + "fmt" + + "github.com/ipfs/go-fs-lock" +) + +func ExampleLockedError() { + _, err := fslock.Lock("/tmp/", "foo.lock") + fmt.Println("locked:", errors.As(err, new(fslock.LockedError))) + + _, err = fslock.Lock("/tmp/", "foo.lock") + fmt.Println("locked:", errors.As(err, new(fslock.LockedError))) + // Output: + // locked: false + // locked: true +} diff --git a/fslock.go b/fslock.go index 9baa453..f9210b3 100644 --- a/fslock.go +++ b/fslock.go @@ -1,7 +1,6 @@ package fslock import ( - "fmt" "io" "os" "path/filepath" @@ -11,22 +10,59 @@ import ( util "github.com/ipfs/go-ipfs-util" logging "github.com/ipfs/go-log" lock "go4.org/lock" + "golang.org/x/xerrors" ) // log is the fsrepo logger var log = logging.Logger("lock") -func errPerm(path string) error { - return fmt.Errorf("failed to take lock at %s: permission denied", path) +// LockedError is returned as the inner error type when the lock is already +// taken. +type LockedError string + +func (e LockedError) Error() string { + return string(e) } // Lock creates the lock. -func Lock(confdir, lockFile string) (io.Closer, error) { - return lock.Lock(filepath.Join(confdir, lockFile)) +func Lock(confdir, lockFileName string) (io.Closer, error) { + lockFilePath := filepath.Join(confdir, lockFileName) + lk, err := lock.Lock(lockFilePath) + if err != nil { + switch { + case err == syscall.EAGAIN: + // EAGAIN == someone else has the lock + fallthrough + case strings.Contains(err.Error(), "resource temporarily unavailable"): + return lk, &os.PathError{ + Op: "lock", + Path: lockFilePath, + Err: LockedError("someone else has the lock"), + } + case strings.Contains(err.Error(), "already locked"): + // we hold the lock ourselves + return lk, &os.PathError{ + Op: "lock", + Path: lockFilePath, + Err: LockedError("lock is already held by us"), + } + case os.IsPermission(err) || isLockCreatePermFail(err): + // lock fails on permissions error + + // Using a path error like this ensures that + // os.IsPermission works on the returned error. + return lk, &os.PathError{ + Op: "lock", + Path: lockFilePath, + Err: os.ErrPermission, + } + } + } + return lk, err } // Locked checks if there is a lock already set. -func Locked(confdir, lockFile string) (bool, error) { +func IsLocked(confdir, lockFile string) (bool, error) { log.Debugf("Checking lock") if !util.FileExists(filepath.Join(confdir, lockFile)) { log.Debugf("File doesn't exist: %s", filepath.Join(confdir, lockFile)) @@ -34,40 +70,18 @@ func Locked(confdir, lockFile string) (bool, error) { } lk, err := Lock(confdir, lockFile) - if err != nil { - // EAGAIN == someone else has the lock - if err == syscall.EAGAIN { - log.Debugf("Someone else has the lock: %s", filepath.Join(confdir, lockFile)) - return true, nil - } - if strings.Contains(err.Error(), "resource temporarily unavailable") { - log.Debugf("Can't lock file: %s.\n reason: %s", filepath.Join(confdir, lockFile), err.Error()) - return true, nil - } + if err == nil { + log.Debugf("No one has a lock") + lk.Close() + return false, nil + } - // we hold the lock ourselves - if strings.Contains(err.Error(), "already locked") { - log.Debugf("Lock is already held by us: %s", filepath.Join(confdir, lockFile)) - return true, nil - } + log.Debug(err) - // lock fails on permissions error - if os.IsPermission(err) { - log.Debugf("Lock fails on permissions error") - return false, errPerm(confdir) - } - if isLockCreatePermFail(err) { - log.Debugf("Lock fails on permissions error") - return false, errPerm(confdir) - } - - // otherwise, we cant guarantee anything, error out - return false, err + if xerrors.As(err, new(LockedError)) { + return true, nil } - - log.Debugf("No one has a lock") - lk.Close() - return false, nil + return false, err } func isLockCreatePermFail(err error) bool { diff --git a/fslock_test.go b/fslock_test.go index 1d9acf1..2fa6b7c 100644 --- a/fslock_test.go +++ b/fslock_test.go @@ -11,7 +11,7 @@ import ( func assertLock(t *testing.T, confdir, lockFile string, expected bool) { t.Helper() - isLocked, err := lock.Locked(confdir, lockFile) + isLocked, err := lock.IsLocked(confdir, lockFile) if err != nil { t.Fatal(err) } diff --git a/go.mod b/go.mod index 8b3764e..e99e71d 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,7 @@ require ( github.com/ipfs/go-ipfs-util v0.0.1 github.com/ipfs/go-log v0.0.1 go4.org v0.0.0-20190218023631-ce4c26f7be8e + golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 ) + +go 1.12 diff --git a/go.sum b/go.sum index f08985a..3a7963a 100644 --- a/go.sum +++ b/go.sum @@ -43,3 +43,5 @@ golang.org/x/sys v0.0.0-20190219092855-153ac476189d/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898 h1:/atklqdjdhuosWIl6AIbOeHJjicWYPqR9bpxqxYG2pA= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/package.json b/package.json deleted file mode 100644 index 9000978..0000000 --- a/package.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "author": "dignifiedquire", - "bugs": { - "url": "https://github.com/ipfs/go-fs-lock/issues" - }, - "gx": { - "dvcsimport": "github.com/ipfs/go-fs-lock" - }, - "gxDependencies": [ - { - "author": "whyrusleeping", - "hash": "QmNohiVssaPw3KVLZik59DBVGTSm2dGvYT9eoXt5DQ36Yz", - "name": "go-ipfs-util", - "version": "1.2.9" - }, - { - "hash": "QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF", - "name": "go-log", - "version": "1.5.9" - }, - { - "author": "hsanjuan", - "hash": "QmYDxsntDcWC7CTisDmeorEvDVjkRAjjY9Vr9DJ6HSkXMS", - "name": "go4-lock", - "version": "0.0.3" - } - ], - "gxVersion": "0.12.1", - "language": "go", - "license": "MIT", - "name": "go-fs-lock", - "releaseCmd": "git commit -a -m \"gx publish $VERSION\"", - "version": "0.1.11" -} -