fakefs.go 23 KB

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