Skip to content

Fix move & copy for prefix filesystems #1998

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Feb 20, 2025
Merged

Conversation

esimkowitz
Copy link
Member

@esimkowitz esimkowitz commented Feb 19, 2025

Also makes recursive the default for copy, adds better error for move without recursive

Copy link
Contributor

coderabbitai bot commented Feb 19, 2025

Warning

Rate limit exceeded

@esimkowitz has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 16 minutes and 35 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between e557f3b and 3c8d8fe.

📒 Files selected for processing (14)
  • frontend/app/store/wshclientapi.ts (1 hunks)
  • frontend/app/view/preview/directorypreview.tsx (12 hunks)
  • frontend/app/view/preview/preview.tsx (1 hunks)
  • pkg/remote/fileshare/fileshare.go (3 hunks)
  • pkg/remote/fileshare/fstype/fstype.go (2 hunks)
  • pkg/remote/fileshare/fsutil/fsutil.go (5 hunks)
  • pkg/remote/fileshare/pathtree/pathtree.go (2 hunks)
  • pkg/remote/fileshare/s3fs/s3fs.go (6 hunks)
  • pkg/remote/fileshare/wavefs/wavefs.go (2 hunks)
  • pkg/remote/fileshare/wshfs/wshfs.go (1 hunks)
  • pkg/util/tarcopy/tarcopy.go (0 hunks)
  • pkg/wshrpc/wshclient/wshclient.go (1 hunks)
  • pkg/wshrpc/wshremote/wshremote.go (12 hunks)
  • pkg/wshrpc/wshrpctypes.go (2 hunks)

Walkthrough

This pull request introduces multiple updates across the codebase. In the frontend, the function signature for renaming items now includes an additional boolean flag to indicate if the item is a directory. This change affects the URI construction and context menu actions. In the go.mod file, the dependency for golang.org/x/sync has been moved from an indirect to a direct dependency. On the backend, functions handling file move and copy operations have been revised to improve type safety and error handling, with some methods now returning a boolean value in addition to an error. New error constants have been added, and several helper functions have been introduced to centralize path determination logic. Additional logging statements have been incorporated in the directory tree handling and remote file operations to enhance traceability during processing.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (7)
pkg/remote/fileshare/fsutil/fsutil.go (5)

61-62: Return value mismatch on directory listing failure
When directory listing fails, the code returns (false, error) even though srcInfo.IsDir == true. While this likely doesn’t impact error handling, consider using (true, error) to be consistent.


90-91: Clarify role of destPathPrefix
A comment explains this prefix for a potential directory destination. Ensure the prefix usage is consistent with actual directory semantics.


95-95: Appending trailing slash to destPathPrefix
Adding a slash is appropriate when interpreting the destination as a directory. Watch out for potential double slashes if the prefix already ends with one.


113-115: Single-file path adjustment
If singleFile is true, fileName becomes the exact destPath. This ensures a correct final name but double-check for trailing slash scenarios.


130-183: Comprehensive path determination logic
This function centralizes checks for merge, overwrite, and destination existence. Highlights include:

  • Immediate error if both merge and overwrite are set.
  • Recomputing destination path if the source is a directory or if an existing directory is detected.
  • Deleting an existing destination when overwrite is true.

All these steps are aligned with the updated copy semantics, though you might consider splitting the logic into smaller helpers for readability.

pkg/remote/fileshare/s3fs/s3fs.go (2)

210-210: Informative log statement for tar path prefix
Logging the scenarios where singleFile or includeDir toggles the tar path prefix is useful. Consider adopting a structured logger for clarity and filtering.


675-675: Delegating to fsutil.PrefixCopyRemote
Reusing PrefixCopyRemote centralizes copy logic. Verify any S3-specific constraints (like SSE encryption or region handling) are addressed before delegation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ce9775c and 4b7637f.

