Преглед изворни кода

:art: 使用第三方同步盘时弹出提示并退出内核 https://github.com/siyuan-note/siyuan/issues/7683

Liang Ding пре 2 година
родитељ
комит
d15200957d
4 измењених фајлова са 121 додато и 2 уклоњено
  1. 5 0
      app/electron/main.js
  2. 1 0
      kernel/main.go
  3. 6 2
      kernel/model/conf.go
  4. 109 0
      kernel/util/runtime.go

+ 5 - 0
app/electron/main.js

@@ -482,6 +482,11 @@ const initKernel = (workspace, port, lang) => {
                                 "⚠️ 创建工作空间目录失败 Failed to create workspace directory",
                                 "<div>创建工作空间目录失败。</div><div>Failed to create workspace directory.</div>");
                             break;
+                        case 26:
+                            showErrorWindow(
+                                "⚠️ 文件系统不一致 File system inconsistent",
+                                "<div>请勿使用第三方同步盘进行数据同步,否则数据会被损坏(OneDrive/Dropbox/Google Drive/坚果云/百度网盘/腾讯微云等)</div><div>Do not use a third-party sync disk for data sync, otherwise the data will be damaged (OneDrive/Dropbox/Google Drive/Nutstore/Baidu Netdisk/Tencent Weiyun, etc.)</div>");
+                            break;
                         case 0:
                         case 1: // Fatal error
                             break;

+ 1 - 0
kernel/main.go

@@ -48,6 +48,7 @@ func main() {
 	job.StartCron()
 	go model.AutoGenerateDocHistory()
 	go cache.LoadAssets()
+	go util.CheckFileSysStatus()
 
 	model.WatchAssets()
 	model.HandleSignal()

+ 6 - 2
kernel/model/conf.go

@@ -32,7 +32,7 @@ import (
 	"github.com/88250/lute"
 	"github.com/88250/lute/ast"
 	"github.com/Xuanwo/go-locale"
-	humanize "github.com/dustin/go-humanize"
+	"github.com/dustin/go-humanize"
 	"github.com/getsentry/sentry-go"
 	"github.com/siyuan-note/filelock"
 	"github.com/siyuan-note/logging"
@@ -58,7 +58,8 @@ type AppConf struct {
 	Editor         *conf.Editor     `json:"editor"`         // 编辑器配置
 	Export         *conf.Export     `json:"export"`         // 导出配置
 	Graph          *conf.Graph      `json:"graph"`          // 关系图配置
-	UILayout       *conf.UILayout   `json:"uiLayout"`       // 界面布局
+	UILayout       *conf.UILayout   `json:"uiLayout"`       // 界面布局,v2.8.0 后这个字段不再使用
+	UILayouts      []*conf.UILayout `json:"uiLayouts"`      // 界面布局列表
 	UserData       string           `json:"userData"`       // 社区用户信息,对 User 加密存储
 	User           *conf.User       `json:"-"`              // 社区用户内存结构,不持久化
 	Account        *conf.Account    `json:"account"`        // 帐号配置
@@ -149,6 +150,9 @@ func InitConf() {
 	if nil == Conf.UILayout {
 		Conf.UILayout = &conf.UILayout{}
 	}
+	if 1 > len(Conf.UILayouts) {
+		Conf.UILayouts = []*conf.UILayout{}
+	}
 	if nil == Conf.Keymap {
 		Conf.Keymap = &conf.Keymap{}
 	}

+ 109 - 0
kernel/util/runtime.go

@@ -17,9 +17,14 @@
 package util
 
 import (
+	"fmt"
+	"math/rand"
 	"os"
+	"path/filepath"
 	"reflect"
 	"runtime"
+	"runtime/debug"
+	"strings"
 	"sync"
 	"time"
 
@@ -38,6 +43,7 @@ const (
 	ExitCodeBlockTreeErr          = 23 // 无法读写 blocktree.msgpack 文件
 	ExitCodeWorkspaceLocked       = 24 // 工作空间已被锁定
 	ExitCodeCreateWorkspaceDirErr = 25 // 创建工作空间失败
+	ExitCodeFileSysInconsistent   = 26 // 文件系统不一致
 	ExitCodeOk                    = 0  // 正常退出
 	ExitCodeFatal                 = 1  // 致命错误
 )
@@ -119,3 +125,106 @@ var (
 	TimeLangs       = map[string]map[string]interface{}{}
 	TaskActionLangs = map[string]map[string]interface{}{}
 )
+
+var thirdPartySyncCheckTicker = time.NewTicker(time.Second * 10)
+
+func CheckFileSysStatus() {
+	if ContainerStd != Container {
+		return
+	}
+
+	reportFileSysFatalError := func(err error) {
+		stack := debug.Stack()
+		logging.LogErrorf("check file system status failed: %s, %s", err, stack)
+		os.Exit(ExitCodeFileSysInconsistent)
+	}
+
+	const fileSysStatusCheckFile = "filesys_status_check"
+
+	for {
+		<-thirdPartySyncCheckTicker.C
+
+		workspaceDirLower := strings.ToLower(WorkspaceDir)
+		if strings.Contains(workspaceDirLower, "onedrive") || strings.Contains(workspaceDirLower, "dropbox") ||
+			strings.Contains(workspaceDirLower, "google drive") || strings.Contains(workspaceDirLower, "pcloud") {
+			reportFileSysFatalError(fmt.Errorf("workspace dir [%s] is in third party sync dir", WorkspaceDir))
+			continue
+		}
+
+		dir := filepath.Join(DataDir, fileSysStatusCheckFile)
+		if err := os.RemoveAll(dir); nil != err {
+			reportFileSysFatalError(err)
+			continue
+		}
+
+		if err := os.MkdirAll(dir, 0755); nil != err {
+			reportFileSysFatalError(err)
+			continue
+		}
+
+		for i := 0; i < 32; i++ {
+			tmp := filepath.Join(dir, "check_"+gulu.Rand.String(7))
+			data := make([]byte, 1024*4)
+			_, err := rand.Read(data)
+			if nil != err {
+				reportFileSysFatalError(err)
+				break
+			}
+
+			if err = os.WriteFile(tmp, data, 0644); nil != err {
+				reportFileSysFatalError(err)
+				break
+			}
+
+			time.Sleep(time.Second)
+
+			for j := 0; j < 32; j++ {
+				f, err := os.Open(tmp)
+				if nil != err {
+					reportFileSysFatalError(err)
+					break
+				}
+
+				if err = f.Close(); nil != err {
+					reportFileSysFatalError(err)
+					break
+				}
+
+				time.Sleep(100 * time.Millisecond)
+
+				if err = os.Rename(tmp, tmp+"_1"); nil != err {
+					reportFileSysFatalError(err)
+					break
+				}
+
+				time.Sleep(100 * time.Millisecond)
+				if err = os.Rename(tmp+"_1", tmp); nil != err {
+					reportFileSysFatalError(err)
+					break
+				}
+			}
+
+			entries, err := os.ReadDir(dir)
+			if nil != err {
+				reportFileSysFatalError(err)
+				break
+			}
+
+			count := 0
+			for _, entry := range entries {
+				if !entry.IsDir() && strings.Contains(entry.Name(), "check_") {
+					count++
+				}
+			}
+			if 1 < count {
+				reportFileSysFatalError(fmt.Errorf("dir [%s] has more than 1 file", dir))
+				break
+			}
+
+			if err = os.RemoveAll(tmp); nil != err {
+				reportFileSysFatalError(err)
+				break
+			}
+		}
+	}
+}