Skip to content

Commit 3a42de3

Browse files
Replace os.Rename with io.Copy for cross filesystem transfers (#77)
* Updated selfupdate to use os.Copy(), which allows copying across filesystems * os.copy doesn't exist, io.copy does. * Updated to keep file permissions and delete the temporary file. * Removed permission synchronization. * Do it right --------- Co-authored-by: Quinten <67589015+QuintenQVD0@users.noreply.github.com>
1 parent 3209d1a commit 3a42de3

1 file changed

Lines changed: 40 additions & 1 deletion

File tree

cmd/selfupdate.go

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,46 @@ func performUpdate(version, binaryName string) error {
118118
return fmt.Errorf("failed to locate current executable: %v", err)
119119
}
120120

121-
return os.Rename(binaryPath, currentExecutable)
121+
// Try rename first (faster if on same filesystem)
122+
err = os.Rename(binaryPath, currentExecutable)
123+
if err != nil {
124+
// If rename fails (likely due to cross-filesystem), use copy instead
125+
fmt.Println("Direct replacement failed, using copy method...")
126+
127+
// Open source file
128+
src, err := os.Open(binaryPath)
129+
if err != nil {
130+
return fmt.Errorf("failed to open source file: %v", err)
131+
}
132+
defer src.Close()
133+
134+
// Create a temporary file in the same directory as the executable
135+
execDir := filepath.Dir(currentExecutable)
136+
tempExec := filepath.Join(execDir, fmt.Sprintf(".%s.new", filepath.Base(currentExecutable)))
137+
138+
// Create the new executable file
139+
dst, err := os.OpenFile(tempExec, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
140+
if err != nil {
141+
return fmt.Errorf("failed to create new executable: %v", err)
142+
}
143+
144+
// Copy the content
145+
_, err = io.Copy(dst, src)
146+
dst.Close()
147+
if err != nil {
148+
os.Remove(tempExec) // Clean up on failure
149+
return fmt.Errorf("failed to copy new binary: %v", err)
150+
}
151+
152+
// Replace the old executable with the new one
153+
err = os.Rename(tempExec, currentExecutable)
154+
if err != nil {
155+
os.Remove(tempExec) // Clean up on failure
156+
return fmt.Errorf("failed to replace executable: %v", err)
157+
}
158+
}
159+
160+
return nil
122161
}
123162

124163
func downloadWithProgress(url, dest string) error {

0 commit comments

Comments
 (0)