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_dir.go 6.9 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. package vfs
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "strings"
  7. "time"
  8. cdssdk "gitlink.org.cn/cloudream/common/sdks/storage"
  9. "gitlink.org.cn/cloudream/common/utils/lo2"
  10. "gitlink.org.cn/cloudream/storage/client2/internal/mount/fuse"
  11. "gitlink.org.cn/cloudream/storage/client2/internal/mount/vfs/cache"
  12. "gitlink.org.cn/cloudream/storage/common/pkgs/db2"
  13. "gorm.io/gorm"
  14. )
  15. type FuseDir struct {
  16. vfs *Vfs
  17. pathComps []string
  18. modTime time.Time
  19. }
  20. func newDirFromCache(ch cache.CacheEntryInfo, vfs *Vfs) *FuseDir {
  21. return &FuseDir{
  22. vfs: vfs,
  23. pathComps: ch.PathComps,
  24. modTime: ch.ModTime,
  25. }
  26. }
  27. func (r *FuseDir) PathComps() []string {
  28. return r.pathComps
  29. }
  30. func (r *FuseDir) Name() string {
  31. return r.pathComps[len(r.pathComps)-1]
  32. }
  33. func (r *FuseDir) Size() int64 {
  34. return 0
  35. }
  36. func (r *FuseDir) Mode() os.FileMode {
  37. return os.ModeDir | 0755
  38. }
  39. func (r *FuseDir) ModTime() time.Time {
  40. return r.modTime
  41. }
  42. func (r *FuseDir) IsDir() bool {
  43. return true
  44. }
  45. func (r *FuseDir) 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 *FuseDir) Child(ctx context.Context, name string) (fuse.FsEntry, error) {
  54. childPathComps := append(lo2.ArrayClone(r.pathComps), name)
  55. ca := r.vfs.cache.Stat(childPathComps)
  56. if ca == nil {
  57. var ret fuse.FsEntry
  58. db := r.vfs.db
  59. err := db.DoTx(func(tx db2.SQLContext) error {
  60. pkg, err := db.Package().GetUserPackageByName(tx, 1, r.pathComps[0], r.pathComps[1])
  61. if err != nil {
  62. return err
  63. }
  64. objPath := cdssdk.JoinObjectPath(childPathComps[2:]...)
  65. obj, err := db.Object().GetByPath(tx, pkg.PackageID, objPath)
  66. if err == nil {
  67. ret = newFileFromObject(r.vfs, childPathComps, obj)
  68. return nil
  69. }
  70. if err != gorm.ErrRecordNotFound {
  71. return err
  72. }
  73. has, err := db.Object().HasObjectWithPrefix(tx, pkg.PackageID, objPath+cdssdk.ObjectPathSeparator)
  74. if err != nil {
  75. return err
  76. }
  77. if has {
  78. dir := r.vfs.cache.LoadDir(childPathComps, &cache.CreateDirOption{
  79. ModTime: time.Now(),
  80. })
  81. if dir == nil {
  82. return nil
  83. }
  84. ret = newDirFromCache(dir.Info(), r.vfs)
  85. }
  86. return nil
  87. })
  88. if err != nil {
  89. return nil, err
  90. }
  91. if ret == nil {
  92. return nil, fuse.ErrNotExists
  93. }
  94. return ret, nil
  95. }
  96. if ca.IsDir {
  97. return newDirFromCache(*ca, r.vfs), nil
  98. }
  99. return newFileFromCache(*ca, r.vfs), nil
  100. }
  101. func (r *FuseDir) Children(ctx context.Context) ([]fuse.FsEntry, error) {
  102. return r.listChildren()
  103. }
  104. func (r *FuseDir) ReadChildren() (fuse.DirReader, error) {
  105. ens, err := r.listChildren()
  106. if err != nil {
  107. return nil, err
  108. }
  109. return newFuseDirReader(ens), nil
  110. }
  111. func (r *FuseDir) listChildren() ([]fuse.FsEntry, error) {
  112. var ens []fuse.FsEntry
  113. infos := r.vfs.cache.StatMany(r.pathComps)
  114. dbEntries := make(map[string]fuse.FsEntry)
  115. db := r.vfs.db
  116. db.DoTx(func(tx db2.SQLContext) error {
  117. pkg, err := db.Package().GetUserPackageByName(tx, 1, r.pathComps[0], r.pathComps[1])
  118. if err != nil {
  119. return err
  120. }
  121. objPath := cdssdk.JoinObjectPath(r.pathComps[2:]...)
  122. coms, err := db.Object().GetCommonPrefixes(tx, pkg.PackageID, objPath+cdssdk.ObjectPathSeparator)
  123. if err != nil {
  124. return fmt.Errorf("getting common prefixes: %w", err)
  125. }
  126. objs, err := db.Object().GetDirectChildren(tx, pkg.PackageID, objPath+cdssdk.ObjectPathSeparator)
  127. if err != nil {
  128. return fmt.Errorf("getting direct children: %w", err)
  129. }
  130. for _, dir := range coms {
  131. dir = strings.TrimSuffix(dir, cdssdk.ObjectPathSeparator)
  132. pathComps := append(lo2.ArrayClone(r.pathComps), cdssdk.BaseName(dir))
  133. cd := r.vfs.cache.LoadDir(pathComps, &cache.CreateDirOption{
  134. ModTime: time.Now(),
  135. })
  136. if cd == nil {
  137. continue
  138. }
  139. dbEntries[dir] = newDirFromCache(cd.Info(), r.vfs)
  140. }
  141. for _, obj := range objs {
  142. pathComps := append(lo2.ArrayClone(r.pathComps), cdssdk.BaseName(obj.Path))
  143. file := newFileFromObject(r.vfs, pathComps, obj)
  144. dbEntries[file.Name()] = file
  145. }
  146. return nil
  147. })
  148. for _, c := range infos {
  149. delete(dbEntries, c.PathComps[len(c.PathComps)-1])
  150. if c.IsDir {
  151. ens = append(ens, newDirFromCache(c, r.vfs))
  152. } else {
  153. ens = append(ens, newFileFromCache(c, r.vfs))
  154. }
  155. }
  156. for _, e := range dbEntries {
  157. ens = append(ens, e)
  158. }
  159. return ens, nil
  160. }
  161. func (r *FuseDir) NewDir(ctx context.Context, name string) (fuse.FsDir, error) {
  162. cache := r.vfs.cache.CreateDir(append(lo2.ArrayClone(r.pathComps), name))
  163. if cache == nil {
  164. return nil, fuse.ErrPermission
  165. }
  166. return newDirFromCache(cache.Info(), r.vfs), nil
  167. }
  168. func (r *FuseDir) NewFile(ctx context.Context, name string, flags uint32) (fuse.FileHandle, error) {
  169. cache := r.vfs.cache.CreateFile(append(lo2.ArrayClone(r.pathComps), name))
  170. if cache == nil {
  171. return nil, fuse.ErrPermission
  172. }
  173. // Open之后会给cache的引用计数额外+1,即使cache先于FileHandle被关闭,
  174. // 也有有FileHandle的计数保持cache的有效性
  175. fileNode := newFileFromCache(cache.Info(), r.vfs)
  176. if flags&uint32(os.O_WRONLY) != 0 {
  177. hd := cache.Open(false)
  178. return newFileHandle(fileNode, hd), nil
  179. }
  180. if flags&uint32(os.O_RDONLY) != 0 {
  181. hd := cache.Open(true)
  182. return newFileHandle(fileNode, hd), nil
  183. }
  184. return nil, fuse.ErrPermission
  185. }
  186. func (r *FuseDir) RemoveChild(ctx context.Context, name string) error {
  187. pathComps := append(lo2.ArrayClone(r.pathComps), name)
  188. err := r.vfs.cache.Remove(pathComps)
  189. if err != nil {
  190. return err
  191. }
  192. // TODO 生成系统事件
  193. // 不关心是否成功
  194. r.vfs.db.DoTx(func(tx db2.SQLContext) error {
  195. d := r.vfs.db
  196. pkg, err := d.Package().GetUserPackageByName(tx, 1, pathComps[0], pathComps[1])
  197. if err != nil {
  198. return err
  199. }
  200. return d.Object().DeleteByPath(tx, pkg.PackageID, cdssdk.JoinObjectPath(pathComps[2:]...))
  201. })
  202. return nil
  203. }
  204. func (r *FuseDir) MoveChild(ctx context.Context, oldName string, newName string, newParent fuse.FsDir) error {
  205. // TODO 有问题
  206. oldPathComps := append(lo2.ArrayClone(r.pathComps), oldName)
  207. newParentNode := newParent.(FuseNode)
  208. _, err := r.vfs.cache.Move(oldPathComps, append(newParentNode.PathComps(), newName))
  209. if err != nil {
  210. return err
  211. }
  212. return nil
  213. }
  214. func (r *FuseDir) loadCacheDir() *cache.CacheDir {
  215. var createOpt *cache.CreateDirOption
  216. err := r.vfs.db.DoTx(func(tx db2.SQLContext) error {
  217. pkg, err := r.vfs.db.Package().GetUserPackageByName(tx, 1, r.pathComps[0], r.pathComps[1])
  218. if err != nil {
  219. return err
  220. }
  221. has, err := r.vfs.db.Object().HasObjectWithPrefix(tx, pkg.PackageID, cdssdk.JoinObjectPath(r.pathComps[2:]...))
  222. if err != nil {
  223. return err
  224. }
  225. if has {
  226. createOpt = &cache.CreateDirOption{
  227. ModTime: time.Now(),
  228. }
  229. }
  230. return nil
  231. })
  232. if err != nil {
  233. return nil
  234. }
  235. return r.vfs.cache.LoadDir(r.pathComps, createOpt)
  236. }
  237. var _ fuse.FsDir = (*FuseDir)(nil)
  238. var _ FuseNode = (*FuseDir)(nil)

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