...

Source file src/github.com/sassoftware/relic/lib/atomicfile/atomicfile.go

Documentation: github.com/sassoftware/relic/lib/atomicfile

     1  //
     2  // Copyright (c) SAS Institute Inc.
     3  //
     4  // Licensed under the Apache License, Version 2.0 (the "License");
     5  // you may not use this file except in compliance with the License.
     6  // You may obtain a copy of the License at
     7  //
     8  //     http://www.apache.org/licenses/LICENSE-2.0
     9  //
    10  // Unless required by applicable law or agreed to in writing, software
    11  // distributed under the License is distributed on an "AS IS" BASIS,
    12  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  //
    16  
    17  // Implement atomic write-rename file pattern. Instead of opening the named
    18  // file it creates a temporary file next to it, then on Commit() renames it. If
    19  // the file is Close()d  before Commit() then it is unlinked instead.
    20  package atomicfile
    21  
    22  import (
    23  	"errors"
    24  	"io"
    25  	"io/ioutil"
    26  	"os"
    27  	"path/filepath"
    28  	"runtime"
    29  )
    30  
    31  // File-like interface used by several functions in this package. Some of them
    32  // may open a file or stdio directly without atomic semantics, in which case
    33  // Commit() is an alias for Close()
    34  type AtomicFile interface {
    35  	io.Reader
    36  	io.ReaderAt
    37  	io.Writer
    38  	io.WriterAt
    39  	io.Seeker
    40  	Truncate(size int64) error
    41  
    42  	// Close and unlink the underlying file object, discarding the contents.
    43  	// No-op if Close() or Commit() was already called.
    44  	io.Closer
    45  	// Get the underlying *File object
    46  	GetFile() *os.File
    47  	// Complete the write-rename pattern and close the file
    48  	Commit() error
    49  }
    50  
    51  type atomicFile struct {
    52  	*os.File
    53  	name string
    54  }
    55  
    56  // Open a temporary file for reading and writing which will ultimately be
    57  // renamed to the given name when Commit() is called.
    58  func New(name string) (AtomicFile, error) {
    59  	tempfile, err := ioutil.TempFile(filepath.Dir(name), filepath.Base(name)+".tmp")
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	f := &atomicFile{tempfile, name}
    64  	runtime.SetFinalizer(f, (*atomicFile).Close)
    65  	return f, nil
    66  }
    67  
    68  func (f *atomicFile) GetFile() *os.File {
    69  	return f.File
    70  }
    71  
    72  func (f *atomicFile) Close() error {
    73  	if f.File == nil {
    74  		return nil
    75  	}
    76  	f.File.Close()
    77  	os.Remove(f.File.Name())
    78  	f.File = nil
    79  	runtime.SetFinalizer(f, nil)
    80  	return nil
    81  }
    82  
    83  func (f *atomicFile) Commit() error {
    84  	if f.File == nil {
    85  		return errors.New("file is closed")
    86  	}
    87  	f.File.Chmod(0644)
    88  	f.File.Close()
    89  	// rename can't overwrite on windows
    90  	if err := os.Remove(f.name); err != nil && !os.IsNotExist(err) {
    91  		return err
    92  	}
    93  	if err := os.Rename(f.File.Name(), f.name); err != nil {
    94  		return err
    95  	}
    96  	f.File = nil
    97  	runtime.SetFinalizer(f, nil)
    98  	return nil
    99  }
   100  

View as plain text