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.
This commit is contained in:
Martin Atkins 2020-10-20 15:04:19 -07:00
parent 960042ad42
commit 2332a7ab47
1 changed files with 6 additions and 0 deletions

View File

@ -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)