os: implement os.Symlink and os.Readlink

Этот коммит содержится в:
Dan Kegel 2022-01-15 20:56:31 -08:00 коммит произвёл Ron Evans
родитель db0efc52c7
коммит 57b8f7e667
6 изменённых файлов: 183 добавлений и 11 удалений

Просмотреть файл

@ -38,6 +38,15 @@ func Mkdir(path string, perm FileMode) error {
return nil return nil
} }
// 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) {
if n < 0 {
n = 0
}
return n, err
}
// Remove removes a file or (empty) directory. If the operation fails, it will // Remove removes a file or (empty) directory. If the operation fails, it will
// return an error of type *PathError. // return an error of type *PathError.
func Remove(path string) error { func Remove(path string) error {
@ -52,11 +61,6 @@ func Remove(path string) error {
return nil return nil
} }
// Symlink is a stub, it is not implemented.
func Symlink(oldname, newname string) error {
return ErrNotImplemented
}
// RemoveAll is a stub, it is not implemented. // RemoveAll is a stub, it is not implemented.
func RemoveAll(path string) error { func RemoveAll(path string) error {
return ErrNotImplemented return ErrNotImplemented
@ -257,11 +261,6 @@ func Getwd() (string, error) {
return syscall.Getwd() return syscall.Getwd()
} }
// Readlink is a stub (for now), always returning the string it was given
func Readlink(name string) (string, error) {
return name, nil
}
// TempDir returns the default directory to use for temporary files. // TempDir returns the default directory to use for temporary files.
// //
// On Unix systems, it returns $TMPDIR if non-empty, else /tmp. // On Unix systems, it returns $TMPDIR if non-empty, else /tmp.

Просмотреть файл

@ -52,6 +52,44 @@ func tempDir() string {
return dir return dir
} }
// Symlink creates newname as a symbolic link to oldname.
// On Windows, a symlink to a non-existent oldname creates a file symlink;
// if oldname is later created as a directory the symlink will not work.
// If there is an error, it will be of type *LinkError.
func Symlink(oldname, newname string) error {
e := ignoringEINTR(func() error {
return syscall.Symlink(oldname, newname)
})
if e != nil {
return &LinkError{"symlink", oldname, newname, e}
}
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) {
for len := 128; ; len *= 2 {
b := make([]byte, len)
var (
n int
e error
)
for {
n, e = fixCount(syscall.Readlink(name, b))
if e != syscall.EINTR {
break
}
}
if e != nil {
return "", &PathError{Op: "readlink", Path: name, Err: e}
}
if n < len {
return string(b[0:n]), nil
}
}
}
// ReadAt reads up to len(b) bytes from the File starting at the given absolute offset. // ReadAt reads up to len(b) bytes from the File starting at the given absolute offset.
// It returns the number of bytes read and any error encountered, possibly io.EOF. // It returns the number of bytes read and any error encountered, possibly io.EOF.
// At end of file, Pread returns 0, io.EOF. // At end of file, Pread returns 0, io.EOF.

Просмотреть файл

@ -15,6 +15,16 @@ import (
type syscallFd = syscall.Handle type syscallFd = syscall.Handle
// Symlink is a stub, it is not implemented.
func Symlink(oldname, newname string) error {
return ErrNotImplemented
}
// Readlink is a stub (for now), always returning the string it was given
func Readlink(name string) (string, error) {
return name, nil
}
func rename(oldname, newname string) error { func rename(oldname, newname string) error {
e := windows.Rename(fixLongPath(oldname), fixLongPath(newname)) e := windows.Rename(fixLongPath(oldname), fixLongPath(newname))
if e != nil { if e != nil {

Просмотреть файл

@ -96,6 +96,28 @@ func TestRemove(t *testing.T) {
} }
} }
// chtmpdir changes the working directory to a new temporary directory and
// provides a cleanup function.
func chtmpdir(t *testing.T) func() {
oldwd, err := Getwd()
if err != nil {
t.Fatalf("chtmpdir: %v", err)
}
d, err := MkdirTemp("", "test")
if err != nil {
t.Fatalf("chtmpdir: %v", err)
}
if err := Chdir(d); err != nil {
t.Fatalf("chtmpdir: %v", err)
}
return func() {
if err := Chdir(oldwd); err != nil {
t.Fatalf("chtmpdir: %v", err)
}
RemoveAll(d)
}
}
func TestRename(t *testing.T) { func TestRename(t *testing.T) {
// TODO: use t.TempDir() // TODO: use t.TempDir()
from, to := TempDir()+"/"+"TestRename-from", TempDir()+"/"+"TestRename-to" from, to := TempDir()+"/"+"TestRename-from", TempDir()+"/"+"TestRename-to"

75
src/os/os_symlink_test.go Обычный файл
Просмотреть файл

@ -0,0 +1,75 @@
// +build !windows,!baremetal,!js,!wasi
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package os_test
import (
. "os"
"testing"
)
// TODO: Move this back into os_anyos_test.go when wasi supports symlink
func TestSymlink(t *testing.T) {
//testenv.MustHaveSymlink(t)
defer chtmpdir(t)()
from, to := "symlinktestfrom", "symlinktestto"
file, err := Create(to)
if err != nil {
t.Fatalf("Create(%q) failed: %v", to, err)
}
if err = file.Close(); err != nil {
t.Errorf("Close(%q) failed: %v", to, err)
}
err = Symlink(to, from)
if err != nil {
t.Fatalf("Symlink(%q, %q) failed: %v", to, from, err)
}
tostat, err := Lstat(to)
if err != nil {
t.Fatalf("Lstat(%q) failed: %v", to, err)
}
if tostat.Mode()&ModeSymlink != 0 {
t.Fatalf("Lstat(%q).Mode()&ModeSymlink = %v, want 0", to, tostat.Mode()&ModeSymlink)
}
fromstat, err := Stat(from)
if err != nil {
t.Fatalf("Stat(%q) failed: %v", from, err)
}
if !SameFile(tostat, fromstat) {
t.Errorf("Symlink(%q, %q) did not create symlink", to, from)
}
fromstat, err = Lstat(from)
if err != nil {
t.Fatalf("Lstat(%q) failed: %v", from, err)
}
if fromstat.Mode()&ModeSymlink == 0 {
t.Fatalf("Lstat(%q).Mode()&ModeSymlink = 0, want %v", from, ModeSymlink)
}
fromstat, err = Stat(from)
if err != nil {
t.Fatalf("Stat(%q) failed: %v", from, err)
}
if fromstat.Name() != from {
t.Errorf("Stat(%q).Name() = %q, want %q", from, fromstat.Name(), from)
}
if fromstat.Mode()&ModeSymlink != 0 {
t.Fatalf("Stat(%q).Mode()&ModeSymlink = %v, want 0", from, fromstat.Mode()&ModeSymlink)
}
s, err := Readlink(from)
if err != nil {
t.Fatalf("Readlink(%q) failed: %v", from, err)
}
if s != to {
t.Fatalf("Readlink(%q) = %q, want %q", from, s, to)
}
file, err = Open(from)
if err != nil {
t.Fatalf("Open(%q) failed: %v", from, err)
}
file.Close()
}

Просмотреть файл

@ -63,6 +63,16 @@ func Open(path string, flag int, mode uint32) (fd int, err error) {
return return
} }
func Readlink(path string, p []byte) (n int, err error) {
data := cstring(path)
buf, count := splitSlice(p)
n = libc_readlink(&data[0], buf, uint(count))
if n < 0 {
err = getErrno()
}
return
}
func Chdir(path string) (err error) { func Chdir(path string) (err error) {
data := cstring(path) data := cstring(path)
fail := int(libc_chdir(&data[0])) fail := int(libc_chdir(&data[0]))
@ -109,6 +119,16 @@ func Rename(from, to string) (err error) {
return return
} }
func Symlink(from, to string) (err error) {
fromdata := cstring(from)
todata := cstring(to)
fail := int(libc_symlink(&fromdata[0], &todata[0]))
if fail < 0 {
err = getErrno()
}
return
}
func Unlink(path string) (err error) { func Unlink(path string) (err error) {
data := cstring(path) data := cstring(path)
fail := int(libc_unlink(&data[0])) fail := int(libc_unlink(&data[0]))
@ -303,7 +323,15 @@ func libc_rmdir(pathname *byte) int32
// int rename(const char *from, *to); // int rename(const char *from, *to);
//export rename //export rename
func libc_rename(from, too *byte) int32 func libc_rename(from, to *byte) int32
// int symlink(const char *from, *to);
//export symlink
func libc_symlink(from, to *byte) int32
// ssize_t readlink(const char *path, void *buf, size_t count);
//export readlink
func libc_readlink(path *byte, buf *byte, count uint) int
// int unlink(const char *pathname); // int unlink(const char *pathname);
//export unlink //export unlink