mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +00:00
os: add Root.Readlink
For #67002 Change-Id: I532a5ffc02c7457796540db54fa2f5ddad86e4b2 Reviewed-on: https://go-review.googlesource.com/c/go/+/658995 Auto-Submit: Damien Neil <dneil@google.com> Reviewed-by: Ian Lance Taylor <iant@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
1eb1579fba
commit
cb0d767a10
@ -2,3 +2,4 @@ pkg os, method (*Root) Chmod(string, fs.FileMode) error #67002
|
||||
pkg os, method (*Root) Chown(string, int, int) error #67002
|
||||
pkg os, method (*Root) Chtimes(string, time.Time, time.Time) error #67002
|
||||
pkg os, method (*Root) Lchown(string, int, int) error #67002
|
||||
pkg os, method (*Root) Readlink(string) (string, error) #67002
|
||||
|
@ -4,3 +4,4 @@ The [os.Root] type supports the following additional methods:
|
||||
* [os.Root.Chown]
|
||||
* [os.Root.Chtimes]
|
||||
* [os.Root.Lchown]
|
||||
* [os.Root.Readlink]
|
||||
|
@ -193,6 +193,12 @@ func (r *Root) Lstat(name string) (FileInfo, error) {
|
||||
return rootStat(r, name, true)
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link in the root.
|
||||
// See [Readlink] for more details.
|
||||
func (r *Root) Readlink(name string) (string, error) {
|
||||
return rootReadlink(r, name)
|
||||
}
|
||||
|
||||
func (r *Root) logOpen(name string) {
|
||||
if log := testlog.Logger(); log != nil {
|
||||
// This won't be right if r's name has changed since it was opened,
|
||||
|
@ -155,3 +155,14 @@ func rootRemove(r *Root, name string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func rootReadlink(r *Root, name string) (string, error) {
|
||||
if err := checkPathEscapesLstat(r, name); err != nil {
|
||||
return "", &PathError{Op: "readlinkat", Path: name, Err: err}
|
||||
}
|
||||
name, err := Readlink(joinPath(r.root.name, name))
|
||||
if err != nil {
|
||||
return "", &PathError{Op: "readlinkat", Path: name, Err: underlyingError(err)}
|
||||
}
|
||||
return name, nil
|
||||
}
|
||||
|
@ -118,6 +118,16 @@ func rootMkdir(r *Root, name string, perm FileMode) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func rootReadlink(r *Root, name string) (string, error) {
|
||||
target, err := doInRoot(r, name, func(parent sysfdType, name string) (string, error) {
|
||||
return readlinkat(parent, name)
|
||||
})
|
||||
if err != nil {
|
||||
return "", &PathError{Op: "readlinkat", Path: name, Err: err}
|
||||
}
|
||||
return target, nil
|
||||
}
|
||||
|
||||
func rootRemove(r *Root, name string) error {
|
||||
_, err := doInRoot(r, name, func(parent sysfdType, name string) (struct{}, error) {
|
||||
return struct{}{}, removeat(parent, name)
|
||||
|
@ -664,6 +664,35 @@ func TestRootLstat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootReadlink(t *testing.T) {
|
||||
for _, test := range rootTestCases {
|
||||
test.run(t, func(t *testing.T, target string, root *os.Root) {
|
||||
const content = "content"
|
||||
wantError := test.wantError
|
||||
if test.ltarget != "" {
|
||||
// Readlink will read the final link, rather than following it.
|
||||
wantError = false
|
||||
} else {
|
||||
// Readlink fails on non-link targets.
|
||||
wantError = true
|
||||
}
|
||||
|
||||
got, err := root.Readlink(test.open)
|
||||
if errEndsTest(t, err, wantError, "root.Readlink(%q)", test.open) {
|
||||
return
|
||||
}
|
||||
|
||||
want, err := os.Readlink(filepath.Join(root.Name(), test.ltarget))
|
||||
if err != nil {
|
||||
t.Fatalf("os.Readlink(%q) = %v, want success", test.ltarget, err)
|
||||
}
|
||||
if got != want {
|
||||
t.Errorf("root.Readlink(%q) = %q, want %q", test.open, got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// A rootConsistencyTest is a test case comparing os.Root behavior with
|
||||
// the corresponding non-Root function.
|
||||
//
|
||||
@ -1063,6 +1092,18 @@ func TestRootConsistencyLstat(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootConsistencyReadlink(t *testing.T) {
|
||||
for _, test := range rootConsistencyTestCases {
|
||||
test.run(t, func(t *testing.T, path string, r *os.Root) (string, error) {
|
||||
if r == nil {
|
||||
return os.Readlink(path)
|
||||
} else {
|
||||
return r.Readlink(path)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRootRenameAfterOpen(t *testing.T) {
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
|
@ -314,3 +314,12 @@ func chtimesat(dirfd syscall.Handle, name string, atime time.Time, mtime time.Ti
|
||||
}
|
||||
return syscall.SetFileTime(h, nil, &a, &w)
|
||||
}
|
||||
|
||||
func readlinkat(dirfd syscall.Handle, name string) (string, error) {
|
||||
fd, err := openat(dirfd, name, windows.O_OPEN_REPARSE, 0)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer syscall.CloseHandle(fd)
|
||||
return readReparseLinkHandle(fd)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user