prop_test.go 17 KB

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