fakefs.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888
  1. // Copyright (C) 2018 The Syncthing Authors.
  2. //
  3. // This Source Code Form is subject to the terms of the Mozilla Public
  4. // License, v. 2.0. If a copy of the MPL was not distributed with this file,
  5. // You can obtain one at https://mozilla.org/MPL/2.0/.
  6. package fs
  7. import (
  8. "context"
  9. "errors"
  10. "fmt"
  11. "hash/fnv"
  12. "io"
  13. "io/ioutil"
  14. "math/rand"
  15. "net/url"
  16. "os"
  17. "path/filepath"
  18. "strconv"
  19. "strings"
  20. "sync"
  21. "time"
  22. )
  23. // see readShortAt()
  24. const randomBlockShift = 14 // 128k
  25. // fakefs is a fake filesystem for testing and benchmarking. It has the
  26. // following properties:
  27. //
  28. // - File metadata is kept in RAM. Specifically, we remember which files and
  29. // directories exist, their dates, permissions and sizes. Symlinks are
  30. // not supported.
  31. //
  32. // - File contents are generated pseudorandomly with just the file name as
  33. // seed. Writes are discarded, other than having the effect of increasing
  34. // the file size. If you only write data that you've read from a file with
  35. // the same name on a different fakefs, you'll never know the difference...
  36. //
  37. // - We totally ignore permissions - pretend you are root.
  38. //
  39. // - The root path can contain URL query-style parameters that pre populate
  40. // the filesystem at creation with a certain amount of random data:
  41. //
  42. // files=n to generate n random files (default 0)
  43. // maxsize=n to generate files up to a total of n MiB (default 0)
  44. // sizeavg=n to set the average size of random files, in bytes (default 1<<20)
  45. // seed=n to set the initial random seed (default 0)
  46. // insens=b "true" makes filesystem case-insensitive Windows- or OSX-style (default false)
  47. // latency=d to set the amount of time each "disk" operation takes, where d is time.ParseDuration format
  48. //
  49. // - Two fakefs:s pointing at the same root path see the same files.
  50. //
  51. type fakefs struct {
  52. uri string
  53. mut sync.Mutex
  54. root *fakeEntry
  55. insens bool
  56. withContent bool
  57. latency time.Duration
  58. }
  59. var (
  60. fakefsMut sync.Mutex
  61. fakefsFs = make(map[string]*fakefs)
  62. )
  63. func newFakeFilesystem(rootURI string, _ ...Option) *fakefs {
  64. fakefsMut.Lock()
  65. defer fakefsMut.Unlock()
  66. root := rootURI
  67. var params url.Values
  68. uri, err := url.Parse(rootURI)
  69. if err == nil {
  70. root = uri.Path
  71. params = uri.Query()
  72. }
  73. if fs, ok := fakefsFs[rootURI]; ok {
  74. // Already have an fs at this path
  75. return fs
  76. }
  77. fs := &fakefs{
  78. uri: "fake://" + rootURI,
  79. root: &fakeEntry{
  80. name: "/",
  81. entryType: fakeEntryTypeDir,
  82. mode: 0700,
  83. mtime: time.Now(),
  84. children: make(map[string]*fakeEntry),
  85. },
  86. }
  87. files, _ := strconv.Atoi(params.Get("files"))
  88. maxsize, _ := strconv.Atoi(params.Get("maxsize"))
  89. sizeavg, _ := strconv.Atoi(params.Get("sizeavg"))
  90. seed, _ := strconv.Atoi(params.Get("seed"))
  91. fs.insens = params.Get("insens") == "true"
  92. fs.withContent = params.Get("content") == "true"
  93. if sizeavg == 0 {
  94. sizeavg = 1 << 20
  95. }
  96. if files > 0 || maxsize > 0 {
  97. // Generate initial data according to specs. Operations in here
  98. // *look* like file I/O, but they are not. Do not worry that they
  99. // might fail.
  100. rng := rand.New(rand.NewSource(int64(seed)))
  101. var createdFiles int
  102. var writtenData int64
  103. for (files == 0 || createdFiles < files) && (maxsize == 0 || writtenData>>20 < int64(maxsize)) {
  104. dir := filepath.Join(fmt.Sprintf("%02x", rng.Intn(255)), fmt.Sprintf("%02x", rng.Intn(255)))
  105. file := fmt.Sprintf("%016x", rng.Int63())
  106. fs.MkdirAll(dir, 0755)
  107. fd, _ := fs.Create(filepath.Join(dir, file))
  108. createdFiles++
  109. fsize := int64(sizeavg/2 + rng.Intn(sizeavg))
  110. fd.Truncate(fsize)
  111. writtenData += fsize
  112. ftime := time.Unix(1000000000+rng.Int63n(10*365*86400), 0)
  113. fs.Chtimes(filepath.Join(dir, file), ftime, ftime)
  114. }
  115. }
  116. // Also create a default folder marker for good measure
  117. fs.Mkdir(".stfolder", 0700)
  118. // We only set the latency after doing the operations required to create
  119. // the filesystem initially.
  120. fs.latency, _ = time.ParseDuration(params.Get("latency"))
  121. fakefsFs[root] = fs
  122. return fs
  123. }
  124. type fakeEntryType int
  125. const (
  126. fakeEntryTypeFile fakeEntryType = iota
  127. fakeEntryTypeDir
  128. fakeEntryTypeSymlink
  129. )
  130. // fakeEntry is an entry (file or directory) in the fake filesystem
  131. type fakeEntry struct {
  132. name string
  133. entryType fakeEntryType
  134. dest string // for symlinks
  135. size int64
  136. mode FileMode
  137. uid int
  138. gid int
  139. mtime time.Time
  140. children map[string]*fakeEntry
  141. content []byte
  142. }
  143. func (fs *fakefs) entryForName(name string) *fakeEntry {
  144. // bug: lookup doesn't work through symlinks.
  145. if fs.insens {
  146. name = UnicodeLowercase(name)
  147. }
  148. name = filepath.ToSlash(name)
  149. if name == "." || name == "/" {
  150. return fs.root
  151. }
  152. name = strings.Trim(name, "/")
  153. comps := strings.Split(name, "/")
  154. entry := fs.root
  155. for _, comp := range comps {
  156. if entry.entryType != fakeEntryTypeDir {
  157. return nil
  158. }
  159. var ok bool
  160. entry, ok = entry.children[comp]
  161. if !ok {
  162. return nil
  163. }
  164. }
  165. return entry
  166. }
  167. func (fs *fakefs) Chmod(name string, mode FileMode) error {
  168. fs.mut.Lock()
  169. defer fs.mut.Unlock()
  170. time.Sleep(fs.latency)
  171. entry := fs.entryForName(name)
  172. if entry == nil {
  173. return os.ErrNotExist
  174. }
  175. entry.mode = mode
  176. return nil
  177. }
  178. func (fs *fakefs) Lchown(name string, uid, gid int) error {
  179. fs.mut.Lock()
  180. defer fs.mut.Unlock()
  181. time.Sleep(fs.latency)
  182. entry := fs.entryForName(name)
  183. if entry == nil {
  184. return os.ErrNotExist
  185. }
  186. entry.uid = uid
  187. entry.gid = gid
  188. return nil
  189. }
  190. func (fs *fakefs) Chtimes(name string, atime time.Time, mtime time.Time) error {
  191. fs.mut.Lock()
  192. defer fs.mut.Unlock()
  193. time.Sleep(fs.latency)
  194. entry := fs.entryForName(name)
  195. if entry == nil {
  196. return os.ErrNotExist
  197. }
  198. entry.mtime = mtime
  199. return nil
  200. }
  201. func (fs *fakefs) create(name string) (*fakeEntry, error) {
  202. fs.mut.Lock()
  203. defer fs.mut.Unlock()
  204. time.Sleep(fs.latency)
  205. if entry := fs.entryForName(name); entry != nil {
  206. if entry.entryType == fakeEntryTypeDir {
  207. return nil, os.ErrExist
  208. } else if entry.entryType == fakeEntryTypeSymlink {
  209. return nil, errors.New("following symlink not supported")
  210. }
  211. entry.size = 0
  212. entry.mtime = time.Now()
  213. entry.mode = 0666
  214. entry.content = nil
  215. if fs.withContent {
  216. entry.content = make([]byte, 0)
  217. }
  218. return entry, nil
  219. }
  220. dir := filepath.Dir(name)
  221. base := filepath.Base(name)
  222. entry := fs.entryForName(dir)
  223. if entry == nil {
  224. return nil, os.ErrNotExist
  225. }
  226. new := &fakeEntry{
  227. name: base,
  228. mode: 0666,
  229. mtime: time.Now(),
  230. }
  231. if fs.insens {
  232. base = UnicodeLowercase(base)
  233. }
  234. if fs.withContent {
  235. new.content = make([]byte, 0)
  236. }
  237. entry.children[base] = new
  238. return new, nil
  239. }
  240. func (fs *fakefs) Create(name string) (File, error) {
  241. entry, err := fs.create(name)
  242. if err != nil {
  243. return nil, err
  244. }
  245. if fs.insens {
  246. return &fakeFile{fakeEntry: entry, presentedName: filepath.Base(name)}, nil
  247. }
  248. return &fakeFile{fakeEntry: entry}, nil
  249. }
  250. func (fs *fakefs) CreateSymlink(target, name string) error {
  251. entry, err := fs.create(name)
  252. if err != nil {
  253. return err
  254. }
  255. entry.entryType = fakeEntryTypeSymlink
  256. entry.dest = target
  257. return nil
  258. }
  259. func (fs *fakefs) DirNames(name string) ([]string, error) {
  260. fs.mut.Lock()
  261. defer fs.mut.Unlock()
  262. time.Sleep(fs.latency)
  263. entry := fs.entryForName(name)
  264. if entry == nil {
  265. return nil, os.ErrNotExist
  266. }
  267. names := make([]string, 0, len(entry.children))
  268. for _, child := range entry.children {
  269. names = append(names, child.name)
  270. }
  271. return names, nil
  272. }
  273. func (fs *fakefs) Lstat(name string) (FileInfo, error) {
  274. fs.mut.Lock()
  275. defer fs.mut.Unlock()
  276. time.Sleep(fs.latency)
  277. entry := fs.entryForName(name)
  278. if entry == nil {
  279. return nil, os.ErrNotExist
  280. }
  281. info := &fakeFileInfo{*entry}
  282. if fs.insens {
  283. info.name = filepath.Base(name)
  284. }
  285. return info, nil
  286. }
  287. func (fs *fakefs) Mkdir(name string, perm FileMode) error {
  288. fs.mut.Lock()
  289. defer fs.mut.Unlock()
  290. time.Sleep(fs.latency)
  291. dir := filepath.Dir(name)
  292. base := filepath.Base(name)
  293. entry := fs.entryForName(dir)
  294. key := base
  295. if entry == nil {
  296. return os.ErrNotExist
  297. }
  298. if entry.entryType != fakeEntryTypeDir {
  299. return os.ErrExist
  300. }
  301. if fs.insens {
  302. key = UnicodeLowercase(key)
  303. }
  304. if _, ok := entry.children[key]; ok {
  305. return os.ErrExist
  306. }
  307. entry.children[key] = &fakeEntry{
  308. name: base,
  309. entryType: fakeEntryTypeDir,
  310. mode: perm,
  311. mtime: time.Now(),
  312. children: make(map[string]*fakeEntry),
  313. }
  314. return nil
  315. }
  316. func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
  317. fs.mut.Lock()
  318. defer fs.mut.Unlock()
  319. time.Sleep(fs.latency)
  320. name = filepath.ToSlash(name)
  321. name = strings.Trim(name, "/")
  322. comps := strings.Split(name, "/")
  323. entry := fs.root
  324. for _, comp := range comps {
  325. key := comp
  326. if fs.insens {
  327. key = UnicodeLowercase(key)
  328. }
  329. next, ok := entry.children[key]
  330. if !ok {
  331. new := &fakeEntry{
  332. name: comp,
  333. entryType: fakeEntryTypeDir,
  334. mode: perm,
  335. mtime: time.Now(),
  336. children: make(map[string]*fakeEntry),
  337. }
  338. entry.children[key] = new
  339. next = new
  340. } else if next.entryType != fakeEntryTypeDir {
  341. return errors.New("not a directory")
  342. }
  343. entry = next
  344. }
  345. return nil
  346. }
  347. func (fs *fakefs) Open(name string) (File, error) {
  348. fs.mut.Lock()
  349. defer fs.mut.Unlock()
  350. time.Sleep(fs.latency)
  351. entry := fs.entryForName(name)
  352. if entry == nil || entry.entryType != fakeEntryTypeFile {
  353. return nil, os.ErrNotExist
  354. }
  355. if fs.insens {
  356. return &fakeFile{fakeEntry: entry, presentedName: filepath.Base(name)}, nil
  357. }
  358. return &fakeFile{fakeEntry: entry}, nil
  359. }
  360. func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error) {
  361. if flags&os.O_CREATE == 0 {
  362. return fs.Open(name)
  363. }
  364. fs.mut.Lock()
  365. defer fs.mut.Unlock()
  366. time.Sleep(fs.latency)
  367. dir := filepath.Dir(name)
  368. base := filepath.Base(name)
  369. entry := fs.entryForName(dir)
  370. key := base
  371. if entry == nil {
  372. return nil, os.ErrNotExist
  373. } else if entry.entryType != fakeEntryTypeDir {
  374. return nil, errors.New("not a directory")
  375. }
  376. if fs.insens {
  377. key = UnicodeLowercase(key)
  378. }
  379. if flags&os.O_EXCL != 0 {
  380. if _, ok := entry.children[key]; ok {
  381. return nil, os.ErrExist
  382. }
  383. }
  384. newEntry := &fakeEntry{
  385. name: base,
  386. mode: mode,
  387. mtime: time.Now(),
  388. }
  389. if fs.withContent {
  390. newEntry.content = make([]byte, 0)
  391. }
  392. entry.children[key] = newEntry
  393. return &fakeFile{fakeEntry: newEntry}, nil
  394. }
  395. func (fs *fakefs) ReadSymlink(name string) (string, error) {
  396. fs.mut.Lock()
  397. defer fs.mut.Unlock()
  398. time.Sleep(fs.latency)
  399. entry := fs.entryForName(name)
  400. if entry == nil {
  401. return "", os.ErrNotExist
  402. } else if entry.entryType != fakeEntryTypeSymlink {
  403. return "", errors.New("not a symlink")
  404. }
  405. return entry.dest, nil
  406. }
  407. func (fs *fakefs) Remove(name string) error {
  408. fs.mut.Lock()
  409. defer fs.mut.Unlock()
  410. time.Sleep(fs.latency)
  411. if fs.insens {
  412. name = UnicodeLowercase(name)
  413. }
  414. entry := fs.entryForName(name)
  415. if entry == nil {
  416. return os.ErrNotExist
  417. }
  418. if len(entry.children) != 0 {
  419. return errors.New("not empty")
  420. }
  421. entry = fs.entryForName(filepath.Dir(name))
  422. delete(entry.children, filepath.Base(name))
  423. return nil
  424. }
  425. func (fs *fakefs) RemoveAll(name string) error {
  426. fs.mut.Lock()
  427. defer fs.mut.Unlock()
  428. time.Sleep(fs.latency)
  429. if fs.insens {
  430. name = UnicodeLowercase(name)
  431. }
  432. entry := fs.entryForName(filepath.Dir(name))
  433. if entry == nil {
  434. return nil // all tested real systems exibit this behaviour
  435. }
  436. // RemoveAll is easy when the file system uses garbage collection under
  437. // the hood... We even get the correct semantics for open fd:s for free.
  438. delete(entry.children, filepath.Base(name))
  439. return nil
  440. }
  441. func (fs *fakefs) Rename(oldname, newname string) error {
  442. fs.mut.Lock()
  443. defer fs.mut.Unlock()
  444. time.Sleep(fs.latency)
  445. oldKey := filepath.Base(oldname)
  446. newKey := filepath.Base(newname)
  447. if fs.insens {
  448. oldKey = UnicodeLowercase(oldKey)
  449. newKey = UnicodeLowercase(newKey)
  450. }
  451. p0 := fs.entryForName(filepath.Dir(oldname))
  452. if p0 == nil {
  453. return os.ErrNotExist
  454. }
  455. entry := p0.children[oldKey]
  456. if entry == nil {
  457. return os.ErrNotExist
  458. }
  459. p1 := fs.entryForName(filepath.Dir(newname))
  460. if p1 == nil {
  461. return os.ErrNotExist
  462. }
  463. dst, ok := p1.children[newKey]
  464. if ok {
  465. if fs.insens && newKey == oldKey {
  466. // case-only in-place rename
  467. entry.name = filepath.Base(newname)
  468. return nil
  469. }
  470. if dst.entryType == fakeEntryTypeDir {
  471. return errors.New("is a directory")
  472. }
  473. }
  474. p1.children[newKey] = entry
  475. entry.name = filepath.Base(newname)
  476. delete(p0.children, oldKey)
  477. return nil
  478. }
  479. func (fs *fakefs) Stat(name string) (FileInfo, error) {
  480. return fs.Lstat(name)
  481. }
  482. func (fs *fakefs) SymlinksSupported() bool {
  483. return false
  484. }
  485. func (fs *fakefs) Walk(name string, walkFn WalkFunc) error {
  486. return errors.New("not implemented")
  487. }
  488. func (fs *fakefs) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, <-chan error, error) {
  489. return nil, nil, ErrWatchNotSupported
  490. }
  491. func (fs *fakefs) Hide(name string) error {
  492. return nil
  493. }
  494. func (fs *fakefs) Unhide(name string) error {
  495. return nil
  496. }
  497. func (fs *fakefs) Glob(pattern string) ([]string, error) {
  498. // gnnh we don't seem to actually require this in practice
  499. return nil, errors.New("not implemented")
  500. }
  501. func (fs *fakefs) Roots() ([]string, error) {
  502. return []string{"/"}, nil
  503. }
  504. func (fs *fakefs) Usage(name string) (Usage, error) {
  505. return Usage{}, errors.New("not implemented")
  506. }
  507. func (fs *fakefs) Type() FilesystemType {
  508. return FilesystemTypeFake
  509. }
  510. func (fs *fakefs) URI() string {
  511. return fs.uri
  512. }
  513. func (fs *fakefs) SameFile(fi1, fi2 FileInfo) bool {
  514. // BUG: real systems base file sameness on path, inodes, etc
  515. // we try our best, but FileInfo just doesn't have enough data
  516. // so there be false positives, especially on Windows
  517. // where ModTime is not that precise
  518. var ok bool
  519. if fs.insens {
  520. ok = UnicodeLowercase(fi1.Name()) == UnicodeLowercase(fi2.Name())
  521. } else {
  522. ok = fi1.Name() == fi2.Name()
  523. }
  524. return ok && fi1.ModTime().Equal(fi2.ModTime()) && fi1.Mode() == fi2.Mode() && fi1.IsDir() == fi2.IsDir() && fi1.IsRegular() == fi2.IsRegular() && fi1.IsSymlink() == fi2.IsSymlink() && fi1.Owner() == fi2.Owner() && fi1.Group() == fi2.Group()
  525. }
  526. // fakeFile is the representation of an open file. We don't care if it's
  527. // opened for reading or writing, it's all good.
  528. type fakeFile struct {
  529. *fakeEntry
  530. mut sync.Mutex
  531. rng io.Reader
  532. seed int64
  533. offset int64
  534. seedOffs int64
  535. presentedName string // present (i.e. != "") on insensitive fs only
  536. }
  537. func (f *fakeFile) Close() error {
  538. return nil
  539. }
  540. func (f *fakeFile) Read(p []byte) (int, error) {
  541. f.mut.Lock()
  542. defer f.mut.Unlock()
  543. return f.readShortAt(p, f.offset)
  544. }
  545. func (f *fakeFile) ReadAt(p []byte, offs int64) (int, error) {
  546. f.mut.Lock()
  547. defer f.mut.Unlock()
  548. // ReadAt is spec:ed to always read a full block unless EOF or failure,
  549. // so we must loop. It's also not supposed to affect the seek position,
  550. // but that would make things annoying or inefficient in terms of
  551. // generating the appropriate RNG etc so I ignore that. In practice we
  552. // currently don't depend on that aspect of it...
  553. var read int
  554. for {
  555. n, err := f.readShortAt(p[read:], offs+int64(read))
  556. read += n
  557. if err != nil {
  558. return read, err
  559. }
  560. if read == len(p) {
  561. return read, nil
  562. }
  563. }
  564. }
  565. func (f *fakeFile) readShortAt(p []byte, offs int64) (int, error) {
  566. // Here be a certain amount of magic... We want to return pseudorandom,
  567. // predictable data so that a read from the same offset in the same file
  568. // always returns the same data. But the RNG is a stream, and reads can
  569. // be random.
  570. //
  571. // We split the file into "blocks" numbered by "seedNo", where each
  572. // block becomes an instantiation of the RNG, seeded with the hash of
  573. // the file number plus the seedNo (block number). We keep the RNG
  574. // around in the hope that the next read will be sequential to this one
  575. // and we can continue reading from the same RNG.
  576. //
  577. // When that's not the case we create a new RNG for the block we are in,
  578. // read as many bytes from it as necessary to get to the right offset,
  579. // and then serve the read from there. We limit the length of the read
  580. // to the end of the block, as another RNG needs to be created to serve
  581. // the next block.
  582. //
  583. // The size of the blocks are a matter of taste... Larger blocks give
  584. // better performance for sequential reads, but worse for random reads
  585. // as we often need to generate and throw away a lot of data at the
  586. // start of the block to serve a given read. 128 KiB blocks fit
  587. // reasonably well with the type of IO Syncthing tends to do.
  588. if f.entryType == fakeEntryTypeDir {
  589. return 0, errors.New("is a directory")
  590. }
  591. if offs >= f.size {
  592. return 0, io.EOF
  593. }
  594. if f.content != nil {
  595. n := copy(p, f.content[int(offs):])
  596. f.offset = offs + int64(n)
  597. return n, nil
  598. }
  599. // Lazily calculate our main seed, a simple 64 bit FNV hash our file
  600. // name.
  601. if f.seed == 0 {
  602. hf := fnv.New64()
  603. hf.Write([]byte(f.name))
  604. f.seed = int64(hf.Sum64())
  605. }
  606. // Check whether the read is a continuation of an RNG we already have or
  607. // we need to set up a new one.
  608. seedNo := offs >> randomBlockShift
  609. minOffs := seedNo << randomBlockShift
  610. nextBlockOffs := (seedNo + 1) << randomBlockShift
  611. if f.rng == nil || f.offset != offs || seedNo != f.seedOffs {
  612. // This is not a straight read continuing from a previous one
  613. f.rng = rand.New(rand.NewSource(f.seed + seedNo))
  614. // If the read is not at the start of the block, discard data
  615. // accordingly.
  616. diff := offs - minOffs
  617. if diff > 0 {
  618. lr := io.LimitReader(f.rng, diff)
  619. io.Copy(ioutil.Discard, lr)
  620. }
  621. f.offset = offs
  622. f.seedOffs = seedNo
  623. }
  624. size := len(p)
  625. // Don't read past the end of the file
  626. if offs+int64(size) > f.size {
  627. size = int(f.size - offs)
  628. }
  629. // Don't read across the block boundary
  630. if offs+int64(size) > nextBlockOffs {
  631. size = int(nextBlockOffs - offs)
  632. }
  633. f.offset += int64(size)
  634. return f.rng.Read(p[:size])
  635. }
  636. func (f *fakeFile) Seek(offset int64, whence int) (int64, error) {
  637. f.mut.Lock()
  638. defer f.mut.Unlock()
  639. if f.entryType == fakeEntryTypeDir {
  640. return 0, errors.New("is a directory")
  641. }
  642. f.rng = nil
  643. switch whence {
  644. case io.SeekCurrent:
  645. f.offset += offset
  646. case io.SeekEnd:
  647. f.offset = f.size - offset
  648. case io.SeekStart:
  649. f.offset = offset
  650. }
  651. if f.offset < 0 {
  652. f.offset = 0
  653. return f.offset, errors.New("seek before start")
  654. }
  655. if f.offset > f.size {
  656. f.offset = f.size
  657. return f.offset, io.EOF
  658. }
  659. return f.offset, nil
  660. }
  661. func (f *fakeFile) Write(p []byte) (int, error) {
  662. f.mut.Lock()
  663. offs := f.offset
  664. f.mut.Unlock()
  665. return f.WriteAt(p, offs)
  666. }
  667. func (f *fakeFile) WriteAt(p []byte, off int64) (int, error) {
  668. f.mut.Lock()
  669. defer f.mut.Unlock()
  670. if f.entryType == fakeEntryTypeDir {
  671. return 0, errors.New("is a directory")
  672. }
  673. if f.content != nil {
  674. if len(f.content) < int(off)+len(p) {
  675. newc := make([]byte, int(off)+len(p))
  676. copy(newc, f.content)
  677. f.content = newc
  678. }
  679. copy(f.content[int(off):], p)
  680. }
  681. f.rng = nil
  682. f.offset = off + int64(len(p))
  683. if f.offset > f.size {
  684. f.size = f.offset
  685. }
  686. return len(p), nil
  687. }
  688. func (f *fakeFile) Name() string {
  689. if f.presentedName != "" {
  690. return f.presentedName
  691. }
  692. return f.name
  693. }
  694. func (f *fakeFile) Truncate(size int64) error {
  695. f.mut.Lock()
  696. defer f.mut.Unlock()
  697. if f.content != nil {
  698. f.content = f.content[:int(size)]
  699. }
  700. f.rng = nil
  701. f.size = size
  702. if f.offset > size {
  703. f.offset = size
  704. }
  705. return nil
  706. }
  707. func (f *fakeFile) Stat() (FileInfo, error) {
  708. info := &fakeFileInfo{*f.fakeEntry}
  709. if f.presentedName != "" {
  710. info.name = f.presentedName
  711. }
  712. return info, nil
  713. }
  714. func (f *fakeFile) Sync() error {
  715. return nil
  716. }
  717. // fakeFileInfo is the stat result.
  718. type fakeFileInfo struct {
  719. fakeEntry // intentionally a copy of the struct
  720. }
  721. func (f *fakeFileInfo) Name() string {
  722. return f.name
  723. }
  724. func (f *fakeFileInfo) Mode() FileMode {
  725. return f.mode
  726. }
  727. func (f *fakeFileInfo) Size() int64 {
  728. return f.size
  729. }
  730. func (f *fakeFileInfo) ModTime() time.Time {
  731. return f.mtime
  732. }
  733. func (f *fakeFileInfo) IsDir() bool {
  734. return f.entryType == fakeEntryTypeDir
  735. }
  736. func (f *fakeFileInfo) IsRegular() bool {
  737. return f.entryType == fakeEntryTypeFile
  738. }
  739. func (f *fakeFileInfo) IsSymlink() bool {
  740. return f.entryType == fakeEntryTypeSymlink
  741. }
  742. func (f *fakeFileInfo) Owner() int {
  743. return f.uid
  744. }
  745. func (f *fakeFileInfo) Group() int {
  746. return f.gid
  747. }