|
4 | 4 | package wavefs
|
5 | 5 |
|
6 | 6 | import (
|
| 7 | + "archive/tar" |
7 | 8 | "context"
|
8 | 9 | "encoding/base64"
|
9 | 10 | "errors"
|
10 | 11 | "fmt"
|
11 | 12 | "io/fs"
|
12 | 13 | "path"
|
13 | 14 | "strings"
|
| 15 | + "time" |
14 | 16 |
|
15 | 17 | "github.com/wavetermdev/waveterm/pkg/filestore"
|
16 | 18 | "github.com/wavetermdev/waveterm/pkg/remote/connparse"
|
17 | 19 | "github.com/wavetermdev/waveterm/pkg/remote/fileshare/fstype"
|
| 20 | + "github.com/wavetermdev/waveterm/pkg/util/fileutil" |
18 | 21 | "github.com/wavetermdev/waveterm/pkg/util/iochan/iochantypes"
|
| 22 | + "github.com/wavetermdev/waveterm/pkg/util/tarcopy" |
19 | 23 | "github.com/wavetermdev/waveterm/pkg/util/wavefileutil"
|
20 | 24 | "github.com/wavetermdev/waveterm/pkg/waveobj"
|
21 | 25 | "github.com/wavetermdev/waveterm/pkg/wps"
|
@@ -97,7 +101,54 @@ func (c WaveClient) Read(ctx context.Context, conn *connparse.Connection, data w
|
97 | 101 | }
|
98 | 102 |
|
99 | 103 | func (c WaveClient) ReadTarStream(ctx context.Context, conn *connparse.Connection, opts *wshrpc.FileCopyOpts) <-chan wshrpc.RespOrErrorUnion[iochantypes.Packet] {
|
100 |
| - return nil |
| 104 | + pathPrefix, err := cleanPath(conn.Path) |
| 105 | + if err != nil { |
| 106 | + return wshutil.SendErrCh[iochantypes.Packet](fmt.Errorf("error cleaning path: %w", err)) |
| 107 | + } |
| 108 | + list, err := c.ListEntries(ctx, conn, nil) |
| 109 | + if err != nil { |
| 110 | + return wshutil.SendErrCh[iochantypes.Packet](fmt.Errorf("error listing blockfiles: %w", err)) |
| 111 | + } |
| 112 | + |
| 113 | + timeout := time.Millisecond * 100 |
| 114 | + if opts.Timeout > 0 { |
| 115 | + timeout = time.Duration(opts.Timeout) * time.Millisecond |
| 116 | + } |
| 117 | + readerCtx, cancel := context.WithTimeout(context.Background(), timeout) |
| 118 | + rtn, writeHeader, fileWriter, tarClose := tarcopy.TarCopySrc(readerCtx, wshrpc.FileChunkSize, pathPrefix) |
| 119 | + |
| 120 | + go func() { |
| 121 | + defer func() { |
| 122 | + tarClose() |
| 123 | + cancel() |
| 124 | + }() |
| 125 | + for _, file := range list { |
| 126 | + if readerCtx.Err() != nil { |
| 127 | + rtn <- wshutil.RespErr[iochantypes.Packet](readerCtx.Err()) |
| 128 | + return |
| 129 | + } |
| 130 | + |
| 131 | + if err = writeHeader(fileutil.ToFsFileInfo(file), file.Path); err != nil { |
| 132 | + rtn <- wshutil.RespErr[iochantypes.Packet](fmt.Errorf("error writing tar header: %w", err)) |
| 133 | + return |
| 134 | + } |
| 135 | + if file.IsDir { |
| 136 | + continue |
| 137 | + } |
| 138 | + |
| 139 | + _, dataBuf, err := filestore.WFS.ReadFile(ctx, conn.Host, file.Path) |
| 140 | + if err != nil { |
| 141 | + rtn <- wshutil.RespErr[iochantypes.Packet](fmt.Errorf("error reading blockfile: %w", err)) |
| 142 | + return |
| 143 | + } |
| 144 | + if _, err = fileWriter.Write(dataBuf); err != nil { |
| 145 | + rtn <- wshutil.RespErr[iochantypes.Packet](fmt.Errorf("error writing tar data: %w", err)) |
| 146 | + return |
| 147 | + } |
| 148 | + } |
| 149 | + }() |
| 150 | + |
| 151 | + return rtn |
101 | 152 | }
|
102 | 153 |
|
103 | 154 | func (c WaveClient) ListEntriesStream(ctx context.Context, conn *connparse.Connection, opts *wshrpc.FileListOpts) <-chan wshrpc.RespOrErrorUnion[wshrpc.CommandRemoteListEntriesRtnData] {
|
@@ -377,6 +428,50 @@ func (c WaveClient) CopyInternal(ctx context.Context, srcConn, destConn *connpar
|
377 | 428 | }
|
378 | 429 |
|
379 | 430 | func (c WaveClient) CopyRemote(ctx context.Context, srcConn, destConn *connparse.Connection, srcClient fstype.FileShareClient, opts *wshrpc.FileCopyOpts) error {
|
| 431 | + zoneId := destConn.Host |
| 432 | + if zoneId == "" { |
| 433 | + return fmt.Errorf("zoneid not found in connection") |
| 434 | + } |
| 435 | + readCtx, cancel := context.WithCancelCause(ctx) |
| 436 | + ioch := srcClient.ReadTarStream(readCtx, srcConn, opts) |
| 437 | + err := tarcopy.TarCopyDest(readCtx, cancel, ioch, func(next *tar.Header, reader *tar.Reader) error { |
| 438 | + if next.Typeflag == tar.TypeDir { |
| 439 | + return nil |
| 440 | + } |
| 441 | + fileName, err := cleanPath(path.Join(destConn.Path, next.Name)) |
| 442 | + _, err = filestore.WFS.Stat(ctx, zoneId, fileName) |
| 443 | + if err != nil { |
| 444 | + if !errors.Is(err, fs.ErrNotExist) { |
| 445 | + return fmt.Errorf("error getting blockfile info: %w", err) |
| 446 | + } |
| 447 | + err := filestore.WFS.MakeFile(ctx, zoneId, fileName, nil, wshrpc.FileOpts{}) |
| 448 | + if err != nil { |
| 449 | + return fmt.Errorf("error making blockfile: %w", err) |
| 450 | + } |
| 451 | + } |
| 452 | + dataBuf := make([]byte, next.Size) |
| 453 | + n, err := reader.Read(dataBuf) |
| 454 | + if err != nil { |
| 455 | + return fmt.Errorf("error reading tar data: %w", err) |
| 456 | + } |
| 457 | + err = filestore.WFS.WriteFile(ctx, zoneId, fileName, dataBuf[:n]) |
| 458 | + if err != nil { |
| 459 | + return fmt.Errorf("error writing to blockfile: %w", err) |
| 460 | + } |
| 461 | + wps.Broker.Publish(wps.WaveEvent{ |
| 462 | + Event: wps.Event_BlockFile, |
| 463 | + Scopes: []string{waveobj.MakeORef(waveobj.OType_Block, zoneId).String()}, |
| 464 | + Data: &wps.WSFileEventData{ |
| 465 | + ZoneId: zoneId, |
| 466 | + FileName: fileName, |
| 467 | + FileOp: wps.FileOp_Invalidate, |
| 468 | + }, |
| 469 | + }) |
| 470 | + return nil |
| 471 | + }) |
| 472 | + if err != nil { |
| 473 | + return fmt.Errorf("error copying tar stream: %w", err) |
| 474 | + } |
380 | 475 | return nil
|
381 | 476 | }
|
382 | 477 |
|
|
0 commit comments