Bläddra i källkod

multiuser support

κρμγ 8 år sedan
förälder
incheckning
55ae98db9c
9 ändrade filer med 213 tillägg och 194 borttagningar
  1. 0 1
      .gitignore
  2. 10 10
      Dockerfile
  3. 12 12
      Dockerfile.arm
  4. 0 135
      dav_cmd/main.go
  5. 57 0
      dav_multiuser_cmd/main.go
  6. 102 0
      dav_multiuser_cmd/userfs.go
  7. 0 21
      ipfs_api/ipfs_api_test.go
  8. 2 0
      wdfs/chain.go
  9. 30 15
      wdfs/filesys.go

+ 0 - 1
.gitignore

@@ -27,7 +27,6 @@ _testmain.go
 *.iws
 .memo
 
-dav_cmd/.diskv/root
 .diskv
 dav_test
 

+ 10 - 10
Dockerfile

@@ -1,10 +1,10 @@
-FROM golang:1.7
-RUN mkdir -p /go/src/github.com/kpmy/mipfs
-COPY . /go/src/github.com/kpmy/mipfs
-ENV GOPATH /go
-RUN go get -v github.com/kpmy/mipfs/dav_cmd
-RUN go install github.com/kpmy/mipfs/dav_cmd
-RUN mkdir -p /go/.diskv
-RUN printf "ipfs:5001" > /go/.diskv/ipfs
-EXPOSE 6001
-CMD dav_cmd
+FROM golang:1.7
+RUN mkdir -p /go/src/github.com/kpmy/mipfs
+COPY . /go/src/github.com/kpmy/mipfs
+ENV GOPATH /go
+RUN go get -v github.com/kpmy/mipfs/dav_multiuser_cmd
+RUN go install github.com/kpmy/mipfs/dav_multiuser_cmd
+RUN mkdir -p /go/.diskv
+RUN printf "ipfs:5001" > /go/.diskv/ipfs
+EXPOSE 6001
+CMD dav_multiuser_cmd

+ 12 - 12
Dockerfile.arm

@@ -1,12 +1,12 @@
-FROM armhf/alpine:edge
-RUN apk add --update musl-dev gcc go git mercurial bash wget ca-certificates
-RUN mkdir -p /go/src/github.com/kpmy/mipfs
-COPY . /go/src/github.com/kpmy/mipfs
-ENV GOPATH /go
-RUN go get -v github.com/kpmy/mipfs/dav_cmd
-RUN go install github.com/kpmy/mipfs/dav_cmd
-RUN mkdir -p /go/.diskv
-RUN printf "ipfs:5001" > /go/.diskv/ipfs
-EXPOSE 6001
-WORKDIR /go
-CMD /go/bin/dav_cmd
+FROM armhf/alpine:edge
+RUN apk add --update musl-dev gcc go git mercurial bash wget ca-certificates
+RUN mkdir -p /go/src/github.com/kpmy/mipfs
+COPY . /go/src/github.com/kpmy/mipfs
+ENV GOPATH /go
+RUN go get -v github.com/kpmy/mipfs/dav_multiuser_cmd
+RUN go install github.com/kpmy/mipfs/dav_multiuser_cmd
+RUN mkdir -p /go/.diskv
+RUN printf "ipfs:5001" > /go/.diskv/ipfs
+EXPOSE 6001
+WORKDIR /go
+CMD /go/bin/dav_multiuser_cmd

+ 0 - 135
dav_cmd/main.go

