Repository: AlistGo/alist (language: go)

Issue:
the SMB driver need remount periodically
### Please make sure of the following things

- [X] I have read the [documentation](https://alist.nn.ci).
- [X] I'm sure there are no duplicate issues or discussions.
- [X] I'm sure it's due to `alist` and not something else(such as `Dependencies` or `Operational`).
- [X] I'm sure I'm using the latest version

### Alist Version / Alist 版本

v3.2.1

### Driver used / 使用的存储驱动

SMB

### Describe the bug / 问题描述

After adding SMB storage for 8 hours, it fails to open files and gives the following error.

> failed get objs: failed to list objs: open : connection error: read tcp **********:55312->***********00:445: read: connection reset by peer

The error disappears once going to Manage-Storages and saving the storage settings again without changing anything.

### Reproduction / 复现链接

https://github.com/alist-org/alist/commit/dd4674e486c899c573711a447d54b2a1a98fdc1b

### Logs / 日志

_No response_


Discussion / hints:
Thanks for opening your first issue here! Be sure to follow the issue template!


Fix patch:
diff --git a/drivers/smb/driver.go b/drivers/smb/driver.go
index 4589d5a42a4..a11315d2af7 100644
--- a/drivers/smb/driver.go
+++ b/drivers/smb/driver.go
@@ -4,6 +4,7 @@ import (
 	"context"
 	"errors"
 	"path/filepath"
+	"time"
 
 	"github.com/alist-org/alist/v3/internal/driver"
 	"github.com/alist-org/alist/v3/internal/model"
@@ -15,7 +16,8 @@ import (
 type SMB struct {
 	model.Storage
 	Addition
-	fs *smb2.Share
+	fs           *smb2.Share
+	lastConnTime time.Time
 }
 
 func (d *SMB) Config() driver.Config {
@@ -43,11 +45,16 @@ func (d *SMB) Drop(ctx context.Context) error {
 }
 
 func (d *SMB) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]model.Obj, error) {
+	if err := d.checkConn(); err != nil {
+		return nil, err
+	}
 	fullPath := d.getSMBPath(dir)
 	rawFiles, err := d.fs.ReadDir(fullPath)
 	if err != nil {
+		d.cleanLastConnTime()
 		return nil, err
 	}
+	d.updateLastConnTime()
 	var files []model.Obj
 	for _, f := range rawFiles {
 		file := model.ObjThumb{
@@ -69,46 +76,69 @@ func (d *SMB) List(ctx context.Context, dir model.Obj, args model.ListArgs) ([]m
 //}
 
 func (d *SMB) Link(ctx context.Context, file model.Obj, args model.LinkArgs) (*model.Link, error) {
+	if err := d.checkConn(); err != nil {
+		return nil, err
+	}
 	fullPath := d.getSMBPath(file)
 	remoteFile, err := d.fs.Open(fullPath)
 	if err != nil {
+		d.cleanLastConnTime()
 		return nil, err
 	}
+	d.updateLastConnTime()
 	return &model.Link{
 		Data: remoteFile,
 	}, nil
 }
 
 func (d *SMB) MakeDir(ctx context.Context, parentDir model.Obj, dirName string) error {
+	if err := d.checkConn(); err != nil {
+		return err
+	}
 	fullPath := filepath.Join(d.getSMBPath(parentDir), dirName)
 	err := d.fs.MkdirAll(fullPath, 0700)
 	if err != nil {
+		d.cleanLastConnTime()
 		return err
 	}
+	d.updateLastConnTime()
 	return nil
 }
 
 func (d *SMB) Move(ctx context.Context, srcObj, dstDir model.Obj) error {
+	if err := d.checkConn(); err != nil {
+		return err
+	}
 	srcPath := d.getSMBPath(srcObj)
 	dstPath := filepath.Join(d.getSMBPath(dstDir), srcObj.GetName())
 	err := d.fs.Rename(srcPath, dstPath)
 	if err != nil {
+		d.cleanLastConnTime()
 		return err
 	}
+	d.updateLastConnTime()
 	return nil
 }
 
 func (d *SMB) Rename(ctx context.Context, srcObj model.Obj, newName string) error {
+	if err := d.checkConn(); err != nil {
+		return err
+	}
 	srcPath := d.getSMBPath(srcObj)
 	dstPath := filepath.Join(filepath.Dir(srcPath), newName)
 	err := d.fs.Rename(srcPath, dstPath)
 	if err != nil {
+		d.cleanLastConnTime()
 		return err
 	}
+	d.updateLastConnTime()
 	return nil
 }
 
 func (d *SMB) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
+	if err := d.checkConn(); err != nil {
+		return err
+	}
 	srcPath := d.getSMBPath(srcObj)
 	dstPath := filepath.Join(d.getSMBPath(dstDir), srcObj.GetName())
 	var err error
@@ -118,12 +148,17 @@ func (d *SMB) Copy(ctx context.Context, srcObj, dstDir model.Obj) error {
 		err = d.CopyFile(srcPath, dstPath)
 	}
 	if err != nil {
+		d.cleanLastConnTime()
 		return err
 	}
+	d.updateLastConnTime()
 	return nil
 }
 
 func (d *SMB) Remove(ctx context.Context, obj model.Obj) error {
+	if err := d.checkConn(); err != nil {
+		return err
+	}
 	var err error
 	fullPath := d.getSMBPath(obj)
 	if obj.IsDir() {
@@ -132,17 +167,24 @@ func (d *SMB) Remove(ctx context.Context, obj model.Obj) error {
 		err = d.fs.Remove(fullPath)
 	}
 	if err != nil {
+		d.cleanLastConnTime()
 		return err
 	}
+	d.updateLastConnTime()
 	return nil
 }
 
 func (d *SMB) Put(ctx context.Context, dstDir model.Obj, stream model.FileStreamer, up driver.UpdateProgress) error {
+	if err := d.checkConn(); err != nil {
+		return err
+	}
 	fullPath := filepath.Join(d.getSMBPath(dstDir), stream.GetName())
 	out, err := d.fs.Create(fullPath)
 	if err != nil {
+		d.cleanLastConnTime()
 		return err
 	}
+	d.updateLastConnTime()
 	defer func() {
 		_ = out.Close()
 		if errors.Is(err, context.Canceled) {
diff --git a/drivers/smb/util.go b/drivers/smb/util.go
index 144ec171e67..87c04009cf2 100644
--- a/drivers/smb/util.go
+++ b/drivers/smb/util.go
@@ -6,11 +6,20 @@ import (
 	"net"
 	"os"
 	"path/filepath"
+	"time"
 
 	"github.com/alist-org/alist/v3/internal/model"
 	"github.com/hirochachacha/go-smb2"
 )
 
+func (d *SMB) updateLastConnTime() {
+	d.lastConnTime = time.Now()
+}
+
+func (d *SMB) cleanLastConnTime() {
+	d.lastConnTime = time.Now().AddDate(0, 0, -1)
+}
+
 func (d *SMB) initFS() error {
 	conn, err := net.Dial("tcp", d.Address)
 	if err != nil {
@@ -30,9 +39,20 @@ func (d *SMB) initFS() error {
 	if err != nil {
 		return err
 	}
+	d.updateLastConnTime()
 	return err
 }
 
+func (d *SMB) checkConn() error {
+	if time.Since(d.lastConnTime) < 5*time.Minute {
+		return nil
+	}
+	if d.fs != nil {
+		_ = d.fs.Umount()
+	}
+	return d.initFS()
+}
+
 func (d *SMB) getSMBPath(dir model.Obj) string {
 	fullPath := dir.GetPath()
 	if fullPath[0:1] != "." {


Test patch: