@@ -31,6 +31,7 @@ import (
31
31
"github.com/containers/podman/v5/libpod/lock"
32
32
"github.com/containers/podman/v5/libpod/plugin"
33
33
"github.com/containers/podman/v5/libpod/shutdown"
34
+ "github.com/containers/podman/v5/pkg/domain/entities"
34
35
"github.com/containers/podman/v5/pkg/rootless"
35
36
"github.com/containers/podman/v5/pkg/systemd"
36
37
"github.com/containers/podman/v5/pkg/util"
@@ -39,9 +40,11 @@ import (
39
40
"github.com/containers/storage/pkg/lockfile"
40
41
"github.com/containers/storage/pkg/unshare"
41
42
"github.com/docker/docker/pkg/namesgenerator"
43
+ "github.com/hashicorp/go-multierror"
42
44
jsoniter "github.com/json-iterator/go"
43
45
spec "github.com/opencontainers/runtime-spec/specs-go"
44
46
"github.com/sirupsen/logrus"
47
+ "golang.org/x/exp/slices"
45
48
)
46
49
47
50
// Set up the JSON library for all of Libpod
@@ -1249,3 +1252,133 @@ func (r *Runtime) LockConflicts() (map[uint32][]string, []uint32, error) {
1249
1252
1250
1253
return toReturn , locksHeld , nil
1251
1254
}
1255
+
1256
+ // SystemCheck checks our storage for consistency, and depending on the options
1257
+ // specified, will attempt to remove anything which fails consistency checks.
1258
+ func (r * Runtime ) SystemCheck (ctx context.Context , options entities.SystemCheckOptions ) (entities.SystemCheckReport , error ) {
1259
+ what := storage .CheckEverything ()
1260
+ if options .Quick {
1261
+ what = storage .CheckMost ()
1262
+ }
1263
+ if options .UnreferencedLayerMaximumAge != nil {
1264
+ tmp := * options .UnreferencedLayerMaximumAge
1265
+ what .LayerUnreferencedMaximumAge = & tmp
1266
+ }
1267
+ storageReport , err := r .store .Check (what )
1268
+ if err != nil {
1269
+ return entities.SystemCheckReport {}, err
1270
+ }
1271
+ if len (storageReport .Containers ) == 0 &&
1272
+ len (storageReport .Layers ) == 0 &&
1273
+ len (storageReport .ROLayers ) == 0 &&
1274
+ len (storageReport .Images ) == 0 &&
1275
+ len (storageReport .ROImages ) == 0 {
1276
+ // no errors detected
1277
+ return entities.SystemCheckReport {}, nil
1278
+ }
1279
+ mapErrorSlicesToStringSlices := func (m map [string ][]error ) map [string ][]string {
1280
+ if len (m ) == 0 {
1281
+ return nil
1282
+ }
1283
+ mapped := make (map [string ][]string , len (m ))
1284
+ for k , errs := range m {
1285
+ strs := make ([]string , len (errs ))
1286
+ for i , e := range errs {
1287
+ strs [i ] = e .Error ()
1288
+ }
1289
+ mapped [k ] = strs
1290
+ }
1291
+ return mapped
1292
+ }
1293
+
1294
+ report := entities.SystemCheckReport {
1295
+ Errors : true ,
1296
+ Layers : mapErrorSlicesToStringSlices (storageReport .Layers ),
1297
+ ROLayers : mapErrorSlicesToStringSlices (storageReport .ROLayers ),
1298
+ Images : mapErrorSlicesToStringSlices (storageReport .Images ),
1299
+ ROImages : mapErrorSlicesToStringSlices (storageReport .ROImages ),
1300
+ Containers : mapErrorSlicesToStringSlices (storageReport .Containers ),
1301
+ }
1302
+ if ! options .Repair && report .Errors {
1303
+ // errors detected, no corrective measures to be taken
1304
+ return report , err
1305
+ }
1306
+
1307
+ // get a list of images that we knew of before we tried to clean up any
1308
+ // that were damaged
1309
+ imagesBefore , err := r .store .Images ()
1310
+ if err != nil {
1311
+ return report , fmt .Errorf ("getting a list of images before attempting repairs: %w" , err )
1312
+ }
1313
+
1314
+ repairOptions := storage.RepairOptions {
1315
+ RemoveContainers : options .RepairLossy ,
1316
+ }
1317
+ var containers []* Container
1318
+ if repairOptions .RemoveContainers {
1319
+ // build a list of the containers that we claim as ours that we
1320
+ // expect to be removing in a bit
1321
+ for containerID := range storageReport .Containers {
1322
+ ctr , lookupErr := r .state .LookupContainer (containerID )
1323
+ if lookupErr != nil {
1324
+ // we're about to remove it, so it's okay that
1325
+ // it isn't even one of ours
1326
+ continue
1327
+ }
1328
+ containers = append (containers , ctr )
1329
+ }
1330
+ }
1331
+
1332
+ // run the cleanup
1333
+ merr := multierror .Append (nil , r .store .Repair (storageReport , & repairOptions )... )
1334
+
1335
+ if repairOptions .RemoveContainers {
1336
+ // get the list of containers that storage will still admit to knowing about
1337
+ containersAfter , err := r .store .Containers ()
1338
+ if err != nil {
1339
+ merr = multierror .Append (merr , fmt .Errorf ("getting a list of containers after attempting repairs: %w" , err ))
1340
+ }
1341
+ for _ , ctr := range containers {
1342
+ // if one of our containers that we tried to remove is
1343
+ // still on disk, report an error
1344
+ if slices .IndexFunc (containersAfter , func (containerAfter storage.Container ) bool {
1345
+ return containerAfter .ID == ctr .ID ()
1346
+ }) != - 1 {
1347
+ merr = multierror .Append (merr , fmt .Errorf ("clearing storage for container %s: %w" , ctr .ID (), err ))
1348
+ continue
1349
+ }
1350
+ // remove the container from our database
1351
+ if removeErr := r .state .RemoveContainer (ctr ); removeErr != nil {
1352
+ merr = multierror .Append (merr , fmt .Errorf ("updating state database to reflect removal of container %s: %w" , ctr .ID (), removeErr ))
1353
+ continue
1354
+ }
1355
+ if report .RemovedContainers == nil {
1356
+ report .RemovedContainers = make (map [string ]string )
1357
+ }
1358
+ report .RemovedContainers [ctr .ID ()] = ctr .config .Name
1359
+ }
1360
+ }
1361
+
1362
+ // get a list of images that are still around after we clean up any
1363
+ // that were damaged
1364
+ imagesAfter , err := r .store .Images ()
1365
+ if err != nil {
1366
+ merr = multierror .Append (merr , fmt .Errorf ("getting a list of images after attempting repairs: %w" , err ))
1367
+ }
1368
+ for _ , imageBefore := range imagesBefore {
1369
+ if slices .IndexFunc (imagesAfter , func (imageAfter storage.Image ) bool {
1370
+ return imageAfter .ID == imageBefore .ID
1371
+ }) == - 1 {
1372
+ if report .RemovedImages == nil {
1373
+ report .RemovedImages = make (map [string ][]string )
1374
+ }
1375
+ report .RemovedImages [imageBefore .ID ] = slices .Clone (imageBefore .Names )
1376
+ }
1377
+ }
1378
+
1379
+ if merr != nil {
1380
+ err = merr .ErrorOrNil ()
1381
+ }
1382
+
1383
+ return report , err
1384
+ }
0 commit comments