filesys.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. package dav_ipfs
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/kpmy/mipfs/ipfs_api"
  6. "github.com/kpmy/ypk/dom"
  7. "github.com/kpmy/ypk/fn"
  8. . "github.com/kpmy/ypk/tc"
  9. "golang.org/x/net/webdav"
  10. "os"
  11. "strings"
  12. "time"
  13. )
  14. const EmptyDirHash = "QmdniF66q5wYDEyp2PYp6wXwgTUg3ssmb8NYSyfytwyf2j"
  15. const EmptyFileHash = "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH"
  16. type filesystem struct {
  17. webdav.FileSystem
  18. rootLevel *chain
  19. get func() string
  20. set func(string)
  21. }
  22. func (f *filesystem) root() *chain {
  23. f.rootLevel.Hash = f.get()
  24. f.rootLevel.name = f.rootLevel.Hash
  25. f.rootLevel.upd = f.set
  26. return f.rootLevel
  27. }
  28. func (f *filesystem) Mkdir(ctx context.Context, name string, perm os.FileMode) (err error) {
  29. root := f.root()
  30. chain := newChain(root.mirror(), root.Hash+"/"+strings.Trim(name, "/"))
  31. if tail := chain.tail(); !tail.exists() {
  32. onlyOne := true
  33. for tail != nil {
  34. if !tail.exists() {
  35. if onlyOne {
  36. tail.Hash, _ = ipfs_api.Shell().NewObject("unixfs-dir")
  37. prop := newPropsModel()
  38. prop.Attr("modified", fmt.Sprint(time.Now().Unix()))
  39. propHash, _ := ipfs_api.Shell().Add(dom.EncodeWithHeader(prop))
  40. if tail.Hash, err = ipfs_api.Shell().PatchLink(tail.Hash, "*", propHash, false); err != nil {
  41. Halt(100, err)
  42. return
  43. }
  44. onlyOne = false
  45. } else {
  46. err = os.ErrNotExist
  47. return
  48. }
  49. }
  50. if tail.down != nil {
  51. tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, tail.down.name, tail.down.Hash, false)
  52. }
  53. tail = tail.up
  54. }
  55. chain.link.update(chain.Hash)
  56. } else {
  57. err = os.ErrExist
  58. }
  59. return
  60. }
  61. func (f *filesystem) OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (ret webdav.File, err error) {
  62. //log.Println("open", name, flag, perm)
  63. root := f.root()
  64. path := newChain(root.mirror(), root.Hash+"/"+strings.Trim(name, "/"))
  65. switch tail := path.tail(); {
  66. case tail.exists() && tail.IsDir():
  67. _l := &loc{ch: tail}
  68. _l.readPropsModel()
  69. ret = _l
  70. case tail.exists() && !tail.IsDir():
  71. _f := &file{ch: tail}
  72. _f.readPropsModel()
  73. ret = _f
  74. case !tail.exists() && flag&os.O_CREATE != 0:
  75. var edir *chain
  76. for edir = tail.up; edir != nil && edir.exists() && edir.IsDir(); edir = edir.up {
  77. }
  78. if fn.IsNil(edir) {
  79. ret = &file{ch: tail}
  80. } else {
  81. err = os.ErrNotExist
  82. }
  83. default:
  84. //log.Println("open error", name, flag, perm)
  85. err = os.ErrNotExist
  86. }
  87. return
  88. }
  89. func (f *filesystem) RemoveAll(ctx context.Context, name string) (err error) {
  90. root := f.root()
  91. chain := newChain(root.mirror(), root.Hash+"/"+strings.Trim(name, "/"))
  92. if tail := chain.tail(); tail.exists() {
  93. tail = tail.up
  94. tail.Hash, _ = ipfs_api.Shell().Patch(tail.Hash, "rm-link", tail.down.name)
  95. if !tail.down.IsDir() {
  96. //удалим пропы
  97. if th, err := ipfs_api.Shell().Patch(tail.Hash, "rm-link", "*"+tail.down.name); err == nil {
  98. tail.Hash = th
  99. }
  100. }
  101. tail = tail.up
  102. for tail != nil {
  103. tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, tail.down.name, tail.down.Hash, false)
  104. tail = tail.up
  105. }
  106. chain.link.update(chain.Hash)
  107. }
  108. return
  109. }
  110. func (f *filesystem) Rename(ctx context.Context, oldName, newName string) (err error) {
  111. //log.Println("rename", oldName, newName)
  112. root := f.root()
  113. on := newChain(root.mirror(), root.Hash+"/"+strings.Trim(oldName, "/"))
  114. var op *chain
  115. if !on.tail().IsDir() {
  116. propPath := ""
  117. for x := on; x != nil; x = x.down {
  118. if x.down == nil {
  119. propPath = propPath + "/" + "*" + x.name
  120. } else {
  121. propPath = propPath + "/" + x.name
  122. }
  123. }
  124. op = newChain(root.mirror(), propPath)
  125. }
  126. nn := newChain(root.mirror(), root.Hash+"/"+strings.Trim(newName, "/"))
  127. if ot := on.tail(); ot.exists() {
  128. if nt := nn.tail(); !nt.exists() {
  129. Assert(ot.depth() == nt.depth(), 40)
  130. tail := ot.up
  131. tail.Hash, _ = ipfs_api.Shell().Patch(tail.Hash, "rm-link", ot.name)
  132. if op != nil {
  133. tail.Hash, _ = ipfs_api.Shell().Patch(tail.Hash, "rm-link", op.tail().name)
  134. }
  135. tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, nt.name, ot.Hash, false)
  136. if op != nil {
  137. tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, "*"+nt.name, op.tail().Hash, false)
  138. }
  139. tail = tail.up
  140. for tail != nil {
  141. tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, tail.down.name, tail.down.Hash, false)
  142. tail = tail.up
  143. }
  144. on.link.update(on.Hash)
  145. } else {
  146. err = os.ErrExist
  147. }
  148. } else {
  149. err = os.ErrNotExist
  150. }
  151. return
  152. }
  153. func (f *filesystem) Stat(ctx context.Context, name string) (fi os.FileInfo, err error) {
  154. //log.Println("stat", name)
  155. root := f.root()
  156. chain := newChain(root.mirror(), root.Hash+"/"+strings.Trim(name, "/"))
  157. tail := chain.tail()
  158. if !tail.exists() {
  159. err = os.ErrNotExist
  160. } else if tail.IsDir() {
  161. _l := &loc{ch: tail}
  162. _l.readPropsModel()
  163. fi = _l
  164. } else {
  165. _f := &file{ch: tail}
  166. _f.readPropsModel()
  167. fi = _f
  168. }
  169. return
  170. }
  171. func (f *filesystem) String() string {
  172. return f.rootLevel.Hash
  173. }
  174. func (f *filesystem) ETag(name string) (ret string, err error) {
  175. var fi os.FileInfo
  176. ctx := context.Background()
  177. if fi, err = f.Stat(ctx, name); err == nil {
  178. ret = fmt.Sprintf(`"%x%x"`, fi.ModTime().UnixNano(), fi.Size())
  179. }
  180. return
  181. }
  182. func NewFS(get func() string, set func(string)) *filesystem {
  183. ch := &chain{}
  184. ch.Hash = get()
  185. ch.name = ch.Hash
  186. ch.Type = "Directory"
  187. return &filesystem{get: get, set: set, rootLevel: ch}
  188. }