2 goredo -- djb's redo implementation on pure Go
3 Copyright (C) 2020-2022 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, version 3 of the License.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
27 "go.cypherpunks.ru/recfile"
30 func depFix(root string) error {
31 tracef(CDebug, "depfix: entering %s", root)
32 dir, err := os.Open(root)
38 fis, err := dir.Readdir(1 << 10)
45 for _, fi := range fis {
47 if err = depFix(path.Join(root, fi.Name())); err != nil {
55 redoDir := path.Join(root, RedoDir)
56 dir, err = os.Open(redoDir)
58 if os.IsNotExist(err) {
64 redoDirChanged := false
66 fis, err := dir.Readdir(1 << 10)
73 for _, fi := range fis {
74 if !strings.HasSuffix(fi.Name(), DepSuffix) {
77 tracef(CDebug, "depfix: checking %s/%s", root, fi.Name())
78 fdDepPath := path.Join(redoDir, fi.Name())
79 fdDep, err := os.Open(fdDepPath)
84 r := recfile.NewReader(fdDep)
85 var fieldses [][]recfile.Field
88 fields, err := r.Next()
90 if errors.Is(err, io.EOF) {
95 fieldses = append(fieldses, fields)
96 m := make(map[string]string, len(fields))
97 for _, f := range fields {
100 if m["Type"] != DepTypeIfchange {
105 return ErrMissingTarget
107 tracef(CDebug, "depfix: checking %s/%s -> %s", root, fi.Name(), dep)
108 theirInode, err := inodeFromRec(m)
112 theirHsh := m["Hash"]
113 fd, err := os.Open(path.Join(root, dep))
115 if os.IsNotExist(err) {
117 CDebug, "depfix: %s/%s -> %s: not exists",
118 root, fi.Name(), dep,
124 inode, err := inodeFromFile(fd)
129 if inode.Size != theirInode.Size {
131 CDebug, "depfix: %s/%s -> %s: size differs",
132 root, fi.Name(), dep,
137 if inode.Equals(theirInode) {
139 CDebug, "depfix: %s/%s -> %s: inode is equal",
140 root, fi.Name(), dep,
145 hsh, err := fileHash(fd)
152 CDebug, "depfix: %s/%s -> %s: hash differs",
153 root, fi.Name(), dep,
157 fields = []recfile.Field{
158 {Name: "Type", Value: DepTypeIfchange},
159 {Name: "Target", Value: dep},
160 {Name: "Hash", Value: hsh},
162 fields = append(fields, inode.RecfileFields()...)
163 fieldses[len(fieldses)-1] = fields
165 CDebug, "depfix: %s/%s -> %s: inode updated",
166 root, fi.Name(), dep,
174 redoDirChanged = true
175 fdDep, err = tempfile(redoDir, fi.Name())
181 CDebug, "depfix: %s/%s: tmp %s",
182 root, fi.Name(), fdDep.Name(),
184 w := recfile.NewWriter(fdDep)
185 if _, err := w.WriteFields(fieldses[0]...); err != nil {
188 fieldses = fieldses[1:]
189 for _, fields := range fieldses {
190 if _, err := w.RecordStart(); err != nil {
193 if _, err := w.WriteFields(fields...); err != nil {
198 if err = fdDep.Sync(); err != nil {
203 if err = os.Rename(fdDep.Name(), fdDepPath); err != nil {
206 tracef(CRedo, "%s", fdDepPath)
209 if redoDirChanged && !NoSync {
210 if err = syncDir(redoDir); err != nil {