|
- package vfs
-
- import (
- "context"
- "strings"
- "time"
-
- "gitlink.org.cn/cloudream/common/utils/lo2"
- "gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
- "gitlink.org.cn/cloudream/jcs-pub/client/internal/mount/fuse"
- "gitlink.org.cn/cloudream/jcs-pub/client/internal/mount/vfs/cache"
- clitypes "gitlink.org.cn/cloudream/jcs-pub/client/types"
- "gorm.io/gorm"
- )
-
- type FuseNode interface {
- PathComps() []string
- }
-
- func child(vfs *Vfs, ctx context.Context, parent FuseNode, name string) (fuse.FsEntry, error) {
- parentPathComps := parent.PathComps()
-
- childPathComps := lo2.AppendNew(parentPathComps, name)
- ca := vfs.cache.Stat(childPathComps)
- if ca == nil {
- var ret fuse.FsEntry
-
- d := vfs.db
- err := d.DoTx(func(tx db.SQLContext) error {
- pkg, err := d.Package().GetByFullName(tx, childPathComps[0], childPathComps[1])
- if err != nil {
- if err != gorm.ErrRecordNotFound {
- return err
- }
- return nil
- }
-
- objPath := clitypes.JoinObjectPath(childPathComps[2:]...)
- obj, err := d.Object().GetByPath(tx, pkg.PackageID, objPath)
- if err == nil {
- ret = newFileFromObject(vfs, childPathComps, obj)
- return nil
- }
- if err != gorm.ErrRecordNotFound {
- return err
- }
-
- err = d.Object().HasObjectWithPrefix(tx, pkg.PackageID, objPath+clitypes.ObjectPathSeparator)
- if err == nil {
- dir := vfs.cache.LoadDir(childPathComps, &cache.CreateDirOption{
- ModTime: time.Now(),
- })
- if dir == nil {
- return nil
- }
-
- ret = newDirFromCache(dir.Info(), vfs)
- return nil
- }
-
- if err == gorm.ErrRecordNotFound {
- return nil
- }
-
- return err
- })
- if err != nil {
- return nil, err
- }
-
- if ret == nil {
- return nil, fuse.ErrNotExists
- }
-
- return ret, nil
- }
-
- if ca.IsDir {
- return newDirFromCache(*ca, vfs), nil
- }
-
- return newFileFromCache(*ca, vfs), nil
- }
-
- func listChildren(vfs *Vfs, ctx context.Context, parent FuseNode) ([]fuse.FsEntry, error) {
- var ens []fuse.FsEntry
-
- myPathComps := parent.PathComps()
-
- infos := vfs.cache.StatMany(myPathComps)
-
- dbEntries := make(map[string]fuse.FsEntry)
-
- d := vfs.db
- d.DoTx(func(tx db.SQLContext) error {
- pkg, err := d.Package().GetByFullName(tx, myPathComps[0], myPathComps[1])
- if err != nil {
- return err
- }
-
- objPath := clitypes.JoinObjectPath(myPathComps[2:]...)
- objPrefix := objPath
- if objPath != "" {
- objPrefix += clitypes.ObjectPathSeparator
- }
-
- objs, coms, err := d.Object().GetByPrefixGrouped(tx, pkg.PackageID, objPrefix)
- if err != nil {
- return err
- }
-
- for _, dir := range coms {
- dir = strings.TrimSuffix(dir, clitypes.ObjectPathSeparator)
- pathComps := lo2.AppendNew(myPathComps, clitypes.BaseName(dir))
-
- cd := vfs.cache.LoadDir(pathComps, &cache.CreateDirOption{
- ModTime: time.Now(),
- })
- if cd == nil {
- continue
- }
-
- dbEntries[dir] = newDirFromCache(cd.Info(), vfs)
- }
-
- for _, obj := range objs {
- pathComps := lo2.AppendNew(myPathComps, clitypes.BaseName(obj.Path))
- file := newFileFromObject(vfs, pathComps, obj)
- dbEntries[file.Name()] = file
- }
-
- return nil
- })
-
- for _, c := range infos {
- delete(dbEntries, c.PathComps[len(c.PathComps)-1])
-
- if c.IsDir {
- ens = append(ens, newDirFromCache(c, vfs))
- } else {
- ens = append(ens, newFileFromCache(c, vfs))
- }
- }
-
- for _, e := range dbEntries {
- ens = append(ens, e)
- }
-
- return ens, nil
- }
-
- func newDir(vfs *Vfs, ctx context.Context, name string, parent FuseNode) (fuse.FsDir, error) {
- cache := vfs.cache.CreateDir(lo2.AppendNew(parent.PathComps(), name))
- if cache == nil {
- return nil, fuse.ErrPermission
- }
-
- return newDirFromCache(cache.Info(), vfs), nil
- }
-
- func newFile(vfs *Vfs, ctx context.Context, name string, parent FuseNode, flags uint32) (fuse.FileHandle, uint32, error) {
- cache := vfs.cache.CreateFile(lo2.AppendNew(parent.PathComps(), name))
- if cache == nil {
- return nil, 0, fuse.ErrPermission
- }
- defer cache.Release()
- // Open之后会给cache的引用计数额外+1,即使cache先于FileHandle被关闭,
- // 也有有FileHandle的计数保持cache的有效性
-
- fileNode := newFileFromCache(cache.Info(), vfs)
- hd := cache.Open(flags)
- return newFileHandle(fileNode, hd), flags, nil
- }
-
- func removeChild(vfs *Vfs, ctx context.Context, name string, parent FuseNode) error {
- pathComps := lo2.AppendNew(parent.PathComps(), name)
- joinedPath := clitypes.JoinObjectPath(pathComps[2:]...)
- d := vfs.db
-
- // TODO 生成系统事件
- return vfs.db.DoTx(func(tx db.SQLContext) error {
- pkg, err := d.Package().GetByFullName(tx, pathComps[0], pathComps[1])
- if err == nil {
- err := d.Object().HasObjectWithPrefix(tx, pkg.PackageID, joinedPath+clitypes.ObjectPathSeparator)
- if err == nil {
- return fuse.ErrNotEmpty
- }
- if err != gorm.ErrRecordNotFound {
- return err
- }
-
- // 存储系统不会保存目录结构,所以这里是尝试删除同名文件
- err = d.Object().DeleteCompleteByPath(tx, pkg.PackageID, joinedPath)
- if err != nil && err != gorm.ErrRecordNotFound {
- return err
- }
-
- } else if err != gorm.ErrRecordNotFound {
- return err
- }
-
- return vfs.cache.Remove(pathComps)
- })
- }
-
- func moveChild(vfs *Vfs, ctx context.Context, oldName string, oldParent FuseNode, newName string, newParent FuseNode) error {
- newParentPath := newParent.PathComps()
- newChildPath := lo2.AppendNew(newParentPath, newName)
- newChildPathJoined := clitypes.JoinObjectPath(newChildPath[2:]...)
-
- // 不允许移动任何内容到Package层级以上
- if len(newParentPath) < 2 {
- return fuse.ErrNotSupported
- }
-
- oldChildPath := lo2.AppendNew(oldParent.PathComps(), oldName)
- oldChildPathJoined := clitypes.JoinObjectPath(oldChildPath[2:]...)
-
- // 先更新远程,再更新本地,因为远程使用事务更新,可以回滚,而本地不行
- return vfs.db.DoTx(func(tx db.SQLContext) error {
- err := moveRemote(vfs, tx, oldChildPath, newParentPath, oldChildPathJoined, newChildPathJoined)
- if err == fuse.ErrExists {
- return err
- }
- if err != nil && err != fuse.ErrNotExists {
- return err
- }
-
- err2 := vfs.cache.Move(oldChildPath, newChildPath)
- if err2 == fuse.ErrNotExists {
- if err == fuse.ErrNotExists {
- return fuse.ErrNotExists
- }
- return nil
- }
-
- return err2
- })
- }
-
- func moveRemote(vfs *Vfs, tx db.SQLContext, oldChildPath []string, newParentPath []string, oldChildPathJoined string, newChildPathJoined string) error {
- d := vfs.db
- newPkg, err := d.Package().GetByFullName(tx, newParentPath[0], newParentPath[1])
- if err != nil {
- if err == gorm.ErrRecordNotFound {
- return fuse.ErrNotExists
- }
- return err
- }
-
- // 检查目的文件或文件夹是否已经存在
- _, err = d.Object().GetByPath(tx, newPkg.PackageID, newChildPathJoined)
- if err == nil {
- return fuse.ErrExists
- }
-
- err = d.Object().HasObjectWithPrefix(tx, newPkg.PackageID, newChildPathJoined+clitypes.ObjectPathSeparator)
- if err == nil {
- return fuse.ErrExists
- }
- if err != gorm.ErrRecordNotFound {
- return err
- }
-
- // 按理来说还需要检查远程文件所在的文件夹是否存在,但对象存储是不存文件夹的,所以不检查,导致的后果就是移动时会创建不存在的文件夹
-
- oldPkg, err := d.Package().GetByFullName(tx, oldChildPath[0], oldChildPath[1])
- if err != nil {
- if err == gorm.ErrRecordNotFound {
- return fuse.ErrNotExists
- }
- return err
- }
-
- // 都不存在,就开始移动文件
- oldObj, err := d.Object().GetByPath(tx, oldPkg.PackageID, oldChildPathJoined)
- if err == nil {
- oldObj.PackageID = newPkg.PackageID
- oldObj.Path = newChildPathJoined
-
- return d.Object().BatchUpdate(tx, []clitypes.Object{oldObj})
- }
- if err != gorm.ErrRecordNotFound {
- return err
- }
-
- err = d.Object().HasObjectWithPrefix(tx, oldPkg.PackageID, oldChildPathJoined+clitypes.ObjectPathSeparator)
- if err == nil {
- return d.Object().MoveByPrefix(tx,
- oldPkg.PackageID, oldChildPathJoined+clitypes.ObjectPathSeparator,
- newPkg.PackageID, newChildPathJoined+clitypes.ObjectPathSeparator,
- )
- }
- if err == gorm.ErrRecordNotFound {
- return fuse.ErrNotExists
- }
- return err
- }
|