浏览代码

locks and properties pass litmus

kpmy 8 年之前
父节点
当前提交
5f0fe73ee8
共有 10 个文件被更改,包括 507 次插入282 次删除
  1. 7 6
      dav_cmd/main.go
  2. 0 17
      wdfs/chain.go
  3. 32 52
      wdfs/files.go
  4. 29 6
      wdfs/filesys.go
  5. 0 72
      wdfs/loc.go
  6. 161 0
      wdfs/locations.go
  7. 156 0
      wdfs/locksys.go
  8. 0 78
      wdfs/ls.go
  9. 0 51
      wdfs/prop.go
  10. 122 0
      wdfs/props.go

+ 7 - 6
dav_cmd/main.go

@@ -68,19 +68,20 @@ func main() {
 		}
 	}(rootCh)
 
-	var fs webdav.FileSystem
-	var ls webdav.LockSystem
+	var fs interface{}
+	var ls interface{}
 	if nodeID, err := ipfs_api.Shell().ID(); err == nil {
-		fs = wdfs.NewFS(nodeID, defaultRoot)
-		ls = wdfs.NewLS(fs)
+		_fs := wdfs.NewFS(nodeID, defaultRoot)
+		fs = _fs
+		ls = wdfs.NewLS(_fs)
 	} else {
 		log.Fatal(err)
 	}
 	if !fn.IsNil(fs) {
 		h := &webdav.Handler{
 			Prefix:     "/ipfs",
-			FileSystem: fs,
-			LockSystem: ls,
+			FileSystem: fs.(webdav.FileSystem),
+			LockSystem: ls.(webdav.LockSystem),
 			Logger: func(r *http.Request, err error) {
 				switch r.Method {
 				case "COPY", "MOVE":

+ 0 - 17
wdfs/chain.go

@@ -6,7 +6,6 @@ import (
 	. "github.com/kpmy/ypk/tc"
 	"os"
 	"strings"
-	"time"
 )
 
 type chain struct {
@@ -84,14 +83,6 @@ func (root *chain) update(hash string) {
 	root.Hash = hash
 }
 
-func (c *chain) Name() string {
-	return c.name
-}
-
-func (c *chain) Size() int64 {
-	return int64(c.UnixLsObject.Size)
-}
-
 func (c *chain) Mode() os.FileMode {
 	if c.Type == "Directory" {
 		return os.ModeDir
@@ -101,14 +92,6 @@ func (c *chain) Mode() os.FileMode {
 	panic(100)
 }
 
-func (c *chain) ModTime() time.Time {
-	return time.Now()
-}
-
 func (c *chain) IsDir() bool {
 	return c.Mode() == os.ModeDir
 }
-func (c *chain) Sys() interface{} {
-	Halt(100)
-	return nil
-}

+ 32 - 52
wdfs/file.go → wdfs/files.go

@@ -11,13 +11,13 @@ import (
 	"sync"
 	"time"
 
-	"bytes"
+	"fmt"
 	"github.com/kpmy/ypk/dom"
 	. "github.com/kpmy/ypk/tc"
 	"golang.org/x/net/webdav"
 	"io"
 	"log"
-	"reflect"
+	"strconv"
 	"strings"
 )
 
@@ -34,6 +34,8 @@ type file struct {
 
 	wr chan *block
 	wg *sync.WaitGroup
+
+	props dom.Element
 }
 
 func (f *file) Name() string {
@@ -41,15 +43,23 @@ func (f *file) Name() string {
 }
 
 func (f *file) Size() int64 {
-	return f.ch.Size()
+	return int64(f.ch.UnixLsObject.Size)
 }
 
 func (f *file) Mode() os.FileMode {
 	return 0
 }
 
-func (f *file) ModTime() time.Time {
-	return time.Now()
+func (f *file) ModTime() (ret time.Time) {
+	ret = time.Now()
+	if !fn.IsNil(f.props) {
+		if ts := f.props.Attr("modified"); ts != "" {
+			if sec, err := strconv.ParseInt(ts, 10, 64); err == nil {
+				ret = time.Unix(sec, 0)
+			}
+		}
+	}
+	return
 }
 
 func (f *file) IsDir() bool {
@@ -146,8 +156,9 @@ func (f *file) update(data io.ReadCloser) {
 		tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, tail.down.name, tail.down.Hash, false)
 		if tail.down.Hash == f.ch.Hash {
 			//создадим пропы
-			prop := newProps()
-			propHash, _ := ipfs_api.Shell().Add(dom.EncodeWithHeader(prop))
+			f.props = newPropsModel()
+			f.props.Attr("modified", fmt.Sprint(time.Now().Unix()))
+			propHash, _ := ipfs_api.Shell().Add(dom.EncodeWithHeader(f.props))
 			tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, "*"+f.ch.name, propHash, false)
 		}
 	}
@@ -180,17 +191,27 @@ func (f *file) Write(p []byte) (n int, err error) {
 	return n, nil
 }
 
-func (f *file) readPropsObject() (props map[xml.Name]dom.Element, err error) {
+func (f *file) readPropsModel() {
 	if !strings.HasPrefix(f.ch.name, "*") {
 		ls, _ := ipfs_api.Shell().FileList(f.ch.up.Hash)
 		pm := propLinksMap(ls)
 		if p, ok := pm[f.ch.name]; ok {
 			rd, _ := ipfs_api.Shell().Cat(p.Hash)
 			if el, err := dom.Decode(rd); err == nil {
-				props = readProps(el.Model())
+				f.props = el.Model()
+			} else {
+				Halt(99, f.ch.name, f.ch.Hash)
 			}
+		} else {
+			f.props = newPropsModel()
 		}
 	}
+}
+
+func (f *file) readPropsObject() (props map[xml.Name]dom.Element, err error) {
+	props = make(map[xml.Name]dom.Element)
+	f.readPropsModel()
+	props = readProps(f.props)
 	return
 }
 
@@ -212,57 +233,16 @@ func (f *file) writePropsObject(props map[xml.Name]dom.Element) {
 
 func (f *file) DeadProps() (ret map[xml.Name]webdav.Property, err error) {
 	log.Println("file prop get")
-	ret = make(map[xml.Name]webdav.Property)
 	pm, _ := f.readPropsObject()
-	for k, v := range pm {
-		p := webdav.Property{XMLName: k}
-		buf := new(bytes.Buffer)
-		Assert(v.ChildrenCount() == 1, 40)
-		c0 := v.Children()[0]
-		switch c := c0.(type) {
-		case dom.Element:
-			rd := dom.Encode(c)
-			io.Copy(buf, rd)
-		case dom.Text:
-			xml.EscapeText(buf, []byte(c.Data()))
-		default:
-			Halt(100, reflect.TypeOf(c))
-		}
-		p.InnerXML = buf.Bytes()
-		ret[k] = p
-	}
+	ret = props2webdav(pm)
 	log.Println("read file props", ret)
 	return
 }
 
 func (f *file) Patch(patch []webdav.Proppatch) (ret []webdav.Propstat, err error) {
 	log.Println("file prop patch", patch)
-	ret = []webdav.Propstat{}
 	pe, _ := f.readPropsObject()
-	for _, pl := range patch {
-		ps := webdav.Propstat{}
-		for _, p := range pl.Props {
-			if pl.Remove {
-				delete(pe, p.XMLName)
-			} else {
-				el := dom.Elem("prop")
-				el.Attr("local", p.XMLName.Local)
-				el.Attr("space", p.XMLName.Space)
-				e, _ := dom.Decode(bytes.NewBuffer(p.InnerXML))
-				if !fn.IsNil(e.Model()) {
-					el.AppendChild(e.Model())
-				} else if !fn.IsNil(e.Data()) {
-					el.AppendChild(e.Data())
-				} else {
-					Halt(100)
-				}
-				pe[p.XMLName] = el
-			}
-			ps.Props = append(ps.Props, p)
-		}
-		ps.Status = 200
-		ret = append(ret, ps)
-	}
+	ret = propsPatch(pe, patch)
 	log.Println("write file props", pe)
 	f.writePropsObject(pe)
 	return

+ 29 - 6
wdfs/fs.go → wdfs/filesys.go

@@ -1,6 +1,7 @@
 package wdfs
 
 import (
+	"fmt"
 	"github.com/ipfs/go-ipfs-api"
 	"github.com/kpmy/mipfs/ipfs_api"
 	"github.com/kpmy/ypk/dom"
@@ -10,6 +11,7 @@ import (
 	"log"
 	"os"
 	"strings"
+	"time"
 )
 
 type filesystem struct {
@@ -26,7 +28,8 @@ func (f *filesystem) Mkdir(name string, perm os.FileMode) (err error) {
 			if !tail.exists() {
 				if onlyOne {
 					tail.Hash, _ = ipfs_api.Shell().NewObject("unixfs-dir")
-					prop := newProps()
+					prop := newPropsModel()
+					prop.Attr("modified", fmt.Sprint(time.Now().Unix()))
 					propHash, _ := ipfs_api.Shell().Add(dom.EncodeWithHeader(prop))
 					if tail.Hash, err = ipfs_api.Shell().PatchLink(tail.Hash, "*", propHash, false); err != nil {
 						log.Fatal(err)
@@ -55,9 +58,13 @@ func (f *filesystem) OpenFile(name string, flag int, perm os.FileMode) (ret webd
 	path := newChain(f.root.mirror(), f.root.Hash+"/"+strings.Trim(name, "/"))
 	switch tail := path.tail(); {
 	case tail.exists() && tail.IsDir():
-		ret = &loc{ch: tail}
+		_l := &loc{ch: tail}
+		_l.readPropsModel()
+		ret = _l
 	case tail.exists() && !tail.IsDir():
-		ret = &file{ch: tail}
+		_f := &file{ch: tail}
+		_f.readPropsModel()
+		ret = _f
 	case !tail.exists() && flag&os.O_CREATE != 0:
 		var edir *chain
 		for edir = tail.up; edir != nil && edir.exists() && edir.IsDir(); edir = edir.up {
@@ -96,7 +103,7 @@ func (f *filesystem) RemoveAll(name string) (err error) {
 }
 
 func (f *filesystem) Rename(oldName, newName string) (err error) {
-	log.Println("rename", oldName, newName)
+	//log.Println("rename", oldName, newName)
 	on := newChain(f.root.mirror(), f.root.Hash+"/"+strings.Trim(oldName, "/"))
 	var op *chain
 	if !on.tail().IsDir() {
@@ -139,11 +146,19 @@ func (f *filesystem) Rename(oldName, newName string) (err error) {
 }
 
 func (f *filesystem) Stat(name string) (fi os.FileInfo, err error) {
-	log.Println("stat", name)
+	//log.Println("stat", name)
 	chain := newChain(f.root.mirror(), f.root.Hash+"/"+strings.Trim(name, "/"))
 	tail := chain.tail()
-	if fi = tail; !tail.exists() {
+	if !tail.exists() {
 		err = os.ErrNotExist
+	} else if tail.IsDir() {
+		_l := &loc{ch: tail}
+		_l.readPropsModel()
+		fi = _l
+	} else {
+		_f := &file{ch: tail}
+		_f.readPropsModel()
+		fi = _f
 	}
 	return
 }
@@ -152,6 +167,14 @@ func (f *filesystem) String() string {
 	return f.root.Hash
 }
 
+func (f *filesystem) ETag(name string) (ret string, err error) {
+	var fi os.FileInfo
+	if fi, err = f.Stat(name); err == nil {
+		ret = fmt.Sprintf(`"%x%x"`, fi.ModTime().UnixNano(), fi.Size())
+	}
+	return
+}
+
 func NewFS(id *shell.IdOutput, root string) *filesystem {
 	ch := &chain{}
 	ch.Hash = root

+ 0 - 72
wdfs/loc.go

@@ -1,72 +0,0 @@
-package wdfs
-
-import (
-	"encoding/xml"
-	"github.com/kpmy/mipfs/ipfs_api"
-	"github.com/kpmy/ypk/dom"
-	. "github.com/kpmy/ypk/tc"
-	"golang.org/x/net/webdav"
-	"log"
-	"os"
-	"strings"
-)
-
-type loc struct {
-	ch *chain
-}
-
-func (l *loc) Close() error {
-	return nil
-}
-
-func (l *loc) Read(p []byte) (n int, err error) {
-	return 0, webdav.ErrForbidden
-}
-
-func (l *loc) Seek(offset int64, whence int) (int64, error) {
-	return 0, webdav.ErrForbidden
-}
-
-func (l *loc) Readdir(count int) (ret []os.FileInfo, err error) {
-	ls, _ := ipfs_api.Shell().FileList(l.ch.Hash)
-	for _, lo := range ls.Links {
-		switch lo.Type {
-		case "File":
-			fallthrough
-		case "Directory":
-			if !strings.HasPrefix(lo.Name, "*") {
-				filepath := l.ch.Hash + "/" + lo.Name
-				ret = append(ret, newChain(l.ch, filepath).tail())
-			}
-		default:
-			Halt(100)
-		}
-	}
-	return
-}
-
-func (l *loc) Stat() (os.FileInfo, error) {
-	return l.ch, nil
-}
-
-func (l *loc) Write(p []byte) (n int, err error) {
-	return 0, webdav.ErrForbidden
-}
-
-func (l *loc) DeadProps() (ret map[xml.Name]webdav.Property, err error) {
-	ls, _ := ipfs_api.Shell().FileList(l.ch.Hash)
-	pm := propLinksMap(ls)
-	ret = make(map[xml.Name]webdav.Property)
-	if p, ok := pm["*"]; ok {
-		rd, _ := ipfs_api.Shell().Cat(p.Hash)
-		if el, err := dom.Decode(rd); err == nil {
-			log.Println("loc props", el.Model())
-		}
-	}
-	return
-}
-
-func (l *loc) Patch(patch []webdav.Proppatch) ([]webdav.Propstat, error) {
-	log.Println("loc prop patch", patch)
-	return nil, nil
-}

+ 161 - 0
wdfs/locations.go

@@ -0,0 +1,161 @@
+package wdfs
+
+import (
+	"encoding/xml"
+	"github.com/kpmy/mipfs/ipfs_api"
+	"github.com/kpmy/ypk/dom"
+	"github.com/kpmy/ypk/fn"
+	. "github.com/kpmy/ypk/tc"
+	"golang.org/x/net/webdav"
+	"log"
+	"os"
+	"strconv"
+	"strings"
+	"time"
+)
+
+type loc struct {
+	ch    *chain
+	props dom.Element
+}
+
+func (l *loc) Name() string {
+	return l.ch.name
+}
+
+func (l *loc) Size() int64 {
+	return int64(l.ch.UnixLsObject.Size)
+}
+
+func (l *loc) Mode() os.FileMode {
+	if l.ch.Type == "Directory" {
+		return os.ModeDir
+	} else if l.ch.Type == "File" {
+		return 0
+	}
+	panic(100)
+}
+
+func (l *loc) ModTime() (ret time.Time) {
+	ret = time.Now()
+	if !fn.IsNil(l.props) {
+		if ts := l.props.Attr("modified"); ts != "" {
+			if sec, err := strconv.ParseInt(ts, 10, 64); err == nil {
+				ret = time.Unix(sec, 0)
+			}
+		}
+	}
+	return
+}
+
+func (l *loc) IsDir() bool {
+	return true
+}
+
+func (l *loc) Sys() interface{} {
+	Halt(100)
+	return nil
+}
+
+func (l *loc) Close() error {
+	return nil
+}
+
+func (l *loc) Read(p []byte) (n int, err error) {
+	return 0, webdav.ErrForbidden
+}
+
+func (l *loc) Seek(offset int64, whence int) (int64, error) {
+	return 0, webdav.ErrForbidden
+}
+
+func (l *loc) Readdir(count int) (ret []os.FileInfo, err error) {
+	ls, _ := ipfs_api.Shell().FileList(l.ch.Hash)
+	for _, lo := range ls.Links {
+		switch lo.Type {
+		case "File":
+			fallthrough
+		case "Directory":
+			if !strings.HasPrefix(lo.Name, "*") {
+				filepath := l.ch.Hash + "/" + lo.Name
+				tail := newChain(l.ch, filepath).tail()
+				var fi os.FileInfo
+				if tail.IsDir() {
+					_l := &loc{ch: tail}
+					_l.readPropsModel()
+					fi = _l
+				} else {
+					_f := &file{ch: tail}
+					_f.readPropsModel()
+					fi = _f
+				}
+				ret = append(ret, fi)
+			}
+		default:
+			Halt(100)
+		}
+	}
+	return
+}
+
+func (l *loc) Stat() (os.FileInfo, error) {
+	return l, nil
+}
+
+func (l *loc) Write(p []byte) (n int, err error) {
+	return 0, webdav.ErrForbidden
+}
+
+func (l *loc) readPropsModel() {
+	ls, _ := ipfs_api.Shell().FileList(l.ch.Hash)
+	pm := propLinksMap(ls)
+	if p, ok := pm["*"]; ok {
+		rd, _ := ipfs_api.Shell().Cat(p.Hash)
+		if el, err := dom.Decode(rd); err == nil {
+			l.props = el.Model()
+		} else {
+			Halt(99)
+		}
+	} else {
+		l.props = newPropsModel()
+	}
+}
+
+func (l *loc) readPropsObject() (props map[xml.Name]dom.Element, err error) {
+	l.readPropsModel()
+	props = make(map[xml.Name]dom.Element)
+	props = readProps(l.props)
+	return
+}
+
+func (l *loc) writePropsObject(props map[xml.Name]dom.Element) {
+	el := writeProps(props)
+	propHash, _ := ipfs_api.Shell().Add(dom.EncodeWithHeader(el))
+	for tail := l.ch; tail != nil; tail = tail.up {
+		if tail.Hash == l.ch.Hash {
+			tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, "*", propHash, false)
+		} else {
+			tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, tail.down.name, tail.down.Hash, false)
+		}
+	}
+	head := l.ch.head()
+	head.link.update(head.Hash)
+}
+
+func (l *loc) DeadProps() (ret map[xml.Name]webdav.Property, err error) {
+	log.Println("loc props get")
+	pm, _ := l.readPropsObject()
+	ret = props2webdav(pm)
+	log.Println(ret)
+	return
+}
+
+func (l *loc) Patch(patch []webdav.Proppatch) (ret []webdav.Propstat, err error) {
+	log.Println("loc prop patch", patch)
+	pe, _ := l.readPropsObject()
+	ret = propsPatch(pe, patch)
+	log.Println("loc file props", pe)
+	l.writePropsObject(pe)
+	return
+
+}

+ 156 - 0
wdfs/locksys.go

@@ -0,0 +1,156 @@
+package wdfs
+
+import (
+	"fmt"
+	"golang.org/x/net/webdav"
+	"log"
+	"strings"
+	"sync"
+	"time"
+)
+
+type locksystem struct {
+	webdav.LockSystem
+	fs *filesystem
+	sync.RWMutex
+	locks  map[string]string
+	tokens map[string]webdav.LockDetails
+	holds  map[string]bool
+	idx    chan string
+}
+
+func (l *locksystem) Confirm(now time.Time, name0, name1 string, conditions ...webdav.Condition) (release func(), err error) {
+	const noLock = "DAV:no-lock"
+	log.Println("confirm", name0, name1, conditions)
+	l.RLock()
+	tok0, ok0 := l.locks[name0]
+	if _, ok1 := l.locks[name1]; ok1 {
+		panic(101)
+	}
+
+	ok := true
+	etag, _ := l.fs.ETag(name0)
+	for _, c := range conditions {
+		token := c.Token
+		if c.ETag != "" {
+			tok0 = etag
+			token = c.ETag
+		}
+		switch {
+		case !c.Not && ok0:
+			ok = tok0 == token
+		case token == noLock:
+			ok = !ok0 && c.Not
+		default:
+			ok = false
+		}
+		if ok == false {
+			break
+		}
+	}
+	log.Println(ok)
+	if ok && !l.holds[name0] {
+		l.RUnlock()
+		l.Lock()
+		l.holds[name0] = true
+		release = func() {
+			delete(l.holds, name0)
+			log.Println(name0, "release")
+		}
+		l.RWMutex.Unlock()
+	} else {
+		err = webdav.ErrConfirmationFailed
+		l.RUnlock()
+	}
+	return
+}
+
+func (l *locksystem) ParentLocks(name string) (ret []string) {
+	l.RLock()
+	ls := []string{"/"}
+	ls = append(ls, strings.Split(strings.Trim(name, "/"), "/")...)
+	var path = ""
+	for i := 0; i < len(ls); i++ {
+		path = path + ls[i]
+		if tok, ok := l.locks[path]; ok {
+			if details := l.tokens[tok]; !details.ZeroDepth {
+				ret = append(ret, tok)
+			}
+		}
+		if i > 0 {
+			path = path + "/"
+			if tok, ok := l.locks[path]; ok {
+				if details := l.tokens[tok]; !details.ZeroDepth {
+					ret = append(ret, tok)
+				}
+			}
+		}
+	}
+	l.RUnlock()
+	return
+}
+
+func (l *locksystem) Create(now time.Time, details webdav.LockDetails) (token string, err error) {
+	log.Println("lock", details)
+	l.RLock()
+	if _, ok := l.locks[details.Root]; !ok && len(l.ParentLocks(details.Root)) == 0 {
+		l.RUnlock()
+		l.Lock()
+		token = <-l.idx + ":" + fmt.Sprint(now.UnixNano())
+		l.locks[details.Root] = token
+		l.tokens[token] = details
+		log.Println("locked", token)
+		l.RWMutex.Unlock()
+	} else {
+		l.RUnlock()
+		err = webdav.ErrLocked
+	}
+	return
+}
+
+func (l *locksystem) Refresh(now time.Time, token string, duration time.Duration) (ret webdav.LockDetails, err error) {
+	l.Lock()
+	if details, ok := l.tokens[token]; ok {
+		details.Duration = duration
+		l.tokens[token] = details
+		ret = details
+	} else {
+		err = webdav.ErrNoSuchLock
+	}
+	l.RWMutex.Unlock()
+	return
+}
+
+func (l *locksystem) Unlock(now time.Time, token string) (err error) {
+	log.Println("unlock", token)
+	l.Lock()
+	if details, ok := l.tokens[token]; ok {
+		if !l.holds[details.Root] {
+			delete(l.tokens, token)
+			delete(l.locks, details.Root)
+		} else {
+			err = webdav.ErrLocked
+		}
+	} else {
+		err = webdav.ErrNoSuchLock
+	}
+	l.RWMutex.Unlock()
+	return
+}
+
+func NewLS(fs *filesystem) *locksystem {
+	ret := &locksystem{}
+	ret.fs = fs
+	ret.locks = make(map[string]string)
+	ret.tokens = make(map[string]webdav.LockDetails)
+	ret.holds = make(map[string]bool)
+	ret.idx = make(chan string)
+	go func(ch chan string) {
+		i := 0
+		for {
+			ch <- fmt.Sprint(i)
+			i++
+		}
+	}(ret.idx)
+	return ret
+}

+ 0 - 78
wdfs/ls.go

@@ -1,78 +0,0 @@
-package wdfs
-
-import (
-	"fmt"
-	"golang.org/x/net/webdav"
-	"log"
-	"sync"
-	"time"
-)
-
-type locksystem struct {
-	webdav.LockSystem
-	sync.RWMutex
-	locks  map[string]string
-	tokens map[string]webdav.LockDetails
-	idx    chan string
-}
-
-func (l *locksystem) Confirm(now time.Time, name0, name1 string, conditions ...webdav.Condition) (release func(), err error) {
-	log.Println("confirm", name0, name1, conditions)
-	l.RLock()
-	if _, ok := l.locks[name0]; ok {
-		release = func() {
-			log.Println(name0, "release")
-		}
-	} else {
-		err = webdav.ErrConfirmationFailed
-	}
-	l.RUnlock()
-	return
-}
-
-func (l *locksystem) Create(now time.Time, details webdav.LockDetails) (token string, err error) {
-	log.Println("lock", details)
-	l.RLock()
-	if _, ok := l.locks[details.Root]; !ok {
-		l.RUnlock()
-		l.Lock()
-		token = <-l.idx + ":" + fmt.Sprint(now.UnixNano())
-		l.locks[details.Root] = token
-		l.tokens[token] = details
-		log.Println("locked", token)
-		l.RWMutex.Unlock()
-	} else {
-		l.RUnlock()
-		err = webdav.ErrLocked
-	}
-	return
-}
-
-func (l *locksystem) Refresh(now time.Time, token string, duration time.Duration) (webdav.LockDetails, error) {
-	panic(100)
-}
-
-func (l *locksystem) Unlock(now time.Time, token string) (err error) {
-	log.Println("unlock", token)
-	l.Lock()
-	details := l.tokens[token]
-	delete(l.tokens, token)
-	delete(l.locks, details.Root)
-	l.RWMutex.Unlock()
-	return
-}
-
-func NewLS(fs webdav.FileSystem) *locksystem {
-	ret := &locksystem{}
-	ret.locks = make(map[string]string)
-	ret.tokens = make(map[string]webdav.LockDetails)
-	ret.idx = make(chan string)
-	go func(ch chan string) {
-		i := 0
-		for {
-			ch <- fmt.Sprint(i)
-			i++
-		}
-	}(ret.idx)
-	return ret
-}

+ 0 - 51
wdfs/prop.go

@@ -1,51 +0,0 @@
-package wdfs
-
-import (
-	"encoding/xml"
-	"github.com/ipfs/go-ipfs-api"
-	"github.com/kpmy/ypk/dom"
-	. "github.com/kpmy/ypk/tc"
-	"reflect"
-	"strings"
-)
-
-func newProps() (ret dom.Element) {
-	ret = dom.Elem("props")
-	return
-}
-
-func readProps(model dom.Element) (ret map[xml.Name]dom.Element) {
-	ret = make(map[xml.Name]dom.Element)
-	for _, _e := range model.Children() {
-		switch e := _e.(type) {
-		case dom.Element:
-			xn := xml.Name{Local: e.Attr("local"), Space: e.Attr("space")}
-			ret[xn] = e
-		default:
-			Halt(100, reflect.TypeOf(e))
-		}
-	}
-	return
-}
-
-func writeProps(props map[xml.Name]dom.Element) (ret dom.Element) {
-	ret = dom.Elem("props")
-	for _, v := range props {
-		ret.AppendChild(v)
-	}
-	return
-}
-
-func propLinksMap(obj *shell.UnixLsObject) (ret map[string]*shell.UnixLsLink) {
-	ret = make(map[string]*shell.UnixLsLink)
-	for _, lo := range obj.Links {
-		if lo.Type == "File" {
-			if lo.Name == "*" {
-				ret["*"] = lo
-			} else if strings.HasPrefix(lo.Name, "*") {
-				ret[strings.Trim(lo.Name, "*")] = lo
-			}
-		}
-	}
-	return
-}

+ 122 - 0
wdfs/props.go

@@ -0,0 +1,122 @@
+package wdfs
+
+import (
+	"bytes"
+	"encoding/xml"
+	"github.com/ipfs/go-ipfs-api"
+	"github.com/kpmy/ypk/dom"
+	"github.com/kpmy/ypk/fn"
+	. "github.com/kpmy/ypk/tc"
+	"golang.org/x/net/webdav"
+	"io"
+	"reflect"
+	"strings"
+)
+
+func newPropsModel() (ret dom.Element) {
+	ret = dom.Elem("props")
+	return
+}
+
+func readProps(model dom.Element) (ret map[xml.Name]dom.Element) {
+	ret = make(map[xml.Name]dom.Element)
+	for _, _e := range model.Children() {
+		switch e := _e.(type) {
+		case dom.Element:
+			xn := xml.Name{Local: e.Attr("local"), Space: e.Attr("space")}
+			ret[xn] = e
+		default:
+			Halt(100, reflect.TypeOf(e))
+		}
+	}
+	for k := range model.AttrAsMap() {
+		xn := xml.Name{Local: k, Space: "IPFSATTR"}
+		ret[xn] = model
+	}
+	return
+}
+
+func writeProps(props map[xml.Name]dom.Element) (ret dom.Element) {
+	ret = dom.Elem("props")
+	for k, v := range props {
+		if k.Space == "IPFSATTR" {
+			ret.Attr(k.Local, v.Attr(k.Local))
+		} else {
+			ret.AppendChild(v)
+		}
+	}
+	return
+}
+
+func props2webdav(pm map[xml.Name]dom.Element) (ret map[xml.Name]webdav.Property) {
+	ret = make(map[xml.Name]webdav.Property)
+	for k, v := range pm {
+		p := webdav.Property{XMLName: k}
+		buf := new(bytes.Buffer)
+		if k.Space == "IPFSATTR" {
+			buf.WriteString(v.Attr(k.Local))
+		} else {
+			Assert(v.ChildrenCount() == 1, 40)
+			c0 := v.Children()[0]
+			switch c := c0.(type) {
+			case dom.Element:
+				rd := dom.Encode(c)
+				io.Copy(buf, rd)
+			case dom.Text:
+				xml.EscapeText(buf, []byte(c.Data()))
+			default:
+				Halt(100, reflect.TypeOf(c))
+			}
+		}
+		p.InnerXML = buf.Bytes()
+		ret[k] = p
+	}
+	return
+}
+
+func propsPatch(pe map[xml.Name]dom.Element, patch []webdav.Proppatch) (ret []webdav.Propstat) {
+	ret = []webdav.Propstat{}
+	for _, pl := range patch {
+		ps := webdav.Propstat{}
+		for _, p := range pl.Props {
+			if pl.Remove {
+				delete(pe, p.XMLName)
+			} else if p.XMLName.Space == "IPFSATTR" {
+				tmp := dom.Elem("props")
+				tmp.Attr(p.XMLName.Local, string(p.InnerXML))
+				pe[p.XMLName] = tmp
+			} else {
+				el := dom.Elem("prop")
+				el.Attr("local", p.XMLName.Local)
+				el.Attr("space", p.XMLName.Space)
+				e, _ := dom.Decode(bytes.NewBuffer(p.InnerXML))
+				if !fn.IsNil(e.Model()) {
+					el.AppendChild(e.Model())
+				} else if !fn.IsNil(e.Data()) {
+					el.AppendChild(e.Data())
+				} else {
+					Halt(100)
+				}
+				pe[p.XMLName] = el
+			}
+			ps.Props = append(ps.Props, p)
+		}
+		ps.Status = 200
+		ret = append(ret, ps)
+	}
+	return
+}
+
+func propLinksMap(obj *shell.UnixLsObject) (ret map[string]*shell.UnixLsLink) {
+	ret = make(map[string]*shell.UnixLsLink)
+	for _, lo := range obj.Links {
+		if lo.Type == "File" {
+			if lo.Name == "*" {
+				ret["*"] = lo
+			} else if strings.HasPrefix(lo.Name, "*") {
+				ret[strings.Trim(lo.Name, "*")] = lo
+			}
+		}
+	}
+	return
+}