X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=ood.go;h=120a7de8afd554d95cb8397c01182a4db6eac99e;hb=HEAD;hp=25253ced2eaac52a57f49ba23f47fac50c91de35;hpb=b69d2ab11a495413ecc16a9bfea0d105b030c606;p=goredo.git diff --git a/ood.go b/ood.go index 25253ce..120a7de 100644 --- a/ood.go +++ b/ood.go @@ -1,43 +1,36 @@ -/* -goredo -- djb's redo implementation on pure Go -Copyright (C) 2020-2023 Sergey Matveev - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, version 3 of the License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ +// goredo -- djb's redo implementation on pure Go +// Copyright (C) 2020-2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . // Out-of-date determination package main import ( + "bytes" "errors" "fmt" "io" + "io/fs" "log" "os" - "path" - "path/filepath" "strings" "golang.org/x/sys/unix" ) const ( - DepTypeIfcreate = "ifcreate" - DepTypeIfchange = "ifchange" - DepTypeAlways = "always" - DepTypeStamp = "stamp" - EnvOODTgtsFd = "REDO_OOD_TGTS_FD" EnvOODTgtsLockFd = "REDO_OOD_TGTS_LOCK_FD" ) @@ -47,10 +40,8 @@ var ( FdOODTgts *os.File FdOODTgtsLock *os.File - OODCache map[string]bool = make(map[string]bool) - FileExistsCache map[string]bool = make(map[string]bool) - - ErrMissingTarget = errors.New("invalid format of .rec: missing Target") + OODCache = make(map[string]bool) + FileExistsCache = make(map[string]bool) ) func FileExists(p string) bool { @@ -62,14 +53,14 @@ func FileExists(p string) bool { FileExistsCache[p] = true return true } - if os.IsNotExist(err) { + if errors.Is(err, fs.ErrNotExist) { FileExistsCache[p] = false } return false } type TgtError struct { - Tgt string + Tgt *Tgt Err error } @@ -79,213 +70,170 @@ func (e TgtError) Error() string { return fmt.Sprintf("%s: %s", e.Tgt, e.Err) } -func cwdMustRel(paths ...string) string { - rel, err := filepath.Rel(Cwd, path.Join(paths...)) - if err != nil { - panic(err) - } - return rel -} - -func cwdAndTgt(tgt string) (string, string) { - cwd, tgt := path.Split(tgt) - cwd, err := filepath.Abs(cwd) - if err != nil { - panic(err) - } - return cwd, tgt -} - -func isSrc(cwd, tgt string) bool { - d, f := path.Split(path.Join(cwd, tgt)) - if !FileExists(path.Join(d, f)) { +func isSrc(tgt *Tgt) bool { + if !FileExists(tgt.a) { return false } - if FileExists(path.Join(d, f+".do")) { + if FileExists(tgt.a + ".do") { return false } - if FileExists(path.Join(d, RedoDir, f+DepSuffix)) { + if FileExists(tgt.dep) { return false } return true } -func isOODByBuildUUID(cwd, tgtOrig string) bool { - cwd, tgt := cwdAndTgt(path.Join(cwd, tgtOrig)) - depPath := path.Join(cwd, RedoDir, tgt+DepSuffix) - fdDep, err := os.Open(depPath) - if err != nil { - return true - } - depInfo, err := depRead(fdDep) - fdDep.Close() - if err != nil || depInfo.build != BuildUUID { - return true - } - return false +func isOODByBuildUUID(tgt *Tgt) bool { + build, err := depBuildRead(tgt.dep) + return err != nil || build != BuildUUID } -func isOOD(cwd, tgtOrig string, level int, seen map[string]struct{}) (bool, error) { +func isOOD(tgt *Tgt, level int, seen map[string]*Tgt) (bool, error) { indent := strings.Repeat(". ", level) - tracef(CDebug, "ood: %s%s checking", indent, tgtOrig) - cwd, tgt := cwdAndTgt(path.Join(cwd, tgtOrig)) - ood, cached := OODCache[path.Join(cwd, tgt)] + tracef(CDebug, "ood: %s%s checking", indent, tgt) + ood, cached := OODCache[tgt.rel] if cached { - tracef(CDebug, "ood: %s%s -> cached: %v", indent, tgtOrig, ood) + tracef(CDebug, "ood: %s%s -> cached: %v", indent, tgt, ood) return ood, nil } - depPath := path.Join(cwd, RedoDir, tgt+DepSuffix) - fdDep, err := os.Open(depPath) - if err != nil { - tracef(CDebug, "ood: %s%s -> no dep: %s", indent, tgtOrig, depPath) - OODCache[path.Join(cwd, tgt)] = true - return true, nil - } - depInfo, err := depRead(fdDep) - fdDep.Close() - if err != nil { - return true, TgtError{tgtOrig, err} + dep := DepCache[tgt.rel] + var err error + if dep == nil { + dep, err = depRead(tgt) + if err != nil { + if errors.Is(err, fs.ErrNotExist) { + if isSrc(tgt) { + ood = false + tracef(CDebug, "ood: %s%s -> is source", indent, tgt) + } else { + ood = true + tracef(CDebug, "ood: %s%s -> no dep: %s", indent, tgt, tgt.dep) + } + OODCache[tgt.rel] = ood + return ood, nil + } + if err != nil { + return true, TgtError{tgt, ErrLine(err)} + } + } + DepCache[tgt.rel] = dep } - if depInfo.build == BuildUUID { - tracef(CDebug, "ood: %s%s -> already built", indent, tgtOrig) - OODCache[path.Join(cwd, tgt)] = false + if dep.build == BuildUUID { + tracef(CDebug, "ood: %s%s -> already built", indent, tgt) + OODCache[tgt.rel] = false return false, nil } - if !FileExists(path.Join(cwd, tgt)) { - tracef(CDebug, "ood: %s%s -> non-existent", indent, tgtOrig) - OODCache[path.Join(cwd, tgt)] = true + if !FileExists(tgt.a) { + tracef(CDebug, "ood: %s%s -> non-existent", indent, tgt) + OODCache[tgt.rel] = true return true, nil } - for _, dep := range depInfo.ifcreates { - if FileExists(path.Join(cwd, dep)) { - tracef(CDebug, "ood: %s%s -> %s created", indent, tgtOrig, dep) + for _, ifcreate := range dep.ifcreates { + if FileExists(ifcreate.a) { + tracef(CDebug, "ood: %s%s -> %s created", indent, tgt, ifcreate) ood = true goto Done } } - for _, m := range depInfo.ifchanges { - dep := m["Target"] - if dep == "" { - return ood, TgtError{tgtOrig, ErrMissingTarget} - } - theirInode, err := inodeFromRec(m) - if err != nil { - return ood, TgtError{tgtOrig, fmt.Errorf("invalid format of .rec: %w", err)} - } - theirHsh := m["Hash"] - tracef(CDebug, "ood: %s%s -> %s: checking", indent, tgtOrig, dep) - ood, cached = OODCache[path.Join(cwd, dep)] + for _, ifchange := range dep.ifchanges { + tracef(CDebug, "ood: %s%s -> %s: checking", indent, tgt, ifchange.tgt) + ood, cached = OODCache[ifchange.tgt.rel] if cached { - tracef(CDebug, "ood: %s%s -> %s: cached: %v", indent, tgtOrig, dep, ood) + tracef(CDebug, "ood: %s%s -> %s: cached: %v", indent, tgt, ifchange.tgt, ood) if ood { goto Done } continue } - inode, err := inodeFromFileByPath(path.Join(cwd, dep)) + inode, err := inodeFromFileByPath(ifchange.tgt.a) if err != nil { - if os.IsNotExist(err) { - tracef(CDebug, "ood: %s%s -> %s: not exists", indent, tgtOrig, dep) + if errors.Is(err, fs.ErrNotExist) { + tracef(CDebug, "ood: %s%s -> %s: not exists", indent, tgt, ifchange.tgt) ood = true - OODCache[path.Join(cwd, dep)] = ood + OODCache[ifchange.tgt.rel] = ood goto Done } - return ood, TgtError{tgtOrig, err} + return ood, TgtError{tgt, ErrLine(err)} } - if inode.Size != theirInode.Size { - tracef(CDebug, "ood: %s%s -> %s: size differs", indent, tgtOrig, dep) + if !bytes.Equal(inode[:8], ifchange.Inode()[:8]) { + tracef(CDebug, "ood: %s%s -> %s: size differs", indent, tgt, ifchange.tgt) ood = true - OODCache[path.Join(cwd, dep)] = ood + OODCache[ifchange.tgt.rel] = ood goto Done } - if InodeTrust != InodeTrustNone && inode.Equals(theirInode) { - tracef(CDebug, "ood: %s%s -> %s: same inode", indent, tgtOrig, dep) + if InodeTrust != InodeTrustNone && inode.Equals(ifchange.Inode()) { + tracef(CDebug, "ood: %s%s -> %s: same inode", indent, tgt, ifchange.tgt) } else { - tracef(CDebug, "ood: %s%s -> %s: inode differs", indent, tgtOrig, dep) - fd, err := os.Open(path.Join(cwd, dep)) + tracef(CDebug, "ood: %s%s -> %s: inode differs", indent, tgt, ifchange.tgt) + fd, err := os.Open(ifchange.tgt.a) if err != nil { - return ood, TgtError{tgtOrig, err} + return ood, TgtError{tgt, ErrLine(err)} } hsh, err := fileHash(fd) fd.Close() if err != nil { - return ood, TgtError{tgtOrig, err} + return ood, TgtError{tgt, ErrLine(err)} } - if theirHsh != hsh { - tracef(CDebug, "ood: %s%s -> %s: hash differs", indent, tgtOrig, dep) + if ifchange.Hash() != hsh { + tracef(CDebug, "ood: %s%s -> %s: hash differs", indent, tgt, ifchange.tgt) ood = true - OODCache[path.Join(cwd, dep)] = ood + OODCache[ifchange.tgt.rel] = ood goto Done } - tracef(CDebug, "ood: %s%s -> %s: same hash", indent, tgtOrig, dep) + tracef(CDebug, "ood: %s%s -> %s: same hash", indent, tgt, ifchange.tgt) } - if dep == tgt { - tracef(CDebug, "ood: %s%s -> %s: same target", indent, tgtOrig, dep) + if ifchange.tgt.rel == tgt.rel { + tracef(CDebug, "ood: %s%s -> %s: same target", indent, tgt, ifchange.tgt) continue } - if isSrc(cwd, dep) { - tracef(CDebug, "ood: %s%s -> %s: is source", indent, tgtOrig, dep) - OODCache[path.Join(cwd, dep)] = false + if isSrc(ifchange.tgt) { + tracef(CDebug, "ood: %s%s -> %s: is source", indent, tgt, ifchange.tgt) + OODCache[ifchange.tgt.rel] = false continue } - if _, ok := seen[cwdMustRel(cwd, dep)]; ok { - tracef(CDebug, "ood: %s%s -> %s: was always built", indent, tgtOrig, dep) - OODCache[path.Join(cwd, dep)] = false + if _, ok := seen[ifchange.tgt.rel]; ok { + tracef(CDebug, "ood: %s%s -> %s: was always built", indent, tgt, ifchange.tgt) + OODCache[ifchange.tgt.rel] = false continue } - depOod, err := isOODWithTrace(cwd, dep, level+1, seen) + depOOD, err := isOODWithTrace(ifchange.tgt, level+1, seen) if err != nil { - return ood, TgtError{tgtOrig, err} + return ood, TgtError{tgt, err} } - if depOod { - tracef(CDebug, "ood: %s%s -> %s: ood", indent, tgtOrig, dep) + if depOOD { + tracef(CDebug, "ood: %s%s -> %s: ood", indent, tgt, ifchange.tgt) ood = true goto Done } - tracef(CDebug, "ood: %s%s -> %s: !ood", indent, tgtOrig, dep) + tracef(CDebug, "ood: %s%s -> %s: !ood", indent, tgt, ifchange.tgt) } Done: - tracef(CDebug, "ood: %s%s: %v", indent, tgtOrig, ood) - OODCache[path.Join(cwd, tgt)] = ood + tracef(CDebug, "ood: %s%s: %v", indent, tgt, ood) + OODCache[tgt.rel] = ood return ood, nil } -func isOODWithTrace( - cwd, tgtOrig string, - level int, - seen map[string]struct{}, -) (bool, error) { - p, err := filepath.Abs(path.Join(cwd, tgtOrig)) - if err != nil { - panic(err) - } - _, ood := OODTgts[p] +func isOODWithTrace(tgt *Tgt, level int, seen map[string]*Tgt) (bool, error) { + _, ood := OODTgts[tgt.a] + var err error if ood { - if !isOODByBuildUUID(cwd, tgtOrig) { - tracef( - CDebug, - "ood: %s%s -> already built", - strings.Repeat(". ", level), tgtOrig, - ) + if !isOODByBuildUUID(tgt) { + tracef(CDebug, "ood: %s%s -> already built", strings.Repeat(". ", level), tgt) return false, nil } - tracef( - CDebug, - "ood: %s%s true, external decision", - strings.Repeat(". ", level), tgtOrig, - ) + tracef(CDebug, "ood: %s%s true, external decision", strings.Repeat(". ", level), tgt) goto RecordOODTgt } - ood, err = isOOD(cwd, tgtOrig, level, seen) + ood, err = isOOD(tgt, level, seen) if !ood { return ood, err } @@ -295,17 +243,17 @@ RecordOODTgt: Whence: io.SeekStart, } if err = unix.FcntlFlock(FdOODTgtsLock.Fd(), unix.F_SETLKW, &flock); err != nil { - log.Fatalln(err) + log.Fatal(err) } if _, err = FdOODTgts.Seek(0, io.SeekEnd); err != nil { - log.Fatalln(err) + log.Fatal(err) } - if _, err := FdOODTgts.WriteString(p + "\x00"); err != nil { - log.Fatalln(err) + if _, err := FdOODTgts.WriteString(tgt.a + "\x00"); err != nil { + log.Fatal(err) } flock.Type = unix.F_UNLCK if err = unix.FcntlFlock(FdOODTgtsLock.Fd(), unix.F_SETLK, &flock); err != nil { - log.Fatalln(err) + log.Fatal(err) } return true, nil } @@ -317,13 +265,13 @@ func oodTgtsClear() { Whence: io.SeekStart, } if err = unix.FcntlFlock(FdOODTgtsLock.Fd(), unix.F_SETLKW, &flock); err != nil { - log.Fatalln(err) + log.Fatal(err) } if err = FdOODTgts.Truncate(0); err != nil { - log.Fatalln(err) + log.Fatal(err) } flock.Type = unix.F_UNLCK if err = unix.FcntlFlock(FdOODTgtsLock.Fd(), unix.F_SETLK, &flock); err != nil { - log.Fatalln(err) + log.Fatal(err) } }