Skip to content

Commit

Permalink
fix: properly handling swapfile during persist file handling (#275)
Browse files Browse the repository at this point in the history
This PR implements the proper way to create swapfile.
For a quick workaround fix, a hook is added in persist file handling to handle swapfile creating as special case.
  • Loading branch information
Bodong-Yang authored Mar 4, 2024
1 parent 1aa06ed commit dfd1237
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 7 deletions.
28 changes: 26 additions & 2 deletions otaclient/_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Any, Callable, TypeVar
from typing_extensions import ParamSpec, Concatenate


from __future__ import annotations
from math import ceil
from pathlib import Path
from typing import Any, Callable, Optional, TypeVar
from typing_extensions import Literal, ParamSpec, Concatenate

P = ParamSpec("P")

Expand Down Expand Up @@ -42,3 +47,22 @@ def _decorator(target: Callable[..., RT]) -> Callable[Concatenate[Any, P], RT]:
return target # type: ignore

return _decorator


_MultiUnits = Literal["GiB", "MiB", "KiB", "Bytes", "KB", "MB", "GB"]
# fmt: off
_multiplier: dict[_MultiUnits, int] = {
"GiB": 1024 ** 3, "MiB": 1024 ** 2, "KiB": 1024 ** 1,
"GB": 1000 ** 3, "MB": 1000 ** 2, "KB": 1000 ** 1,
"Bytes": 1,
}
# fmt: on


def get_file_size(
swapfile_fpath: str | Path, units: _MultiUnits = "Bytes"
) -> Optional[int]:
"""Helper for get file size with <units>."""
swapfile_fpath = Path(swapfile_fpath)
if swapfile_fpath.is_file():
return ceil(swapfile_fpath.stat().st_size / _multiplier[units])
65 changes: 65 additions & 0 deletions otaclient/_utils/linux.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Copyright 2022 TIER IV, INC. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from __future__ import annotations
from pathlib import Path
from subprocess import check_call


def create_swapfile(
swapfile_fpath: str | Path, size_in_MiB: int, *, timeout=900
) -> Path:
"""Create swapfile at <swapfile_fpath> with <size_in_MiB>MiB.
Reference: https://wiki.archlinux.org/title/swap#Swap_file_creation
Args:
swapfile_fpath(StrOrPath): the path to place the created swapfile.
size_in_MiB(int): the size of to-be-created swapfile.
timeout: timeout of swapfile creating, default is 15mins.
Returns:
The Path object to the newly created swapfile.
Raises:
ValueError on file already exists at <swapfile_fpath>, SubprocessCallFailed
on failed swapfile creation.
"""
swapfile_fpath = Path(swapfile_fpath)
if swapfile_fpath.exists():
raise ValueError(f"{swapfile_fpath=} exists, skip")

# create a new file with <size_in_MiB>MiB size
# executes:
# dd if=/dev/zero of=/swapfile bs=1M count=8k
# chmod 0600 /swapfile
check_call(
[
"dd",
"if=/dev/zero",
f"of={str(swapfile_fpath)}",
"bs=1M",
f"count={size_in_MiB}",
],
timeout=timeout,
)
swapfile_fpath.chmod(0o600)

# prepare the created file as swapfile
# executes:
# mkswap /swapfile
check_call(["mkswap", str(swapfile_fpath)], timeout=timeout)

return swapfile_fpath
23 changes: 18 additions & 5 deletions otaclient/app/ota_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@
)
from . import log_setting

from otaclient._utils import get_file_size
from otaclient._utils.linux import create_swapfile

try:
from otaclient import __version__ # type: ignore
except ImportError:
Expand Down Expand Up @@ -304,13 +307,23 @@ def _process_persistents(self):
for _perinf in self._otameta.iter_metafile(
ota_metadata.MetafilesV1.PERSISTENT_FNAME
):
_perinf_path = Path(_perinf.path)
_per_fpath = Path(_perinf.path)

# NOTE(20240220): fast fix for handling swapfile
if str(_per_fpath) in ["/swapfile", "/swap.img"]:
_new_swapfile = standby_slot_mp / _per_fpath
try:
_swapfile_size = get_file_size(_per_fpath, units="MiB")
assert _swapfile_size is not None, f"{_per_fpath} doesn't exist"
create_swapfile(_new_swapfile, _swapfile_size)
except Exception as e:
logger.warning(f"failed to create {_per_fpath}, skip: {e!r}")
continue

if (
_perinf_path.is_file()
or _perinf_path.is_dir()
or _perinf_path.is_symlink()
_per_fpath.is_file() or _per_fpath.is_dir() or _per_fpath.is_symlink()
): # NOTE: not equivalent to perinf.path.exists()
_copy_tree.copy_with_parents(_perinf_path, standby_slot_mp)
_copy_tree.copy_with_parents(_per_fpath, standby_slot_mp)

def _execute_update(
self,
Expand Down

0 comments on commit dfd1237

Please sign in to comment.