266 lines
6.1 KiB
Go
266 lines
6.1 KiB
Go
package vfs
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"os"
|
||
"time"
|
||
|
||
"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 FuseBucket struct {
|
||
vfs *Vfs
|
||
bktName string
|
||
modTime time.Time
|
||
mode os.FileMode
|
||
}
|
||
|
||
func newBucketFromCache(c cache.CacheEntryInfo, vfs *Vfs) fuse.FsDir {
|
||
return &FuseBucket{
|
||
vfs: vfs,
|
||
bktName: c.PathComps[len(c.PathComps)-1],
|
||
modTime: c.ModTime,
|
||
mode: c.Mode,
|
||
}
|
||
}
|
||
|
||
func (r *FuseBucket) PathComps() []string {
|
||
return []string{r.bktName}
|
||
}
|
||
|
||
func (r *FuseBucket) Name() string {
|
||
return r.bktName
|
||
}
|
||
|
||
func (r *FuseBucket) Size() int64 {
|
||
return 0
|
||
}
|
||
|
||
func (r *FuseBucket) Mode() os.FileMode {
|
||
return os.ModeDir | r.mode
|
||
}
|
||
|
||
func (r *FuseBucket) ModTime() time.Time {
|
||
return r.modTime
|
||
}
|
||
|
||
func (r *FuseBucket) IsDir() bool {
|
||
return true
|
||
}
|
||
|
||
func (r *FuseBucket) SetModTime(time time.Time) error {
|
||
dir := r.loadCacheDir()
|
||
if dir == nil {
|
||
return fuse.ErrNotExists
|
||
}
|
||
|
||
return dir.SetModTime(time)
|
||
}
|
||
|
||
// 如果不存在,应该返回ErrNotExists
|
||
func (r *FuseBucket) Child(ctx context.Context, name string) (fuse.FsEntry, error) {
|
||
childPathComps := []string{r.bktName, name}
|
||
ca := r.vfs.cache.Stat(childPathComps)
|
||
|
||
if ca == nil {
|
||
// TODO UserID
|
||
|
||
pkg, err := r.vfs.db.Package().GetByFullName(r.vfs.db.DefCtx(), r.bktName, name)
|
||
if err == nil {
|
||
dir := r.vfs.cache.LoadDir(childPathComps, &cache.CreateDirOption{
|
||
ModTime: pkg.CreateTime,
|
||
})
|
||
if dir == nil {
|
||
return nil, fuse.ErrNotExists
|
||
}
|
||
|
||
return newPackageFromCache(dir.Info(), r.vfs), nil
|
||
}
|
||
|
||
if err == gorm.ErrRecordNotFound {
|
||
return nil, fuse.ErrNotExists
|
||
}
|
||
|
||
return nil, err
|
||
}
|
||
|
||
if ca.IsDir {
|
||
return newPackageFromCache(*ca, r.vfs), nil
|
||
}
|
||
|
||
return newFileFromCache(*ca, r.vfs), nil
|
||
}
|
||
|
||
func (r *FuseBucket) Children(ctx context.Context) ([]fuse.FsEntry, error) {
|
||
return r.listChildren()
|
||
}
|
||
|
||
func (r *FuseBucket) ReadChildren() (fuse.DirReader, error) {
|
||
ens, err := r.listChildren()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
return newFuseDirReader(ens), nil
|
||
}
|
||
|
||
func (r *FuseBucket) listChildren() ([]fuse.FsEntry, error) {
|
||
var ens []fuse.FsEntry
|
||
|
||
infos := r.vfs.cache.StatMany([]string{r.bktName})
|
||
|
||
pkgs, err := r.vfs.db.Package().GetBucketPackagesByName(r.vfs.db.DefCtx(), r.bktName)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
pkgMap := make(map[string]*clitypes.Package)
|
||
for _, pkg := range pkgs {
|
||
p := pkg
|
||
pkgMap[pkg.Name] = &p
|
||
}
|
||
|
||
for _, c := range infos {
|
||
delete(pkgMap, c.PathComps[len(c.PathComps)-1])
|
||
|
||
if c.IsDir {
|
||
ens = append(ens, newPackageFromCache(c, r.vfs))
|
||
} else {
|
||
ens = append(ens, newFileFromCache(c, r.vfs))
|
||
}
|
||
}
|
||
|
||
// 顺便创建一下文件夹
|
||
for _, pkg := range pkgMap {
|
||
dir := r.vfs.cache.LoadDir([]string{r.bktName, pkg.Name}, &cache.CreateDirOption{
|
||
ModTime: pkg.CreateTime,
|
||
})
|
||
if dir == nil {
|
||
continue
|
||
}
|
||
|
||
ens = append(ens, newPackageFromCache(dir.Info(), r.vfs))
|
||
}
|
||
|
||
return ens, nil
|
||
}
|
||
|
||
func (r *FuseBucket) NewDir(ctx context.Context, name string) (fuse.FsDir, error) {
|
||
cache := r.vfs.cache.CreateDir([]string{r.bktName, name})
|
||
if cache == nil {
|
||
return nil, fuse.ErrPermission
|
||
}
|
||
|
||
// TODO 用户ID,失败了可以打个日志
|
||
// TODO 生成系统事件
|
||
// 不关注创建是否成功,仅尝试一下
|
||
r.vfs.db.DoTx(func(tx db.SQLContext) error {
|
||
db := r.vfs.db
|
||
bkt, err := db.Bucket().GetByName(tx, r.bktName)
|
||
if err != nil {
|
||
return fmt.Errorf("get bucket: %v", err)
|
||
}
|
||
|
||
_, err = db.Package().Create(tx, bkt.BucketID, name)
|
||
if err != nil {
|
||
return fmt.Errorf("create package: %v", err)
|
||
}
|
||
|
||
return err
|
||
})
|
||
|
||
return newPackageFromCache(cache.Info(), r.vfs), nil
|
||
}
|
||
|
||
func (r *FuseBucket) NewFile(ctx context.Context, name string, flags uint32) (fuse.FileHandle, uint32, error) {
|
||
return newFile(r.vfs, ctx, name, r, flags)
|
||
}
|
||
|
||
func (r *FuseBucket) RemoveChild(ctx context.Context, name string) error {
|
||
// TODO 生成系统事件
|
||
return r.vfs.db.DoTx(func(tx db.SQLContext) error {
|
||
d := r.vfs.db
|
||
|
||
pkg, err := d.Package().GetByFullName(tx, r.bktName, name)
|
||
if err == nil {
|
||
err = d.Object().HasObjectWithPrefix(tx, pkg.PackageID, "")
|
||
if err == nil {
|
||
return fuse.ErrNotEmpty
|
||
}
|
||
if err != gorm.ErrRecordNotFound {
|
||
return err
|
||
}
|
||
|
||
err = d.Package().DeleteComplete(tx, pkg.PackageID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
} else if err != gorm.ErrRecordNotFound {
|
||
return err
|
||
}
|
||
|
||
return r.vfs.cache.Remove([]string{r.bktName, name})
|
||
})
|
||
}
|
||
|
||
func (r *FuseBucket) MoveChild(ctx context.Context, oldName string, newName string, newParent fuse.FsDir) error {
|
||
newParentNode := newParent.(FuseNode)
|
||
newParentPath := newParentNode.PathComps()
|
||
|
||
// 仅允许在不同桶之间移动
|
||
if len(newParentPath) != 1 {
|
||
return fuse.ErrNotSupported
|
||
}
|
||
|
||
d := r.vfs.db
|
||
return d.DoTx(func(tx db.SQLContext) error {
|
||
_, err := d.Package().GetByFullName(tx, newParentPath[0], newName)
|
||
if err == nil {
|
||
// 目标节点已经存在,不能重命名,直接退出
|
||
return fuse.ErrExists
|
||
} else if err != gorm.ErrRecordNotFound {
|
||
return err
|
||
}
|
||
|
||
newBkt, err := d.Bucket().GetByName(tx, newParentPath[0])
|
||
if err == nil {
|
||
oldPkg, err := d.Package().GetByFullName(tx, r.bktName, oldName)
|
||
if err == nil {
|
||
err = d.Package().Move(tx, oldPkg.PackageID, newBkt.BucketID, newName)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
} else if err != gorm.ErrRecordNotFound {
|
||
return err
|
||
}
|
||
|
||
} else if err != gorm.ErrRecordNotFound {
|
||
return err
|
||
}
|
||
|
||
// 有可能是移动文件,所以如果是源文件夹未找到,也尝试进行移动
|
||
return r.vfs.cache.Move([]string{r.bktName, oldName}, []string{newParentPath[0], newName})
|
||
})
|
||
}
|
||
|
||
func (r *FuseBucket) loadCacheDir() *cache.CacheDir {
|
||
var createOpt *cache.CreateDirOption
|
||
bkt, err := r.vfs.db.Bucket().GetByName(r.vfs.db.DefCtx(), r.bktName)
|
||
if err == nil {
|
||
createOpt = &cache.CreateDirOption{
|
||
ModTime: bkt.CreateTime,
|
||
}
|
||
}
|
||
|
||
return r.vfs.cache.LoadDir([]string{r.bktName}, createOpt)
|
||
}
|
||
|
||
var _ fuse.FsDir = (*FuseBucket)(nil)
|
||
var _ FuseNode = (*FuseBucket)(nil)
|