Selaa lähdekoodia

:bug: Notebook data may be corrupted during data synchronization https://github.com/siyuan-note/siyuan/issues/9594

Daniel 1 vuosi sitten
vanhempi
commit
70c56c8e25

+ 2 - 3
kernel/api/asset.go

@@ -18,7 +18,6 @@ package api
 
 import (
 	"net/http"
-	"os"
 	"path/filepath"
 	"strings"
 
@@ -153,12 +152,12 @@ func getFileAnnotation(c *gin.Context) {
 		ret.Data = map[string]interface{}{"closeTimeout": 5000}
 		return
 	}
-	if !gulu.File.IsExist(readPath) {
+	if !filelock.IsExist(readPath) {
 		ret.Code = 1
 		return
 	}
 
-	data, err := os.ReadFile(readPath)
+	data, err := filelock.ReadFile(readPath)
 	if nil != err {
 		ret.Code = -1
 		ret.Msg = err.Error()

+ 4 - 11
kernel/api/file.go

@@ -204,28 +204,21 @@ func renameFile(c *gin.Context) {
 
 	filePath := arg["path"].(string)
 	filePath = filepath.Join(util.WorkspaceDir, filePath)
-	_, err := os.Stat(filePath)
-	if os.IsNotExist(err) {
+	if !filelock.IsExist(filePath) {
 		ret.Code = 404
-		ret.Msg = err.Error()
-		return
-	}
-	if nil != err {
-		logging.LogErrorf("stat [%s] failed: %s", filePath, err)
-		ret.Code = 500
-		ret.Msg = err.Error()
+		ret.Msg = "the [path] file or directory does not exist"
 		return
 	}
 
 	newPath := arg["newPath"].(string)
 	newPath = filepath.Join(util.WorkspaceDir, newPath)
-	if gulu.File.IsExist(newPath) {
+	if filelock.IsExist(newPath) {
 		ret.Code = 409
 		ret.Msg = "the [newPath] file or directory already exists"
 		return
 	}
 
-	if err = filelock.Rename(filePath, newPath); nil != err {
+	if err := filelock.Rename(filePath, newPath); nil != err {
 		logging.LogErrorf("rename file [%s] to [%s] failed: %s", filePath, newPath, err)
 		ret.Code = 500
 		ret.Msg = err.Error()

+ 1 - 1
kernel/av/av.go

@@ -586,7 +586,7 @@ func NewAttributeView(id string) (ret *AttributeView) {
 
 func ParseAttributeView(avID string) (ret *AttributeView, err error) {
 	avJSONPath := GetAttributeViewDataPath(avID)
-	if !gulu.File.IsExist(avJSONPath) {
+	if !filelock.IsExist(avJSONPath) {
 		err = ErrViewNotFound
 		return
 	}

+ 4 - 4
kernel/av/mirror.go

@@ -21,7 +21,7 @@ func GetMirrorBlockIDs(avID string) []string {
 	defer attributeViewBlocksLock.Unlock()
 
 	blocks := filepath.Join(util.DataDir, "storage", "av", "blocks.msgpack")
-	if !gulu.File.IsExist(blocks) {
+	if !filelock.IsExist(blocks) {
 		return nil
 	}
 
@@ -46,7 +46,7 @@ func IsMirror(avID string) bool {
 	defer attributeViewBlocksLock.Unlock()
 
 	blocks := filepath.Join(util.DataDir, "storage", "av", "blocks.msgpack")
-	if !gulu.File.IsExist(blocks) {
+	if !filelock.IsExist(blocks) {
 		return false
 	}
 
@@ -71,7 +71,7 @@ func RemoveBlockRel(avID, blockID string) {
 	defer attributeViewBlocksLock.Unlock()
 
 	blocks := filepath.Join(util.DataDir, "storage", "av", "blocks.msgpack")
-	if !gulu.File.IsExist(blocks) {
+	if !filelock.IsExist(blocks) {
 		return
 	}
 
@@ -117,7 +117,7 @@ func UpsertBlockRel(avID, blockID string) {
 
 	avBlocks := map[string][]string{}
 	blocks := filepath.Join(util.DataDir, "storage", "av", "blocks.msgpack")
-	if !gulu.File.IsExist(blocks) {
+	if !filelock.IsExist(blocks) {
 		if err := os.MkdirAll(filepath.Dir(blocks), 0755); nil != err {
 			logging.LogErrorf("create attribute view dir failed: %s", err)
 			return

+ 6 - 6
kernel/bazaar/package.go

@@ -241,11 +241,11 @@ func getPreferredFunding(funding *Funding) string {
 
 func PluginJSON(pluginDirName string) (ret *Plugin, err error) {
 	p := filepath.Join(util.DataDir, "plugins", pluginDirName, "plugin.json")
-	if !gulu.File.IsExist(p) {
+	if !filelock.IsExist(p) {
 		err = os.ErrNotExist
 		return
 	}
-	data, err := os.ReadFile(p)
+	data, err := filelock.ReadFile(p)
 	if nil != err {
 		logging.LogErrorf("read plugin.json [%s] failed: %s", p, err)
 		return
@@ -261,11 +261,11 @@ func PluginJSON(pluginDirName string) (ret *Plugin, err error) {
 
 func WidgetJSON(widgetDirName string) (ret *Widget, err error) {
 	p := filepath.Join(util.DataDir, "widgets", widgetDirName, "widget.json")
-	if !gulu.File.IsExist(p) {
+	if !filelock.IsExist(p) {
 		err = os.ErrNotExist
 		return
 	}
-	data, err := os.ReadFile(p)
+	data, err := filelock.ReadFile(p)
 	if nil != err {
 		logging.LogErrorf("read widget.json [%s] failed: %s", p, err)
 		return
@@ -301,11 +301,11 @@ func IconJSON(iconDirName string) (ret *Icon, err error) {
 
 func TemplateJSON(templateDirName string) (ret *Template, err error) {
 	p := filepath.Join(util.DataDir, "templates", templateDirName, "template.json")
-	if !gulu.File.IsExist(p) {
+	if !filelock.IsExist(p) {
 		err = os.ErrNotExist
 		return
 	}
-	data, err := os.ReadFile(p)
+	data, err := filelock.ReadFile(p)
 	if nil != err {
 		logging.LogErrorf("read template.json [%s] failed: %s", p, err)
 		return

+ 5 - 5
kernel/go.mod

@@ -50,19 +50,19 @@ require (
 	github.com/siyuan-note/dejavu v0.0.0-20231031012159-d985a65825cf
 	github.com/siyuan-note/encryption v0.0.0-20220713091850-5ecd92177b75
 	github.com/siyuan-note/eventbus v0.0.0-20230804030110-cf250f838c80
-	github.com/siyuan-note/filelock v0.0.0-20231030092426-0ff158f5f8ca
+	github.com/siyuan-note/filelock v0.0.0-20231106131407-1cd9e9a9230c
 	github.com/siyuan-note/httpclient v0.0.0-20231028070852-3c2c5a151c13
 	github.com/siyuan-note/logging v0.0.0-20231030034701-8265764f00ff
-	github.com/siyuan-note/riff v0.0.0-20230928143458-ffd178c7e0fc
+	github.com/siyuan-note/riff v0.0.0-20231106140614-c67ff0c69e33
 	github.com/steambap/captcha v1.4.1
 	github.com/studio-b12/gowebdav v0.9.0
-	github.com/vmihailenco/msgpack/v5 v5.4.0
+	github.com/vmihailenco/msgpack/v5 v5.4.1
 	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673
 	github.com/xuri/excelize/v2 v2.8.0
 	golang.org/x/image v0.12.0
 	golang.org/x/mobile v0.0.0-20230901161150-52620a4a7557
 	golang.org/x/mod v0.13.0
-	golang.org/x/text v0.13.0
+	golang.org/x/text v0.14.0
 )
 
 require (
@@ -158,7 +158,7 @@ require (
 	golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
 	golang.org/x/net v0.17.0 // indirect
 	golang.org/x/sync v0.4.0 // indirect
-	golang.org/x/sys v0.13.0 // indirect
+	golang.org/x/sys v0.14.0 // indirect
 	golang.org/x/tools v0.14.0 // indirect
 	google.golang.org/protobuf v1.31.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect

+ 10 - 9
kernel/go.sum

@@ -344,14 +344,14 @@ github.com/siyuan-note/encryption v0.0.0-20220713091850-5ecd92177b75 h1:Bi7/7f29
 github.com/siyuan-note/encryption v0.0.0-20220713091850-5ecd92177b75/go.mod h1:H8fyqqAbp9XreANjeSbc72zEdFfKTXYN34tc1TjZwtw=
 github.com/siyuan-note/eventbus v0.0.0-20230804030110-cf250f838c80 h1:XghjHKJd+SiL0DkGYFVC+UGUDFtnR4v9gkAbPeh9Eq8=
 github.com/siyuan-note/eventbus v0.0.0-20230804030110-cf250f838c80/go.mod h1:Sqo4FYX5lAXu7gWkbEdJF0e6P57tNNVV4WDKYDctokI=
-github.com/siyuan-note/filelock v0.0.0-20231030092426-0ff158f5f8ca h1:e0+lU1gxFXV+SOjuce4H2QH9VNfAibXbPyuU+dlC4wk=
-github.com/siyuan-note/filelock v0.0.0-20231030092426-0ff158f5f8ca/go.mod h1:H735iw2ycTmG31yNWu4/zEzFtYUSTTcY3DPEV8y98IQ=
+github.com/siyuan-note/filelock v0.0.0-20231106131407-1cd9e9a9230c h1:thK+FFmQEGHjVxpTCM7BAlbEQ9HD71nzR92rV/bgZYA=
+github.com/siyuan-note/filelock v0.0.0-20231106131407-1cd9e9a9230c/go.mod h1:HjtfP1NLDw53BRSeAPJgZDIGqyxi4bANYn1IhgDCQUY=
 github.com/siyuan-note/httpclient v0.0.0-20231028070852-3c2c5a151c13 h1:DQwiRQa63wsjSKQN5TvTWI2Dd5XSulhnXvxIdOG8/x8=
 github.com/siyuan-note/httpclient v0.0.0-20231028070852-3c2c5a151c13/go.mod h1:mS0nX5fX99R/5HLnKqG40kpbathSCXxL/oxe88rURNI=
 github.com/siyuan-note/logging v0.0.0-20231030034701-8265764f00ff h1:5GcxrTOJTsusXOLhg4GuHWbEa4M5gu+CNfL0giwNjDM=
 github.com/siyuan-note/logging v0.0.0-20231030034701-8265764f00ff/go.mod h1:6mRFtAAvYPn3cDzqvyv+t8BVPGqpONDMMb5ywOhY1D4=
-github.com/siyuan-note/riff v0.0.0-20230928143458-ffd178c7e0fc h1:EDbkzdLfR+gMYyU4h/xUiwxngz75d3L5M0IN0NTqdVU=
-github.com/siyuan-note/riff v0.0.0-20230928143458-ffd178c7e0fc/go.mod h1:WxG165xAPeGok0Q/15KMhnNbesoU5ZG+/zikTyWzZi8=
+github.com/siyuan-note/riff v0.0.0-20231106140614-c67ff0c69e33 h1:oEJoBL0ZSDwKckZcS37zMuIsPplTnN1aHz+04700P2o=
+github.com/siyuan-note/riff v0.0.0-20231106140614-c67ff0c69e33/go.mod h1:Y1kKl2ZTGKovrs+/4k/4OteHpF04XbgJ+MsnLetP8Jk=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/goconvey v1.6.7 h1:I6tZjLXD2Q1kjvNbIzB1wvQBsXmKXiVrhpRE8ZjP5jY=
@@ -393,8 +393,8 @@ github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95
 github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
 github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
 github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
-github.com/vmihailenco/msgpack/v5 v5.4.0 h1:hRM0digJwyR6vll33NNAwCFguy5JuBD6jxDmQP3l608=
-github.com/vmihailenco/msgpack/v5 v5.4.0/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
+github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
+github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
 github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
 github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
 github.com/wmentor/html v1.0.1 h1:iIuDyH7pohHMMzdD5WQhvya5UyIecFDWTYzdM873Ook=
@@ -484,8 +484,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q=
+golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -506,8 +506,9 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
 golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
 golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
 golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

+ 6 - 6
kernel/model/assets.go

@@ -440,7 +440,7 @@ func RemoveUnusedAssets() (ret []string) {
 	var hashes []string
 	for _, p := range unusedAssets {
 		historyPath := filepath.Join(historyDir, p)
-		if p = filepath.Join(util.DataDir, p); gulu.File.IsExist(p) {
+		if p = filepath.Join(util.DataDir, p); filelock.IsExist(p) {
 			if err = filelock.Copy(p, historyPath); nil != err {
 				return
 			}
@@ -453,7 +453,7 @@ func RemoveUnusedAssets() (ret []string) {
 	sql.BatchRemoveAssetsQueue(hashes)
 
 	for _, unusedAsset := range unusedAssets {
-		if unusedAsset = filepath.Join(util.DataDir, unusedAsset); gulu.File.IsExist(unusedAsset) {
+		if unusedAsset = filepath.Join(util.DataDir, unusedAsset); filelock.IsExist(unusedAsset) {
 			if err := os.RemoveAll(unusedAsset); nil != err {
 				logging.LogErrorf("remove unused asset [%s] failed: %s", unusedAsset, err)
 			}
@@ -471,7 +471,7 @@ func RemoveUnusedAssets() (ret []string) {
 
 func RemoveUnusedAsset(p string) (ret string) {
 	absPath := filepath.Join(util.DataDir, p)
-	if !gulu.File.IsExist(absPath) {
+	if !filelock.IsExist(absPath) {
 		return absPath
 	}
 
@@ -483,7 +483,7 @@ func RemoveUnusedAsset(p string) (ret string) {
 
 	newP := strings.TrimPrefix(absPath, util.DataDir)
 	historyPath := filepath.Join(historyDir, newP)
-	if gulu.File.IsExist(absPath) {
+	if filelock.IsExist(absPath) {
 		if err = filelock.Copy(absPath, historyPath); nil != err {
 			return
 		}
@@ -528,7 +528,7 @@ func RenameAsset(oldPath, newName string) (err error) {
 		return
 	}
 
-	if gulu.File.IsExist(filepath.Join(util.DataDir, oldPath+".sya")) {
+	if filelock.IsExist(filepath.Join(util.DataDir, oldPath+".sya")) {
 		// Rename the .sya annotation file when renaming a PDF asset https://github.com/siyuan-note/siyuan/issues/9390
 		if err = filelock.Copy(filepath.Join(util.DataDir, oldPath+".sya"), filepath.Join(util.DataDir, newPath+".sya")); nil != err {
 			logging.LogErrorf("copy PDF annotation [%s] failed: %s", oldPath+".sya", err)
@@ -804,7 +804,7 @@ func MissingAssets() (ret []string) {
 			if "" == assetsPathMap[dest] {
 				if strings.HasPrefix(dest, "assets/.") {
 					// Assets starting with `.` should not be considered missing assets https://github.com/siyuan-note/siyuan/issues/8821
-					if !gulu.File.IsExist(filepath.Join(util.DataDir, dest)) {
+					if !filelock.IsExist(filepath.Join(util.DataDir, dest)) {
 						ret = append(ret, dest)
 					}
 				} else {

+ 2 - 1
kernel/model/attribute_view.go

@@ -27,6 +27,7 @@ import (
 	"github.com/88250/lute/ast"
 	"github.com/88250/lute/parse"
 	"github.com/Masterminds/sprig/v3"
+	"github.com/siyuan-note/filelock"
 	"github.com/siyuan-note/logging"
 	"github.com/siyuan-note/siyuan/kernel/av"
 	"github.com/siyuan-note/siyuan/kernel/treenode"
@@ -183,7 +184,7 @@ func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) {
 func RenderAttributeView(avID string) (viewable av.Viewable, attrView *av.AttributeView, err error) {
 	waitForSyncingStorages()
 
-	if avJSONPath := av.GetAttributeViewDataPath(avID); !gulu.File.IsExist(avJSONPath) {
+	if avJSONPath := av.GetAttributeViewDataPath(avID); !filelock.IsExist(avJSONPath) {
 		attrView = av.NewAttributeView(avID)
 		if err = av.SaveAttributeView(attrView); nil != err {
 			logging.LogErrorf("save attribute view [%s] failed: %s", avID, err)

+ 3 - 3
kernel/model/box.go

@@ -111,7 +111,7 @@ func ListNotebooks() (ret []*Box, err error) {
 		boxConf := conf.NewBoxConf()
 		boxDirPath := filepath.Join(util.DataDir, dir.Name())
 		boxConfPath := filepath.Join(boxDirPath, ".siyuan", "conf.json")
-		if !gulu.File.IsExist(boxConfPath) {
+		if !filelock.IsExist(boxConfPath) {
 			// Automatically move corrupted notebook folders to the corrupted folder https://github.com/siyuan-note/siyuan/issues/9202
 			logging.LogWarnf("found a corrupted box [%s]", boxDirPath)
 			to := filepath.Join(util.WorkspaceDir, "corrupted", time.Now().Format("2006-01-02-150405"), dir.Name())
@@ -184,7 +184,7 @@ func (box *Box) GetConf() (ret *conf.BoxConf) {
 	ret = conf.NewBoxConf()
 
 	confPath := filepath.Join(util.DataDir, box.ID, ".siyuan/conf.json")
-	if !gulu.File.IsExist(confPath) {
+	if !filelock.IsExist(confPath) {
 		return
 	}
 
@@ -305,7 +305,7 @@ func (box *Box) Stat(p string) (ret *FileInfo) {
 }
 
 func (box *Box) Exist(p string) bool {
-	return gulu.File.IsExist(filepath.Join(util.DataDir, box.ID, p))
+	return filelock.IsExist(filepath.Join(util.DataDir, box.ID, p))
 }
 
 func (box *Box) Mkdir(path string) error {

+ 2 - 2
kernel/model/conf.go

@@ -812,7 +812,7 @@ func clearCorruptedNotebooks() {
 
 		boxDirPath := filepath.Join(util.DataDir, dir.Name())
 		boxConfPath := filepath.Join(boxDirPath, ".siyuan", "conf.json")
-		if !gulu.File.IsExist(boxConfPath) {
+		if !filelock.IsExist(boxConfPath) {
 			logging.LogWarnf("found a corrupted box [%s]", boxDirPath)
 			continue
 		}
@@ -898,7 +898,7 @@ func upgradeUserGuide() {
 		boxDirPath := filepath.Join(util.DataDir, boxID)
 		boxConf := conf.NewBoxConf()
 		boxConfPath := filepath.Join(boxDirPath, ".siyuan", "conf.json")
-		if !gulu.File.IsExist(boxConfPath) {
+		if !filelock.IsExist(boxConfPath) {
 			logging.LogWarnf("found a corrupted box [%s]", boxDirPath)
 			continue
 		}

+ 4 - 4
kernel/model/export.go

@@ -1347,7 +1347,7 @@ func exportSYZip(boxID, rootDirPath, baseFolderName string, docPaths []string) (
 
 			if !gulu.File.IsDir(srcPath) && strings.HasSuffix(strings.ToLower(srcPath), ".pdf") {
 				sya := srcPath + ".sya"
-				if gulu.File.IsExist(sya) {
+				if filelock.IsExist(sya) {
 					// Related PDF annotation information is not exported when exporting .sy.zip https://github.com/siyuan-note/siyuan/issues/7836
 					if syaErr := filelock.Copy(sya, destPath+".sya"); nil != syaErr {
 						logging.LogErrorf("copy sya from [%s] to [%s] failed: %s", sya, destPath+".sya", syaErr)
@@ -1373,7 +1373,7 @@ func exportSYZip(boxID, rootDirPath, baseFolderName string, docPaths []string) (
 
 			avID := n.AttributeViewID
 			avJSONPath := av.GetAttributeViewDataPath(avID)
-			if !gulu.File.IsExist(avJSONPath) {
+			if !filelock.IsExist(avJSONPath) {
 				return ast.WalkContinue
 			}
 
@@ -1410,7 +1410,7 @@ func exportSYZip(boxID, rootDirPath, baseFolderName string, docPaths []string) (
 	sortIDs := map[string]int{}
 	var sortData []byte
 	var sortErr error
-	if gulu.File.IsExist(sortPath) {
+	if filelock.IsExist(sortPath) {
 		sortData, sortErr = filelock.ReadFile(sortPath)
 		if nil != sortErr {
 			logging.LogErrorf("read sort conf failed: %s", sortErr)
@@ -1900,7 +1900,7 @@ func exportTree(tree *parse.Tree, wysiwyg, expandKaTexMacros, keepFold bool,
 		}
 
 		avID := n.AttributeViewID
-		if avJSONPath := av.GetAttributeViewDataPath(avID); !gulu.File.IsExist(avJSONPath) {
+		if avJSONPath := av.GetAttributeViewDataPath(avID); !filelock.IsExist(avJSONPath) {
 			return ast.WalkContinue
 		}
 

+ 7 - 7
kernel/model/file.go

@@ -1205,7 +1205,7 @@ func moveDoc(fromBox *Box, fromPath string, toBox *Box, toPath string, luteEngin
 		} else {
 			absFromPath := filepath.Join(util.DataDir, fromBox.ID, fromFolder)
 			absToPath := filepath.Join(util.DataDir, toBox.ID, newFolder)
-			if gulu.File.IsExist(absToPath) {
+			if filelock.IsExist(absToPath) {
 				filelock.Remove(absToPath)
 			}
 			if err = filelock.Rename(absFromPath, absToPath); nil != err {
@@ -1445,7 +1445,7 @@ func CreateDailyNote(boxID string) (p string, existed bool, err error) {
 	var dom string
 	if "" != boxConf.DailyNoteTemplatePath {
 		tplPath := filepath.Join(util.DataDir, "templates", boxConf.DailyNoteTemplatePath)
-		if !gulu.File.IsExist(tplPath) {
+		if !filelock.IsExist(tplPath) {
 			logging.LogWarnf("not found daily note template [%s]", tplPath)
 		} else {
 			dom, err = renderTemplate(tplPath, id, false)
@@ -1570,7 +1570,7 @@ func moveSorts(rootID, fromBox, toBox string) {
 	ids := treenode.RootChildIDs(rootID)
 	fromConfPath := filepath.Join(util.DataDir, fromBox, ".siyuan", "sort.json")
 	fromFullSortIDs := map[string]int{}
-	if gulu.File.IsExist(fromConfPath) {
+	if filelock.IsExist(fromConfPath) {
 		data, err := filelock.ReadFile(fromConfPath)
 		if nil != err {
 			logging.LogErrorf("read sort conf failed: %s", err)
@@ -1587,7 +1587,7 @@ func moveSorts(rootID, fromBox, toBox string) {
 
 	toConfPath := filepath.Join(util.DataDir, toBox, ".siyuan", "sort.json")
 	toFullSortIDs := map[string]int{}
-	if gulu.File.IsExist(toConfPath) {
+	if filelock.IsExist(toConfPath) {
 		data, err := filelock.ReadFile(toConfPath)
 		if nil != err {
 			logging.LogErrorf("read sort conf failed: %s", err)
@@ -1663,7 +1663,7 @@ func ChangeFileTreeSort(boxID string, paths []string) {
 	confPath := filepath.Join(confDir, "sort.json")
 	fullSortIDs := map[string]int{}
 	var data []byte
-	if gulu.File.IsExist(confPath) {
+	if filelock.IsExist(confPath) {
 		data, err = filelock.ReadFile(confPath)
 		if nil != err {
 			logging.LogErrorf("read sort conf failed: %s", err)
@@ -1694,7 +1694,7 @@ func ChangeFileTreeSort(boxID string, paths []string) {
 
 func (box *Box) fillSort(files *[]*File) {
 	confPath := filepath.Join(util.DataDir, box.ID, ".siyuan", "sort.json")
-	if !gulu.File.IsExist(confPath) {
+	if !filelock.IsExist(confPath) {
 		return
 	}
 
@@ -1718,7 +1718,7 @@ func (box *Box) fillSort(files *[]*File) {
 
 func (box *Box) removeSort(ids []string) {
 	confPath := filepath.Join(util.DataDir, box.ID, ".siyuan", "sort.json")
-	if !gulu.File.IsExist(confPath) {
+	if !filelock.IsExist(confPath) {
 		return
 	}
 

+ 5 - 4
kernel/model/flashcard.go

@@ -30,6 +30,7 @@ import (
 	"github.com/88250/lute/ast"
 	"github.com/88250/lute/parse"
 	"github.com/open-spaced-repetition/go-fsrs"
+	"github.com/siyuan-note/filelock"
 	"github.com/siyuan-note/logging"
 	"github.com/siyuan-note/riff"
 	"github.com/siyuan-note/siyuan/kernel/cache"
@@ -814,15 +815,15 @@ func RemoveDeck(deckID string) (err error) {
 
 	riffSavePath := getRiffDir()
 	deckPath := filepath.Join(riffSavePath, deckID+".deck")
-	if gulu.File.IsExist(deckPath) {
-		if err = os.Remove(deckPath); nil != err {
+	if filelock.IsExist(deckPath) {
+		if err = filelock.Remove(deckPath); nil != err {
 			return
 		}
 	}
 
 	cardsPath := filepath.Join(riffSavePath, deckID+".cards")
-	if gulu.File.IsExist(cardsPath) {
-		if err = os.Remove(cardsPath); nil != err {
+	if filelock.IsExist(cardsPath) {
+		if err = filelock.Remove(cardsPath); nil != err {
 			return
 		}
 	}

+ 5 - 0
kernel/model/history.go

@@ -142,6 +142,7 @@ func ClearWorkspaceHistory() (err error) {
 
 func GetDocHistoryContent(historyPath, keyword string) (id, rootID, content string, isLargeDoc bool, err error) {
 	if !gulu.File.IsExist(historyPath) {
+		logging.LogWarnf("doc history [%s] not exist", historyPath)
 		return
 	}
 
@@ -214,6 +215,7 @@ func GetDocHistoryContent(historyPath, keyword string) (id, rootID, content stri
 
 func RollbackDocHistory(boxID, historyPath string) (err error) {
 	if !gulu.File.IsExist(historyPath) {
+		logging.LogWarnf("doc history [%s] not exist", historyPath)
 		return
 	}
 
@@ -268,6 +270,7 @@ func getRollbackDockPath(boxID, historyPath string) (destPath string, err error)
 func RollbackAssetsHistory(historyPath string) (err error) {
 	historyPath = filepath.Join(util.WorkspaceDir, historyPath)
 	if !gulu.File.IsExist(historyPath) {
+		logging.LogWarnf("assets history [%s] not exist", historyPath)
 		return
 	}
 
@@ -285,6 +288,7 @@ func RollbackAssetsHistory(historyPath string) (err error) {
 
 func RollbackNotebookHistory(historyPath string) (err error) {
 	if !gulu.File.IsExist(historyPath) {
+		logging.LogWarnf("notebook history [%s] not exist", historyPath)
 		return
 	}
 
@@ -482,6 +486,7 @@ func (box *Box) generateDocHistory0() {
 
 func clearOutdatedHistoryDir(historyDir string) {
 	if !gulu.File.IsExist(historyDir) {
+		logging.LogWarnf("history dir [%s] not exist", historyDir)
 		return
 	}
 

+ 2 - 2
kernel/model/import.go

@@ -349,7 +349,7 @@ func ImportSY(zipPath, boxID, toPath string) (err error) {
 	var sortData []byte
 	var sortErr error
 	sortPath := filepath.Join(unzipRootPath, ".siyuan", "sort.json")
-	if gulu.File.IsExist(sortPath) {
+	if filelock.IsExist(sortPath) {
 		sortData, sortErr = filelock.ReadFile(sortPath)
 		if nil != sortErr {
 			logging.LogErrorf("read import sort conf failed: %s", sortErr)
@@ -360,7 +360,7 @@ func ImportSY(zipPath, boxID, toPath string) (err error) {
 		}
 
 		boxSortPath := filepath.Join(util.DataDir, boxID, ".siyuan", "sort.json")
-		if gulu.File.IsExist(boxSortPath) {
+		if filelock.IsExist(boxSortPath) {
 			sortData, sortErr = filelock.ReadFile(boxSortPath)
 			if nil != sortErr {
 				logging.LogErrorf("read box sort conf failed: %s", sortErr)

+ 1 - 1
kernel/model/mount.go

@@ -78,7 +78,7 @@ func RemoveBox(boxID string) (err error) {
 	}
 
 	localPath := filepath.Join(util.DataDir, boxID)
-	if !gulu.File.IsExist(localPath) {
+	if !filelock.IsExist(localPath) {
 		return
 	}
 	if !gulu.File.IsDir(localPath) {

+ 5 - 13
kernel/model/ocr.go

@@ -1,7 +1,6 @@
 package model
 
 import (
-	"io"
 	"os"
 	"path/filepath"
 	"runtime/debug"
@@ -10,6 +9,7 @@ import (
 
 	"github.com/88250/gulu"
 	"github.com/dustin/go-humanize"
+	"github.com/siyuan-note/filelock"
 	"github.com/siyuan-note/logging"
 	"github.com/siyuan-note/siyuan/kernel/cache"
 	"github.com/siyuan-note/siyuan/kernel/task"
@@ -57,7 +57,7 @@ func cleanNotExistAssetsTexts() {
 	for asset, _ := range util.AssetsTexts {
 		assetAbsPath := strings.TrimPrefix(asset, "assets")
 		assetAbsPath = filepath.Join(assetsPath, assetAbsPath)
-		if !gulu.File.IsExist(assetAbsPath) {
+		if !filelock.IsExist(assetAbsPath) {
 			toRemoves = append(toRemoves, asset)
 		}
 	}
@@ -98,20 +98,12 @@ func FlushAssetsTextsJob() {
 func LoadAssetsTexts() {
 	assetsPath := util.GetDataAssetsAbsPath()
 	assetsTextsPath := filepath.Join(assetsPath, "ocr-texts.json")
-	if !gulu.File.IsExist(assetsTextsPath) {
+	if !filelock.IsExist(assetsTextsPath) {
 		return
 	}
 
 	start := time.Now()
-	var err error
-	fh, err := os.OpenFile(assetsTextsPath, os.O_RDWR, 0644)
-	if nil != err {
-		logging.LogErrorf("open assets texts failed: %s", err)
-		return
-	}
-	defer fh.Close()
-
-	data, err := io.ReadAll(fh)
+	data, err := filelock.ReadFile(assetsTextsPath)
 	if nil != err {
 		logging.LogErrorf("read assets texts failed: %s", err)
 		return
@@ -151,7 +143,7 @@ func SaveAssetsTexts() {
 
 	assetsPath := util.GetDataAssetsAbsPath()
 	assetsTextsPath := filepath.Join(assetsPath, "ocr-texts.json")
-	if err = gulu.File.WriteFileSafer(assetsTextsPath, data, 0644); nil != err {
+	if err = filelock.WriteFile(assetsTextsPath, data); nil != err {
 		logging.LogErrorf("write assets texts failed: %s", err)
 		return
 	}

+ 3 - 3
kernel/model/plugin.go

@@ -101,7 +101,7 @@ func LoadPetals(frontend string) (ret []*Petal) {
 func loadCode(petal *Petal) {
 	pluginDir := filepath.Join(util.DataDir, "plugins", petal.Name)
 	jsPath := filepath.Join(pluginDir, "index.js")
-	if !gulu.File.IsExist(jsPath) {
+	if !filelock.IsExist(jsPath) {
 		logging.LogErrorf("plugin [%s] js not found", petal.Name)
 		return
 	}
@@ -114,7 +114,7 @@ func loadCode(petal *Petal) {
 	petal.JS = string(data)
 
 	cssPath := filepath.Join(pluginDir, "index.css")
-	if gulu.File.IsExist(cssPath) {
+	if filelock.IsExist(cssPath) {
 		data, err = filelock.ReadFile(cssPath)
 		if nil != err {
 			logging.LogErrorf("read plugin [%s] css failed: %s", petal.Name, err)
@@ -204,7 +204,7 @@ func getPetals() (ret []*Petal) {
 	}
 
 	confPath := filepath.Join(petalDir, "petals.json")
-	if !gulu.File.IsExist(confPath) {
+	if !filelock.IsExist(confPath) {
 		data, err := gulu.JSON.MarshalIndentJSON(ret, "", "\t")
 		if nil != err {
 			logging.LogErrorf("marshal petals failed: %s", err)

+ 1 - 1
kernel/model/snippet.go

@@ -68,7 +68,7 @@ func LoadSnippets() (ret []*conf.Snippet, err error) {
 func loadSnippets() (ret []*conf.Snippet, err error) {
 	ret = []*conf.Snippet{}
 	confPath := filepath.Join(util.SnippetsPath, "conf.json")
-	if !gulu.File.IsExist(confPath) {
+	if !filelock.IsExist(confPath) {
 		return
 	}
 

+ 3 - 3
kernel/model/storage.go

@@ -129,7 +129,7 @@ func setRecentDocs(recentDocs []*RecentDoc) (err error) {
 func getRecentDocs() (ret []*RecentDoc, err error) {
 	tmp := []*RecentDoc{}
 	dataPath := filepath.Join(util.DataDir, "storage/recent-doc.json")
-	if !gulu.File.IsExist(dataPath) {
+	if !filelock.IsExist(dataPath) {
 		return
 	}
 
@@ -270,7 +270,7 @@ func setCriteria(criteria []*Criterion) (err error) {
 func getCriteria() (ret []*Criterion, err error) {
 	ret = []*Criterion{}
 	dataPath := filepath.Join(util.DataDir, "storage/criteria.json")
-	if !gulu.File.IsExist(dataPath) {
+	if !filelock.IsExist(dataPath) {
 		return
 	}
 
@@ -351,7 +351,7 @@ func getLocalStorage() (ret map[string]interface{}) {
 	// When local.json is corrupted, clear the file to avoid being unable to enter the main interface https://github.com/siyuan-note/siyuan/issues/7911
 	ret = map[string]interface{}{}
 	lsPath := filepath.Join(util.DataDir, "storage/local.json")
-	if !gulu.File.IsExist(lsPath) {
+	if !filelock.IsExist(lsPath) {
 		return
 	}
 

+ 1 - 1
kernel/model/template.go

@@ -153,7 +153,7 @@ func DocSaveAsTemplate(id, name string, overwrite bool) (code int, err error) {
 	name = util.FilterFileName(name) + ".md"
 	name = util.TruncateLenFileName(name)
 	savePath := filepath.Join(util.DataDir, "templates", name)
-	if gulu.File.IsExist(savePath) {
+	if filelock.IsExist(savePath) {
 		if !overwrite {
 			code = 1
 			return

+ 2 - 2
kernel/model/upload.go

@@ -278,9 +278,9 @@ func Upload(c *gin.Context) {
 
 func getAssetsDir(boxLocalPath, docDirLocalPath string) (assets string) {
 	assets = filepath.Join(docDirLocalPath, "assets")
-	if !gulu.File.IsExist(assets) {
+	if !filelock.IsExist(assets) {
 		assets = filepath.Join(boxLocalPath, "assets")
-		if !gulu.File.IsExist(assets) {
+		if !filelock.IsExist(assets) {
 			assets = filepath.Join(util.DataDir, "assets")
 		}
 	}

+ 4 - 4
kernel/sql/asset.go

@@ -18,10 +18,10 @@ package sql
 
 import (
 	"database/sql"
+	"github.com/siyuan-note/filelock"
 	"path/filepath"
 	"strings"
 
-	"github.com/88250/gulu"
 	"github.com/88250/lute/ast"
 	"github.com/siyuan-note/logging"
 	"github.com/siyuan-note/siyuan/kernel/treenode"
@@ -139,17 +139,17 @@ func scanAssetRows(rows *sql.Rows) (ret *Asset) {
 
 func assetLocalPath(linkDest, boxLocalPath, docDirLocalPath string) (ret string) {
 	ret = filepath.Join(docDirLocalPath, linkDest)
-	if gulu.File.IsExist(ret) {
+	if filelock.IsExist(ret) {
 		return
 	}
 
 	ret = filepath.Join(boxLocalPath, linkDest)
-	if gulu.File.IsExist(ret) {
+	if filelock.IsExist(ret) {
 		return
 	}
 
 	ret = filepath.Join(util.DataDir, linkDest)
-	if gulu.File.IsExist(ret) {
+	if filelock.IsExist(ret) {
 		return
 	}
 	return ""