248 lines
5.6 KiB
Go
248 lines
5.6 KiB
Go
package main
|
|
|
|
import(
|
|
"compress/zlib"
|
|
"fmt"
|
|
"log"
|
|
"os"
|
|
"io"
|
|
"io/ioutil"
|
|
"encoding/json"
|
|
"syscall"
|
|
"path/filepath"
|
|
)
|
|
|
|
// Imported from https://github.com/haiwen/seafile-server/blob/master/fileserver/fsmgr/fsmgr.go
|
|
// License AGPLv3
|
|
|
|
const emptyId = "0000000000000000000000000000000000000000"
|
|
|
|
//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
|
|
|
|
ReadBytes uint64
|
|
CurrentBlock *os.File
|
|
RemainingBlocks []string
|
|
}
|
|
|
|
func (fe* FileElem) String() string {
|
|
return fmt.Sprintf("Size: %v, Blocks: %v", fe.FileSize, len(fe.BlkIds))
|
|
}
|
|
|
|
type TreeObserver interface {
|
|
onDir(dir *DirNode)
|
|
onFile(file *FileNode)
|
|
}
|
|
|
|
func expandId(config *configCollect, userId string) string {
|
|
if len(userId) == len(emptyId) {
|
|
return userId
|
|
}
|
|
|
|
if len(userId) < 2 {
|
|
log.Fatal("User ID",userId,"is too short and thus cannot be expanded")
|
|
}
|
|
|
|
path := filepath.Join(config.Storage, "fs", config.RepoId, userId[:2])
|
|
files, err := ioutil.ReadDir(path)
|
|
if err != nil { log.Fatal("Unable to read dir", path, "in order to expand ID", userId) }
|
|
for _, f := range files {
|
|
if f.Name()[:len(userId)-2] == userId[2:] {
|
|
return userId[:2] + f.Name()
|
|
}
|
|
}
|
|
|
|
log.Fatal("Unable to find", userId[2:],"in",path)
|
|
return emptyId
|
|
}
|
|
|
|
func NewEntryNode(config *configCollect) *DirNode {
|
|
entryNode := new(DirNode)
|
|
entryNode.Ent = new(DirEnt)
|
|
entryNode.Ent.Id = expandId(config, 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 NewEntryFileNode(config *configCollect) *FileNode {
|
|
fn := new (FileNode)
|
|
fn.Ent = new(DirEnt)
|
|
fn.Ent.Id = expandId(config, config.FileId)
|
|
fn.Config = config
|
|
|
|
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) }
|
|
|
|
fn.RemainingBlocks = fn.Elem.BlkIds
|
|
}
|
|
|
|
func (fn* FileNode) Read(p []byte) (n int, err error) {
|
|
if fn.CurrentBlock == nil && len(fn.RemainingBlocks) == 0 {
|
|
return 0, io.EOF
|
|
}
|
|
|
|
if fn.CurrentBlock == nil {
|
|
blockId := fn.RemainingBlocks[0]
|
|
fn.RemainingBlocks = fn.RemainingBlocks[1:]
|
|
path := filepath.Join(fn.Config.Storage, "blocks", fn.Config.RepoId, blockId[:2], blockId[2:])
|
|
fn.CurrentBlock, err = os.Open(path)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
|
|
n, err = fn.CurrentBlock.Read(p)
|
|
fn.ReadBytes += uint64(n)
|
|
|
|
if err == io.EOF {
|
|
err = nil
|
|
fn.CurrentBlock.Close()
|
|
fn.CurrentBlock = nil
|
|
}
|
|
|
|
return n, err
|
|
}
|
|
|
|
func (dn* DirNode) Children() ([]*DirNode, []*FileNode) {
|
|
folders := make([]*DirNode,0)
|
|
files := make([]*FileNode, 0)
|
|
|
|
for _, el := range dn.Elem.Entries {
|
|
if el.Id == emptyId {
|
|
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
|
|
}
|
|
|
|
|