From b4b149f834bfe497475c8f8ffc5857a670da891e Mon Sep 17 00:00:00 2001 From: Scott Owens Date: Tue, 10 Dec 2019 16:24:03 +1100 Subject: [PATCH] adding support for Linker and LinkReader --- basepath.go | 26 ++++++++++++++++++++++++++ copyOnWriteFs.go | 20 ++++++++++++++++++++ os.go | 8 ++++++++ readonlyfs.go | 12 ++++++++++++ symlink.go | 14 ++++++++++++++ 5 files changed, 80 insertions(+) diff --git a/basepath.go b/basepath.go index 616ff8f..3a14b83 100644 --- a/basepath.go +++ b/basepath.go @@ -177,4 +177,30 @@ func (b *BasePathFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { return fi, false, err } +func (b *BasePathFs) SymlinkIfPossible(oldname, newname string) error { + oldname, err := b.RealPath(oldname) + if err != nil { + return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: err} + } + newname, err = b.RealPath(newname) + if err != nil { + return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: err} + } + if linker, ok := b.source.(Linker); ok { + return linker.SymlinkIfPossible(oldname, newname) + } + return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink} +} + +func (b *BasePathFs) ReadlinkIfPossible(name string) (string, error) { + name, err := b.RealPath(name) + if err != nil { + return "", &os.PathError{Op: "readlink", Path: name, Err: err} + } + if reader, ok := b.source.(LinkReader); ok { + return reader.ReadlinkIfPossible(name) + } + return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink} +} + // vim: ts=4 sw=4 noexpandtab nolist syn=go diff --git a/copyOnWriteFs.go b/copyOnWriteFs.go index e8108a8..96b7701 100644 --- a/copyOnWriteFs.go +++ b/copyOnWriteFs.go @@ -117,6 +117,26 @@ func (u *CopyOnWriteFs) LstatIfPossible(name string) (os.FileInfo, bool, error) return fi, false, err } +func (u *CopyOnWriteFs) SymlinkIfPossible(oldname, newname string) error { + if slayer, ok := u.layer.(Linker); ok { + return slayer.SymlinkIfPossible(oldname, newname) + } + + return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink} +} + +func (u *CopyOnWriteFs) ReadlinkIfPossible(name string) (string, error) { + if rlayer, ok := u.layer.(LinkReader); ok { + return rlayer.ReadlinkIfPossible(name) + } + + if rbase, ok := u.base.(LinkReader); ok { + return rbase.ReadlinkIfPossible(name) + } + + return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink} +} + func (u *CopyOnWriteFs) isNotExist(err error) bool { if e, ok := err.(*os.PathError); ok { err = e.Err diff --git a/os.go b/os.go index 13cc1b8..4761db5 100644 --- a/os.go +++ b/os.go @@ -99,3 +99,11 @@ func (OsFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { fi, err := os.Lstat(name) return fi, true, err } + +func (OsFs) SymlinkIfPossible(oldname, newname string) error { + return os.Symlink(oldname, newname) +} + +func (OsFs) ReadlinkIfPossible(name string) (string, error) { + return os.Readlink(name) +} diff --git a/readonlyfs.go b/readonlyfs.go index c6376ec..f94b181 100644 --- a/readonlyfs.go +++ b/readonlyfs.go @@ -44,6 +44,18 @@ func (r *ReadOnlyFs) LstatIfPossible(name string) (os.FileInfo, bool, error) { return fi, false, err } +func (r *ReadOnlyFs) SymlinkIfPossible(oldname, newname string) error { + return &os.LinkError{Op: "symlink", Old: oldname, New: newname, Err: ErrNoSymlink} +} + +func (r *ReadOnlyFs) ReadlinkIfPossible(name string) (string, error) { + if srdr, ok := r.source.(LinkReader); ok { + return srdr.ReadlinkIfPossible(name) + } + + return "", &os.PathError{Op: "readlink", Path: name, Err: ErrNoReadlink} +} + func (r *ReadOnlyFs) Rename(o, n string) error { return syscall.EPERM } diff --git a/symlink.go b/symlink.go index ba9d179..d1c6ea5 100644 --- a/symlink.go +++ b/symlink.go @@ -13,6 +13,10 @@ package afero +import ( + "errors" +) + // Symlinker is an optional interface in Afero. It is only implemented by the // filesystems saying so. // It indicates support for 3 symlink related interfaces that implement the @@ -34,8 +38,18 @@ type Linker interface { SymlinkIfPossible(oldname, newname string) error } +// ErrNoSymlink is the error that will be wrapped in an os.LinkError if a file system +// does not support Symlink's either directly or through its delegated filesystem. +// As expressed by support for the Linker interface. +var ErrNoSymlink = errors.New("symlink not supported") + // LinkReader is an optional interface in Afero. It is only implemented by the // filesystems saying so. type LinkReader interface { ReadlinkIfPossible(name string) (string, error) } + +// ErrNoReadlink is the error that will be wrapped in an os.Path if a file system +// does not support the readlink operation either directly or through its delegated filesystem. +// As expressed by support for the LinkReader interface. +var ErrNoReadlink = errors.New("readlink not supported")