| index e4a39b9..bc3667c 100644 |
| --- a/internal/provisioner/floor_linux.go |
| +++ b/internal/provisioner/floor_linux.go |
| @@ -15,6 +15,7 @@ import ( |
| family "amadan.net/rastrillo/rastrillo/crypto" |
| "github.com/go-json-experiment/json" |
| "github.com/go-json-experiment/json/jsontext" |
| + "golang.org/x/sys/unix" |
| ) |
| |
| const floorContext = "oficina/provisioner/floor/v1" |
| @@ -109,7 +110,10 @@ func (f *nativeFloor) initialize(k *family.Keypair) error { |
| if e != nil { |
| return e |
| } |
| - return f.dir.create(floorName(0), raw) |
| + if e = f.dir.create(floorName(0), raw); e != nil { |
| + return e |
| + } |
| + return f.updateMarker(floorPoint{}) |
| } |
| func (f *nativeFloor) names() ([]string, error) { |
| // Open a fresh directory description: a retained Readdir offset must never |
| @@ -127,16 +131,16 @@ func (f *nativeFloor) names() ([]string, error) { |
| if e != nil || !os.SameFile(oldInfo, newInfo) { |
| return nil, ErrRefused |
| } |
| - entries, e := fd.file.Readdirnames(maxRecords + 3) |
| + entries, e := fd.file.Readdirnames(maxRecords + 5) |
| if e != nil && e != io.EOF { |
| return nil, ErrUnavailable |
| } |
| - if len(entries) > maxRecords+2 { |
| + if len(entries) > maxRecords+4 { |
| return nil, ErrRefused |
| } |
| names := make([]string, 0, len(entries)) |
| for _, name := range entries { |
| - if name == "lock" { |
| + if name == "lock" || name == "head" || name == "head.tmp" { |
| continue |
| } |
| if len(name) != 25 || name[20:] != ".json" { |
| @@ -207,5 +211,27 @@ func (f *nativeFloor) advance(k *family.Keypair, expected, next floorPoint) erro |
| if e != nil { |
| return e |
| } |
| - return f.dir.create(floorName(next.Sequence), raw) |
| + if e = f.dir.create(floorName(next.Sequence), raw); e != nil { |
| + return e |
| + } |
| + return f.updateMarker(next) |
| +} |
| + |
| +// The marker is an optimization only. Every open scans signed immutable records, |
| +// including a durable newer record written before a marker-update crash. |
| +func (f *nativeFloor) updateMarker(point floorPoint) error { |
| + raw, e := canonicalJSON(point) |
| + if e != nil { |
| + return e |
| + } |
| + if e = f.dir.create("head.tmp", raw); e != nil { |
| + existing, readErr := f.dir.read("head.tmp", 8192) |
| + if readErr != nil || !bytes.Equal(existing, raw) { |
| + return ErrRefused |
| + } |
| + } |
| + if unix.Renameat(int(f.dir.file.Fd()), "head.tmp", int(f.dir.file.Fd()), "head") != nil || f.dir.file.Sync() != nil { |
| + return ErrUnavailable |
| + } |
| + return nil |
| } |