summaryrefslogtreecommitdiff
path: root/vendor/github.com/containers/storage/lockfile.go
blob: 7f07b9ac50fa76127e5567cbb7da2754437f2a5f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package storage

import (
	"path/filepath"
	"sync"
	"time"

	"github.com/pkg/errors"
)

// A Locker represents a file lock where the file is used to cache an
// identifier of the last party that made changes to whatever's being protected
// by the lock.
type Locker interface {
	// Acquire a writer lock.
	Lock()

	// Unlock the lock.
	Unlock()

	// Acquire a reader lock.
	RLock()

	// Touch records, for others sharing the lock, that the caller was the
	// last writer.  It should only be called with the lock held.
	Touch() error

	// Modified() checks if the most recent writer was a party other than the
	// last recorded writer.  It should only be called with the lock held.
	Modified() (bool, error)

	// TouchedSince() checks if the most recent writer modified the file (likely using Touch()) after the specified time.
	TouchedSince(when time.Time) bool

	// IsReadWrite() checks if the lock file is read-write
	IsReadWrite() bool

	// Locked() checks if lock is locked
	Locked() bool
}

var (
	lockfiles     map[string]Locker
	lockfilesLock sync.Mutex
)

// GetLockfile opens a read-write lock file, creating it if necessary.  The
// Locker object may already be locked if the path has already been requested
// by the current process.
func GetLockfile(path string) (Locker, error) {
	return getLockfile(path, false)
}

// GetROLockfile opens a read-only lock file, creating it if necessary.  The
// Locker object may already be locked if the path has already been requested
// by the current process.
func GetROLockfile(path string) (Locker, error) {
	return getLockfile(path, true)
}

// getLockfile is a helper for GetLockfile and GetROLockfile and returns Locker
// based on the path and read-only property.
func getLockfile(path string, ro bool) (Locker, error) {
	lockfilesLock.Lock()
	defer lockfilesLock.Unlock()
	if lockfiles == nil {
		lockfiles = make(map[string]Locker)
	}
	cleanPath := filepath.Clean(path)
	if locker, ok := lockfiles[cleanPath]; ok {
		if ro && locker.IsReadWrite() {
			return nil, errors.Errorf("lock %q is not a read-only lock", cleanPath)
		}
		if !ro && !locker.IsReadWrite() {
			return nil, errors.Errorf("lock %q is not a read-write lock", cleanPath)
		}
		return locker, nil
	}
	locker, err := getLockFile(path, ro) // platform dependent locker
	if err != nil {
		return nil, err
	}
	lockfiles[filepath.Clean(path)] = locker
	return locker, nil
}