├── .gitignore ├── LICENSE ├── README.md ├── bin.sh └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.img 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Mathias Buus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rpi-dualbootify 2 | 3 | Combine two Raspberry Pi images into a single one that contains both. 4 | 5 | ``` 6 | npm install -g rpi-dualbootify 7 | ``` 8 | 9 | ## Usage 10 | 11 | First download two Pi images you want to combine. 12 | 13 | For an example try downloading [piCore](http://tinycorelinux.net/9.x/armv6/releases/RPi/) and [raspbian](https://www.raspberrypi.org/downloads/raspbian/) (remember to unzip them to get the raw .img files). 14 | 15 | Then to combine the two run: 16 | 17 | ``` sh 18 | rpi-dualbootify piCore-9.0.3.img 2017-09-07-raspbian-stretch-lite.img dual-image.img 19 | ``` 20 | 21 | The combined image will be stored in `dual-image.img`. 22 | 23 | You can now flash this to your SD card and it will boot the first image on the dual image. 24 | To boot the other one you can use the `autoboot.txt` feature on most bootloaders. 25 | 26 | For more options run 27 | 28 | ``` sh 29 | rpi-dualbootify --help 30 | ``` 31 | 32 | ## License 33 | 34 | This project is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 35 | -------------------------------------------------------------------------------- /bin.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | if [ "$1" == "" ] || [ "$2" == "" ] || [ "$3" == "" ]; then 6 | echo "Usage: rpi-dualbootify [options]" 7 | echo 8 | echo " --extra-sectors " 9 | echo " --extra-bytes " 10 | echo 11 | exit 1 12 | fi 13 | 14 | EXTRA_SECTORS=0 15 | IMAGE_ONE="$1" 16 | IMAGE_TWO="$2" 17 | IMAGE_DUAL="$3" 18 | 19 | shift 20 | shift 21 | shift 22 | 23 | while true; do 24 | case "$1" in 25 | --extra-sectors) EXTRA_SECTORS=$2; shift; shift ;; 26 | --extra-bytes) EXTRA_BYTES=$2; shift; shift ;; 27 | *) break ;; 28 | esac 29 | done 30 | 31 | if [ "$EXTRA_BYTES" != "" ]; then 32 | EXTRA_BYTES=$(($(echo $EXTRA_BYTES | sed 's|[bB]||' | sed 's|[kK]|* 1024|' | sed 's|[mM]|* 1024 * 1024|' | sed 's|[gG]|* 1024 * 1024 * 1024|'))) 33 | EXTRA_SECTORS=$(($EXTRA_BYTES / 512)) 34 | fi 35 | 36 | parse_image () { 37 | local IFS=$'\n' 38 | local first=true 39 | local NAME="$1_BOOT" 40 | 41 | for i in $(fdisk $2 -l | grep "^$2"); do 42 | eval "$NAME"_START=$(echo $i | awk '{print $2}') 43 | eval "$NAME"_SECTORS=$(($(echo $i | awk '{print $4}') - 1)) 44 | local NAME="$1_ROOT" 45 | done 46 | } 47 | 48 | parse_image IMAGE_ONE $IMAGE_ONE 49 | parse_image IMAGE_TWO $IMAGE_TWO 50 | 51 | IMAGE_TWO_ROOT_SECTORS=$(($IMAGE_TWO_ROOT_SECTORS + $EXTRA_SECTORS)) 52 | FDISK_CMD="$FDISK_CMD""n\np\n1\n$IMAGE_ONE_BOOT_START\n+$IMAGE_ONE_BOOT_SECTORS\n" 53 | FDISK_CMD="$FDISK_CMD""n\np\n2\n$IMAGE_ONE_ROOT_START\n+$IMAGE_ONE_ROOT_SECTORS\n" 54 | 55 | EXTENDED_START=$(($IMAGE_ONE_ROOT_START + $IMAGE_ONE_ROOT_SECTORS)) 56 | EXTENDED_SECTORS=$(($IMAGE_TWO_BOOT_SECTORS + $IMAGE_TWO_ROOT_SECTORS + 4096 + 1)) 57 | LOGICAL_START=$(($EXTENDED_START + 2048)) 58 | 59 | IMAGE_TWO_BOOT_LOGICAL_START=$(($LOGICAL_START + 1)) 60 | 61 | FDISK_CMD="$FDISK_CMD""n\ne\n3\n$(($EXTENDED_START + 1))\n+$EXTENDED_SECTORS\n" 62 | FDISK_CMD="$FDISK_CMD""n\nl\n$IMAGE_TWO_BOOT_LOGICAL_START\n+$IMAGE_TWO_BOOT_SECTORS\n" 63 | 64 | LOGICAL_START=$(($LOGICAL_START + 2048 + $IMAGE_TWO_BOOT_SECTORS + 1)) 65 | IMAGE_TWO_ROOT_LOGICAL_START=$(($LOGICAL_START + 1)) 66 | 67 | FDISK_CMD="$FDISK_CMD""n\nl\n$(($LOGICAL_START + 1))\n+$IMAGE_TWO_ROOT_SECTORS\n" 68 | FDISK_CMD="$FDISK_CMD""t\n1\nc\nt\n5\nc\n" 69 | 70 | IMAGE_SIZE=$((512 * (1 + $LOGICAL_START + 1 + $IMAGE_TWO_ROOT_SECTORS + 1))) 71 | 72 | echo Allocating "$IMAGE_DUAL"... 73 | rm -f "$IMAGE_DUAL" 74 | fallocate -l $IMAGE_SIZE "$IMAGE_DUAL" 75 | 76 | echo Running fdisk to setup partitions 77 | printf "$FDISK_CMD""w\n" | fdisk "$IMAGE_DUAL" >/dev/null 78 | 79 | echo Copying image one... 80 | dd skip=$IMAGE_ONE_BOOT_START seek=$IMAGE_ONE_BOOT_START count=$(($IMAGE_ONE_BOOT_SECTORS + 1)) ibs=512 obs=512 if=$IMAGE_ONE of=$IMAGE_DUAL conv=notrunc 2>/dev/null 81 | dd skip=$IMAGE_ONE_ROOT_START seek=$IMAGE_ONE_ROOT_START count=$(($IMAGE_ONE_ROOT_SECTORS + 1)) ibs=512 obs=512 if=$IMAGE_ONE of=$IMAGE_DUAL conv=notrunc 2>/dev/null 82 | 83 | echo Copying image two... 84 | dd skip=$IMAGE_TWO_BOOT_START seek=$IMAGE_TWO_BOOT_LOGICAL_START count=$(($IMAGE_TWO_BOOT_SECTORS + 1)) ibs=512 obs=512 if=$IMAGE_TWO of=$IMAGE_DUAL conv=notrunc 2>/dev/null 85 | dd skip=$IMAGE_TWO_ROOT_START seek=$IMAGE_TWO_ROOT_LOGICAL_START count=$(($IMAGE_TWO_ROOT_SECTORS + 1)) ibs=512 obs=512 if=$IMAGE_TWO of=$IMAGE_DUAL conv=notrunc 2>/dev/null 86 | 87 | if [ "$EXTRA_SECTORS" != "0" ]; then 88 | echo Resizing image two... 89 | DEVICE=$(sudo losetup -f --show "$IMAGE_DUAL" --offset=$((512 * IMAGE_TWO_ROOT_LOGICAL_START))) 90 | sudo e2fsck -p -f $DEVICE >/dev/null 91 | sudo resize2fs $DEVICE >/dev/null 2>/dev/null 92 | sudo losetup -d $DEVICE >/dev/null 93 | fi 94 | 95 | echo Done. Run fdisk -l $IMAGE_DUAL to see the joint partitions 96 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rpi-dualbootify", 3 | "version": "1.2.0", 4 | "description": "Combine two raspberry pi images into a single one that contains both", 5 | "dependencies": {}, 6 | "devDependencies": {}, 7 | "bin": { 8 | "rpi-dualbootify": "./bin.sh" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "https://github.com/mafintosh/rpi-dualbootify.git" 13 | }, 14 | "author": "Mathias Buus (@mafintosh)", 15 | "license": "MIT", 16 | "bugs": { 17 | "url": "https://github.com/mafintosh/rpi-dualbootify/issues" 18 | }, 19 | "homepage": "https://github.com/mafintosh/rpi-dualbootify" 20 | } 21 | --------------------------------------------------------------------------------