2024-04-26 17:16:32 +08:00
|
|
|
|
// Copyright 2024 GoEdge CDN goedge.cdn@gmail.com. All rights reserved. Official site: https://goedge.cn .
|
|
|
|
|
|
|
|
|
|
|
|
package bfs
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"bytes"
|
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/fasttime"
|
2024-05-11 09:23:54 +08:00
|
|
|
|
"github.com/TeaOSLab/EdgeNode/internal/utils/zero"
|
2024-04-26 17:16:32 +08:00
|
|
|
|
"io"
|
|
|
|
|
|
"os"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
const MFileExt = ".m"
|
|
|
|
|
|
const Version1 = 1
|
|
|
|
|
|
|
|
|
|
|
|
type MetaFile struct {
|
|
|
|
|
|
fp *os.File
|
|
|
|
|
|
filename string
|
2024-04-27 20:11:50 +08:00
|
|
|
|
headerMap map[string]*LazyFileHeader // hash => *LazyFileHeader
|
|
|
|
|
|
mu *sync.RWMutex // TODO 考虑单独一个,不要和bFile共享?
|
2024-04-26 17:16:32 +08:00
|
|
|
|
|
|
|
|
|
|
isModified bool
|
2024-04-27 20:11:50 +08:00
|
|
|
|
modifiedHashMap map[string]zero.Zero // hash => Zero
|
2024-04-26 17:16:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-26 18:44:29 +08:00
|
|
|
|
func OpenMetaFile(filename string, mu *sync.RWMutex) (*MetaFile, error) {
|
2024-04-26 17:16:32 +08:00
|
|
|
|
fp, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, 0666)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var mFile = &MetaFile{
|
|
|
|
|
|
filename: filename,
|
|
|
|
|
|
fp: fp,
|
2024-04-27 20:11:50 +08:00
|
|
|
|
headerMap: map[string]*LazyFileHeader{},
|
2024-04-26 17:16:32 +08:00
|
|
|
|
mu: mu,
|
|
|
|
|
|
modifiedHashMap: map[string]zero.Zero{},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 从文件中加载已有的文件头信息
|
|
|
|
|
|
err = mFile.load()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return nil, err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return mFile, nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *MetaFile) load() error {
|
2024-04-27 07:09:14 +08:00
|
|
|
|
AckReadThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
_, err := this.fp.Seek(0, io.SeekStart)
|
2024-04-27 07:09:14 +08:00
|
|
|
|
ReleaseReadThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 20:11:50 +08:00
|
|
|
|
// TODO 检查文件是否完整
|
2024-04-26 17:16:32 +08:00
|
|
|
|
|
|
|
|
|
|
var buf = make([]byte, 4<<10)
|
|
|
|
|
|
var blockBytes []byte
|
|
|
|
|
|
for {
|
2024-04-27 07:09:14 +08:00
|
|
|
|
AckReadThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
n, readErr := this.fp.Read(buf)
|
2024-04-27 07:09:14 +08:00
|
|
|
|
ReleaseReadThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if n > 0 {
|
|
|
|
|
|
blockBytes = append(blockBytes, buf[:n]...)
|
|
|
|
|
|
for len(blockBytes) > 4 {
|
|
|
|
|
|
var l = int(binary.BigEndian.Uint32(blockBytes[:4])) + 4 /* Len **/
|
|
|
|
|
|
if len(blockBytes) < l {
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
action, hash, data, decodeErr := DecodeMetaBlock(blockBytes[:l])
|
|
|
|
|
|
if decodeErr != nil {
|
|
|
|
|
|
return decodeErr
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
switch action {
|
|
|
|
|
|
case MetaActionNew:
|
2024-04-27 20:11:50 +08:00
|
|
|
|
this.headerMap[hash] = NewLazyFileHeaderFromData(data)
|
2024-04-26 17:16:32 +08:00
|
|
|
|
case MetaActionRemove:
|
|
|
|
|
|
delete(this.headerMap, hash)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
blockBytes = blockBytes[l:]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if readErr != nil {
|
|
|
|
|
|
if readErr == io.EOF {
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
return readErr
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *MetaFile) WriteMeta(hash string, status int, expiresAt int64, expectedFileSize int64) error {
|
2024-04-27 20:11:50 +08:00
|
|
|
|
|
2024-04-26 17:16:32 +08:00
|
|
|
|
this.mu.Lock()
|
|
|
|
|
|
defer this.mu.Unlock()
|
|
|
|
|
|
|
2024-04-27 20:11:50 +08:00
|
|
|
|
this.headerMap[hash] = NewLazyFileHeader(&FileHeader{
|
2024-04-26 17:16:32 +08:00
|
|
|
|
Version: Version1,
|
|
|
|
|
|
ExpiresAt: expiresAt,
|
|
|
|
|
|
Status: status,
|
|
|
|
|
|
ExpiredBodySize: expectedFileSize,
|
|
|
|
|
|
IsWriting: true,
|
2024-04-27 20:11:50 +08:00
|
|
|
|
})
|
2024-04-26 17:16:32 +08:00
|
|
|
|
|
|
|
|
|
|
this.modifiedHashMap[hash] = zero.Zero{}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *MetaFile) WriteHeaderBlockUnsafe(hash string, bOffsetFrom int64, bOffsetTo int64) error {
|
2024-04-27 20:11:50 +08:00
|
|
|
|
lazyHeader, ok := this.headerMap[hash]
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if !ok {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 20:11:50 +08:00
|
|
|
|
header, err := lazyHeader.FileHeaderUnsafe()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-26 17:16:32 +08:00
|
|
|
|
// TODO 合并相邻block
|
|
|
|
|
|
header.HeaderBlocks = append(header.HeaderBlocks, BlockInfo{
|
|
|
|
|
|
BFileOffsetFrom: bOffsetFrom,
|
|
|
|
|
|
BFileOffsetTo: bOffsetTo,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
this.modifiedHashMap[hash] = zero.Zero{}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *MetaFile) WriteBodyBlockUnsafe(hash string, bOffsetFrom int64, bOffsetTo int64, originOffsetFrom int64, originOffsetTo int64) error {
|
2024-04-27 20:11:50 +08:00
|
|
|
|
lazyHeader, ok := this.headerMap[hash]
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if !ok {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 20:11:50 +08:00
|
|
|
|
header, err := lazyHeader.FileHeaderUnsafe()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-26 17:16:32 +08:00
|
|
|
|
// TODO 合并相邻block
|
|
|
|
|
|
header.BodyBlocks = append(header.BodyBlocks, BlockInfo{
|
|
|
|
|
|
OriginOffsetFrom: originOffsetFrom,
|
|
|
|
|
|
OriginOffsetTo: originOffsetTo,
|
|
|
|
|
|
BFileOffsetFrom: bOffsetFrom,
|
|
|
|
|
|
BFileOffsetTo: bOffsetTo,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
this.modifiedHashMap[hash] = zero.Zero{}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *MetaFile) WriteClose(hash string, headerSize int64, bodySize int64) error {
|
|
|
|
|
|
// TODO 考虑单个hash多次重复调用的情况
|
|
|
|
|
|
|
|
|
|
|
|
this.mu.Lock()
|
2024-04-27 20:11:50 +08:00
|
|
|
|
lazyHeader, ok := this.headerMap[hash]
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if !ok {
|
2024-04-27 20:11:50 +08:00
|
|
|
|
this.mu.Unlock()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 20:11:50 +08:00
|
|
|
|
header, err := lazyHeader.FileHeaderUnsafe()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
// TODO 检查bodySize和expectedBodySize是否一致,如果不一致则从headerMap中删除
|
|
|
|
|
|
|
|
|
|
|
|
header.ModifiedAt = fasttime.Now().Unix()
|
|
|
|
|
|
header.HeaderSize = headerSize
|
|
|
|
|
|
header.BodySize = bodySize
|
|
|
|
|
|
header.Compact()
|
|
|
|
|
|
|
2024-04-28 10:06:29 +08:00
|
|
|
|
blockBytes, err := header.Encode(hash)
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.mu.Lock()
|
|
|
|
|
|
defer this.mu.Unlock()
|
|
|
|
|
|
|
2024-04-27 07:09:14 +08:00
|
|
|
|
AckReadThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
_, err = this.fp.Seek(0, io.SeekEnd)
|
2024-04-27 07:09:14 +08:00
|
|
|
|
ReleaseReadThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 07:09:14 +08:00
|
|
|
|
AckWriteThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
_, err = this.fp.Write(blockBytes)
|
2024-04-27 07:09:14 +08:00
|
|
|
|
ReleaseWriteThread()
|
|
|
|
|
|
|
2024-04-26 17:16:32 +08:00
|
|
|
|
this.isModified = true
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *MetaFile) RemoveFile(hash string) error {
|
|
|
|
|
|
this.mu.Lock()
|
|
|
|
|
|
defer this.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
_, ok := this.headerMap[hash]
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
delete(this.headerMap, hash)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
blockBytes, err := EncodeMetaBlock(MetaActionRemove, hash, nil)
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 07:09:14 +08:00
|
|
|
|
AckWriteThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
_, err = this.fp.Write(blockBytes)
|
2024-04-27 07:09:14 +08:00
|
|
|
|
ReleaseWriteThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
this.isModified = true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-26 18:44:29 +08:00
|
|
|
|
func (this *MetaFile) FileHeader(hash string) (header *FileHeader, ok bool) {
|
2024-04-26 17:16:32 +08:00
|
|
|
|
this.mu.RLock()
|
|
|
|
|
|
defer this.mu.RUnlock()
|
2024-04-27 20:11:50 +08:00
|
|
|
|
|
|
|
|
|
|
lazyHeader, ok := this.headerMap[hash]
|
|
|
|
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
var err error
|
|
|
|
|
|
header, err = lazyHeader.FileHeaderUnsafe()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ok = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-04-26 17:16:32 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 18:27:49 +08:00
|
|
|
|
func (this *MetaFile) FileHeaderUnsafe(hash string) (header *FileHeader, ok bool) {
|
2024-04-27 20:11:50 +08:00
|
|
|
|
lazyHeader, ok := this.headerMap[hash]
|
|
|
|
|
|
|
|
|
|
|
|
if ok {
|
|
|
|
|
|
var err error
|
|
|
|
|
|
header, err = lazyHeader.FileHeaderUnsafe()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ok = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 18:27:49 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-26 18:44:29 +08:00
|
|
|
|
func (this *MetaFile) CloneFileHeader(hash string) (header *FileHeader, ok bool) {
|
2024-04-26 17:16:32 +08:00
|
|
|
|
this.mu.RLock()
|
|
|
|
|
|
defer this.mu.RUnlock()
|
2024-04-27 20:11:50 +08:00
|
|
|
|
lazyHeader, ok := this.headerMap[hash]
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if !ok {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 20:11:50 +08:00
|
|
|
|
var err error
|
|
|
|
|
|
header, err = lazyHeader.FileHeaderUnsafe()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
ok = false
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-26 17:16:32 +08:00
|
|
|
|
header = header.Clone()
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 20:11:50 +08:00
|
|
|
|
func (this *MetaFile) FileHeaders() map[string]*LazyFileHeader {
|
2024-04-26 17:16:32 +08:00
|
|
|
|
this.mu.RLock()
|
|
|
|
|
|
defer this.mu.RUnlock()
|
|
|
|
|
|
return this.headerMap
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-26 18:44:29 +08:00
|
|
|
|
func (this *MetaFile) ExistFile(hash string) bool {
|
|
|
|
|
|
this.mu.RLock()
|
|
|
|
|
|
defer this.mu.RUnlock()
|
|
|
|
|
|
|
|
|
|
|
|
_, ok := this.headerMap[hash]
|
|
|
|
|
|
return ok
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-26 17:16:32 +08:00
|
|
|
|
// Compact the meta file
|
|
|
|
|
|
// TODO 考虑自动Compact的时机(脏数据比例?)
|
|
|
|
|
|
func (this *MetaFile) Compact() error {
|
|
|
|
|
|
this.mu.Lock()
|
|
|
|
|
|
defer this.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
var buf = bytes.NewBuffer(nil)
|
2024-04-27 20:11:50 +08:00
|
|
|
|
for hash, lazyHeader := range this.headerMap {
|
|
|
|
|
|
header, err := lazyHeader.FileHeaderUnsafe()
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-28 10:06:29 +08:00
|
|
|
|
blockBytes, err := header.Encode(hash)
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
buf.Write(blockBytes)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 07:09:14 +08:00
|
|
|
|
AckWriteThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
err := this.fp.Truncate(int64(buf.Len()))
|
2024-04-27 07:09:14 +08:00
|
|
|
|
ReleaseWriteThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 07:09:14 +08:00
|
|
|
|
AckReadThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
_, err = this.fp.Seek(0, io.SeekStart)
|
2024-04-27 07:09:14 +08:00
|
|
|
|
ReleaseReadThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 07:09:14 +08:00
|
|
|
|
AckWriteThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
_, err = this.fp.Write(buf.Bytes())
|
2024-04-27 07:09:14 +08:00
|
|
|
|
ReleaseWriteThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
this.isModified = true
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (this *MetaFile) SyncUnsafe() error {
|
|
|
|
|
|
if !this.isModified {
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-27 07:09:14 +08:00
|
|
|
|
AckWriteThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
err := this.fp.Sync()
|
2024-04-27 07:09:14 +08:00
|
|
|
|
ReleaseWriteThread()
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
|
return err
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for hash := range this.modifiedHashMap {
|
2024-04-27 20:11:50 +08:00
|
|
|
|
lazyHeader, ok := this.headerMap[hash]
|
2024-04-26 17:16:32 +08:00
|
|
|
|
if ok {
|
2024-04-27 20:11:50 +08:00
|
|
|
|
header, decodeErr := lazyHeader.FileHeaderUnsafe()
|
|
|
|
|
|
if decodeErr != nil {
|
|
|
|
|
|
return decodeErr
|
|
|
|
|
|
}
|
2024-04-26 17:16:32 +08:00
|
|
|
|
header.IsWriting = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
this.isModified = false
|
|
|
|
|
|
this.modifiedHashMap = map[string]zero.Zero{}
|
|
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-04-26 18:44:29 +08:00
|
|
|
|
// Close 关闭当前文件
|
2024-04-26 17:16:32 +08:00
|
|
|
|
func (this *MetaFile) Close() error {
|
|
|
|
|
|
return this.fp.Close()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// RemoveAll 删除所有数据
|
|
|
|
|
|
func (this *MetaFile) RemoveAll() error {
|
|
|
|
|
|
_ = this.fp.Close()
|
|
|
|
|
|
return os.Remove(this.fp.Name())
|
|
|
|
|
|
}
|