Skip to content

Commit b0e3b6d

Browse files
authored
Fix move & copy for prefix filesystems (#1998)
Also makes recursive the default for copy, adds better error for move without recursive
1 parent 1f43020 commit b0e3b6d

File tree

15 files changed

+367
-425
lines changed

15 files changed

+367
-425
lines changed

frontend/app/store/wshclientapi.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ class RpcApiType {
293293
}
294294

295295
// command "remotefilecopy" [call]
296-
RemoteFileCopyCommand(client: WshClient, data: CommandFileCopyData, opts?: RpcOpts): Promise<void> {
296+
RemoteFileCopyCommand(client: WshClient, data: CommandFileCopyData, opts?: RpcOpts): Promise<boolean> {
297297
return client.wshRpcCall("remotefilecopy", data, opts);
298298
}
299299

frontend/app/view/preview/directorypreview.tsx

Lines changed: 123 additions & 182 deletions
Large diffs are not rendered by default.

frontend/app/view/preview/preview.tsx

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1229,18 +1229,22 @@ const ErrorOverlay = memo(({ errorMsg, resetOverlay }: { errorMsg: ErrorMsg; res
12291229
/>
12301230
<div>{errorMsg.text}</div>
12311231
</OverlayScrollbarsComponent>
1232-
{errorMsg.buttons?.map((buttonDef) => (
1233-
<Button
1234-
className={buttonClassName}
1235-
onClick={() => {
1236-
buttonDef.onClick();
1237-
resetOverlay();
1238-
}}
1239-
key={crypto.randomUUID()}
1240-
>
1241-
{buttonDef.text}
1242-
</Button>
1243-
))}
1232+
{!!errorMsg.buttons && (
1233+
<div className="flex flex-row gap-2">
1234+
{errorMsg.buttons?.map((buttonDef) => (
1235+
<Button
1236+
className={buttonClassName}
1237+
onClick={() => {
1238+
buttonDef.onClick();
1239+
resetOverlay();
1240+
}}
1241+
key={crypto.randomUUID()}
1242+
>
1243+
{buttonDef.text}
1244+
</Button>
1245+
))}
1246+
</div>
1247+
)}
12441248
</div>
12451249

12461250
{showDismiss && (

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ require (
3434
github.com/wavetermdev/htmltoken v0.2.0
3535
golang.org/x/crypto v0.33.0
3636
golang.org/x/mod v0.23.0
37+
golang.org/x/sync v0.11.0
3738
golang.org/x/sys v0.30.0
3839
golang.org/x/term v0.29.0
3940
google.golang.org/api v0.221.0
@@ -95,7 +96,6 @@ require (
9596
go.uber.org/atomic v1.7.0 // indirect
9697
golang.org/x/net v0.35.0 // indirect
9798
golang.org/x/oauth2 v0.26.0 // indirect
98-
golang.org/x/sync v0.11.0 // indirect
9999
golang.org/x/text v0.22.0 // indirect
100100
golang.org/x/time v0.10.0 // indirect
101101
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect

pkg/remote/fileshare/fileshare.go

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,11 @@ func Mkdir(ctx context.Context, path string) error {
119119
}
120120

121121
func Move(ctx context.Context, data wshrpc.CommandFileCopyData) error {
122-
log.Printf("Move: %v", data)
122+
opts := data.Opts
123+
if opts == nil {
124+
opts = &wshrpc.FileCopyOpts{}
125+
}
126+
log.Printf("Move: srcuri: %v, desturi: %v, opts: %v", data.SrcUri, data.DestUri, opts)
123127
srcClient, srcConn := CreateFileShareClient(ctx, data.SrcUri)
124128
if srcConn == nil || srcClient == nil {
125129
return fmt.Errorf("error creating fileshare client, could not parse source connection %s", data.SrcUri)
@@ -129,26 +133,23 @@ func Move(ctx context.Context, data wshrpc.CommandFileCopyData) error {
129133
return fmt.Errorf("error creating fileshare client, could not parse destination connection %s", data.DestUri)
130134
}
131135
if srcConn.Host != destConn.Host {
132-
finfo, err := srcClient.Stat(ctx, srcConn)
133-
if err != nil {
134-
return fmt.Errorf("cannot stat %q: %w", data.SrcUri, err)
135-
}
136-
recursive := data.Opts != nil && data.Opts.Recursive
137-
if finfo.IsDir && data.Opts != nil && !recursive {
138-
return fmt.Errorf("cannot move directory %q to %q without recursive flag", data.SrcUri, data.DestUri)
139-
}
140-
err = destClient.CopyRemote(ctx, srcConn, destConn, srcClient, data.Opts)
136+
isDir, err := destClient.CopyRemote(ctx, srcConn, destConn, srcClient, opts)
141137
if err != nil {
142138
return fmt.Errorf("cannot copy %q to %q: %w", data.SrcUri, data.DestUri, err)
143139
}
144-
return srcClient.Delete(ctx, srcConn, recursive)
140+
return srcClient.Delete(ctx, srcConn, opts.Recursive && isDir)
145141
} else {
146-
return srcClient.MoveInternal(ctx, srcConn, destConn, data.Opts)
142+
return srcClient.MoveInternal(ctx, srcConn, destConn, opts)
147143
}
148144
}
149145

150146
func Copy(ctx context.Context, data wshrpc.CommandFileCopyData) error {
151-
log.Printf("Copy: %v", data)
147+
opts := data.Opts
148+
if opts == nil {
149+
opts = &wshrpc.FileCopyOpts{}
150+
}
151+
opts.Recursive = true
152+
log.Printf("Copy: srcuri: %v, desturi: %v, opts: %v", data.SrcUri, data.DestUri, opts)
152153
srcClient, srcConn := CreateFileShareClient(ctx, data.SrcUri)
153154
if srcConn == nil || srcClient == nil {
154155
return fmt.Errorf("error creating fileshare client, could not parse source connection %s", data.SrcUri)
@@ -158,9 +159,11 @@ func Copy(ctx context.Context, data wshrpc.CommandFileCopyData) error {
158159
return fmt.Errorf("error creating fileshare client, could not parse destination connection %s", data.DestUri)
159160
}
160161
if srcConn.Host != destConn.Host {
161-
return destClient.CopyRemote(ctx, srcConn, destConn, srcClient, data.Opts)
162+
_, err := destClient.CopyRemote(ctx, srcConn, destConn, srcClient, opts)
163+
return err
162164
} else {
163-
return srcClient.CopyInternal(ctx, srcConn, destConn, data.Opts)
165+
_, err := srcClient.CopyInternal(ctx, srcConn, destConn, opts)
166+
return err
164167
}
165168
}
166169

pkg/remote/fileshare/fstype/fstype.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ import (
1414
)
1515

1616
const (
17-
DefaultTimeout = 30 * time.Second
18-
FileMode os.FileMode = 0644
19-
DirMode os.FileMode = 0755 | os.ModeDir
17+
DefaultTimeout = 30 * time.Second
18+
FileMode os.FileMode = 0644
19+
DirMode os.FileMode = 0755 | os.ModeDir
20+
RecursiveRequiredError = "recursive flag must be set for directory operations"
21+
MergeRequiredError = "directory already exists at %q, set overwrite flag to delete the existing contents or set merge flag to merge the contents"
22+
OverwriteRequiredError = "file already exists at %q, set overwrite flag to delete the existing file"
2023
)
2124

2225
type FileShareClient interface {
@@ -40,10 +43,10 @@ type FileShareClient interface {
4043
Mkdir(ctx context.Context, conn *connparse.Connection) error
4144
// Move moves the file within the same connection
4245
MoveInternal(ctx context.Context, srcConn, destConn *connparse.Connection, opts *wshrpc.FileCopyOpts) error
43-
// Copy copies the file within the same connection
44-
CopyInternal(ctx context.Context, srcConn, destConn *connparse.Connection, opts *wshrpc.FileCopyOpts) error
45-
// CopyRemote copies the file between different connections
46-
CopyRemote(ctx context.Context, srcConn, destConn *connparse.Connection, srcClient FileShareClient, opts *wshrpc.FileCopyOpts) error
46+
// Copy copies the file within the same connection. Returns whether the copy source was a directory
47+
CopyInternal(ctx context.Context, srcConn, destConn *connparse.Connection, opts *wshrpc.FileCopyOpts) (bool, error)
48+
// CopyRemote copies the file between different connections. Returns whether the copy source was a directory
49+
CopyRemote(ctx context.Context, srcConn, destConn *connparse.Connection, srcClient FileShareClient, opts *wshrpc.FileCopyOpts) (bool, error)
4750
// Delete deletes the entry at the given path
4851
Delete(ctx context.Context, conn *connparse.Connection, recursive bool) error
4952
// Join joins the given parts to the connection path

0 commit comments

Comments
 (0)