package vfs import ( "context" "os" "time" db2 "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 FuseRoot struct { vfs *Vfs } func newRoot(vfs *Vfs) *FuseRoot { return &FuseRoot{ vfs: vfs, } } func (r *FuseRoot) PathComps() []string { return []string{} } func (r *FuseRoot) Name() string { return "" } func (r *FuseRoot) Size() int64 { return 0 } func (r *FuseRoot) Mode() os.FileMode { return os.ModeDir | 0755 } func (r *FuseRoot) ModTime() time.Time { return time.Now() } func (r *FuseRoot) IsDir() bool { return true } func (r *FuseRoot) SetModTime(time time.Time) error { return nil } // 如果不存在,应该返回ErrNotExists func (r *FuseRoot) Child(ctx context.Context, name string) (fuse.FsEntry, error) { ca := r.vfs.cache.Stat([]string{name}) if ca == nil { bkt, err := r.vfs.db.Bucket().GetByName(r.vfs.db.DefCtx(), name) if err == nil { dir := r.vfs.cache.LoadDir([]string{name}, &cache.CreateDirOption{ ModTime: bkt.CreateTime, }) if dir == nil { return nil, fuse.ErrNotExists } return newBucketFromCache(dir.Info(), r.vfs), nil } if err == gorm.ErrRecordNotFound { return nil, fuse.ErrNotExists } return nil, err } if ca.IsDir { return newBucketFromCache(*ca, r.vfs), nil } return newFileFromCache(*ca, r.vfs), nil } func (r *FuseRoot) Children(ctx context.Context) ([]fuse.FsEntry, error) { return r.listChildren() } func (r *FuseRoot) ReadChildren() (fuse.DirReader, error) { ens, err := r.listChildren() if err != nil { return nil, err } return newFuseDirReader(ens), nil } func (r *FuseRoot) listChildren() ([]fuse.FsEntry, error) { var ens []fuse.FsEntry infos := r.vfs.cache.StatMany([]string{}) bkts, err := r.vfs.db.Bucket().GetAll(r.vfs.db.DefCtx()) if err != nil { return nil, err } bktMap := make(map[string]*clitypes.Bucket) for _, bkt := range bkts { b := bkt bktMap[bkt.Name] = &b } for _, c := range infos { delete(bktMap, c.PathComps[len(c.PathComps)-1]) if c.IsDir { ens = append(ens, newBucketFromCache(c, r.vfs)) } else { ens = append(ens, newFileFromCache(c, r.vfs)) } } // 将远端目录同步到本地缓存中,防止在给目录中的远端对象创建本地缓存时,顺便创建的目录的元数据不对的情况 for _, bkt := range bktMap { dir := r.vfs.cache.LoadDir([]string{bkt.Name}, &cache.CreateDirOption{ ModTime: bkt.CreateTime, }) if dir == nil { continue } ens = append(ens, newBucketFromCache(dir.Info(), r.vfs)) } return ens, nil } func (r *FuseRoot) NewDir(ctx context.Context, name string) (fuse.FsDir, error) { cache := r.vfs.cache.CreateDir([]string{name}) if cache == nil { return nil, fuse.ErrPermission } // TODO 用户ID,失败了可以打个日志 // TODO 生成系统事件 // 不关注创建是否成功,仅尝试一下 r.vfs.db.Bucket().Create(r.vfs.db.DefCtx(), name, cache.ModTime()) return newBucketFromCache(cache.Info(), r.vfs), nil } func (r *FuseRoot) NewFile(ctx context.Context, name string, flags uint32) (fuse.FileHandle, uint32, error) { return newFile(r.vfs, ctx, name, r, flags) } func (r *FuseRoot) RemoveChild(ctx context.Context, name string) error { // TODO 生成系统事件 db := r.vfs.db return r.vfs.db.DoTx(func(tx db2.SQLContext) error { bkt, err := db.Bucket().GetByName(tx, name) if err == nil { err := db.Package().HasPackageIn(tx, bkt.BucketID) if err == nil { return fuse.ErrNotEmpty } if err != gorm.ErrRecordNotFound { return err } // 不管是否成功 db.Bucket().DeleteComplete(tx, bkt.BucketID) } else if err != gorm.ErrRecordNotFound { return err } return r.vfs.cache.Remove([]string{name}) }) } func (r *FuseRoot) MoveChild(ctx context.Context, oldName string, newName string, newParent fuse.FsDir) error { newParentNode := newParent.(FuseNode) newParentPath := newParentNode.PathComps() // 只允许同层级内改名 if len(newParentPath) != 0 { return fuse.ErrNotSupported } d := r.vfs.db return d.DoTx(func(tx db2.SQLContext) error { _, err := d.Bucket().GetByName(tx, newName) if err == nil { // 目标节点已经存在,不能重命名,直接退出 return fuse.ErrExists } oldBkt, err := d.Bucket().GetByName(tx, oldName) if err == nil { err = d.Bucket().Rename(tx, oldBkt.BucketID, newName) if err != nil { return err } } else if err != gorm.ErrRecordNotFound { return err } return r.vfs.cache.Move([]string{oldName}, []string{newName}) }) } var _ fuse.FsDir = (*FuseRoot)(nil) var _ FuseNode = (*FuseRoot)(nil)