github.com/AbhinandanKurakure/podman/v3@v3.4.10/libpod/lock/file/file_lock.go (about)

     1  package file
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"path/filepath"
     7  	"strconv"
     8  	"syscall"
     9  
    10  	"github.com/containers/storage"
    11  	"github.com/pkg/errors"
    12  	"github.com/sirupsen/logrus"
    13  )
    14  
    15  // FileLocks is a struct enabling POSIX lock locking in a shared memory
    16  // segment.
    17  type FileLocks struct { // nolint
    18  	lockPath string
    19  	valid    bool
    20  }
    21  
    22  // CreateFileLock sets up a directory containing the various lock files.
    23  func CreateFileLock(path string) (*FileLocks, error) {
    24  	_, err := os.Stat(path)
    25  	if err == nil {
    26  		return nil, errors.Wrapf(syscall.EEXIST, "directory %s exists", path)
    27  	}
    28  	if err := os.MkdirAll(path, 0711); err != nil {
    29  		return nil, err
    30  	}
    31  
    32  	locks := new(FileLocks)
    33  	locks.lockPath = path
    34  	locks.valid = true
    35  
    36  	return locks, nil
    37  }
    38  
    39  // OpenFileLock opens an existing directory with the lock files.
    40  func OpenFileLock(path string) (*FileLocks, error) {
    41  	_, err := os.Stat(path)
    42  	if err != nil {
    43  		return nil, err
    44  	}
    45  
    46  	locks := new(FileLocks)
    47  	locks.lockPath = path
    48  	locks.valid = true
    49  
    50  	return locks, nil
    51  }
    52  
    53  // Close closes an existing shared-memory segment.
    54  // The segment will be rendered unusable after closing.
    55  // WARNING: If you Close() while there are still locks locked, these locks may
    56  // fail to release, causing a program freeze.
    57  // Close() is only intended to be used while testing the locks.
    58  func (locks *FileLocks) Close() error {
    59  	if !locks.valid {
    60  		return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
    61  	}
    62  	err := os.RemoveAll(locks.lockPath)
    63  	if err != nil {
    64  		return errors.Wrapf(err, "deleting directory %s", locks.lockPath)
    65  	}
    66  	return nil
    67  }
    68  
    69  func (locks *FileLocks) getLockPath(lck uint32) string {
    70  	return filepath.Join(locks.lockPath, strconv.FormatInt(int64(lck), 10))
    71  }
    72  
    73  // AllocateLock allocates a lock and returns the index of the lock that was allocated.
    74  func (locks *FileLocks) AllocateLock() (uint32, error) {
    75  	if !locks.valid {
    76  		return 0, errors.Wrapf(syscall.EINVAL, "locks have already been closed")
    77  	}
    78  
    79  	id := uint32(0)
    80  	for ; ; id++ {
    81  		path := locks.getLockPath(id)
    82  		f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
    83  		if err != nil {
    84  			if os.IsExist(err) {
    85  				continue
    86  			}
    87  			return 0, errors.Wrap(err, "creating lock file")
    88  		}
    89  		f.Close()
    90  		break
    91  	}
    92  	return id, nil
    93  }
    94  
    95  // AllocateGivenLock allocates the given lock from the shared-memory
    96  // segment for use by a container or pod.
    97  // If the lock is already in use or the index is invalid an error will be
    98  // returned.
    99  func (locks *FileLocks) AllocateGivenLock(lck uint32) error {
   100  	if !locks.valid {
   101  		return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
   102  	}
   103  
   104  	f, err := os.OpenFile(locks.getLockPath(lck), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
   105  	if err != nil {
   106  		return errors.Wrapf(err, "error creating lock %d", lck)
   107  	}
   108  	f.Close()
   109  
   110  	return nil
   111  }
   112  
   113  // DeallocateLock frees a lock in a shared-memory segment so it can be
   114  // reallocated to another container or pod.
   115  // The given lock must be already allocated, or an error will be returned.
   116  func (locks *FileLocks) DeallocateLock(lck uint32) error {
   117  	if !locks.valid {
   118  		return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
   119  	}
   120  	if err := os.Remove(locks.getLockPath(lck)); err != nil {
   121  		return errors.Wrapf(err, "deallocating lock %d", lck)
   122  	}
   123  	return nil
   124  }
   125  
   126  // DeallocateAllLocks frees all locks so they can be reallocated to
   127  // other containers and pods.
   128  func (locks *FileLocks) DeallocateAllLocks() error {
   129  	if !locks.valid {
   130  		return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
   131  	}
   132  	files, err := ioutil.ReadDir(locks.lockPath)
   133  	if err != nil {
   134  		return errors.Wrapf(err, "error reading directory %s", locks.lockPath)
   135  	}
   136  	var lastErr error
   137  	for _, f := range files {
   138  		p := filepath.Join(locks.lockPath, f.Name())
   139  		err := os.Remove(p)
   140  		if err != nil {
   141  			lastErr = err
   142  			logrus.Errorf("deallocating lock %s", p)
   143  		}
   144  	}
   145  	return lastErr
   146  }
   147  
   148  // LockFileLock locks the given lock.
   149  func (locks *FileLocks) LockFileLock(lck uint32) error {
   150  	if !locks.valid {
   151  		return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
   152  	}
   153  
   154  	l, err := storage.GetLockfile(locks.getLockPath(lck))
   155  	if err != nil {
   156  		return errors.Wrapf(err, "error acquiring lock")
   157  	}
   158  
   159  	l.Lock()
   160  	return nil
   161  }
   162  
   163  // UnlockFileLock unlocks the given lock.
   164  func (locks *FileLocks) UnlockFileLock(lck uint32) error {
   165  	if !locks.valid {
   166  		return errors.Wrapf(syscall.EINVAL, "locks have already been closed")
   167  	}
   168  	l, err := storage.GetLockfile(locks.getLockPath(lck))
   169  	if err != nil {
   170  		return errors.Wrapf(err, "error acquiring lock")
   171  	}
   172  
   173  	l.Unlock()
   174  	return nil
   175  }