]> Cypherpunks.ru repositories - goredo.git/blob - depfix.go
2182fd27bae104006fd01531c62869ff8bf69a5d
[goredo.git] / depfix.go
1 /*
2 goredo -- djb's redo implementation on pure Go
3 Copyright (C) 2020-2024 Sergey Matveev <stargrave@stargrave.org>
4
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.
8
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.
13
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/>.
16 */
17
18 package main
19
20 import (
21         "bufio"
22         "bytes"
23         "errors"
24         "io"
25         "io/fs"
26         "os"
27         "path"
28         "strings"
29
30         "github.com/google/uuid"
31         "go.cypherpunks.ru/recfile"
32 )
33
34 var DepFixHashCache map[string]Hash
35
36 func recfileWrite(fdDep io.StringWriter, fields ...recfile.Field) error {
37         w := recfile.NewWriter(fdDep)
38         if _, err := w.RecordStart(); err != nil {
39                 return err
40         }
41         if _, err := w.WriteFields(fields...); err != nil {
42                 return err
43         }
44         return nil
45 }
46
47 func depFix(root string) error {
48         tracef(CDebug, "depfix: entering %s", root)
49         dir, err := os.Open(root)
50         if err != nil {
51                 return ErrLine(err)
52         }
53         defer dir.Close()
54         for {
55                 entries, err := dir.ReadDir(1 << 10)
56                 if err != nil {
57                         if err == io.EOF {
58                                 break
59                         }
60                         return ErrLine(err)
61                 }
62                 for _, entry := range entries {
63                         if entry.IsDir() {
64                                 if err = depFix(path.Join(root, entry.Name())); err != nil {
65                                         return err
66                                 }
67                         }
68                 }
69         }
70         dir.Close()
71
72         redoDir := path.Join(root, RedoDir)
73         dir, err = os.Open(redoDir)
74         if err != nil {
75                 if errors.Is(err, fs.ErrNotExist) {
76                         return nil
77                 }
78                 return ErrLine(err)
79         }
80         defer dir.Close()
81         for {
82                 entries, err := dir.ReadDir(1 << 10)
83                 if err != nil {
84                         if err == io.EOF {
85                                 break
86                         }
87                         return ErrLine(err)
88                 }
89                 for _, entry := range entries {
90                         switch path.Ext(entry.Name()) {
91                         case DepSuffix:
92                         case ".rec":
93                         default:
94                                 continue
95                         }
96                         tracef(CDebug, "depfix: checking %s/%s", root, entry.Name())
97                         fdDepPath := path.Join(redoDir, entry.Name())
98                         data, err := os.ReadFile(fdDepPath)
99                         if err != nil {
100                                 return ErrLine(err)
101                         }
102                         fdDep, err := tempfile(redoDir, entry.Name())
103                         if err != nil {
104                                 return ErrLine(err)
105                         }
106                         defer os.Remove(fdDep.Name())
107                         tracef(
108                                 CDebug, "depfix: %s/%s: tmp %s",
109                                 root, entry.Name(), fdDep.Name(),
110                         )
111                         fdDepW := bufio.NewWriter(fdDep)
112                         switch path.Ext(entry.Name()) {
113                         case DepSuffix:
114                                 if _, err = depParse(NewTgt(""), data); err != nil {
115                                         return ErrLine(err)
116                                 }
117                                 build, data, err := depHeadParse(data)
118                                 if err != nil {
119                                         return ErrLine(err)
120                                 }
121                                 if err = depBuildWrite(fdDepW, build); err != nil {
122                                         return ErrLine(err)
123                                 }
124                                 var typ byte
125                                 var chunk []byte
126                                 for len(data) > 0 {
127                                         typ, chunk, data, _ = chunkRead(data)
128                                         switch typ {
129                                         case DepTypeAlways:
130                                                 err = always(fdDepW, fdDep.Name())
131                                         case DepTypeStamp:
132                                                 p := mustAbs(path.Join(root,
133                                                         strings.TrimSuffix(entry.Name(), DepSuffix)))
134                                                 hsh, ok := DepFixHashCache[p]
135                                                 if !ok {
136                                                         var fd *os.File
137                                                         fd, err = os.Open(p)
138                                                         if err != nil {
139                                                                 break
140                                                         }
141                                                         hsh, err = fileHash(fd)
142                                                         fd.Close()
143                                                         if err != nil {
144                                                                 break
145                                                         }
146                                                         DepFixHashCache[p] = hsh
147                                                 }
148                                                 err = stamp(fdDepW, fdDep.Name(), hsh)
149                                         case DepTypeIfcreate:
150                                                 err = ifcreate(fdDepW, fdDep.Name(), string(chunk))
151                                         case DepTypeIfchange:
152                                                 name := string(chunk[InodeLen+HashLen:])
153                                                 p := mustAbs(path.Join(root, name))
154                                                 var fd *os.File
155                                                 fd, err = os.Open(p)
156                                                 if err != nil {
157                                                         break
158                                                 }
159                                                 var inode *Inode
160                                                 inode, _, err = inodeFromFileByFd(fd)
161                                                 if err != nil {
162                                                         fd.Close()
163                                                         break
164                                                 }
165                                                 hsh, ok := DepFixHashCache[p]
166                                                 if !ok {
167                                                         hsh, err = fileHash(fd)
168                                                         if err != nil {
169                                                                 break
170                                                         }
171                                                         DepFixHashCache[p] = hsh
172                                                 }
173                                                 fd.Close()
174                                                 _, err = io.Copy(fdDepW, bytes.NewBuffer(
175                                                         chunkWrite(bytes.Join([][]byte{
176                                                                 {DepTypeIfchange},
177                                                                 inode[:],
178                                                                 []byte(hsh),
179                                                                 []byte(name),
180                                                         }, nil))))
181                                         case DepTypeIfchangeNonex:
182                                                 err = depWriteNonex(fdDepW, fdDep.Name(), string(chunk))
183                                         }
184                                         if err != nil {
185                                                 return ErrLine(err)
186                                         }
187                                 }
188                         case ".rec":
189                                 defer os.Remove(fdDepPath)
190                                 fdDepPath = fdDepPath[:len(fdDepPath)-4] + DepSuffix
191                                 r := recfile.NewReader(bytes.NewReader(data))
192                                 m, err := r.NextMap()
193                                 if err != nil {
194                                         return err
195                                 }
196                                 var build uuid.UUID
197                                 build, err = uuid.Parse(m["Build"])
198                                 if err != nil {
199                                         break
200                                 }
201                                 if err = depBuildWrite(fdDepW, build); err != nil {
202                                         return ErrLine(err)
203                                 }
204                                 for {
205                                         m, err := r.NextMap()
206                                         if err != nil {
207                                                 if errors.Is(err, io.EOF) {
208                                                         break
209                                                 }
210                                                 return ErrLine(err)
211                                         }
212                                         switch m["Type"] {
213                                         case "always":
214                                                 err = always(fdDepW, m["Target"])
215                                         case "stamp":
216                                                 p := mustAbs(path.Join(root,
217                                                         strings.TrimSuffix(entry.Name(), DepSuffix)))
218                                                 hsh, ok := DepFixHashCache[p]
219                                                 if !ok {
220                                                         var fd *os.File
221                                                         fd, err = os.Open(p)
222                                                         if err != nil {
223                                                                 break
224                                                         }
225                                                         hsh, err = fileHash(fd)
226                                                         fd.Close()
227                                                         if err != nil {
228                                                                 break
229                                                         }
230                                                         DepFixHashCache[p] = hsh
231                                                 }
232                                                 err = stamp(fdDepW, fdDep.Name(), hsh)
233                                         case "ifcreate":
234                                                 err = ifcreate(fdDepW, fdDep.Name(), m["Target"])
235                                         case "ifchange":
236                                                 if m["Size"] == "" {
237                                                         err = depWriteNonex(fdDepW, fdDep.Name(), m["Target"])
238                                                         break
239                                                 }
240                                                 name := string(m["Target"])
241                                                 p := mustAbs(path.Join(root, name))
242                                                 var fd *os.File
243                                                 fd, err = os.Open(p)
244                                                 if err != nil {
245                                                         break
246                                                 }
247                                                 var inode *Inode
248                                                 inode, _, err = inodeFromFileByFd(fd)
249                                                 if err != nil {
250                                                         fd.Close()
251                                                         break
252                                                 }
253                                                 hsh, ok := DepFixHashCache[p]
254                                                 if !ok {
255                                                         hsh, err = fileHash(fd)
256                                                         if err != nil {
257                                                                 break
258                                                         }
259                                                         DepFixHashCache[p] = hsh
260                                                 }
261                                                 fd.Close()
262                                                 _, err = io.Copy(fdDepW, bytes.NewBuffer(
263                                                         chunkWrite(bytes.Join([][]byte{
264                                                                 {DepTypeIfchange},
265                                                                 inode[:],
266                                                                 []byte(hsh),
267                                                                 []byte(name),
268                                                         }, nil))))
269                                         }
270                                         if err != nil {
271                                                 return ErrLine(err)
272                                         }
273                                 }
274                         }
275                         err = fdDepW.Flush()
276                         if err != nil {
277                                 return ErrLine(err)
278                         }
279                         if !NoSync {
280                                 if err = fdDep.Sync(); err != nil {
281                                         return ErrLine(err)
282                                 }
283                         }
284                         fdDep.Close()
285                         if err = os.Rename(fdDep.Name(), fdDepPath); err != nil {
286                                 return ErrLine(err)
287                         }
288                         tracef(CRedo, "%s", fdDepPath)
289                 }
290                 if !NoSync {
291                         if err = syncDir(redoDir); err != nil {
292                                 return err
293                         }
294                 }
295         }
296         return nil
297 }