@@ -1,135 +0,0 @@
-package main
-
-import (
-	"log"
-	"net/http"
-	"net/url"
-
-	"fmt"
-	"os"
-
-	"bytes"
-	"github.com/abbot/go-http-auth"
-	"github.com/kpmy/mipfs/ipfs_api"
-	"github.com/kpmy/mipfs/wdfs"
-	"github.com/kpmy/ypk/fn"
-	. "github.com/kpmy/ypk/tc"
-	"github.com/peterbourgon/diskv"
-	"golang.org/x/crypto/bcrypt"
-	"golang.org/x/net/webdav"
-	"io"
-)
-
-var KV *diskv.Diskv
-
-func init() {
-	KV = diskv.New(diskv.Options{
-		BasePath: ".diskv",
-		Transform: func(s string) []string {
-			return []string{}
-		},
-	})
-}
-
-const emptyDir = "QmdniF66q5wYDEyp2PYp6wXwgTUg3ssmb8NYSyfytwyf2j"
-
-func main() {
-	dir, _ := os.Getwd()
-	log.Println("started at", dir)
-
-	defaultRoot := emptyDir
-	if r, err := KV.Read("root"); err == nil && len(r) > 0 {
-		defaultRoot = string(r)
-	} else {
-		KV.Write("root", []byte(defaultRoot))
-	}
-
-	log.Println("root hash", defaultRoot)
-
-	if r, err := KV.Read("ipfs"); err == nil {
-		ipfs_api.Addr = string(r)
-	}
-
-	log.Println("ipfs api at", ipfs_api.Addr)
-
-	rootCh := make(chan string, 16)
-	go func(ch chan string) {
-		for {
-			i := 0
-			for s := range ch {
-				if s != "" {
-					if old, err := KV.Read("root"); err == nil && s != string(old) {
-						history := new(bytes.Buffer)
-						history.Write(old)
-						history.Write([]byte("\n"))
-						if hs, err := KV.Read("root.history"); err == nil {
-							io.CopyN(history, bytes.NewBuffer(hs), int64(history.Len()*128)) //лимит истории
-						}
-						KV.Write("root.history", history.Bytes())
-						i++
-					}
-					KV.Write("root", []byte(s))
-				} else {
-					Halt(100, "empty root")
-				}
-			}
-		}
-	}(rootCh)
-
-	var fs interface{}
-	var ls interface{}
-	if nodeID, err := ipfs_api.Shell().ID(); err == nil {
-		_fs := wdfs.NewFS(nodeID, defaultRoot)
-		fs = _fs
-		ls = wdfs.NewLS(_fs)
-	} else {
-		log.Fatal(err)
-	}
-	Assert(!fn.IsNil(fs), 40)
-
-	dav := &webdav.Handler{
-		Prefix:     "/ipfs",
-		FileSystem: fs.(webdav.FileSystem),
-		LockSystem: ls.(webdav.LockSystem),
-		Logger: func(r *http.Request, err error) {
-			switch r.Method {
-			case "COPY", "MOVE":
-				dst := ""
-				if u, err := url.Parse(r.Header.Get("Destination")); err == nil {
-					dst = u.Path
-				}
-				o := r.Header.Get("Overwrite")
-				log.Println(r.Method, r.URL.Path, dst, o, err)
-			default:
-				log.Println(r.Method, r.URL.Path, err)
-			}
-			//log.Println(fs)
-			rootCh <- fmt.Sprint(fs)
-		},
-	}
-
-	davAuth := auth.NewBasicAuthenticator("ipfs", func(user, realm string) (ret string) {
-		switch user {
-		case "root":
-			hash, _ := bcrypt.GenerateFromPassword([]byte("root"), bcrypt.DefaultCost)
-			ret = string(hash)
-		}
-		return
-	}).Wrap(func(resp http.ResponseWriter, req *auth.AuthenticatedRequest) {
-		dav.ServeHTTP(resp, &req.Request)
-	})
-
-	http.Handle("/ipfs/", davAuth)
-	http.Handle("/ipfs", davAuth)
-
-	http.HandleFunc("/hash", func(resp http.ResponseWriter, req *http.Request) {
-		if rh, err := KV.Read("root"); err == nil {
-			rootHash := string(rh)
-			tpl := "<html><body><a href='http://o.ocsf.in:8080/ipfs/" + rootHash + "'>" + rootHash + "</a></body></html>"
-			resp.Write([]byte(tpl))
-		}
-	})
-	const addr = "0.0.0.0:6001"
-	log.Println("webdav server started at", addr)
-	http.ListenAndServe(addr, nil)
-}

+ 57 - 0
dav_multiuser_cmd/main.go

