summaryrefslogtreecommitdiff
path: root/libpod/lock/file/file_lock.go
diff options
context:
space:
mode:
authorGiuseppe Scrivano <gscrivan@redhat.com>2019-06-27 12:56:29 +0200
committerGiuseppe Scrivano <gscrivan@redhat.com>2019-07-02 16:41:10 +0200
commit827ac0859f9649a971c6d4092fcd26158afa5478 (patch)
treea580e3e12a57347dcf29676025cff2167414a0e0 /libpod/lock/file/file_lock.go
parent82164a2e9ed5c6112e3ef70895c153025807b282 (diff)
downloadpodman-827ac0859f9649a971c6d4092fcd26158afa5478.tar.gz
podman-827ac0859f9649a971c6d4092fcd26158afa5478.tar.bz2
podman-827ac0859f9649a971c6d4092fcd26158afa5478.zip
lock: new lock type "file"
it is a wrapper around containers/storage file locking. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
Diffstat (limited to 'libpod/lock/file/file_lock.go')
-rw-r--r--libpod/lock/file/file_lock.go175
1 files changed, 175 insertions, 0 deletions
diff --git a/libpod/lock/file/file_lock.go b/libpod/lock/file/file_lock.go
new file mode 100644
index 000000000..e50d67321
--- /dev/null
+++ b/libpod/lock/file/file_lock.go
@@ -0,0 +1,175 @@
+package file
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strconv"
+ "syscall"
+
+ "github.com/containers/storage"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+)
+
+// FileLocks is a struct enabling POSIX lock locking in a shared memory
+// segment.
+type FileLocks struct { // nolint
+ lockPath string
+ valid bool
+}
+
+// CreateFileLock sets up a directory containing the various lock files.
+func CreateFileLock(path string) (*FileLocks, error) {
+ _, err := os.Stat(path)
+ if err == nil {
+ return nil, errors.Wrapf(syscall.EEXIST, "directory %s exists", path)
+ }
+ if err := os.MkdirAll(path, 0711); err != nil {
+ return nil, errors.Wrapf(err, "cannot create %s", path)
+ }
+
+ locks := new(FileLocks)
+ locks.lockPath = path
+ locks.valid = true
+
+ return locks, nil
+}
+
+// OpenFileLock opens an existing directory with the lock files.
+func OpenFileLock(path string) (*FileLocks, error) {
+ _, err := os.Stat(path)
+ if err != nil {
+ return nil, errors.Wrapf(err, "accessing directory %s", path)
+ }
+
+ locks := new(FileLocks)
+ locks.lockPath = path
+ locks.valid = true
+
+ return locks, nil
+}
+
+// Close closes an existing shared-memory segment.
+// The segment will be rendered unusable after closing.
+// WARNING: If you Close() while there are still locks locked, these locks may
+// fail to release, causing a program freeze.
+// Close() is only intended to be used while testing the locks.
+func (locks *FileLocks) Close() error {
+ if !locks.valid {
+ return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
+ }
+ err := os.RemoveAll(locks.lockPath)
+ if err != nil {
+ return errors.Wrapf(err, "deleting directory %s", locks.lockPath)
+ }
+ return nil
+}
+
+func (locks *FileLocks) getLockPath(lck uint32) string {
+ return filepath.Join(locks.lockPath, strconv.FormatInt(int64(lck), 10))
+}
+
+// AllocateLock allocates a lock and returns the index of the lock that was allocated.
+func (locks *FileLocks) AllocateLock() (uint32, error) {
+ if !locks.valid {
+ return 0, errors.Wrapf(syscall.EINVAL, "locks have already been closed")
+ }
+
+ id := uint32(0)
+ for ; ; id++ {
+ path := locks.getLockPath(id)
+ f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+ if err != nil {
+ if os.IsExist(err) {
+ continue
+ }
+ return 0, errors.Wrapf(err, "creating lock file")
+ }
+ f.Close()
+ break
+ }
+ return id, nil
+}
+
+// AllocateGivenLock allocates the given lock from the shared-memory
+// segment for use by a container or pod.
+// If the lock is already in use or the index is invalid an error will be
+// returned.
+func (locks *FileLocks) AllocateGivenLock(lck uint32) error {
+ if !locks.valid {
+ return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
+ }
+
+ f, err := os.OpenFile(locks.getLockPath(lck), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
+ if err != nil {
+ return errors.Wrapf(err, "error creating lock %d", lck)
+ }
+ f.Close()
+
+ return nil
+}
+
+// DeallocateLock frees a lock in a shared-memory segment so it can be
+// reallocated to another container or pod.
+// The given lock must be already allocated, or an error will be returned.
+func (locks *FileLocks) DeallocateLock(lck uint32) error {
+ if !locks.valid {
+ return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
+ }
+ if err := os.Remove(locks.getLockPath(lck)); err != nil {
+ return errors.Wrapf(err, "deallocating lock %d", lck)
+ }
+ return nil
+}
+
+// DeallocateAllLocks frees all locks so they can be reallocated to
+// other containers and pods.
+func (locks *FileLocks) DeallocateAllLocks() error {
+ if !locks.valid {
+ return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
+ }
+ files, err := ioutil.ReadDir(locks.lockPath)
+ if err != nil {
+ return errors.Wrapf(err, "error reading directory %s", locks.lockPath)
+ }
+ var lastErr error
+ for _, f := range files {
+ p := filepath.Join(locks.lockPath, f.Name())
+ err := os.Remove(p)
+ if err != nil {
+ lastErr = err
+ logrus.Errorf("deallocating lock %s", p)
+ }
+ }
+ return lastErr
+}
+
+// LockFileLock locks the given lock.
+func (locks *FileLocks) LockFileLock(lck uint32) error {
+ if !locks.valid {
+ return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
+ }
+
+ l, err := storage.GetLockfile(locks.getLockPath(lck))
+ if err != nil {
+ return errors.Wrapf(err, "error acquiring lock")
+ }
+
+ l.Lock()
+ return nil
+}
+
+// UnlockFileLock unlocks the given lock.
+func (locks *FileLocks) UnlockFileLock(lck uint32) error {
+ if !locks.valid {
+ return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
+ }
+ l, err := storage.GetLockfile(locks.getLockPath(lck))
+ if err != nil {
+ return errors.Wrapf(err, "error acquiring lock")
+ }
+
+ l.Unlock()
+ return nil
+}