blob: 939fea6238181fe933667c844561aac98bc5ac2f [file] [log] [blame]
khenaidood948f772021-08-11 17:49:24 -04001// Copyright 2016 The etcd Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15// +build linux
16
17package fileutil
18
19import (
20 "io"
21 "os"
22 "syscall"
23)
24
25// This used to call syscall.Flock() but that call fails with EBADF on NFS.
26// An alternative is lockf() which works on NFS but that call lets a process lock
27// the same file twice. Instead, use Linux's non-standard open file descriptor
28// locks which will block if the process already holds the file lock.
29//
30// constants from /usr/include/bits/fcntl-linux.h
31const (
32 F_OFD_GETLK = 37
33 F_OFD_SETLK = 37
34 F_OFD_SETLKW = 38
35)
36
37var (
38 wrlck = syscall.Flock_t{
39 Type: syscall.F_WRLCK,
40 Whence: int16(io.SeekStart),
41 Start: 0,
42 Len: 0,
43 }
44
45 linuxTryLockFile = flockTryLockFile
46 linuxLockFile = flockLockFile
47)
48
49func init() {
50 // use open file descriptor locks if the system supports it
51 getlk := syscall.Flock_t{Type: syscall.F_RDLCK}
52 if err := syscall.FcntlFlock(0, F_OFD_GETLK, &getlk); err == nil {
53 linuxTryLockFile = ofdTryLockFile
54 linuxLockFile = ofdLockFile
55 }
56}
57
58func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
59 return linuxTryLockFile(path, flag, perm)
60}
61
62func ofdTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
63 f, err := os.OpenFile(path, flag, perm)
64 if err != nil {
65 return nil, err
66 }
67
68 flock := wrlck
69 if err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLK, &flock); err != nil {
70 f.Close()
71 if err == syscall.EWOULDBLOCK {
72 err = ErrLocked
73 }
74 return nil, err
75 }
76 return &LockedFile{f}, nil
77}
78
79func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
80 return linuxLockFile(path, flag, perm)
81}
82
83func ofdLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
84 f, err := os.OpenFile(path, flag, perm)
85 if err != nil {
86 return nil, err
87 }
88
89 flock := wrlck
90 err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLKW, &flock)
91
92 if err != nil {
93 f.Close()
94 return nil, err
95 }
96 return &LockedFile{f}, err
97}