mirror of
https://github.com/tailscale/tailscale.git
synced 2025-02-20 11:58:39 +00:00

I moved the actual rename into separate, GOOS-specific files. On non-Windows, we do a simple os.Rename. On Windows, we first try ReplaceFile with a fallback to os.Rename if the target file does not exist. ReplaceFile is the recommended way to rename the file in this use case, as it preserves attributes and ACLs set on the target file. Updates #14428 Signed-off-by: Aaron Klotz <aaron@tailscale.com>
53 lines
1.4 KiB
Go
53 lines
1.4 KiB
Go
// Copyright (c) Tailscale Inc & AUTHORS
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
// Package atomicfile contains code related to writing to filesystems
|
|
// atomically.
|
|
//
|
|
// This package should be considered internal; its API is not stable.
|
|
package atomicfile // import "tailscale.com/atomicfile"
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
)
|
|
|
|
// WriteFile writes data to filename+some suffix, then renames it into filename.
|
|
// The perm argument is ignored on Windows, but if the target filename already
|
|
// exists then the target file's attributes and ACLs are preserved. If the target
|
|
// filename already exists but is not a regular file, WriteFile returns an error.
|
|
func WriteFile(filename string, data []byte, perm os.FileMode) (err error) {
|
|
fi, err := os.Stat(filename)
|
|
if err == nil && !fi.Mode().IsRegular() {
|
|
return fmt.Errorf("%s already exists and is not a regular file", filename)
|
|
}
|
|
f, err := os.CreateTemp(filepath.Dir(filename), filepath.Base(filename)+".tmp")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
tmpName := f.Name()
|
|
defer func() {
|
|
if err != nil {
|
|
f.Close()
|
|
os.Remove(tmpName)
|
|
}
|
|
}()
|
|
if _, err := f.Write(data); err != nil {
|
|
return err
|
|
}
|
|
if runtime.GOOS != "windows" {
|
|
if err := f.Chmod(perm); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if err := f.Sync(); err != nil {
|
|
return err
|
|
}
|
|
if err := f.Close(); err != nil {
|
|
return err
|
|
}
|
|
return rename(tmpName, filename)
|
|
}
|