diff options
| author | Quentin Carbonneaux | 2021-11-16 22:33:57 +0100 |
|---|---|---|
| committer | Quentin Carbonneaux | 2021-11-16 22:33:57 +0100 |
| commit | d0b6239c8be29b244d27cc4bb860100b0c46d316 (patch) | |
| tree | 277fb9515c5d6530d3ee94925f9d48f0bf7b2030 | |
| parent | af1173599b455eb722c47c8617488909100abf2b (diff) | |
[mx] dir v1 / better sync / error refactor
| -rw-r--r-- | memex/diff.go | 4 | ||||
| -rw-r--r-- | memex/main.go | 270 | ||||
| -rw-r--r-- | memex/pack.go | 68 | ||||
| -rw-r--r-- | memex/repo.go | 14 | ||||
| -rw-r--r-- | memex/revs.go | 39 | ||||
| -rw-r--r-- | memex/sdar.go | 128 |
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) } |
