@@ -22,6 +22,7 @@ import (
22
22
"github.com/alist-org/alist/v3/pkg/utils"
23
23
"github.com/alist-org/alist/v3/server/common"
24
24
"github.com/alist-org/times"
25
+ cp "github.com/otiai10/copy"
25
26
log "github.com/sirupsen/logrus"
26
27
_ "golang.org/x/image/webp"
27
28
)
@@ -241,11 +242,22 @@ func (d *Local) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
241
242
if utils .IsSubPath (srcPath , dstPath ) {
242
243
return fmt .Errorf ("the destination folder is a subfolder of the source folder" )
243
244
}
244
- err := os .Rename (srcPath , dstPath )
245
- if err != nil {
245
+ if err := os .Rename (srcPath , dstPath ); err != nil && strings .Contains (err .Error (), "invalid cross-device link" ) {
246
+ // Handle cross-device file move in local driver
247
+ if err = d .Copy (ctx , srcObj , dstDir ); err != nil {
248
+ return err
249
+ } else {
250
+ // Directly remove file without check recycle bin if successfully copied
251
+ if srcObj .IsDir () {
252
+ err = os .RemoveAll (srcObj .GetPath ())
253
+ } else {
254
+ err = os .Remove (srcObj .GetPath ())
255
+ }
256
+ return err
257
+ }
258
+ } else {
246
259
return err
247
260
}
248
- return nil
249
261
}
250
262
251
263
func (d * Local ) Rename (ctx context.Context , srcObj model.Obj , newName string ) error {
@@ -258,22 +270,18 @@ func (d *Local) Rename(ctx context.Context, srcObj model.Obj, newName string) er
258
270
return nil
259
271
}
260
272
261
- func (d * Local ) Copy (ctx context.Context , srcObj , dstDir model.Obj ) error {
273
+ func (d * Local ) Copy (_ context.Context , srcObj , dstDir model.Obj ) error {
262
274
srcPath := srcObj .GetPath ()
263
275
dstPath := filepath .Join (dstDir .GetPath (), srcObj .GetName ())
264
276
if utils .IsSubPath (srcPath , dstPath ) {
265
277
return fmt .Errorf ("the destination folder is a subfolder of the source folder" )
266
278
}
267
- var err error
268
- if srcObj .IsDir () {
269
- err = utils .CopyDir (srcPath , dstPath )
270
- } else {
271
- err = utils .CopyFile (srcPath , dstPath )
272
- }
273
- if err != nil {
274
- return err
275
- }
276
- return nil
279
+ // Copy using otiai10/copy to perform more secure & efficient copy
280
+ return cp .Copy (srcPath , dstPath , cp.Options {
281
+ Sync : true , // Sync file to disk after copy, may have performance penalty in filesystem such as ZFS
282
+ PreserveTimes : true ,
283
+ NumOfWorkers : 0 , // Serialized copy without using goroutine
284
+ })
277
285
}
278
286
279
287
func (d * Local ) Remove (ctx context.Context , obj model.Obj ) error {
0 commit comments