blob: b0abc98eeb00090e1864631ec0246d1a97414f03 [file] [log] [blame]
khenaidoo59ce9dd2019-11-11 13:05:32 -05001// 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 "fmt"
21 "io"
22 "os"
23 "syscall"
24)
25
26// This used to call syscall.Flock() but that call fails with EBADF on NFS.
27// An alternative is lockf() which works on NFS but that call lets a process lock
28// the same file twice. Instead, use Linux's non-standard open file descriptor
29// locks which will block if the process already holds the file lock.
30//
31// constants from /usr/include/bits/fcntl-linux.h
32const (
33 F_OFD_GETLK = 37
34 F_OFD_SETLK = 37
35 F_OFD_SETLKW = 38
36)
37
38var (
39 wrlck = syscall.Flock_t{
40 Type: syscall.F_WRLCK,
41 Whence: int16(io.SeekStart),
42 Start: 0,
43 Len: 0,
44 }
45
46 linuxTryLockFile = flockTryLockFile
47 linuxLockFile = flockLockFile
48)
49
50func init() {
51 // use open file descriptor locks if the system supports it
52 getlk := syscall.Flock_t{Type: syscall.F_RDLCK}
53 if err := syscall.FcntlFlock(0, F_OFD_GETLK, &getlk); err == nil {
54 linuxTryLockFile = ofdTryLockFile
55 linuxLockFile = ofdLockFile
56 }
57}
58
59func TryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
60 return linuxTryLockFile(path, flag, perm)
61}
62
63func ofdTryLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
64 f, err := os.OpenFile(path, flag, perm)
65 if err != nil {
66 return nil, fmt.Errorf("ofdTryLockFile failed to open %q (%v)", path, err)
67 }
68
69 flock := wrlck
70 if err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLK, &flock); err != nil {
71 f.Close()
72 if err == syscall.EWOULDBLOCK {
73 err = ErrLocked
74 }
75 return nil, err
76 }
77 return &LockedFile{f}, nil
78}
79
80func LockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
81 return linuxLockFile(path, flag, perm)
82}
83
84func ofdLockFile(path string, flag int, perm os.FileMode) (*LockedFile, error) {
85 f, err := os.OpenFile(path, flag, perm)
86 if err != nil {
87 return nil, fmt.Errorf("ofdLockFile failed to open %q (%v)", path, err)
88 }
89
90 flock := wrlck
91 err = syscall.FcntlFlock(f.Fd(), F_OFD_SETLKW, &flock)
92 if err != nil {
93 f.Close()
94 return nil, err
95 }
96 return &LockedFile{f}, nil
97}