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

8 months ago
8 months ago
8 months ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. package vfs
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "time"
  7. "gitlink.org.cn/cloudream/storage2/client/internal/db"
  8. "gitlink.org.cn/cloudream/storage2/client/internal/mount/fuse"
  9. "gitlink.org.cn/cloudream/storage2/client/internal/mount/vfs/cache"
  10. clitypes "gitlink.org.cn/cloudream/storage2/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().GetUserPackageByName(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. 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 用户ID,失败了可以打个日志
  125. // TODO 生成系统事件
  126. // 不关注创建是否成功,仅尝试一下
  127. r.vfs.db.DoTx(func(tx db.SQLContext) error {
  128. db := r.vfs.db
  129. bkt, err := db.Bucket().GetByName(tx, r.bktName)
  130. if err != nil {
  131. return fmt.Errorf("get bucket: %v", err)
  132. }
  133. _, err = db.Package().Create(tx, bkt.BucketID, name)
  134. if err != nil {
  135. return fmt.Errorf("create package: %v", err)
  136. }
  137. return err
  138. })
  139. return newPackageFromCache(cache.Info(), r.vfs), nil
  140. }
  141. func (r *FuseBucket) NewFile(ctx context.Context, name string, flags uint32) (fuse.FileHandle, uint32, error) {
  142. cache := r.vfs.cache.CreateFile([]string{r.bktName, name})
  143. if cache == nil {
  144. return nil, 0, fuse.ErrPermission
  145. }
  146. defer cache.Release()
  147. // Open之后会给cache的引用计数额外+1,即使cache先于FileHandle被关闭,
  148. // 也有有FileHandle的计数保持cache的有效性
  149. fileNode := newFileFromCache(cache.Info(), r.vfs)
  150. hd := cache.Open(flags)
  151. return newFileHandle(fileNode, hd), flags, nil
  152. }
  153. func (r *FuseBucket) RemoveChild(ctx context.Context, name string) error {
  154. // TODO 生成系统事件
  155. return r.vfs.db.DoTx(func(tx db.SQLContext) error {
  156. d := r.vfs.db
  157. pkg, err := d.Package().GetUserPackageByName(tx, r.bktName, name)
  158. if err == nil {
  159. has, err := d.Object().HasObjectWithPrefix(tx, pkg.PackageID, "")
  160. if err != nil {
  161. return err
  162. }
  163. if has {
  164. return fuse.ErrNotEmpty
  165. }
  166. } else if err != gorm.ErrRecordNotFound {
  167. return err
  168. }
  169. err = r.vfs.cache.Remove([]string{r.bktName, name})
  170. if err != nil {
  171. return err
  172. }
  173. if pkg.PackageID != 0 {
  174. d.Package().DeleteComplete(tx, pkg.PackageID)
  175. }
  176. return nil
  177. })
  178. }
  179. func (r *FuseBucket) MoveChild(ctx context.Context, oldName string, newName string, newParent fuse.FsDir) error {
  180. newParentNode := newParent.(FuseNode)
  181. newParentPath := newParentNode.PathComps()
  182. // 仅允许在不同桶之间移动
  183. if len(newParentPath) != 1 {
  184. return fuse.ErrNotSupported
  185. }
  186. d := r.vfs.db
  187. return d.DoTx(func(tx db.SQLContext) error {
  188. _, err := d.Package().GetUserPackageByName(tx, newParentPath[0], newName)
  189. if err == nil {
  190. // 目标节点已经存在,不能重命名,直接退出
  191. return fuse.ErrExists
  192. } else if err != gorm.ErrRecordNotFound {
  193. return err
  194. }
  195. newBkt, err := d.Bucket().GetByName(tx, newParentPath[0])
  196. if err == nil {
  197. oldPkg, err := d.Package().GetUserPackageByName(tx, r.bktName, oldName)
  198. if err == nil {
  199. err = d.Package().Move(tx, oldPkg.PackageID, newBkt.BucketID, newName)
  200. if err != nil {
  201. return err
  202. }
  203. } else if err != gorm.ErrRecordNotFound {
  204. return err
  205. }
  206. } else if err != gorm.ErrRecordNotFound {
  207. return err
  208. }
  209. // 有可能是移动文件,所以如果是源文件夹未找到,也尝试进行移动
  210. return r.vfs.cache.Move([]string{r.bktName, oldName}, []string{newParentPath[0], newName})
  211. })
  212. }
  213. func (r *FuseBucket) loadCacheDir() *cache.CacheDir {
  214. var createOpt *cache.CreateDirOption
  215. bkt, err := r.vfs.db.Bucket().GetByName(r.vfs.db.DefCtx(), r.bktName)
  216. if err == nil {
  217. createOpt = &cache.CreateDirOption{
  218. ModTime: bkt.CreateTime,
  219. }
  220. }
  221. return r.vfs.cache.LoadDir([]string{r.bktName}, createOpt)
  222. }
  223. var _ fuse.FsDir = (*FuseBucket)(nil)
  224. var _ FuseNode = (*FuseBucket)(nil)

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