Line data Source code
1 : // Copyright 2014 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 : // of this source code is governed by a BSD-style license that can be found in 3 : // the LICENSE file. 4 : 5 : //go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris 6 : // +build darwin dragonfly freebsd linux netbsd openbsd solaris 7 : 8 : package vfs 9 : 10 : import ( 11 : "io" 12 : "os" 13 : "sync" 14 : 15 : "github.com/cockroachdb/errors" 16 : "golang.org/x/sys/unix" 17 : ) 18 : 19 : var lockedFiles struct { 20 : mu struct { 21 : sync.Mutex 22 : files map[string]bool 23 : } 24 : } 25 : 26 : // lockCloser hides all of an os.File's methods, except for Close. 27 : type lockCloser struct { 28 : name string 29 : f *os.File 30 : } 31 : 32 0 : func (l lockCloser) Close() error { 33 0 : lockedFiles.mu.Lock() 34 0 : defer lockedFiles.mu.Unlock() 35 0 : if !lockedFiles.mu.files[l.name] { 36 0 : panic(errors.Errorf("lock file %q is not locked", l.name)) 37 : } 38 0 : delete(lockedFiles.mu.files, l.name) 39 0 : 40 0 : return l.f.Close() 41 : } 42 : 43 0 : func (defaultFS) Lock(name string) (io.Closer, error) { 44 0 : lockedFiles.mu.Lock() 45 0 : defer lockedFiles.mu.Unlock() 46 0 : if lockedFiles.mu.files == nil { 47 0 : lockedFiles.mu.files = map[string]bool{} 48 0 : } 49 0 : if lockedFiles.mu.files[name] { 50 0 : return nil, errors.New("lock held by current process") 51 0 : } 52 : 53 0 : f, err := os.Create(name) 54 0 : if err != nil { 55 0 : return nil, err 56 0 : } 57 0 : spec := unix.Flock_t{ 58 0 : Type: unix.F_WRLCK, 59 0 : Whence: io.SeekStart, 60 0 : Start: 0, 61 0 : Len: 0, // 0 means to lock the entire file. 62 0 : Pid: int32(os.Getpid()), 63 0 : } 64 0 : if err := unix.FcntlFlock(f.Fd(), unix.F_SETLK, &spec); err != nil { 65 0 : f.Close() 66 0 : return nil, err 67 0 : } 68 0 : lockedFiles.mu.files[name] = true 69 0 : return lockCloser{name, f}, nil 70 : }