diff options
| author | Damien Neil <dneil@google.com> | 2025-03-25 10:31:00 -0700 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2025-03-28 11:02:40 -0700 |
| commit | 26fdb07d4ce58885305283ba18960f582f4eaa73 (patch) | |
| tree | 6291830ac80f793d1979ce338377f4556c86247a /src/os/root_windows.go | |
| parent | 656b5b3abe25d026725edff49edbdaa9862c9d77 (diff) | |
| download | go-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.go | 46 |
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. |
