waveterm/pkg/remote/fileshare/fileshare.go
Evan Simkowitz b0e3b6d777
Fix move & copy for prefix filesystems (#1998)
Also makes recursive the default for copy, adds better error for move
without recursive
2025-02-20 10:17:32 -08:00

205 lines
7.4 KiB
Go

package fileshare
import (
"context"
"fmt"
"log"
"github.com/wavetermdev/waveterm/pkg/remote/awsconn"
"github.com/wavetermdev/waveterm/pkg/remote/connparse"
"github.com/wavetermdev/waveterm/pkg/remote/fileshare/fstype"
"github.com/wavetermdev/waveterm/pkg/remote/fileshare/s3fs"
"github.com/wavetermdev/waveterm/pkg/remote/fileshare/wavefs"
"github.com/wavetermdev/waveterm/pkg/remote/fileshare/wshfs"
"github.com/wavetermdev/waveterm/pkg/util/iochan/iochantypes"
"github.com/wavetermdev/waveterm/pkg/wshrpc"
"github.com/wavetermdev/waveterm/pkg/wshutil"
)
const (
ErrorParsingConnection = "error creating fileshare client, could not parse connection %s"
)
// CreateFileShareClient creates a fileshare client based on the connection string
// Returns the client and the parsed connection
func CreateFileShareClient(ctx context.Context, connection string) (fstype.FileShareClient, *connparse.Connection) {
conn, err := connparse.ParseURIAndReplaceCurrentHost(ctx, connection)
if err != nil {
log.Printf("error parsing connection: %v", err)
return nil, nil
}
conntype := conn.GetType()
if conntype == connparse.ConnectionTypeS3 {
config, err := awsconn.GetConfig(ctx, connection)
if err != nil {
log.Printf("error getting aws config: %v", err)
return nil, nil
}
return s3fs.NewS3Client(config), conn
} else if conntype == connparse.ConnectionTypeWave {
return wavefs.NewWaveClient(), conn
} else if conntype == connparse.ConnectionTypeWsh {
return wshfs.NewWshClient(), conn
} else {
log.Printf("unsupported connection type: %s", conntype)
return nil, nil
}
}
func Read(ctx context.Context, data wshrpc.FileData) (*wshrpc.FileData, error) {
log.Printf("Read: %v", data.Info.Path)
client, conn := CreateFileShareClient(ctx, data.Info.Path)
if conn == nil || client == nil {
return nil, fmt.Errorf(ErrorParsingConnection, data.Info.Path)
}
return client.Read(ctx, conn, data)
}
func ReadStream(ctx context.Context, data wshrpc.FileData) <-chan wshrpc.RespOrErrorUnion[wshrpc.FileData] {
log.Printf("ReadStream: %v", data.Info.Path)
client, conn := CreateFileShareClient(ctx, data.Info.Path)
if conn == nil || client == nil {
return wshutil.SendErrCh[wshrpc.FileData](fmt.Errorf(ErrorParsingConnection, data.Info.Path))
}
return client.ReadStream(ctx, conn, data)
}
func ReadTarStream(ctx context.Context, data wshrpc.CommandRemoteStreamTarData) <-chan wshrpc.RespOrErrorUnion[iochantypes.Packet] {
log.Printf("ReadTarStream: %v", data.Path)
client, conn := CreateFileShareClient(ctx, data.Path)
if conn == nil || client == nil {
return wshutil.SendErrCh[iochantypes.Packet](fmt.Errorf(ErrorParsingConnection, data.Path))
}
return client.ReadTarStream(ctx, conn, data.Opts)
}
func ListEntries(ctx context.Context, path string, opts *wshrpc.FileListOpts) ([]*wshrpc.FileInfo, error) {
log.Printf("ListEntries: %v", path)
client, conn := CreateFileShareClient(ctx, path)
if conn == nil || client == nil {
return nil, fmt.Errorf(ErrorParsingConnection, path)
}
return client.ListEntries(ctx, conn, opts)
}
func ListEntriesStream(ctx context.Context, path string, opts *wshrpc.FileListOpts) <-chan wshrpc.RespOrErrorUnion[wshrpc.CommandRemoteListEntriesRtnData] {
log.Printf("ListEntriesStream: %v", path)
client, conn := CreateFileShareClient(ctx, path)
if conn == nil || client == nil {
return wshutil.SendErrCh[wshrpc.CommandRemoteListEntriesRtnData](fmt.Errorf(ErrorParsingConnection, path))
}
return client.ListEntriesStream(ctx, conn, opts)
}
func Stat(ctx context.Context, path string) (*wshrpc.FileInfo, error) {
log.Printf("Stat: %v", path)
client, conn := CreateFileShareClient(ctx, path)
if conn == nil || client == nil {
return nil, fmt.Errorf(ErrorParsingConnection, path)
}
return client.Stat(ctx, conn)
}
func PutFile(ctx context.Context, data wshrpc.FileData) error {
log.Printf("PutFile: %v", data.Info.Path)
client, conn := CreateFileShareClient(ctx, data.Info.Path)
if conn == nil || client == nil {
return fmt.Errorf(ErrorParsingConnection, data.Info.Path)
}
return client.PutFile(ctx, conn, data)
}
func Mkdir(ctx context.Context, path string) error {
log.Printf("Mkdir: %v", path)
client, conn := CreateFileShareClient(ctx, path)
if conn == nil || client == nil {
return fmt.Errorf(ErrorParsingConnection, path)
}
return client.Mkdir(ctx, conn)
}
func Move(ctx context.Context, data wshrpc.CommandFileCopyData) error {
opts := data.Opts
if opts == nil {
opts = &wshrpc.FileCopyOpts{}
}
log.Printf("Move: srcuri: %v, desturi: %v, opts: %v", data.SrcUri, data.DestUri, opts)
srcClient, srcConn := CreateFileShareClient(ctx, data.SrcUri)
if srcConn == nil || srcClient == nil {
return fmt.Errorf("error creating fileshare client, could not parse source connection %s", data.SrcUri)
}
destClient, destConn := CreateFileShareClient(ctx, data.DestUri)
if destConn == nil || destClient == nil {
return fmt.Errorf("error creating fileshare client, could not parse destination connection %s", data.DestUri)
}
if srcConn.Host != destConn.Host {
isDir, err := destClient.CopyRemote(ctx, srcConn, destConn, srcClient, opts)
if err != nil {
return fmt.Errorf("cannot copy %q to %q: %w", data.SrcUri, data.DestUri, err)
}
return srcClient.Delete(ctx, srcConn, opts.Recursive && isDir)
} else {
return srcClient.MoveInternal(ctx, srcConn, destConn, opts)
}
}
func Copy(ctx context.Context, data wshrpc.CommandFileCopyData) error {
opts := data.Opts
if opts == nil {
opts = &wshrpc.FileCopyOpts{}
}
opts.Recursive = true
log.Printf("Copy: srcuri: %v, desturi: %v, opts: %v", data.SrcUri, data.DestUri, opts)
srcClient, srcConn := CreateFileShareClient(ctx, data.SrcUri)
if srcConn == nil || srcClient == nil {
return fmt.Errorf("error creating fileshare client, could not parse source connection %s", data.SrcUri)
}
destClient, destConn := CreateFileShareClient(ctx, data.DestUri)
if destConn == nil || destClient == nil {
return fmt.Errorf("error creating fileshare client, could not parse destination connection %s", data.DestUri)
}
if srcConn.Host != destConn.Host {
_, err := destClient.CopyRemote(ctx, srcConn, destConn, srcClient, opts)
return err
} else {
_, err := srcClient.CopyInternal(ctx, srcConn, destConn, opts)
return err
}
}
func Delete(ctx context.Context, data wshrpc.CommandDeleteFileData) error {
log.Printf("Delete: %v", data)
client, conn := CreateFileShareClient(ctx, data.Path)
if conn == nil || client == nil {
return fmt.Errorf(ErrorParsingConnection, data.Path)
}
return client.Delete(ctx, conn, data.Recursive)
}
func Join(ctx context.Context, path string, parts ...string) (*wshrpc.FileInfo, error) {
log.Printf("Join: %v", path)
client, conn := CreateFileShareClient(ctx, path)
if conn == nil || client == nil {
return nil, fmt.Errorf(ErrorParsingConnection, path)
}
return client.Join(ctx, conn, parts...)
}
func Append(ctx context.Context, data wshrpc.FileData) error {
log.Printf("Append: %v", data.Info.Path)
client, conn := CreateFileShareClient(ctx, data.Info.Path)
if conn == nil || client == nil {
return fmt.Errorf(ErrorParsingConnection, data.Info.Path)
}
return client.AppendFile(ctx, conn, data)
}
func GetCapability(ctx context.Context, path string) (wshrpc.FileShareCapability, error) {
log.Printf("GetCapability: %v", path)
client, conn := CreateFileShareClient(ctx, path)
if conn == nil || client == nil {
return wshrpc.FileShareCapability{}, fmt.Errorf(ErrorParsingConnection, path)
}
return client.GetCapability(), nil
}