diff --git a/pkg/system/chtimes.go b/pkg/system/chtimes.go index c26a4e24b6..6a6bca43ed 100644 --- a/pkg/system/chtimes.go +++ b/pkg/system/chtimes.go @@ -2,24 +2,41 @@ package system // import "github.com/docker/docker/pkg/system" import ( "os" + "syscall" "time" + "unsafe" ) -// Chtimes changes the access time and modified time of a file at the given path +// Used by Chtimes +var unixEpochTime, unixMaxTime time.Time + +func init() { + unixEpochTime = time.Unix(0, 0) + if unsafe.Sizeof(syscall.Timespec{}.Nsec) == 8 { + // This is a 64 bit timespec + // os.Chtimes limits time to the following + // + // Note that this intentionally sets nsec (not sec), which sets both sec + // and nsec internally in time.Unix(); + // https://github.com/golang/go/blob/go1.19.2/src/time/time.go#L1364-L1380 + unixMaxTime = time.Unix(0, 1<<63-1) + } else { + // This is a 32 bit timespec + unixMaxTime = time.Unix(1<<31-1, 0) + } +} + +// Chtimes changes the access time and modified time of a file at the given path. +// If the modified time is prior to the Unix Epoch (unixMinTime), or after the +// end of Unix Time (unixEpochTime), os.Chtimes has undefined behavior. In this +// case, Chtimes defaults to Unix Epoch, just in case. func Chtimes(name string, atime time.Time, mtime time.Time) error { - unixMinTime := time.Unix(0, 0) - unixMaxTime := maxTime - - // If the modified time is prior to the Unix Epoch, or after the - // end of Unix Time, os.Chtimes has undefined behavior - // default to Unix Epoch in this case, just in case - - if atime.Before(unixMinTime) || atime.After(unixMaxTime) { - atime = unixMinTime + if atime.Before(unixEpochTime) || atime.After(unixMaxTime) { + atime = unixEpochTime } - if mtime.Before(unixMinTime) || mtime.After(unixMaxTime) { - mtime = unixMinTime + if mtime.Before(unixEpochTime) || mtime.After(unixMaxTime) { + mtime = unixEpochTime } if err := os.Chtimes(name, atime, mtime); err != nil { diff --git a/pkg/system/chtimes_linux_test.go b/pkg/system/chtimes_linux_test.go index 97f860443c..662aaf147d 100644 --- a/pkg/system/chtimes_linux_test.go +++ b/pkg/system/chtimes_linux_test.go @@ -2,88 +2,109 @@ package system // import "github.com/docker/docker/pkg/system" import ( "os" + "path/filepath" "syscall" "testing" "time" ) -// TestChtimesLinux tests Chtimes access time on a tempfile on Linux -func TestChtimesLinux(t *testing.T) { - file, dir := prepareTempFile(t) - defer os.RemoveAll(dir) +// TestChtimesATime tests Chtimes access time on a tempfile. +func TestChtimesATime(t *testing.T) { + file := filepath.Join(t.TempDir(), "exist") + if err := os.WriteFile(file, []byte("hello"), 0o644); err != nil { + t.Fatal(err) + } - beforeUnixEpochTime := time.Unix(0, 0).Add(-100 * time.Second) - unixEpochTime := time.Unix(0, 0) - afterUnixEpochTime := time.Unix(100, 0) - unixMaxTime := maxTime + beforeUnixEpochTime := unixEpochTime.Add(-100 * time.Second) + afterUnixEpochTime := unixEpochTime.Add(100 * time.Second) // Test both aTime and mTime set to Unix Epoch - Chtimes(file, unixEpochTime, unixEpochTime) + t.Run("both aTime and mTime set to Unix Epoch", func(t *testing.T) { + if err := Chtimes(file, unixEpochTime, unixEpochTime); err != nil { + t.Error(err) + } - f, err := os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - stat := f.Sys().(*syscall.Stat_t) - aTime := time.Unix(stat.Atim.Unix()) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } + stat := f.Sys().(*syscall.Stat_t) + aTime := time.Unix(stat.Atim.Unix()) + if aTime != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) + } + }) // Test aTime before Unix Epoch and mTime set to Unix Epoch - Chtimes(file, beforeUnixEpochTime, unixEpochTime) + t.Run("aTime before Unix Epoch and mTime set to Unix Epoch", func(t *testing.T) { + if err := Chtimes(file, beforeUnixEpochTime, unixEpochTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(stat.Atim.Unix()) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } + stat := f.Sys().(*syscall.Stat_t) + aTime := time.Unix(stat.Atim.Unix()) + if aTime != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) + } + }) // Test aTime set to Unix Epoch and mTime before Unix Epoch - Chtimes(file, unixEpochTime, beforeUnixEpochTime) + t.Run("aTime set to Unix Epoch and mTime before Unix Epoch", func(t *testing.T) { + if err := Chtimes(file, unixEpochTime, beforeUnixEpochTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(stat.Atim.Unix()) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } + stat := f.Sys().(*syscall.Stat_t) + aTime := time.Unix(stat.Atim.Unix()) + if aTime != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) + } + }) // Test both aTime and mTime set to after Unix Epoch (valid time) - Chtimes(file, afterUnixEpochTime, afterUnixEpochTime) + t.Run("both aTime and mTime set to after Unix Epoch (valid time)", func(t *testing.T) { + if err := Chtimes(file, afterUnixEpochTime, afterUnixEpochTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(stat.Atim.Unix()) - if aTime != afterUnixEpochTime { - t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, aTime) - } + stat := f.Sys().(*syscall.Stat_t) + aTime := time.Unix(stat.Atim.Unix()) + if aTime != afterUnixEpochTime { + t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, aTime) + } + }) // Test both aTime and mTime set to Unix max time - Chtimes(file, unixMaxTime, unixMaxTime) + t.Run("both aTime and mTime set to Unix max time", func(t *testing.T) { + if err := Chtimes(file, unixMaxTime, unixMaxTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - stat = f.Sys().(*syscall.Stat_t) - aTime = time.Unix(stat.Atim.Unix()) - if aTime.Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { - t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), aTime.Truncate(time.Second)) - } + stat := f.Sys().(*syscall.Stat_t) + aTime := time.Unix(stat.Atim.Unix()) + if aTime.Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { + t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), aTime.Truncate(time.Second)) + } + }) } diff --git a/pkg/system/chtimes_test.go b/pkg/system/chtimes_test.go index 3bb1fb2a60..ad95df2f09 100644 --- a/pkg/system/chtimes_test.go +++ b/pkg/system/chtimes_test.go @@ -7,87 +7,94 @@ import ( "time" ) -// prepareTempFile creates a temporary file in a temporary directory. -func prepareTempFile(t *testing.T) (string, string) { - dir, err := os.MkdirTemp("", "docker-system-test") - if err != nil { +// TestChtimesModTime tests Chtimes on a tempfile. Test only mTime, because +// aTime is OS dependent. +func TestChtimesModTime(t *testing.T) { + file := filepath.Join(t.TempDir(), "exist") + if err := os.WriteFile(file, []byte("hello"), 0o644); err != nil { t.Fatal(err) } - file := filepath.Join(dir, "exist") - if err := os.WriteFile(file, []byte("hello"), 0644); err != nil { - t.Fatal(err) - } - return file, dir -} - -// TestChtimes tests Chtimes on a tempfile. Test only mTime, because aTime is OS dependent -func TestChtimes(t *testing.T) { - file, dir := prepareTempFile(t) - defer os.RemoveAll(dir) - - beforeUnixEpochTime := time.Unix(0, 0).Add(-100 * time.Second) - unixEpochTime := time.Unix(0, 0) - afterUnixEpochTime := time.Unix(100, 0) - unixMaxTime := maxTime + beforeUnixEpochTime := unixEpochTime.Add(-100 * time.Second) + afterUnixEpochTime := unixEpochTime.Add(100 * time.Second) // Test both aTime and mTime set to Unix Epoch - Chtimes(file, unixEpochTime, unixEpochTime) + t.Run("both aTime and mTime set to Unix Epoch", func(t *testing.T) { + if err := Chtimes(file, unixEpochTime, unixEpochTime); err != nil { + t.Error(err) + } - f, err := os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - if f.ModTime() != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, f.ModTime()) - } + if f.ModTime() != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, f.ModTime()) + } + }) // Test aTime before Unix Epoch and mTime set to Unix Epoch - Chtimes(file, beforeUnixEpochTime, unixEpochTime) + t.Run("aTime before Unix Epoch and mTime set to Unix Epoch", func(t *testing.T) { + if err := Chtimes(file, beforeUnixEpochTime, unixEpochTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - if f.ModTime() != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, f.ModTime()) - } + if f.ModTime() != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, f.ModTime()) + } + }) // Test aTime set to Unix Epoch and mTime before Unix Epoch - Chtimes(file, unixEpochTime, beforeUnixEpochTime) + t.Run("aTime set to Unix Epoch and mTime before Unix Epoch", func(t *testing.T) { + if err := Chtimes(file, unixEpochTime, beforeUnixEpochTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - if f.ModTime() != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, f.ModTime()) - } + if f.ModTime() != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, f.ModTime()) + } + }) // Test both aTime and mTime set to after Unix Epoch (valid time) - Chtimes(file, afterUnixEpochTime, afterUnixEpochTime) + t.Run("both aTime and mTime set to after Unix Epoch (valid time)", func(t *testing.T) { + if err := Chtimes(file, afterUnixEpochTime, afterUnixEpochTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - if f.ModTime() != afterUnixEpochTime { - t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, f.ModTime()) - } + if f.ModTime() != afterUnixEpochTime { + t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, f.ModTime()) + } + }) // Test both aTime and mTime set to Unix max time - Chtimes(file, unixMaxTime, unixMaxTime) + t.Run("both aTime and mTime set to Unix max time", func(t *testing.T) { + if err := Chtimes(file, unixMaxTime, unixMaxTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - if f.ModTime().Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { - t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), f.ModTime().Truncate(time.Second)) - } + if f.ModTime().Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { + t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), f.ModTime().Truncate(time.Second)) + } + }) } diff --git a/pkg/system/chtimes_windows.go b/pkg/system/chtimes_windows.go index 6664b8bcad..ab478f5c38 100644 --- a/pkg/system/chtimes_windows.go +++ b/pkg/system/chtimes_windows.go @@ -9,18 +9,17 @@ import ( // setCTime will set the create time on a file. On Windows, this requires // calling SetFileTime and explicitly including the create time. func setCTime(path string, ctime time.Time) error { - ctimespec := windows.NsecToTimespec(ctime.UnixNano()) - pathp, e := windows.UTF16PtrFromString(path) - if e != nil { - return e + pathp, err := windows.UTF16PtrFromString(path) + if err != nil { + return err } - h, e := windows.CreateFile(pathp, + h, err := windows.CreateFile(pathp, windows.FILE_WRITE_ATTRIBUTES, windows.FILE_SHARE_WRITE, nil, windows.OPEN_EXISTING, windows.FILE_FLAG_BACKUP_SEMANTICS, 0) - if e != nil { - return e + if err != nil { + return err } defer windows.Close(h) - c := windows.NsecToFiletime(windows.TimespecToNsec(ctimespec)) + c := windows.NsecToFiletime(ctime.UnixNano()) return windows.SetFileTime(h, &c, nil, nil) } diff --git a/pkg/system/chtimes_windows_test.go b/pkg/system/chtimes_windows_test.go index 060c515003..6184336f3e 100644 --- a/pkg/system/chtimes_windows_test.go +++ b/pkg/system/chtimes_windows_test.go @@ -5,83 +5,104 @@ package system // import "github.com/docker/docker/pkg/system" import ( "os" + "path/filepath" "syscall" "testing" "time" ) -// TestChtimesWindows tests Chtimes access time on a tempfile on Windows -func TestChtimesWindows(t *testing.T) { - file, dir := prepareTempFile(t) - defer os.RemoveAll(dir) +// TestChtimesATimeWindows tests Chtimes access time on a tempfile on Windows. +func TestChtimesATimeWindows(t *testing.T) { + file := filepath.Join(t.TempDir(), "exist") + if err := os.WriteFile(file, []byte("hello"), 0o644); err != nil { + t.Fatal(err) + } - beforeUnixEpochTime := time.Unix(0, 0).Add(-100 * time.Second) - unixEpochTime := time.Unix(0, 0) - afterUnixEpochTime := time.Unix(100, 0) - unixMaxTime := maxTime + beforeUnixEpochTime := unixEpochTime.Add(-100 * time.Second) + afterUnixEpochTime := unixEpochTime.Add(100 * time.Second) // Test both aTime and mTime set to Unix Epoch - Chtimes(file, unixEpochTime, unixEpochTime) + t.Run("both aTime and mTime set to Unix Epoch", func(t *testing.T) { + if err := Chtimes(file, unixEpochTime, unixEpochTime); err != nil { + t.Error(err) + } - f, err := os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - aTime := time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } + aTime := time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) + if aTime != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) + } + }) // Test aTime before Unix Epoch and mTime set to Unix Epoch - Chtimes(file, beforeUnixEpochTime, unixEpochTime) + t.Run("aTime before Unix Epoch and mTime set to Unix Epoch", func(t *testing.T) { + if err := Chtimes(file, beforeUnixEpochTime, unixEpochTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - aTime = time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } + aTime := time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) + if aTime != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) + } + }) // Test aTime set to Unix Epoch and mTime before Unix Epoch - Chtimes(file, unixEpochTime, beforeUnixEpochTime) + t.Run("aTime set to Unix Epoch and mTime before Unix Epoch", func(t *testing.T) { + if err := Chtimes(file, unixEpochTime, beforeUnixEpochTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - aTime = time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) - if aTime != unixEpochTime { - t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) - } + aTime := time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) + if aTime != unixEpochTime { + t.Fatalf("Expected: %s, got: %s", unixEpochTime, aTime) + } + }) // Test both aTime and mTime set to after Unix Epoch (valid time) - Chtimes(file, afterUnixEpochTime, afterUnixEpochTime) + t.Run("both aTime and mTime set to after Unix Epoch (valid time)", func(t *testing.T) { + if err := Chtimes(file, afterUnixEpochTime, afterUnixEpochTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - aTime = time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) - if aTime != afterUnixEpochTime { - t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, aTime) - } + aTime := time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) + if aTime != afterUnixEpochTime { + t.Fatalf("Expected: %s, got: %s", afterUnixEpochTime, aTime) + } + }) // Test both aTime and mTime set to Unix max time - Chtimes(file, unixMaxTime, unixMaxTime) + t.Run("both aTime and mTime set to Unix max time", func(t *testing.T) { + if err := Chtimes(file, unixMaxTime, unixMaxTime); err != nil { + t.Error(err) + } - f, err = os.Stat(file) - if err != nil { - t.Fatal(err) - } + f, err := os.Stat(file) + if err != nil { + t.Fatal(err) + } - aTime = time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) - if aTime.Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { - t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), aTime.Truncate(time.Second)) - } + aTime := time.Unix(0, f.Sys().(*syscall.Win32FileAttributeData).LastAccessTime.Nanoseconds()) + if aTime.Truncate(time.Second) != unixMaxTime.Truncate(time.Second) { + t.Fatalf("Expected: %s, got: %s", unixMaxTime.Truncate(time.Second), aTime.Truncate(time.Second)) + } + }) } diff --git a/pkg/system/init.go b/pkg/system/init.go deleted file mode 100644 index a17597aaba..0000000000 --- a/pkg/system/init.go +++ /dev/null @@ -1,22 +0,0 @@ -package system // import "github.com/docker/docker/pkg/system" - -import ( - "syscall" - "time" - "unsafe" -) - -// Used by chtimes -var maxTime time.Time - -func init() { - // chtimes initialization - if unsafe.Sizeof(syscall.Timespec{}.Nsec) == 8 { - // This is a 64 bit timespec - // os.Chtimes limits time to the following - maxTime = time.Unix(0, 1<<63-1) - } else { - // This is a 32 bit timespec - maxTime = time.Unix(1<<31-1, 0) - } -} diff --git a/pkg/system/lstat_unix_test.go b/pkg/system/lstat_unix_test.go index 943b7d6c63..a38a524c4d 100644 --- a/pkg/system/lstat_unix_test.go +++ b/pkg/system/lstat_unix_test.go @@ -5,13 +5,17 @@ package system // import "github.com/docker/docker/pkg/system" import ( "os" + "path/filepath" "testing" ) // TestLstat tests Lstat for existing and non existing files func TestLstat(t *testing.T) { - file, invalid, _, dir := prepareFiles(t) - defer os.RemoveAll(dir) + tmpDir := t.TempDir() + file := filepath.Join(tmpDir, "exist") + if err := os.WriteFile(file, []byte("hello"), 0o644); err != nil { + t.Fatal(err) + } statFile, err := Lstat(file) if err != nil { @@ -21,7 +25,7 @@ func TestLstat(t *testing.T) { t.Fatal("returned empty stat for existing file") } - statInvalid, err := Lstat(invalid) + statInvalid, err := Lstat(filepath.Join(tmpDir, "nosuchfile")) if err == nil { t.Fatal("did not return error for non-existing file") } diff --git a/pkg/system/stat_unix_test.go b/pkg/system/stat_unix_test.go index 416b07eaa5..a7b6f3e22d 100644 --- a/pkg/system/stat_unix_test.go +++ b/pkg/system/stat_unix_test.go @@ -5,6 +5,7 @@ package system // import "github.com/docker/docker/pkg/system" import ( "os" + "path/filepath" "syscall" "testing" @@ -13,8 +14,10 @@ import ( // TestFromStatT tests fromStatT for a tempfile func TestFromStatT(t *testing.T) { - file, _, _, dir := prepareFiles(t) - defer os.RemoveAll(dir) + file := filepath.Join(t.TempDir(), "exist") + if err := os.WriteFile(file, []byte("hello"), 0o644); err != nil { + t.Fatal(err) + } stat := &syscall.Stat_t{} err := syscall.Lstat(file, stat) diff --git a/pkg/system/utimes_unix_test.go b/pkg/system/utimes_unix_test.go index 30482b7c07..fad50d5051 100644 --- a/pkg/system/utimes_unix_test.go +++ b/pkg/system/utimes_unix_test.go @@ -11,30 +11,26 @@ import ( ) // prepareFiles creates files for testing in the temp directory -func prepareFiles(t *testing.T) (string, string, string, string) { - dir, err := os.MkdirTemp("", "docker-system-test") - if err != nil { +func prepareFiles(t *testing.T) (file, invalid, symlink string) { + t.Helper() + dir := t.TempDir() + + file = filepath.Join(dir, "exist") + if err := os.WriteFile(file, []byte("hello"), 0o644); err != nil { t.Fatal(err) } - file := filepath.Join(dir, "exist") - if err := os.WriteFile(file, []byte("hello"), 0644); err != nil { - t.Fatal(err) - } - - invalid := filepath.Join(dir, "doesnt-exist") - - symlink := filepath.Join(dir, "symlink") + invalid = filepath.Join(dir, "doesnt-exist") + symlink = filepath.Join(dir, "symlink") if err := os.Symlink(file, symlink); err != nil { t.Fatal(err) } - return file, invalid, symlink, dir + return file, invalid, symlink } func TestLUtimesNano(t *testing.T) { - file, invalid, symlink, dir := prepareFiles(t) - defer os.RemoveAll(dir) + file, invalid, symlink := prepareFiles(t) before, err := os.Stat(file) if err != nil {