fakefs.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  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. //
  47. // - Two fakefs:s pointing at the same root path see the same files.
  48. //
  49. type fakefs struct {
  50. mut sync.Mutex
  51. root *fakeEntry
  52. }
  53. var (
  54. fakefsMut sync.Mutex
  55. fakefsFs = make(map[string]*fakefs)
  56. )
  57. func newFakeFilesystem(root string) *fakefs {
  58. fakefsMut.Lock()
  59. defer fakefsMut.Unlock()
  60. var params url.Values
  61. uri, err := url.Parse(root)
  62. if err == nil {
  63. root = uri.Path
  64. params = uri.Query()
  65. }
  66. if fs, ok := fakefsFs[root]; ok {
  67. // Already have an fs at this path
  68. return fs
  69. }
  70. fs := &fakefs{
  71. root: &fakeEntry{
  72. name: "/",
  73. entryType: fakeEntryTypeDir,
  74. mode: 0700,
  75. mtime: time.Now(),
  76. children: make(map[string]*fakeEntry),
  77. },
  78. }
  79. files, _ := strconv.Atoi(params.Get("files"))
  80. maxsize, _ := strconv.Atoi(params.Get("maxsize"))
  81. sizeavg, _ := strconv.Atoi(params.Get("sizeavg"))
  82. seed, _ := strconv.Atoi(params.Get("seed"))
  83. if sizeavg == 0 {
  84. sizeavg = 1 << 20
  85. }
  86. if files > 0 || maxsize > 0 {
  87. // Generate initial data according to specs. Operations in here
  88. // *look* like file I/O, but they are not. Do not worry that they
  89. // might fail.
  90. rng := rand.New(rand.NewSource(int64(seed)))
  91. var createdFiles int
  92. var writtenData int64
  93. for (files == 0 || createdFiles < files) && (maxsize == 0 || writtenData>>20 < int64(maxsize)) {
  94. dir := filepath.Join(fmt.Sprintf("%02x", rng.Intn(255)), fmt.Sprintf("%02x", rng.Intn(255)))
  95. file := fmt.Sprintf("%016x", rng.Int63())
  96. fs.MkdirAll(dir, 0755)
  97. fd, _ := fs.Create(filepath.Join(dir, file))
  98. createdFiles++
  99. fsize := int64(sizeavg/2 + rng.Intn(sizeavg))
  100. fd.Truncate(fsize)
  101. writtenData += fsize
  102. ftime := time.Unix(1000000000+rng.Int63n(10*365*86400), 0)
  103. fs.Chtimes(filepath.Join(dir, file), ftime, ftime)
  104. }
  105. }
  106. // Also create a default folder marker for good measure
  107. fs.Mkdir(".stfolder", 0700)
  108. fakefsFs[root] = fs
  109. return fs
  110. }
  111. type fakeEntryType int
  112. const (
  113. fakeEntryTypeFile fakeEntryType = iota
  114. fakeEntryTypeDir
  115. fakeEntryTypeSymlink
  116. )
  117. // fakeEntry is an entry (file or directory) in the fake filesystem
  118. type fakeEntry struct {
  119. name string
  120. entryType fakeEntryType
  121. dest string // for symlinks
  122. size int64
  123. mode FileMode
  124. uid int
  125. gid int
  126. mtime time.Time
  127. children map[string]*fakeEntry
  128. }
  129. func (fs *fakefs) entryForName(name string) *fakeEntry {
  130. // bug: lookup doesn't work through symlinks.
  131. name = filepath.ToSlash(name)
  132. if name == "." || name == "/" {
  133. return fs.root
  134. }
  135. name = strings.Trim(name, "/")
  136. comps := strings.Split(name, "/")
  137. entry := fs.root
  138. for _, comp := range comps {
  139. if entry.entryType != fakeEntryTypeDir {
  140. return nil
  141. }
  142. var ok bool
  143. entry, ok = entry.children[comp]
  144. if !ok {
  145. return nil
  146. }
  147. }
  148. return entry
  149. }
  150. func (fs *fakefs) Chmod(name string, mode FileMode) error {
  151. fs.mut.Lock()
  152. defer fs.mut.Unlock()
  153. entry := fs.entryForName(name)
  154. if entry == nil {
  155. return os.ErrNotExist
  156. }
  157. entry.mode = mode
  158. return nil
  159. }
  160. func (fs *fakefs) Lchown(name string, uid, gid int) error {
  161. fs.mut.Lock()
  162. defer fs.mut.Unlock()
  163. entry := fs.entryForName(name)
  164. if entry == nil {
  165. return os.ErrNotExist
  166. }
  167. entry.uid = uid
  168. entry.gid = gid
  169. return nil
  170. }
  171. func (fs *fakefs) Chtimes(name string, atime time.Time, mtime time.Time) error {
  172. fs.mut.Lock()
  173. defer fs.mut.Unlock()
  174. entry := fs.entryForName(name)
  175. if entry == nil {
  176. return os.ErrNotExist
  177. }
  178. entry.mtime = mtime
  179. return nil
  180. }
  181. func (fs *fakefs) create(name string) (*fakeEntry, error) {
  182. fs.mut.Lock()
  183. defer fs.mut.Unlock()
  184. if entry := fs.entryForName(name); entry != nil {
  185. if entry.entryType == fakeEntryTypeDir {
  186. return nil, os.ErrExist
  187. } else if entry.entryType == fakeEntryTypeSymlink {
  188. return nil, errors.New("following symlink not supported")
  189. }
  190. entry.size = 0
  191. entry.mtime = time.Now()
  192. entry.mode = 0666
  193. return entry, nil
  194. }
  195. dir := filepath.Dir(name)
  196. base := filepath.Base(name)
  197. entry := fs.entryForName(dir)
  198. if entry == nil {
  199. return nil, os.ErrNotExist
  200. }
  201. new := &fakeEntry{
  202. name: base,
  203. mode: 0666,
  204. mtime: time.Now(),
  205. }
  206. entry.children[base] = new
  207. return new, nil
  208. }
  209. func (fs *fakefs) Create(name string) (File, error) {
  210. entry, err := fs.create(name)
  211. if err != nil {
  212. return nil, err
  213. }
  214. return &fakeFile{fakeEntry: entry}, nil
  215. }
  216. func (fs *fakefs) CreateSymlink(target, name string) error {
  217. entry, err := fs.create(name)
  218. if err != nil {
  219. return err
  220. }
  221. entry.entryType = fakeEntryTypeSymlink
  222. entry.dest = target
  223. return nil
  224. }
  225. func (fs *fakefs) DirNames(name string) ([]string, error) {
  226. fs.mut.Lock()
  227. defer fs.mut.Unlock()
  228. entry := fs.entryForName(name)
  229. if entry == nil {
  230. return nil, os.ErrNotExist
  231. }
  232. names := make([]string, 0, len(entry.children))
  233. for name := range entry.children {
  234. names = append(names, name)
  235. }
  236. return names, nil
  237. }
  238. func (fs *fakefs) Lstat(name string) (FileInfo, error) {
  239. fs.mut.Lock()
  240. defer fs.mut.Unlock()
  241. entry := fs.entryForName(name)
  242. if entry == nil {
  243. return nil, os.ErrNotExist
  244. }
  245. return &fakeFileInfo{*entry}, nil
  246. }
  247. func (fs *fakefs) Mkdir(name string, perm FileMode) error {
  248. fs.mut.Lock()
  249. defer fs.mut.Unlock()
  250. dir := filepath.Dir(name)
  251. base := filepath.Base(name)
  252. entry := fs.entryForName(dir)
  253. if entry == nil {
  254. return os.ErrNotExist
  255. }
  256. if entry.entryType != fakeEntryTypeDir {
  257. return os.ErrExist
  258. }
  259. if _, ok := entry.children[base]; ok {
  260. return os.ErrExist
  261. }
  262. entry.children[base] = &fakeEntry{
  263. name: base,
  264. entryType: fakeEntryTypeDir,
  265. mode: perm,
  266. mtime: time.Now(),
  267. children: make(map[string]*fakeEntry),
  268. }
  269. return nil
  270. }
  271. func (fs *fakefs) MkdirAll(name string, perm FileMode) error {
  272. name = filepath.ToSlash(name)
  273. name = strings.Trim(name, "/")
  274. comps := strings.Split(name, "/")
  275. entry := fs.root
  276. for _, comp := range comps {
  277. next, ok := entry.children[comp]
  278. if !ok {
  279. new := &fakeEntry{
  280. name: comp,
  281. entryType: fakeEntryTypeDir,
  282. mode: perm,
  283. mtime: time.Now(),
  284. children: make(map[string]*fakeEntry),
  285. }
  286. entry.children[comp] = new
  287. next = new
  288. } else if next.entryType != fakeEntryTypeDir {
  289. return errors.New("not a directory")
  290. }
  291. entry = next
  292. }
  293. return nil
  294. }
  295. func (fs *fakefs) Open(name string) (File, error) {
  296. fs.mut.Lock()
  297. defer fs.mut.Unlock()
  298. entry := fs.entryForName(name)
  299. if entry == nil || entry.entryType != fakeEntryTypeFile {
  300. return nil, os.ErrNotExist
  301. }
  302. return &fakeFile{fakeEntry: entry}, nil
  303. }
  304. func (fs *fakefs) OpenFile(name string, flags int, mode FileMode) (File, error) {
  305. fs.mut.Lock()
  306. defer fs.mut.Unlock()
  307. if flags&os.O_CREATE == 0 {
  308. return fs.Open(name)
  309. }
  310. dir := filepath.Dir(name)
  311. base := filepath.Base(name)
  312. entry := fs.entryForName(dir)
  313. if entry == nil {
  314. return nil, os.ErrNotExist
  315. } else if entry.entryType != fakeEntryTypeDir {
  316. return nil, errors.New("not a directory")
  317. }
  318. if flags&os.O_EXCL != 0 {
  319. if _, ok := entry.children[base]; ok {
  320. return nil, os.ErrExist
  321. }
  322. }
  323. newEntry := &fakeEntry{
  324. name: base,
  325. mode: mode,
  326. mtime: time.Now(),
  327. }
  328. entry.children[base] = newEntry
  329. return &fakeFile{fakeEntry: newEntry}, nil
  330. }
  331. func (fs *fakefs) ReadSymlink(name string) (string, error) {
  332. fs.mut.Lock()
  333. defer fs.mut.Unlock()
  334. entry := fs.entryForName(name)
  335. if entry == nil {
  336. return "", os.ErrNotExist
  337. } else if entry.entryType != fakeEntryTypeSymlink {
  338. return "", errors.New("not a symlink")
  339. }
  340. return entry.dest, nil
  341. }
  342. func (fs *fakefs) Remove(name string) error {
  343. fs.mut.Lock()
  344. defer fs.mut.Unlock()
  345. entry := fs.entryForName(name)
  346. if entry == nil {
  347. return os.ErrNotExist
  348. }
  349. if len(entry.children) != 0 {
  350. return errors.New("not empty")
  351. }
  352. entry = fs.entryForName(filepath.Dir(name))
  353. delete(entry.children, filepath.Base(name))
  354. return nil
  355. }
  356. func (fs *fakefs) RemoveAll(name string) error {
  357. fs.mut.Lock()
  358. defer fs.mut.Unlock()
  359. entry := fs.entryForName(filepath.Dir(name))
  360. if entry == nil {
  361. return os.ErrNotExist
  362. }
  363. // RemoveAll is easy when the file system uses garbage collection under
  364. // the hood... We even get the correct semantics for open fd:s for free.
  365. delete(entry.children, filepath.Base(name))
  366. return nil
  367. }
  368. func (fs *fakefs) Rename(oldname, newname string) error {
  369. fs.mut.Lock()
  370. defer fs.mut.Unlock()
  371. p0 := fs.entryForName(filepath.Dir(oldname))
  372. if p0 == nil {
  373. return os.ErrNotExist
  374. }
  375. entry := p0.children[filepath.Base(oldname)]
  376. if entry == nil {
  377. return os.ErrNotExist
  378. }
  379. p1 := fs.entryForName(filepath.Dir(newname))
  380. if p1 == nil {
  381. return os.ErrNotExist
  382. }
  383. dst, ok := p1.children[filepath.Base(newname)]
  384. if ok && dst.entryType == fakeEntryTypeDir {
  385. return errors.New("is a directory")
  386. }
  387. p1.children[filepath.Base(newname)] = entry
  388. delete(p0.children, filepath.Base(oldname))
  389. return nil
  390. }
  391. func (fs *fakefs) Stat(name string) (FileInfo, error) {
  392. return fs.Lstat(name)
  393. }
  394. func (fs *fakefs) SymlinksSupported() bool {
  395. return false
  396. }
  397. func (fs *fakefs) Walk(name string, walkFn WalkFunc) error {
  398. return errors.New("not implemented")
  399. }
  400. func (fs *fakefs) Watch(path string, ignore Matcher, ctx context.Context, ignorePerms bool) (<-chan Event, error) {
  401. return nil, ErrWatchNotSupported
  402. }
  403. func (fs *fakefs) Hide(name string) error {
  404. return nil
  405. }
  406. func (fs *fakefs) Unhide(name string) error {
  407. return nil
  408. }
  409. func (fs *fakefs) Glob(pattern string) ([]string, error) {
  410. // gnnh we don't seem to actually require this in practice
  411. return nil, errors.New("not implemented")
  412. }
  413. func (fs *fakefs) Roots() ([]string, error) {
  414. return []string{"/"}, nil
  415. }
  416. func (fs *fakefs) Usage(name string) (Usage, error) {
  417. return Usage{}, errors.New("not implemented")
  418. }
  419. func (fs *fakefs) Type() FilesystemType {
  420. return FilesystemTypeFake
  421. }
  422. func (fs *fakefs) URI() string {
  423. return "fake://" + fs.root.name
  424. }
  425. func (fs *fakefs) SameFile(fi1, fi2 FileInfo) bool {
  426. return fi1.Name() == fi2.Name()
  427. }
  428. // fakeFile is the representation of an open file. We don't care if it's
  429. // opened for reading or writing, it's all good.
  430. type fakeFile struct {
  431. *fakeEntry
  432. mut sync.Mutex
  433. rng io.Reader
  434. seed int64
  435. offset int64
  436. seedOffs int64
  437. }
  438. func (f *fakeFile) Close() error {
  439. return nil
  440. }
  441. func (f *fakeFile) Read(p []byte) (int, error) {
  442. f.mut.Lock()
  443. defer f.mut.Unlock()
  444. return f.readShortAt(p, f.offset)
  445. }
  446. func (f *fakeFile) ReadAt(p []byte, offs int64) (int, error) {
  447. f.mut.Lock()
  448. defer f.mut.Unlock()
  449. // ReadAt is spec:ed to always read a full block unless EOF or failure,
  450. // so we must loop. It's also not supposed to affect the seek position,
  451. // but that would make things annoying or inefficient in terms of
  452. // generating the appropriate RNG etc so I ignore that. In practice we
  453. // currently don't depend on that aspect of it...
  454. var read int
  455. for {
  456. n, err := f.readShortAt(p[read:], offs+int64(read))
  457. read += n
  458. if err != nil {
  459. return read, err
  460. }
  461. if read == len(p) {
  462. return read, nil
  463. }
  464. }
  465. }
  466. func (f *fakeFile) readShortAt(p []byte, offs int64) (int, error) {
  467. // Here be a certain amount of magic... We want to return pseudorandom,
  468. // predictable data so that a read from the same offset in the same file
  469. // always returns the same data. But the RNG is a stream, and reads can
  470. // be random.
  471. //
  472. // We split the file into "blocks" numbered by "seedNo", where each
  473. // block becomes an instantiation of the RNG, seeded with the hash of
  474. // the file number plus the seedNo (block number). We keep the RNG
  475. // around in the hope that the next read will be sequential to this one
  476. // and we can continue reading from the same RNG.
  477. //
  478. // When that's not the case we create a new RNG for the block we are in,
  479. // read as many bytes from it as necessary to get to the right offset,
  480. // and then serve the read from there. We limit the length of the read
  481. // to the end of the block, as another RNG needs to be created to serve
  482. // the next block.
  483. //
  484. // The size of the blocks are a matter of taste... Larger blocks give
  485. // better performance for sequential reads, but worse for random reads
  486. // as we often need to generate and throw away a lot of data at the
  487. // start of the block to serve a given read. 128 KiB blocks fit
  488. // reasonably well with the type of IO Syncthing tends to do.
  489. if f.entryType == fakeEntryTypeDir {
  490. return 0, errors.New("is a directory")
  491. }
  492. if offs >= f.size {
  493. return 0, io.EOF
  494. }
  495. // Lazily calculate our main seed, a simple 64 bit FNV hash our file
  496. // name.
  497. if f.seed == 0 {
  498. hf := fnv.New64()
  499. hf.Write([]byte(f.name))
  500. f.seed = int64(hf.Sum64())
  501. }
  502. // Check whether the read is a continuation of an RNG we already have or
  503. // we need to set up a new one.
  504. seedNo := offs >> randomBlockShift
  505. minOffs := seedNo << randomBlockShift
  506. nextBlockOffs := (seedNo + 1) << randomBlockShift
  507. if f.rng == nil || f.offset != offs || seedNo != f.seedOffs {
  508. // This is not a straight read continuing from a previous one
  509. f.rng = rand.New(rand.NewSource(f.seed + seedNo))
  510. // If the read is not at the start of the block, discard data
  511. // accordingly.
  512. diff := offs - minOffs
  513. if diff > 0 {
  514. lr := io.LimitReader(f.rng, diff)
  515. io.Copy(ioutil.Discard, lr)
  516. }
  517. f.offset = offs
  518. f.seedOffs = seedNo
  519. }
  520. size := len(p)
  521. // Don't read past the end of the file
  522. if offs+int64(size) > f.size {
  523. size = int(f.size - offs)
  524. }
  525. // Don't read across the block boundary
  526. if offs+int64(size) > nextBlockOffs {
  527. size = int(nextBlockOffs - offs)
  528. }
  529. f.offset += int64(size)
  530. return f.rng.Read(p[:size])
  531. }
  532. func (f *fakeFile) Seek(offset int64, whence int) (int64, error) {
  533. f.mut.Lock()
  534. defer f.mut.Unlock()
  535. if f.entryType == fakeEntryTypeDir {
  536. return 0, errors.New("is a directory")
  537. }
  538. f.rng = nil
  539. switch whence {
  540. case io.SeekCurrent:
  541. f.offset += offset
  542. case io.SeekEnd:
  543. f.offset = f.size - offset
  544. case io.SeekStart:
  545. f.offset = offset
  546. }
  547. if f.offset < 0 {
  548. f.offset = 0
  549. return f.offset, errors.New("seek before start")
  550. }
  551. if f.offset > f.size {
  552. f.offset = f.size
  553. return f.offset, io.EOF
  554. }
  555. return f.offset, nil
  556. }
  557. func (f *fakeFile) Write(p []byte) (int, error) {
  558. return f.WriteAt(p, f.offset)
  559. }
  560. func (f *fakeFile) WriteAt(p []byte, off int64) (int, error) {
  561. f.mut.Lock()
  562. defer f.mut.Unlock()
  563. if f.entryType == fakeEntryTypeDir {
  564. return 0, errors.New("is a directory")
  565. }
  566. f.rng = nil
  567. f.offset = off + int64(len(p))
  568. if f.offset > f.size {
  569. f.size = f.offset
  570. }
  571. return len(p), nil
  572. }
  573. func (f *fakeFile) Name() string {
  574. return f.name
  575. }
  576. func (f *fakeFile) Truncate(size int64) error {
  577. f.mut.Lock()
  578. defer f.mut.Unlock()
  579. f.rng = nil
  580. f.size = size
  581. if f.offset > size {
  582. f.offset = size
  583. }
  584. return nil
  585. }
  586. func (f *fakeFile) Stat() (FileInfo, error) {
  587. return &fakeFileInfo{*f.fakeEntry}, nil
  588. }
  589. func (f *fakeFile) Sync() error {
  590. return nil
  591. }
  592. // fakeFileInfo is the stat result.
  593. type fakeFileInfo struct {
  594. fakeEntry // intentionally a copy of the struct
  595. }
  596. func (f *fakeFileInfo) Name() string {
  597. return f.name
  598. }
  599. func (f *fakeFileInfo) Mode() FileMode {
  600. return f.mode
  601. }
  602. func (f *fakeFileInfo) Size() int64 {
  603. return f.size
  604. }
  605. func (f *fakeFileInfo) ModTime() time.Time {
  606. return f.mtime
  607. }
  608. func (f *fakeFileInfo) IsDir() bool {
  609. return f.entryType == fakeEntryTypeDir
  610. }
  611. func (f *fakeFileInfo) IsRegular() bool {
  612. return f.entryType == fakeEntryTypeFile
  613. }
  614. func (f *fakeFileInfo) IsSymlink() bool {
  615. return f.entryType == fakeEntryTypeSymlink
  616. }
  617. func (f *fakeFileInfo) Owner() int {
  618. return f.uid
  619. }
  620. func (f *fakeFileInfo) Group() int {
  621. return f.gid
  622. }