summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Carbonneaux2021-11-16 22:33:57 +0100
committerQuentin Carbonneaux2021-11-16 22:33:57 +0100
commitd0b6239c8be29b244d27cc4bb860100b0c46d316 (patch)
tree277fb9515c5d6530d3ee94925f9d48f0bf7b2030
parentaf1173599b455eb722c47c8617488909100abf2b (diff)
[mx] dir v1 / better sync / error refactor
-rw-r--r--memex/diff.go4
-rw-r--r--memex/main.go270
-rw-r--r--memex/pack.go68
-rw-r--r--memex/repo.go14
-rw-r--r--memex/revs.go39
-rw-r--r--memex/sdar.go128
6 files changed, 250 insertions, 273 deletions
diff --git a/memex/diff.go b/memex/diff.go
index 7608cc6..b29bc7b 100644
--- a/memex/diff.go
+++ b/memex/diff.go
@@ -212,8 +212,8 @@ func WalkTo(w Walker, p string) (Walker, error) {
if err != nil {
return nil, err
}
- i := 0
- for ; i < len(ws); i++ {
+ var i int
+ for i = 0; i < len(ws); i++ {
if ws[i].Here().Name == lhs {
break
}
diff --git a/memex/main.go b/memex/main.go
index 1ccda11..01da47d 100644
--- a/memex/main.go
+++ b/memex/main.go
@@ -2,6 +2,7 @@ package main
import (
"archive/tar"
+ "errors"
"flag"
"fmt"
"os"
@@ -57,9 +58,15 @@ func (k EntryKind) String() string {
}
func errf(msg string, objs ...interface{}) {
- fmt.Fprintf(os.Stderr, "error: "+msg+"\n", objs...)
+ fmt.Fprintf(os.Stderr, msg+"\n", objs...)
}
+func fail(msg string, objs ...interface{}) {
+ errf(msg, objs...)
+ os.Exit(2)
+}
+func faile(e error) { fail("%v", e) }
+
/* Modifies a revision, scanned using the Walker argument, to add
* the file system data selected in paths (the paths are relative
* to the walker's current path)
@@ -249,57 +256,46 @@ func addCommit(args commitArgs, head Addr) (new Addr, err error) {
})
}
-func commitCmd(args commitArgs) int {
+func commitCmd(args commitArgs) {
if args.msg == "" && !args.force {
- errf("no commit message")
- return 2
+ fail("missing commit message")
}
/* grab a lock to avoid racy accesses to the stash,
* it is released when the commit subcommand exits */
if _, err := GetRepoLock(config.Arch); err != nil {
- errf("could not lock archive: %v", err)
- return 2
+ faile(err)
}
_, err := os.Stat(path.Join(config.Arch, "stash/log"))
if err == nil {
- errf("flush %s/stash/ before continuing", config.Arch)
- return 2
+ fail("flush %s/stash/ before continuing", config.Arch)
} else if !os.IsNotExist(err) {
- errf("%v", err)
- return 2
+ faile(err)
}
/* read the ref after grabbing the lock since
* we will use it to build the new commit */
head, err := ReadRef(config.Arch, config.Ref)
if err != nil {
- errf("could not read main ref")
- return 2
+ faile(err)
}
newhead, err := addCommit(args, head)
if err != nil {
- errf("failed to add data: %v", err)
- return 2
+ faile(err)
}
msg := fmt.Sprintf("%d %s", args.now.Unix(), args.msg)
- if SdarCommit(config.Arch, msg) != nil {
- errf("sdar commit failed")
- return 2
+ if err := SdarCommit(config.Arch, msg); err != nil {
+ faile(err)
}
if WriteRef(config.Arch, config.Ref, newhead) != nil {
- errf("could not write ref/%s (%v)", config.Ref, newhead)
- return 2
+ fail("could not set ref/%s to %v", config.Ref, newhead)
}
if SetSync(config.Ref, &config) != nil {
- errf("could not update config to sync=%s", config.Ref)
- return 2
+ fail("could not update config to sync=%s", config.Ref)
}
-
- return 0
}
func walkEnt(w Walker, ent *Entry, verbose bool) {
@@ -333,7 +329,7 @@ func walkEnt(w Walker, ent *Entry, verbose bool) {
func walkDir(w Walker, rec, verbose bool) {
ws, err := w.Dive()
if err != nil {
- panic(err)
+ faile(err)
}
for _, w := range ws {
walkEnt(w, w.Here(), verbose)
@@ -343,20 +339,22 @@ func walkDir(w Walker, rec, verbose bool) {
}
}
-func walkRevAt(rd SdarReader, rev, rel string) Walker {
- ci, err := Revspec(rev, rd)
- if err != nil {
- errf("could not parse revspec '%s'", rev)
- return nil
+func parseRevspec(rev string, rd SdarReader) Addr {
+ ci := Revspec(rev, rd)
+ if ci == nil {
+ fail("could not parse revspec '%s'", rev)
}
- w, err := MakeSdarWalker(ci, rd)
+ return ci
+}
+
+func walkRevAt(rd SdarReader, rev, rel string) Walker {
+ addr := parseRevspec(rev, rd)
+ w, err := MakeSdarWalker(addr, rd)
if err == nil {
w, err = WalkTo(w, rel)
}
if err != nil {
- errf("could not walk to %s:%s", rev, rel)
- errf("... %v", err)
- return nil
+ faile(err)
}
return w
}
@@ -364,8 +362,7 @@ func walkRevAt(rd SdarReader, rev, rel string) Walker {
func makeArchiveReader() SdarReader {
rd, err := MakeReader(config.Arch)
if err != nil {
- errf("could not read archive: %v", err)
- os.Exit(2)
+ faile(err)
}
return rd
@@ -379,7 +376,7 @@ type walkArgs struct {
verb bool
}
-func walkCmd(args walkArgs) int {
+func walkCmd(args walkArgs) {
if len(args.paths) == 0 {
args.paths = []string{"/"}
}
@@ -388,17 +385,11 @@ func walkCmd(args walkArgs) int {
defer rd.Done()
w := walkRevAt(rd, args.rev, "")
- if w == nil {
- return 2
- }
-
for _, p := range args.paths {
rel := path.Join(args.cur, p)
wp, err := WalkTo(w, rel)
if err != nil {
- errf("could not walk to %s:%s", args.rev, rel)
- errf("... %v", err)
- return 2
+ faile(err)
}
if len(p) > 0 && p[len(p)-1] == '/' {
walkDir(wp, args.deep, args.verb)
@@ -406,7 +397,6 @@ func walkCmd(args walkArgs) int {
walkEnt(wp, wp.Here(), args.verb)
}
}
- return 0
}
func walkCommit(a Addr, c Commit) {
@@ -429,15 +419,12 @@ type logArgs struct {
paths []string
}
-func logCmd(args logArgs) int {
+func logCmd(args logArgs) {
rd := makeArchiveReader()
defer rd.Done()
- rev, err := Revspec(args.rev, rd)
- if err != nil {
- errf("could not resolve revspec '%s'", args.rev)
- return 2
- }
+ var acur, apre Addr
+ acur = parseRevspec(args.rev, rd)
addrs := make(map[string]Addr)
for i := 0; i < len(args.paths); i++ {
@@ -445,18 +432,16 @@ func logCmd(args logArgs) int {
addrs[p] = nil
}
- var cur, prec Commit
- var prea Addr
+ var cur, pre Commit
for {
var wrev Walker = DummyWalker("")
- if !rev.IsZero() {
- cur, err = rd.ReadCommit(rev)
- wrev = MakeCommitWalker(&cur, rd)
+ if !acur.IsZero() {
+ var err error
+ cur, err = rd.ReadCommit(acur)
if err != nil {
- errf("could not read commit %v", rev)
- errf("... %v", err)
- return 2
+ faile(err)
}
+ wrev = MakeCommitWalker(&cur, rd)
}
/* find out if we need to show
@@ -465,8 +450,7 @@ func logCmd(args logArgs) int {
for p, old := range addrs {
w, err := WalkTo(wrev, p)
if err != nil {
- errf("walk failed: %v", err)
- return 2
+ faile(err)
}
if w.Here() == nil {
continue
@@ -477,20 +461,18 @@ func logCmd(args logArgs) int {
}
addrs[p] = new
}
- if prea.Ok() && (changed || rev.IsZero()) {
- walkCommit(prea, prec)
+ if apre.Ok() && (changed || acur.IsZero()) {
+ walkCommit(apre, pre)
}
- if rev.IsZero() {
+ if acur.IsZero() {
break
}
- prec = cur
- prea = rev
- rev = cur.Prev
+ pre = cur
+ apre = acur
+ acur = cur.Prev
}
-
- return 0
}
type diffArgs struct {
@@ -524,14 +506,10 @@ func diffCmd(args diffArgs) int {
} else {
wb = walkRevAt(rd, args.brev, args.cur)
}
- if wa == nil || wb == nil {
- return 2
- }
chgs, err := Diff(wa, wb, simpleDiff, args.deep)
if err != nil {
- errf("diff failed: %v", err)
- return 2
+ faile(err)
}
if args.shush {
@@ -685,7 +663,7 @@ func syncFs(cs []Change, rd SdarReader) error {
abs := path.Join(config.Root, c.path)
mode := os.FileMode(uint32(c.e2.Mode))
err := os.Chmod(abs, mode)
- if err == nil {
+ if err == nil && c.e2.Kind == Ereg {
time := time.Unix(c.e2.Msec, 0)
err = os.Chtimes(abs, time, time)
}
@@ -705,10 +683,10 @@ type syncArgs struct {
paths []string
}
-func syncCmd(args syncArgs) int {
+func syncCmd(args syncArgs) {
if len(args.paths) == 0 && args.rev == "" {
fmt.Println(config.Sync)
- return 0
+ return
}
rd := makeArchiveReader()
@@ -719,9 +697,6 @@ func syncCmd(args syncArgs) int {
var wsyn Walker
if !args.force {
wsyn = walkRevAt(rd, ".", args.cur)
- if wsyn == nil {
- return 2
- }
}
sync := false
@@ -737,65 +712,53 @@ func syncCmd(args syncArgs) int {
rel := path.Join(args.cur, dir)
wfs := MakeFsDirWalker(rel, false)
if wsyn != nil {
- wsyn, err := WalkTo(wsyn, dir)
+ war, err := WalkTo(wsyn, dir)
if err != nil {
- errf("walk failed: %v", err)
- return 2
+ faile(err)
}
- cs, err := Diff(wfs, wsyn, simpleDiff, false)
+ cs, err := Diff(wfs, war, simpleDiff, false)
if err != nil {
- errf("diff failed: %v", err)
- return 2
+ faile(err)
}
if len(cs) > 0 {
- errf("%s has changes", dir)
+ errf("skipping dirty path %s", dir)
continue
}
}
wrev := walkRevAt(rd, args.rev, rel)
- if wrev == nil {
- return 2
- }
-
cs, err := Diff(wfs, wrev, simpleDiff, true)
if err != nil {
- errf("diff failed: %v", err)
- return 2
+ faile(err)
}
/* execute the changes given by the diff */
err = syncFs(cs, rd)
if err != nil {
- errf("fs sync failed: %v", err)
- return 2
+ fail("fs sync failed: %v", err)
}
}
if sync {
- ci, err := Revspec(args.rev, rd)
- if err == nil {
- err = SetSync(ci.String(), &config)
+ syncRev := args.rev
+ if !RevAbsolute(syncRev) {
+ addr := Revspec(args.rev, rd)
+ syncRev = addr.String()
}
- if err != nil {
- errf("could not update config to sync=%s", ci)
- return 2
+ if SetSync(syncRev, &config) != nil {
+ fail("could not update config to sync=%s", syncRev)
}
}
-
- return 0
}
-func initCmd(args []string) int {
+func initCmd(args []string) {
for _, p := range args {
if err := InitArchive(p); err != nil {
- errf("%v", err)
- return 2
+ faile(err)
} else {
fmt.Println("initialized new archive in", p)
}
}
- return 0
}
/* Visit kinds */
@@ -837,7 +800,7 @@ func checkData(ent *Entry, chk *Check) (err error) {
err = chk.reader.WriteTo(ent.Addr, writer)
cksum := digest.Sum64()
bad := (chk.seen-seen) != ent.Size || cksum != ent.Xxh
- if err == readError {
+ if errors.Is(err, readError) {
bad = true
/* the reader is still in a consistent
* state so we can swallow the error */
@@ -907,11 +870,10 @@ func checkCmd(revs []string) int {
})
for _, rev := range revs {
- addr, err := Revspec(rev, rd)
- if err != nil {
+ addr := Revspec(rev, rd)
+ if addr == nil {
done()
- errf("could not resolve revspec '%s'", rev)
- return 2
+ fail("could not parse revspec '%s'", rev)
}
for !addr.IsZero() && !visited(addr, Vcommit, chk) {
chk.loc = append(chk.loc[:0], addr.String())
@@ -935,8 +897,7 @@ func checkCmd(revs []string) int {
}
if err != nil {
done()
- errf("%v", err)
- return 2
+ faile(err)
}
addr = commit.Prev
}
@@ -1013,31 +974,26 @@ type getArgs struct {
paths []string
}
-func getCmd(args getArgs) int {
+func getCmd(args getArgs) {
rd := makeArchiveReader()
defer rd.Done()
curw := walkRevAt(rd, args.rev, args.cur)
- if curw == nil {
- return 2
- }
if !args.tar && len(args.paths) == 1 {
/* if a single file is asked for we
* send it to stdout straight */
w, err := WalkTo(curw, args.paths[0])
if err != nil {
- errf("coud not walk to %s: %v", args.paths[0], err)
- return 2
+ faile(err)
}
ent := w.Here()
if ent != nil && ent.Kind == Ereg {
err := rd.WriteTo(ent.Addr, os.Stdout)
if err != nil {
- errf("%v", err)
- return 2
+ faile(err)
}
- return 0
+ return
}
}
@@ -1046,17 +1002,13 @@ func getCmd(args getArgs) int {
for _, rel := range args.paths {
w, err := WalkTo(curw, rel)
if err != nil {
- errf("could not walk to %s: %v", rel, err)
- return 2
+ faile(err)
}
if err = tarAt(w, rd, tw); err != nil {
- errf("%v", err)
- return 2
+ faile(err)
}
}
tw.Close()
-
- return 0
}
type resetArgs struct {
@@ -1065,65 +1017,55 @@ type resetArgs struct {
creat bool
}
-func resetCmd(args resetArgs) int {
+func resetCmd(args resetArgs) {
rd := makeArchiveReader()
defer rd.Done()
- ci, err := Revspec(args.rev, rd)
- if err != nil {
- errf("could not parse revspec '%s'", args.rev)
- return 2
- }
+ addr := parseRevspec(args.rev, rd)
if args.ref == "" && !args.sync {
- fmt.Println(ci)
- return 0
+ fmt.Println(addr)
+ return
}
if args.sync {
- if err := SetSync(ci.String(), &config); err != nil {
- errf("could not update config to sync=%s", ci)
- return 2
+ if SetSync(addr.String(), &config) != nil {
+ fail("could not update config to sync=%s", addr)
}
- return 0
+ return
}
if args.creat {
if err := NewRef(config.Arch, args.ref); err != nil {
- errf("%v", err)
- return 2
+ faile(err)
}
}
- if err := WriteRef(config.Arch, args.ref, ci); err != nil {
- errf("could not write ref/%s (%v)", args.ref, ci)
- return 2
+ if WriteRef(config.Arch, args.ref, addr) != nil {
+ fail("could not set ref/%s to %v", args.ref, addr)
}
-
- return 0
}
func main() {
FindSdarBin()
if len(os.Args) < 2 {
- errf("subcommand expected")
- os.Exit(2)
+ fail("subcommand expected")
}
if os.Args[1] == "init" {
- os.Exit(initCmd(os.Args[2:]))
+ initCmd(os.Args[2:])
+ os.Exit(0)
}
dir, _ := os.Getwd()
cur, err := ParseConfig(dir, &config)
if err != nil {
if err == notFoundError {
- errf("%s is not a memex checkout", dir)
+ fail("%s is not a memex checkout", dir)
} else {
- errf("could parse config: %v", err)
+ fail("could parse config: %v", err)
}
- os.Exit(1)
}
switch os.Args[1] {
@@ -1138,7 +1080,7 @@ func main() {
args.cur = cur
args.now = time.Now()
args.paths = cmd.Args()
- os.Exit(commitCmd(args))
+ commitCmd(args)
case "log":
/*
@@ -1152,7 +1094,7 @@ func main() {
args.cur = cur
args.paths = cmd.Args()
- os.Exit(logCmd(args))
+ logCmd(args)
case "walk":
var args walkArgs
@@ -1164,7 +1106,7 @@ func main() {
args.cur = cur
args.paths = cmd.Args()
- os.Exit(walkCmd(args))
+ walkCmd(args)
case "diff":
/*
@@ -1196,7 +1138,7 @@ func main() {
args.cur = cur
args.paths = cmd.Args()
- os.Exit(getCmd(args))
+ getCmd(args)
case "sync":
var args syncArgs
@@ -1207,7 +1149,7 @@ func main() {
args.cur = cur
args.paths = cmd.Args()
- os.Exit(syncCmd(args))
+ syncCmd(args)
case "reset":
var args resetArgs
@@ -1219,8 +1161,7 @@ func main() {
rest := cmd.Args()
if args.sync {
if len(rest) != 1 {
- errf("one argument expected (rev)")
- os.Exit(2)
+ fail("one argument expected (rev)")
}
args.rev = rest[0]
} else {
@@ -1231,18 +1172,17 @@ func main() {
args.ref = rest[0]
args.rev = rest[1]
default:
- errf("expected arguments: [ref] rev")
- os.Exit(2)
+ fail("expected arguments: [ref] rev")
}
}
- os.Exit(resetCmd(args))
+ resetCmd(args)
case "check":
os.Exit(checkCmd(os.Args[2:]))
default:
- errf("unkown command '%s'", os.Args[1])
- os.Exit(2)
+ fail("unkown command '%s'", os.Args[1])
}
+ os.Exit(0)
}
diff --git a/memex/pack.go b/memex/pack.go
index b7316fb..087e324 100644
--- a/memex/pack.go
+++ b/memex/pack.go
@@ -40,7 +40,7 @@ func readUvarint(r *bytes.Buffer) (int64, error) {
for i := 0; ; i++ {
b, err := r.ReadByte()
if err != nil {
- return 0, err
+ return 0, badDataError
}
if b < 0x80 {
if i >= 9 {
@@ -163,44 +163,62 @@ func readStruct(r *bytes.Buffer, flds []Field, v reflect.Value) error {
return nil
}
-var entryPackets = map[EntryKind][]Field{
- Ereg: []Field{
- Field{"Addr", Faddr},
- Field{"Name", Fstring},
- Field{"Mode", Fuint16},
- Field{"Msec", Fuvarint},
- Field{"Size", Fuvarint},
- Field{"Xxh", Fuint64},
+var entryPackets = []map[EntryKind][]Field{
+ {
+ Ereg: []Field{
+ Field{"Addr", Faddr},
+ Field{"Name", Fstring},
+ Field{"Mode", Fuint16},
+ Field{"Msec", Fuvarint},
+ Field{"Size", Fuvarint},
+ Field{"Xxh", Fuint64},
+ },
+ Elnk: []Field{
+ Field{"Name", Fstring},
+ Field{"Dest", Fstring},
+ },
+ Edir: []Field{
+ Field{"Addr", Faddr},
+ Field{"Name", Fstring},
+ Field{"Mode", Fuint16},
+ Field{"Msec", Fuvarint},
+ },
},
- Elnk: []Field{
- Field{"Name", Fstring},
- Field{"Dest", Fstring},
- },
- Edir: []Field{
- Field{"Addr", Faddr},
- Field{"Name", Fstring},
- Field{"Mode", Fuint16},
- Field{"Msec", Fuvarint},
+ {
+ Ereg: []Field{
+ Field{"Addr", Faddr},
+ Field{"Name", Fstring},
+ Field{"Mode", Fuint16},
+ Field{"Msec", Fuvarint},
+ Field{"Size", Fuvarint},
+ Field{"Xxh", Fuint64},
+ },
+ Elnk: []Field{
+ Field{"Name", Fstring},
+ Field{"Dest", Fstring},
+ },
+ Edir: []Field{
+ Field{"Addr", Faddr},
+ Field{"Name", Fstring},
+ Field{"Mode", Fuint16},
+ },
},
}
func writeEntry(w *bytes.Buffer, e Entry) {
w.WriteByte(byte(e.Kind))
v := reflect.ValueOf(e)
- writeStruct(w, entryPackets[e.Kind], v)
+ writeStruct(w, entryPackets[1][e.Kind], v)
}
-func readEntry(r *bytes.Buffer, e *Entry) error {
+func readEntry(version int, r *bytes.Buffer, e *Entry) error {
b, err := r.ReadByte()
- if err != nil {
- return err
- }
- if b >= byte(Elast) {
+ if err != nil || b >= byte(Elast) {
return badDataError
}
e.Kind = EntryKind(b)
v := reflect.ValueOf(e).Elem()
- return readStruct(r, entryPackets[e.Kind], v)
+ return readStruct(r, entryPackets[version][e.Kind], v)
}
var commitPacket = []Field{
diff --git a/memex/repo.go b/memex/repo.go
index ba7cebf..b2bd174 100644
--- a/memex/repo.go
+++ b/memex/repo.go
@@ -15,14 +15,8 @@ import (
"time"
)
-type lockError struct{ pid int }
-
type RepoLock struct{ *os.File }
-func (e *lockError) Error() string {
- return fmt.Sprintf("lock held by process %d", e.pid)
-}
-
func GetRepoLock(arch string) (*RepoLock, error) {
lpath := path.Join(arch, "memex/lock")
f, err := os.OpenFile(lpath, os.O_RDWR|os.O_CREATE, 0644)
@@ -37,7 +31,8 @@ func GetRepoLock(arch string) (*RepoLock, error) {
var buf bytes.Buffer
io.Copy(&buf, f)
pid, _ := strconv.Atoi(buf.String())
- return nil, &lockError{pid}
+ err = fmt.Errorf("%s: held by process %d", lpath, pid)
+ return nil, err
}
return nil, err
}
@@ -75,7 +70,10 @@ func ReadRef(arch, ref string) (Addr, error) {
if len(b) > 0 {
b = b[:len(b)-1]
}
- return DecodeAddr(b)
+ if a := DecodeAddr(b); a != nil {
+ return a, nil
+ }
+ return nil, fmt.Errorf("invalid address '%s' in ref/%s", b, ref)
}
func Reflog(arch, ref string, addr Addr) {
diff --git a/memex/revs.go b/memex/revs.go
index 0ac1dc0..471c856 100644
--- a/memex/revs.go
+++ b/memex/revs.go
@@ -1,7 +1,6 @@
package main
import (
- "errors"
"regexp"
"sort"
"strconv"
@@ -77,22 +76,20 @@ func makeCache(r SdarReader) {
}
}
-var revSpecError = errors.New("invalid revision")
-
-func baseAddr(ref string, r SdarReader) (Addr, error) {
+func baseAddr(ref string, r SdarReader) Addr {
if ref == "." {
ref = config.Sync
}
addr, err := ReadRef(r.archive_path, ref)
if err == nil {
- return addr, nil
+ return addr
}
if commits == nil {
makeCache(r)
}
if len(ref) > AddrDisplayLen {
- return nil, revSpecError
+ return nil
}
if len(ref) == AddrDisplayLen {
return DecodeAddr([]byte(ref))
@@ -100,25 +97,25 @@ func baseAddr(ref string, r SdarReader) (Addr, error) {
i := sort.SearchStrings(commits, ref)
if i == len(commits) || commits[i][:len(ref)] != ref {
- return nil, revSpecError
+ return nil
}
/* check that the refspec is not ambiguous */
if i < len(commits)-1 && commits[i+1][:len(ref)] == ref {
- return nil, revSpecError
+ return nil
}
return DecodeAddr([]byte(commits[i]))
}
-func Revspec(ref string, r SdarReader) (Addr, error) {
- lex := &Tokenizer{s: ref}
+func Revspec(rev string, r SdarReader) Addr {
+ lex := &Tokenizer{s: rev}
if !lex.Next() {
- return nil, revSpecError
+ return nil
}
- a, err := baseAddr(lex.Token(), r)
- if err != nil {
- return nil, err
+ a := baseAddr(lex.Token(), r)
+ if a == nil {
+ return nil
}
var ups int
@@ -128,24 +125,30 @@ func Revspec(ref string, r SdarReader) (Addr, error) {
nb, err := strconv.Atoi(lex.Token())
if err == nil {
ups += nb
+ return true
} else {
ups += 1
+ return false
}
- return err == nil
})
}
if !lex.Eof() {
- return nil, revSpecError
+ return nil
}
for ; ups > 0; ups-- {
c, err := r.ReadCommit(a)
if err != nil {
- return nil, err
+ return nil
}
a = c.Prev
}
- return a, nil
+ return a
+}
+
+func RevAbsolute(rev string) bool {
+ lex := &Tokenizer{s: rev}
+ return lex.Next() && lex.Token() != "."
}
diff --git a/memex/sdar.go b/memex/sdar.go
index 87be3ae..ac4666b 100644
--- a/memex/sdar.go
+++ b/memex/sdar.go
@@ -21,8 +21,9 @@ const logIo = false
* the commit tag is longer for data recovery
* purposes */
const (
- TagDirV1 byte = 0x11
- TagCommitV1 string = "\x17\xee\x7b\xa6"
+ TagDirV0 byte = 0x11
+ TagDirV1 byte = 0x12
+ TagCommitV0 string = "\x17\xee\x7b\xa6"
)
/* The first byte is the level in {0,1,2} */
@@ -41,19 +42,12 @@ type SdarAdder struct {
}
type SdarReader struct{ *SdarProcess }
-type SdarProcessFailure struct {
- command string
- code int
-}
+var SdarPath string
var (
- SdarPath string
-
readError = errors.New("sdar read failed")
- checkError = errors.New("sdar check failed")
addError = errors.New("sdar add failed")
protoError = errors.New("sdar protocol error")
- addrError = errors.New("invalid sdar address")
)
func (a Addr) Ok() bool {
@@ -69,10 +63,6 @@ func (a Addr) String() string {
var ZeroAddr Addr = make([]byte, 33)
-func (e SdarProcessFailure) Error() string {
- return fmt.Sprintf("sdar %s exited with code %d", e.command, e.code)
-}
-
func (a Addr) IsZero() bool {
return bytes.Equal(a, ZeroAddr)
}
@@ -147,17 +137,21 @@ func startSdar(command, archive_path string) (*SdarProcess, error) {
return sdar, nil
}
-func (sdar *SdarProcess) Done() error {
- sdar.input.Close()
- err := sdar.Wait()
+func sdarSubprocessError(op string, err error) error {
if err == nil {
return nil
}
- var eerr *exec.ExitError
- if !errors.As(err, &eerr) {
+ var e *exec.ExitError
+ if !errors.As(err, &e) {
return err
}
- return SdarProcessFailure{sdar.command, eerr.ExitCode()}
+ return fmt.Errorf("sdar %s exited with code %d",
+ op, e.ExitCode())
+}
+
+func (sdar *SdarProcess) Done() error {
+ sdar.input.Close()
+ return sdarSubprocessError(sdar.command, sdar.Wait())
}
/* Use this counter to prevent multiple concurrent write
@@ -174,11 +168,11 @@ func startWriteOp() {
func SdarCommit(archive_path, msg string) error {
startWriteOp()
defer func() { writeOps-- }()
- c := exec.Command(SdarPath, "commit", "-a", archive_path, "-m", msg)
+ c := exec.Command(SdarPath, "commit", "-xx", "-a", archive_path, "-m", msg)
c.Stdin = nil
c.Stdout = os.Stdout
c.Stderr = os.Stderr
- return c.Run()
+ return sdarSubprocessError("commit", c.Run())
}
func MakeAdder(archive_path string) (SdarAdder, error) {
@@ -201,17 +195,17 @@ func MakeReader(archive_path string) (SdarReader, error) {
const AddrDisplayLen = 65
-func DecodeAddr(s []byte) (Addr, error) {
+func DecodeAddr(s []byte) Addr {
if len(s) != AddrDisplayLen {
- return nil, addrError
+ return nil
}
addr := make([]byte, 33)
addr[0] = s[0] - '0'
_, err := hex.Decode(addr[1:], s[1:])
if addr[0] > 2 || err != nil {
- return nil, addrError
+ return nil
}
- return addr, nil
+ return addr
}
func readPkt(r io.Reader) []byte {
@@ -238,20 +232,28 @@ func readPkt(r io.Reader) []byte {
return line
}
+func (sp *SdarProcess) err(e error) error {
+ if e == nil {
+ return nil
+ }
+ return fmt.Errorf("%s: sdar %s: %w",
+ sp.archive_path, sp.command, e)
+}
+
func (a SdarAdder) getAddr() (Addr, error) {
ans := readPkt(a.output)
if len(ans) < 3 {
- return nil, protoError
+ return nil, a.err(protoError)
}
switch string(ans[:3]) {
default:
- return nil, protoError
+ return nil, a.err(protoError)
case "err":
- return nil, addError
+ return nil, a.err(addError)
case "ok ":
- addr, err := DecodeAddr(ans[3 : len(ans)-1])
- if err != nil {
- return nil, protoError
+ addr := DecodeAddr(ans[3 : len(ans)-1])
+ if addr == nil {
+ return nil, a.err(protoError)
}
return addr, nil
}
@@ -267,7 +269,7 @@ func (sp *SdarProcess) sendPkt(msg string, args ...interface{}) error {
log.Println("=>", pkt)
}
_, err := io.WriteString(sp.input, pkt)
- return err
+ return sp.err(err)
}
func (a SdarAdder) AddBuffer(b *bytes.Buffer) (Addr, error) {
@@ -275,7 +277,7 @@ func (a SdarAdder) AddBuffer(b *bytes.Buffer) (Addr, error) {
return nil, err
}
if _, err := b.WriteTo(a.input); err != nil {
- return nil, err
+ return nil, a.err(err)
}
return a.getAddr()
}
@@ -284,26 +286,26 @@ func (r SdarReader) writeStreamTo(limit int, w io.Writer) error {
for {
ans := readPkt(r.output)
if len(ans) < 3 {
- return protoError
+ return r.err(protoError)
}
switch string(ans[:3]) {
default:
- return protoError
+ return r.err(protoError)
case "err":
- return readError
+ return r.err(readError)
case "end":
return nil
case "raw":
if len(ans) < 4 || ans[3] != ' ' {
- return protoError
+ return r.err(protoError)
}
nb, err := strconv.Atoi(string(ans[4 : len(ans)-1]))
if err != nil || (limit >= 0 && nb > limit) {
- return protoError
+ return r.err(protoError)
}
n, err := io.CopyN(w, r.output, int64(nb))
if int(n) != nb || err != nil {
- return protoError
+ return r.err(protoError)
}
if limit >= 0 {
limit -= int(n)
@@ -350,12 +352,15 @@ func (r SdarReader) ReadCommit(addr Addr) (Commit, error) {
if err != nil {
return c, err
}
- if string(b.Next(4)) != TagCommitV1 {
- return c, badDataError
+ if string(b.Next(4)) != TagCommitV0 {
+ short := addr.String()[:10] + "..."
+ e := fmt.Errorf("bad header for commit %s", short)
+ return c, r.err(e)
}
- err = readCommit(b, &c)
- if err != nil {
- return c, err
+ if readCommit(b, &c) != nil {
+ short := addr.String()[:10] + "..."
+ e := fmt.Errorf("invalid data in commit %v", short)
+ return c, r.err(e)
}
return c, nil
}
@@ -394,31 +399,44 @@ func (walk *SdarWalker) Here() *Entry {
return &walk.entry
}
+func (walk *SdarWalker) err(e error) error {
+ return fmt.Errorf("%s:%s: walk error: %w",
+ walk.reader.archive_path, walk.path, e)
+}
+
func (walk *SdarWalker) Dive() ([]Walker, error) {
if walk.entry.Kind != Edir {
return nil, nil
}
b, err := walk.reader.ReadAll(walk.entry.Addr)
if err != nil {
- return nil, err
+ return nil, walk.err(errors.Unwrap(err))
}
+
v, err := b.ReadByte()
if err != nil {
- return nil, err
+ return nil, walk.err(badDataError)
}
- if v != TagDirV1 {
- return nil, badDataError
+
+ var entVersion int
+ switch v {
+ case TagDirV0:
+ entVersion = 0
+ case TagDirV1:
+ entVersion = 1
+ default:
+ return nil, walk.err(badDataError)
}
+
nent, err := readUvarint(b)
if err != nil {
- return nil, err
+ return nil, walk.err(badDataError)
}
ents := make([]Walker, nent)
for i := int64(0); i < nent; i++ {
w := SdarWalker{reader: walk.reader}
- err := readEntry(b, &w.entry)
- if err != nil {
- return ents[:i], err
+ if readEntry(entVersion, b, &w.entry) != nil {
+ return nil, walk.err(badDataError)
}
w.path = path.Join(walk.path, w.entry.Name)
ents[i] = &w
@@ -453,7 +471,7 @@ func (a SdarAdder) addReg(fpath string, sz int64) (Addr, uint64, error) {
digest := xxhash.New()
w := io.MultiWriter(a.input, digest)
if _, err = SizedWriteTo(w, f, sz); err != nil {
- return nil, 0, err
+ return nil, 0, a.err(err)
}
addr, err := a.getAddr()
cksum := digest.Sum64()
@@ -511,7 +529,7 @@ func (a SdarAdder) AddFsAt(w Walker) (ent Entry, err error) {
func (a SdarAdder) AddCommit(c Commit) (Addr, error) {
var buf bytes.Buffer
- buf.WriteString(TagCommitV1)
+ buf.WriteString(TagCommitV0)
writeCommit(&buf, c)
return a.AddBuffer(&buf)
}