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 Folders []*DirNode Files []*FileNode Parent *DirNode AbsolutePath string } type FileNode struct { Config *configCollect Ent *DirEnt Elem FileElem AbsolutePath string } type RepoFs struct { Config *configCollect EntryNode *DirNode } func NewRepoFs(config *configCollect) *RepoFs { rf := new(RepoFs) rf.Config = config rf.EntryNode = new(DirNode) rf.EntryNode.Ent = new(DirEnt) rf.EntryNode.Ent.Id = config.PathId rf.EntryNode.Ent.Name = "" rf.EntryNode.AbsolutePath = "/" rf.EntryNode.Files = make([]*FileNode,0) rf.EntryNode.Folders = make([]*DirNode,0) rf.EntryNode.Config = config return rf } func NewDirNode(parent *DirNode, ent *DirEnt) *DirNode { dn := new(DirNode) dn.Ent = ent dn.Config = parent.Config dn.Files = make([]*FileNode,0) dn.Folders = make([]*DirNode,0) dn.Parent = parent dn.AbsolutePath = dn.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) } for _, el := range dn.Elem.Entries { if IsDir(el.Mode) { dn.Folders = append(dn.Folders, NewDirNode(dn, el)) } else if IsRegular(el.Mode) { dn.Files = append(dn.Files, NewFileNode(dn, el)) } else { log.Fatal("Unknown mode", el.Mode, "for", el.Name, "in object id", dn.Ent.Id) } } } 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) } } type ExpandStrat int const ( ExpandOnlyDirectory ExpandStrat = iota ExpandAll ) func (dn* DirNode) Walk(strat ExpandStrat, depth int) { qNode := []*DirNode{dn} for len(qNode) > 0 && depth != 0{ if depth > 0 { depth = depth - 1 } cursor := qNode[0] qNode = qNode[1:] cursor.Parse() for _, ndn := range dn.Folders { qNode = append(qNode, ndn) } if ExpandAll == strat { for _, nfn := range dn.Files { nfn.Parse() } } } } /*func (rf* RepoFs) Walk() { qPathIds := make([]string, 0) qPathIds = append(qPathIds, rf.PathId) for limit := 0 ; limit < 20000 && len(pathIds) > 0 ; limit++ { pathId := qPathIds[0] qPathIds = qPathIds[1:] NewDirNode(filepath.Join(rf.Config.Storage, "fs", rf.Config.RepoId), pathId) } }*/ // 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 }