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
* [os.Root.Chown]
* [os.Root.Chtimes]
* [os.Root.Lchown]
+ * [os.Root.Readlink]
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,
}
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
+}
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)
}
}
+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.
//
}
}
+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":
}
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)
+}