...

Source file src/go.etcd.io/etcd/client/pkg/v3/fileutil/lock_linux.go

Documentation: go.etcd.io/etcd/client/pkg/v3/fileutil

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

View as plain text