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