📒 Files selected for processing (13)
  • frontend/app/store/wshclientapi.ts (1 hunks)
  • frontend/app/view/preview/directorypreview.tsx (5 hunks)
  • go.mod (1 hunks)
  • pkg/remote/fileshare/fileshare.go (2 hunks)
  • pkg/remote/fileshare/fstype/fstype.go (1 hunks)
  • pkg/remote/fileshare/fsutil/fsutil.go (7 hunks)
  • pkg/remote/fileshare/pathtree/pathtree.go (3 hunks)
  • pkg/remote/fileshare/s3fs/s3fs.go (5 hunks)
  • pkg/remote/fileshare/wavefs/wavefs.go (2 hunks)
  • pkg/remote/fileshare/wshfs/wshfs.go (1 hunks)
  • pkg/wshrpc/wshclient/wshclient.go (1 hunks)
  • pkg/wshrpc/wshremote/wshremote.go (9 hunks)
  • pkg/wshrpc/wshrpctypes.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
pkg/remote/fileshare/wavefs/wavefs.go (1)
Learnt from: esimkowitz
PR: wavetermdev/waveterm#1725
File: pkg/remote/fileshare/wavefs/wavefs.go:441-494
Timestamp: 2025-01-29T04:21:11.649Z
Learning: The `CopyRemote` function in WaveTerm's file operations has proper error handling that prevents partial writes by failing fast and using context cancellation. Each step (path cleaning, file operations, tar reading/writing) is guarded by error checks that prevent proceeding with writes on error.
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: Build for TestDriver.ai
🔇 Additional comments (36)
pkg/remote/fileshare/fsutil/fsutil.go (10)

31-31: Clarify root parent path behavior
Returning an empty string for the root path might cause confusion when building parent paths. Confirm that your logic handles top-level paths correctly in all use cases.


43-43: Edge case handling for slash in path
Consider verifying the behavior when hostAndPath contains only a leading slash. If lastSlash is 0, slicing may yield an empty path, which could be unintended.


46-46: New return signature for PrefixCopyInternal
The function now returns a boolean flagging whether the source is a directory. This follows the updated contract, so ensure all callers properly handle the additional return.


49-52: Check error return path in DetermineCopyDestPath usage
If there's an error from DetermineCopyDestPath, the function emits (false, err). Verify that the (false, ...) part won’t cause downstream confusion when the source is actually a directory.


76-77: Properly indicating directory copy
Returning (true, ...) here correctly conveys that the source being copied is a directory. Good job matching the new interface requirements.


85-86: Return value for single-file copy
Returning (false, copyFunc(...)) clearly denotes a non-directory source. Just confirm that copyFunc applies any necessary overwrite or merge logic.


89-89: PrefixCopyRemote now returns (bool, error)
Returning a boolean to indicate directory status aligns with the updated interface.


93-93: Error handling for invalid destination path
Skipping the copy with (false, fmt.Errorf(...)) is a clear approach. Confirm logs or user feedback reflect that the path was invalid.


97-99: Bubbling up DetermineCopyDestPath error
Returning (false, err) is consistent with the contract. Make sure the caller can distinguish between errors versus a non-directory response if needed.


125-128: Final return clarifies directory status
The code returns (false, err) on failure and (srcInfo.IsDir, nil) otherwise, precisely reflecting the new interface requirement.

pkg/remote/fileshare/s3fs/s3fs.go (6)

293-293: Aggregated error handling
Using errors.Join(errs...) consolidates multiple concurrent errors for better debugging. This is a solid approach to surfacing parallel fetch issues.


648-653: MoveInternal: capturing directory status
Obtaining isDir from CopyInternal is necessary to correctly invoke Delete(..., isDir). Ensure you don’t attempt a delete when the copy fails.


656-656: CopyRemote signature updated to (bool, error)
The boolean return clarifies directory status in line with the revised interface. Confirm callers either use or safely ignore the returned boolean.


662-662: Destination validation in CopyRemote
Short-circuiting with (false, fmt.Errorf(...)) is consistent. Just ensure user-facing layers or logs clarify that the required bucket field was blank.


679-679: S3Client CopyInternal now returns (bool, error)
This supports indicating source directory status. Consider partial-copy scenarios, ensuring you handle them gracefully.


