mirror of
				https://github.com/containers/podman.git
				synced 2025-10-31 18:08:51 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			180 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			180 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package file
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"io/fs"
 | |
| 	"os"
 | |
| 	"path/filepath"
 | |
| 	"strconv"
 | |
| 	"syscall"
 | |
| 
 | |
| 	"github.com/containers/storage/pkg/fileutils"
 | |
| 	"github.com/containers/storage/pkg/lockfile"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| )
 | |
| 
 | |
| // FileLocks is a struct enabling POSIX lock locking in a shared memory
 | |
| // segment.
 | |
| type FileLocks struct { //nolint:revive // struct name stutters
 | |
| 	lockPath string
 | |
| 	valid    bool
 | |
| }
 | |
| 
 | |
| // CreateFileLock sets up a directory containing the various lock files.
 | |
| func CreateFileLock(path string) (*FileLocks, error) {
 | |
| 	err := fileutils.Exists(path)
 | |
| 	if err == nil {
 | |
| 		return nil, fmt.Errorf("directory %s exists: %w", path, syscall.EEXIST)
 | |
| 	}
 | |
| 	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 := fileutils.Exists(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 fmt.Errorf("locks have already been closed: %w", syscall.EINVAL)
 | |
| 	}
 | |
| 	err := os.RemoveAll(locks.lockPath)
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("deleting directory %s: %w", locks.lockPath, err)
 | |
| 	}
 | |
| 	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, fmt.Errorf("locks have already been closed: %w", syscall.EINVAL)
 | |
| 	}
 | |
| 
 | |
| 	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, fmt.Errorf("creating lock file: %w", err)
 | |
| 		}
 | |
| 		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 fmt.Errorf("locks have already been closed: %w", syscall.EINVAL)
 | |
| 	}
 | |
| 
 | |
| 	f, err := os.OpenFile(locks.getLockPath(lck), os.O_RDWR|os.O_CREATE|os.O_EXCL, 0666)
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("creating lock %d: %w", lck, err)
 | |
| 	}
 | |
| 	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 fmt.Errorf("locks have already been closed: %w", syscall.EINVAL)
 | |
| 	}
 | |
| 	if err := os.Remove(locks.getLockPath(lck)); err != nil {
 | |
| 		return fmt.Errorf("deallocating lock %d: %w", lck, err)
 | |
| 	}
 | |
| 	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 fmt.Errorf("locks have already been closed: %w", syscall.EINVAL)
 | |
| 	}
 | |
| 	files, err := os.ReadDir(locks.lockPath)
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("reading directory %s: %w", locks.lockPath, err)
 | |
| 	}
 | |
| 	var lastErr error
 | |
| 	for _, f := range files {
 | |
| 		p := filepath.Join(locks.lockPath, f.Name())
 | |
| 		err := os.Remove(p)
 | |
| 		if err != nil {
 | |
| 			if errors.Is(err, fs.ErrNotExist) {
 | |
| 				continue
 | |
| 			}
 | |
| 			logrus.Errorf("Deallocating lock %s", p)
 | |
| 		}
 | |
| 	}
 | |
| 	return lastErr
 | |
| }
 | |
| 
 | |
| // LockFileLock locks the given lock.
 | |
| func (locks *FileLocks) LockFileLock(lck uint32) error {
 | |
| 	if !locks.valid {
 | |
| 		return fmt.Errorf("locks have already been closed: %w", syscall.EINVAL)
 | |
| 	}
 | |
| 
 | |
| 	l, err := lockfile.GetLockFile(locks.getLockPath(lck))
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("acquiring lock: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	l.Lock()
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| // UnlockFileLock unlocks the given lock.
 | |
| func (locks *FileLocks) UnlockFileLock(lck uint32) error {
 | |
| 	if !locks.valid {
 | |
| 		return fmt.Errorf("locks have already been closed: %w", syscall.EINVAL)
 | |
| 	}
 | |
| 	l, err := lockfile.GetLockFile(locks.getLockPath(lck))
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("acquiring lock: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	l.Unlock()
 | |
| 	return nil
 | |
| }
 | 
