@@ -75,7 +75,22 @@ func (c *Cache) fileName(id [HashSize]byte, key string) string {
7575 return filepath .Join (c .dir , fmt .Sprintf ("%02x" , id [0 ]), fmt .Sprintf ("%x" , id )+ "-" + key )
7676}
7777
78- var errMissing = errors .New ("cache entry not found" )
78+ // An entryNotFoundError indicates that a cache entry was not found, with an
79+ // optional underlying reason.
80+ type entryNotFoundError struct {
81+ Err error
82+ }
83+
84+ func (e * entryNotFoundError ) Error () string {
85+ if e .Err == nil {
86+ return "cache entry not found"
87+ }
88+ return fmt .Sprintf ("cache entry not found: %v" , e .Err )
89+ }
90+
91+ func (e * entryNotFoundError ) Unwrap () error {
92+ return e .Err
93+ }
7994
8095const (
8196 // action entry file is "v1 <hex id> <hex out> <decimal size space-padded to 20 bytes> <unixnano space-padded to 20 bytes>\n"
@@ -94,6 +109,8 @@ const (
94109// GODEBUG=gocacheverify=1.
95110var verify = false
96111
112+ var errVerifyMode = errors .New ("gocachevrify=1" )
113+
97114// DebugTest is set when GODEBUG=gocachetest=1 is in the environment.
98115var DebugTest = false
99116
@@ -122,7 +139,7 @@ func initEnv() {
122139// saved file for that output ID is still available.
123140func (c * Cache ) Get (id ActionID ) (Entry , error ) {
124141 if verify {
125- return Entry {}, errMissing
142+ return Entry {}, & entryNotFoundError { Err : errVerifyMode }
126143 }
127144 return c .get (id )
128145}
@@ -135,50 +152,60 @@ type Entry struct {
135152
136153// get is Get but does not respect verify mode, so that Put can use it.
137154func (c * Cache ) get (id ActionID ) (Entry , error ) {
138- missing := func () (Entry , error ) {
139- return Entry {}, errMissing
155+ missing := func (reason error ) (Entry , error ) {
156+ return Entry {}, & entryNotFoundError { Err : reason }
140157 }
141158 f , err := os .Open (c .fileName (id , "a" ))
142159 if err != nil {
143- if os .IsNotExist (err ) {
144- return missing ()
145- }
146- return Entry {}, err
160+ return missing (err )
147161 }
148162 defer f .Close ()
149163 entry := make ([]byte , entrySize + 1 ) // +1 to detect whether f is too long
150- if n , err := io .ReadFull (f , entry ); n != entrySize || err != io .ErrUnexpectedEOF {
151- return Entry {}, fmt .Errorf ("read %d/%d bytes from %s with error %w" , n , entrySize , c .fileName (id , "a" ), err )
164+ if n , err := io .ReadFull (f , entry ); n > entrySize {
165+ return missing (errors .New ("too long" ))
166+ } else if err != io .ErrUnexpectedEOF {
167+ if err == io .EOF {
168+ return missing (errors .New ("file is empty" ))
169+ }
170+ return missing (err )
171+ } else if n < entrySize {
172+ return missing (errors .New ("entry file incomplete" ))
152173 }
153174 if entry [0 ] != 'v' || entry [1 ] != '1' || entry [2 ] != ' ' || entry [3 + hexSize ] != ' ' || entry [3 + hexSize + 1 + hexSize ] != ' ' || entry [3 + hexSize + 1 + hexSize + 1 + 20 ] != ' ' || entry [entrySize - 1 ] != '\n' {
154- return Entry {}, fmt . Errorf ( "bad data in %s" , c . fileName ( id , "a " ))
175+ return missing ( errors . New ( "invalid header " ))
155176 }
156177 eid , entry := entry [3 :3 + hexSize ], entry [3 + hexSize :]
157178 eout , entry := entry [1 :1 + hexSize ], entry [1 + hexSize :]
158179 esize , entry := entry [1 :1 + 20 ], entry [1 + 20 :]
159180 etime , entry := entry [1 :1 + 20 ], entry [1 + 20 :]
160181 var buf [HashSize ]byte
161- if _ , err := hex .Decode (buf [:], eid ); err != nil || buf != id {
162- return Entry {}, fmt .Errorf ("failed to hex decode eid data in %s: %w" , c .fileName (id , "a" ), err )
182+ if _ , err := hex .Decode (buf [:], eid ); err != nil {
183+ return missing (fmt .Errorf ("decoding ID: %v" , err ))
184+ } else if buf != id {
185+ return missing (errors .New ("mismatched ID" ))
163186 }
164187 if _ , err := hex .Decode (buf [:], eout ); err != nil {
165- return Entry {}, fmt .Errorf ("failed to hex decode eout data in %s : %w " , c . fileName ( id , "a" ), err )
188+ return missing ( fmt .Errorf ("decoding output ID : %v " , err ) )
166189 }
167190 i := 0
168191 for i < len (esize ) && esize [i ] == ' ' {
169192 i ++
170193 }
171194 size , err := strconv .ParseInt (string (esize [i :]), 10 , 64 )
172- if err != nil || size < 0 {
173- return Entry {}, fmt .Errorf ("failed to parse esize int from %s with error %w" , c .fileName (id , "a" ), err )
195+ if err != nil {
196+ return missing (fmt .Errorf ("parsing size: %v" , err ))
197+ } else if size < 0 {
198+ return missing (errors .New ("negative size" ))
174199 }
175200 i = 0
176201 for i < len (etime ) && etime [i ] == ' ' {
177202 i ++
178203 }
179204 tm , err := strconv .ParseInt (string (etime [i :]), 10 , 64 )
180- if err != nil || tm < 0 {
181- return Entry {}, fmt .Errorf ("failed to parse etime int from %s with error %w" , c .fileName (id , "a" ), err )
205+ if err != nil {
206+ return missing (fmt .Errorf ("parsing timestamp: %v" , err ))
207+ } else if tm < 0 {
208+ return missing (errors .New ("negative timestamp" ))
182209 }
183210
184211 err = c .used (c .fileName (id , "a" ))
@@ -203,8 +230,11 @@ func (c *Cache) GetFile(id ActionID) (file string, entry Entry, err error) {
203230 }
204231
205232 info , err := os .Stat (file )
206- if err != nil || info .Size () != entry .Size {
207- return "" , Entry {}, errMissing
233+ if err != nil {
234+ return "" , Entry {}, & entryNotFoundError {Err : err }
235+ }
236+ if info .Size () != entry .Size {
237+ return "" , Entry {}, & entryNotFoundError {Err : errors .New ("file incomplete" )}
208238 }
209239 return file , entry , nil
210240}
@@ -222,7 +252,7 @@ func (c *Cache) GetBytes(id ActionID) ([]byte, Entry, error) {
222252 return nil , entry , err
223253 }
224254 if sha256 .Sum256 (data ) != entry .OutputID {
225- return nil , entry , errMissing
255+ return nil , entry , & entryNotFoundError { Err : errors . New ( "bad checksum" )}
226256 }
227257 return data , entry , nil
228258}
@@ -272,9 +302,9 @@ func (c *Cache) used(file string) error {
272302
273303 if err != nil {
274304 if os .IsNotExist (err ) {
275- return errMissing
305+ return & entryNotFoundError { Err : err }
276306 }
277- return fmt .Errorf ("failed to stat file %s: %w" , file , err )
307+ return & entryNotFoundError { Err : fmt .Errorf ("failed to stat file %s: %w" , file , err )}
278308 }
279309
280310 err = os .Chtimes (file , c .now (), c .now ())
0 commit comments