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