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 17 kB

8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753
  1. package cache
  2. import (
  3. "fmt"
  4. "io"
  5. "os"
  6. "sync"
  7. "time"
  8. "gitlink.org.cn/cloudream/common/pkgs/logger"
  9. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  10. "gitlink.org.cn/cloudream/common/utils/io2"
  11. "gitlink.org.cn/cloudream/common/utils/lo2"
  12. "gitlink.org.cn/cloudream/common/utils/math2"
  13. "gitlink.org.cn/cloudream/common/utils/serder"
  14. "gitlink.org.cn/cloudream/storage2/client2/internal/mount/fuse"
  15. )
  16. type FileInfo struct {
  17. // 文件总大小。可能会超过对应的远端文件的大小。
  18. // 此大小可能与本地缓存文件大小也不同,需要定时将本地缓存文件大小修正到与这个值相同。
  19. Size int64
  20. // 如果大于0,则代表有未提交的修改
  21. Revision int
  22. // 数据段列表,按照段开始位置从小到大排列
  23. Segments []*Range
  24. // 文件对应的对象ID,仅在文件是一个缓存文件时才有值
  25. // ObjectID cdssdk.ObjectID
  26. // 文件对应的对象大小,仅在文件是一个缓存文件时才有值。
  27. // 此值代表有多少数据应该从远端加载,所以可能会小于远端实际大小
  28. ObjectSize int64
  29. // 如果本文件完全是一个缓存文件,那么这个字段记录了其内容的哈希值,用于在下载缓存数据时,检查远端文件是否被修改过
  30. // Hash cdssdk.FileHash
  31. // 文件的最后修改时间
  32. ModTime time.Time
  33. // 文件的权限
  34. Perm os.FileMode
  35. }
  36. func (f *FileInfo) Clone() FileInfo {
  37. n := *f
  38. n.Segments = make([]*Range, len(f.Segments))
  39. for i, seg := range f.Segments {
  40. n.Segments[i] = &Range{
  41. Position: seg.Position,
  42. Length: seg.Length,
  43. }
  44. }
  45. return n
  46. }
  47. type Range struct {
  48. Position int64
  49. Length int64
  50. }
  51. func (r *Range) GetPosition() int64 {
  52. return r.Position
  53. }
  54. func (r *Range) SetPosition(pos int64) {
  55. r.Position = pos
  56. }
  57. func (r *Range) GetLength() int64 {
  58. return r.Length
  59. }
  60. func (r *Range) SetLength(length int64) {
  61. r.Length = length
  62. }
  63. func (r *Range) End() int64 {
  64. return r.Position + r.Length
  65. }
  66. // 所有读写过程共用同一个CacheFile对象。
  67. // 不应该将此结构体保存到对象中
  68. type CacheFile struct {
  69. cache *Cache
  70. pathComps []string
  71. info FileInfo
  72. remoteObj *cdssdk.Object
  73. rwLock *sync.RWMutex
  74. readers []*CacheFileHandle
  75. writers []*CacheFileHandle
  76. saveMetaChan chan any
  77. noSaveMeta bool // 防止在Unload之后又保存了文件
  78. isDeleted bool
  79. metaFile *os.File
  80. dataFile *os.File
  81. writeLock *sync.RWMutex
  82. // 缓存文件的状态,用于管理缓存文件的生命周期。不受rwLock保护,而是由Cache管理
  83. state cacheState
  84. }
  85. type cacheState struct {
  86. refCount int
  87. freeTime time.Time
  88. unloadTime time.Time
  89. isLoaded bool
  90. uploading *uploadingObject
  91. }
  92. func createNewCacheFile(cache *Cache, pathComps []string) (*CacheFile, error) {
  93. metaPath := cache.GetCacheMetaPath(pathComps...)
  94. dataPath := cache.GetCacheDataPath(pathComps...)
  95. info := FileInfo{
  96. Revision: 1,
  97. ModTime: time.Now(),
  98. Perm: 0777,
  99. }
  100. infoData, err := serder.ObjectToJSON(info)
  101. if err != nil {
  102. return nil, err
  103. }
  104. metaFile, err := os.OpenFile(metaPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
  105. if err != nil {
  106. return nil, fmt.Errorf("create cache meta file: %w", err)
  107. }
  108. err = io2.WriteAll(metaFile, infoData)
  109. if err != nil {
  110. metaFile.Close()
  111. return nil, fmt.Errorf("save cache meta file: %w", err)
  112. }
  113. dataFile, err := os.OpenFile(dataPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
  114. if err != nil {
  115. metaFile.Close()
  116. return nil, fmt.Errorf("create cache data file: %w", err)
  117. }
  118. ch := &CacheFile{
  119. cache: cache,
  120. pathComps: pathComps,
  121. info: info,
  122. rwLock: &sync.RWMutex{},
  123. saveMetaChan: make(chan any, 1),
  124. metaFile: metaFile,
  125. dataFile: dataFile,
  126. writeLock: &sync.RWMutex{},
  127. state: cacheState{
  128. isLoaded: true,
  129. },
  130. }
  131. go ch.serving(ch.saveMetaChan)
  132. return ch, nil
  133. }
  134. func loadCacheFile(cache *Cache, pathComps []string) (*CacheFile, error) {
  135. metaPath := cache.GetCacheMetaPath(pathComps...)
  136. dataPath := cache.GetCacheDataPath(pathComps...)
  137. metaFile, err := os.OpenFile(metaPath, os.O_RDWR, 0644)
  138. if err != nil {
  139. // 不要包装这里的err
  140. return nil, err
  141. }
  142. info := &FileInfo{}
  143. err = serder.JSONToObjectStream(metaFile, info)
  144. if err != nil {
  145. metaFile.Close()
  146. return nil, err
  147. }
  148. dataFile, err := os.OpenFile(dataPath, os.O_RDWR, 0644)
  149. if err != nil {
  150. metaFile.Close()
  151. // 不要包装这里的err
  152. return nil, err
  153. }
  154. ch := &CacheFile{
  155. cache: cache,
  156. pathComps: pathComps,
  157. info: *info,
  158. rwLock: &sync.RWMutex{},
  159. saveMetaChan: make(chan any, 1),
  160. metaFile: metaFile,
  161. dataFile: dataFile,
  162. writeLock: &sync.RWMutex{},
  163. state: cacheState{
  164. isLoaded: true,
  165. },
  166. }
  167. go ch.serving(ch.saveMetaChan)
  168. return ch, nil
  169. }
  170. func newCacheFileFromObject(cache *Cache, pathComps []string, obj *cdssdk.Object) (*CacheFile, error) {
  171. metaPath := cache.GetCacheMetaPath(pathComps...)
  172. dataPath := cache.GetCacheDataPath(pathComps...)
  173. info := FileInfo{
  174. Size: obj.Size,
  175. ObjectSize: obj.Size,
  176. ModTime: obj.UpdateTime,
  177. Perm: 0755,
  178. }
  179. infoData, err := serder.ObjectToJSON(info)
  180. if err != nil {
  181. return nil, err
  182. }
  183. metaFile, err := os.OpenFile(metaPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
  184. if err != nil {
  185. return nil, fmt.Errorf("create cache meta file: %w", err)
  186. }
  187. err = io2.WriteAll(metaFile, infoData)
  188. if err != nil {
  189. metaFile.Close()
  190. return nil, fmt.Errorf("save cache meta file: %w", err)
  191. }
  192. dataFile, err := os.OpenFile(dataPath, os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
  193. if err != nil {
  194. metaFile.Close()
  195. return nil, fmt.Errorf("create cache file: %w", err)
  196. }
  197. ch := &CacheFile{
  198. cache: cache,
  199. pathComps: pathComps,
  200. info: info,
  201. remoteObj: obj,
  202. rwLock: &sync.RWMutex{},
  203. saveMetaChan: make(chan any, 1),
  204. metaFile: metaFile,
  205. dataFile: dataFile,
  206. writeLock: &sync.RWMutex{},
  207. state: cacheState{
  208. isLoaded: true,
  209. },
  210. }
  211. go ch.serving(ch.saveMetaChan)
  212. return ch, nil
  213. }
  214. func loadCacheFileInfo(cache *Cache, pathComps []string) (*CacheEntryInfo, error) {
  215. metaPath := cache.GetCacheMetaPath(pathComps...)
  216. metaData, err := os.ReadFile(metaPath)
  217. if err != nil {
  218. return nil, err
  219. }
  220. info := &FileInfo{}
  221. err = serder.JSONToObject(metaData, info)
  222. if err != nil {
  223. return nil, err
  224. }
  225. return &CacheEntryInfo{
  226. PathComps: pathComps,
  227. Size: info.Size,
  228. Mode: info.Perm,
  229. ModTime: info.ModTime,
  230. IsDir: false,
  231. }, nil
  232. }
  233. // 加载缓存文件。如果已经加载了,则无任何效果
  234. func (f *CacheFile) Load() error {
  235. f.rwLock.Lock()
  236. defer f.rwLock.Unlock()
  237. if f.isDeleted {
  238. return fmt.Errorf("cache deleted")
  239. }
  240. metaPath := f.cache.GetCacheMetaPath(f.pathComps...)
  241. dataPath := f.cache.GetCacheDataPath(f.pathComps...)
  242. metaFile, err := os.OpenFile(metaPath, os.O_RDWR, 0644)
  243. if err != nil {
  244. return err
  245. }
  246. dataFile, err := os.OpenFile(dataPath, os.O_RDWR, 0644)
  247. if err != nil {
  248. metaFile.Close()
  249. return err
  250. }
  251. f.saveMetaChan = make(chan any)
  252. f.noSaveMeta = false
  253. f.metaFile = metaFile
  254. f.dataFile = dataFile
  255. go f.serving(f.saveMetaChan)
  256. return nil
  257. }
  258. // 关闭缓存文件,保存元数据。但缓存对象依然会留在内存里,以备随时查询元数据。
  259. //
  260. // 只应该在引用计数为0时调用。
  261. func (f *CacheFile) Unload() {
  262. f.rwLock.Lock()
  263. defer f.rwLock.Unlock()
  264. if !f.isDeleted {
  265. // TODO 日志
  266. f.saveMeta(f.info)
  267. }
  268. // 防止在关闭缓存后又保存了文件
  269. close(f.saveMetaChan)
  270. f.saveMetaChan = nil
  271. f.noSaveMeta = true
  272. f.metaFile.Close()
  273. f.dataFile.Close()
  274. }
  275. // 可在Unload状态下调用
  276. func (f *CacheFile) RevisionUploaded(rev int) {
  277. f.rwLock.Lock()
  278. defer f.rwLock.Unlock()
  279. if f.info.Revision == rev {
  280. f.info.Revision = 0
  281. }
  282. if f.saveMetaChan != nil {
  283. f.letSave()
  284. }
  285. }
  286. // 可在Unload状态下调用
  287. func (f *CacheFile) Info() CacheEntryInfo {
  288. return CacheEntryInfo{
  289. PathComps: f.pathComps,
  290. Size: f.info.Size,
  291. Mode: f.info.Perm,
  292. ModTime: f.info.ModTime,
  293. IsDir: false,
  294. }
  295. }
  296. func (f *CacheFile) Revision() int {
  297. f.rwLock.RLock()
  298. defer f.rwLock.RUnlock()
  299. return f.info.Revision
  300. }
  301. // 可在Unload状态下调用
  302. func (f *CacheFile) Delete() {
  303. f.writeLock.Lock()
  304. defer f.writeLock.Unlock()
  305. f.rwLock.Lock()
  306. defer f.rwLock.Unlock()
  307. metaPath := f.cache.GetCacheMetaPath(f.pathComps...)
  308. dataPath := f.cache.GetCacheDataPath(f.pathComps...)
  309. os.Remove(metaPath)
  310. os.Remove(dataPath)
  311. // 可能是在被使用状态下删除,也可能是在Unload状态下删除,所以这里不关闭saveMetaChan,而是设置isDeleted为true
  312. f.isDeleted = true
  313. if f.saveMetaChan != nil {
  314. f.letSave()
  315. }
  316. }
  317. // 可在Unload状态下调用
  318. func (f *CacheFile) Move(newPathComps []string) {
  319. f.writeLock.Lock()
  320. defer f.writeLock.Unlock()
  321. f.rwLock.Lock()
  322. defer f.rwLock.Unlock()
  323. f.pathComps = newPathComps
  324. if f.saveMetaChan != nil {
  325. f.letSave()
  326. }
  327. }
  328. // 打开一个写入句柄,同时支持读取
  329. //
  330. // 不可在Unload状态下调用!
  331. func (f *CacheFile) Open(flags uint32) *CacheFileHandle {
  332. logger.Tracef("CacheFile.Open: %v, %#x", f.pathComps, flags)
  333. f.cache.lock.Lock()
  334. f.state.refCount++
  335. f.cache.lock.Unlock()
  336. f.rwLock.Lock()
  337. defer f.rwLock.Unlock()
  338. h := &CacheFileHandle{
  339. file: f,
  340. remoteLock: &sync.Mutex{},
  341. revision: f.info.Revision,
  342. }
  343. if flags&uint32(os.O_RDWR) == uint32(os.O_RDWR) {
  344. h.readable = true
  345. h.writeable = true
  346. } else if flags&uint32(os.O_WRONLY) == uint32(os.O_WRONLY) {
  347. h.writeable = true
  348. } else if flags&uint32(os.O_RDONLY) == uint32(os.O_RDONLY) {
  349. h.readable = true
  350. }
  351. if f.remoteObj != nil {
  352. h.remote = newRemoteLoader(f)
  353. }
  354. if h.writeable {
  355. f.writers = append(f.writers, h)
  356. } else {
  357. f.readers = append(f.readers, h)
  358. }
  359. return h
  360. }
  361. // 打开一个读取句柄,用于同步本地文件到远端。由于此方法会在扫描缓存时调用,所以refCount增加时不需要加锁
  362. //
  363. // 不可在Unload状态下调用!
  364. func (f *CacheFile) OpenReadWhenScanning() *CacheFileHandle {
  365. f.rwLock.Lock()
  366. defer f.rwLock.Unlock()
  367. f.state.refCount++
  368. h := &CacheFileHandle{
  369. file: f,
  370. remoteLock: &sync.Mutex{},
  371. revision: f.info.Revision,
  372. readable: true,
  373. }
  374. if f.remoteObj != nil {
  375. h.remote = newRemoteLoader(f)
  376. }
  377. f.readers = append(f.readers, h)
  378. return h
  379. }
  380. // 不可在Unload状态下调用!
  381. func (f *CacheFile) SetModTime(modTime time.Time) error {
  382. logger.Tracef("CacheFile.SetModTime: %v, %v", f.pathComps, modTime)
  383. f.rwLock.Lock()
  384. f.info.ModTime = modTime
  385. f.rwLock.Unlock()
  386. f.letSave()
  387. return nil
  388. }
  389. // 不可在Unload状态下调用!
  390. func (f *CacheFile) Truncate(size int64) error {
  391. logger.Tracef("CacheFile.Truncate: %v, %v", f.pathComps, size)
  392. // 修改文件大小前不允许写入
  393. f.writeLock.Lock()
  394. defer f.writeLock.Unlock()
  395. err := f.dataFile.Truncate(size)
  396. if err != nil {
  397. return err
  398. }
  399. f.rwLock.Lock()
  400. defer f.rwLock.Unlock()
  401. // 调整能从远端下载的大小
  402. f.info.ObjectSize = math2.Min(f.info.ObjectSize, size)
  403. // 调整本地缓存文件里的有效数据大小
  404. if size < f.info.Size {
  405. f.info.Segments = TruncateRange(f.info.Segments, size)
  406. } else if size > f.info.Size {
  407. f.info.Segments = AddRange(f.info.Segments, &Range{Position: f.info.Size, Length: size - f.info.Size})
  408. }
  409. if f.info.Size != size {
  410. f.info.Revision++
  411. }
  412. f.info.Size = size
  413. f.letSave()
  414. return nil
  415. }
  416. // 减少一个引用计数
  417. func (f *CacheFile) Release() {
  418. f.cache.lock.Lock()
  419. defer f.cache.lock.Unlock()
  420. f.state.refCount--
  421. if f.state.refCount == 0 {
  422. f.state.freeTime = time.Now()
  423. }
  424. }
  425. func (f *CacheFile) serving(saveMetaChan chan any) {
  426. ticker := time.NewTicker(time.Second * 5)
  427. defer ticker.Stop()
  428. for {
  429. select {
  430. case _, ok := <-saveMetaChan:
  431. if !ok {
  432. return
  433. }
  434. case <-ticker.C:
  435. }
  436. f.rwLock.RLock()
  437. info := f.info.Clone()
  438. // 如果文件已被删除,则不能再保存元数据,防止覆盖掉新创建的同名文件
  439. if f.isDeleted {
  440. f.rwLock.RUnlock()
  441. break
  442. }
  443. // 停止保存元数据的线程
  444. if f.noSaveMeta {
  445. f.rwLock.RUnlock()
  446. break
  447. }
  448. f.rwLock.RUnlock()
  449. // TODO 错误日志
  450. f.saveMeta(info)
  451. f.metaFile.Sync()
  452. }
  453. }
  454. func (f *CacheFile) saveMeta(info FileInfo) error {
  455. jsonData, err := serder.ObjectToJSON(info)
  456. if err != nil {
  457. return err
  458. }
  459. err = f.metaFile.Truncate(0)
  460. if err != nil {
  461. return err
  462. }
  463. _, err = f.metaFile.Seek(0, io.SeekStart)
  464. if err != nil {
  465. return err
  466. }
  467. err = io2.WriteAll(f.metaFile, jsonData)
  468. if err != nil {
  469. return err
  470. }
  471. return nil
  472. }
  473. func (f *CacheFile) letSave() {
  474. select {
  475. case f.saveMetaChan <- nil:
  476. default:
  477. }
  478. }
  479. type CacheFileHandle struct {
  480. file *CacheFile
  481. readable bool
  482. writeable bool
  483. remote *RemoteLoader
  484. remoteLock *sync.Mutex
  485. revision int // 打开文件时,文件的版本号
  486. }
  487. func (h *CacheFileHandle) ReadAt(buf []byte, off int64) (int, error) {
  488. if !h.readable {
  489. return 0, fuse.ErrPermission
  490. }
  491. logger.Tracef("CacheFileReadWriter.ReadAt: %v, %v, %v", h.file.pathComps, off, len(buf))
  492. // 读取数据必须读满整个buf,否则就会被认为是文件已经结束了
  493. totalReadLen := 0
  494. for totalReadLen < len(buf) {
  495. curBuf := buf[totalReadLen:]
  496. curOff := off + int64(totalReadLen)
  497. h.file.rwLock.RLock()
  498. if curOff >= h.file.info.Size {
  499. h.file.rwLock.RUnlock()
  500. break
  501. }
  502. /// 1. 先尝试从本地缓存文件里读取
  503. rngIdx := FirstContainsIndex(h.file.info.Segments, curOff)
  504. if rngIdx >= 0 && h.file.info.Segments[rngIdx].End() > curOff {
  505. readLen := math2.Min(int64(len(curBuf)), h.file.info.Segments[rngIdx].End()-curOff)
  506. realReadLen, err := h.file.dataFile.ReadAt(curBuf[:readLen], curOff)
  507. totalReadLen += realReadLen
  508. h.file.rwLock.RUnlock()
  509. logger.Tracef("read from local cache, n: %v, err: %v", realReadLen, err)
  510. if err != nil {
  511. return totalReadLen, err
  512. }
  513. continue
  514. }
  515. // 否则从远端下载,计算一下要加载的长度
  516. loadLen := math2.Min(int64(len(curBuf)), h.file.info.ObjectSize-curOff)
  517. if rngIdx+1 < len(h.file.info.Segments) {
  518. // 最多加载到下一个段的开头
  519. loadLen = math2.Min(loadLen, h.file.info.Segments[rngIdx+1].Position-curOff)
  520. }
  521. h.file.rwLock.RUnlock()
  522. /// 2. 开始从远端下载数据
  523. if h.remote == nil {
  524. return totalReadLen, fmt.Errorf("no remote file")
  525. }
  526. // 由于RemoteLoader的Load方法没有加锁,所以这里要加锁,防止并发Seek导致的问题
  527. // 可以考虑在RemoteLoader里加锁,这样可以实现跨Writer共用Loader
  528. h.remoteLock.Lock()
  529. realLoadLen, err := h.remote.Load(curBuf[:loadLen], curOff)
  530. totalReadLen += realLoadLen
  531. if err != nil {
  532. h.remoteLock.Unlock()
  533. return totalReadLen, err
  534. }
  535. h.remoteLock.Unlock()
  536. logger.Tracef("load from remote: %v", realLoadLen)
  537. /// 3. 数据加载完毕,写入到本地文件
  538. // 在写入到本地之前,先停止其他的写入,防止冲突
  539. h.file.writeLock.Lock()
  540. // 停止其他写入后,就可以计算一下实际要写回的长度。
  541. h.file.rwLock.RLock()
  542. loadRng := &Range{Position: curOff, Length: int64(realLoadLen)}
  543. DifferentRange(loadRng, h.file.info.Segments)
  544. h.file.rwLock.RUnlock()
  545. if loadRng.Length == 0 {
  546. h.file.writeLock.Unlock()
  547. continue
  548. }
  549. // 写入到本地缓存文件
  550. writeStart := loadRng.Position - curOff
  551. _, err = h.file.dataFile.WriteAt(curBuf[writeStart:writeStart+loadRng.Length], curOff)
  552. if err != nil {
  553. h.file.writeLock.Unlock()
  554. logger.Tracef("save to local file: %v", err)
  555. return totalReadLen, fmt.Errorf("save to local file: %w", err)
  556. }
  557. logger.Tracef("save to local: %v", loadRng.Length)
  558. h.file.writeLock.Unlock()
  559. // 提交到段列表里
  560. h.file.rwLock.Lock()
  561. h.file.info.Segments = AddRange(h.file.info.Segments, loadRng)
  562. h.file.rwLock.Unlock()
  563. h.file.letSave()
  564. }
  565. return totalReadLen, nil
  566. }
  567. func (h *CacheFileHandle) WriteAt(buf []byte, off int64) (int, error) {
  568. if !h.writeable {
  569. return 0, fuse.ErrPermission
  570. }
  571. logger.Tracef("CacheFileReadWriter.WriteAt: %v, %v, %v", h.file.pathComps, off, len(buf))
  572. // 允许多线程并行写入,但在数据加载期间不能写入
  573. h.file.writeLock.RLock()
  574. defer h.file.writeLock.RUnlock()
  575. // 写入到本地缓存文件
  576. writeLen, err := h.file.dataFile.WriteAt(buf, off)
  577. if err != nil {
  578. return writeLen, fmt.Errorf("save to local file: %w", err)
  579. }
  580. // 提交到段列表里
  581. h.file.rwLock.Lock()
  582. defer h.file.rwLock.Unlock()
  583. h.file.info.Segments = AddRange(h.file.info.Segments, &Range{Position: off, Length: int64(writeLen)})
  584. h.file.info.Size = math2.Max(h.file.info.Size, off+int64(writeLen))
  585. h.file.info.Revision++
  586. h.file.letSave()
  587. return writeLen, nil
  588. }
  589. func (f *CacheFileHandle) Sync() error {
  590. return f.file.dataFile.Sync()
  591. }
  592. func (f *CacheFileHandle) Close() error {
  593. f.Sync()
  594. if f.remote != nil {
  595. f.remote.Close()
  596. }
  597. f.file.cache.lock.Lock()
  598. f.file.state.refCount--
  599. if f.file.state.refCount == 0 {
  600. f.file.state.freeTime = time.Now()
  601. }
  602. f.file.cache.lock.Unlock()
  603. f.file.rwLock.Lock()
  604. defer f.file.rwLock.Unlock()
  605. if f.writeable {
  606. f.file.writers = lo2.Remove(f.file.writers, f)
  607. } else if f.readable {
  608. f.file.readers = lo2.Remove(f.file.readers, f)
  609. }
  610. return nil
  611. }

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