Skip to content

Commit a0576cb

Browse files
committed
archive: do not follow reparse points in chtimes
Symlink entries are handled separately through lchtimes. Update chtimes to open the final path component with OBJ_DONT_REPARSE and return an error if a reparse point is encountered. This avoids following an unexpected reparse point if the destination is replaced after its parent path has been safely resolved, preventing metadata updates from escaping the intended filesystem object. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent b0d5baf commit a0576cb

1 file changed

Lines changed: 16 additions & 1 deletion

File tree

time_windows.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package archive
22

33
import (
4+
"errors"
45
"os"
56
"path/filepath"
67
"time"
@@ -12,6 +13,10 @@ import (
1213
// chtimes changes the access and modification time of a file at the given
1314
// path relative to root.
1415
//
16+
// Symlink entries are handled separately through lchtimes. The final path
17+
// component is expected not to be a reparse point; if one is encountered,
18+
// chtimes returns an error.
19+
//
1520
// Callers must use boundTime to ensure timestamps are within the range
1621
// supported by os.Chtimes.
1722
func chtimes(root *os.Root, name string, atime, mtime time.Time) error {
@@ -21,7 +26,12 @@ func chtimes(root *os.Root, name string, atime, mtime time.Time) error {
2126
}
2227
defer parent.Close()
2328

24-
return chtimesAt(parent, filepath.Base(name), atime, mtime, false)
29+
// Symlink entries are handled by lchtimes. The destination for all
30+
// chtimes callers is therefore expected not to be a reparse point.
31+
//
32+
// Do not follow the final component: if it was concurrently replaced
33+
// with a reparse point, fail instead of updating its target.
34+
return chtimesAt(parent, filepath.Base(name), atime, mtime, true)
2535
}
2636

2737
func lchtimes(root *os.Root, name string, atime time.Time, mtime time.Time) error {
@@ -31,6 +41,11 @@ func lchtimes(root *os.Root, name string, atime time.Time, mtime time.Time) erro
3141
func chtimesAt(parent *os.File, name string, atime, mtime time.Time, noFollow bool) error {
3242
h, err := openForWriteAttributesAt(windows.Handle(parent.Fd()), name, noFollow)
3343
if err != nil {
44+
if noFollow && errors.Is(err, windows.STATUS_REPARSE_POINT_ENCOUNTERED) {
45+
// Encountering a reparse point when noFollow is requested is unexpected.
46+
// Treat it as a potential breakout to fail extraction safely.
47+
return breakoutError(err)
48+
}
3449
return err
3550
}
3651
defer func() { _ = windows.Close(h) }()

0 commit comments

Comments
 (0)