Source file src/cmd/go/internal/lockedfile/lockedfile_filelock.go

     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  	"cmd/go/internal/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  }
    66  

View as plain text