filebrowser/fileutils/copy.go

98 lines
1.9 KiB
Go

package fileutils
import (
"os"
"path"
"path/filepath"
"strings"
"github.com/spf13/afero"
)
// Copy copies a file or folder from one place to another.
func Copy(fs afero.Fs, src, dst string) error {
if src = path.Clean("/" + src); src == "" {
return os.ErrNotExist
}
if dst = path.Clean("/" + dst); dst == "" {
return os.ErrNotExist
}
if src == "/" || dst == "/" {
// Prohibit copying from or to the virtual root directory.
return os.ErrInvalid
}
if dst == src {
return os.ErrInvalid
}
info, err := fs.Stat(src)
if err != nil {
return err
}
if info.IsDir() {
return CopyDir(fs, src, dst)
}
return CopyFile(fs, src, dst)
}
// Same as Copy, but checks scope in symlinks
func CopyScoped(fs afero.Fs, src, dst, scope string) error {
if src = path.Clean("/" + src); src == "" {
return os.ErrNotExist
}
if dst = path.Clean("/" + dst); dst == "" {
return os.ErrNotExist
}
if src == "/" || dst == "/" {
// Prohibit copying from or to the virtual root directory.
return os.ErrInvalid
}
if dst == src {
return os.ErrInvalid
}
info, err := fs.Stat(src)
if err != nil {
return err
}
switch info.Mode() & os.ModeType {
case os.ModeDir:
return CopyDirScoped(fs, src, dst, scope)
case os.ModeSymlink:
return CopySymLinkScoped(fs, src, dst, scope)
default:
return CopyFile(fs, src, dst)
}
}
func CopySymLinkScoped(fs afero.Fs, source, dest, scope string) error {
if reader, ok := fs.(afero.LinkReader); ok {
link, err := reader.ReadlinkIfPossible(source)
if err != nil {
return err
}
if filepath.IsAbs(link) {
link = strings.TrimPrefix(link, scope)
link = filepath.Join(string(os.PathSeparator), link)
} else {
link = filepath.Clean(filepath.Join(filepath.Dir(source), link))
}
if linker, ok := fs.(afero.Linker); ok {
return linker.SymlinkIfPossible(link, dest)
}
return nil
}
return nil
}