github.com/cockroachdb/tools@v0.0.0-20230222021103-a6d27438930d/internal/lockedfile/lockedfile_filelock.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 //go:build !plan9 6 // +build !plan9 7 8 package lockedfile 9 10 import ( 11 "io/fs" 12 "os" 13 14 "golang.org/x/tools/internal/lockedfile/internal/filelock" 15 ) 16 17 func openFile(name string, flag int, perm fs.FileMode) (*os.File, error) { 18 // On BSD systems, we could add the O_SHLOCK or O_EXLOCK flag to the OpenFile 19 // call instead of locking separately, but we have to support separate locking 20 // calls for Linux and Windows anyway, so it's simpler to use that approach 21 // consistently. 22 23 f, err := os.OpenFile(name, flag&^os.O_TRUNC, perm) 24 if err != nil { 25 return nil, err 26 } 27 28 switch flag & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) { 29 case os.O_WRONLY, os.O_RDWR: 30 err = filelock.Lock(f) 31 default: 32 err = filelock.RLock(f) 33 } 34 if err != nil { 35 f.Close() 36 return nil, err 37 } 38 39 if flag&os.O_TRUNC == os.O_TRUNC { 40 if err := f.Truncate(0); err != nil { 41 // The documentation for os.O_TRUNC says “if possible, truncate file when 42 // opened”, but doesn't define “possible” (golang.org/issue/28699). 43 // We'll treat regular files (and symlinks to regular files) as “possible” 44 // and ignore errors for the rest. 45 if fi, statErr := f.Stat(); statErr != nil || fi.Mode().IsRegular() { 46 filelock.Unlock(f) 47 f.Close() 48 return nil, err 49 } 50 } 51 } 52 53 return f, nil 54 } 55 56 func closeFile(f *os.File) error { 57 // Since locking syscalls operate on file descriptors, we must unlock the file 58 // while the descriptor is still valid — that is, before the file is closed — 59 // and avoid unlocking files that are already closed. 60 err := filelock.Unlock(f) 61 62 if closeErr := f.Close(); err == nil { 63 err = closeErr 64 } 65 return err 66 }