Skip to content

Commit ca401b9

Browse files
authored
fix(local): assign non-CoW copy requests to the task module (OpenListTeam#1669)
* fix(local): assign non-CoW copy requests to the task module * fix build * fix cross device
1 parent addce8b commit ca401b9

File tree

8 files changed

+126
-24
lines changed

8 files changed

+126
-24
lines changed

drivers/local/copy_namedpipes.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//go:build !windows && !plan9 && !netbsd && !aix && !illumos && !solaris && !js
2+
3+
package local
4+
5+
import (
6+
"os"
7+
"path/filepath"
8+
"syscall"
9+
)
10+
11+
func copyNamedPipe(dstPath string, mode os.FileMode, dirMode os.FileMode) error {
12+
if err := os.MkdirAll(filepath.Dir(dstPath), dirMode); err != nil {
13+
return err
14+
}
15+
return syscall.Mkfifo(dstPath, uint32(mode))
16+
}

drivers/local/copy_namedpipes_x.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//go:build windows || plan9 || netbsd || aix || illumos || solaris || js
2+
3+
package local
4+
5+
import "os"
6+
7+
func copyNamedPipe(_ string, _, _ os.FileMode) error {
8+
return nil
9+
}

drivers/local/driver.go

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
2424
"github.com/OpenListTeam/OpenList/v4/server/common"
2525
"github.com/OpenListTeam/times"
26-
cp "github.com/otiai10/copy"
2726
log "github.com/sirupsen/logrus"
2827
_ "golang.org/x/image/webp"
2928
)
@@ -297,16 +296,9 @@ func (d *Local) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
297296
return fmt.Errorf("the destination folder is a subfolder of the source folder")
298297
}
299298
err := os.Rename(srcPath, dstPath)
300-
if err != nil && strings.Contains(err.Error(), "invalid cross-device link") {
301-
// 跨设备移动,先复制再删除
302-
if err := d.Copy(ctx, srcObj, dstDir); err != nil {
303-
return err
304-
}
305-
// 复制成功后直接删除源文件/文件夹
306-
if srcObj.IsDir() {
307-
return os.RemoveAll(srcObj.GetPath())
308-
}
309-
return os.Remove(srcObj.GetPath())
299+
if isCrossDeviceError(err) {
300+
// 跨设备移动,变更为移动任务
301+
return errs.NotImplement
310302
}
311303
if err == nil {
312304
srcParent := filepath.Dir(srcPath)
@@ -347,15 +339,14 @@ func (d *Local) Copy(_ context.Context, srcObj, dstDir model.Obj) error {
347339
if utils.IsSubPath(srcPath, dstPath) {
348340
return fmt.Errorf("the destination folder is a subfolder of the source folder")
349341
}
350-
// Copy using otiai10/copy to perform more secure & efficient copy
351-
err := cp.Copy(srcPath, dstPath, cp.Options{
352-
Sync: true, // Sync file to disk after copy, may have performance penalty in filesystem such as ZFS
353-
PreserveTimes: true,
354-
PreserveOwner: true,
355-
})
342+
info, err := os.Lstat(srcPath)
356343
if err != nil {
357344
return err
358345
}
346+
// 复制regular文件会返回errs.NotImplement, 转为复制任务
347+
if err = d.tryCopy(srcPath, dstPath, info); err != nil {
348+
return err
349+
}
359350

360351
if d.directoryMap.Has(filepath.Dir(dstPath)) {
361352
d.directoryMap.UpdateDirSize(filepath.Dir(dstPath))

drivers/local/util.go

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package local
33
import (
44
"bytes"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"io/fs"
89
"os"
@@ -14,7 +15,9 @@ import (
1415
"strings"
1516
"sync"
1617

18+
"github.com/KarpelesLab/reflink"
1719
"github.com/OpenListTeam/OpenList/v4/internal/conf"
20+
"github.com/OpenListTeam/OpenList/v4/internal/errs"
1821
"github.com/OpenListTeam/OpenList/v4/internal/model"
1922
"github.com/OpenListTeam/OpenList/v4/pkg/utils"
2023
"github.com/disintegration/imaging"
@@ -148,7 +151,7 @@ func (d *Local) getThumb(file model.Obj) (*bytes.Buffer, *string, error) {
148151
return nil, nil, err
149152
}
150153
if d.ThumbCacheFolder != "" {
151-
err = os.WriteFile(filepath.Join(d.ThumbCacheFolder, thumbName), buf.Bytes(), 0666)
154+
err = os.WriteFile(filepath.Join(d.ThumbCacheFolder, thumbName), buf.Bytes(), 0o666)
152155
if err != nil {
153156
return nil, nil, err
154157
}
@@ -405,3 +408,79 @@ func (m *DirectoryMap) DeleteDirNode(dirname string) error {
405408

406409
return nil
407410
}
411+
412+
func (d *Local) tryCopy(srcPath, dstPath string, info os.FileInfo) error {
413+
if info.Mode()&os.ModeDevice != 0 {
414+
return errors.New("cannot copy a device")
415+
} else if info.Mode()&os.ModeSymlink != 0 {
416+
return d.copySymlink(srcPath, dstPath)
417+
} else if info.Mode()&os.ModeNamedPipe != 0 {
418+
return copyNamedPipe(dstPath, info.Mode(), os.FileMode(d.mkdirPerm))
419+
} else if info.IsDir() {
420+
return d.recurAndTryCopy(srcPath, dstPath)
421+
} else {
422+
return tryReflinkCopy(srcPath, dstPath)
423+
}
424+
}
425+
426+
func (d *Local) copySymlink(srcPath, dstPath string) error {
427+
linkOrig, err := os.Readlink(srcPath)
428+
if err != nil {
429+
return err
430+
}
431+
dstDir := filepath.Dir(dstPath)
432+
if !filepath.IsAbs(linkOrig) {
433+
srcDir := filepath.Dir(srcPath)
434+
rel, err := filepath.Rel(dstDir, srcDir)
435+
if err != nil {
436+
rel, err = filepath.Abs(srcDir)
437+
}
438+
if err != nil {
439+
return err
440+
}
441+
linkOrig = filepath.Clean(filepath.Join(rel, linkOrig))
442+
}
443+
err = os.MkdirAll(dstDir, os.FileMode(d.mkdirPerm))
444+
if err != nil {
445+
return err
446+
}
447+
return os.Symlink(linkOrig, dstPath)
448+
}
449+
450+
func (d *Local) recurAndTryCopy(srcPath, dstPath string) error {
451+
err := os.MkdirAll(dstPath, os.FileMode(d.mkdirPerm))
452+
if err != nil {
453+
return err
454+
}
455+
files, err := readDir(srcPath)
456+
if err != nil {
457+
return err
458+
}
459+
for _, f := range files {
460+
if !f.IsDir() {
461+
sp := filepath.Join(srcPath, f.Name())
462+
dp := filepath.Join(dstPath, f.Name())
463+
if err = d.tryCopy(sp, dp, f); err != nil {
464+
return err
465+
}
466+
}
467+
}
468+
for _, f := range files {
469+
if f.IsDir() {
470+
sp := filepath.Join(srcPath, f.Name())
471+
dp := filepath.Join(dstPath, f.Name())
472+
if err = d.recurAndTryCopy(sp, dp); err != nil {
473+
return err
474+
}
475+
}
476+
}
477+
return nil
478+
}
479+
480+
func tryReflinkCopy(srcPath, dstPath string) error {
481+
err := reflink.Always(srcPath, dstPath)
482+
if errors.Is(err, reflink.ErrReflinkUnsupported) || errors.Is(err, reflink.ErrReflinkFailed) || isCrossDeviceError(err) {
483+
return errs.NotImplement
484+
}
485+
return err
486+
}

drivers/local/util_unix.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
package local
44

55
import (
6+
"errors"
67
"io/fs"
78
"strings"
89
"syscall"
910

1011
"github.com/OpenListTeam/OpenList/v4/internal/model"
12+
"golang.org/x/sys/unix"
1113
)
1214

1315
func isHidden(f fs.FileInfo, _ string) bool {
@@ -27,3 +29,7 @@ func getDiskUsage(path string) (model.DiskUsage, error) {
2729
FreeSpace: free,
2830
}, nil
2931
}
32+
33+
func isCrossDeviceError(err error) bool {
34+
return errors.Is(err, unix.EXDEV)
35+
}

drivers/local/util_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,7 @@ func getDiskUsage(path string) (model.DiskUsage, error) {
4949
FreeSpace: freeBytes,
5050
}, nil
5151
}
52+
53+
func isCrossDeviceError(err error) bool {
54+
return errors.Is(err, windows.ERROR_NOT_SAME_DEVICE)
55+
}

go.mod

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.23.4
55
require (
66
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.1
77
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.2
8+
github.com/KarpelesLab/reflink v1.0.2
89
github.com/KirCute/zip v1.0.1
910
github.com/OpenListTeam/go-cache v0.1.0
1011
github.com/OpenListTeam/sftpd-openlist v1.0.1
@@ -114,7 +115,6 @@ require (
114115
github.com/minio/minlz v1.0.0 // indirect
115116
github.com/minio/xxml v0.0.3 // indirect
116117
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
117-
github.com/otiai10/mint v1.6.3 // indirect
118118
github.com/quic-go/qpack v0.5.1 // indirect
119119
github.com/relvacode/iso8601 v1.6.0 // indirect
120120
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
@@ -256,7 +256,6 @@ require (
256256
github.com/multiformats/go-multihash v0.2.3 // indirect
257257
github.com/multiformats/go-multistream v0.4.1 // indirect
258258
github.com/multiformats/go-varint v0.0.7 // indirect
259-
github.com/otiai10/copy v1.14.1
260259
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
261260
github.com/pierrec/lz4/v4 v4.1.22 // indirect
262261
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect

go.sum

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
3939
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
4040
github.com/Da3zKi7/saferith v0.33.0-fixed h1:fnIWTk7EP9mZAICf7aQjeoAwpfrlCrkOvqmi6CbWdTk=
4141
github.com/Da3zKi7/saferith v0.33.0-fixed/go.mod h1:QKJhjoqUtBsXCAVEjw38mFqoi7DebT7kthcD7UzbnoA=
42+
github.com/KarpelesLab/reflink v1.0.2 h1:hQ1aM3TmjU2kTNUx5p/HaobDoADYk+a6AuEinG4Cv88=
43+
github.com/KarpelesLab/reflink v1.0.2/go.mod h1:WGkTOKNjd1FsJKBw3mu4JvrPEDJyJJ+JPtxBkbPoCok=
4244
github.com/KirCute/zip v1.0.1 h1:L/tVZglOiDVKDi9Ud+fN49htgKdQ3Z0H80iX8OZk13c=
4345
github.com/KirCute/zip v1.0.1/go.mod h1:xhF7dCB+Bjvy+5a56lenYCKBsH+gxDNPZSy5Cp+nlXk=
4446
github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7YgDP83g=
@@ -585,10 +587,6 @@ github.com/ncw/swift/v2 v2.0.4 h1:hHWVFxn5/YaTWAASmn4qyq2p6OyP/Hm3vMLzkjEqR7w=
585587
github.com/ncw/swift/v2 v2.0.4/go.mod h1:cbAO76/ZwcFrFlHdXPjaqWZ9R7Hdar7HpjRXBfbjigk=
586588
github.com/nwaples/rardecode/v2 v2.1.1 h1:OJaYalXdliBUXPmC8CZGQ7oZDxzX1/5mQmgn0/GASew=
587589
github.com/nwaples/rardecode/v2 v2.1.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
588-
github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
589-
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
590-
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
591-
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
592590
github.com/panjf2000/ants/v2 v2.4.2/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
593591
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
594592
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=

0 commit comments

Comments
 (0)