@@ -0,0 +1,57 @@
+package main
+
+import (
+	"github.com/abbot/go-http-auth"
+	"github.com/kpmy/mipfs/ipfs_api"
+	"github.com/kpmy/mipfs/wdfs"
+	. "github.com/kpmy/ypk/tc"
+	"github.com/peterbourgon/diskv"
+	"github.com/tv42/zbase32"
+	"log"
+	"net/http"
+	"os"
+)
+
+var KV *diskv.Diskv
+
+func init() {
+	KV = diskv.New(diskv.Options{
+		BasePath: ".diskv",
+		Transform: func(s string) []string {
+			return []string{}
+		},
+	})
+}
+
+func main() {
+	dir, _ := os.Getwd()
+	log.Println("started at", dir)
+
+	if r, err := KV.Read("ipfs"); err == nil {
+		ipfs_api.Addr = string(r)
+	}
+
+	log.Println("ipfs api at", ipfs_api.Addr)
+	Assert(ipfs_api.Shell().Pin(wdfs.EmptyDirHash) == nil && ipfs_api.Shell().Pin(wdfs.EmptyFileHash) == nil, 40)
+
+	dav := handler()
+	http.Handle("/ipfs/", dav)
+	http.Handle("/ipfs", dav)
+	http.HandleFunc("/hash", auth.NewBasicAuthenticator("ipfs", func(user, realm string) (ret string) {
+		un := zbase32.EncodeToString([]byte(user))
+		if hash, err := KV.Read(un); err == nil {
+			ret = string(hash)
+		}
+		return
+	}).Wrap(func(resp http.ResponseWriter, req *auth.AuthenticatedRequest) {
+		un := zbase32.EncodeToString([]byte(req.Username))
+		if rh, err := KV.Read(un + ".root"); err == nil {
+			rootHash := string(rh)
+			tpl := "<html><body><a href='https://ipfs.io/ipfs/" + rootHash + "' target='_blank'>/ipfs/" + rootHash + "</a></body></html>"
+			resp.Write([]byte(tpl))
+		}
+	}))
+	const addr = "0.0.0.0:6001"
+	log.Println("webdav server started at", addr)
+	log.Println(http.ListenAndServe(addr, nil))
+}

+ 102 - 0
dav_multiuser_cmd/userfs.go

@@ -0,0 +1,102 @@
+package main
+
+import (
+	"bytes"
+	"github.com/abbot/go-http-auth"
+	"github.com/kpmy/mipfs/wdfs"
+	. "github.com/kpmy/ypk/tc"
+	"github.com/streamrail/concurrent-map"
+	"github.com/tv42/zbase32"
+	"golang.org/x/net/webdav"
+	"io"
+	"log"
+	"net/http"
+	"net/url"
+)
+
+var um cmap.ConcurrentMap = cmap.New()
+
+type FileLockSys struct {
+	fs  webdav.FileSystem
+	ls  webdav.LockSystem
+	dav *webdav.Handler
+}
+
+func handler() http.Handler {
+	return auth.NewBasicAuthenticator("ipfs", func(user, realm string) (ret string) {
+		un := zbase32.EncodeToString([]byte(user))
+		if hash, err := KV.Read(un); err == nil {
+			ret = string(hash)
+		}
+		return
+	}).Wrap(func(resp http.ResponseWriter, req *auth.AuthenticatedRequest) {
+		if !um.Has(req.Username) {
+			fl := new(FileLockSys)
+
+			rootCh := make(chan string, 256)
+			go func(ch chan string, user string) {
+				user = zbase32.EncodeToString([]byte(user))
+				for {
+					i := 0
+					for s := range ch {
+						if s != "" {
+							if old, err := KV.Read(user + ".root"); err == nil && s != string(old) {
+								history := new(bytes.Buffer)
+								history.Write(old)
+								history.Write([]byte("\n"))
+								if hs, err := KV.Read(user + ".root.history"); err == nil {
+									io.CopyN(history, bytes.NewBuffer(hs), int64(history.Len()*128)) //лимит истории
+								}
+								KV.Write(user+".root.history", history.Bytes())
+								i++
+							}
+							KV.Write(user+".root", []byte(s))
+						} else {
+							Halt(100, "empty root")
+						}
+					}
+				}
+			}(rootCh, req.Username)
+
+			fs := wdfs.NewFS(func() string {
+				user := zbase32.EncodeToString([]byte(req.Username))
+				defaultRoot := wdfs.EmptyDirHash
+				if r, err := KV.Read(user + ".root"); err == nil && len(r) > 0 {
+					defaultRoot = string(r)
+				} else {
+					KV.Write(user+".root", []byte(defaultRoot))
+				}
+				return defaultRoot
+			}, func(hash string) {
+				rootCh <- hash
+			})
+			fl.fs = fs
+			fl.ls = wdfs.NewLS(fs)
+			fl.dav = &webdav.Handler{
+				Prefix:     "/ipfs",
+				FileSystem: fl.fs,
+				LockSystem: fl.ls,
+				Logger: func(r *http.Request, err error) {
+					switch r.Method {
+					case "COPY", "MOVE":
+						dst := ""
+						if u, err := url.Parse(r.Header.Get("Destination")); err == nil {
+							dst = u.Path
+						}
+						o := r.Header.Get("Overwrite")
+						log.Println(r.Method, r.URL.Path, dst, o, fl.fs, err)
+					default:
+						log.Println(r.Method, r.URL.Path, fl.fs, err)
+					}
+				},
+			}
+
+			um.Set(req.Username, fl)
+		}
+		if fl, ok := um.Get(req.Username); ok {
+			fl.(*FileLockSys).dav.ServeHTTP(resp, &req.Request)
+		} else {
+			Halt(100, "strange user", req.Username)
+		}
+	})
+}

