From 2332a7ab4705de117aa125728bd9d4ce132d1203 Mon Sep 17 00:00:00 2001 From: Martin Atkins Date: Tue, 20 Oct 2020 15:04:19 -0700 Subject: [PATCH] replacefile: don't create the temporary file in TMPDIR ioutil.TempFile has a special case where an empty string for its dir argument is interpreted as a request to automatically look up the system temporary directory, which is commonly /tmp . We don't want that behavior here because we're specifically trying to create the temporary file in the same directory as the file we're hoping to replace. If the file gets created in /tmp then it might be on a different device and thus the later atomic rename won't work. Instead, we'll add our own special case to explicitly use "." when the given filename is in the current working directory. That overrides the special automatic behavior of ioutil.TempFile and thus forces the behavior we need. This hadn't previously mattered for earlier callers of this code because they were creating files in subdirectories, but this codepath was failing for the dependency lock file due to it always being created directly in the current working directory. Unfortunately since this is a picky implementation detail I couldn't find a good way to write a unit test for it without considerable refactoring. Instead, I verified manually that the temporary filename wasn't in /tmp on my Linux system, and hope that the comment inline will explain this situation well enough to avoid an accidental regression in future maintenence. --- internal/replacefile/writefile.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/replacefile/writefile.go b/internal/replacefile/writefile.go index 23adde0fa..d4dfb8ec1 100644 --- a/internal/replacefile/writefile.go +++ b/internal/replacefile/writefile.go @@ -27,6 +27,12 @@ import ( // in a leftover temporary file. func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { dir, file := filepath.Split(filename) + if dir == "" { + // If the file is in the current working directory then dir will + // end up being "", but that's not right here because TempFile + // treats an empty dir as meaning "use the TMPDIR environment variable". + dir = "." + } f, err := ioutil.TempFile(dir, file) // alongside target file and with a similar name if err != nil { return fmt.Errorf("cannot create temporary file to update %s: %s", filename, err)