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.

fuse_bucket.go 6.1 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. package vfs
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "time"
  7. "gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
  8. "gitlink.org.cn/cloudream/jcs-pub/client/internal/mount/fuse"
  9. "gitlink.org.cn/cloudream/jcs-pub/client/internal/mount/vfs/cache"
  10. clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
  11. "gorm.io/gorm"
  12. )
  13. type FuseBucket struct {
  14. vfs *Vfs
  15. bktName string
  16. modTime time.Time
  17. perm os.FileMode
  18. }
  19. func newBucketFromCache(c cache.CacheEntryInfo, vfs *Vfs) fuse.FsDir {
  20. return &FuseBucket{
  21. vfs: vfs,
  22. bktName: c.PathComps[len(c.PathComps)-1],
  23. modTime: c.ModTime,
  24. perm: c.Perm,
  25. }
  26. }
  27. func (r *FuseBucket) PathComps() []string {
  28. return []string{r.bktName}
  29. }
  30. func (r *FuseBucket) Name() string {
  31. return r.bktName
  32. }
  33. func (r *FuseBucket) Size() int64 {
  34. return 0
  35. }
  36. func (r *FuseBucket) Perm() os.FileMode {
  37. return r.perm
  38. }
  39. func (r *FuseBucket) ModTime() time.Time {
  40. return r.modTime
  41. }
  42. func (r *FuseBucket) IsDir() bool {
  43. return true
  44. }
  45. func (r *FuseBucket) SetModTime(time time.Time) error {
  46. dir := r.loadCacheDir()
  47. if dir == nil {
  48. return fuse.ErrNotExists
  49. }
  50. return dir.SetModTime(time)
  51. }
  52. // 如果不存在,应该返回ErrNotExists
  53. func (r *FuseBucket) Child(ctx context.Context, name string) (fuse.FsEntry, error) {
  54. childPathComps := []string{r.bktName, name}
  55. ca := r.vfs.cache.Stat(childPathComps)
  56. if ca == nil {
  57. pkg, err := r.vfs.db.Package().GetByFullName(r.vfs.db.DefCtx(), r.bktName, name)
  58. if err == nil {
  59. dir := r.vfs.cache.LoadDir(childPathComps, &cache.CreateDirOption{
  60. ModTime: pkg.CreateTime,
  61. })
  62. if dir == nil {
  63. return nil, fuse.ErrNotExists
  64. }
  65. return newPackageFromCache(dir.Info(), r.vfs), nil
  66. }
  67. if err == gorm.ErrRecordNotFound {
  68. return nil, fuse.ErrNotExists
  69. }
  70. return nil, err
  71. }
  72. if ca.IsDir {
  73. return newPackageFromCache(*ca, r.vfs), nil
  74. }
  75. return newFileFromCache(*ca, r.vfs), nil
  76. }
  77. func (r *FuseBucket) Children(ctx context.Context) ([]fuse.FsEntry, error) {
  78. return r.listChildren()
  79. }
  80. func (r *FuseBucket) ReadChildren() (fuse.DirReader, error) {
  81. ens, err := r.listChildren()
  82. if err != nil {
  83. return nil, err
  84. }
  85. return newFuseDirReader(ens), nil
  86. }
  87. func (r *FuseBucket) listChildren() ([]fuse.FsEntry, error) {
  88. var ens []fuse.FsEntry
  89. infos := r.vfs.cache.StatMany([]string{r.bktName})
  90. pkgs, err := r.vfs.db.Package().GetBucketPackagesByName(r.vfs.db.DefCtx(), r.bktName)
  91. if err != nil {
  92. return nil, err
  93. }
  94. pkgMap := make(map[string]*clitypes.Package)
  95. for _, pkg := range pkgs {
  96. p := pkg
  97. pkgMap[pkg.Name] = &p
  98. }
  99. for _, c := range infos {
  100. delete(pkgMap, c.PathComps[len(c.PathComps)-1])
  101. if c.IsDir {
  102. ens = append(ens, newPackageFromCache(c, r.vfs))
  103. } else {
  104. ens = append(ens, newFileFromCache(c, r.vfs))
  105. }
  106. }
  107. // 顺便创建一下文件夹
  108. for _, pkg := range pkgMap {
  109. dir := r.vfs.cache.LoadDir([]string{r.bktName, pkg.Name}, &cache.CreateDirOption{
  110. ModTime: pkg.CreateTime,
  111. })
  112. if dir == nil {
  113. continue
  114. }
  115. ens = append(ens, newPackageFromCache(dir.Info(), r.vfs))
  116. }
  117. return ens, nil
  118. }
  119. func (r *FuseBucket) NewDir(ctx context.Context, name string) (fuse.FsDir, error) {
  120. cache := r.vfs.cache.CreateDir([]string{r.bktName, name})
  121. if cache == nil {
  122. return nil, fuse.ErrPermission
  123. }
  124. // TODO 生成系统事件
  125. // 不关注创建是否成功,仅尝试一下
  126. r.vfs.db.DoTx(func(tx db.SQLContext) error {
  127. db := r.vfs.db
  128. bkt, err := db.Bucket().GetByName(tx, r.bktName)
  129. if err != nil {
  130. return fmt.Errorf("get bucket: %v", err)
  131. }
  132. _, err = db.Package().Create(tx, bkt.BucketID, name, time.Now())
  133. if err != nil {
  134. return fmt.Errorf("create package: %v", err)
  135. }
  136. return err
  137. })
  138. return newPackageFromCache(cache.Info(), r.vfs), nil
  139. }
  140. func (r *FuseBucket) NewFile(ctx context.Context, name string, flags uint32) (fuse.FileHandle, uint32, error) {
  141. return newFile(r.vfs, ctx, name, r, flags)
  142. }
  143. func (r *FuseBucket) RemoveChild(ctx context.Context, name string) error {
  144. // TODO 生成系统事件
  145. return r.vfs.db.DoTx(func(tx db.SQLContext) error {
  146. d := r.vfs.db
  147. pkg, err := d.Package().GetByFullName(tx, r.bktName, name)
  148. if err == nil {
  149. err = d.Object().HasObjectWithPrefix(tx, pkg.PackageID, "")
  150. if err == nil {
  151. return fuse.ErrNotEmpty
  152. }
  153. if err != gorm.ErrRecordNotFound {
  154. return err
  155. }
  156. err = d.Package().DeleteComplete(tx, pkg.PackageID)
  157. if err != nil {
  158. return err
  159. }
  160. } else if err != gorm.ErrRecordNotFound {
  161. return err
  162. }
  163. return r.vfs.cache.Remove([]string{r.bktName, name})
  164. })
  165. }
  166. func (r *FuseBucket) MoveChild(ctx context.Context, oldName string, newName string, newParent fuse.FsDir) error {
  167. newParentNode := newParent.(FuseNode)
  168. newParentPath := newParentNode.PathComps()
  169. // 仅允许在不同桶之间移动
  170. if len(newParentPath) != 1 {
  171. return fuse.ErrNotSupported
  172. }
  173. d := r.vfs.db
  174. return d.DoTx(func(tx db.SQLContext) error {
  175. _, err := d.Package().GetByFullName(tx, newParentPath[0], newName)
  176. if err == nil {
  177. // 目标节点已经存在,不能重命名,直接退出
  178. return fuse.ErrExists
  179. } else if err != gorm.ErrRecordNotFound {
  180. return err
  181. }
  182. newBkt, err := d.Bucket().GetByName(tx, newParentPath[0])
  183. if err == nil {
  184. oldPkg, err := d.Package().GetByFullName(tx, r.bktName, oldName)
  185. if err == nil {
  186. err = d.Package().Move(tx, oldPkg.PackageID, newBkt.BucketID, newName)
  187. if err != nil {
  188. return err
  189. }
  190. } else if err != gorm.ErrRecordNotFound {
  191. return err
  192. }
  193. } else if err != gorm.ErrRecordNotFound {
  194. return err
  195. }
  196. // 有可能是移动文件,所以如果是源文件夹未找到,也尝试进行移动
  197. return r.vfs.cache.Move([]string{r.bktName, oldName}, []string{newParentPath[0], newName})
  198. })
  199. }
  200. func (r *FuseBucket) loadCacheDir() *cache.CacheDir {
  201. var createOpt *cache.CreateDirOption
  202. bkt, err := r.vfs.db.Bucket().GetByName(r.vfs.db.DefCtx(), r.bktName)
  203. if err == nil {
  204. createOpt = &cache.CreateDirOption{
  205. ModTime: bkt.CreateTime,
  206. }
  207. }
  208. return r.vfs.cache.LoadDir([]string{r.bktName}, createOpt)
  209. }
  210. var _ fuse.FsDir = (*FuseBucket)(nil)
  211. var _ FuseNode = (*FuseBucket)(nil)

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