@@ -18,6 +18,7 @@ package trace
18
18
import (
19
19
"context"
20
20
"errors"
21
+ "fmt"
21
22
"io/fs"
22
23
"slices"
23
24
"sort"
@@ -54,7 +55,7 @@ type locationAndIndex struct {
54
55
// Note that a precondition of this algorithm is that the chain layers are ordered by order of
55
56
// creation.
56
57
func PopulateLayerDetails (ctx context.Context , inventory []* extractor.Inventory , chainLayers []scalibrImage.ChainLayer , config * filesystem.Config ) {
57
- chainLayerDetailsList := []* extractor.LayerDetails {}
58
+ layerDetailsList := []* extractor.LayerDetails {}
58
59
59
60
// Create list of layer details struct to be referenced by inventory.
60
61
for i , chainLayer := range chainLayers {
@@ -65,7 +66,7 @@ func PopulateLayerDetails(ctx context.Context, inventory []*extractor.Inventory,
65
66
diffID = chainLayer .Layer ().DiffID ().Encoded ()
66
67
}
67
68
68
- chainLayerDetailsList = append (chainLayerDetailsList , & extractor.LayerDetails {
69
+ layerDetailsList = append (layerDetailsList , & extractor.LayerDetails {
69
70
Index : i ,
70
71
DiffID : diffID ,
71
72
Command : chainLayer .Layer ().Command (),
@@ -90,7 +91,7 @@ func PopulateLayerDetails(ctx context.Context, inventory []*extractor.Inventory,
90
91
lastLayerIndex := len (chainLayers ) - 1
91
92
92
93
for _ , inv := range inventory {
93
- layerDetails := chainLayerDetailsList [lastLayerIndex ]
94
+ layerDetails := layerDetailsList [lastLayerIndex ]
94
95
invExtractor , isFilesystemExtractor := inv .Extractor .(filesystem.Extractor )
95
96
96
97
// Only filesystem extractors are supported for layer scanning. Also, if the inventory has no
@@ -101,33 +102,39 @@ func PopulateLayerDetails(ctx context.Context, inventory []*extractor.Inventory,
101
102
}
102
103
103
104
var foundOrigin bool
105
+ fileLocation := inv .Locations [0 ]
104
106
105
107
// Go backwards through the chain layers and find the first layer where the inventory is not
106
108
// present. Such layer is the layer in which the inventory was introduced. If the inventory is
107
109
// present in all layers, then it means it was introduced in the first layer.
108
- // TODO: b/381249869 - Optimization: Skip layers if file not found.
109
110
for i := len (chainLayers ) - 2 ; i >= 0 ; i -- {
110
111
oldChainLayer := chainLayers [i ]
111
112
112
113
invLocationAndIndex := locationAndIndex {
113
- location : inv . Locations [ 0 ] ,
114
+ location : fileLocation ,
114
115
index : i ,
115
116
}
116
117
118
+ // If the file of interest is not present in the underlying layer, then there will be no
119
+ // difference in the extracted inventory from oldChainLayer, so extraction can be skipped in
120
+ // the chain layer. This is an optimization to avoid extracting the same inventory multiple
121
+ // times.
122
+ if ! isFileInLayerDiff (oldChainLayer , fileLocation ) {
123
+ continue
124
+ }
125
+
117
126
var oldInventory []* extractor.Inventory
118
127
if cachedInventory , ok := locationIndexToInventory [invLocationAndIndex ]; ok {
119
128
oldInventory = cachedInventory
120
129
} else {
121
130
// Check if file still exist in this layer, if not skip extraction.
122
131
// This is both an optimization, and avoids polluting the log output with false file not found errors.
123
- if _ , err := oldChainLayer .FS ().Stat (inv .Locations [0 ]); errors .Is (err , fs .ErrNotExist ) {
124
- oldInventory = []* extractor.Inventory {}
125
- } else {
132
+ if _ , err := oldChainLayer .FS ().Stat (fileLocation ); err == nil {
126
133
// Update the extractor config to use the files from the current layer.
127
134
// We only take extract the first location because other locations are derived from the initial
128
135
// extraction location. If other locations can no longer be determined from the first location
129
136
// they should not be included here, and the trace for those packages stops here.
130
- updateExtractorConfig ([]string {inv . Locations [ 0 ] }, invExtractor , oldChainLayer .FS ())
137
+ updateExtractorConfig ([]string {fileLocation }, invExtractor , oldChainLayer .FS ())
131
138
132
139
var err error
133
140
oldInventory , _ , err = filesystem .Run (ctx , config )
@@ -150,7 +157,7 @@ func PopulateLayerDetails(ctx context.Context, inventory []*extractor.Inventory,
150
157
151
158
// If the inventory is not present in the old layer, then it was introduced in layer i+1.
152
159
if ! foundPackage {
153
- layerDetails = chainLayerDetailsList [i + 1 ]
160
+ layerDetails = layerDetailsList [i + 1 ]
154
161
foundOrigin = true
155
162
break
156
163
}
@@ -159,7 +166,7 @@ func PopulateLayerDetails(ctx context.Context, inventory []*extractor.Inventory,
159
166
// If the inventory is present in every layer, then it means it was introduced in the first
160
167
// layer.
161
168
if ! foundOrigin {
162
- layerDetails = chainLayerDetailsList [0 ]
169
+ layerDetails = layerDetailsList [0 ]
163
170
}
164
171
inv .LayerDetails = layerDetails
165
172
}
@@ -192,3 +199,30 @@ func areInventoriesEqual(inv1 *extractor.Inventory, inv2 *extractor.Inventory) b
192
199
}
193
200
return true
194
201
}
202
+
203
+ // getSingleLayerFSFromChainLayer returns the filesystem of the underlying layer in the chain layer.
204
+ func getLayerFSFromChainLayer (chainLayer scalibrImage.ChainLayer ) (scalibrfs.FS , error ) {
205
+ layer := chainLayer .Layer ()
206
+ if layer == nil {
207
+ return nil , fmt .Errorf ("chain layer has no layer" )
208
+ }
209
+
210
+ fs := layer .FS ()
211
+ if fs == nil {
212
+ return nil , fmt .Errorf ("layer has no filesystem" )
213
+ }
214
+
215
+ return fs , nil
216
+ }
217
+
218
+ // isFileInLayerDiff checks if a file is present in the underlying layer of a chain layer.
219
+ func isFileInLayerDiff (chainLayer scalibrImage.ChainLayer , fileLocation string ) bool {
220
+ layerFS , err := getLayerFSFromChainLayer (chainLayer )
221
+ if err != nil {
222
+ return false
223
+ }
224
+ if _ , err := layerFS .Stat (fileLocation ); errors .Is (err , fs .ErrNotExist ) {
225
+ return true
226
+ }
227
+ return false
228
+ }
0 commit comments