|
@@ -0,0 +1,278 @@
|
|
|
+package projection //import "github.com/kpmy/mipfs/wdfs/projection"
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "github.com/kpmy/mipfs/ipfs_api"
|
|
|
+ "github.com/kpmy/mipfs/wdfs"
|
|
|
+ "github.com/streamrail/concurrent-map"
|
|
|
+ "golang.org/x/net/webdav"
|
|
|
+ "io"
|
|
|
+ "log"
|
|
|
+ "os"
|
|
|
+ "strings"
|
|
|
+ "time"
|
|
|
+)
|
|
|
+
|
|
|
+const Active = true
|
|
|
+
|
|
|
+const Passive = false
|
|
|
+
|
|
|
+const ProjectionRoot = ".fs"
|
|
|
+
|
|
|
+type Extension interface {
|
|
|
+ ConnectTo(root string)
|
|
|
+ Open(chain []string) (webdav.File, error)
|
|
|
+ os.FileInfo
|
|
|
+ webdav.File
|
|
|
+}
|
|
|
+
|
|
|
+type item struct {
|
|
|
+ name string
|
|
|
+}
|
|
|
+
|
|
|
+func (i *item) Size() int64 {
|
|
|
+ return 0
|
|
|
+}
|
|
|
+func (i *item) Mode() os.FileMode {
|
|
|
+ return 0
|
|
|
+}
|
|
|
+func (i *item) ModTime() time.Time {
|
|
|
+ return time.Now()
|
|
|
+}
|
|
|
+func (i *item) IsDir() bool {
|
|
|
+ return false
|
|
|
+}
|
|
|
+func (i *item) Sys() interface{} {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+func (i *item) Close() error {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (i *item) Read(p []byte) (n int, err error) {
|
|
|
+ return 0, io.EOF
|
|
|
+}
|
|
|
+
|
|
|
+func (i *item) Seek(offset int64, whence int) (int64, error) {
|
|
|
+ log.Println("seek")
|
|
|
+ return 0, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (i *item) Write(p []byte) (n int, err error) {
|
|
|
+ return 0, webdav.ErrForbidden
|
|
|
+}
|
|
|
+func (i *item) Readdir(count int) (ret []os.FileInfo, err error) {
|
|
|
+ return nil, webdav.ErrForbidden
|
|
|
+}
|
|
|
+
|
|
|
+func (i *item) Stat() (os.FileInfo, error) {
|
|
|
+ return i, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (i *item) Name() string {
|
|
|
+ return i.name
|
|
|
+}
|
|
|
+
|
|
|
+type locator struct {
|
|
|
+}
|
|
|
+
|
|
|
+func (l *locator) Size() int64 {
|
|
|
+ return 0
|
|
|
+}
|
|
|
+func (l *locator) Mode() os.FileMode {
|
|
|
+ return os.ModeDir
|
|
|
+}
|
|
|
+func (l *locator) ModTime() time.Time {
|
|
|
+ return time.Now()
|
|
|
+}
|
|
|
+func (l *locator) IsDir() bool {
|
|
|
+ return true
|
|
|
+}
|
|
|
+func (l *locator) Sys() interface{} {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+func (l *locator) Close() error {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+func (l *locator) Read(p []byte) (n int, err error) {
|
|
|
+ return 0, webdav.ErrForbidden
|
|
|
+}
|
|
|
+func (l *locator) Seek(offset int64, whence int) (int64, error) {
|
|
|
+ return 0, webdav.ErrForbidden
|
|
|
+}
|
|
|
+func (l *locator) Write(p []byte) (n int, err error) {
|
|
|
+ return 0, webdav.ErrForbidden
|
|
|
+}
|
|
|
+
|
|
|
+type ext struct {
|
|
|
+ locator
|
|
|
+ root string
|
|
|
+ name string
|
|
|
+}
|
|
|
+
|
|
|
+func (e *ext) Name() string {
|
|
|
+ return e.name
|
|
|
+}
|
|
|
+
|
|
|
+func (e *ext) Readdir(count int) (ret []os.FileInfo, err error) {
|
|
|
+ log.Println(e.root)
|
|
|
+ var ch <-chan string
|
|
|
+ if ch, err = ipfs_api.Shell().Refs(e.root, true); err == nil {
|
|
|
+ for s := range ch {
|
|
|
+ ret = append(ret, &item{name: s})
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (e *ext) Stat() (os.FileInfo, error) {
|
|
|
+ return e, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (e *ext) Open(chain []string) (ret webdav.File, err error) {
|
|
|
+ if len(chain) == 1 {
|
|
|
+ i := &item{name: chain[0]}
|
|
|
+ ret = i
|
|
|
+ } else {
|
|
|
+ err = os.ErrPermission
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (e *ext) ConnectTo(root string) {
|
|
|
+ e.root = root
|
|
|
+}
|
|
|
+
|
|
|
+type cat struct {
|
|
|
+ locator
|
|
|
+ pl map[string]Extension
|
|
|
+}
|
|
|
+
|
|
|
+func (c *cat) Name() string {
|
|
|
+ return ProjectionRoot
|
|
|
+}
|
|
|
+
|
|
|
+func (c *cat) Readdir(count int) (ret []os.FileInfo, err error) {
|
|
|
+ for _, v := range c.pl {
|
|
|
+ ret = append(ret, v)
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (c *cat) Stat() (os.FileInfo, error) {
|
|
|
+ return c, nil
|
|
|
+}
|
|
|
+
|
|
|
+type projection struct {
|
|
|
+ inner webdav.FileSystem
|
|
|
+ root string
|
|
|
+ cache cmap.ConcurrentMap
|
|
|
+ all *cat
|
|
|
+
|
|
|
+ set func(string)
|
|
|
+ get func() string
|
|
|
+}
|
|
|
+
|
|
|
+func isProjection(split []string) bool {
|
|
|
+ return strings.ToLower(split[0]) == ProjectionRoot
|
|
|
+}
|
|
|
+
|
|
|
+func (p *projection) Mkdir(name string, perm os.FileMode) (err error) {
|
|
|
+ ls := splitPath(name)
|
|
|
+ switch {
|
|
|
+ case len(ls) > 1 && isProjection(ls):
|
|
|
+ err = os.ErrPermission
|
|
|
+ case len(ls) == 1 && isProjection(ls):
|
|
|
+ err = p.inner.Mkdir(strings.ToLower(name), perm)
|
|
|
+ default:
|
|
|
+ err = p.inner.Mkdir(name, perm)
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (p *projection) OpenFile(name string, flag int, perm os.FileMode) (ret webdav.File, err error) {
|
|
|
+ log.Println("open", name)
|
|
|
+ ls := splitPath(name)
|
|
|
+ switch {
|
|
|
+ case isProjection(ls) && len(ls) == 1:
|
|
|
+ ret = p.all
|
|
|
+ case isProjection(ls) && len(ls) > 1:
|
|
|
+ if e, ok := p.all.pl[ls[1]]; ok {
|
|
|
+ e.ConnectTo(p.root)
|
|
|
+ if len(ls) > 2 {
|
|
|
+ ret, err = e.Open(ls[2:])
|
|
|
+ } else {
|
|
|
+ ret = e
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ err = os.ErrNotExist
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ ret, err = p.inner.OpenFile(name, flag, perm)
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (p *projection) RemoveAll(name string) (err error) {
|
|
|
+ ls := splitPath(name)
|
|
|
+ switch {
|
|
|
+ case isProjection(ls):
|
|
|
+ err = os.ErrPermission
|
|
|
+ default:
|
|
|
+ err = p.inner.RemoveAll(name)
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (p *projection) Rename(oldName, newName string) (err error) {
|
|
|
+ ls := splitPath(oldName)
|
|
|
+ switch {
|
|
|
+ case isProjection(ls):
|
|
|
+ err = os.ErrPermission
|
|
|
+ default:
|
|
|
+ err = p.inner.Rename(oldName, newName)
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (p *projection) Stat(name string) (fi os.FileInfo, err error) {
|
|
|
+ log.Println("stat", name)
|
|
|
+ ls := splitPath(name)
|
|
|
+ switch {
|
|
|
+ case isProjection(ls):
|
|
|
+ fi = &cat{}
|
|
|
+ default:
|
|
|
+ fi, err = p.inner.Stat(name)
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (p *projection) String() string {
|
|
|
+ return fmt.Sprint(p.inner)
|
|
|
+}
|
|
|
+
|
|
|
+func NewPS(get func() string, set func(string), active bool) (fs webdav.FileSystem, ls webdav.LockSystem) {
|
|
|
+ var pr *projection
|
|
|
+ if active {
|
|
|
+ pr = &projection{get: get}
|
|
|
+ pr.set = func(hash string) {
|
|
|
+ pr.root = hash
|
|
|
+ set(hash)
|
|
|
+ }
|
|
|
+ xs := wdfs.NewFS(pr.get, pr.set)
|
|
|
+ ls = wdfs.NewLS(xs)
|
|
|
+ pr.inner = xs
|
|
|
+
|
|
|
+ pr.cache = cmap.New()
|
|
|
+
|
|
|
+ pr.all = &cat{}
|
|
|
+ pr.all.pl = map[string]Extension{"all": &ext{name: "all"}}
|
|
|
+
|
|
|
+ fs = pr
|
|
|
+ } else {
|
|
|
+ xs := wdfs.NewFS(get, set)
|
|
|
+ ls = wdfs.NewLS(xs)
|
|
|
+ fs = xs
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|