github.com/kobeld/docker@v1.12.0-rc1/pkg/ioutils/fswriters.go (about)

     1  package ioutils
     2  
     3  import (
     4  	"io"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  )
     9  
    10  // NewAtomicFileWriter returns WriteCloser so that writing to it writes to a
    11  // temporary file and closing it atomically changes the temporary file to
    12  // destination path. Writing and closing concurrently is not allowed.
    13  func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) {
    14  	f, err := ioutil.TempFile(filepath.Dir(filename), ".tmp-"+filepath.Base(filename))
    15  	if err != nil {
    16  		return nil, err
    17  	}
    18  	abspath, err := filepath.Abs(filename)
    19  	if err != nil {
    20  		return nil, err
    21  	}
    22  	return &atomicFileWriter{
    23  		f:  f,
    24  		fn: abspath,
    25  	}, nil
    26  }
    27  
    28  // AtomicWriteFile atomically writes data to a file named by filename.
    29  func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error {
    30  	f, err := NewAtomicFileWriter(filename, perm)
    31  	if err != nil {
    32  		return err
    33  	}
    34  	n, err := f.Write(data)
    35  	if err == nil && n < len(data) {
    36  		err = io.ErrShortWrite
    37  	}
    38  	if err1 := f.Close(); err == nil {
    39  		err = err1
    40  	}
    41  	return err
    42  }
    43  
    44  type atomicFileWriter struct {
    45  	f        *os.File
    46  	fn       string
    47  	writeErr error
    48  }
    49  
    50  func (w *atomicFileWriter) Write(dt []byte) (int, error) {
    51  	n, err := w.f.Write(dt)
    52  	if err != nil {
    53  		w.writeErr = err
    54  	}
    55  	return n, err
    56  }
    57  
    58  func (w *atomicFileWriter) Close() (retErr error) {
    59  	defer func() {
    60  		if retErr != nil {
    61  			os.Remove(w.f.Name())
    62  		}
    63  	}()
    64  	if err := w.f.Sync(); err != nil {
    65  		w.f.Close()
    66  		return err
    67  	}
    68  	if err := w.f.Close(); err != nil {
    69  		return err
    70  	}
    71  	if w.writeErr == nil {
    72  		return os.Rename(w.f.Name(), w.fn)
    73  	}
    74  	return nil
    75  }