File: flock.go

package info (click to toggle)
snapd 2.72-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 80,412 kB
  • sloc: sh: 16,506; ansic: 16,211; python: 11,213; makefile: 1,919; exp: 190; awk: 58; xml: 22
file content (112 lines) | stat: -rw-r--r-- 3,269 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
 * Copyright (C) 2017 Canonical Ltd
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package osutil

import (
	"errors"
	"os"
	"syscall"
)

// FileLock describes a file system lock
type FileLock struct {
	file *os.File
}

var ErrAlreadyLocked = errors.New("cannot acquire lock, already locked")

// OpenExistingLockForReading opens an existing lock file given by "path".
// The lock is opened in read-only mode.
func OpenExistingLockForReading(path string) (*FileLock, error) {
	flag := syscall.O_RDONLY | syscall.O_NOFOLLOW | syscall.O_CLOEXEC
	file, err := os.OpenFile(path, flag, 0)
	if err != nil {
		return nil, err
	}
	l := &FileLock{file: file}
	return l, nil
}

// NewFileLockWithMode creates and opens the lock file given by "path" with the given mode.
func NewFileLockWithMode(path string, mode os.FileMode) (*FileLock, error) {
	flag := syscall.O_RDWR | syscall.O_CREAT | syscall.O_NOFOLLOW | syscall.O_CLOEXEC
	file, err := os.OpenFile(path, flag, mode)
	if err != nil {
		return nil, err
	}
	l := &FileLock{file: file}
	return l, nil
}

// NewFileLockWithFile wraps an open file with a file lock which can be used for
// locking.
func NewFileLockWithFile(f *os.File) *FileLock {
	return &FileLock{file: f}
}

// NewFileLock creates and opens the lock file given by "path" with mode 0600.
func NewFileLock(path string) (*FileLock, error) {
	return NewFileLockWithMode(path, 0600)
}

// Path returns the path of the lock file.
func (l *FileLock) Path() string {
	return l.file.Name()
}

// File returns the underlying file.
func (l *FileLock) File() *os.File {
	return l.file
}

// Close closes the lock, unlocking it automatically if needed.
func (l *FileLock) Close() error {
	return l.file.Close()
}

// Lock acquires an exclusive lock and blocks until the lock is free.
//
// Only one process can acquire an exclusive lock at a given time, preventing
// shared or exclusive locks from being acquired.
func (l *FileLock) Lock() error {
	return syscall.Flock(int(l.file.Fd()), syscall.LOCK_EX)
}

// Lock acquires an shared lock and blocks until the lock is free.
//
// Multiple processes can acquire a shared lock at the same time, unless an
// exclusive lock is held.
func (l *FileLock) ReadLock() error {
	return syscall.Flock(int(l.file.Fd()), syscall.LOCK_SH)
}

// TryLock acquires an exclusive lock and errors if the lock cannot be acquired.
func (l *FileLock) TryLock() error {
	err := syscall.Flock(int(l.file.Fd()), syscall.LOCK_EX|syscall.LOCK_NB)
	if err == syscall.EWOULDBLOCK {
		return ErrAlreadyLocked
	}
	return err
}

// Unlock releases an acquired lock.
func (l *FileLock) Unlock() error {
	return syscall.Flock(int(l.file.Fd()), syscall.LOCK_UN)
}