756-779: New listFilesPrefix pagination logic
The ListObjectsV2Paginator usage handles large S3 prefixes well. Make sure:

  • The loop terminates properly if fileCallback returns false.
  • Errors for missing or inaccessible buckets are propagated.
  • Partial listings and concurrency are considered in the overall flow.

Overall, a strong approach for fetching multiple pages.

pkg/remote/fileshare/fstype/fstype.go (1)

43-46: Interface updates: CopyInternal and CopyRemote now return directory status
Exposing a boolean to indicate if the source was a directory helps higher-level logic (e.g., subsequent delete operations). Great consistency with the rest of the PR.

pkg/remote/fileshare/pathtree/pathtree.go (4)

38-38: LGTM! Helpful warning message.

The added log statement provides clear visibility into path formatting issues.


64-64: LGTM! Useful debug information.

The log statement helps track path processing by showing the calculated relative path.


73-73: LGTM! Good duplicate path logging.

The log statement helps identify when paths are skipped due to already being in the tree.


81-81: LGTM! Comprehensive path processing logging.

The added log statements provide excellent visibility into the path processing workflow:

  1. Invalid path components are logged for debugging
  2. Path additions to existing parents are tracked
  3. New path additions are recorded

Also applies to: 88-88, 92-92

pkg/remote/fileshare/wshfs/wshfs.go (1)

117-119: LGTM! Enhanced return types for better operation feedback.

The methods now return a boolean indicating whether the source is a directory, which improves the handling of file operations by providing explicit information about the source type.

Also applies to: 121-130

pkg/remote/fileshare/fileshare.go (2)

132-136: LGTM! Improved directory handling in Move operation.

The code now correctly uses the source's directory status to determine whether to perform a recursive deletion, making the move operation more robust.


153-158: LGTM! Consistent error handling in Copy operation.

The code properly handles the enhanced return types from copy operations while maintaining clean error propagation.

frontend/app/store/wshclientapi.ts (1)

296-296: LGTM! Updated return type to match backend changes.

The RemoteFileCopyCommand's return type now correctly reflects the enhanced feedback from the backend regarding the source's directory status.

pkg/remote/fileshare/wavefs/wavefs.go (3)

421-433: LGTM! Clean implementation of the move operation.

The changes correctly propagate the directory status from CopyInternal to Delete, maintaining proper error handling throughout the operation.


435-468: LGTM! Well-structured implementation of the copy operation.

The function properly implements the new return type and maintains robust error handling through the fsutil.PrefixCopyInternal helper.


470-477: LGTM! Proper error handling in the remote copy operation.

The error handling is consistent with the retrieved learning about preventing partial writes and using proper error checks.

pkg/wshrpc/wshclient/wshclient.go (1)

359-362: LGTM! Clean implementation of the updated RPC client method.

The changes correctly implement the new return type while maintaining the existing RPC helper pattern.

pkg/wshrpc/wshremote/wshremote.go (3)

309-322: LGTM! Proper initialization and error handling.

The function maintains robust error handling while adding the new boolean return value.


420-432: LGTM! Clean implementation of directory status tracking.

The srcIsDir flag is properly initialized and updated based on the source file's status.


493-494: LGTM! Proper directory status handling in tar operations.

The srcIsDir flag is correctly set based on the singleFile parameter from the tar operation.

pkg/wshrpc/wshrpctypes.go (1)

231-231: LGTM! Clean interface update.

The interface method signature has been properly updated to include the boolean return value.

frontend/app/view/preview/directorypreview.tsx (3)

294-310: LGTM! Proper handling of directory paths.

The implementation correctly appends a trailing slash for directories, which is essential for prefix filesystems to properly handle directory moves.


861-861: LGTM! Improved type safety.

The explicit boolean type declaration for isDir enhances code clarity and type safety.


51-51:

❓ Verification inconclusive

LGTM! Verify implementations of the updated interface.

