aboutsummaryrefslogtreecommitdiff
path: root/src/os/root_windows.go
diff options
context:
space:
mode:
authorDamien Neil <dneil@google.com>2025-03-25 10:31:00 -0700
committerGopher Robot <gobot@golang.org>2025-03-28 11:02:40 -0700
commit26fdb07d4ce58885305283ba18960f582f4eaa73 (patch)
tree6291830ac80f793d1979ce338377f4556c86247a /src/os/root_windows.go
parent656b5b3abe25d026725edff49edbdaa9862c9d77 (diff)
downloadgo-26fdb07d4ce58885305283ba18960f582f4eaa73.tar.xz
os: add Root.Symlink
For #67002 Change-Id: Ia1637b61eae49e97e1d07f058ad2390e74cd3403 Reviewed-on: https://go-review.googlesource.com/c/go/+/660635 Reviewed-by: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Quim Muntal <quimmuntal@gmail.com> Auto-Submit: Damien Neil <dneil@google.com>
Diffstat (limited to 'src/os/root_windows.go')
-rw-r--r--src/os/root_windows.go46
1 files changed, 46 insertions, 0 deletions
diff --git a/src/os/root_windows.go b/src/os/root_windows.go
index 0c37acb089..eb82715046 100644
--- a/src/os/root_windows.go
+++ b/src/os/root_windows.go
@@ -233,6 +233,52 @@ func rootStat(r *Root, name string, lstat bool) (FileInfo, error) {
return fi, nil
}
+func rootSymlink(r *Root, oldname, newname string) error {
+ if oldname == "" {
+ return syscall.EINVAL
+ }
+
+ // CreateSymbolicLinkW converts volume-relative paths into absolute ones.
+ // Do the same.
+ if filepathlite.VolumeNameLen(oldname) > 0 && !filepathlite.IsAbs(oldname) {
+ p, err := syscall.FullPath(oldname)
+ if err == nil {
+ oldname = p
+ }
+ }
+
+ // If oldname can be resolved to a directory in the root, create a directory link.
+ // Otherwise, create a file link.
+ var flags windows.SymlinkatFlags
+ if filepathlite.VolumeNameLen(oldname) == 0 && !IsPathSeparator(oldname[0]) {
+ // oldname is a path relative to the directory containing newname.
+ // Prepend newname's directory to it to make a path relative to the root.
+ // For example, if oldname=old and newname=a\new, destPath=a\old.
+ destPath := oldname
+ if dir := dirname(newname); dir != "." {
+ destPath = dir + `\` + oldname
+ }
+ fi, err := r.Stat(destPath)
+ if err == nil && fi.IsDir() {
+ flags |= windows.SYMLINKAT_DIRECTORY
+ }
+ }
+
+ // Empirically, CreateSymbolicLinkW appears to set the relative flag iff
+ // the target does not contain a volume name.
+ if filepathlite.VolumeNameLen(oldname) == 0 {
+ flags |= windows.SYMLINKAT_RELATIVE
+ }
+
+ _, err := doInRoot(r, newname, func(parent sysfdType, name string) (struct{}, error) {
+ return struct{}{}, windows.Symlinkat(oldname, parent, name, flags)
+ })
+ if err != nil {
+ return &LinkError{"symlinkat", oldname, newname, err}
+ }
+ return nil
+}
+
func chmodat(parent syscall.Handle, name string, mode FileMode) error {
// Currently, on Windows os.Chmod("symlink") will act on "symlink",
// not on any file it points to.