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 }