fakefs.go 15 KB

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