mirror of
https://github.com/containers/podman.git
synced 2025-07-15 03:02:52 +08:00

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>
176 lines
4.5 KiB
Go
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
|
|
}
|