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

Documentation: cmd/go/internal/lockedfile

     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  // Package lockedfile creates and manipulates files whose contents should only
     6  // change atomically.
     7  package lockedfile
     8  
     9  import (
    10  	"fmt"
    11  	"io"
    12  	"io/ioutil"
    13  	"os"
    14  	"runtime"
    15  )
    16  
    17  // A File is a locked *os.File.
    18  //
    19  // Closing the file releases the lock.
    20  //
    21  // If the program exits while a file is locked, the operating system releases
    22  // the lock but may not do so promptly: callers must ensure that all locked
    23  // files are closed before exiting.
    24  type File struct {
    25  	osFile
    26  	closed bool
    27  }
    28  
    29  // osFile embeds a *os.File while keeping the pointer itself unexported.
    30  // (When we close a File, it must be the same file descriptor that we opened!)
    31  type osFile struct {
    32  	*os.File
    33  }
    34  
    35  // OpenFile is like os.OpenFile, but returns a locked file.
    36  // If flag includes os.O_WRONLY or os.O_RDWR, the file is write-locked;
    37  // otherwise, it is read-locked.
    38  func OpenFile(name string, flag int, perm os.FileMode) (*File, error) {
    39  	var (
    40  		f   = new(File)
    41  		err error
    42  	)
    43  	f.osFile.File, err = openFile(name, flag, perm)
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  
    48  	// Although the operating system will drop locks for open files when the go
    49  	// command exits, we want to hold locks for as little time as possible, and we
    50  	// especially don't want to leave a file locked after we're done with it. Our
    51  	// Close method is what releases the locks, so use a finalizer to report
    52  	// missing Close calls on a best-effort basis.
    53  	runtime.SetFinalizer(f, func(f *File) {
    54  		panic(fmt.Sprintf("lockedfile.File %s became unreachable without a call to Close", f.Name()))
    55  	})
    56  
    57  	return f, nil
    58  }
    59  
    60  // Open is like os.Open, but returns a read-locked file.
    61  func Open(name string) (*File, error) {
    62  	return OpenFile(name, os.O_RDONLY, 0)
    63  }
    64  
    65  // Create is like os.Create, but returns a write-locked file.
    66  func Create(name string) (*File, error) {
    67  	return OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
    68  }
    69  
    70  // Edit creates the named file with mode 0666 (before umask),
    71  // but does not truncate existing contents.
    72  //
    73  // If Edit succeeds, methods on the returned File can be used for I/O.
    74  // The associated file descriptor has mode O_RDWR and the file is write-locked.
    75  func Edit(name string) (*File, error) {
    76  	return OpenFile(name, os.O_RDWR|os.O_CREATE, 0666)
    77  }
    78  
    79  // Close unlocks and closes the underlying file.
    80  //
    81  // Close may be called multiple times; all calls after the first will return a
    82  // non-nil error.
    83  func (f *File) Close() error {
    84  	if f.closed {
    85  		return &os.PathError{
    86  			Op:   "close",
    87  			Path: f.Name(),
    88  			Err:  os.ErrClosed,
    89  		}
    90  	}
    91  	f.closed = true
    92  
    93  	err := closeFile(f.osFile.File)
    94  	runtime.SetFinalizer(f, nil)
    95  	return err
    96  }
    97  
    98  // Read opens the named file with a read-lock and returns its contents.
    99  func Read(name string) ([]byte, error) {
   100  	f, err := Open(name)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  	defer f.Close()
   105  
   106  	return ioutil.ReadAll(f)
   107  }
   108  
   109  // Write opens the named file (creating it with the given permissions if needed),
   110  // then write-locks it and overwrites it with the given content.
   111  func Write(name string, content io.Reader, perm os.FileMode) (err error) {
   112  	f, err := OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	_, err = io.Copy(f, content)
   118  	if closeErr := f.Close(); err == nil {
   119  		err = closeErr
   120  	}
   121  	return err
   122  }
   123  

View as plain text