123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- package embedded_filer
- import (
- "bufio"
- "fmt"
- "io"
- "os"
- "path/filepath"
- "strconv"
- "strings"
- "sync"
- "github.com/chrislusf/seaweedfs/weed/filer"
- "github.com/chrislusf/seaweedfs/weed/util"
- )
- var writeLock sync.Mutex //serialize changes to dir.log
- type DirectoryId int32
- type DirectoryEntryInMap struct {
- sync.Mutex
- Name string
- Parent *DirectoryEntryInMap
- subDirectories map[string]*DirectoryEntryInMap
- Id DirectoryId
- }
- func (de *DirectoryEntryInMap) getChild(dirName string) (*DirectoryEntryInMap, bool) {
- de.Lock()
- defer de.Unlock()
- child, ok := de.subDirectories[dirName]
- return child, ok
- }
- func (de *DirectoryEntryInMap) addChild(dirName string, child *DirectoryEntryInMap) {
- de.Lock()
- defer de.Unlock()
- de.subDirectories[dirName] = child
- }
- func (de *DirectoryEntryInMap) removeChild(dirName string) {
- de.Lock()
- defer de.Unlock()
- delete(de.subDirectories, dirName)
- }
- func (de *DirectoryEntryInMap) hasChildren() bool {
- de.Lock()
- defer de.Unlock()
- return len(de.subDirectories) > 0
- }
- func (de *DirectoryEntryInMap) children() (dirNames []filer.DirectoryName) {
- de.Lock()
- defer de.Unlock()
- for k, _ := range de.subDirectories {
- dirNames = append(dirNames, filer.DirectoryName(k))
- }
- return dirNames
- }
- type DirectoryManagerInMap struct {
- Root *DirectoryEntryInMap
- max DirectoryId
- logFile *os.File
- isLoading bool
- }
- func (dm *DirectoryManagerInMap) newDirectoryEntryInMap(parent *DirectoryEntryInMap, name string) (d *DirectoryEntryInMap, err error) {
- d = &DirectoryEntryInMap{Name: name, Parent: parent, subDirectories: make(map[string]*DirectoryEntryInMap)}
- var parts []string
- for p := d; p != nil && p.Name != ""; p = p.Parent {
- parts = append(parts, p.Name)
- }
- n := len(parts)
- if n <= 0 {
- return nil, fmt.Errorf("Failed to create folder %s/%s", parent.Name, name)
- }
- for i := 0; i < n/2; i++ {
- parts[i], parts[n-1-i] = parts[n-1-i], parts[i]
- }
- dm.max++
- d.Id = dm.max
- dm.log("add", "/"+strings.Join(parts, "/"), strconv.Itoa(int(d.Id)))
- return d, nil
- }
- func (dm *DirectoryManagerInMap) log(words ...string) {
- if !dm.isLoading {
- dm.logFile.WriteString(strings.Join(words, "\t") + "\n")
- }
- }
- func NewDirectoryManagerInMap(dirLogFile string) (dm *DirectoryManagerInMap, err error) {
- dm = &DirectoryManagerInMap{}
- //dm.Root do not use newDirectoryEntryInMap, since dm.max will be changed
- dm.Root = &DirectoryEntryInMap{subDirectories: make(map[string]*DirectoryEntryInMap)}
- if dm.logFile, err = os.OpenFile(dirLogFile, os.O_RDWR|os.O_CREATE, 0644); err != nil {
- return nil, fmt.Errorf("cannot write directory log file %s: %v", dirLogFile, err)
- }
- return dm, dm.load()
- }
- func (dm *DirectoryManagerInMap) processEachLine(line string) error {
- if strings.HasPrefix(line, "#") {
- return nil
- }
- if line == "" {
- return nil
- }
- parts := strings.Split(line, "\t")
- if len(parts) == 0 {
- return nil
- }
- switch parts[0] {
- case "add":
- v, pe := strconv.Atoi(parts[2])
- if pe != nil {
- return pe
- }
- if e := dm.loadDirectory(parts[1], DirectoryId(v)); e != nil {
- return e
- }
- case "mov":
- newName := ""
- if len(parts) >= 4 {
- newName = parts[3]
- }
- if e := dm.MoveUnderDirectory(parts[1], parts[2], newName); e != nil {
- return e
- }
- case "del":
- if e := dm.DeleteDirectory(parts[1]); e != nil {
- return e
- }
- default:
- fmt.Printf("line %s has %s!\n", line, parts[0])
- return nil
- }
- return nil
- }
- func (dm *DirectoryManagerInMap) load() error {
- dm.max = 0
- lines := bufio.NewReader(dm.logFile)
- dm.isLoading = true
- defer func() { dm.isLoading = false }()
- for {
- line, err := util.Readln(lines)
- if err != nil && err != io.EOF {
- return err
- }
- if pe := dm.processEachLine(string(line)); pe != nil {
- return pe
- }
- if err == io.EOF {
- return nil
- }
- }
- }
- func (dm *DirectoryManagerInMap) findDirectory(dirPath string) (*DirectoryEntryInMap, error) {
- if dirPath == "" {
- return dm.Root, nil
- }
- dirPath = CleanFilePath(dirPath)
- if dirPath == "/" {
- return dm.Root, nil
- }
- parts := strings.Split(dirPath, "/")
- dir := dm.Root
- for i := 1; i < len(parts); i++ {
- if sub, ok := dir.getChild(parts[i]); ok {
- dir = sub
- } else {
- return dm.Root, filer.ErrNotFound
- }
- }
- return dir, nil
- }
- func (dm *DirectoryManagerInMap) findDirectoryId(dirPath string) (DirectoryId, error) {
- d, e := dm.findDirectory(dirPath)
- if e == nil {
- return d.Id, nil
- }
- return dm.Root.Id, e
- }
- func (dm *DirectoryManagerInMap) loadDirectory(dirPath string, dirId DirectoryId) error {
- dirPath = CleanFilePath(dirPath)
- if dirPath == "/" {
- return nil
- }
- parts := strings.Split(dirPath, "/")
- dir := dm.Root
- for i := 1; i < len(parts); i++ {
- sub, ok := dir.getChild(parts[i])
- if !ok {
- writeLock.Lock()
- if sub2, createdByOtherThread := dir.getChild(parts[i]); createdByOtherThread {
- sub = sub2
- } else {
- if i != len(parts)-1 {
- writeLock.Unlock()
- return fmt.Errorf("%s should be created after parent %s", dirPath, parts[i])
- }
- var err error
- sub, err = dm.newDirectoryEntryInMap(dir, parts[i])
- if err != nil {
- writeLock.Unlock()
- return err
- }
- if sub.Id != dirId {
- writeLock.Unlock()
- // the dir.log should be the same order as in-memory directory id
- return fmt.Errorf("%s should be have id %v instead of %v", dirPath, sub.Id, dirId)
- }
- dir.addChild(parts[i], sub)
- }
- writeLock.Unlock()
- }
- dir = sub
- }
- return nil
- }
- func (dm *DirectoryManagerInMap) makeDirectory(dirPath string) (dir *DirectoryEntryInMap, created bool) {
- dirPath = CleanFilePath(dirPath)
- if dirPath == "/" {
- return dm.Root, false
- }
- parts := strings.Split(dirPath, "/")
- dir = dm.Root
- for i := 1; i < len(parts); i++ {
- sub, ok := dir.getChild(parts[i])
- if !ok {
- writeLock.Lock()
- if sub2, createdByOtherThread := dir.getChild(parts[i]); createdByOtherThread {
- sub = sub2
- } else {
- var err error
- sub, err = dm.newDirectoryEntryInMap(dir, parts[i])
- if err != nil {
- writeLock.Unlock()
- return nil, false
- }
- dir.addChild(parts[i], sub)
- created = true
- }
- writeLock.Unlock()
- }
- dir = sub
- }
- return dir, created
- }
- func (dm *DirectoryManagerInMap) MakeDirectory(dirPath string) (DirectoryId, error) {
- dir, _ := dm.makeDirectory(dirPath)
- return dir.Id, nil
- }
- func (dm *DirectoryManagerInMap) MoveUnderDirectory(oldDirPath string, newParentDirPath string, newName string) error {
- writeLock.Lock()
- defer writeLock.Unlock()
- oldDir, oe := dm.findDirectory(oldDirPath)
- if oe != nil {
- return oe
- }
- parentDir, pe := dm.findDirectory(newParentDirPath)
- if pe != nil {
- return pe
- }
- dm.log("mov", oldDirPath, newParentDirPath, newName)
- oldDir.Parent.removeChild(oldDir.Name)
- if newName == "" {
- newName = oldDir.Name
- }
- parentDir.addChild(newName, oldDir)
- oldDir.Name = newName
- oldDir.Parent = parentDir
- return nil
- }
- func (dm *DirectoryManagerInMap) ListDirectories(dirPath string) (dirNames []filer.DirectoryName, err error) {
- d, e := dm.findDirectory(dirPath)
- if e != nil {
- return dirNames, e
- }
- return d.children(), nil
- }
- func (dm *DirectoryManagerInMap) DeleteDirectory(dirPath string) error {
- writeLock.Lock()
- defer writeLock.Unlock()
- if dirPath == "/" {
- return fmt.Errorf("Can not delete %s", dirPath)
- }
- d, e := dm.findDirectory(dirPath)
- if e != nil {
- return e
- }
- if d.hasChildren() {
- return fmt.Errorf("dir %s still has sub directories", dirPath)
- }
- d.Parent.removeChild(d.Name)
- d.Parent = nil
- dm.log("del", dirPath)
- return nil
- }
- func CleanFilePath(fp string) string {
- ret := filepath.Clean(fp)
- if os.PathSeparator == '\\' {
- return strings.Replace(ret, "\\", "/", -1)
- }
- return ret
- }
|