github.com/mutagen-io/mutagen@v0.18.0-rc1/pkg/filesystem/locking/locker.go (about) 1 package locking 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 ) 8 9 // Locker provides file locking facilities. 10 type Locker struct { 11 // file is the underlying file object that's locked. 12 file *os.File 13 // held indicates whether or not the lock is currently held. 14 held bool 15 } 16 17 // NewLocker attempts to create a lock with the file at the specified path, 18 // creating the file if necessary. The lock is returned in an unlocked state. 19 func NewLocker(path string, permissions os.FileMode) (*Locker, error) { 20 mode := os.O_RDWR | os.O_CREATE | os.O_APPEND 21 if file, err := os.OpenFile(path, mode, permissions); err != nil { 22 return nil, fmt.Errorf("unable to open lock file: %w", err) 23 } else { 24 return &Locker{file: file}, nil 25 } 26 } 27 28 // Held returns whether or not the lock is currently held. 29 func (l *Locker) Held() bool { 30 return l.held 31 } 32 33 // Read implements io.Reader.Read on the underlying file, but errors if the lock 34 // is not currently held. 35 func (l *Locker) Read(buffer []byte) (int, error) { 36 // Verify that the lock is held. 37 if !l.held { 38 return 0, errors.New("lock not held") 39 } 40 41 // Perform the read. 42 return l.file.Read(buffer) 43 } 44 45 // Write implements io.Writer.Write on the underlying file, but errors if the 46 // lock is not currently held. 47 func (l *Locker) Write(buffer []byte) (int, error) { 48 // Verify that the lock is held. 49 if !l.held { 50 return 0, errors.New("lock not held") 51 } 52 53 // Perform the write. 54 return l.file.Write(buffer) 55 } 56 57 // Truncate implements file truncation for the underlying file, but errors if 58 // the lock is not currently held. 59 func (l *Locker) Truncate(size int64) error { 60 // Verify that the lock is held. 61 if !l.held { 62 return errors.New("lock not held") 63 } 64 65 // Perform the truncation. 66 return l.file.Truncate(size) 67 } 68 69 // Close closes the file underlying the locker. This will release any lock held 70 // on the file and disable future locking. On POSIX platforms, this also 71 // releases other locks held on the same file. 72 func (l *Locker) Close() error { 73 return l.file.Close() 74 }