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

266 lines
6.1 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"
"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)