Skip to content

Commit 2011be4

Browse files
committed
storage/pkg/chunked: add FindFileByDigest for content-addressed lookup
Add a new public function FindFileByDigest that allows looking up files or chunks by their digest using the existing layer cache infrastructure. Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
1 parent 34d6d48 commit 2011be4

2 files changed

Lines changed: 93 additions & 1 deletion

File tree

storage/pkg/chunked/cache_linux.go

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,8 @@ type digestLookupResult struct {
791791
Path string
792792
// Offset is the offset within the file where the content starts.
793793
Offset int64
794+
// Size is the size of the content.
795+
Size int64
794796
}
795797

796798
func (c *layersCache) findDigestInternal(digest string) (*digestLookupResult, error) {
@@ -817,7 +819,7 @@ func (c *layersCache) findDigestInternal(digest string) (*digestLookupResult, er
817819
}
818820
fileLocationData := layer.cacheFile.vdata[off : off+tagLen]
819821

820-
fnamePosition, offFile, _, err := parseFileLocation(fileLocationData)
822+
fnamePosition, offFile, lenFile, err := parseFileLocation(fileLocationData)
821823
if err != nil {
822824
return nil, fmt.Errorf("corrupted cache file for layer %q", layer.id)
823825
}
@@ -836,6 +838,7 @@ func (c *layersCache) findDigestInternal(digest string) (*digestLookupResult, er
836838
Target: layer.target,
837839
Path: path,
838840
Offset: int64(offFile),
841+
Size: int64(lenFile),
839842
}, nil
840843
}
841844
}
@@ -979,3 +982,68 @@ func unmarshalToc(manifest []byte) (*minimal.TOC, error) {
979982

980983
return &toc, nil
981984
}
985+
986+
// FileByDigestResult contains the result of a file lookup by digest.
987+
type FileByDigestResult struct {
988+
File *os.File
989+
Offset int64
990+
Size int64
991+
}
992+
993+
// FindFileByDigest looks up a file or chunk by its digest in the layer cache.
994+
// It returns an open file descriptor, the offset within the file, and the size.
995+
// The caller is responsible for closing the returned file descriptor.
996+
func FindFileByDigest(store storage.Store, digest string) (*FileByDigestResult, error) {
997+
cache, err := getLayersCache(store)
998+
if err != nil {
999+
return nil, fmt.Errorf("failed to get layers cache: %w", err)
1000+
}
1001+
1002+
result, err := cache.findDigestInternal(digest)
1003+
if err != nil {
1004+
return nil, err
1005+
}
1006+
if result == nil {
1007+
return nil, nil // Not found
1008+
}
1009+
1010+
// Construct the full path to the file
1011+
driver, err := store.GraphDriver()
1012+
if err != nil {
1013+
return nil, fmt.Errorf("failed to get graph driver: %w", err)
1014+
}
1015+
1016+
layerPath, err := driver.Get(result.Target, graphdriver.MountOpts{})
1017+
if err != nil {
1018+
return nil, fmt.Errorf("failed to get layer path: %w", err)
1019+
}
1020+
defer driver.Put(result.Target)
1021+
1022+
// Open the layer directory first
1023+
layerDirFd, err := unix.Open(layerPath, unix.O_RDONLY|unix.O_DIRECTORY|unix.O_CLOEXEC, 0)
1024+
if err != nil {
1025+
return nil, fmt.Errorf("failed to open layer directory: %w", err)
1026+
}
1027+
defer unix.Close(layerDirFd)
1028+
1029+
// Use Openat2 with RESOLVE_BENEATH to safely open the file within the layer directory
1030+
fd, err := unix.Openat2(layerDirFd, result.Path, &unix.OpenHow{
1031+
Flags: unix.O_RDONLY | unix.O_CLOEXEC,
1032+
Resolve: unix.RESOLVE_BENEATH | unix.RESOLVE_NO_SYMLINKS,
1033+
})
1034+
if err != nil {
1035+
return nil, fmt.Errorf("failed to open file: %w", err)
1036+
}
1037+
1038+
file := os.NewFile(uintptr(fd), result.Path)
1039+
if file == nil {
1040+
unix.Close(fd)
1041+
return nil, fmt.Errorf("failed to create File from fd")
1042+
}
1043+
1044+
return &FileByDigestResult{
1045+
File: file,
1046+
Offset: result.Offset,
1047+
Size: result.Size,
1048+
}, nil
1049+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//go:build !linux
2+
3+
package chunked
4+
5+
import (
6+
"errors"
7+
"os"
8+
9+
storage "go.podman.io/storage"
10+
)
11+
12+
var errUnsupported = errors.New("FindFileByDigest is not supported on this platform")
13+
14+
// FileByDigestResult contains the result of a file lookup by digest.
15+
type FileByDigestResult struct {
16+
File *os.File
17+
Offset int64
18+
Size int64
19+
}
20+
21+
// FindFileByDigest is not supported on non-Linux platforms.
22+
func FindFileByDigest(store storage.Store, digest string) (*FileByDigestResult, error) {
23+
return nil, errUnsupported
24+
}

0 commit comments

Comments
 (0)