| 1 | #!/bin/sh |
|---|
| 2 | # Philipp Wuensche |
|---|
| 3 | # This script is considered beer ware (http://en.wikipedia.org/wiki/Beerware) |
|---|
| 4 | # |
|---|
| 5 | # DISCLAIMER: Use at your own risk! Always make backups, don't blame me if this renders your system unusable or you lose any data! |
|---|
| 6 | # |
|---|
| 7 | # This only works with FreeBSD 8.0 (tested with 8.0-RELEASE), you have been warned! |
|---|
| 8 | # |
|---|
| 9 | # Startup the FreeBSD livefs CD. Go into the Fixit console. Create /var/db if you want to use DHCP. Configure |
|---|
| 10 | # your network settings. Fetch http://anonsvn.h3q.com/s/gpt-zfsroot.sh |
|---|
| 11 | # Execute the script with the following parameter: |
|---|
| 12 | # |
|---|
| 13 | # -p sets the geom provider to use, you can use multiple. Add a name for the GPT labels: -p ad4=black -p ad6=white |
|---|
| 14 | # -s sets the swap_partition_size to create, you can use m/M for megabyte or g/G for gigabyte |
|---|
| 15 | # -S sets the zfs_partition_size to create, you can use m/M for megabyte or g/G for gigabyte, default is all available size |
|---|
| 16 | # -n sets the name of the zpool to create |
|---|
| 17 | # -f sets the ftp-server used for getting the freebsd packages, default is ftp.freebsd.org |
|---|
| 18 | # -m sets the zpool raid-mode, stripe (only single disk), mirror (at least two disks) and raidz (at least three disks) |
|---|
| 19 | # -d sets local directory to get distribution packages from |
|---|
| 20 | # |
|---|
| 21 | # You can use more than one device, creating a mirror. To specify more than one device, use multiple -p options. |
|---|
| 22 | # eg. create-zfsboot-gpt_livecd.sh -p ad0 -p ad1 -s 512m -n tank |
|---|
| 23 | |
|---|
| 24 | ftphost='ftp.freebsd.org' |
|---|
| 25 | |
|---|
| 26 | usage="Usage: create-zfsboot-gpt_livecd.sh -p <geom_provider> -s <swap_partition_size> -S <zfs_partition_size> -n <zpoolname> -m <zpool-raidmode> -d <distdir> -f <ftphost>" |
|---|
| 27 | |
|---|
| 28 | exerr () { echo -e "$*" >&2 ; exit 1; } |
|---|
| 29 | |
|---|
| 30 | while getopts p:s:S:n:f:m:d: arg |
|---|
| 31 | do case ${arg} in |
|---|
| 32 | p) provider="$provider ${OPTARG}";; |
|---|
| 33 | s) swap_partition_size=${OPTARG};; |
|---|
| 34 | S) zfs_partition_size=${OPTARG};; |
|---|
| 35 | n) poolname=${OPTARG};; |
|---|
| 36 | f) ftphost=${OPTARG};; |
|---|
| 37 | m) mode=${OPTARG};; |
|---|
| 38 | d) distdir=${OPTARG};; |
|---|
| 39 | ?) exerr ${usage};; |
|---|
| 40 | esac; done; shift $(( ${OPTIND} - 1 )) |
|---|
| 41 | |
|---|
| 42 | if [ -z "$poolname" ] || [ -z "$provider" ] ; then |
|---|
| 43 | exerr ${usage} |
|---|
| 44 | exit |
|---|
| 45 | fi |
|---|
| 46 | |
|---|
| 47 | if [ "$distdir" -a ! -f "$distdir/base/install.sh" -o ! -f "$distdir/kernels/install.sh" -o ! -f "$distdir/src/install.sh" ]; then |
|---|
| 48 | echo "Sorry, no distribution files found in ${distdir}!" |
|---|
| 49 | exit |
|---|
| 50 | fi |
|---|
| 51 | |
|---|
| 52 | # count the number of providers |
|---|
| 53 | devcount=`echo ${provider} |wc -w` |
|---|
| 54 | |
|---|
| 55 | # set our default zpool mirror-mode |
|---|
| 56 | if [ -z "$mode" ]; then |
|---|
| 57 | if [ "$devcount" -gt "1" ]; then |
|---|
| 58 | mode='mirror' |
|---|
| 59 | else |
|---|
| 60 | mode='stripe' |
|---|
| 61 | fi |
|---|
| 62 | fi |
|---|
| 63 | |
|---|
| 64 | # check the settings for the users that want to set the mode on their own |
|---|
| 65 | if [ "$devcount" -eq "1" -a "$mode" = "mirror" ]; then |
|---|
| 66 | echo "A mirror needs at least two disks!" |
|---|
| 67 | exit |
|---|
| 68 | fi |
|---|
| 69 | if [ "$devcount" -lt "3" -a "$mode" = "raidz" ]; then |
|---|
| 70 | echo "Sorry, you need at least three disks for a zfs raidz!" |
|---|
| 71 | exit |
|---|
| 72 | fi |
|---|
| 73 | |
|---|
| 74 | check_size () { |
|---|
| 75 | ref_disk_size=`gpart list $ref_disk | grep 'Mediasize' | awk '{print $2}'` |
|---|
| 76 | if [ "${zfs_partition_size}" ]; then |
|---|
| 77 | _zfs_partition_size=`echo "${zfs_partition_size}"|tr GMKBWX gmkbwx|sed -Ees:g:km:g -es:m:kk:g -es:k:"*2b":g -es:b:"*128w":g -es:w:"*4 ":g -e"s:(^|[^0-9])0x:\1\0X:g" -ey:x:"*":|bc |sed "s:\.[0-9]*$::g"` |
|---|
| 78 | fi |
|---|
| 79 | if [ "${swap_partition_size}" ]; then |
|---|
| 80 | _swap_partition_size=`echo "${swap_partition_size}"|tr GMKBWX gmkbwx|sed -Ees:g:km:g -es:m:kk:g -es:k:"*2b":g -es:b:"*128w":g -es:w:"*4 ":g -e"s:(^|[^0-9])0x:\1\0X:g" -ey:x:"*":|bc |sed "s:\.[0-9]*$::g"` |
|---|
| 81 | fi |
|---|
| 82 | total_size=$((${_zfs_partition_size}+${_swap_partition_size}+162)) |
|---|
| 83 | if [ "${total_size}" -gt "${ref_disk_size}" ]; then |
|---|
| 84 | echo "ERROR: The current settings for the partitions sizes will not fit onto your disk." |
|---|
| 85 | exit |
|---|
| 86 | else |
|---|
| 87 | fi |
|---|
| 88 | } |
|---|
| 89 | |
|---|
| 90 | get_disk_labelname () { |
|---|
| 91 | label=${disk##*=} |
|---|
| 92 | disk=${disk%%=*} |
|---|
| 93 | } |
|---|
| 94 | |
|---|
| 95 | echo "Creating GPT label on disks:" |
|---|
| 96 | for disk in $provider; do |
|---|
| 97 | get_disk_labelname |
|---|
| 98 | if [ ! -e "/dev/$disk" ]; then |
|---|
| 99 | echo " -> ERROR: $disk does not exist" |
|---|
| 100 | exit |
|---|
| 101 | fi |
|---|
| 102 | echo " -> $disk" |
|---|
| 103 | dd if=/dev/zero of=/dev/$disk bs=512 count=79 > /dev/null 2>&1 |
|---|
| 104 | gpart create -s gpt $disk > /dev/null |
|---|
| 105 | done |
|---|
| 106 | |
|---|
| 107 | echo |
|---|
| 108 | sleep 1 |
|---|
| 109 | |
|---|
| 110 | smallest_disk_size='0' |
|---|
| 111 | echo "Checking disks for size:" |
|---|
| 112 | for disk in $provider; do |
|---|
| 113 | get_disk_labelname |
|---|
| 114 | disk_size=`gpart show $disk | grep '\- free \-' | awk '{print $2}'` |
|---|
| 115 | echo " -> $disk - total size $disk_size" |
|---|
| 116 | if [ "$smallest_disk_size" -gt "$disk_size" ] || [ "$smallest_disk_size" -eq "0" ]; then |
|---|
| 117 | smallest_disk_size=$disk_size |
|---|
| 118 | ref_disk=$disk |
|---|
| 119 | fi |
|---|
| 120 | done |
|---|
| 121 | |
|---|
| 122 | # check if the size fits |
|---|
| 123 | check_size |
|---|
| 124 | |
|---|
| 125 | echo |
|---|
| 126 | echo "NOTICE: Using $ref_disk (smallest or only disk) as reference disk for calculation offsets" |
|---|
| 127 | echo |
|---|
| 128 | sleep 2 |
|---|
| 129 | |
|---|
| 130 | echo "Creating GPT boot partition on disks:" |
|---|
| 131 | for disk in $provider; do |
|---|
| 132 | get_disk_labelname |
|---|
| 133 | echo " -> ${disk}" |
|---|
| 134 | gpart add -s 128 -t freebsd-boot $disk > /dev/null |
|---|
| 135 | done |
|---|
| 136 | |
|---|
| 137 | echo |
|---|
| 138 | sleep 2 |
|---|
| 139 | |
|---|
| 140 | if [ "$swap_partition_size" ]; then |
|---|
| 141 | echo "Creating GPT swap partition on with size ${swap_partition_size} on disks: " |
|---|
| 142 | for disk in $provider; do |
|---|
| 143 | get_disk_labelname |
|---|
| 144 | echo " -> ${disk} (Label: ${label})" |
|---|
| 145 | gpart add -s $swap_partition_size -t freebsd-swap -l swap-${label} ${disk} > /dev/null |
|---|
| 146 | done |
|---|
| 147 | fi |
|---|
| 148 | |
|---|
| 149 | echo |
|---|
| 150 | sleep 2 |
|---|
| 151 | |
|---|
| 152 | offset=`gpart show $ref_disk | grep '\- free \-' | awk '{print $1}'` |
|---|
| 153 | if [ -z "${zfs_partition_size}" ]; then |
|---|
| 154 | size=`gpart show $ref_disk | grep '\- free \-' | awk '{print $2}'` |
|---|
| 155 | else |
|---|
| 156 | size=${zfs_partition_size} |
|---|
| 157 | fi |
|---|
| 158 | |
|---|
| 159 | echo "Creating GPT ZFS partition on with size ${size} on disks: " |
|---|
| 160 | for disk in $provider; do |
|---|
| 161 | get_disk_labelname |
|---|
| 162 | echo " -> ${disk} (Label: ${label})" |
|---|
| 163 | gpart add -b $offset -s $size -t freebsd-zfs -l system-${label} ${disk} > /dev/null |
|---|
| 164 | labellist="${labellist} gpt/system-${label}" |
|---|
| 165 | done |
|---|
| 166 | |
|---|
| 167 | echo |
|---|
| 168 | sleep 2 |
|---|
| 169 | |
|---|
| 170 | # Make first partition active so the BIOS boots from it |
|---|
| 171 | for disk in $provider; do |
|---|
| 172 | get_disk_labelname |
|---|
| 173 | echo 'a 1' | fdisk -f - $disk > /dev/null 2>&1 |
|---|
| 174 | done |
|---|
| 175 | |
|---|
| 176 | echo |
|---|
| 177 | sleep 2 |
|---|
| 178 | |
|---|
| 179 | kldload /mnt2/boot/kernel/opensolaris.ko |
|---|
| 180 | kldload /mnt2/boot/kernel/zfs.ko |
|---|
| 181 | |
|---|
| 182 | # we need to create /boot/zfs so zpool.cache can be written. |
|---|
| 183 | mkdir /boot/zfs |
|---|
| 184 | |
|---|
| 185 | # Create the pool and the rootfs |
|---|
| 186 | |
|---|
| 187 | if [ "$mode" = "raidz" ]; then |
|---|
| 188 | zpool create -f $poolname raidz ${labellist} |
|---|
| 189 | fi |
|---|
| 190 | if [ "$mode" = "mirror" ]; then |
|---|
| 191 | zpool create -f $poolname mirror ${labellist} |
|---|
| 192 | fi |
|---|
| 193 | if [ "$mode" = "stripe" ]; then |
|---|
| 194 | zpool create -f $poolname ${labellist} |
|---|
| 195 | fi |
|---|
| 196 | |
|---|
| 197 | if [ `zpool list -H -o name $poolname` != "$poolname" ]; then |
|---|
| 198 | echo "ERROR: Could not create zpool $poolname" |
|---|
| 199 | exit |
|---|
| 200 | fi |
|---|
| 201 | |
|---|
| 202 | sleep 2 |
|---|
| 203 | |
|---|
| 204 | echo "Setting checksum to fletcher4" |
|---|
| 205 | zfs set checksum=fletcher4 ${poolname} |
|---|
| 206 | |
|---|
| 207 | rootzfs="$poolname/ROOT/$poolname" |
|---|
| 208 | |
|---|
| 209 | zfs create -p $rootzfs |
|---|
| 210 | zfs set freebsd:boot-environment=1 $rootzfs |
|---|
| 211 | |
|---|
| 212 | # Now we create some stuff we also would like to have in seperate filesystems |
|---|
| 213 | for filesystem in usr-src usr-obj usr-local tmp; do |
|---|
| 214 | echo "Creating $poolname/$filesystem" |
|---|
| 215 | zfs create $poolname/$filesystem |
|---|
| 216 | if [ "$filesystem" = "tmp" ]; then |
|---|
| 217 | chmod 1777 /$poolname/tmp |
|---|
| 218 | fi |
|---|
| 219 | zfs umount $poolname/$filesystem |
|---|
| 220 | _filesystem=`echo $filesystem | sed s:-:\/:g` |
|---|
| 221 | zfs set mountpoint=/${_filesystem} $poolname/${filesystem} |
|---|
| 222 | done |
|---|
| 223 | |
|---|
| 224 | mkdir /$rootzfs/usr |
|---|
| 225 | |
|---|
| 226 | zfs set mountpoint=/$rootzfs/usr/src $poolname/usr-src |
|---|
| 227 | zfs mount $poolname/usr-src |
|---|
| 228 | |
|---|
| 229 | zfs set mountpoint=/$rootzfs/usr/obj $poolname/usr-obj |
|---|
| 230 | zfs mount $poolname/usr-obj |
|---|
| 231 | |
|---|
| 232 | echo #################################### |
|---|
| 233 | if [ -z "$distdir" ]; then |
|---|
| 234 | echo "Now installing base, ssys, slib and kernels via $ftphost. This may take a while, depending on your network connection." |
|---|
| 235 | zfs create $poolname/installdata |
|---|
| 236 | cd /$poolname/installdata |
|---|
| 237 | |
|---|
| 238 | if [ `pwd` != "/$poolname/installdata" ]; then |
|---|
| 239 | echo "ERROR: Could not change directoy to /$poolname/installdata. Aborting." |
|---|
| 240 | exit |
|---|
| 241 | fi |
|---|
| 242 | |
|---|
| 243 | sleep 2 |
|---|
| 244 | arch=`uname -p` |
|---|
| 245 | release=`uname -r` |
|---|
| 246 | echo |
|---|
| 247 | echo "Fetching FreeBSD ${release}-${arch}:" |
|---|
| 248 | for pkg in base kernels; do |
|---|
| 249 | mkdir /$poolname/installdata/${pkg} |
|---|
| 250 | cd /$poolname/installdata/${pkg} |
|---|
| 251 | echo " -> $pkg" |
|---|
| 252 | ftp -V "$ftphost:pub/FreeBSD/releases/${arch}/${release}/${pkg}/*" |
|---|
| 253 | done |
|---|
| 254 | mkdir /$poolname/installdata/src |
|---|
| 255 | cd /$poolname/installdata/src/ |
|---|
| 256 | echo " -> ssys" |
|---|
| 257 | ftp -V "$ftphost:pub/FreeBSD/releases/${arch}/${release}/src/ssys*" |
|---|
| 258 | echo " -> slib" |
|---|
| 259 | ftp -V "$ftphost:pub/FreeBSD/releases/${arch}/${release}/src/slib*" |
|---|
| 260 | ftp -V "$ftphost:pub/FreeBSD/releases/${arch}/${release}/src/install.sh" |
|---|
| 261 | distdir="/$poolname/installdata" |
|---|
| 262 | else |
|---|
| 263 | echo "Using distribution packages from $distdir" |
|---|
| 264 | fi |
|---|
| 265 | |
|---|
| 266 | export DESTDIR=/$rootzfs/ |
|---|
| 267 | |
|---|
| 268 | echo |
|---|
| 269 | echo "Extracting base into $DESTDIR" |
|---|
| 270 | cd /$distdir/base ; cat base.?? | tar --unlink -xpzf - -C ${DESTDIR:-/} |
|---|
| 271 | cd /$distdir/src ; sh ./install.sh sys lib |
|---|
| 272 | echo "Extracting kernel into ${DESTDIR}boot" |
|---|
| 273 | cd /$distdir/kernels ; sh ./install.sh generic |
|---|
| 274 | |
|---|
| 275 | cd /$rootzfs/boot ; cp -rp GENERIC/* /$rootzfs/boot/kernel/ |
|---|
| 276 | |
|---|
| 277 | if [ -z "$distdir" ]; then |
|---|
| 278 | zfs destroy $poolname/installdata |
|---|
| 279 | fi |
|---|
| 280 | |
|---|
| 281 | echo 'LOADER_ZFS_SUPPORT=YES' >> /$rootzfs/etc/make.conf |
|---|
| 282 | |
|---|
| 283 | echo |
|---|
| 284 | echo "I will now build the ZFS aware boot-loader, expect some funky compile output and ignore the errors." |
|---|
| 285 | sleep 2 |
|---|
| 286 | echo '#!/bin/sh |
|---|
| 287 | mount -t devfs devfs /dev |
|---|
| 288 | export DESTDIR="" |
|---|
| 289 | cd /usr/src/sys/boot/ |
|---|
| 290 | make -s obj |
|---|
| 291 | make -s depend |
|---|
| 292 | make -s |
|---|
| 293 | mkdir -p /usr/share/man/man5 |
|---|
| 294 | cd /usr/src/sys/boot/i386/loader; make -s install |
|---|
| 295 | cd /usr/src/sys/boot/i386/zfsboot; make -s install |
|---|
| 296 | cd /usr/src/sys/boot/i386/gptzfsboot; make -s install |
|---|
| 297 | cd /usr/src/sys/boot/i386/pmbr; make -s install |
|---|
| 298 | umount /dev |
|---|
| 299 | exit' > /$rootzfs/tmp/chroot-command.sh |
|---|
| 300 | chmod +x /$rootzfs/tmp/chroot-command.sh |
|---|
| 301 | chroot /$rootzfs/ /tmp/chroot-command.sh |
|---|
| 302 | rm /$rootzfs/tmp/chroot-command.sh |
|---|
| 303 | |
|---|
| 304 | # fix usr/share/man/man8 |
|---|
| 305 | rm /$rootzfs/usr/share/man/man8 |
|---|
| 306 | |
|---|
| 307 | echo |
|---|
| 308 | echo "Installing new bootcode on disks: " |
|---|
| 309 | for disk in $provider; do |
|---|
| 310 | get_disk_labelname |
|---|
| 311 | echo " -> ${disk}" |
|---|
| 312 | gpart bootcode -b /$rootzfs/boot/pmbr -p /$rootzfs/boot/gptzfsboot -i 1 $disk > /dev/null |
|---|
| 313 | done |
|---|
| 314 | echo |
|---|
| 315 | |
|---|
| 316 | # We need to fix /usr/src so it is mounted correct when booting from ZFS |
|---|
| 317 | zfs umount $poolname/usr-src |
|---|
| 318 | zfs set mountpoint=/usr/src $poolname/usr-src |
|---|
| 319 | |
|---|
| 320 | # We need to fix /usr/obj so it is mounted correct when booting from ZFS |
|---|
| 321 | zfs umount $poolname/usr-obj |
|---|
| 322 | zfs set mountpoint=/usr/obj $poolname/usr-obj |
|---|
| 323 | |
|---|
| 324 | # Enable the new filesystem as zpool bootfs |
|---|
| 325 | zpool set bootfs=$rootzfs $poolname |
|---|
| 326 | |
|---|
| 327 | # We still need to tell the kernel from where to mount its root-filesystem |
|---|
| 328 | echo 'zfs_load="YES"' >> /$rootzfs/boot/loader.conf |
|---|
| 329 | echo "vfs.root.mountfrom=\"zfs:$rootzfs\"" >> /$rootzfs/boot/loader.conf |
|---|
| 330 | echo 'zfs_enable="YES"' >> /$rootzfs/etc/rc.conf |
|---|
| 331 | touch /$rootzfs/etc/fstab |
|---|
| 332 | |
|---|
| 333 | if [ "$swap_partition_size" ]; then |
|---|
| 334 | echo "Adding swap partitions in fstab:" |
|---|
| 335 | for disk in $provider; do |
|---|
| 336 | get_disk_labelname |
|---|
| 337 | echo " -> /dev/gpt/swap-${label}" |
|---|
| 338 | echo "/dev/gpt/swap-${label} none swap sw 0 0" >> /$rootzfs/etc/fstab |
|---|
| 339 | done |
|---|
| 340 | fi |
|---|
| 341 | echo |
|---|
| 342 | |
|---|
| 343 | # Copy the zpool.cache to the new filesystem |
|---|
| 344 | cp /boot/zfs/zpool.cache /$rootzfs/boot/zfs/zpool.cache |
|---|
| 345 | |
|---|
| 346 | sleep 5 |
|---|
| 347 | |
|---|
| 348 | echo "Please reboot the system from the harddisk(s), remove the FreeBSD CD from you cdrom!" |
|---|