215 lines
4.7 KiB
Go
215 lines
4.7 KiB
Go
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)
|