You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

cache.go 8.1 kB

8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. package cache
  2. import (
  3. "errors"
  4. "os"
  5. "path/filepath"
  6. "sync"
  7. "syscall"
  8. "time"
  9. "gitlink.org.cn/cloudream/common/pkgs/logger"
  10. "gitlink.org.cn/cloudream/common/pkgs/trie"
  11. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  12. "gitlink.org.cn/cloudream/common/utils/lo2"
  13. "gitlink.org.cn/cloudream/storage/client2/internal/mount/fuse"
  14. "gitlink.org.cn/cloudream/storage/common/pkgs/db2"
  15. "gitlink.org.cn/cloudream/storage/common/pkgs/downloader"
  16. )
  17. type CacheEntry interface {
  18. fuse.FsEntry
  19. // 在虚拟文件系统中的路径,即不包含缓存目录的路径
  20. PathComps() []string
  21. // 不再使用本缓存条目
  22. // Release()
  23. }
  24. type CacheEntryInfo struct {
  25. PathComps []string
  26. Size int64
  27. Mode os.FileMode
  28. ModTime time.Time
  29. IsDir bool
  30. }
  31. type Cache struct {
  32. db *db2.DB
  33. downloader *downloader.Downloader
  34. cacheDataDir string
  35. cacheMetaDir string
  36. activeCache *trie.Trie[*CacheFile]
  37. lock *sync.RWMutex
  38. }
  39. func NewCache(db *db2.DB, downloader *downloader.Downloader, cacheDataDir, cacheMetaDir string) *Cache {
  40. return &Cache{
  41. db: db,
  42. downloader: downloader,
  43. cacheDataDir: cacheDataDir,
  44. cacheMetaDir: cacheMetaDir,
  45. activeCache: trie.NewTrie[*CacheFile](),
  46. lock: &sync.RWMutex{},
  47. }
  48. }
  49. func (c *Cache) GetCacheDataPath(comps ...string) string {
  50. comps2 := make([]string, len(comps)+1)
  51. comps2[0] = c.cacheDataDir
  52. copy(comps2[1:], comps)
  53. return filepath.Join(comps2...)
  54. }
  55. func (c *Cache) GetCacheDataPathComps(comps ...string) []string {
  56. comps2 := make([]string, len(comps)+1)
  57. comps2[0] = c.cacheDataDir
  58. copy(comps2[1:], comps)
  59. return comps2
  60. }
  61. func (c *Cache) GetCacheMetaPath(comps ...string) string {
  62. comps2 := make([]string, len(comps)+1)
  63. comps2[0] = c.cacheMetaDir
  64. copy(comps2[1:], comps)
  65. return filepath.Join(comps2...)
  66. }
  67. func (c *Cache) GetCacheMetaPathComps(comps ...string) []string {
  68. comps2 := make([]string, len(comps)+1)
  69. comps2[0] = c.cacheMetaDir
  70. copy(comps2[1:], comps)
  71. return comps2
  72. }
  73. // 获取指定位置的缓存条目信息。如果路径不存在,则返回nil。
  74. func (c *Cache) Stat(pathComps []string) *CacheEntryInfo {
  75. c.lock.RLock()
  76. defer c.lock.RUnlock()
  77. node, ok := c.activeCache.WalkEnd(pathComps)
  78. if ok && node.Value != nil {
  79. info := node.Value.Info()
  80. return &info
  81. }
  82. metaPath := c.GetCacheMetaPath(pathComps...)
  83. stat, err := os.Stat(metaPath)
  84. if err != nil {
  85. // TODO 日志记录
  86. return nil
  87. }
  88. if stat.IsDir() {
  89. info, err := loadCacheDirInfo(c, pathComps)
  90. if err != nil {
  91. return nil
  92. }
  93. return info
  94. }
  95. info, err := loadCacheFileInfo(c, pathComps)
  96. if err != nil {
  97. return nil
  98. }
  99. return info
  100. }
  101. // 创建一个缓存文件。如果文件已经存在,则会覆盖已有文件。如果加载过程中发生了错误,或者目标位置是一个目录,则会返回nil。
  102. func (c *Cache) CreateFile(pathComps []string) *CacheFile {
  103. c.lock.Lock()
  104. defer c.lock.Unlock()
  105. ch, err := createNewCacheFile(c, pathComps)
  106. if err != nil {
  107. // TODO 日志记录
  108. return nil
  109. }
  110. c.activeCache.CreateWords(pathComps).Value = ch
  111. return ch
  112. }
  113. // 尝试加载缓存文件,如果文件不存在,则使用obj的信息创建一个新缓存文件,而如果obj为nil,那么会返回nil。
  114. func (c *Cache) LoadFile(pathComps []string, obj *cdssdk.Object) *CacheFile {
  115. c.lock.Lock()
  116. defer c.lock.Unlock()
  117. node, ok := c.activeCache.WalkEnd(pathComps)
  118. if ok && node.Value != nil {
  119. return node.Value
  120. }
  121. ch, err := loadCacheFile(c, pathComps)
  122. if err == nil {
  123. ch.remoteObj = obj
  124. c.activeCache.CreateWords(pathComps).Value = ch
  125. return ch
  126. }
  127. if !os.IsNotExist(err) {
  128. // TODO 日志记录
  129. logger.Tracef("load cache file: %v", err)
  130. return nil
  131. }
  132. if obj == nil {
  133. return nil
  134. }
  135. ch, err = newCacheFileFromObject(c, pathComps, obj)
  136. if err != nil {
  137. // TODO 日志记录
  138. logger.Tracef("make cache file from object: %v", err)
  139. return nil
  140. }
  141. c.activeCache.CreateWords(pathComps).Value = ch
  142. return ch
  143. }
  144. // 创建一个缓存目录。如果目录已经存在,则会重置目录属性。如果加载过程中发生了错误,或者目标位置是一个文件,则会返回nil
  145. func (c *Cache) CreateDir(pathComps []string) *CacheDir {
  146. c.lock.Lock()
  147. defer c.lock.Unlock()
  148. ch, err := createNewCacheDir(c, pathComps)
  149. if err != nil {
  150. // TODO 日志记录
  151. return nil
  152. }
  153. return ch
  154. }
  155. type CreateDirOption struct {
  156. ModTime time.Time
  157. }
  158. // 加载指定缓存目录,如果目录不存在,则使用createOpt选项创建目录,而如果createOpt为nil,那么会返回nil。
  159. func (c *Cache) LoadDir(pathComps []string, createOpt *CreateDirOption) *CacheDir {
  160. c.lock.Lock()
  161. defer c.lock.Unlock()
  162. ch, err := loadCacheDir(c, pathComps)
  163. if err == nil {
  164. return ch
  165. }
  166. if !os.IsNotExist(err) {
  167. // TODO 日志记录
  168. return nil
  169. }
  170. if createOpt == nil {
  171. return nil
  172. }
  173. // 创建目录
  174. ch, err = makeCacheDirFromOption(c, pathComps, *createOpt)
  175. if err != nil {
  176. // TODO 日志记录
  177. return nil
  178. }
  179. return ch
  180. }
  181. // 加载指定路径下的所有缓存条目信息
  182. func (c *Cache) StatMany(pathComps []string) []CacheEntryInfo {
  183. c.lock.RLock()
  184. defer c.lock.RUnlock()
  185. var infos []CacheEntryInfo
  186. exists := make(map[string]bool)
  187. node, ok := c.activeCache.WalkEnd(pathComps)
  188. if ok {
  189. for name, child := range node.WordNexts {
  190. if child.Value != nil {
  191. infos = append(infos, child.Value.Info())
  192. exists[name] = true
  193. }
  194. }
  195. }
  196. osEns, err := os.ReadDir(c.GetCacheMetaPath(pathComps...))
  197. if err != nil {
  198. return nil
  199. }
  200. for _, e := range osEns {
  201. if exists[e.Name()] {
  202. continue
  203. }
  204. if e.IsDir() {
  205. info, err := loadCacheDirInfo(c, append(lo2.ArrayClone(pathComps), e.Name()))
  206. if err != nil {
  207. continue
  208. }
  209. infos = append(infos, *info)
  210. } else {
  211. info, err := loadCacheFileInfo(c, append(lo2.ArrayClone(pathComps), e.Name()))
  212. if err != nil {
  213. continue
  214. }
  215. infos = append(infos, *info)
  216. }
  217. }
  218. return infos
  219. }
  220. // 删除指定路径的缓存文件或目录。删除目录时如果目录不为空,则会报错。
  221. func (c *Cache) Remove(pathComps []string) error {
  222. c.lock.Lock()
  223. defer c.lock.Unlock()
  224. node, ok := c.activeCache.WalkEnd(pathComps)
  225. if ok {
  226. if len(node.WordNexts) > 0 {
  227. return fuse.ErrNotEmpty
  228. }
  229. if node.Value != nil {
  230. node.Value.Delete()
  231. }
  232. node.RemoveSelf(true)
  233. return nil
  234. }
  235. metaPath := c.GetCacheMetaPath(pathComps...)
  236. err := os.Remove(metaPath)
  237. if err == nil || os.IsNotExist(err) {
  238. return nil
  239. }
  240. if errors.Is(err, syscall.ENOTEMPTY) {
  241. return fuse.ErrNotEmpty
  242. }
  243. return err
  244. }
  245. // 移动指定路径的缓存文件或目录到新的路径。如果目标路径已经存在,则会报错。
  246. //
  247. // 如果移动成功,则返回移动后的缓存文件或目录。如果文件或目录不存在,则返回nil。
  248. func (c *Cache) Move(pathComps []string, newPathComps []string) error {
  249. c.lock.Lock()
  250. defer c.lock.Unlock()
  251. _, ok := c.activeCache.WalkEnd(newPathComps)
  252. if ok {
  253. return fuse.ErrExists
  254. }
  255. newMetaPath := c.GetCacheMetaPath(newPathComps...)
  256. newDataPath := c.GetCacheDataPath(newPathComps...)
  257. _, err := os.Stat(newMetaPath)
  258. if err == nil {
  259. return fuse.ErrExists
  260. } else if !os.IsNotExist(err) {
  261. return err
  262. }
  263. metaPath := c.GetCacheMetaPath(pathComps...)
  264. dataPath := c.GetCacheDataPath(pathComps...)
  265. // 每个缓存文件持有meta文件和data文件的句柄,所以这里移动文件,不影响句柄的使用。
  266. // 只能忽略这里的错误
  267. os.Rename(metaPath, newMetaPath)
  268. os.Rename(dataPath, newDataPath)
  269. // 更新缓存
  270. oldNode, ok := c.activeCache.WalkEnd(pathComps)
  271. if ok {
  272. newNode := c.activeCache.CreateWords(newPathComps)
  273. newNode.Value = oldNode.Value
  274. newNode.WordNexts = oldNode.WordNexts
  275. oldNode.RemoveSelf(false)
  276. if newNode.Value != nil {
  277. newNode.Value.Move(newPathComps)
  278. }
  279. newNode.Iterate(func(path []string, node *trie.Node[*CacheFile], isWordNode bool) trie.VisitCtrl {
  280. if node.Value != nil {
  281. node.Value.Move(lo2.AppendNew(newPathComps, path...))
  282. }
  283. return trie.VisitContinue
  284. })
  285. }
  286. return nil
  287. }

本项目旨在将云际存储公共基础设施化,使个人及企业可低门槛使用高效的云际存储服务(安装开箱即用云际存储客户端即可,无需关注其他组件的部署),同时支持用户灵活便捷定制云际存储的功能细节。