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 }