From 0731de6a51c7c1e0e0aa852d1ab6683c068bf25b Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Mon, 9 Oct 2023 11:22:09 +0100 Subject: [PATCH] smb: fix "unverified packet returned" error when using multithread copy Before this change, in the WriterAt method we removed the session while the WriterAt handle was still open. This meant that in long lived multithread copies the session was closed which caused the "unverified packet returned" errors. This fixes the problem by wrapping it in a custom Close to close remove the session only when the WriterAt handle is closed. See: https://forum.rclone.org/t/problems-copying-big-files-from-local-to-remote-using-rclone-rc/42169/ --- backend/smb/smb.go | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/backend/smb/smb.go b/backend/smb/smb.go index 704c998f9..cbd06da5d 100644 --- a/backend/smb/smb.go +++ b/backend/smb/smb.go @@ -12,6 +12,7 @@ import ( "sync/atomic" "time" + smb2 "github.com/hirochachacha/go-smb2" "github.com/rclone/rclone/fs" "github.com/rclone/rclone/fs/config" "github.com/rclone/rclone/fs/config/configmap" @@ -476,6 +477,26 @@ func (f *Fs) About(ctx context.Context) (_ *fs.Usage, err error) { return usage, nil } +// Wrap a smb2.File with a custom Close method +type closeSession struct { + *smb2.File + close func() error + closed bool +} + +// Close the handle and call the custom code +func (c *closeSession) Close() error { + err := c.File.Close() + if !c.closed { + err2 := c.close() + if err == nil { + err = err2 + } + c.closed = true + } + return err +} + // OpenWriterAt opens with a handle for random access writes // // Pass in the remote desired and the size if known. @@ -509,10 +530,19 @@ func (f *Fs) OpenWriterAt(ctx context.Context, remote string, size int64) (fs.Wr fl, err := cn.smbShare.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o644) if err != nil { + o.fs.putConnection(&cn) return nil, fmt.Errorf("failed to open: %w", err) } - return fl, nil + // Connection is returned in the closeSession.Close method + c := &closeSession{ + File: fl, + close: func() error { + o.fs.putConnection(&cn) + return nil + }, + } + return c, nil } // Shutdown the backend, closing any background tasks and any