@@ -36,13 +36,13 @@ import (
3636 spec "github.com/opencontainers/image-spec/specs-go"
3737 ocispec "github.com/opencontainers/image-spec/specs-go/v1"
3838 "github.com/sirupsen/logrus"
39- "golang.org/x/sys/unix"
4039
4140 buildconfig "github.com/modelpack/modctl/pkg/backend/build/config"
4241 "github.com/modelpack/modctl/pkg/backend/build/hooks"
4342 "github.com/modelpack/modctl/pkg/backend/build/interceptor"
4443 pkgcodec "github.com/modelpack/modctl/pkg/codec"
4544 "github.com/modelpack/modctl/pkg/storage"
45+ "github.com/modelpack/modctl/pkg/xattr"
4646)
4747
4848// OutputType defines the type of output to generate.
@@ -288,73 +288,80 @@ func BuildModelConfig(modelConfig *buildconfig.Model, layers []ocispec.Descripto
288288
289289// computeDigestAndSize computes the digest and size for the encoded content, using xattrs if available.
290290func computeDigestAndSize (mediaType , path , workDirPath string , info os.FileInfo , reader io.Reader , codec pkgcodec.Codec ) (io.Reader , string , int64 , error ) {
291- var digest string
292- var size int64
293-
291+ // Try to retrieve valid digest from xattrs cache.
294292 if pkgcodec .IsRawMediaType (mediaType ) {
295- // By default let's assume the mtime and size has changed.
296- mtimeChanged := true
297- sizeChanged := true
298-
299- if mtime , err := getXattr (path , xattrMtimeKey (mediaType )); err == nil {
300- if string (mtime ) == fmt .Sprintf ("%d" , info .ModTime ().UnixNano ()) {
301- mtimeChanged = false
302- }
293+ if digest , size , ok := retrieveCachedDigest (path , info ); ok {
294+ return reader , digest , size , nil
303295 }
296+ }
304297
305- if sizeBytes , err := getXattr (path , xattrSizeKey (mediaType )); err == nil {
306- if parsedSize , err := strconv .ParseInt (string (sizeBytes ), 10 , 64 ); err == nil {
307- if parsedSize == info .Size () {
308- sizeChanged = false
309- }
310- }
311- }
298+ logrus .Infof ("builder: calculating digest for file %s" , path )
312299
313- if ! mtimeChanged && ! sizeChanged {
314- // Check xattrs for cached digest and size.
315- if sha256 , err := getXattr (path , xattrSha256Key (mediaType )); err == nil {
316- digest = string (sha256 )
317- logrus .Infof ("builder: retrieved sha256 hash from xattr for file %s [digest: %s]" , path , digest )
318- }
319-
320- if sizeBytes , err := getXattr (path , xattrSizeKey (mediaType )); err == nil {
321- if parsedSize , err := strconv .ParseInt (string (sizeBytes ), 10 , 64 ); err == nil {
322- size = parsedSize
323- logrus .Infof ("builder: retrieved size from xattr for file %s [size: %d]" , path , size )
324- }
325- }
326- }
300+ hash := sha256 .New ()
301+ size , err := io .Copy (hash , reader )
302+ if err != nil {
303+ return reader , "" , 0 , fmt .Errorf ("failed to copy content to hash: %w" , err )
327304 }
305+ digest := fmt .Sprintf ("sha256:%x" , hash .Sum (nil ))
328306
329- // Compute digest and size if not retrieved from xattrs.
330- if digest == "" {
331- logrus .Infof ("builder: calculating digest for file %s" , path )
332- var err error
333- hash := sha256 .New ()
334- size , err = io .Copy (hash , reader )
335- if err != nil {
336- return reader , "" , 0 , fmt .Errorf ("failed to copy content to hash: %w" , err )
337- }
338- digest = fmt .Sprintf ("sha256:%x" , hash .Sum (nil ))
339- logrus .Infof ("builder: calculated digest for file %s [digest: %s]" , path , digest )
307+ logrus .Infof ("builder: calculated digest for file %s [digest: %s]" , path , digest )
340308
341- // Reset reader
342- reader , err = resetReader (reader , path , workDirPath , codec )
343- if err != nil {
344- return reader , "" , 0 , err
345- }
309+ // Reset reader for subsequent use.
310+ reader , err = resetReader (reader , path , workDirPath , codec )
311+ if err != nil {
312+ return reader , "" , 0 , err
313+ }
346314
347- // Store xattrs if raw media type.
348- if pkgcodec .IsRawMediaType (mediaType ) {
349- setXattr (path , xattrMtimeKey (mediaType ), fmt .Appendf ([]byte {}, "%d" , info .ModTime ().UnixNano ()))
350- setXattr (path , xattrSha256Key (mediaType ), []byte (digest ))
351- setXattr (path , xattrSizeKey (mediaType ), fmt .Appendf ([]byte {}, "%d" , size ))
315+ // Update xattrs cache.
316+ if pkgcodec .IsRawMediaType (mediaType ) {
317+ if err := updateCachedDigest (path , info .ModTime ().UnixNano (), size , digest ); err != nil {
318+ logrus .Warnf ("builder: failed to update xattrs for file %s: %s" , path , err )
352319 }
353320 }
354321
355322 return reader , digest , size , nil
356323}
357324
325+ // retrieveCachedDigest checks if mtime and size match, then returns the cached digest.
326+ func retrieveCachedDigest (path string , info os.FileInfo ) (string , int64 , bool ) {
327+ mtimeData , err := xattr .Get (path , xattr .MakeKey (xattr .KeyMtime ))
328+ if err != nil || string (mtimeData ) != strconv .FormatInt (info .ModTime ().UnixNano (), 10 ) {
329+ return "" , 0 , false
330+ }
331+
332+ sizeData , err := xattr .Get (path , xattr .MakeKey (xattr .KeySize ))
333+ if err != nil {
334+ return "" , 0 , false
335+ }
336+ cachedSize , err := strconv .ParseInt (string (sizeData ), 10 , 64 )
337+ if err != nil || cachedSize != info .Size () {
338+ return "" , 0 , false
339+ }
340+
341+ digestData , err := xattr .Get (path , xattr .MakeKey (xattr .KeySha256 ))
342+ if err != nil {
343+ return "" , 0 , false
344+ }
345+
346+ digest := string (digestData )
347+ logrus .Infof ("builder: retrieved from xattr cache for file %s [digest: %s]" , path , digest )
348+ return digest , cachedSize , true
349+ }
350+
351+ // updateCachedDigest writes mtime, size, and digest to xattrs.
352+ func updateCachedDigest (path string , mtime , size int64 , digest string ) error {
353+ if err := xattr .Set (path , xattr .MakeKey (xattr .KeyMtime ), []byte (strconv .FormatInt (mtime , 10 ))); err != nil {
354+ return err
355+ }
356+ if err := xattr .Set (path , xattr .MakeKey (xattr .KeySha256 ), []byte (digest )); err != nil {
357+ return err
358+ }
359+ if err := xattr .Set (path , xattr .MakeKey (xattr .KeySize ), []byte (strconv .FormatInt (size , 10 ))); err != nil {
360+ return err
361+ }
362+ return nil
363+ }
364+
358365// resetReader resets the reader to the beginning or re-encodes if not seekable.
359366func resetReader (reader io.Reader , path , workDirPath string , codec pkgcodec.Codec ) (io.Reader , error ) {
360367 if seeker , ok := reader .(io.ReadSeeker ); ok {
@@ -442,52 +449,3 @@ func getFileMetadata(path string) (modelspec.FileMetadata, error) {
442449
443450 return metadata , nil
444451}
445-
446- func xattrSha256Key (mediaType string ) string {
447- // Uniformity between linux and mac platforms is simplified by adding the prefix 'user.',
448- // because the key may be unlimited under mac,
449- // but on linux, in some cases, the user can only manipulate the user space.
450- return fmt .Sprintf ("user.%s.sha256" , mediaType )
451- }
452-
453- func xattrSizeKey (mediaType string ) string {
454- // Uniformity between linux and mac platforms is simplified by adding the prefix 'user.',
455- // because the key may be unlimited under mac,
456- // but on linux, in some cases, the user can only manipulate the user space.
457- return fmt .Sprintf ("user.%s.size" , mediaType )
458- }
459-
460- func xattrMtimeKey (mediaType string ) string {
461- // Uniformity between linux and mac platforms is simplified by adding the prefix 'user.',
462- // because the key may be unlimited under mac,
463- // but on linux, in some cases, the user can only manipulate the user space.
464- return fmt .Sprintf ("user.%s.mtime" , mediaType )
465- }
466-
467- // getXattr retrieves an xattr value for a given key.
468- func getXattr (path , key string ) ([]byte , error ) {
469- var value []byte
470- sz , err := unix .Getxattr (path , key , value )
471- if err != nil {
472- logrus .Warnf ("builder: failed to get xattr %s for file %s: %v" , key , path , err )
473- return nil , err
474- }
475-
476- value = make ([]byte , sz )
477- _ , err = unix .Getxattr (path , key , value )
478- if err != nil {
479- logrus .Warnf ("builder: failed to get xattr %s for file %s: %v" , key , path , err )
480- return nil , err
481- }
482-
483- return value , nil
484- }
485-
486- // setXattr sets an xattr value for a given key.
487- func setXattr (path , key string , value []byte ) {
488- if err := unix .Setxattr (path , key , value , 0 ); err != nil {
489- logrus .Warnf ("builder: failed to set xattr %s for file %s: %v" , key , path , err )
490- } else {
491- logrus .Infof ("builder: set xattr %s for file %s: %s" , key , path , string (value ))
492- }
493- }
0 commit comments