os: implement os.Symlink and os.Readlink
Этот коммит содержится в:
родитель
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
Обычный файл
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
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче