mirror of
https://github.com/clearlinux/rkt.git
synced 2026-08-21 22:56:03 +00:00
9685f37575
Inspired by a similar change in the Kubernetes project [1], this changes the copyright header to the more generic "The rkt Authors" rather than "CoreOS, Inc.", which is only sometimes correct. https://github.com/GoogleCloudPlatform/kubernetes/commit/6b3a6e6b983f967c88d14d26542ec6e30c49ebd3
191 lines
5.2 KiB
Go
191 lines
5.2 KiB
Go
// Copyright 2014 The rkt Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// Package lock implements simple locking primitives on a
|
|
// regular file or directory using flock
|
|
package lock
|
|
|
|
import (
|
|
"errors"
|
|
"syscall"
|
|
)
|
|
|
|
var (
|
|
ErrLocked = errors.New("file already locked")
|
|
ErrNotExist = errors.New("file does not exist")
|
|
ErrPermission = errors.New("permission denied")
|
|
ErrNotRegular = errors.New("not a regular file")
|
|
)
|
|
|
|
// FileLock represents a lock on a regular file or a directory
|
|
type FileLock struct {
|
|
path string
|
|
fd int
|
|
}
|
|
|
|
type LockType int
|
|
|
|
const (
|
|
Dir LockType = iota
|
|
RegFile
|
|
)
|
|
|
|
// TryExclusiveLock takes an exclusive lock without blocking.
|
|
// This is idempotent when the Lock already represents an exclusive lock,
|
|
// and tries promote a shared lock to exclusive atomically.
|
|
// It will return ErrLocked if any lock is already held.
|
|
func (l *FileLock) TryExclusiveLock() error {
|
|
err := syscall.Flock(l.fd, syscall.LOCK_EX|syscall.LOCK_NB)
|
|
if err == syscall.EWOULDBLOCK {
|
|
err = ErrLocked
|
|
}
|
|
return err
|
|
}
|
|
|
|
// TryExclusiveLock takes an exclusive lock on a file/directory without blocking.
|
|
// It will return ErrLocked if any lock is already held on the file/directory.
|
|
func TryExclusiveLock(path string, lockType LockType) (*FileLock, error) {
|
|
l, err := NewLock(path, lockType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = l.TryExclusiveLock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return l, err
|
|
}
|
|
|
|
// ExclusiveLock takes an exclusive lock.
|
|
// This is idempotent when the Lock already represents an exclusive lock,
|
|
// and promotes a shared lock to exclusive atomically.
|
|
// It will block if an exclusive lock is already held.
|
|
func (l *FileLock) ExclusiveLock() error {
|
|
return syscall.Flock(l.fd, syscall.LOCK_EX)
|
|
}
|
|
|
|
// ExclusiveLock takes an exclusive lock on a file/directory.
|
|
// It will block if an exclusive lock is already held on the file/directory.
|
|
func ExclusiveLock(path string, lockType LockType) (*FileLock, error) {
|
|
l, err := NewLock(path, lockType)
|
|
if err == nil {
|
|
err = l.ExclusiveLock()
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return l, nil
|
|
}
|
|
|
|
// TrySharedLock takes a co-operative (shared) lock without blocking.
|
|
// This is idempotent when the Lock already represents a shared lock,
|
|
// and tries demote an exclusive lock to shared atomically.
|
|
// It will return ErrLocked if an exclusive lock already exists.
|
|
func (l *FileLock) TrySharedLock() error {
|
|
err := syscall.Flock(l.fd, syscall.LOCK_SH|syscall.LOCK_NB)
|
|
if err == syscall.EWOULDBLOCK {
|
|
err = ErrLocked
|
|
}
|
|
return err
|
|
}
|
|
|
|
// TrySharedLock takes a co-operative (shared) lock on a file/directory without blocking.
|
|
// It will return ErrLocked if an exclusive lock already exists on the file/directory.
|
|
func TrySharedLock(path string, lockType LockType) (*FileLock, error) {
|
|
l, err := NewLock(path, lockType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = l.TrySharedLock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return l, nil
|
|
}
|
|
|
|
// SharedLock takes a co-operative (shared) lock on.
|
|
// This is idempotent when the Lock already represents a shared lock,
|
|
// and demotes an exclusive lock to shared atomically.
|
|
// It will block if an exclusive lock is already held.
|
|
func (l *FileLock) SharedLock() error {
|
|
return syscall.Flock(l.fd, syscall.LOCK_SH)
|
|
}
|
|
|
|
// SharedLock takes a co-operative (shared) lock on a file/directory.
|
|
// It will block if an exclusive lock is already held on the file/directory.
|
|
func SharedLock(path string, lockType LockType) (*FileLock, error) {
|
|
l, err := NewLock(path, lockType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = l.SharedLock()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return l, nil
|
|
}
|
|
|
|
// Unlock unlocks the lock
|
|
func (l *FileLock) Unlock() error {
|
|
return syscall.Flock(l.fd, syscall.LOCK_UN)
|
|
}
|
|
|
|
// Fd returns the lock's file descriptor, or an error if the lock is closed
|
|
func (l *FileLock) Fd() (int, error) {
|
|
var err error
|
|
if l.fd == -1 {
|
|
err = errors.New("lock closed")
|
|
}
|
|
return l.fd, err
|
|
}
|
|
|
|
// Close closes the lock which implicitly unlocks it as well
|
|
func (l *FileLock) Close() error {
|
|
fd := l.fd
|
|
l.fd = -1
|
|
return syscall.Close(fd)
|
|
}
|
|
|
|
// NewLock opens a new lock on a file without acquisition
|
|
func NewLock(path string, lockType LockType) (*FileLock, error) {
|
|
l := &FileLock{path: path, fd: -1}
|
|
|
|
mode := syscall.O_RDONLY | syscall.O_CLOEXEC
|
|
if lockType == Dir {
|
|
mode |= syscall.O_DIRECTORY
|
|
}
|
|
lfd, err := syscall.Open(l.path, mode, 0)
|
|
if err != nil {
|
|
if err == syscall.ENOENT {
|
|
err = ErrNotExist
|
|
} else if err == syscall.EACCES {
|
|
err = ErrPermission
|
|
}
|
|
return nil, err
|
|
}
|
|
l.fd = lfd
|
|
|
|
var stat syscall.Stat_t
|
|
err = syscall.Fstat(lfd, &stat)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Check if the file is a regular file
|
|
if lockType == RegFile && !(stat.Mode&syscall.S_IFMT == syscall.S_IFREG) {
|
|
return nil, ErrNotRegular
|
|
}
|
|
|
|
return l, nil
|
|
}
|