2 goredo -- djb's redo implementation on pure Go
3 Copyright (C) 2020-2023 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/>.
28 "go.cypherpunks.ru/recfile"
31 func depFix(root string) error {
32 tracef(CDebug, "depfix: entering %s", root)
33 dir, err := os.Open(root)
39 entries, err := dir.ReadDir(1 << 10)
46 for _, entry := range entries {
48 if err = depFix(path.Join(root, entry.Name())); err != nil {
56 redoDir := path.Join(root, RedoDir)
57 dir, err = os.Open(redoDir)
59 if errors.Is(err, fs.ErrNotExist) {
65 redoDirChanged := false
67 entries, err := dir.ReadDir(1 << 10)
74 for _, entry := range entries {
75 if !strings.HasSuffix(entry.Name(), DepSuffix) {
78 tracef(CDebug, "depfix: checking %s/%s", root, entry.Name())
79 fdDepPath := path.Join(redoDir, entry.Name())
80 fdDep, err := os.Open(fdDepPath)
85 r := recfile.NewReader(fdDep)
86 var fieldses [][]recfile.Field
89 fields, err := r.Next()
91 if errors.Is(err, io.EOF) {
96 fieldses = append(fieldses, fields)
97 m := make(map[string]string, len(fields))
98 for _, f := range fields {
101 if m["Type"] != DepTypeIfchange {
106 return ErrMissingTarget
108 tracef(CDebug, "depfix: checking %s/%s -> %s", root, entry.Name(), dep)
109 theirInode, err := inodeFromRec(m)
113 theirHsh := mustHashDecode(m["Hash"])
114 fd, err := os.Open(path.Join(root, dep))
116 if errors.Is(err, fs.ErrNotExist) {
118 CDebug, "depfix: %s/%s -> %s: not exists",
119 root, entry.Name(), dep,
125 inode, _, err := inodeFromFileByFd(fd)
130 if inode.Size != theirInode.Size {
132 CDebug, "depfix: %s/%s -> %s: size differs",
133 root, entry.Name(), dep,
138 if inode.Equals(theirInode) {
140 CDebug, "depfix: %s/%s -> %s: inode is equal",
141 root, entry.Name(), dep,
146 hsh, err := fileHash(fd)
153 CDebug, "depfix: %s/%s -> %s: hash differs",
154 root, entry.Name(), dep,
158 fields = []recfile.Field{
159 {Name: "Type", Value: DepTypeIfchange},
160 {Name: "Target", Value: dep},
161 {Name: "Hash", Value: hsh.String()},
163 fields = append(fields, inode.RecfileFields()...)
164 fieldses[len(fieldses)-1] = fields
166 CDebug, "depfix: %s/%s -> %s: inode updated",
167 root, entry.Name(), dep,
175 redoDirChanged = true
176 fdDep, err = tempfile(redoDir, entry.Name())
182 CDebug, "depfix: %s/%s: tmp %s",
183 root, entry.Name(), fdDep.Name(),
185 w := recfile.NewWriter(fdDep)
186 if _, err := w.WriteFields(fieldses[0]...); err != nil {
189 fieldses = fieldses[1:]
190 for _, fields := range fieldses {
191 if _, err := w.RecordStart(); err != nil {
194 if _, err := w.WriteFields(fields...); err != nil {
199 if err = fdDep.Sync(); err != nil {
204 if err = os.Rename(fdDep.Name(), fdDepPath); err != nil {
207 tracef(CRedo, "%s", fdDepPath)
210 if redoDirChanged && !NoSync {
211 if err = syncDir(redoDir); err != nil {