prop_test.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  1. // Copyright 2015 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package webdav
  5. import (
  6. "encoding/xml"
  7. "fmt"
  8. "net/http"
  9. "os"
  10. "reflect"
  11. "sort"
  12. "testing"
  13. )
  14. func TestMemPS(t *testing.T) {
  15. // calcProps calculates the getlastmodified and getetag DAV: property
  16. // values in pstats for resource name in file-system fs.
  17. calcProps := func(name string, fs FileSystem, ls LockSystem, pstats []Propstat) error {
  18. fi, err := fs.Stat(name)
  19. if err != nil {
  20. return err
  21. }
  22. for _, pst := range pstats {
  23. for i, p := range pst.Props {
  24. switch p.XMLName {
  25. case xml.Name{Space: "DAV:", Local: "getlastmodified"}:
  26. p.InnerXML = []byte(fi.ModTime().Format(http.TimeFormat))
  27. pst.Props[i] = p
  28. case xml.Name{Space: "DAV:", Local: "getetag"}:
  29. if fi.IsDir() {
  30. continue
  31. }
  32. etag, err := findETag(fs, ls, name, fi)
  33. if err != nil {
  34. return err
  35. }
  36. p.InnerXML = []byte(etag)
  37. pst.Props[i] = p
  38. }
  39. }
  40. }
  41. return nil
  42. }
  43. type propOp struct {
  44. op string
  45. name string
  46. pnames []xml.Name
  47. patches []Proppatch
  48. wantPnames []xml.Name
  49. wantPropstats []Propstat
  50. }
  51. testCases := []struct {
  52. desc string
  53. noDeadProps bool
  54. buildfs []string
  55. propOp []propOp
  56. }{{
  57. desc: "propname",
  58. buildfs: []string{"mkdir /dir", "touch /file"},
  59. propOp: []propOp{{
  60. op: "propname",
  61. name: "/dir",
  62. wantPnames: []xml.Name{
  63. xml.Name{Space: "DAV:", Local: "resourcetype"},
  64. xml.Name{Space: "DAV:", Local: "displayname"},
  65. xml.Name{Space: "DAV:", Local: "getcontentlength"},
  66. xml.Name{Space: "DAV:", Local: "getlastmodified"},
  67. xml.Name{Space: "DAV:", Local: "getcontenttype"},
  68. xml.Name{Space: "DAV:", Local: "supportedlock"},
  69. },
  70. }, {
  71. op: "propname",
  72. name: "/file",
  73. wantPnames: []xml.Name{
  74. xml.Name{Space: "DAV:", Local: "resourcetype"},
  75. xml.Name{Space: "DAV:", Local: "displayname"},
  76. xml.Name{Space: "DAV:", Local: "getcontentlength"},
  77. xml.Name{Space: "DAV:", Local: "getlastmodified"},
  78. xml.Name{Space: "DAV:", Local: "getcontenttype"},
  79. xml.Name{Space: "DAV:", Local: "getetag"},
  80. xml.Name{Space: "DAV:", Local: "supportedlock"},
  81. },
  82. }},
  83. }, {
  84. desc: "allprop dir and file",
  85. buildfs: []string{"mkdir /dir", "write /file foobarbaz"},
  86. propOp: []propOp{{
  87. op: "allprop",
  88. name: "/dir",
  89. wantPropstats: []Propstat{{
  90. Status: http.StatusOK,
  91. Props: []Property{{
  92. XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
  93. InnerXML: []byte(`<collection xmlns="DAV:"/>`),
  94. }, {
  95. XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
  96. InnerXML: []byte("dir"),
  97. }, {
  98. XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
  99. InnerXML: []byte("0"),
  100. }, {
  101. XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
  102. InnerXML: nil, // Calculated during test.
  103. }, {
  104. XMLName: xml.Name{Space: "DAV:", Local: "getcontenttype"},
  105. InnerXML: []byte("text/plain; charset=utf-8"),
  106. }, {
  107. XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
  108. InnerXML: []byte(`` +
  109. `<lockentry xmlns="DAV:">` +
  110. `<lockscope><exclusive/></lockscope>` +
  111. `<locktype><write/></locktype>` +
  112. `</lockentry>`,
  113. ),
  114. }},
  115. }},
  116. }, {
  117. op: "allprop",
  118. name: "/file",
  119. wantPropstats: []Propstat{{
  120. Status: http.StatusOK,
  121. Props: []Property{{
  122. XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
  123. InnerXML: []byte(""),
  124. }, {
  125. XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
  126. InnerXML: []byte("file"),
  127. }, {
  128. XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
  129. InnerXML: []byte("9"),
  130. }, {
  131. XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
  132. InnerXML: nil, // Calculated during test.
  133. }, {
  134. XMLName: xml.Name{Space: "DAV:", Local: "getcontenttype"},
  135. InnerXML: []byte("text/plain; charset=utf-8"),
  136. }, {
  137. XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
  138. InnerXML: nil, // Calculated during test.
  139. }, {
  140. XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
  141. InnerXML: []byte(`` +
  142. `<lockentry xmlns="DAV:">` +
  143. `<lockscope><exclusive/></lockscope>` +
  144. `<locktype><write/></locktype>` +
  145. `</lockentry>`,
  146. ),
  147. }},
  148. }},
  149. }, {
  150. op: "allprop",
  151. name: "/file",
  152. pnames: []xml.Name{
  153. {"DAV:", "resourcetype"},
  154. {"foo", "bar"},
  155. },
  156. wantPropstats: []Propstat{{
  157. Status: http.StatusOK,
  158. Props: []Property{{
  159. XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
  160. InnerXML: []byte(""),
  161. }, {
  162. XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
  163. InnerXML: []byte("file"),
  164. }, {
  165. XMLName: xml.Name{Space: "DAV:", Local: "getcontentlength"},
  166. InnerXML: []byte("9"),
  167. }, {
  168. XMLName: xml.Name{Space: "DAV:", Local: "getlastmodified"},
  169. InnerXML: nil, // Calculated during test.
  170. }, {
  171. XMLName: xml.Name{Space: "DAV:", Local: "getcontenttype"},
  172. InnerXML: []byte("text/plain; charset=utf-8"),
  173. }, {
  174. XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
  175. InnerXML: nil, // Calculated during test.
  176. }, {
  177. XMLName: xml.Name{Space: "DAV:", Local: "supportedlock"},
  178. InnerXML: []byte(`` +
  179. `<lockentry xmlns="DAV:">` +
  180. `<lockscope><exclusive/></lockscope>` +
  181. `<locktype><write/></locktype>` +
  182. `</lockentry>`,
  183. ),
  184. }}}, {
  185. Status: http.StatusNotFound,
  186. Props: []Property{{
  187. XMLName: xml.Name{Space: "foo", Local: "bar"},
  188. }}},
  189. },
  190. }},
  191. }, {
  192. desc: "propfind DAV:resourcetype",
  193. buildfs: []string{"mkdir /dir", "touch /file"},
  194. propOp: []propOp{{
  195. op: "propfind",
  196. name: "/dir",
  197. pnames: []xml.Name{{"DAV:", "resourcetype"}},
  198. wantPropstats: []Propstat{{
  199. Status: http.StatusOK,
  200. Props: []Property{{
  201. XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
  202. InnerXML: []byte(`<collection xmlns="DAV:"/>`),
  203. }},
  204. }},
  205. }, {
  206. op: "propfind",
  207. name: "/file",
  208. pnames: []xml.Name{{"DAV:", "resourcetype"}},
  209. wantPropstats: []Propstat{{
  210. Status: http.StatusOK,
  211. Props: []Property{{
  212. XMLName: xml.Name{Space: "DAV:", Local: "resourcetype"},
  213. InnerXML: []byte(""),
  214. }},
  215. }},
  216. }},
  217. }, {
  218. desc: "propfind unsupported DAV properties",
  219. buildfs: []string{"mkdir /dir"},
  220. propOp: []propOp{{
  221. op: "propfind",
  222. name: "/dir",
  223. pnames: []xml.Name{{"DAV:", "getcontentlanguage"}},
  224. wantPropstats: []Propstat{{
  225. Status: http.StatusNotFound,
  226. Props: []Property{{
  227. XMLName: xml.Name{Space: "DAV:", Local: "getcontentlanguage"},
  228. }},
  229. }},
  230. }, {
  231. op: "propfind",
  232. name: "/dir",
  233. pnames: []xml.Name{{"DAV:", "creationdate"}},
  234. wantPropstats: []Propstat{{
  235. Status: http.StatusNotFound,
  236. Props: []Property{{
  237. XMLName: xml.Name{Space: "DAV:", Local: "creationdate"},
  238. }},
  239. }},
  240. }},
  241. }, {
  242. desc: "propfind getetag for files but not for directories",
  243. buildfs: []string{"mkdir /dir", "touch /file"},
  244. propOp: []propOp{{
  245. op: "propfind",
  246. name: "/dir",
  247. pnames: []xml.Name{{"DAV:", "getetag"}},
  248. wantPropstats: []Propstat{{
  249. Status: http.StatusNotFound,
  250. Props: []Property{{
  251. XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
  252. }},
  253. }},
  254. }, {
  255. op: "propfind",
  256. name: "/file",
  257. pnames: []xml.Name{{"DAV:", "getetag"}},
  258. wantPropstats: []Propstat{{
  259. Status: http.StatusOK,
  260. Props: []Property{{
  261. XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
  262. InnerXML: nil, // Calculated during test.
  263. }},
  264. }},
  265. }},
  266. }, {
  267. desc: "proppatch property on no-dead-properties file system",
  268. buildfs: []string{"mkdir /dir"},
  269. noDeadProps: true,
  270. propOp: []propOp{{
  271. op: "proppatch",
  272. name: "/dir",
  273. patches: []Proppatch{{
  274. Props: []Property{{
  275. XMLName: xml.Name{Space: "foo", Local: "bar"},
  276. }},
  277. }},
  278. wantPropstats: []Propstat{{
  279. Status: http.StatusForbidden,
  280. Props: []Property{{
  281. XMLName: xml.Name{Space: "foo", Local: "bar"},
  282. }},
  283. }},
  284. }, {
  285. op: "proppatch",
  286. name: "/dir",
  287. patches: []Proppatch{{
  288. Props: []Property{{
  289. XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
  290. }},
  291. }},
  292. wantPropstats: []Propstat{{
  293. Status: http.StatusForbidden,
  294. XMLError: `<error xmlns="DAV:"><cannot-modify-protected-property/></error>`,
  295. Props: []Property{{
  296. XMLName: xml.Name{Space: "DAV:", Local: "getetag"},
  297. }},
  298. }},
  299. }},
  300. }, {
  301. desc: "proppatch dead property",
  302. buildfs: []string{"mkdir /dir"},
  303. propOp: []propOp{{
  304. op: "proppatch",
  305. name: "/dir",
  306. patches: []Proppatch{{
  307. Props: []Property{{
  308. XMLName: xml.Name{Space: "foo", Local: "bar"},
  309. InnerXML: []byte("baz"),
  310. }},
  311. }},
  312. wantPropstats: []Propstat{{
  313. Status: http.StatusOK,
  314. Props: []Property{{
  315. XMLName: xml.Name{Space: "foo", Local: "bar"},
  316. }},
  317. }},
  318. }, {
  319. op: "propfind",
  320. name: "/dir",
  321. pnames: []xml.Name{{Space: "foo", Local: "bar"}},
  322. wantPropstats: []Propstat{{
  323. Status: http.StatusOK,
  324. Props: []Property{{
  325. XMLName: xml.Name{Space: "foo", Local: "bar"},
  326. InnerXML: []byte("baz"),
  327. }},
  328. }},
  329. }},
  330. }, {
  331. desc: "proppatch dead property with failed dependency",
  332. buildfs: []string{"mkdir /dir"},
  333. propOp: []propOp{{
  334. op: "proppatch",
  335. name: "/dir",
  336. patches: []Proppatch{{
  337. Props: []Property{{
  338. XMLName: xml.Name{Space: "foo", Local: "bar"},
  339. InnerXML: []byte("baz"),
  340. }},
  341. }, {
  342. Props: []Property{{
  343. XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
  344. InnerXML: []byte("xxx"),
  345. }},
  346. }},
  347. wantPropstats: []Propstat{{
  348. Status: http.StatusForbidden,
  349. XMLError: `<error xmlns="DAV:"><cannot-modify-protected-property/></error>`,
  350. Props: []Property{{
  351. XMLName: xml.Name{Space: "DAV:", Local: "displayname"},
  352. }},
  353. }, {
  354. Status: StatusFailedDependency,
  355. Props: []Property{{
  356. XMLName: xml.Name{Space: "foo", Local: "bar"},
  357. }},
  358. }},
  359. }, {
  360. op: "propfind",
  361. name: "/dir",
  362. pnames: []xml.Name{{Space: "foo", Local: "bar"}},
  363. wantPropstats: []Propstat{{
  364. Status: http.StatusNotFound,
  365. Props: []Property{{
  366. XMLName: xml.Name{Space: "foo", Local: "bar"},
  367. }},
  368. }},
  369. }},
  370. }, {
  371. desc: "proppatch remove dead property",
  372. buildfs: []string{"mkdir /dir"},
  373. propOp: []propOp{{
  374. op: "proppatch",
  375. name: "/dir",
  376. patches: []Proppatch{{
  377. Props: []Property{{
  378. XMLName: xml.Name{Space: "foo", Local: "bar"},
  379. InnerXML: []byte("baz"),
  380. }, {
  381. XMLName: xml.Name{Space: "spam", Local: "ham"},
  382. InnerXML: []byte("eggs"),
  383. }},
  384. }},
  385. wantPropstats: []Propstat{{
  386. Status: http.StatusOK,
  387. Props: []Property{{
  388. XMLName: xml.Name{Space: "foo", Local: "bar"},
  389. }, {
  390. XMLName: xml.Name{Space: "spam", Local: "ham"},
  391. }},
  392. }},
  393. }, {
  394. op: "propfind",
  395. name: "/dir",
  396. pnames: []xml.Name{
  397. {Space: "foo", Local: "bar"},
  398. {Space: "spam", Local: "ham"},
  399. },
  400. wantPropstats: []Propstat{{
  401. Status: http.StatusOK,
  402. Props: []Property{{
  403. XMLName: xml.Name{Space: "foo", Local: "bar"},
  404. InnerXML: []byte("baz"),
  405. }, {
  406. XMLName: xml.Name{Space: "spam", Local: "ham"},
  407. InnerXML: []byte("eggs"),
  408. }},
  409. }},
  410. }, {
  411. op: "proppatch",
  412. name: "/dir",
  413. patches: []Proppatch{{
  414. Remove: true,
  415. Props: []Property{{
  416. XMLName: xml.Name{Space: "foo", Local: "bar"},
  417. }},
  418. }},
  419. wantPropstats: []Propstat{{
  420. Status: http.StatusOK,
  421. Props: []Property{{
  422. XMLName: xml.Name{Space: "foo", Local: "bar"},
  423. }},
  424. }},
  425. }, {
  426. op: "propfind",
  427. name: "/dir",
  428. pnames: []xml.Name{
  429. {Space: "foo", Local: "bar"},
  430. {Space: "spam", Local: "ham"},
  431. },
  432. wantPropstats: []Propstat{{
  433. Status: http.StatusNotFound,
  434. Props: []Property{{
  435. XMLName: xml.Name{Space: "foo", Local: "bar"},
  436. }},
  437. }, {
  438. Status: http.StatusOK,
  439. Props: []Property{{
  440. XMLName: xml.Name{Space: "spam", Local: "ham"},
  441. InnerXML: []byte("eggs"),
  442. }},
  443. }},
  444. }},
  445. }, {
  446. desc: "propname with dead property",
  447. buildfs: []string{"touch /file"},
  448. propOp: []propOp{{
  449. op: "proppatch",
  450. name: "/file",
  451. patches: []Proppatch{{
  452. Props: []Property{{
  453. XMLName: xml.Name{Space: "foo", Local: "bar"},
  454. InnerXML: []byte("baz"),
  455. }},
  456. }},
  457. wantPropstats: []Propstat{{
  458. Status: http.StatusOK,
  459. Props: []Property{{
  460. XMLName: xml.Name{Space: "foo", Local: "bar"},
  461. }},
  462. }},
  463. }, {
  464. op: "propname",
  465. name: "/file",
  466. wantPnames: []xml.Name{
  467. xml.Name{Space: "DAV:", Local: "resourcetype"},
  468. xml.Name{Space: "DAV:", Local: "displayname"},
  469. xml.Name{Space: "DAV:", Local: "getcontentlength"},
  470. xml.Name{Space: "DAV:", Local: "getlastmodified"},
  471. xml.Name{Space: "DAV:", Local: "getcontenttype"},
  472. xml.Name{Space: "DAV:", Local: "getetag"},
  473. xml.Name{Space: "DAV:", Local: "supportedlock"},
  474. xml.Name{Space: "foo", Local: "bar"},
  475. },
  476. }},
  477. }, {
  478. desc: "proppatch remove unknown dead property",
  479. buildfs: []string{"mkdir /dir"},
  480. propOp: []propOp{{
  481. op: "proppatch",
  482. name: "/dir",
  483. patches: []Proppatch{{
  484. Remove: true,
  485. Props: []Property{{
  486. XMLName: xml.Name{Space: "foo", Local: "bar"},
  487. }},
  488. }},
  489. wantPropstats: []Propstat{{
  490. Status: http.StatusOK,
  491. Props: []Property{{
  492. XMLName: xml.Name{Space: "foo", Local: "bar"},
  493. }},
  494. }},
  495. }},
  496. }, {
  497. desc: "bad: propfind unknown property",
  498. buildfs: []string{"mkdir /dir"},
  499. propOp: []propOp{{
  500. op: "propfind",
  501. name: "/dir",
  502. pnames: []xml.Name{{"foo:", "bar"}},
  503. wantPropstats: []Propstat{{
  504. Status: http.StatusNotFound,
  505. Props: []Property{{
  506. XMLName: xml.Name{Space: "foo:", Local: "bar"},
  507. }},
  508. }},
  509. }},
  510. }}
  511. for _, tc := range testCases {
  512. fs, err := buildTestFS(tc.buildfs)
  513. if err != nil {
  514. t.Fatalf("%s: cannot create test filesystem: %v", tc.desc, err)
  515. }
  516. if tc.noDeadProps {
  517. fs = noDeadPropsFS{fs}
  518. }
  519. ls := NewMemLS()
  520. for _, op := range tc.propOp {
  521. desc := fmt.Sprintf("%s: %s %s", tc.desc, op.op, op.name)
  522. if err = calcProps(op.name, fs, ls, op.wantPropstats); err != nil {
  523. t.Fatalf("%s: calcProps: %v", desc, err)
  524. }
  525. // Call property system.
  526. var propstats []Propstat
  527. switch op.op {
  528. case "propname":
  529. pnames, err := propnames(fs, ls, op.name)
  530. if err != nil {
  531. t.Errorf("%s: got error %v, want nil", desc, err)
  532. continue
  533. }
  534. sort.Sort(byXMLName(pnames))
  535. sort.Sort(byXMLName(op.wantPnames))
  536. if !reflect.DeepEqual(pnames, op.wantPnames) {
  537. t.Errorf("%s: pnames\ngot %q\nwant %q", desc, pnames, op.wantPnames)
  538. }
  539. continue
  540. case "allprop":
  541. propstats, err = allprop(fs, ls, op.name, op.pnames)
  542. case "propfind":
  543. propstats, err = props(fs, ls, op.name, op.pnames)
  544. case "proppatch":
  545. propstats, err = patch(fs, ls, op.name, op.patches)
  546. default:
  547. t.Fatalf("%s: %s not implemented", desc, op.op)
  548. }
  549. if err != nil {
  550. t.Errorf("%s: got error %v, want nil", desc, err)
  551. continue
  552. }
  553. // Compare return values from allprop, propfind or proppatch.
  554. for _, pst := range propstats {
  555. sort.Sort(byPropname(pst.Props))
  556. }
  557. for _, pst := range op.wantPropstats {
  558. sort.Sort(byPropname(pst.Props))
  559. }
  560. sort.Sort(byStatus(propstats))
  561. sort.Sort(byStatus(op.wantPropstats))
  562. if !reflect.DeepEqual(propstats, op.wantPropstats) {
  563. t.Errorf("%s: propstat\ngot %q\nwant %q", desc, propstats, op.wantPropstats)
  564. }
  565. }
  566. }
  567. }
  568. func cmpXMLName(a, b xml.Name) bool {
  569. if a.Space != b.Space {
  570. return a.Space < b.Space
  571. }
  572. return a.Local < b.Local
  573. }
  574. type byXMLName []xml.Name
  575. func (b byXMLName) Len() int { return len(b) }
  576. func (b byXMLName) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
  577. func (b byXMLName) Less(i, j int) bool { return cmpXMLName(b[i], b[j]) }
  578. type byPropname []Property
  579. func (b byPropname) Len() int { return len(b) }
  580. func (b byPropname) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
  581. func (b byPropname) Less(i, j int) bool { return cmpXMLName(b[i].XMLName, b[j].XMLName) }
  582. type byStatus []Propstat
  583. func (b byStatus) Len() int { return len(b) }
  584. func (b byStatus) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
  585. func (b byStatus) Less(i, j int) bool { return b[i].Status < b[j].Status }
  586. type noDeadPropsFS struct {
  587. FileSystem
  588. }
  589. func (fs noDeadPropsFS) OpenFile(name string, flag int, perm os.FileMode) (File, error) {
  590. f, err := fs.FileSystem.OpenFile(name, flag, perm)
  591. if err != nil {
  592. return nil, err
  593. }
  594. return noDeadPropsFile{f}, nil
  595. }
  596. // noDeadPropsFile wraps a File but strips any optional DeadPropsHolder methods
  597. // provided by the underlying File implementation.
  598. type noDeadPropsFile struct {
  599. f File
  600. }
  601. func (f noDeadPropsFile) Close() error { return f.f.Close() }
  602. func (f noDeadPropsFile) Read(p []byte) (int, error) { return f.f.Read(p) }
  603. func (f noDeadPropsFile) Readdir(count int) ([]os.FileInfo, error) { return f.f.Readdir(count) }
  604. func (f noDeadPropsFile) Seek(off int64, whence int) (int64, error) { return f.f.Seek(off, whence) }
  605. func (f noDeadPropsFile) Stat() (os.FileInfo, error) { return f.f.Stat() }
  606. func (f noDeadPropsFile) Write(p []byte) (int, error) { return f.f.Write(p) }