seafile_recovery/fs.go

176 lines
3.9 KiB
Go

package main
import(
"compress/zlib"
"fmt"
"log"
"os"
"encoding/json"
"syscall"
"path/filepath"
)
// Imported from https://github.com/haiwen/seafile-server/blob/master/fileserver/fsmgr/fsmgr.go
// License AGPLv3
//SeafDir is a dir object
type DirElem struct {
Version int `json:"version"`
DirType int `json:"type,omitempty"`
DirId string `json:"dir_id,omitempty"`
Entries []*DirEnt `json:"dirents"`
}
// SeafDirent is a dir entry object
type DirEnt struct {
Mode uint32 `json:"mode"`
Id string `json:"id"`
Name string `json:"name"`
Mtime int64 `json:"mtime"`
Modifier string `json:"modifier"`
Size int64 `json:"size"`
}
type FileElem struct {
Version int `json:"version"`
FileType int `json:"type,omitempty"`
FileId string `json:"file_id,omitempty"`
FileSize uint64 `json:"size"`
BlkIds []string `json:"block_ids"`
}
type DirNode struct {
Config *configCollect
Ent *DirEnt
Elem DirElem
AbsolutePath string
}
type FileNode struct {
Config *configCollect
Ent *DirEnt
Elem FileElem
AbsolutePath string
}
type TreeObserver interface {
onDir(dir *DirNode)
onFile(file *FileNode)
}
func NewEntryNode(config *configCollect) *DirNode {
entryNode := new(DirNode)
entryNode.Ent = new(DirEnt)
entryNode.Ent.Id = config.DirId
entryNode.Ent.Name = ""
entryNode.AbsolutePath = "/"
entryNode.Config = config
return entryNode
}
func NewDirNode(parent *DirNode, ent *DirEnt) *DirNode {
dn := new(DirNode)
dn.Ent = ent
dn.Config = parent.Config
dn.AbsolutePath = parent.AbsolutePath + ent.Name + "/"
return dn
}
func (dn* DirNode) String() string {
return fmt.Sprintf("%v %v", dn.Ent.Id[:6], dn.AbsolutePath)
}
func (fn* FileNode) String() string {
return fmt.Sprintf("%v %v", fn.Ent.Id[:6], fn.AbsolutePath)
}
func NewFileNode(parent *DirNode, ent *DirEnt) *FileNode {
fn := new (FileNode)
fn.Ent = ent
fn.Config = parent.Config
fn.AbsolutePath = parent.AbsolutePath + ent.Name
return fn
}
func (dn* DirNode) Parse() {
path := filepath.Join(dn.Config.Storage, "fs", dn.Config.RepoId, dn.Ent.Id[:2], dn.Ent.Id[2:])
file, err := os.Open(path)
if err != nil { log.Fatal(err) }
defer file.Close()
zfile, err := zlib.NewReader(file)
if err != nil { log.Fatal(err) }
jdec := json.NewDecoder(zfile)
err = jdec.Decode(&dn.Elem)
if err != nil { log.Fatal(err) }
}
func (fn* FileNode) Parse() {
path := filepath.Join(fn.Config.Storage, "fs", fn.Config.RepoId, fn.Ent.Id[:2], fn.Ent.Id[2:])
file, err := os.Open(path)
if err != nil { log.Fatal(err) }
defer file.Close()
zfile, err := zlib.NewReader(file)
if err != nil { log.Fatal(err) }
jdec := json.NewDecoder(zfile)
err = jdec.Decode(&fn.Elem)
if err != nil { log.Fatal(err) }
}
func (dn* DirNode) Children() ([]*DirNode, []*FileNode) {
folders := make([]*DirNode,0)
files := make([]*FileNode, 0)
for _, el := range dn.Elem.Entries {
if el.Id == "0000000000000000000000000000000000000000" {
log.Println("[Lost] "+dn.AbsolutePath+el.Name)
} else if IsDir(el.Mode) {
folders = append(folders, NewDirNode(dn, el))
} else if IsRegular(el.Mode) {
files = append(files, NewFileNode(dn, el))
} else {
log.Fatal("Unknown mode", el.Mode, "for", el.Name, "in object id", dn.Ent.Id)
}
}
return folders, files
}
func (dn* DirNode) Walk(tobs TreeObserver) {
qNode := []*DirNode{dn}
for len(qNode) > 0 {
cursor := qNode[0]
qNode = qNode[1:]
cursor.Parse()
dir, file := cursor.Children()
qNode = append(dir, qNode...)
tobs.onDir(cursor)
for _, f := range file {
tobs.onFile(f)
}
}
}
// IsDir check if the mode is dir.
func IsDir(m uint32) bool {
return (m & syscall.S_IFMT) == syscall.S_IFDIR
}
// IsRegular Check if the mode is regular.
func IsRegular(m uint32) bool {
return (m & syscall.S_IFMT) == syscall.S_IFREG
}