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.2 kB

8 months ago
8 months ago
8 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  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. mode 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. mode: c.Mode,
  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) Mode() os.FileMode {
  37. return os.ModeDir | r.mode
  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. // TODO UserID
  58. pkg, err := r.vfs.db.Package().GetByFullName(r.vfs.db.DefCtx(), r.bktName, name)
  59. if err == nil {
  60. dir := r.vfs.cache.LoadDir(childPathComps, &cache.CreateDirOption{
  61. ModTime: pkg.CreateTime,
  62. })
  63. if dir == nil {
  64. return nil, fuse.ErrNotExists
  65. }
  66. return newPackageFromCache(dir.Info(), r.vfs), nil
  67. }
  68. if err == gorm.ErrRecordNotFound {
  69. return nil, fuse.ErrNotExists
  70. }
  71. return nil, err
  72. }
  73. if ca.IsDir {
  74. return newPackageFromCache(*ca, r.vfs), nil
  75. }
  76. return newFileFromCache(*ca, r.vfs), nil
  77. }
  78. func (r *FuseBucket) Children(ctx context.Context) ([]fuse.FsEntry, error) {
  79. return r.listChildren()
  80. }
  81. func (r *FuseBucket) ReadChildren() (fuse.DirReader, error) {
  82. ens, err := r.listChildren()
  83. if err != nil {
  84. return nil, err
  85. }
  86. return newFuseDirReader(ens), nil
  87. }
  88. func (r *FuseBucket) listChildren() ([]fuse.FsEntry, error) {
  89. var ens []fuse.FsEntry
  90. infos := r.vfs.cache.StatMany([]string{r.bktName})
  91. pkgs, err := r.vfs.db.Package().GetBucketPackagesByName(r.vfs.db.DefCtx(), r.bktName)
  92. if err != nil {
  93. return nil, err
  94. }
  95. pkgMap := make(map[string]*clitypes.Package)
  96. for _, pkg := range pkgs {
  97. p := pkg
  98. pkgMap[pkg.Name] = &p
  99. }
  100. for _, c := range infos {
  101. delete(pkgMap, c.PathComps[len(c.PathComps)-1])
  102. if c.IsDir {
  103. ens = append(ens, newPackageFromCache(c, r.vfs))
  104. } else {
  105. ens = append(ens, newFileFromCache(c, r.vfs))
  106. }
  107. }
  108. // 顺便创建一下文件夹
  109. for _, pkg := range pkgMap {
  110. dir := r.vfs.cache.LoadDir([]string{r.bktName, pkg.Name}, &cache.CreateDirOption{
  111. ModTime: pkg.CreateTime,
  112. })
  113. if dir == nil {
  114. continue
  115. }
  116. ens = append(ens, newPackageFromCache(dir.Info(), r.vfs))
  117. }
  118. return ens, nil
  119. }
  120. func (r *FuseBucket) NewDir(ctx context.Context, name string) (fuse.FsDir, error) {
  121. cache := r.vfs.cache.CreateDir([]string{r.bktName, name})
  122. if cache == nil {
  123. return nil, fuse.ErrPermission
  124. }
  125. // TODO 用户ID,失败了可以打个日志
  126. // TODO 生成系统事件
  127. // 不关注创建是否成功,仅尝试一下
  128. r.vfs.db.DoTx(func(tx db.SQLContext) error {
  129. db := r.vfs.db
  130. bkt, err := db.Bucket().GetByName(tx, r.bktName)
  131. if err != nil {
  132. return fmt.Errorf("get bucket: %v", err)
  133. }
  134. _, err = db.Package().Create(tx, bkt.BucketID, name)
  135. if err != nil {
  136. return fmt.Errorf("create package: %v", err)
  137. }
  138. return err
  139. })
  140. return newPackageFromCache(cache.Info(), r.vfs), nil
  141. }
  142. func (r *FuseBucket) NewFile(ctx context.Context, name string, flags uint32) (fuse.FileHandle, uint32, error) {
  143. return newFile(r.vfs, ctx, name, r, flags)
  144. }
  145. func (r *FuseBucket) RemoveChild(ctx context.Context, name string) error {
  146. // TODO 生成系统事件
  147. return r.vfs.db.DoTx(func(tx db.SQLContext) error {
  148. d := r.vfs.db
  149. pkg, err := d.Package().GetByFullName(tx, r.bktName, name)
  150. if err == nil {
  151. err = d.Object().HasObjectWithPrefix(tx, pkg.PackageID, "")
  152. if err == nil {
  153. return fuse.ErrNotEmpty
  154. }
  155. if err != gorm.ErrRecordNotFound {
  156. return err
  157. }
  158. err = d.Package().DeleteComplete(tx, pkg.PackageID)
  159. if err != nil {
  160. return err
  161. }
  162. } else if err != gorm.ErrRecordNotFound {
  163. return err
  164. }
  165. return r.vfs.cache.Remove([]string{r.bktName, name})
  166. })
  167. }
  168. func (r *FuseBucket) MoveChild(ctx context.Context, oldName string, newName string, newParent fuse.FsDir) error {
  169. newParentNode := newParent.(FuseNode)
  170. newParentPath := newParentNode.PathComps()
  171. // 仅允许在不同桶之间移动
  172. if len(newParentPath) != 1 {
  173. return fuse.ErrNotSupported
  174. }
  175. d := r.vfs.db
  176. return d.DoTx(func(tx db.SQLContext) error {
  177. _, err := d.Package().GetByFullName(tx, newParentPath[0], newName)
  178. if err == nil {
  179. // 目标节点已经存在,不能重命名,直接退出
  180. return fuse.ErrExists
  181. } else if err != gorm.ErrRecordNotFound {
  182. return err
  183. }
  184. newBkt, err := d.Bucket().GetByName(tx, newParentPath[0])
  185. if err == nil {
  186. oldPkg, err := d.Package().GetByFullName(tx, r.bktName, oldName)
  187. if err == nil {
  188. err = d.Package().Move(tx, oldPkg.PackageID, newBkt.BucketID, newName)
  189. if err != nil {
  190. return err
  191. }
  192. } else if err != gorm.ErrRecordNotFound {
  193. return err
  194. }
  195. } else if err != gorm.ErrRecordNotFound {
  196. return err
  197. }
  198. // 有可能是移动文件,所以如果是源文件夹未找到,也尝试进行移动
  199. return r.vfs.cache.Move([]string{r.bktName, oldName}, []string{newParentPath[0], newName})
  200. })
  201. }
  202. func (r *FuseBucket) loadCacheDir() *cache.CacheDir {
  203. var createOpt *cache.CreateDirOption
  204. bkt, err := r.vfs.db.Bucket().GetByName(r.vfs.db.DefCtx(), r.bktName)
  205. if err == nil {
  206. createOpt = &cache.CreateDirOption{
  207. ModTime: bkt.CreateTime,
  208. }
  209. }
  210. return r.vfs.cache.LoadDir([]string{r.bktName}, createOpt)
  211. }
  212. var _ fuse.FsDir = (*FuseBucket)(nil)
  213. var _ FuseNode = (*FuseBucket)(nil)

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