files.go 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. package dav_ipfs
  2. import (
  3. "encoding/xml"
  4. "github.com/ipfs/go-ipfs-api"
  5. "github.com/kpmy/mipfs/ipfs_api"
  6. "github.com/kpmy/ypk/fn"
  7. "github.com/mattetti/filebuffer"
  8. "io/ioutil"
  9. "os"
  10. "sync"
  11. "time"
  12. "fmt"
  13. "github.com/kpmy/ypk/dom"
  14. . "github.com/kpmy/ypk/tc"
  15. "golang.org/x/net/webdav"
  16. "io"
  17. "strconv"
  18. "strings"
  19. )
  20. type block struct {
  21. pos int64
  22. data *filebuffer.Buffer
  23. }
  24. type file struct {
  25. ch *chain
  26. pos int64
  27. links []*shell.LsLink
  28. buf *filebuffer.Buffer
  29. wr chan *block
  30. wg *sync.WaitGroup
  31. props dom.Element
  32. }
  33. func (f *file) Name() string {
  34. return f.ch.name
  35. }
  36. func (f *file) Size() int64 {
  37. return int64(f.ch.UnixLsObject.Size)
  38. }
  39. func (f *file) Mode() os.FileMode {
  40. return 0
  41. }
  42. func (f *file) ModTime() (ret time.Time) {
  43. ret = time.Now()
  44. if !fn.IsNil(f.props) {
  45. if ts := f.props.Attr("modified"); ts != "" {
  46. if sec, err := strconv.ParseInt(ts, 10, 64); err == nil {
  47. ret = time.Unix(sec, 0)
  48. }
  49. }
  50. }
  51. return
  52. }
  53. func (f *file) IsDir() bool {
  54. return false
  55. }
  56. func (f *file) Sys() interface{} {
  57. Halt(100)
  58. return nil
  59. }
  60. func (f *file) Read(p []byte) (n int, err error) {
  61. if f.links == nil {
  62. f.links, _ = ipfs_api.Shell().List(f.ch.Hash)
  63. }
  64. if len(f.links) == 0 {
  65. if fn.IsNil(f.buf) {
  66. f.buf = filebuffer.New(nil)
  67. rd, _ := ipfs_api.Shell().Cat(f.ch.Hash)
  68. io.Copy(f.buf, rd)
  69. }
  70. f.buf.Seek(f.pos, io.SeekStart)
  71. n, err = f.buf.Read(p)
  72. f.pos = f.pos + int64(n)
  73. return n, err
  74. } else {
  75. var end int64 = 0
  76. for _, l := range f.links {
  77. begin := end
  78. end = begin + int64(l.Size)
  79. if begin <= f.pos && f.pos < end {
  80. if f.buf == nil {
  81. rd, _ := ipfs_api.Shell().Cat(l.Hash)
  82. f.buf = filebuffer.New(nil)
  83. io.Copy(f.buf, rd)
  84. l.Size = uint64(f.buf.Buff.Len())
  85. }
  86. f.buf.Seek(f.pos-begin, io.SeekStart)
  87. n, err = f.buf.Read(p)
  88. f.pos = f.pos + int64(n)
  89. if f.buf.Index == int64(l.Size) {
  90. f.buf = nil
  91. }
  92. return
  93. }
  94. }
  95. panic(100)
  96. }
  97. }
  98. func (f *file) Seek(offset int64, whence int) (seek int64, err error) {
  99. switch whence {
  100. case io.SeekStart:
  101. f.pos = offset
  102. case io.SeekCurrent:
  103. f.pos = f.pos + offset
  104. case io.SeekEnd:
  105. f.pos = f.Size() + offset
  106. default:
  107. Halt(100)
  108. }
  109. Assert(f.pos >= 0, 60)
  110. seek = f.pos
  111. return
  112. }
  113. func (f *file) Readdir(count int) (ret []os.FileInfo, err error) {
  114. return nil, webdav.ErrForbidden
  115. }
  116. func (f *file) Stat() (os.FileInfo, error) {
  117. return f, nil
  118. }
  119. const emptyFileHash = "QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH"
  120. func (f *file) Close() error {
  121. if f.wr != nil {
  122. close(f.wr)
  123. f.wg.Wait()
  124. } else if !f.ch.exists() {
  125. f.update(nil)
  126. }
  127. return nil
  128. }
  129. func (f *file) update(data io.ReadCloser) {
  130. if !fn.IsNil(data) {
  131. f.ch.Hash, _ = ipfs_api.Shell().Add(data)
  132. } else {
  133. f.ch.Hash = emptyFileHash
  134. }
  135. for tail := f.ch.up; tail != nil; tail = tail.up {
  136. tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, tail.down.name, tail.down.Hash, false)
  137. if tail.down.Hash == f.ch.Hash {
  138. //создадим пропы
  139. f.props = newPropsModel()
  140. f.props.Attr("modified", fmt.Sprint(time.Now().Unix()))
  141. propHash, _ := ipfs_api.Shell().Add(dom.EncodeWithHeader(f.props))
  142. tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, "*"+f.ch.name, propHash, false)
  143. }
  144. }
  145. head := f.ch.head()
  146. head.link.update(head.Hash)
  147. }
  148. const BufferLimit = 1024 * 128
  149. type ioFile interface {
  150. io.Seeker
  151. io.ReadCloser
  152. io.Writer
  153. }
  154. func (f *file) Write(p []byte) (n int, err error) {
  155. if f.wr == nil {
  156. f.wr = make(chan *block, 16)
  157. f.wg = new(sync.WaitGroup)
  158. f.wg.Add(1)
  159. go func(f *file) {
  160. var tmp ioFile
  161. buf := filebuffer.New(nil)
  162. tmp = buf
  163. for w := range f.wr {
  164. tmp.Seek(w.pos, io.SeekStart)
  165. w.data.Seek(0, io.SeekStart)
  166. io.Copy(tmp, w.data)
  167. if !fn.IsNil(buf) && buf.Buff.Len() > BufferLimit {
  168. tf, _ := ioutil.TempFile(os.TempDir(), "mipfs")
  169. buf.Seek(0, io.SeekStart)
  170. io.Copy(tf, buf)
  171. tmp = tf
  172. buf = nil
  173. }
  174. }
  175. tmp.Seek(0, io.SeekStart)
  176. f.update(tmp)
  177. f.wg.Done()
  178. }(f)
  179. }
  180. b := &block{pos: f.pos}
  181. b.data = filebuffer.New(nil)
  182. n, err = b.data.Write(p)
  183. f.wr <- b
  184. f.pos = f.pos + int64(n)
  185. return n, nil
  186. }
  187. func (f *file) readPropsModel() {
  188. if !strings.HasPrefix(f.ch.name, "*") {
  189. ls, _ := ipfs_api.Shell().FileList(f.ch.up.Hash)
  190. pm := propLinksMap(ls)
  191. if p, ok := pm[f.ch.name]; ok {
  192. rd, _ := ipfs_api.Shell().CacheCat(p.Hash)
  193. if el, err := dom.Decode(rd); err == nil {
  194. f.props = el.Model()
  195. } else {
  196. Halt(99, f.ch.name, f.ch.Hash)
  197. }
  198. } else {
  199. f.props = newPropsModel()
  200. }
  201. }
  202. }
  203. func (f *file) readPropsObject() (props map[xml.Name]dom.Element, err error) {
  204. props = make(map[xml.Name]dom.Element)
  205. f.readPropsModel()
  206. props = readProps(f.props)
  207. return
  208. }
  209. func (f *file) writePropsObject(props map[xml.Name]dom.Element) {
  210. if !strings.HasPrefix(f.ch.name, "*") {
  211. el := writeProps(props)
  212. propHash, _ := ipfs_api.Shell().Add(dom.EncodeWithHeader(el))
  213. for tail := f.ch.up; tail != nil; tail = tail.up {
  214. if tail.down.Hash == f.ch.Hash {
  215. tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, "*"+f.ch.name, propHash, false)
  216. } else {
  217. tail.Hash, _ = ipfs_api.Shell().PatchLink(tail.Hash, tail.down.name, tail.down.Hash, false)
  218. }
  219. }
  220. head := f.ch.head()
  221. head.link.update(head.Hash)
  222. }
  223. }
  224. func (f *file) DeadProps() (ret map[xml.Name]webdav.Property, err error) {
  225. //log.Println("file prop get")
  226. pm, _ := f.readPropsObject()
  227. ret = props2webdav(pm)
  228. //log.Println("read file props", ret)
  229. return
  230. }
  231. func (f *file) Patch(patch []webdav.Proppatch) (ret []webdav.Propstat, err error) {
  232. //log.Println("file prop patch", patch)
  233. pe, _ := f.readPropsObject()
  234. ret = propsPatch(pe, patch)
  235. //log.Println("write file props", pe)
  236. f.writePropsObject(pe)
  237. return
  238. }