Line data Source code
1 : // Copyright 2012 The LevelDB-Go and Pebble Authors. All rights reserved. Use
2 : // of this source code is governed by a BSD-style license that can be found in
3 : // the LICENSE file.
4 :
5 : package vfs // import "github.com/cockroachdb/pebble/vfs"
6 :
7 : import (
8 : "bytes"
9 : "fmt"
10 : "io"
11 : "os"
12 : "path"
13 : "sort"
14 : "strings"
15 : "sync"
16 : "sync/atomic"
17 : "time"
18 :
19 : "github.com/cockroachdb/errors"
20 : "github.com/cockroachdb/errors/oserror"
21 : "github.com/cockroachdb/pebble/internal/invariants"
22 : )
23 :
24 : const sep = "/"
25 :
26 : // NewMem returns a new memory-backed FS implementation.
27 1 : func NewMem() *MemFS {
28 1 : return &MemFS{
29 1 : root: newRootMemNode(),
30 1 : }
31 1 : }
32 :
33 : // NewStrictMem returns a "strict" memory-backed FS implementation. The behaviour is strict wrt
34 : // needing a Sync() call on files or directories for the state changes to be finalized. Any
35 : // changes that are not finalized are visible to reads until MemFS.ResetToSyncedState() is called,
36 : // at which point they are discarded and no longer visible.
37 : //
38 : // Expected usage:
39 : //
40 : // strictFS := NewStrictMem()
41 : // db := Open(..., &Options{FS: strictFS})
42 : // // Do and commit various operations.
43 : // ...
44 : // // Prevent any more changes to finalized state.
45 : // strictFS.SetIgnoreSyncs(true)
46 : // // This will finish any ongoing background flushes, compactions but none of these writes will
47 : // // be finalized since syncs are being ignored.
48 : // db.Close()
49 : // // Discard unsynced state.
50 : // strictFS.ResetToSyncedState()
51 : // // Allow changes to finalized state.
52 : // strictFS.SetIgnoreSyncs(false)
53 : // // Open the DB. This DB should have the same state as if the earlier strictFS operations and
54 : // // db.Close() were not called.
55 : // db := Open(..., &Options{FS: strictFS})
56 1 : func NewStrictMem() *MemFS {
57 1 : return &MemFS{
58 1 : root: newRootMemNode(),
59 1 : strict: true,
60 1 : }
61 1 : }
62 :
63 : // NewMemFile returns a memory-backed File implementation. The memory-backed
64 : // file takes ownership of data.
65 1 : func NewMemFile(data []byte) File {
66 1 : n := &memNode{}
67 1 : n.refs.Store(1)
68 1 : n.mu.data = data
69 1 : n.mu.modTime = time.Now()
70 1 : return &memFile{
71 1 : n: n,
72 1 : read: true,
73 1 : }
74 1 : }
75 :
76 : // MemFS implements FS.
77 : type MemFS struct {
78 : mu sync.Mutex
79 : root *memNode
80 :
81 : strict bool
82 : ignoreSyncs bool
83 : // Windows has peculiar semantics with respect to hard links and deleting
84 : // open files. In tests meant to exercise this behavior, this flag can be
85 : // set to error if removing an open file.
86 : windowsSemantics bool
87 : }
88 :
89 : var _ FS = &MemFS{}
90 :
91 : // UseWindowsSemantics configures whether the MemFS implements Windows-style
92 : // semantics, in particular with respect to whether any of an open file's links
93 : // may be removed. Windows semantics default to off.
94 1 : func (y *MemFS) UseWindowsSemantics(windowsSemantics bool) {
95 1 : y.mu.Lock()
96 1 : defer y.mu.Unlock()
97 1 : y.windowsSemantics = windowsSemantics
98 1 : }
99 :
100 : // String dumps the contents of the MemFS.
101 1 : func (y *MemFS) String() string {
102 1 : y.mu.Lock()
103 1 : defer y.mu.Unlock()
104 1 :
105 1 : s := new(bytes.Buffer)
106 1 : y.root.dump(s, 0)
107 1 : return s.String()
108 1 : }
109 :
110 : // SetIgnoreSyncs sets the MemFS.ignoreSyncs field. See the usage comment with NewStrictMem() for
111 : // details.
112 1 : func (y *MemFS) SetIgnoreSyncs(ignoreSyncs bool) {
113 1 : y.mu.Lock()
114 1 : if !y.strict {
115 0 : // noop
116 0 : return
117 0 : }
118 1 : y.ignoreSyncs = ignoreSyncs
119 1 : y.mu.Unlock()
120 : }
121 :
122 : // ResetToSyncedState discards state in the FS that is not synced. See the usage comment with
123 : // NewStrictMem() for details.
124 1 : func (y *MemFS) ResetToSyncedState() {
125 1 : if !y.strict {
126 0 : // noop
127 0 : return
128 0 : }
129 1 : y.mu.Lock()
130 1 : y.root.resetToSyncedState()
131 1 : y.mu.Unlock()
132 : }
133 :
134 : // walk walks the directory tree for the fullname, calling f at each step. If
135 : // f returns an error, the walk will be aborted and return that same error.
136 : //
137 : // Each walk is atomic: y's mutex is held for the entire operation, including
138 : // all calls to f.
139 : //
140 : // dir is the directory at that step, frag is the name fragment, and final is
141 : // whether it is the final step. For example, walking "/foo/bar/x" will result
142 : // in 3 calls to f:
143 : // - "/", "foo", false
144 : // - "/foo/", "bar", false
145 : // - "/foo/bar/", "x", true
146 : //
147 : // Similarly, walking "/y/z/", with a trailing slash, will result in 3 calls to f:
148 : // - "/", "y", false
149 : // - "/y/", "z", false
150 : // - "/y/z/", "", true
151 1 : func (y *MemFS) walk(fullname string, f func(dir *memNode, frag string, final bool) error) error {
152 1 : y.mu.Lock()
153 1 : defer y.mu.Unlock()
154 1 :
155 1 : // For memfs, the current working directory is the same as the root directory,
156 1 : // so we strip off any leading "/"s to make fullname a relative path, and
157 1 : // the walk starts at y.root.
158 1 : for len(fullname) > 0 && fullname[0] == sep[0] {
159 1 : fullname = fullname[1:]
160 1 : }
161 1 : dir := y.root
162 1 :
163 1 : for {
164 1 : frag, remaining := fullname, ""
165 1 : i := strings.IndexRune(fullname, rune(sep[0]))
166 1 : final := i < 0
167 1 : if !final {
168 1 : frag, remaining = fullname[:i], fullname[i+1:]
169 1 : for len(remaining) > 0 && remaining[0] == sep[0] {
170 0 : remaining = remaining[1:]
171 0 : }
172 : }
173 1 : if err := f(dir, frag, final); err != nil {
174 1 : return err
175 1 : }
176 1 : if final {
177 1 : break
178 : }
179 1 : child := dir.children[frag]
180 1 : if child == nil {
181 1 : return &os.PathError{
182 1 : Op: "open",
183 1 : Path: fullname,
184 1 : Err: oserror.ErrNotExist,
185 1 : }
186 1 : }
187 1 : if !child.isDir {
188 1 : return &os.PathError{
189 1 : Op: "open",
190 1 : Path: fullname,
191 1 : Err: errors.New("not a directory"),
192 1 : }
193 1 : }
194 1 : dir, fullname = child, remaining
195 : }
196 1 : return nil
197 : }
198 :
199 : // Create implements FS.Create.
200 1 : func (y *MemFS) Create(fullname string) (File, error) {
201 1 : var ret *memFile
202 1 : err := y.walk(fullname, func(dir *memNode, frag string, final bool) error {
203 1 : if final {
204 1 : if frag == "" {
205 0 : return errors.New("pebble/vfs: empty file name")
206 0 : }
207 1 : n := &memNode{name: frag}
208 1 : dir.children[frag] = n
209 1 : ret = &memFile{
210 1 : n: n,
211 1 : fs: y,
212 1 : read: true,
213 1 : write: true,
214 1 : }
215 : }
216 1 : return nil
217 : })
218 1 : if err != nil {
219 1 : return nil, err
220 1 : }
221 1 : ret.n.refs.Add(1)
222 1 : return ret, nil
223 : }
224 :
225 : // Link implements FS.Link.
226 1 : func (y *MemFS) Link(oldname, newname string) error {
227 1 : var n *memNode
228 1 : err := y.walk(oldname, func(dir *memNode, frag string, final bool) error {
229 1 : if final {
230 1 : if frag == "" {
231 0 : return errors.New("pebble/vfs: empty file name")
232 0 : }
233 1 : n = dir.children[frag]
234 : }
235 1 : return nil
236 : })
237 1 : if err != nil {
238 0 : return err
239 0 : }
240 1 : if n == nil {
241 1 : return &os.LinkError{
242 1 : Op: "link",
243 1 : Old: oldname,
244 1 : New: newname,
245 1 : Err: oserror.ErrNotExist,
246 1 : }
247 1 : }
248 1 : return y.walk(newname, func(dir *memNode, frag string, final bool) error {
249 1 : if final {
250 1 : if frag == "" {
251 0 : return errors.New("pebble/vfs: empty file name")
252 0 : }
253 1 : if _, ok := dir.children[frag]; ok {
254 1 : return &os.LinkError{
255 1 : Op: "link",
256 1 : Old: oldname,
257 1 : New: newname,
258 1 : Err: oserror.ErrExist,
259 1 : }
260 1 : }
261 1 : dir.children[frag] = n
262 : }
263 1 : return nil
264 : })
265 : }
266 :
267 1 : func (y *MemFS) open(fullname string, openForWrite bool) (File, error) {
268 1 : var ret *memFile
269 1 : err := y.walk(fullname, func(dir *memNode, frag string, final bool) error {
270 1 : if final {
271 1 : if frag == "" {
272 1 : ret = &memFile{
273 1 : n: dir,
274 1 : fs: y,
275 1 : }
276 1 : return nil
277 1 : }
278 1 : if n := dir.children[frag]; n != nil {
279 1 : ret = &memFile{
280 1 : n: n,
281 1 : fs: y,
282 1 : read: true,
283 1 : write: openForWrite,
284 1 : }
285 1 : }
286 : }
287 1 : return nil
288 : })
289 1 : if err != nil {
290 1 : return nil, err
291 1 : }
292 1 : if ret == nil {
293 1 : return nil, &os.PathError{
294 1 : Op: "open",
295 1 : Path: fullname,
296 1 : Err: oserror.ErrNotExist,
297 1 : }
298 1 : }
299 1 : ret.n.refs.Add(1)
300 1 : return ret, nil
301 : }
302 :
303 : // Open implements FS.Open.
304 1 : func (y *MemFS) Open(fullname string, opts ...OpenOption) (File, error) {
305 1 : return y.open(fullname, false /* openForWrite */)
306 1 : }
307 :
308 : // OpenReadWrite implements FS.OpenReadWrite.
309 1 : func (y *MemFS) OpenReadWrite(fullname string, opts ...OpenOption) (File, error) {
310 1 : f, err := y.open(fullname, true /* openForWrite */)
311 1 : pathErr, ok := err.(*os.PathError)
312 1 : if ok && pathErr.Err == oserror.ErrNotExist {
313 1 : return y.Create(fullname)
314 1 : }
315 1 : return f, err
316 : }
317 :
318 : // OpenDir implements FS.OpenDir.
319 1 : func (y *MemFS) OpenDir(fullname string) (File, error) {
320 1 : return y.open(fullname, false /* openForWrite */)
321 1 : }
322 :
323 : // Remove implements FS.Remove.
324 1 : func (y *MemFS) Remove(fullname string) error {
325 1 : return y.walk(fullname, func(dir *memNode, frag string, final bool) error {
326 1 : if final {
327 1 : if frag == "" {
328 0 : return errors.New("pebble/vfs: empty file name")
329 0 : }
330 1 : child, ok := dir.children[frag]
331 1 : if !ok {
332 1 : return oserror.ErrNotExist
333 1 : }
334 1 : if y.windowsSemantics {
335 1 : // Disallow removal of open files/directories which implements
336 1 : // Windows semantics. This ensures that we don't regress in the
337 1 : // ordering of operations and try to remove a file while it is
338 1 : // still open.
339 1 : if n := child.refs.Load(); n > 0 {
340 1 : return oserror.ErrInvalid
341 1 : }
342 : }
343 1 : if len(child.children) > 0 {
344 1 : return errNotEmpty
345 1 : }
346 1 : delete(dir.children, frag)
347 : }
348 1 : return nil
349 : })
350 : }
351 :
352 : // RemoveAll implements FS.RemoveAll.
353 1 : func (y *MemFS) RemoveAll(fullname string) error {
354 1 : err := y.walk(fullname, func(dir *memNode, frag string, final bool) error {
355 1 : if final {
356 1 : if frag == "" {
357 0 : return errors.New("pebble/vfs: empty file name")
358 0 : }
359 1 : _, ok := dir.children[frag]
360 1 : if !ok {
361 1 : return nil
362 1 : }
363 1 : delete(dir.children, frag)
364 : }
365 1 : return nil
366 : })
367 : // Match os.RemoveAll which returns a nil error even if the parent
368 : // directories don't exist.
369 1 : if oserror.IsNotExist(err) {
370 1 : err = nil
371 1 : }
372 1 : return err
373 : }
374 :
375 : // Rename implements FS.Rename.
376 1 : func (y *MemFS) Rename(oldname, newname string) error {
377 1 : var n *memNode
378 1 : err := y.walk(oldname, func(dir *memNode, frag string, final bool) error {
379 1 : if final {
380 1 : if frag == "" {
381 0 : return errors.New("pebble/vfs: empty file name")
382 0 : }
383 1 : n = dir.children[frag]
384 1 : delete(dir.children, frag)
385 : }
386 1 : return nil
387 : })
388 1 : if err != nil {
389 0 : return err
390 0 : }
391 1 : if n == nil {
392 1 : return &os.PathError{
393 1 : Op: "open",
394 1 : Path: oldname,
395 1 : Err: oserror.ErrNotExist,
396 1 : }
397 1 : }
398 1 : return y.walk(newname, func(dir *memNode, frag string, final bool) error {
399 1 : if final {
400 1 : if frag == "" {
401 0 : return errors.New("pebble/vfs: empty file name")
402 0 : }
403 1 : dir.children[frag] = n
404 1 : n.name = frag
405 : }
406 1 : return nil
407 : })
408 : }
409 :
410 : // ReuseForWrite implements FS.ReuseForWrite.
411 1 : func (y *MemFS) ReuseForWrite(oldname, newname string) (File, error) {
412 1 : if err := y.Rename(oldname, newname); err != nil {
413 1 : return nil, err
414 1 : }
415 1 : f, err := y.Open(newname)
416 1 : if err != nil {
417 0 : return nil, err
418 0 : }
419 1 : y.mu.Lock()
420 1 : defer y.mu.Unlock()
421 1 :
422 1 : mf := f.(*memFile)
423 1 : mf.read = false
424 1 : mf.write = true
425 1 : return f, nil
426 : }
427 :
428 : // MkdirAll implements FS.MkdirAll.
429 1 : func (y *MemFS) MkdirAll(dirname string, perm os.FileMode) error {
430 1 : return y.walk(dirname, func(dir *memNode, frag string, final bool) error {
431 1 : if frag == "" {
432 1 : if final {
433 1 : return nil
434 1 : }
435 0 : return errors.New("pebble/vfs: empty file name")
436 : }
437 1 : child := dir.children[frag]
438 1 : if child == nil {
439 1 : dir.children[frag] = &memNode{
440 1 : name: frag,
441 1 : children: make(map[string]*memNode),
442 1 : isDir: true,
443 1 : }
444 1 : return nil
445 1 : }
446 1 : if !child.isDir {
447 0 : return &os.PathError{
448 0 : Op: "open",
449 0 : Path: dirname,
450 0 : Err: errors.New("not a directory"),
451 0 : }
452 0 : }
453 1 : return nil
454 : })
455 : }
456 :
457 : // Lock implements FS.Lock.
458 1 : func (y *MemFS) Lock(fullname string) (io.Closer, error) {
459 1 : // FS.Lock excludes other processes, but other processes cannot see this
460 1 : // process' memory. We translate Lock into Create so that have the normal
461 1 : // detection of non-existent directory paths.
462 1 : return y.Create(fullname)
463 1 : }
464 :
465 : // List implements FS.List.
466 1 : func (y *MemFS) List(dirname string) ([]string, error) {
467 1 : if !strings.HasSuffix(dirname, sep) {
468 1 : dirname += sep
469 1 : }
470 1 : var ret []string
471 1 : err := y.walk(dirname, func(dir *memNode, frag string, final bool) error {
472 1 : if final {
473 1 : if frag != "" {
474 0 : panic("unreachable")
475 : }
476 1 : ret = make([]string, 0, len(dir.children))
477 1 : for s := range dir.children {
478 1 : ret = append(ret, s)
479 1 : }
480 : }
481 1 : return nil
482 : })
483 1 : return ret, err
484 : }
485 :
486 : // Stat implements FS.Stat.
487 1 : func (y *MemFS) Stat(name string) (os.FileInfo, error) {
488 1 : f, err := y.Open(name)
489 1 : if err != nil {
490 1 : if pe, ok := err.(*os.PathError); ok {
491 1 : pe.Op = "stat"
492 1 : }
493 1 : return nil, err
494 : }
495 1 : defer f.Close()
496 1 : return f.Stat()
497 : }
498 :
499 : // PathBase implements FS.PathBase.
500 1 : func (*MemFS) PathBase(p string) string {
501 1 : // Note that MemFS uses forward slashes for its separator, hence the use of
502 1 : // path.Base, not filepath.Base.
503 1 : return path.Base(p)
504 1 : }
505 :
506 : // PathJoin implements FS.PathJoin.
507 1 : func (*MemFS) PathJoin(elem ...string) string {
508 1 : // Note that MemFS uses forward slashes for its separator, hence the use of
509 1 : // path.Join, not filepath.Join.
510 1 : return path.Join(elem...)
511 1 : }
512 :
513 : // PathDir implements FS.PathDir.
514 1 : func (*MemFS) PathDir(p string) string {
515 1 : // Note that MemFS uses forward slashes for its separator, hence the use of
516 1 : // path.Dir, not filepath.Dir.
517 1 : return path.Dir(p)
518 1 : }
519 :
520 : // GetDiskUsage implements FS.GetDiskUsage.
521 1 : func (*MemFS) GetDiskUsage(string) (DiskUsage, error) {
522 1 : return DiskUsage{}, ErrUnsupported
523 1 : }
524 :
525 : // memNode holds a file's data or a directory's children, and implements os.FileInfo.
526 : type memNode struct {
527 : name string
528 : isDir bool
529 : refs atomic.Int32
530 :
531 : // Mutable state.
532 : // - For a file: data, syncedDate, modTime: A file is only being mutated by a single goroutine,
533 : // but there can be concurrent readers e.g. DB.Checkpoint() which can read WAL or MANIFEST
534 : // files that are being written to. Additionally Sync() calls can be concurrent with writing.
535 : // - For a directory: children and syncedChildren. Concurrent writes are possible, and
536 : // these are protected using MemFS.mu.
537 : mu struct {
538 : sync.Mutex
539 : data []byte
540 : syncedData []byte
541 : modTime time.Time
542 : }
543 :
544 : children map[string]*memNode
545 : syncedChildren map[string]*memNode
546 : }
547 :
548 1 : func newRootMemNode() *memNode {
549 1 : return &memNode{
550 1 : name: "/", // set the name to match what file systems do
551 1 : children: make(map[string]*memNode),
552 1 : isDir: true,
553 1 : }
554 1 : }
555 :
556 1 : func (f *memNode) IsDir() bool {
557 1 : return f.isDir
558 1 : }
559 :
560 0 : func (f *memNode) ModTime() time.Time {
561 0 : f.mu.Lock()
562 0 : defer f.mu.Unlock()
563 0 : return f.mu.modTime
564 0 : }
565 :
566 0 : func (f *memNode) Mode() os.FileMode {
567 0 : if f.isDir {
568 0 : return os.ModeDir | 0755
569 0 : }
570 0 : return 0755
571 : }
572 :
573 1 : func (f *memNode) Name() string {
574 1 : return f.name
575 1 : }
576 :
577 1 : func (f *memNode) Size() int64 {
578 1 : f.mu.Lock()
579 1 : defer f.mu.Unlock()
580 1 : return int64(len(f.mu.data))
581 1 : }
582 :
583 0 : func (f *memNode) Sys() interface{} {
584 0 : return nil
585 0 : }
586 :
587 1 : func (f *memNode) dump(w *bytes.Buffer, level int) {
588 1 : if f.isDir {
589 1 : w.WriteString(" ")
590 1 : } else {
591 1 : f.mu.Lock()
592 1 : fmt.Fprintf(w, "%8d ", len(f.mu.data))
593 1 : f.mu.Unlock()
594 1 : }
595 1 : for i := 0; i < level; i++ {
596 1 : w.WriteString(" ")
597 1 : }
598 1 : w.WriteString(f.name)
599 1 : if !f.isDir {
600 1 : w.WriteByte('\n')
601 1 : return
602 1 : }
603 1 : if level > 0 { // deal with the fact that the root's name is already "/"
604 1 : w.WriteByte(sep[0])
605 1 : }
606 1 : w.WriteByte('\n')
607 1 : names := make([]string, 0, len(f.children))
608 1 : for name := range f.children {
609 1 : names = append(names, name)
610 1 : }
611 1 : sort.Strings(names)
612 1 : for _, name := range names {
613 1 : f.children[name].dump(w, level+1)
614 1 : }
615 : }
616 :
617 1 : func (f *memNode) resetToSyncedState() {
618 1 : if f.isDir {
619 1 : f.children = make(map[string]*memNode)
620 1 : for k, v := range f.syncedChildren {
621 1 : f.children[k] = v
622 1 : }
623 1 : for _, v := range f.children {
624 1 : v.resetToSyncedState()
625 1 : }
626 1 : } else {
627 1 : f.mu.Lock()
628 1 : f.mu.data = append([]byte(nil), f.mu.syncedData...)
629 1 : f.mu.Unlock()
630 1 : }
631 : }
632 :
633 : // memFile is a reader or writer of a node's data, and implements File.
634 : type memFile struct {
635 : n *memNode
636 : fs *MemFS // nil for a standalone memFile
637 : rpos int
638 : wpos int
639 : read, write bool
640 : }
641 :
642 : var _ File = (*memFile)(nil)
643 :
644 1 : func (f *memFile) Close() error {
645 1 : if n := f.n.refs.Add(-1); n < 0 {
646 0 : panic(fmt.Sprintf("pebble: close of unopened file: %d", n))
647 : }
648 1 : f.n = nil
649 1 : return nil
650 : }
651 :
652 1 : func (f *memFile) Read(p []byte) (int, error) {
653 1 : if !f.read {
654 0 : return 0, errors.New("pebble/vfs: file was not opened for reading")
655 0 : }
656 1 : if f.n.isDir {
657 0 : return 0, errors.New("pebble/vfs: cannot read a directory")
658 0 : }
659 1 : f.n.mu.Lock()
660 1 : defer f.n.mu.Unlock()
661 1 : if f.rpos >= len(f.n.mu.data) {
662 1 : return 0, io.EOF
663 1 : }
664 1 : n := copy(p, f.n.mu.data[f.rpos:])
665 1 : f.rpos += n
666 1 : return n, nil
667 : }
668 :
669 1 : func (f *memFile) ReadAt(p []byte, off int64) (int, error) {
670 1 : if !f.read {
671 0 : return 0, errors.New("pebble/vfs: file was not opened for reading")
672 0 : }
673 1 : if f.n.isDir {
674 0 : return 0, errors.New("pebble/vfs: cannot read a directory")
675 0 : }
676 1 : f.n.mu.Lock()
677 1 : defer f.n.mu.Unlock()
678 1 : if off >= int64(len(f.n.mu.data)) {
679 0 : return 0, io.EOF
680 0 : }
681 1 : n := copy(p, f.n.mu.data[off:])
682 1 : if n < len(p) {
683 1 : return n, io.EOF
684 1 : }
685 1 : return n, nil
686 : }
687 :
688 1 : func (f *memFile) Write(p []byte) (int, error) {
689 1 : if !f.write {
690 0 : return 0, errors.New("pebble/vfs: file was not created for writing")
691 0 : }
692 1 : if f.n.isDir {
693 0 : return 0, errors.New("pebble/vfs: cannot write a directory")
694 0 : }
695 1 : f.n.mu.Lock()
696 1 : defer f.n.mu.Unlock()
697 1 : f.n.mu.modTime = time.Now()
698 1 : if f.wpos+len(p) <= len(f.n.mu.data) {
699 1 : n := copy(f.n.mu.data[f.wpos:f.wpos+len(p)], p)
700 1 : if n != len(p) {
701 0 : panic("stuff")
702 : }
703 1 : } else {
704 1 : f.n.mu.data = append(f.n.mu.data[:f.wpos], p...)
705 1 : }
706 1 : f.wpos += len(p)
707 1 :
708 1 : if invariants.Enabled {
709 0 : // Mutate the input buffer to flush out bugs in Pebble which expect the
710 0 : // input buffer to be unmodified.
711 0 : for i := range p {
712 0 : p[i] ^= 0xff
713 0 : }
714 : }
715 1 : return len(p), nil
716 : }
717 :
718 1 : func (f *memFile) WriteAt(p []byte, ofs int64) (int, error) {
719 1 : if !f.write {
720 0 : return 0, errors.New("pebble/vfs: file was not created for writing")
721 0 : }
722 1 : if f.n.isDir {
723 0 : return 0, errors.New("pebble/vfs: cannot write a directory")
724 0 : }
725 1 : f.n.mu.Lock()
726 1 : defer f.n.mu.Unlock()
727 1 : f.n.mu.modTime = time.Now()
728 1 :
729 1 : for len(f.n.mu.data) < int(ofs)+len(p) {
730 1 : f.n.mu.data = append(f.n.mu.data, 0)
731 1 : }
732 :
733 1 : n := copy(f.n.mu.data[int(ofs):int(ofs)+len(p)], p)
734 1 : if n != len(p) {
735 0 : panic("stuff")
736 : }
737 :
738 1 : return len(p), nil
739 : }
740 :
741 1 : func (f *memFile) Prefetch(offset int64, length int64) error { return nil }
742 1 : func (f *memFile) Preallocate(offset, length int64) error { return nil }
743 :
744 1 : func (f *memFile) Stat() (os.FileInfo, error) {
745 1 : return f.n, nil
746 1 : }
747 :
748 1 : func (f *memFile) Sync() error {
749 1 : if f.fs != nil && f.fs.strict {
750 1 : f.fs.mu.Lock()
751 1 : defer f.fs.mu.Unlock()
752 1 : if f.fs.ignoreSyncs {
753 1 : return nil
754 1 : }
755 1 : if f.n.isDir {
756 1 : f.n.syncedChildren = make(map[string]*memNode)
757 1 : for k, v := range f.n.children {
758 1 : f.n.syncedChildren[k] = v
759 1 : }
760 1 : } else {
761 1 : f.n.mu.Lock()
762 1 : f.n.mu.syncedData = append([]byte(nil), f.n.mu.data...)
763 1 : f.n.mu.Unlock()
764 1 : }
765 : }
766 1 : return nil
767 : }
768 :
769 1 : func (f *memFile) SyncData() error {
770 1 : return f.Sync()
771 1 : }
772 :
773 0 : func (f *memFile) SyncTo(length int64) (fullSync bool, err error) {
774 0 : // NB: This SyncTo implementation lies, with its return values claiming it
775 0 : // synced the data up to `length`. When fullSync=false, SyncTo provides no
776 0 : // durability guarantees, so this can help surface bugs where we improperly
777 0 : // rely on SyncTo providing durability.
778 0 : return false, nil
779 0 : }
780 :
781 1 : func (f *memFile) Fd() uintptr {
782 1 : return InvalidFd
783 1 : }
784 :
785 : // Flush is a no-op and present only to prevent buffering at higher levels
786 : // (e.g. it prevents sstable.Writer from using a bufio.Writer).
787 1 : func (f *memFile) Flush() error {
788 1 : return nil
789 1 : }
|