lb_binary_layered: fix mtimes in layered squashfses

layer construction involves rsync, and that process ignores times to
avoid some of the layers being larger than they would otherwise where
the only difference is times.  This saves a small amount of space,
around 14MiB, but results in files in the layers having non-intended
time values.  Ensure mtime and atime in the source chroot match what is
found in the destination chroot.
This commit is contained in:
Dan Bungert 2025-05-08 15:49:51 +02:00
parent 9092fd30e9
commit eec13dad68
3 changed files with 45 additions and 0 deletions

1
debian/install vendored
View File

@ -4,3 +4,4 @@ get-ppa-fingerprint usr/share/livecd-rootfs
minimize-manual usr/share/livecd-rootfs
checkout-translations-branch usr/share/livecd-rootfs
update-source-catalog usr/share/livecd-rootfs
sync-mtime usr/share/livecd-rootfs

View File

@ -169,6 +169,12 @@ build_layered_squashfs () {
# --del because we want to remove files that have been
# deleted in this layer.
rsync -aXHAS --checksum --no-times --del chroot/ chroot-2/
# but since we used --no-times, mtime was not actually
# replicated to chroot-2, instead using the mtime of
# the moment of rsync. This will break anything that
# requires an accurate mtime, so go fix that up.
# LP: #2107332
/usr/share/livecd-rootfs/sync-mtime chroot chroot-2
umount chroot-2
rmdir chroot-2
overlay_dir="$overlay_dir-2"

38
sync-mtime Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/python3
# usage: sync-mtime src dst
#
# synchronize atime/mtime on files between src and dst.
#
# src and dst are directories, where dst is expected to contain a subset of the
# files found in src. for each file present in dst or a subdirectory thereof,
# if atime/mtime differ between the same file in src and that file in dst,
# update atime/mtime on the file in dst.
import os
import sys
from pathlib import Path
def time_eq(a: os.stat_result, b: os.stat_result) -> bool:
return (
(a.st_mtime_ns == b.st_mtime_ns) and
(a.st_atime_ns == b.st_atime_ns)
)
src, dst = sys.argv[1:]
for dirpath, dirnames, filenames in Path(dst).walk():
for filename in filenames:
dst_file = dirpath / filename
if not dst_file.is_file():
continue
src_file = src / dst_file.relative_to(dst)
src_stat = src_file.stat(follow_symlinks=False)
dst_stat = dst_file.stat(follow_symlinks=False)
if time_eq(src_stat, dst_stat):
continue
ns = (src_stat.st_atime_ns, src_stat.st_mtime_ns)
os.utime(dst_file, ns=ns, follow_symlinks=False)