blob: 5f14da2826bfdd590dd4f0a03fe4e8dfd6c869fd [file]
# Copyright 2019 The ChromiumOS Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utilities to handle/generate CrOS stateful payloads."""
import logging
import os
from pathlib import Path
from typing import List, Optional, Union
from chromite.lib import compression_lib
from chromite.lib import constants
from chromite.lib import image_lib
from chromite.lib import metrics_lib
from chromite.lib import osutils
def _generate_stateful_payload(
image_path: Union[Path, str],
output: Union[Path, int, str],
compression: compression_lib.CompressionType,
compressor: Optional[List[str]] = None,
) -> None:
"""Generates a stateful update payload given a path/fd and compression.
Args:
image_path: Path to the image.
output: Path or fd to the output target.
compression: The compression to use.
compressor: Explicitly specify the compressor to use.
"""
logging.info("Generating stateful update payload.")
# Mount the image to pull out the important directories.
with (
osutils.TempDir() as stateful_mnt,
image_lib.LoopbackPartitions(image_path, stateful_mnt) as image,
):
stateful_dir = image.Mount((constants.PART_STATE,))[0]
try:
logging.info("Tarring up /usr/local and /var!")
inputs = ["dev_image", "var_overlay"]
if os.path.exists(os.path.join(stateful_dir, "unencrypted")):
inputs += ["unencrypted"]
compression_lib.create_tarball(
output,
".",
sudo=True,
compression=compression,
compressor=compressor,
inputs=inputs,
extra_args=[
"--selinux",
"--directory=%s" % stateful_dir,
"--transform=s,^dev_image,dev_image_new,",
"--transform=s,^var_overlay,var_new,",
"--transform=s,^unencrypted/dev_image.block,unencrypted/dev_image_new.block,",
],
)
except:
logging.error("Failed to create stateful update file")
raise
if isinstance(output, int):
logging.info("Successfully generated stateful update payload.")
else:
logging.info(
"Successfully generated stateful update payload %s.", output
)
@metrics_lib.timed("paygen_stateful_payload_lib.GenerateStatefulPayload")
def GenerateStatefulPayload(
image_path: Union[Path, str], output: Union[Path, int, str]
) -> Union[Path, int, str]:
"""Generates a stateful update payload given a full path to an image.
Args:
image_path: Full path to the image.
output: Can be either the path to the directory to leave the resulting
payload or a file descriptor to write the payload into.
Returns:
Union[Path, int, str]: The path or fd to the generated stateful update
payload.
"""
if isinstance(output, int):
output_gz = output
else:
output_gz = os.path.join(
output, constants.QUICK_PROVISION_PAYLOAD_STATEFUL
)
_generate_stateful_payload(
image_path, output_gz, compression_lib.CompressionType.GZIP
)
return output_gz
@metrics_lib.timed("paygen_stateful_payload_lib.GenerateZstdStatefulPayload")
def GenerateZstdStatefulPayload(
image_path: Union[Path, str], output: Union[Path, int, str]
) -> Union[Path, int, str]:
"""Generates a zstd stateful update payload given a full path to an image.
Args:
image_path: Full path to the image.
output: Can be either the path to the directory to leave the resulting
payload or a file descriptor to write the payload into.
Returns:
Union[Path, int, str]: The path or fd to the generated stateful update
payload.
"""
if isinstance(output, int):
output_zstd = output
else:
output_zstd = os.path.join(output, constants.STATEFUL_PAYLOAD)
_generate_stateful_payload(
image_path,
output_zstd,
compression_lib.CompressionType.ZSTD,
compressor=["zstdmt", "-19"],
)
return output_zstd