mirror of
https://github.com/rclone/rclone.git
synced 2025-12-06 00:03:32 +00:00
vfs: add symlink support to VFS
This is somewhat limited in that it only resolves symlinks when files are opened. This will work fine for the intended use in rclone mount, but is inadequate for the other servers probably.
This commit is contained in:
85
vfs/vfs.go
85
vfs/vfs.go
@@ -791,6 +791,9 @@ func (vfs *VFS) WriteFile(name string, data []byte, perm os.FileMode) (err error
|
||||
}
|
||||
|
||||
// AddVirtual adds the object (file or dir) to the directory cache
|
||||
//
|
||||
// This is used by the vfs cache to insert objects that are uploading
|
||||
// into the directory tree.
|
||||
func (vfs *VFS) AddVirtual(remote string, size int64, isDir bool) (err error) {
|
||||
remote = strings.TrimRight(remote, "/")
|
||||
var dir *Dir
|
||||
@@ -808,3 +811,85 @@ func (vfs *VFS) AddVirtual(remote string, size int64, isDir bool) (err error) {
|
||||
dir.AddVirtual(leaf, size, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Readlink returns the destination of the named symbolic link.
|
||||
// If there is an error, it will be of type *PathError.
|
||||
func (vfs *VFS) Readlink(name string) (s string, err error) {
|
||||
if !vfs.Opt.Links {
|
||||
fs.Errorf(nil, "symlinks not supported without the --links flag: %v", name)
|
||||
return "", ENOSYS
|
||||
}
|
||||
node, err := vfs.Stat(name)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
file, ok := node.(*File)
|
||||
if !ok || !file.IsSymlink() {
|
||||
return "", EINVAL // not a symlink
|
||||
}
|
||||
fd, err := file.Open(os.O_RDONLY | o_SYMLINK)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer fs.CheckClose(fd, &err)
|
||||
b, err := io.ReadAll(fd)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(b), nil
|
||||
}
|
||||
|
||||
// CreateSymlink 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.
|
||||
// It returns the node created
|
||||
func (vfs *VFS) CreateSymlink(oldname, newname string) (Node, error) {
|
||||
if !vfs.Opt.Links {
|
||||
fs.Errorf(newname, "symlinks not supported without the --links flag")
|
||||
return nil, ENOSYS
|
||||
}
|
||||
|
||||
// Destination can't exist
|
||||
_, err := vfs.Stat(newname)
|
||||
if err == nil {
|
||||
return nil, EEXIST
|
||||
} else if err != ENOENT {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find the parent
|
||||
dir, leaf, err := vfs.StatParent(newname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the file node
|
||||
flags := os.O_WRONLY | os.O_CREATE | os.O_TRUNC | o_SYMLINK
|
||||
file, err := dir.Create(leaf, flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Force the file to be a link
|
||||
file.setSymlink()
|
||||
|
||||
// Open the file
|
||||
fh, err := file.Open(flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer fs.CheckClose(fh, &err)
|
||||
|
||||
// Write the symlink data
|
||||
_, err = fh.Write([]byte(strings.ReplaceAll(oldname, "\\", "/")))
|
||||
|
||||
return file, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (vfs *VFS) Symlink(oldname, newname string) error {
|
||||
_, err := vfs.CreateSymlink(oldname, newname)
|
||||
return err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user