@@ -330,15 +330,50 @@ function dump_image {
330330 local load_text=" Loading ${image_basename} ..."
331331 local title_text=" Installation..."
332332 local dump
333+ local parttable
334+ local count_32k
335+ local image_size
336+ local count=0
337+ local block_size=32k
338+
339+ # can we dump this
340+ check_image_fits_target " ${image_target} "
333341
342+ # select dump method
334343 if [ -n " ${image_from_remote} " ]; then
335344 dump=dump_remote_image
336345 else
337346 dump=dump_local_image
338347 fi
339348
340- check_image_fits_target " ${image_target} "
349+ # setup blocks and blocksize to retain last
350+ if getargbool 0 rd.kiwi.install.retain_last; then
351+ if [ -n " ${image_from_remote} " ]; then
352+ image_size=$(( blocks * blocksize))
353+ parttable=$(
354+ fetch_remote_partition_table " ${image_source} " " ${image_size} "
355+ )
356+ else
357+ parttable=$(
358+ fetch_local_partition_table " ${image_source} "
359+ )
360+ fi
361+ if compatible_to_retain " ${parttable} " " ${image_target} " ; then
362+ count=$( get_disk_offset_retain_last_partition " ${parttable} " )
363+ fi
364+ if [ " ${count} " -gt 0 ]; then
365+ block_size=$( get_sector_size_from_table_dump " ${parttable} " )
366+ count_32k=$(
367+ optimize_count_for_32k_blocksize " ${block_size} " " ${count} "
368+ )
369+ if [ ! " ${count} " = " ${count_32k} " ]; then
370+ count=" ${count_32k} "
371+ block_size=32k
372+ fi
373+ fi
374+ fi
341375
376+ # last chance to stop us
342377 if [ " ${kiwi_oemunattended} " = " false" ]; then
343378 local ack_dump_text=" Destroying ALL data on ${image_target} , continue ?"
344379 if ! run_dialog --yesno " \" ${ack_dump_text} \" " 7 80; then
@@ -347,48 +382,182 @@ function dump_image {
347382 fi
348383 fi
349384
385+ # deploy
350386 echo " ${load_text} [${image_target} ]..."
351387 if command -v pv & > /dev/null && [ " ${kiwi_oemsilentinstall} " = " false" ]
352388 then
353389 # dump with dialog based progress information
354390 setup_progress_fifo ${progress}
355- eval " ${dump} " " ${image_source} " " ${image_target} " " ${progress} " &
391+ eval \
392+ " ${dump} " \
393+ " ${image_source} " \
394+ " ${image_target} " \
395+ " ${count} " \
396+ " ${block_size} " \
397+ " ${progress} " \
398+ &
356399 run_progress_dialog " ${load_text} " " ${title_text} "
357400 else
358401 # dump with silently blocked console
359- if ! eval " ${dump} " " ${image_source} " " ${image_target} " ; then
402+ if ! eval \
403+ " ${dump} " \
404+ " ${image_source} " \
405+ " ${image_target} " \
406+ " ${count} " \
407+ " ${block_size} "
408+ then
360409 report_and_quit " Failed to install image"
361410 fi
362411 fi
412+ if [ " ${count} " -gt 0 ]; then
413+ recreate_last_partition " ${image_target} "
414+ fi
415+ }
416+
417+ function fetch_local_partition_table {
418+ local image_source=$1
419+ local parttable=/tmp/parttable
420+ sfdisk -d " ${image_source} " > " ${parttable} " 2> /dev/null
421+ echo " ${parttable} "
422+ }
423+
424+ function fetch_remote_partition_table {
425+ local image_source=$1
426+ local image_size=$2
427+ local parttable=/tmp/parttable
428+ dd if=/dev/zero of=/tmp/table bs=1 count=0 seek=" ${image_size} " & > /dev/null
429+ fetch_file " ${image_source} " 2> /dev/null | dd of=/tmp/table bs=512 count=1 conv=notrunc & > /dev/null
430+ sfdisk -d /tmp/table > " ${parttable} " 2> /dev/null
431+ echo " ${parttable} "
432+ }
433+
434+ function get_sector_size_from_table_dump {
435+ local parttable=$1
436+ sector_size=$( grep sector-size: " ${parttable} " | cut -f2 -d:)
437+ echo " ${sector_size} "
438+ }
439+
440+ function compatible_to_retain {
441+ local parttable=$1
442+ local image_target=$2
443+ local source_start
444+ local target_start
445+ source_start=$(
446+ tail -n 1 " ${parttable} " | cut -f2 -d= | cut -f1 -d,
447+ )
448+ target_start=$(
449+ sfdisk -d " ${image_target} " 2> /dev/null | \
450+ tail -n 1 | cut -f2 -d= | cut -f1 -d,
451+ )
452+ if [ -z " ${source_start} " ] || [ -z " ${target_start} " ]; then
453+ # no partition information for either source or target
454+ # broken or net new deployment on empty disk
455+ touch /tmp/retain_not_applicable
456+ return 1
457+ fi
458+ if [ ! " ${source_start} " = " ${target_start} " ]; then
459+ report_and_quit " Cannot retain partition, start address mismatch"
460+ fi
461+ return 0
462+ }
463+
464+ function get_disk_offset_retain_last_partition {
465+ local parttable=$1
466+ local next_to_last
467+ local start
468+ local size
469+ local offset
470+ next_to_last=$( tail -n 2 " ${parttable} " | head -n 1)
471+ if [ -n " ${next_to_last} " ]; then
472+ start=$( echo " ${next_to_last} " | cut -f2 -d= | cut -f1 -d,)
473+ size=$( echo " ${next_to_last} " | cut -f3 -d= | cut -f1 -d,)
474+ offset=$(( start + size))
475+ echo " ${offset} "
476+ else
477+ echo 0
478+ fi
479+ }
480+
481+ function optimize_count_for_32k_blocksize {
482+ local block_size=$1
483+ local count=$2
484+ local dump_bytes
485+ dump_bytes=$(( block_size * count))
486+ if [ $(( ${dump_bytes} % 32768 )) -eq 0 ]; then
487+ # dump_bytes is a multiple of 32k, use it for better I/O performance
488+ count=$(( ${dump_bytes} / 32768 ))
489+ fi
490+ echo " ${count} "
491+ }
492+
493+ function recreate_last_partition {
494+ local image_target=$1
495+ local table_type
496+ table_type=$( get_partition_table_type " ${image_target} " )
497+ if [ " ${table_type} " = " gpt" ]; then
498+ relocate_gpt_at_end_of_disk " ${image_target} "
499+ fi
500+ sfdisk -d " ${image_target} " > /tmp/parttable 2> /dev/null
501+ head -n -1 /tmp/parttable > /tmp/parttable_1
502+ tail -n 1 /tmp/parttable > /tmp/parttable_2
503+ if [ " ${table_type} " = " gpt" ]; then
504+ sed -ie " s@\(/dev/.* : start=.*\), size=.*, \(type=.*, uuid=.*, name=\" .*\" \)@\1, \2@" \
505+ /tmp/parttable_2
506+ else
507+ sed -ie " s@\(/dev/.* : start=.*\), size=.*, \(type=.*\)@\1, \2@" \
508+ /tmp/parttable_2
509+ fi
510+ cat /tmp/parttable_1 /tmp/parttable_2 > /tmp/parttable
511+ set_device_lock " ${image_target} " \
512+ sfdisk -f " ${image_target} " < /tmp/parttable
363513}
364514
365515function dump_local_image {
366516 local image_source=$1
367517 local image_target=$2
368- local progress=$3
518+ local count=$3
519+ local block_size=$4
520+ local progress=$5
521+ if [ " ${count} " -gt 0 ]; then
522+ count=" count=${count} "
523+ else
524+ unset count
525+ fi
526+ # shellcheck disable=SC2086
369527 if [ -e " ${progress} " ]; then
370528 (
371- pv -n " ${image_source} " | dd bs=32k of=" ${image_target} " & > /dev/null
529+ pv -n " ${image_source} " | \
530+ dd bs=" ${block_size} " ${count} of=" ${image_target} " & > /dev/null
372531 ) 2> " ${progress} "
373532 else
374- dd if=" ${image_source} " bs=32k of=" ${image_target} " & > /dev/null
533+ dd \
534+ if=" ${image_source} " bs=" ${block_size} " ${count} \
535+ of=" ${image_target} " & > /dev/null
375536 fi
376537}
377538
378539function dump_remote_image {
379540 local image_source=$1
380541 local image_target=$2
381- local progress=$3
542+ local count=$3
543+ local block_size=$4
544+ local progress=$5
382545 local image_size
383546 image_size=$(( blocks * blocksize))
547+ if [ " ${count} " -gt 0 ]; then
548+ count=" count=${count} "
549+ else
550+ unset count
551+ fi
552+ # shellcheck disable=SC2086
384553 if [ -e " ${progress} " ]; then
385554 (
386555 fetch_file " ${image_source} " " ${image_size} " | \
387- dd bs=32k of=" ${image_target} " & > /dev/null
556+ dd bs=" ${block_size} " ${count} of=" ${image_target} " & > /dev/null
388557 ) 2> " ${progress} "
389558 else
390559 fetch_file " ${image_source} " | \
391- dd bs=32k of=" ${image_target} " & > /dev/null
560+ dd bs=" ${block_size} " ${count} of=" ${image_target} " & > /dev/null
392561 fi
393562}
394563
@@ -406,6 +575,13 @@ function check_image_integrity {
406575 # no verification wanted
407576 return
408577 fi
578+ if getargbool 0 rd.kiwi.install.retain_last; then
579+ if [ ! -e /tmp/retain_not_applicable ]; then
580+ # no verification possible as only a portion of
581+ # the image got deployed intentionally
582+ return
583+ fi
584+ fi
409585 if command -v pv & > /dev/null && [ " ${kiwi_oemsilentverify} " = " false" ]
410586 then
411587 # verify with dialog based progress information
0 commit comments