+ 0 - 21
ipfs_api/ipfs_api_test.go

@@ -1,21 +0,0 @@
-package ipfs_api
-
-import (
-	"testing"
-
-	"net/http"
-
-	go_ipfs_api "github.com/ipfs/go-ipfs-api"
-)
-
-func TestShell(t *testing.T) {
-	sh := go_ipfs_api.NewShellWithClient("127.0.0.1:5001", http.DefaultClient)
-	id, _ := sh.ID()
-	t.Log(id)
-	root, _ := sh.Resolve("/ipns/" + id.ID)
-	ls, _ := sh.FileList(root)
-	t.Log(ls)
-	for _, x := range ls.Links {
-		t.Log(x)
-	}
-}

+ 2 - 0
wdfs/chain.go

@@ -12,6 +12,7 @@ type chain struct {
 	up, down, link *chain
 	shell.UnixLsObject
 	name string
+	upd  func(string)
 }
 
 func newChain(root *chain, filepath string) (ret *chain) {
@@ -81,6 +82,7 @@ func (root *chain) mirror() (ret *chain) {
 func (root *chain) update(hash string) {
 	Assert(hash != "", 20)
 	root.Hash = hash
+	root.upd(hash)
 }
 
 func (c *chain) Mode() os.FileMode {

+ 30 - 15
wdfs/filesys.go

@@ -2,7 +2,6 @@ package wdfs
 
 import (
 	"fmt"
-	"github.com/ipfs/go-ipfs-api"
 	"github.com/kpmy/mipfs/ipfs_api"
 	"github.com/kpmy/ypk/dom"
 	"github.com/kpmy/ypk/fn"
@@ -13,14 +12,26 @@ import (
 	"time"
 )
 
+const EmptyDirHash = "QmdniF66q5wYDEyp2PYp6wXwgTUg3ssmb8NYSyfytwyf2j"
+const EmptyFileHash = "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH"
+
 type filesystem struct {
 	webdav.FileSystem
-	nodeId *shell.IdOutput
-	root   *chain
+	rootLevel *chain
+	get       func() string
+	set       func(string)
+}
+
+func (f *filesystem) root() *chain {
+	f.rootLevel.Hash = f.get()
+	f.rootLevel.name = f.rootLevel.Hash
+	f.rootLevel.upd = f.set
+	return f.rootLevel
 }
 
 func (f *filesystem) Mkdir(name string, perm os.FileMode) (err error) {
-	chain := newChain(f.root.mirror(), f.root.Hash+"/"+strings.Trim(name, "/"))
+	root := f.root()
+	chain := newChain(root.mirror(), root.Hash+"/"+strings.Trim(name, "/"))
 	if tail := chain.tail(); !tail.exists() {
 		onlyOne := true
 		for tail != nil {
@@ -54,7 +65,8 @@ func (f *filesystem) Mkdir(name string, perm os.FileMode) (err error) {
 
 func (f *filesystem) OpenFile(name string, flag int, perm os.FileMode) (ret webdav.File, err error) {
 	//log.Println("open", name, flag, perm)
-	path := newChain(f.root.mirror(), f.root.Hash+"/"+strings.Trim(name, "/"))
+	root := f.root()
+	path := newChain(root.mirror(), root.Hash+"/"+strings.Trim(name, "/"))
 	switch tail := path.tail(); {
 	case tail.exists() && tail.IsDir():
 		_l := &loc{ch: tail}
@@ -81,7 +93,8 @@ func (f *filesystem) OpenFile(name string, flag int, perm os.FileMode) (ret webd
 }
 
 func (f *filesystem) RemoveAll(name string) (err error) {
-	chain := newChain(f.root.mirror(), f.root.Hash+"/"+strings.Trim(name, "/"))
+	root := f.root()
+	chain := newChain(root.mirror(), root.Hash+"/"+strings.Trim(name, "/"))
 	if tail := chain.tail(); tail.exists() {
 		tail = tail.up
 		tail.Hash, _ = ipfs_api.Shell().Patch(tail.Hash, "rm-link", tail.down.name)
@@ -103,7 +116,8 @@ func (f *filesystem) RemoveAll(name string) (err error) {
 
 func (f *filesystem) Rename(oldName, newName string) (err error) {
 	//log.Println("rename", oldName, newName)
-	on := newChain(f.root.mirror(), f.root.Hash+"/"+strings.Trim(oldName, "/"))
+	root := f.root()
+	on := newChain(root.mirror(), root.Hash+"/"+strings.Trim(oldName, "/"))
 	var op *chain
 	if !on.tail().IsDir() {
 		propPath := ""
@@ -114,9 +128,9 @@ func (f *filesystem) Rename(oldName, newName string) (err error) {
 				propPath = propPath + "/" + x.name
 			}
 		}
-		op = newChain(f.root.mirror(), propPath)
+		op = newChain(root.mirror(), propPath)
 	}
-	nn := newChain(f.root.mirror(), f.root.Hash+"/"+strings.Trim(newName, "/"))
+	nn := newChain(root.mirror(), root.Hash+"/"+strings.Trim(newName, "/"))
 	if ot := on.tail(); ot.exists() {
 		if nt := nn.tail(); !nt.exists() {
 			Assert(ot.depth() == nt.depth(), 40)
@@ -146,7 +160,8 @@ func (f *filesystem) Rename(oldName, newName string) (err error) {
 
 func (f *filesystem) Stat(name string) (fi os.FileInfo, err error) {
 	//log.Println("stat", name)
-	chain := newChain(f.root.mirror(), f.root.Hash+"/"+strings.Trim(name, "/"))
+	root := f.root()
+	chain := newChain(root.mirror(), root.Hash+"/"+strings.Trim(name, "/"))
 	tail := chain.tail()
 	if !tail.exists() {
 		err = os.ErrNotExist
@@ -163,7 +178,7 @@ func (f *filesystem) Stat(name string) (fi os.FileInfo, err error) {
 }
 
 func (f *filesystem) String() string {
-	return f.root.Hash
+	return f.rootLevel.Hash
 }
 
 func (f *filesystem) ETag(name string) (ret string, err error) {
@@ -174,10 +189,10 @@ func (f *filesystem) ETag(name string) (ret string, err error) {
 	return
 }
 
-func NewFS(id *shell.IdOutput, root string) *filesystem {
+func NewFS(get func() string, set func(string)) *filesystem {
 	ch := &chain{}
-	ch.Hash = root
-	ch.name = root
+	ch.Hash = get()
+	ch.name = ch.Hash
 	ch.Type = "Directory"
-	return &filesystem{nodeId: id, root: ch}
+	return &filesystem{get: get, set: set, rootLevel: ch}
 }