The addition of the isDir parameter helps distinguish between files and directories, which is crucial for proper path handling.

Run the following script to verify all implementations of the updateName method:


🏁 Script executed:

#!/bin/bash
# Description: Find all implementations of the updateName method to ensure they've been updated with the new signature.
rg -A 5 'updateName.*\(.*\).*{' --type ts --type tsx

Length of output: 160


Revised Verification Required – Please Re-run the Updated Check

The change to include the isDir parameter is correct and essential for distinguishing file types. However, the initial verification script produced a regex error. Please verify all implementations of updateName with the revised script below:

#!/bin/bash
# Revised regex search: Locate all updateName implementations using the updated signature.
rg "updateName\s*:\s*\(.*\)\s*=>" --type ts --type tsx

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
cmd/wsh/cmd/wshcmd-file.go (1)

106-108: ⚠️ Potential issue

Remove unused recursive flag from fileMvCmd.

The recursive flag is defined but not used in the implementation. According to the changes, recursive functionality has been removed from the move operation.

Apply this diff to remove the unused flag:

-fileMvCmd.Flags().BoolP("recursive", "r", false, "move directories recursively")
fileMvCmd.Flags().BoolP("force", "f", false, "force overwrite of existing files")
🧹 Nitpick comments (5)
pkg/wshrpc/wshremote/wshremote.go (5)

420-421: Consider initializing srcIsDir closer to its first use.

The srcIsDir variable is initialized at line 420 but not used until line 493. Consider moving the initialization closer to its first usage to improve code readability and maintain variable scope closer to its usage.

