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