Browse Source

os: implement os.Symlink and os.Readlink

pull/2541/head
Dan Kegel 3 years ago
committed by Ron Evans
parent
commit
57b8f7e667
  1. 19
      src/os/file.go
  2. 38
      src/os/file_unix.go
  3. 10
      src/os/file_windows.go
  4. 22
      src/os/os_anyos_test.go
  5. 75
      src/os/os_symlink_test.go
  6. 30
      src/syscall/syscall_libc.go

19
src/os/file.go

@ -38,6 +38,15 @@ func Mkdir(path string, perm FileMode) error {
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
// return an error of type *PathError.
func Remove(path string) error {
@ -52,11 +61,6 @@ func Remove(path string) error {
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.
func RemoveAll(path string) error {
return ErrNotImplemented
@ -257,11 +261,6 @@ func Getwd() (string, error) {
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.
//
// On Unix systems, it returns $TMPDIR if non-empty, else /tmp.

38
src/os/file_unix.go

@ -52,6 +52,44 @@ func tempDir() string {
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.
// It returns the number of bytes read and any error encountered, possibly io.EOF.
// At end of file, Pread returns 0, io.EOF.

10
src/os/file_windows.go

@ -15,6 +15,16 @@ import (
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 {
e := windows.Rename(fixLongPath(oldname), fixLongPath(newname))
if e != nil {

22
src/os/os_anyos_test.go

@ -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) {
// TODO: use t.TempDir()
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()
}

30
src/syscall/syscall_libc.go

@ -63,6 +63,16 @@ func Open(path string, flag int, mode uint32) (fd int, err error) {
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) {
data := cstring(path)
fail := int(libc_chdir(&data[0]))
@ -109,6 +119,16 @@ func Rename(from, to string) (err error) {
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) {
data := cstring(path)
fail := int(libc_unlink(&data[0]))
@ -303,7 +323,15 @@ func libc_rmdir(pathname *byte) int32
// int rename(const char *from, *to);
//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);
//export unlink

Loading…
Cancel
Save