-	srcIsDir := false
-
 	if srcConn.Host == destConn.Host {
 		srcPathCleaned := filepath.Clean(wavebase.ExpandHomeDirSafe(srcConn.Path))

Also applies to: 493-493


476-479: Consider extracting timeout calculation to a helper function.

The timeout calculation logic appears in multiple places in the codebase. Consider extracting it to a helper function to improve code reusability and maintainability.

+func getTimeout(opts *wshrpc.FileCopyOpts) time.Duration {
+	if opts != nil && opts.Timeout > 0 {
+		return time.Duration(opts.Timeout) * time.Millisecond
+	}
+	return fstype.DefaultTimeout
+}
+
 func (impl *ServerImpl) RemoteFileCopyCommand(ctx context.Context, data wshrpc.CommandFileCopyData) (bool, error) {
   // ...
-		timeout := fstype.DefaultTimeout
-		if opts.Timeout > 0 {
-			timeout = time.Duration(opts.Timeout) * time.Millisecond
-		}
+		timeout := getTimeout(opts)

480-482: Consider using a defer for the timeout cancellation immediately after creation.

The timeout cancellation should be deferred right after the context creation to ensure proper cleanup.

 		readCtx, cancel := context.WithCancelCause(ctx)
 		readCtx, timeoutCancel := context.WithTimeoutCause(readCtx, timeout, fmt.Errorf("timeout copying file %q to %q", srcUri, destUri))
-		defer timeoutCancel()
+		defer func() {
+			timeoutCancel()
+			cancel()
+		}()
-		defer cancel()

489-505: Consider extracting the file copy callback to a separate method.

The anonymous function passed to TarCopyDest is complex enough to warrant its own method. This would improve readability and testability.

+func (impl *ServerImpl) handleTarCopyFile(destPathCleaned string, destHasSlash bool) func(*tar.Header, *tar.Reader, bool) error {
+	return func(next *tar.Header, reader *tar.Reader, singleFile bool) error {
+		numFiles++
+		nextpath := filepath.Join(destPathCleaned, next.Name)
+		log.Printf("RemoteFileCopyCommand: copying %q to %q\n", next.Name, nextpath)
+		srcIsDir = !singleFile
+		if singleFile && !destHasSlash {
+			nextpath = destPathCleaned
+		}
+		finfo := next.FileInfo()
+		n, err := copyFileFunc(nextpath, finfo, reader)
+		if err != nil {
+			return fmt.Errorf("cannot copy file %q: %w", next.Name, err)
+		}
+		totalBytes += n
+		return nil
+	}
+}

509-515: Consider using structured logging for better observability.

The performance metrics logging could benefit from structured logging to make it easier to parse and analyze the logs.

-		log.Printf("RemoteFileCopyCommand: done; %d files copied in %.3fs, total of %.4f MB, %.2f MB/s, %d files skipped\n", numFiles, totalTime, totalMegaBytes, rate, numSkipped)
+		log.Printf("RemoteFileCopyCommand: done; files_copied=%d duration=%.3fs total_mb=%.4f rate_mbps=%.2f files_skipped=%d\n", 
+			numFiles, totalTime, totalMegaBytes, rate, numSkipped)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b7637f and 82d2bb3.

📒 Files selected for processing (4)
  • cmd/wsh/cmd/wshcmd-file.go (1 hunks)
  • frontend/types/gotypes.d.ts (0 hunks)
  • pkg/wshrpc/wshremote/wshremote.go (9 hunks)
  • pkg/wshrpc/wshrpctypes.go (2 hunks)
💤 Files with no reviewable changes (1)
  • frontend/types/gotypes.d.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/wshrpc/wshrpctypes.go
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Analyze (go)
  • GitHub Check: Build for TestDriver.ai
🔇 Additional comments (2)
cmd/wsh/cmd/wshcmd-file.go (1)

434-436: LGTM! Clean implementation of the move operation.

The changes correctly implement the move operation without recursive functionality while maintaining proper error handling and force flag support.

pkg/wshrpc/wshremote/wshremote.go (1)

309-309: LGTM! Method signature change enhances functionality.

The return type change from error to (bool, error) is a good improvement, providing explicit information about the source file type.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
pkg/remote/fileshare/fsutil/fsutil.go (2)

95-99: Consider renaming variable for clarity.

The variable destPathPrefix could be renamed to destDirPath to better reflect its purpose as the path when copying to a directory.


134-185: Consider improving error messages and breaking down the function.

While the implementation is solid, consider these improvements:

  1. Make error messages more descriptive, e.g., include the conflicting options' values.
  2. Break out the destination path determination logic (lines 159-171) into a separate function for better maintainability.

Otherwise, great job centralizing the path determination logic with comprehensive error handling!

Example error message improvement:

-		return "", "", nil, fmt.Errorf("cannot specify both overwrite and merge")
+		return "", "", nil, fmt.Errorf("conflicting options: cannot specify both overwrite=%v and merge=%v", overwrite, merge)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 82d2bb3 and e557f3b.

📒 Files selected for processing (8)
  • frontend/app/view/preview/directorypreview.tsx (5 hunks)
  • pkg/remote/fileshare/fileshare.go (3 hunks)
  • pkg/remote/fileshare/fstype/fstype.go (2 hunks)
  • pkg/remote/fileshare/fsutil/fsutil.go (5 hunks)
  • pkg/remote/fileshare/s3fs/s3fs.go (6 hunks)
  • pkg/remote/fileshare/wavefs/wavefs.go (1 hunks)
  • pkg/wshrpc/wshremote/wshremote.go (4 hunks)
  • pkg/wshrpc/wshrpctypes.go (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • pkg/wshrpc/wshrpctypes.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • pkg/remote/fileshare/fileshare.go
  • pkg/remote/fileshare/fstype/fstype.go
  • pkg/remote/fileshare/wavefs/wavefs.go
  • pkg/remote/fileshare/s3fs/s3fs.go
  • pkg/wshrpc/wshremote/wshremote.go
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Analyze (javascript-typescript)
  • GitHub Check: Analyze (go)
  • GitHub Check: Build for TestDriver.ai
🔇 Additional comments (8)
frontend/app/view/preview/directorypreview.tsx (3)

49-55: LGTM! Interface change improves type safety.

The addition of the isDir parameter to updateName method signature enhances type safety by explicitly declaring whether the path refers to a directory.


294-331: LGTM! Implementation correctly handles directory URIs.

The function now properly handles directory paths by appending a trailing slash to the source URI when moving directories, which is essential for prefix filesystem operations.


861-884: LGTM! Enhanced error handling for copy operations.

The error handling has been improved to:

  1. Distinguish between file and directory copy failures
  2. Provide appropriate retry options based on the error type
  3. Show clear error messages to users
pkg/remote/fileshare/fsutil/fsutil.go (5)

29-44: LGTM! Improved path handling consistency.

The changes make the function more consistent by returning empty string for both empty input and root path cases.


53-58: LGTM! Improved error handling for directory operations.

The addition of the recursive flag check with a specific error message improves the user experience when attempting to copy directories.


188-191: LGTM! Improved empty path handling.

The change improves consistency by treating empty path as a valid case returning an empty string.


46-52:

✅ Verification successful

Verify function signature.

The AI summary indicates this function should return (bool, error), but the implementation returns only error. Please verify the correct signature.


🏁 Script executed:

#!/bin/bash
# Search for any other implementations or interfaces requiring this function
rg "PrefixCopyInternal.*\(" --type go

Length of output: 852


Function signature verified and improvements approved.
After reviewing the code and usage in wavefs and s3fs, it's clear that the actual implementation of PrefixCopyInternal returns only an error—which is aligned with how the function is invoked elsewhere. The AI summary's mention of a (bool, error) return appears to be outdated or incorrect. The improvements made (including centralized path determination and enhanced error handling) are therefore validated.

  • File: pkg/remote/fileshare/fsutil/fsutil.go (Lines 46-52)
  • Action: No further changes required regarding the function signature.

93-104:

✅ Verification successful

Verify function signature.

The AI summary indicates this function should return (bool, error), but the implementation returns only error. Please verify the correct signature.


🏁 Script executed:

#!/bin/bash
# Search for any other implementations or interfaces requiring this function
rg "PrefixCopyRemote.*\(" --type go

Length of output: 660


Function Signature Verified & Variable Naming Suggestion

  • Verified that the repository consistently uses the signature returning only an error (as seen in fsutil.go, wavefs.go, and s3fs.go). The AI summary suggesting a (bool, error) return is outdated and should be updated.
  • Consider renaming destPathPrefix to something more descriptive if it clarifies its role (e.g., dirPathPrefix).

if !overwrite {
return 0, fmt.Errorf(fstype.OverwriteCopyError, path)
} else if finfo.IsDir() {
err := os.RemoveAll(path)

Check failure

Code scanning / CodeQL

Uncontrolled data used in path expression High

This path depends on a
user-provided value
.

Copilot Autofix

AI 4 months ago

To fix the problem, we need to validate the user input before using it to construct a file path. Specifically, we should ensure that the path does not contain any path separators or parent directory references if it is expected to be a single file name. If the input can be a path with multiple components, we should verify that the path is within a specific directory that is considered safe.

In this case, we will implement a validation function to ensure that the path is within a specific safe directory. We will then use this function to validate the path variable before performing any file operations.

Suggested changeset 1
pkg/wshrpc/wshremote/wshremote.go

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/pkg/wshrpc/wshremote/wshremote.go b/pkg/wshrpc/wshremote/wshremote.go
--- a/pkg/wshrpc/wshremote/wshremote.go
+++ b/pkg/wshrpc/wshremote/wshremote.go
@@ -354,5 +354,12 @@
 	copyFileFunc := func(path string, finfo fs.FileInfo, srcFile io.Reader) (int64, error) {
-		nextinfo, err := os.Stat(path)
+		// Validate the path
+		safeDir := "/home/user/safe_directory"
+		absPath, err := filepath.Abs(filepath.Join(safeDir, path))
+		if err != nil || !strings.HasPrefix(absPath, safeDir) {
+			return 0, fmt.Errorf("invalid file path: %q", path)
+		}
+
+		nextinfo, err := os.Stat(absPath)
 		if err != nil && !errors.Is(err, fs.ErrNotExist) {
-			return 0, fmt.Errorf("cannot stat file %q: %w", path, err)
+			return 0, fmt.Errorf("cannot stat file %q: %w", absPath, err)
 		}
@@ -363,17 +370,17 @@
 					// try to create file in directory
-					path = filepath.Join(path, filepath.Base(finfo.Name()))
-					newdestinfo, err := os.Stat(path)
+					absPath = filepath.Join(absPath, filepath.Base(finfo.Name()))
+					newdestinfo, err := os.Stat(absPath)
 					if err != nil && !errors.Is(err, fs.ErrNotExist) {
-						return 0, fmt.Errorf("cannot stat file %q: %w", path, err)
+						return 0, fmt.Errorf("cannot stat file %q: %w", absPath, err)
 					}
 					if newdestinfo != nil && !overwrite {
-						return 0, fmt.Errorf(fstype.OverwriteRequiredError, path)
+						return 0, fmt.Errorf(fstype.OverwriteRequiredError, absPath)
 					}
 				} else if overwrite {
-					err := os.RemoveAll(path)
+					err := os.RemoveAll(absPath)
 					if err != nil {
-						return 0, fmt.Errorf("cannot remove directory %q: %w", path, err)
+						return 0, fmt.Errorf("cannot remove directory %q: %w", absPath, err)
 					}
 				} else if !merge {
-					return 0, fmt.Errorf(fstype.MergeRequiredError, path)
+					return 0, fmt.Errorf(fstype.MergeRequiredError, absPath)
 				}
@@ -381,7 +388,7 @@
 				if !overwrite {
-					return 0, fmt.Errorf(fstype.OverwriteRequiredError, path)
+					return 0, fmt.Errorf(fstype.OverwriteRequiredError, absPath)
 				} else if finfo.IsDir() {
-					err := os.RemoveAll(path)
+					err := os.RemoveAll(absPath)
 					if err != nil {
-						return 0, fmt.Errorf("cannot remove directory %q: %w", path, err)
+						return 0, fmt.Errorf("cannot remove directory %q: %w", absPath, err)
 					}
@@ -392,5 +399,5 @@
 		if finfo.IsDir() {
-			err := os.MkdirAll(path, finfo.Mode())
+			err := os.MkdirAll(absPath, finfo.Mode())
 			if err != nil {
-				return 0, fmt.Errorf("cannot create directory %q: %w", path, err)
+				return 0, fmt.Errorf("cannot create directory %q: %w", absPath, err)
 			}
@@ -398,5 +405,5 @@
 		} else {
-			err := os.MkdirAll(filepath.Dir(path), 0755)
+			err := os.MkdirAll(filepath.Dir(absPath), 0755)
 			if err != nil {
-				return 0, fmt.Errorf("cannot create parent directory %q: %w", filepath.Dir(path), err)
+				return 0, fmt.Errorf("cannot create parent directory %q: %w", filepath.Dir(absPath), err)
 			}
@@ -404,10 +411,10 @@
 
-		file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, finfo.Mode())
+		file, err := os.OpenFile(absPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, finfo.Mode())
 		if err != nil {
-			return 0, fmt.Errorf("cannot create new file %q: %w", path, err)
+			return 0, fmt.Errorf("cannot create new file %q: %w", absPath, err)
 		}
-		defer utilfn.GracefulClose(file, "RemoteFileCopyCommand", path)
+		defer utilfn.GracefulClose(file, "RemoteFileCopyCommand", absPath)
 		_, err = io.Copy(file, srcFile)
 		if err != nil {
-			return 0, fmt.Errorf("cannot write file %q: %w", path, err)
+			return 0, fmt.Errorf("cannot write file %q: %w", absPath, err)
 		}
EOF
Copilot is powered by AI and may make mistakes. Always verify output.
Unable to commit as this autofix suggestion is now outdated
@esimkowitz esimkowitz merged commit b0e3b6d into main Feb 20, 2025
7 of 8 checks passed
@esimkowitz esimkowitz deleted the evan/prefix-copy-move-fix branch February 20, 2025 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant