Skip to content

Commit 74e64d3

Browse files
committed
fix: maybe "invalid cross-device link" in update ui
#2270
1 parent 0c556bc commit 74e64d3

File tree

1 file changed

+56
-1
lines changed

1 file changed

+56
-1
lines changed

component/updater/update_ui.go

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import (
55
"archive/zip"
66
"bytes"
77
"compress/gzip"
8+
"errors"
89
"fmt"
910
"io"
1011
"os"
1112
"path"
1213
"path/filepath"
1314
"strings"
1415
"sync"
16+
"syscall"
1517

1618
C "github.com/metacubex/mihomo/constant"
1719
"github.com/metacubex/mihomo/log"
@@ -308,14 +310,67 @@ func moveDir(src string, dst string) error {
308310
}
309311

310312
for _, dirEntry := range dirEntryList {
311-
err = os.Rename(filepath.Join(src, dirEntry.Name()), filepath.Join(dst, dirEntry.Name()))
313+
srcDir := filepath.Join(src, dirEntry.Name())
314+
dstDir := filepath.Join(dst, dirEntry.Name())
315+
err = os.Rename(srcDir, dstDir)
316+
if err != nil {
317+
// Fallback for invalid cross-device link (errno:18).
318+
if errors.Is(err, syscall.Errno(18)) {
319+
err = copyDir(srcDir, dstDir)
320+
_ = os.RemoveAll(srcDir)
321+
}
322+
}
312323
if err != nil {
313324
return err
314325
}
315326
}
316327
return nil
317328
}
318329

330+
// copyDir copy the src directory to dst
331+
// modify from [os.CopyFS]
332+
func copyDir(src, dst string) error {
333+
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
334+
if err != nil {
335+
return err
336+
}
337+
fpath, err := filepath.Rel(src, path)
338+
if err != nil {
339+
return err
340+
}
341+
newPath := filepath.Join(dst, fpath)
342+
343+
switch info.Mode().Type() {
344+
case os.ModeDir:
345+
return os.MkdirAll(newPath, info.Mode().Perm())
346+
case os.ModeSymlink:
347+
target, err := os.Readlink(path)
348+
if err != nil {
349+
return err
350+
}
351+
return os.Symlink(target, newPath)
352+
case 0:
353+
r, err := os.Open(path)
354+
if err != nil {
355+
return err
356+
}
357+
defer r.Close()
358+
w, err := os.OpenFile(newPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, info.Mode().Perm())
359+
if err != nil {
360+
return err
361+
}
362+
363+
if _, err := io.Copy(w, r); err != nil {
364+
w.Close()
365+
return &os.PathError{Op: "Copy", Path: newPath, Err: err}
366+
}
367+
return w.Close()
368+
default:
369+
return &os.PathError{Op: "CopyFS", Path: path, Err: os.ErrInvalid}
370+
}
371+
})
372+
}
373+
319374
func inDest(fpath, dest string) bool {
320375
if rel, err := filepath.Rel(dest, fpath); err == nil {
321376
if filepath.IsLocal(rel) {

0 commit comments

Comments
 (0)