JCS-pub/client/internal/mount/vfs/fuse_root.go

215 lines
4.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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) Perm() os.FileMode {
return 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 生成系统事件
// 不关注创建是否成功,仅尝试一下
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)