diff options
| author | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 21:21:58 +0200 |
|---|---|---|
| committer | Lukasz Kasprzak <lukas@labunix.xyz> | 2026-09-14 21:21:58 +0200 |
| commit | a92ce86c0dff7ce8f1de4113f20edd76c249371e (patch) | |
| tree | c41a4f01c610a49cf3b60e9e9e658d6cb8b94b33 /internal/journal/journal.go | |
| parent | 3890b27b5a68f5521f4f0a234cebf528744356ee (diff) | |
| download | krino-a92ce86c0dff7ce8f1de4113f20edd76c249371e.tar.gz krino-a92ce86c0dff7ce8f1de4113f20edd76c249371e.zip | |
plan 9: the log restores a missing final newline
Diffstat (limited to 'internal/journal/journal.go')
| -rw-r--r-- | internal/journal/journal.go | 26 |
1 files changed, 25 insertions, 1 deletions
diff --git a/internal/journal/journal.go b/internal/journal/journal.go index 465cd51..085c88a 100644 --- a/internal/journal/journal.go +++ b/internal/journal/journal.go @@ -54,13 +54,37 @@ func Open(path string) (*Writer, error) { if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return nil, fmt.Errorf("journal: %w", err) } - f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) + f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o644) if err != nil { return nil, fmt.Errorf("journal: %w", err) } + if err := endWithNewline(f); err != nil { + f.Close() + return nil, fmt.Errorf("journal: %w", err) + } return &Writer{f: f}, nil } +// endWithNewline restores a missing final newline. A crash can cut the log's +// last line short (spec ยง15.1: krino survives a truncated line); appending +// straight after the fragment would glue the next run's first line onto it, +// and that run could then never be undone. +func endWithNewline(f *os.File) error { + fi, err := f.Stat() + if err != nil || fi.Size() == 0 { + return err + } + last := make([]byte, 1) + if _, err := f.ReadAt(last, fi.Size()-1); err != nil { + return err + } + if last[0] == '\n' { + return nil + } + _, err = f.Write([]byte{'\n'}) + return err +} + // Append writes e as one line and flushes it before returning. The whole // line is written with a single Write call so that two concurrent runs // appending to the same file cannot interleave a partial line. |
