99 "encoding/base64"
1010 "fmt"
1111 "io"
12- "io/fs"
1312 "log"
1413 "os"
1514 "path"
@@ -31,8 +30,7 @@ const (
3130 WaveFileScheme = "wavefile"
3231 WaveFilePrefix = "wavefile://"
3332
34- DefaultFileTimeout = 5000
35- TimeoutYear = int64 (365 ) * 24 * 60 * 60 * 1000
33+ TimeoutYear = int64 (365 ) * 24 * 60 * 60 * 1000
3634
3735 UriHelpText = `
3836
@@ -83,12 +81,12 @@ Wave Terminal is capable of managing files from remote SSH hosts, S3-compatible
8381systems, and the internal Wave filesystem. Files are addressed via URIs, which
8482vary depending on the storage system.` + UriHelpText }
8583
86- var fileTimeout int
84+ var fileTimeout int64
8785
8886func init () {
8987 rootCmd .AddCommand (fileCmd )
9088
91- fileCmd .PersistentFlags ().IntVarP (& fileTimeout , "timeout" , "t" , 15000 , "timeout in milliseconds for long operations" )
89+ fileCmd .PersistentFlags ().Int64VarP (& fileTimeout , "timeout" , "t" , 15000 , "timeout in milliseconds for long operations" )
9290
9391 fileListCmd .Flags ().BoolP ("recursive" , "r" , false , "list subdirectories recursively" )
9492 fileListCmd .Flags ().BoolP ("long" , "l" , false , "use long listing format" )
@@ -103,7 +101,6 @@ func init() {
103101 fileCmd .AddCommand (fileInfoCmd )
104102 fileCmd .AddCommand (fileAppendCmd )
105103 fileCpCmd .Flags ().BoolP ("merge" , "m" , false , "merge directories" )
106- fileCpCmd .Flags ().BoolP ("recursive" , "r" , false , "copy directories recursively" )
107104 fileCpCmd .Flags ().BoolP ("force" , "f" , false , "force overwrite of existing files" )
108105 fileCmd .AddCommand (fileCpCmd )
109106 fileMvCmd .Flags ().BoolP ("recursive" , "r" , false , "move directories recursively" )
@@ -174,7 +171,7 @@ var fileAppendCmd = &cobra.Command{
174171var fileCpCmd = & cobra.Command {
175172 Use : "cp [source-uri] [destination-uri]" + UriHelpText ,
176173 Aliases : []string {"copy" },
177- Short : "copy files between storage systems" ,
174+ Short : "copy files between storage systems, recursively if needed " ,
178175 Long : "Copy files between different storage systems." + UriHelpText ,
179176 Example : " wsh file cp wavefile://block/config.txt ./local-config.txt\n wsh file cp ./local-config.txt wavefile://block/config.txt\n wsh file cp wsh://user@ec2/home/user/config.txt wavefile://client/config.txt" ,
180177 Args : cobra .ExactArgs (2 ),
@@ -202,17 +199,7 @@ func fileCatRun(cmd *cobra.Command, args []string) error {
202199 Info : & wshrpc.FileInfo {
203200 Path : path }}
204201
205- // Get file info first to check existence and get size
206- info , err := wshclient .FileInfoCommand (RpcClient , fileData , & wshrpc.RpcOpts {Timeout : 2000 })
207- err = convertNotFoundErr (err )
208- if err == fs .ErrNotExist {
209- return fmt .Errorf ("%s: no such file" , path )
210- }
211- if err != nil {
212- return fmt .Errorf ("getting file info: %w" , err )
213- }
214-
215- err = streamReadFromFile (fileData , info .Size , os .Stdout )
202+ err = streamReadFromFile (cmd .Context (), fileData , os .Stdout )
216203 if err != nil {
217204 return fmt .Errorf ("reading file: %w" , err )
218205 }
@@ -229,7 +216,7 @@ func fileInfoRun(cmd *cobra.Command, args []string) error {
229216 Info : & wshrpc.FileInfo {
230217 Path : path }}
231218
232- info , err := wshclient .FileInfoCommand (RpcClient , fileData , & wshrpc.RpcOpts {Timeout : DefaultFileTimeout })
219+ info , err := wshclient .FileInfoCommand (RpcClient , fileData , & wshrpc.RpcOpts {Timeout : fileTimeout })
233220 err = convertNotFoundErr (err )
234221 if err != nil {
235222 return fmt .Errorf ("getting file info: %w" , err )
@@ -265,20 +252,8 @@ func fileRmRun(cmd *cobra.Command, args []string) error {
265252 if err != nil {
266253 return err
267254 }
268- fileData := wshrpc.FileData {
269- Info : & wshrpc.FileInfo {
270- Path : path }}
271-
272- _ , err = wshclient .FileInfoCommand (RpcClient , fileData , & wshrpc.RpcOpts {Timeout : DefaultFileTimeout })
273- err = convertNotFoundErr (err )
274- if err == fs .ErrNotExist {
275- return fmt .Errorf ("%s: no such file" , path )
276- }
277- if err != nil {
278- return fmt .Errorf ("getting file info: %w" , err )
279- }
280255
281- err = wshclient .FileDeleteCommand (RpcClient , wshrpc.CommandDeleteFileData {Path : path , Recursive : recursive }, & wshrpc.RpcOpts {Timeout : DefaultFileTimeout })
256+ err = wshclient .FileDeleteCommand (RpcClient , wshrpc.CommandDeleteFileData {Path : path , Recursive : recursive }, & wshrpc.RpcOpts {Timeout : fileTimeout })
282257 if err != nil {
283258 return fmt .Errorf ("removing file: %w" , err )
284259 }
@@ -295,14 +270,31 @@ func fileWriteRun(cmd *cobra.Command, args []string) error {
295270 Info : & wshrpc.FileInfo {
296271 Path : path }}
297272
298- _ , err = ensureFile ( path , fileData )
273+ capability , err := wshclient . FileShareCapabilityCommand ( RpcClient , fileData . Info . Path , & wshrpc. RpcOpts { Timeout : fileTimeout } )
299274 if err != nil {
300- return err
275+ return fmt . Errorf ( "getting fileshare capability: %w" , err )
301276 }
302-
303- err = streamWriteToFile (fileData , WrappedStdin )
304- if err != nil {
305- return fmt .Errorf ("writing file: %w" , err )
277+ if capability .CanAppend {
278+ err = streamWriteToFile (fileData , WrappedStdin )
279+ if err != nil {
280+ return fmt .Errorf ("writing file: %w" , err )
281+ }
282+ } else {
283+ buf := make ([]byte , MaxFileSize )
284+ n , err := WrappedStdin .Read (buf )
285+ if err != nil && err != io .EOF {
286+ return fmt .Errorf ("reading input: %w" , err )
287+ }
288+ if int64 (n ) == MaxFileSize {
289+ if _ , err := WrappedStdin .Read (make ([]byte , 1 )); err != io .EOF {
290+ return fmt .Errorf ("input exceeds maximum file size of %d bytes" , MaxFileSize )
291+ }
292+ }
293+ fileData .Data64 = base64 .StdEncoding .EncodeToString (buf [:n ])
294+ err = wshclient .FileWriteCommand (RpcClient , fileData , & wshrpc.RpcOpts {Timeout : fileTimeout })
295+ if err != nil {
296+ return fmt .Errorf ("writing file: %w" , err )
297+ }
306298 }
307299
308300 return nil
@@ -317,7 +309,7 @@ func fileAppendRun(cmd *cobra.Command, args []string) error {
317309 Info : & wshrpc.FileInfo {
318310 Path : path }}
319311
320- info , err := ensureFile (path , fileData )
312+ info , err := ensureFile (fileData )
321313 if err != nil {
322314 return err
323315 }
@@ -346,7 +338,7 @@ func fileAppendRun(cmd *cobra.Command, args []string) error {
346338
347339 if buf .Len () >= 8192 { // 8KB batch size
348340 fileData .Data64 = base64 .StdEncoding .EncodeToString (buf .Bytes ())
349- err = wshclient .FileAppendCommand (RpcClient , fileData , & wshrpc.RpcOpts {Timeout : int64 ( fileTimeout ) })
341+ err = wshclient .FileAppendCommand (RpcClient , fileData , & wshrpc.RpcOpts {Timeout : fileTimeout })
350342 if err != nil {
351343 return fmt .Errorf ("appending to file: %w" , err )
352344 }
@@ -357,7 +349,7 @@ func fileAppendRun(cmd *cobra.Command, args []string) error {
357349
358350 if buf .Len () > 0 {
359351 fileData .Data64 = base64 .StdEncoding .EncodeToString (buf .Bytes ())
360- err = wshclient .FileAppendCommand (RpcClient , fileData , & wshrpc.RpcOpts {Timeout : int64 ( fileTimeout ) })
352+ err = wshclient .FileAppendCommand (RpcClient , fileData , & wshrpc.RpcOpts {Timeout : fileTimeout })
361353 if err != nil {
362354 return fmt .Errorf ("appending to file: %w" , err )
363355 }
@@ -398,10 +390,6 @@ func getTargetPath(src, dst string) (string, error) {
398390
399391func fileCpRun (cmd * cobra.Command , args []string ) error {
400392 src , dst := args [0 ], args [1 ]
401- recursive , err := cmd .Flags ().GetBool ("recursive" )
402- if err != nil {
403- return err
404- }
405393 merge , err := cmd .Flags ().GetBool ("merge" )
406394 if err != nil {
407395 return err
@@ -419,9 +407,9 @@ func fileCpRun(cmd *cobra.Command, args []string) error {
419407 if err != nil {
420408 return fmt .Errorf ("unable to parse dest path: %w" , err )
421409 }
422- log .Printf ("Copying %s to %s; recursive: %v, merge: %v, force: %v" , srcPath , destPath , recursive , merge , force )
410+ log .Printf ("Copying %s to %s; merge: %v, force: %v" , srcPath , destPath , merge , force )
423411 rpcOpts := & wshrpc.RpcOpts {Timeout : TimeoutYear }
424- err = wshclient .FileCopyCommand (RpcClient , wshrpc.CommandFileCopyData {SrcUri : srcPath , DestUri : destPath , Opts : & wshrpc.FileCopyOpts {Recursive : recursive , Merge : merge , Overwrite : force , Timeout : TimeoutYear }}, rpcOpts )
412+ err = wshclient .FileCopyCommand (RpcClient , wshrpc.CommandFileCopyData {SrcUri : srcPath , DestUri : destPath , Opts : & wshrpc.FileCopyOpts {Merge : merge , Overwrite : force , Timeout : TimeoutYear }}, rpcOpts )
425413 if err != nil {
426414 return fmt .Errorf ("copying file: %w" , err )
427415 }
@@ -449,7 +437,7 @@ func fileMvRun(cmd *cobra.Command, args []string) error {
449437 }
450438 log .Printf ("Moving %s to %s; recursive: %v, force: %v" , srcPath , destPath , recursive , force )
451439 rpcOpts := & wshrpc.RpcOpts {Timeout : TimeoutYear }
452- err = wshclient .FileMoveCommand (RpcClient , wshrpc.CommandFileCopyData {SrcUri : srcPath , DestUri : destPath , Opts : & wshrpc.FileCopyOpts {Recursive : recursive , Overwrite : force , Timeout : TimeoutYear }}, rpcOpts )
440+ err = wshclient .FileMoveCommand (RpcClient , wshrpc.CommandFileCopyData {SrcUri : srcPath , DestUri : destPath , Opts : & wshrpc.FileCopyOpts {Overwrite : force , Timeout : TimeoutYear , Recursive : recursive }}, rpcOpts )
453441 if err != nil {
454442 return fmt .Errorf ("moving file: %w" , err )
455443 }
@@ -562,10 +550,7 @@ func fileListRun(cmd *cobra.Command, args []string) error {
562550
563551 filesChan := wshclient .FileListStreamCommand (RpcClient , wshrpc.FileListData {Path : path , Opts : & wshrpc.FileListOpts {All : recursive }}, & wshrpc.RpcOpts {Timeout : 2000 })
564552 // Drain the channel when done
565- defer func () {
566- for range filesChan {
567- }
568- }()
553+ defer utilfn .DrainChannelSafe (filesChan , "fileListRun" )
569554 if longForm {
570555 return filePrintLong (filesChan )
571556 }
0 commit comments