| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 | 
							- // Copyright (C) 2015 The Syncthing Authors.
 
- //
 
- // This Source Code Form is subject to the terms of the Mozilla Public
 
- // License, v. 2.0. If a copy of the MPL was not distributed with this file,
 
- // You can obtain one at https://mozilla.org/MPL/2.0/.
 
- package osutil
 
- import (
 
- 	"errors"
 
- 	"path/filepath"
 
- 	"runtime"
 
- 	"github.com/syncthing/syncthing/lib/fs"
 
- )
 
- var (
 
- 	ErrClosed  = errors.New("write to closed writer")
 
- 	TempPrefix = ".syncthing.tmp."
 
- )
 
- // An AtomicWriter is an *os.File that writes to a temporary file in the same
 
- // directory as the final path. On successful Close the file is renamed to
 
- // its final path. Any error on Write or during Close is accumulated and
 
- // returned on Close, so a lazy user can ignore errors until Close.
 
- type AtomicWriter struct {
 
- 	path string
 
- 	next fs.File
 
- 	fs   fs.Filesystem
 
- 	err  error
 
- }
 
- // CreateAtomic is like os.Create, except a temporary file name is used
 
- // instead of the given name. The file is created with secure (0600)
 
- // permissions.
 
- func CreateAtomic(path string) (*AtomicWriter, error) {
 
- 	fs := fs.NewFilesystem(fs.FilesystemTypeBasic, filepath.Dir(path))
 
- 	return CreateAtomicFilesystem(fs, filepath.Base(path))
 
- }
 
- // CreateAtomicFilesystem is like os.Create, except a temporary file name is used
 
- // instead of the given name. The file is created with secure (0600)
 
- // permissions.
 
- func CreateAtomicFilesystem(filesystem fs.Filesystem, path string) (*AtomicWriter, error) {
 
- 	// The security of this depends on the tempfile having secure
 
- 	// permissions, 0600, from the beginning. This is what ioutil.TempFile
 
- 	// does. We have a test that verifies that that is the case, should this
 
- 	// ever change in the standard library in the future.
 
- 	fd, err := TempFile(filesystem, filepath.Dir(path), TempPrefix)
 
- 	if err != nil {
 
- 		return nil, err
 
- 	}
 
- 	w := &AtomicWriter{
 
- 		path: path,
 
- 		next: fd,
 
- 		fs:   filesystem,
 
- 	}
 
- 	return w, nil
 
- }
 
- // Write is like io.Writer, but is a no-op on an already failed AtomicWriter.
 
- func (w *AtomicWriter) Write(bs []byte) (int, error) {
 
- 	if w.err != nil {
 
- 		return 0, w.err
 
- 	}
 
- 	n, err := w.next.Write(bs)
 
- 	if err != nil {
 
- 		w.err = err
 
- 		w.next.Close()
 
- 	}
 
- 	return n, err
 
- }
 
- // Close closes the temporary file and renames it to the final path. It is
 
- // invalid to call Write() or Close() after Close().
 
- func (w *AtomicWriter) Close() error {
 
- 	if w.err != nil {
 
- 		return w.err
 
- 	}
 
- 	// Try to not leave temp file around, but ignore error.
 
- 	defer w.fs.Remove(w.next.Name())
 
- 	if err := w.next.Sync(); err != nil {
 
- 		w.err = err
 
- 		return err
 
- 	}
 
- 	if err := w.next.Close(); err != nil {
 
- 		w.err = err
 
- 		return err
 
- 	}
 
- 	err := w.fs.Rename(w.next.Name(), w.path)
 
- 	if runtime.GOOS == "windows" && fs.IsPermission(err) {
 
- 		// On Windows, we might not be allowed to rename over the file
 
- 		// because it's read-only. Get us some write permissions and try
 
- 		// again.
 
- 		_ = w.fs.Chmod(w.path, 0644)
 
- 		err = w.fs.Rename(w.next.Name(), w.path)
 
- 	}
 
- 	if err != nil {
 
- 		w.err = err
 
- 		return err
 
- 	}
 
- 	// fsync the directory too
 
- 	if fd, err := w.fs.Open(filepath.Dir(w.next.Name())); err == nil {
 
- 		fd.Sync()
 
- 		fd.Close()
 
- 	}
 
- 	// Set w.err to return appropriately for any future operations.
 
- 	w.err = ErrClosed
 
- 	return nil
 
- }
 
 
  |