mirror of https://github.com/golang/go.git
os: document Readlink behavior for relative links
Also provide a runnable example to illustrate that behavior. This should help users to avoid the common mistake of expecting os.Readlink to return an absolute path. Fixes #57766. Change-Id: I8f60aa111ebda0cae985758615019aaf26d5cb41 Reviewed-on: https://go-review.googlesource.com/c/go/+/546995 Auto-Submit: Bryan Mills <bcmills@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Carlos Amedee <carlos@golang.org> Reviewed-by: Rob Pike <r@golang.org>
This commit is contained in:
parent
4601857c1c
commit
bb34112d4d
|
|
@ -263,3 +263,57 @@ func ExampleMkdirAll() {
|
|||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleReadlink() {
|
||||
// First, we create a relative symlink to a file.
|
||||
d, err := os.MkdirTemp("", "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(d)
|
||||
targetPath := filepath.Join(d, "hello.txt")
|
||||
if err := os.WriteFile(targetPath, []byte("Hello, Gophers!"), 0644); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
linkPath := filepath.Join(d, "hello.link")
|
||||
if err := os.Symlink("hello.txt", filepath.Join(d, "hello.link")); err != nil {
|
||||
if errors.Is(err, errors.ErrUnsupported) {
|
||||
// Allow the example to run on platforms that do not support symbolic links.
|
||||
fmt.Printf("%s links to %s\n", filepath.Base(linkPath), "hello.txt")
|
||||
return
|
||||
}
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Readlink returns the relative path as passed to os.Symlink.
|
||||
dst, err := os.Readlink(linkPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("%s links to %s\n", filepath.Base(linkPath), dst)
|
||||
|
||||
var dstAbs string
|
||||
if filepath.IsAbs(dst) {
|
||||
dstAbs = dst
|
||||
} else {
|
||||
// Symlink targets are relative to the directory containing the link.
|
||||
dstAbs = filepath.Join(filepath.Dir(linkPath), dst)
|
||||
}
|
||||
|
||||
// Check that the target is correct by comparing it with os.Stat
|
||||
// on the original target path.
|
||||
dstInfo, err := os.Stat(dstAbs)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
targetInfo, err := os.Stat(targetPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if !os.SameFile(dstInfo, targetInfo) {
|
||||
log.Fatalf("link destination (%s) is not the same file as %s", dstAbs, targetPath)
|
||||
}
|
||||
|
||||
// Output:
|
||||
// hello.link links to hello.txt
|
||||
}
|
||||
|
|
|
|||
|
|
@ -392,6 +392,15 @@ func Rename(oldpath, newpath string) error {
|
|||
return rename(oldpath, newpath)
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
//
|
||||
// If the link destination is relative, Readlink returns the relative path
|
||||
// without resolving it to an absolute one.
|
||||
func Readlink(name string) (string, error) {
|
||||
return readlink(name)
|
||||
}
|
||||
|
||||
// Many functions in package syscall return a count of -1 instead of 0.
|
||||
// Using fixCount(call()) instead of call() corrects the count.
|
||||
func fixCount(n int, err error) (int, error) {
|
||||
|
|
|
|||
|
|
@ -505,9 +505,7 @@ func Symlink(oldname, newname string) error {
|
|||
return &LinkError{"symlink", oldname, newname, syscall.EPLAN9}
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Readlink(name string) (string, error) {
|
||||
func readlink(name string) (string, error) {
|
||||
return "", &PathError{Op: "readlink", Path: name, Err: syscall.EPLAN9}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -426,9 +426,7 @@ func Symlink(oldname, newname string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Readlink(name string) (string, error) {
|
||||
func readlink(name string) (string, error) {
|
||||
for len := 128; ; len *= 2 {
|
||||
b := make([]byte, len)
|
||||
var (
|
||||
|
|
|
|||
|
|
@ -406,7 +406,7 @@ func normaliseLinkPath(path string) (string, error) {
|
|||
return "", errors.New("GetFinalPathNameByHandle returned unexpected path: " + s)
|
||||
}
|
||||
|
||||
func readlink(path string) (string, error) {
|
||||
func readReparseLink(path string) (string, error) {
|
||||
h, err := openSymlink(path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
|
@ -438,10 +438,8 @@ func readlink(path string) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func Readlink(name string) (string, error) {
|
||||
s, err := readlink(fixLongPath(name))
|
||||
func readlink(name string) (string, error) {
|
||||
s, err := readReparseLink(fixLongPath(name))
|
||||
if err != nil {
|
||||
return "", &PathError{Op: "readlink", Path: name, Err: err}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue