fakefs.go 23 KB

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