Files
podman/libpod/lock/file/file_lock.go
Kir Kolyshkin 4878dff3e2 Remove excessive error wrapping
In case os.Open[File], os.Mkdir[All], ioutil.ReadFile and the like
fails, the error message already contains the file name and the
operation that fails, so there is no need to wrap the error with
something like "open %s failed".

While at it

 - replace a few places with os.Open, ioutil.ReadAll with
   ioutil.ReadFile.

 - replace errors.Wrapf with errors.Wrap for cases where there
   are no %-style arguments.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2020-10-05 15:30:37 -07:00

176 lines
4.5 KiB
Go

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, err
}
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, err
}
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.Wrap(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
}