...

Source file src/pkg/cmd/go/internal/lockedfile/lockedfile.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	// 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