Skip to content

Commit

Permalink
Merge pull request #22 from virtuald/nfs-lock
Browse files Browse the repository at this point in the history
Fix exclusive lock to work on Linux+NFS

Signed-off-by: Tim Heckman <t@heckman.io>
  • Loading branch information
theckman committed Aug 12, 2018
2 parents 8afa76c + bc29234 commit dd99ba2
Showing 1 changed file with 50 additions and 1 deletion.
51 changes: 50 additions & 1 deletion flock_unix.go
Expand Up @@ -7,6 +7,7 @@
package flock

import (
"os"
"syscall"
)

Expand Down Expand Up @@ -53,7 +54,18 @@ func (f *Flock) lock(locked *bool, flag int) error {
}

if err := syscall.Flock(int(f.fh.Fd()), flag); err != nil {
return err
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
Expand Down Expand Up @@ -132,6 +144,8 @@ func (f *Flock) try(locked *bool, flag int) (bool, error) {
}
}

var retried bool
retry:
err := syscall.Flock(int(f.fh.Fd()), flag|syscall.LOCK_NB)

switch err {
Expand All @@ -141,6 +155,41 @@ func (f *Flock) try(locked *bool, flag int) (bool, error) {
*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
}

0 comments on commit dd99ba2

Please sign in to comment.