123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- package dav_ipfs
- import (
- "context"
- "fmt"
- "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"
- "os"
- "strings"
- "time"
- )
- const EmptyDirHash = "QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn"
- const EmptyFileHash = "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH"
- type filesystem struct {
- webdav.FileSystem
- 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(ctx context.Context, name string, perm os.FileMode) (err error) {
- root := f.root()
- chain := newChain(root.mirror(), root.Hash+"/"+strings.Trim(name, "/"))
- if tail := chain.tail(); !tail.exists() {
- onlyOne := true
- for tail != nil {
- if !tail.exists() {
- if onlyOne {
- tail.Hash, _ = ipfs_api.Shell().NewObject("unixfs-dir")
- 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 {
- Halt(100, err)
- return
- }
- onlyOne = false
- } else {
- err = os.ErrNotExist
- return
- }
- }
- if tail.down != nil {
- tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, tail.down.name, tail.down.Hash, false)
- }
- tail = tail.up
- }
- chain.link.update(chain.Hash)
- } else {
- err = os.ErrExist
- }
- return
- }
- func (f *filesystem) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (ret webdav.File, err error) {
- //log.Println("open", name, flag, perm)
- 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}
- _l.readPropsModel()
- ret = _l
- case tail.exists() && !tail.IsDir():
- _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 {
- }
- if fn.IsNil(edir) {
- ret = &file{ch: tail}
- } else {
- err = os.ErrNotExist
- }
- default:
- //log.Println("open error", name, flag, perm)
- err = os.ErrNotExist
- }
- return
- }
- func (f *filesystem) RemoveAll(ctx context.Context, name string) (err error) {
- 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)
- if !tail.down.IsDir() {
- //удалим пропы
- if th, err := ipfs_api.Shell().Patch(tail.Hash, "rm-link", "*"+tail.down.name); err == nil {
- tail.Hash = th
- }
- }
- tail = tail.up
- for tail != nil {
- tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, tail.down.name, tail.down.Hash, false)
- tail = tail.up
- }
- chain.link.update(chain.Hash)
- }
- return
- }
- func (f *filesystem) Rename(ctx context.Context, oldName, newName string) (err error) {
- //log.Println("rename", oldName, newName)
- root := f.root()
- on := newChain(root.mirror(), root.Hash+"/"+strings.Trim(oldName, "/"))
- var op *chain
- if !on.tail().IsDir() {
- propPath := ""
- for x := on; x != nil; x = x.down {
- if x.down == nil {
- propPath = propPath + "/" + "*" + x.name
- } else {
- propPath = propPath + "/" + x.name
- }
- }
- op = newChain(root.mirror(), propPath)
- }
- 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)
- tail := ot.up
- tail.Hash, _ = ipfs_api.Shell().Patch(tail.Hash, "rm-link", ot.name)
- if op != nil {
- tail.Hash, _ = ipfs_api.Shell().Patch(tail.Hash, "rm-link", op.tail().name)
- }
- tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, nt.name, ot.Hash, false)
- if op != nil {
- tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, "*"+nt.name, op.tail().Hash, false)
- }
- tail = tail.up
- for tail != nil {
- tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, tail.down.name, tail.down.Hash, false)
- tail = tail.up
- }
- on.link.update(on.Hash)
- } else {
- err = os.ErrExist
- }
- } else {
- err = os.ErrNotExist
- }
- return
- }
- func (f *filesystem) Stat(ctx context.Context, name string) (fi os.FileInfo, err error) {
- //log.Println("stat", name)
- root := f.root()
- chain := newChain(root.mirror(), root.Hash+"/"+strings.Trim(name, "/"))
- tail := chain.tail()
- 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
- }
- func (f *filesystem) String() string {
- return f.rootLevel.Hash
- }
- func (f *filesystem) ETag(name string) (ret string, err error) {
- var fi os.FileInfo
- ctx := context.Background()
- if fi, err = f.Stat(ctx, name); err == nil {
- ret = fmt.Sprintf(`"%x%x"`, fi.ModTime().UnixNano(), fi.Size())
- }
- return
- }
- func NewFS(get func() string, set func(string)) *filesystem {
- ch := &chain{}
- ch.Hash = get()
- ch.name = ch.Hash
- ch.Type = "Directory"
- return &filesystem{get: get, set: set, rootLevel: ch}
- }
|