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.

file.go 12 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. package cache
  2. import (
  3. "fmt"
  4. "os"
  5. "sync"
  6. "time"
  7. "gitlink.org.cn/cloudream/common/pkgs/logger"
  8. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  9. "gitlink.org.cn/cloudream/common/utils/lo2"
  10. "gitlink.org.cn/cloudream/common/utils/math2"
  11. "gitlink.org.cn/cloudream/common/utils/serder"
  12. "gitlink.org.cn/cloudream/storage/client2/internal/mount/fuse"
  13. )
  14. type FileInfo struct {
  15. // 文件总大小。可能会超过对应的远端文件的大小。
  16. // 此大小可能与本地缓存文件大小也不同,需要定时将本地缓存文件大小修正到与这个值相同。
  17. Size int64
  18. // 本文件是否有未提交的修改
  19. Dirty bool
  20. // 数据段列表,按照段开始位置从小到大排列
  21. Segments []*Range
  22. // 文件对应的对象ID,仅在文件是一个缓存文件时才有值
  23. // ObjectID cdssdk.ObjectID
  24. // 文件对应的对象大小,仅在文件是一个缓存文件时才有值。
  25. // 此值代表有多少数据应该从远端加载,所以可能会小于远端实际大小
  26. ObjectSize int64
  27. // 如果本文件完全是一个缓存文件,那么这个字段记录了其内容的哈希值,用于在下载缓存数据时,检查远端文件是否被修改过
  28. // Hash cdssdk.FileHash
  29. // 文件的最后修改时间
  30. ModTime time.Time
  31. // 文件的权限
  32. Perm os.FileMode
  33. }
  34. func (f *FileInfo) Clone() FileInfo {
  35. n := *f
  36. n.Segments = make([]*Range, len(f.Segments))
  37. for i, seg := range f.Segments {
  38. n.Segments[i] = &Range{
  39. Position: seg.Position,
  40. Length: seg.Length,
  41. }
  42. }
  43. return n
  44. }
  45. type Range struct {
  46. Position int64
  47. Length int64
  48. }
  49. func (r *Range) GetPosition() int64 {
  50. return r.Position
  51. }
  52. func (r *Range) SetPosition(pos int64) {
  53. r.Position = pos
  54. }
  55. func (r *Range) GetLength() int64 {
  56. return r.Length
  57. }
  58. func (r *Range) SetLength(length int64) {
  59. r.Length = length
  60. }
  61. func (r *Range) End() int64 {
  62. return r.Position + r.Length
  63. }
  64. // 所有读写过程共用同一个CacheFile对象。
  65. // 不应该将此结构体保存到对象中
  66. type CacheFile struct {
  67. cache *Cache
  68. pathComps []string
  69. name string
  70. info FileInfo
  71. remoteObj *cdssdk.Object
  72. infoRev int64
  73. rwLock *sync.RWMutex
  74. readers []*CacheFileReadWriter
  75. writers []*CacheFileReadWriter
  76. backgroundChan chan any
  77. localFile *os.File
  78. writeLock *sync.RWMutex
  79. }
  80. func createNewCacheFile(cache *Cache, pathComps []string) (*CacheFile, error) {
  81. metaPath := cache.GetCacheMetaPath(pathComps...)
  82. dataPath := cache.GetCacheDataPath(pathComps...)
  83. info := FileInfo{
  84. Dirty: true,
  85. ModTime: time.Now(),
  86. Perm: 0644,
  87. }
  88. infoData, err := serder.ObjectToJSON(info)
  89. if err != nil {
  90. return nil, err
  91. }
  92. err = os.WriteFile(metaPath, infoData, 0644)
  93. if err != nil {
  94. return nil, fmt.Errorf("save cache file: %w", err)
  95. }
  96. localFile, err := os.OpenFile(dataPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
  97. if err != nil {
  98. return nil, fmt.Errorf("create cache file: %w", err)
  99. }
  100. ch := &CacheFile{
  101. cache: cache,
  102. pathComps: pathComps,
  103. name: pathComps[len(pathComps)-1],
  104. info: info,
  105. rwLock: &sync.RWMutex{},
  106. backgroundChan: make(chan any, 1),
  107. localFile: localFile,
  108. writeLock: &sync.RWMutex{},
  109. }
  110. go ch.background()
  111. return ch, nil
  112. }
  113. func loadCacheFile(cache *Cache, pathComps []string) (*CacheFile, error) {
  114. metaPath := cache.GetCacheMetaPath(pathComps...)
  115. dataPath := cache.GetCacheDataPath(pathComps...)
  116. metaData, err := os.ReadFile(metaPath)
  117. if err != nil {
  118. return nil, err
  119. }
  120. info := &FileInfo{}
  121. err = serder.JSONToObject(metaData, info)
  122. if err != nil {
  123. return nil, err
  124. }
  125. localFile, err := os.OpenFile(dataPath, os.O_CREATE|os.O_RDWR, 0644)
  126. if err != nil {
  127. return nil, fmt.Errorf("create cache file: %w", err)
  128. }
  129. ch := &CacheFile{
  130. cache: cache,
  131. pathComps: pathComps,
  132. name: pathComps[len(pathComps)-1],
  133. info: *info,
  134. rwLock: &sync.RWMutex{},
  135. backgroundChan: make(chan any, 1),
  136. localFile: localFile,
  137. writeLock: &sync.RWMutex{},
  138. }
  139. go ch.background()
  140. return ch, nil
  141. }
  142. func makeCacheFileFromObject(cache *Cache, pathComps []string, obj *cdssdk.Object) (*CacheFile, error) {
  143. metaPath := cache.GetCacheMetaPath(pathComps...)
  144. dataPath := cache.GetCacheDataPath(pathComps...)
  145. info := FileInfo{
  146. Size: obj.Size,
  147. ObjectSize: obj.Size,
  148. ModTime: obj.UpdateTime,
  149. Perm: 0755,
  150. }
  151. infoData, err := serder.ObjectToJSON(info)
  152. if err != nil {
  153. return nil, err
  154. }
  155. err = os.WriteFile(metaPath, infoData, 0644)
  156. if err != nil {
  157. return nil, fmt.Errorf("save cache file: %w", err)
  158. }
  159. localFile, err := os.OpenFile(dataPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
  160. if err != nil {
  161. return nil, fmt.Errorf("create cache file: %w", err)
  162. }
  163. ch := &CacheFile{
  164. cache: cache,
  165. pathComps: pathComps,
  166. name: pathComps[len(pathComps)-1],
  167. info: info,
  168. remoteObj: obj,
  169. rwLock: &sync.RWMutex{},
  170. backgroundChan: make(chan any, 1),
  171. localFile: localFile,
  172. writeLock: &sync.RWMutex{},
  173. }
  174. go ch.background()
  175. return ch, nil
  176. }
  177. func loadCacheFileInfo(cache *Cache, pathComps []string) (*CacheEntryInfo, error) {
  178. metaPath := cache.GetCacheMetaPath(pathComps...)
  179. metaData, err := os.ReadFile(metaPath)
  180. if err != nil {
  181. return nil, err
  182. }
  183. info := &FileInfo{}
  184. err = serder.JSONToObject(metaData, info)
  185. if err != nil {
  186. return nil, err
  187. }
  188. return &CacheEntryInfo{
  189. PathComps: pathComps,
  190. Size: info.Size,
  191. Mode: info.Perm,
  192. ModTime: info.ModTime,
  193. IsDir: false,
  194. }, nil
  195. }
  196. func (f *CacheFile) PathComps() []string {
  197. return f.pathComps
  198. }
  199. func (f *CacheFile) Name() string {
  200. return f.name
  201. }
  202. func (f *CacheFile) Size() int64 {
  203. return f.info.Size
  204. }
  205. func (f *CacheFile) Mode() os.FileMode {
  206. return f.info.Perm
  207. }
  208. func (f *CacheFile) ModTime() time.Time {
  209. return f.info.ModTime
  210. }
  211. func (f *CacheFile) IsDir() bool {
  212. return false
  213. }
  214. func (f *CacheFile) Info() CacheEntryInfo {
  215. return CacheEntryInfo{
  216. PathComps: f.pathComps,
  217. Size: f.info.Size,
  218. Mode: f.info.Perm,
  219. ModTime: f.info.ModTime,
  220. IsDir: false,
  221. }
  222. }
  223. func (f *CacheFile) SetRemoteObject(obj *cdssdk.Object) {
  224. f.remoteObj = obj
  225. }
  226. // 打开一个写入句柄,同时支持读取
  227. func (f *CacheFile) Open(readOnly bool) *CacheFileReadWriter {
  228. logger.Tracef("CacheFile.Open: %v, %v", f.name, readOnly)
  229. f.rwLock.Lock()
  230. defer f.rwLock.Unlock()
  231. h := &CacheFileReadWriter{
  232. file: f,
  233. readOnly: readOnly,
  234. remoteLock: &sync.Mutex{},
  235. }
  236. if f.remoteObj != nil {
  237. h.remote = newRemoteLoader(f)
  238. }
  239. if readOnly {
  240. f.readers = append(f.readers, h)
  241. } else {
  242. f.writers = append(f.writers, h)
  243. }
  244. return h
  245. }
  246. func (f *CacheFile) SetModTime(modTime time.Time) error {
  247. f.rwLock.Lock()
  248. f.info.ModTime = modTime
  249. f.infoRev++
  250. f.rwLock.Unlock()
  251. f.notifyBackground()
  252. return nil
  253. }
  254. func (f *CacheFile) Truncate(size int64) error {
  255. // 修改文件大小前不允许写入
  256. f.writeLock.Lock()
  257. defer f.writeLock.Unlock()
  258. err := f.localFile.Truncate(size)
  259. if err != nil {
  260. return err
  261. }
  262. f.rwLock.Lock()
  263. defer f.rwLock.Unlock()
  264. // 调整能从远端下载的大小
  265. f.info.ObjectSize = math2.Min(f.info.ObjectSize, size)
  266. // 调整本地缓存文件里的有效数据大小
  267. if size < f.info.Size {
  268. f.info.Segments = TruncateRange(f.info.Segments, size)
  269. } else {
  270. f.info.Segments = AddRange(f.info.Segments, &Range{Position: f.info.Size, Length: size - f.info.Size})
  271. }
  272. f.info.Size = size
  273. f.infoRev++
  274. f.notifyBackground()
  275. return nil
  276. }
  277. // 不再使用缓存文件
  278. // func (f *CacheFile) Release() {
  279. // }
  280. func (f *CacheFile) background() {
  281. savedInfoRev := int64(0)
  282. ticker := time.NewTicker(time.Second * 5)
  283. defer ticker.Stop()
  284. for {
  285. select {
  286. case _, ok := <-f.backgroundChan:
  287. if !ok {
  288. return
  289. }
  290. case <-ticker.C:
  291. }
  292. f.rwLock.Lock()
  293. for {
  294. if f.infoRev == savedInfoRev {
  295. break
  296. }
  297. jsonData, err := serder.ObjectToJSON(f.info)
  298. if err != nil {
  299. // TODO 日志
  300. break
  301. }
  302. err = os.WriteFile(f.cache.GetCacheMetaPath(f.pathComps...), jsonData, 0644)
  303. if err != nil {
  304. // TODO 日志
  305. break
  306. }
  307. savedInfoRev = f.infoRev
  308. break
  309. }
  310. f.rwLock.Unlock()
  311. }
  312. }
  313. func (f *CacheFile) notifyBackground() {
  314. select {
  315. case f.backgroundChan <- nil:
  316. default:
  317. }
  318. }
  319. type CacheFileReadWriter struct {
  320. file *CacheFile
  321. readOnly bool
  322. remote *RemoteLoader
  323. remoteLock *sync.Mutex
  324. }
  325. func (h *CacheFileReadWriter) ReadAt(buf []byte, off int64) (int, error) {
  326. logger.Tracef("CacheFileReadWriter.ReadAt: %v, %v, %v", h.file.name, off, len(buf))
  327. totalReadLen := 0
  328. for totalReadLen < len(buf) {
  329. curBuf := buf[totalReadLen:]
  330. curOff := off + int64(totalReadLen)
  331. h.file.rwLock.RLock()
  332. if curOff >= h.file.info.Size {
  333. h.file.rwLock.RUnlock()
  334. break
  335. }
  336. // 先尝试从本地缓存文件里读取
  337. rngIdx := FirstContainsIndex(h.file.info.Segments, curOff)
  338. if rngIdx >= 0 && h.file.info.Segments[rngIdx].End() > curOff {
  339. readLen := math2.Min(int64(len(curBuf)), h.file.info.Segments[rngIdx].End()-curOff)
  340. realReadLen, err := h.file.localFile.ReadAt(curBuf[:readLen], curOff)
  341. totalReadLen += realReadLen
  342. h.file.rwLock.RUnlock()
  343. logger.Tracef("read from local cache, n: %v, err: %v", realReadLen, err)
  344. if err != nil {
  345. return totalReadLen, err
  346. }
  347. continue
  348. }
  349. // 否则从远端下载
  350. loadLen := math2.Min(int64(len(curBuf)), h.file.info.ObjectSize-curOff)
  351. if rngIdx+1 < len(h.file.info.Segments) {
  352. // 最多加载到下一个段的开头
  353. loadLen = math2.Min(loadLen, h.file.info.Segments[rngIdx+1].Position-curOff)
  354. }
  355. h.file.rwLock.RUnlock()
  356. if h.remote == nil {
  357. return totalReadLen, fmt.Errorf("no remote file")
  358. }
  359. fmt.Printf("load from remote\n")
  360. // 加锁,防止并发Seek
  361. h.remoteLock.Lock()
  362. realLoadLen, err := h.remote.Load(curBuf[:loadLen], curOff)
  363. totalReadLen += realLoadLen
  364. if err != nil {
  365. h.remoteLock.Unlock()
  366. return totalReadLen, err
  367. }
  368. h.remoteLock.Unlock()
  369. logger.Tracef("load from remote: %v", realLoadLen)
  370. // 在写入到本地之前,先停止其他的写入,防止冲突
  371. h.file.writeLock.Lock()
  372. // 停止其他写入后,就可以计算一下实际要写回的长度。
  373. h.file.rwLock.RLock()
  374. loadRng := &Range{Position: curOff, Length: int64(realLoadLen)}
  375. DifferentRange(loadRng, h.file.info.Segments)
  376. h.file.rwLock.RUnlock()
  377. if loadRng.Length == 0 {
  378. h.file.writeLock.Unlock()
  379. continue
  380. }
  381. // 写入到本地缓存文件
  382. writeStart := loadRng.Position - curOff
  383. _, err = h.file.localFile.WriteAt(curBuf[writeStart:writeStart+loadRng.Length], curOff)
  384. if err != nil {
  385. h.file.writeLock.Unlock()
  386. logger.Tracef("save to local file: %v", err)
  387. return totalReadLen, fmt.Errorf("save to local file: %w", err)
  388. }
  389. logger.Tracef("save to local: %v", loadRng.Length)
  390. h.file.writeLock.Unlock()
  391. // 提交到段列表里
  392. h.file.rwLock.Lock()
  393. h.file.info.Segments = AddRange(h.file.info.Segments, loadRng)
  394. h.file.infoRev++
  395. h.file.notifyBackground()
  396. h.file.rwLock.Unlock()
  397. }
  398. return totalReadLen, nil
  399. }
  400. func (h *CacheFileReadWriter) WriteAt(buf []byte, off int64) (int, error) {
  401. if h.readOnly {
  402. return 0, fuse.ErrPermission
  403. }
  404. logger.Tracef("CacheFileReadWriter.WriteAt: %v, %v, %v", h.file.name, off, len(buf))
  405. // 允许多线程并行写入,但在数据加载期间不能写入
  406. h.file.writeLock.RLock()
  407. defer h.file.writeLock.RUnlock()
  408. // 写入到本地缓存文件
  409. writeLen, err := h.file.localFile.WriteAt(buf, off)
  410. if err != nil {
  411. return writeLen, fmt.Errorf("save to local file: %w", err)
  412. }
  413. // 提交到段列表里
  414. h.file.rwLock.Lock()
  415. defer h.file.rwLock.Unlock()
  416. h.file.info.Segments = AddRange(h.file.info.Segments, &Range{Position: off, Length: int64(writeLen)})
  417. h.file.info.Size = math2.Max(h.file.info.Size, off+int64(writeLen))
  418. h.file.info.Dirty = true
  419. h.file.infoRev++
  420. h.file.notifyBackground()
  421. return writeLen, nil
  422. }
  423. func (f *CacheFileReadWriter) Sync() error {
  424. return f.file.localFile.Sync()
  425. }
  426. func (f *CacheFileReadWriter) Close() error {
  427. f.Sync()
  428. f.remote.Close()
  429. f.file.rwLock.Lock()
  430. if f.readOnly {
  431. f.file.readers = lo2.Remove(f.file.readers, f)
  432. } else {
  433. f.file.writers = lo2.Remove(f.file.writers, f)
  434. }
  435. f.file.rwLock.Unlock()
  436. return nil
  437. }

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