3 # This script can make a customized bootable image of Windows PE.
6 # Copyright (C) 2012, 2013 Eric Biggers
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 script_name="$(basename "$0")"
23 WIMLIB_VERSION=@VERSION@
28 # stty will fail when stdin isn't a terminal
30 # stty gives "rows cols"; strip the rows number, we just want columns
31 STAT_COL=${STAT_COL##* }
32 elif tput cols &>/dev/null; then
33 # is /usr/share/terminfo already mounted, and TERM recognized?
36 if (( STAT_COL == 0 )); then
37 # if output was 0 (serial console), set default width to 80
41 # we use 13 characters for our own stuff
42 STAT_COL=$(( STAT_COL - 13 ))
46 RESTORE_POSITION="\e[u"
47 DEL_TEXT="\e[$(( STAT_COL + 4 ))G"
61 printf "${PREFIX_REG} ${1} "
62 printf "${SAVE_POSITION}"
85 Usage: $script_name [OPTIONS] IMAGE
87 -i, --iso Make an ISO image instead of a disk image.
88 -o, --only-wim Make neither a disk image nor an ISO image;
89 instead, only make a modified boot.wim file.
90 -W, --windows-dir=DIR Use DIR as the location of the mounted Windows 7
91 or Windows 8 DVD. Default is /mnt/windows,
92 then /mnt/windows7, then /mnt/windows8.
93 -A, --waik-dir=DIR Get the boot files and boot.wim from the ISO of the
94 Windows Automated Installation Kit mounted on DIR
95 instead of from the Windows 7 or Windows 8 DVD.
96 This also works if the mounted ISO is for the
97 WAIK supplement rather than the WAIK itself.
98 -s, --start-script=FILE Add FILE to the root directory of Windows PE image
99 and adjust \Windows\System32\winpeshl.ini to
100 execute FILE when Windows PE starts up.
101 -w, --wim=WIM Use WIM as the boot.wim file. This defaults to the
102 appropriate WIM file from the Windows DVD, WAIK,
104 -O, --overlay=DIR Adds all the files in DIR to the Windows PE image.
105 -t, --tmp-dir=DIR Use DIR as the temporary base of the ISO filesystem.
106 Defaults to making one using "mktemp -d".
107 -a, --arch=ARCH Use the Windows PE version from the WAIK that has
108 the CPU architecture ARCH. Possible values:
109 "x86" or "amd64". Default is "x86".
110 -h, --help Display this information.
111 -v, --version Show version information.
113 See \`man mkwinpeimg' for more information.
118 echo "$script_name (distributed with wimlib $WIMLIB_VERSION)"
124 process_command_line() {
126 if ! options=$(getopt -o oiw:W:s:O:t:A:a:hv -l \
127 only-wim,iso,wim:,windows-dir:,start-script:,overlay:,tmp-dir:,waik-dir:,arch:,help,version \
137 eval set -- "$options"
138 while [ $# -gt 0 ]; do
148 windows_dir_specified=yes
149 if [ -n "$waik_dir" ]; then
150 echo 1>&2 "ERROR: Cannot specify both --windows-dir and --waik-dir!"
157 if [ -n "$windows_dir" ]; then
158 echo 1>&2 "ERROR: Cannot specify both --windows-dir and --waik-dir!"
181 if [ "$2" == "x86" ]; then
184 # Need to test Itanium images before making it an
185 # option. Note: syslinux is x86 only so can't be used
186 # for the Itanium disk image.
187 #elif [ "$2" == "ia64" ]; then
190 elif [ "$2" == "amd64" ]; then
194 echo 1>&2 "ERROR: $2 is not a valid arch (x86/amd64)"
211 echo 1>&2 "Invalid option \"$1\""
219 if [ $# -ne 1 ]; then
220 echo 1>&2 "You must specify the name of the image file to create!"
221 echo 1>&2 "Run \"$script_name -h\" to see usage information."
229 if [ -z "$windows_dir_specified" ]; then
230 for windows_dir in /mnt/windows /mnt/windows7 /mnt/windows8; do
231 if [ -d "$windows_dir"/sources ]; then
236 if [ ! -d "$windows_dir" ]; then
237 if [ -z "$windows_dir_specified" ]; then
239 ERROR: Could not find the directory that the Windows 7 or 8 ISO image is mounted
240 on! Please specify this directory using the --windows-dir option.
243 echo 1>&2 "ERROR: Could not find the directory \"$windows_dir\"!"
247 if [ ! -d "$windows_dir/sources" ]; then
249 ERROR: The directory "$windows_dir" exists, but it seems that the Windows 7 or 8
250 ISO image is not mounted on it. Please mount the image to continue.
256 check_needed_programs() {
257 if [ -z "$waik_dir" -o -n "$modify_wim" ]; then
258 if ! type -P wimlib-imagex &> /dev/null ; then
260 ERROR: To make a customized image of Windows PE, we need the wimlib-imagex program
261 from "wimlib" so that we can modify the boot.wim file. However, wimlib-imagex
262 doesn't seem to be installed. Please install "wimlib" to continue.
268 if [ $make = iso ]; then
269 if ! type -P mkisofs &> /dev/null ; then
271 ERROR: To make a bootable ISO image of Windows PE, we need the "mkisofs"
272 program, but it doesn't seem to be installed. Please install the "cdrkit"
273 package to continue, or try omitting the --iso option to make a disk image
274 instead of an ISO image.
278 elif [ $make = disk ] ; then
279 if ! type -P syslinux &> /dev/null ; then
281 ERROR: To make a bootable disk image of Windows PE, we need the "syslinux"
282 program, but it doesn't seem to be installed. Please install the "syslinux"
283 package to continue, or try using the --iso option to make an ISO image instead
289 if ! type -P mformat mcopy &> /dev/null; then
291 ERROR: To make a bootable disk image of Windows PE, we need the "mformat" and
292 "mcopy" programs from the "mtools" package. These programs allow us to
293 format a FAT filesystem and copy files to it without needing root privileges.
294 Please install "mtools" if you want to make a disk image of Windows PE. Or,
295 try using the --iso option to make an ISO image instead of a disk image.
300 if [ -n "$waik_dir" ] && [ -f "$waik_dir"/wAIK${arch}.msi ]; then
301 if ! type -P cabextract &> /dev/null ; then
303 ERROR: The boot files in the Windows Automated Installation Kit (WAIK) are
304 inside cabinet archives. To extract these files, we need the "cabextract"
305 program, but it doesn't seem to be installed. Please install "cabextract" to
314 get_primary_boot_files() {
315 if [ -n "$waik_dir" ]; then
316 # Get boot files from the WAIK.
317 stat_busy "Copying primary boot files from the Windows Automated Installation Kit ($waik_dir, $arch)"
319 if [ -f "$waik_dir"/wAIK${arch}.msi ]; then
320 if [ $make = iso ]; then
321 cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_etfsboot.com -p \
322 > "$tmp_dir"/etfsboot.com || stat_fail
324 cabextract "$waik_dir"/wAIK${arch}.msi -F F${arch_id}_BOOTMGR -p \
325 > "$tmp_dir"/bootmgr || stat_fail
326 cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_boot.sdi -p \
327 > "$tmp_dir"/boot/boot.sdi || stat_fail
328 cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_bcd -p \
329 > "$tmp_dir"/boot/bcd || stat_fail
330 # The WAIK supplement disc has a different structure
332 # Note: fuseiso, mount default to map=normal i.e. lowercase
333 if [ $make = iso ]; then
334 cp "$waik_dir"/${arch,,}/boot/etfsboot.com $tmp_dir/etfsboot.com || stat_fail
336 cp "$waik_dir"/${arch,,}/bootmgr $tmp_dir/bootmgr || stat_fail
337 cp "$waik_dir"/${arch,,}/boot/boot.sdi $tmp_dir/boot/boot.sdi || stat_fail
338 cp "$waik_dir"/${arch,,}/boot/bcd $tmp_dir/boot/bcd || stat_fail
342 # Get boot files from the Windows ISO
344 stat_busy "Copying primary boot files from mounted Windows DVD ($windows_dir)"
345 if [ $make = iso ]; then
346 cp "$windows_dir"/boot/etfsboot.com "$tmp_dir" || stat_fail
348 cp "$windows_dir"/bootmgr "$tmp_dir" || stat_fail
349 cp "$windows_dir"/boot/{bcd,boot.sdi} "$tmp_dir"/boot || stat_fail
356 # Copy the WIM over, or export the 2nd image in the WIM in the case of boot.wim
357 # from the Windows DVD.
359 if [ -z "$wim" ]; then
361 # WIM file unspecified- grab it from the WAIK or the Windows DVD
362 if [ -n "$waik_dir" ]; then
364 if [ -f "$waik_dir/WinPE.cab" ]; then
365 stat_busy "Extracting boot.wim from \"$waik_dir/WinPE.cab\""
366 cabextract "$waik_dir/WinPE.cab" -F F${arch_id}_WINPE.WIM -p \
367 > "$boot_wim" 2>/dev/null || stat_fail
368 # WAIK supplement has different layout
370 stat_busy "Copying boot.wim from \"${waik_dir}/${arch,,}/winpe.wim\""
371 cp "$waik_dir"/${arch,,}/winpe.wim "$boot_wim" || stat_fail
378 wim="$windows_dir/sources/boot.wim"
379 stat_busy "Exporting image from \"$wim\""
380 wimlib-imagex export "$windows_dir"/sources/boot.wim 2 \
381 --boot "$boot_wim" || stat_fail
386 stat_busy "Copying $wim to temporary directory"
387 cp "$wim" "$boot_wim"|| stat_fail
392 # Make modifications to the WIM.
397 exec 3>"$tmp_dir/__mkwinpeimg.update.cmds"
399 if [ -n "$remove_setup" ]; then
400 stat_busy "Renaming setup.exe to prevent it from bothering us"
402 rename /setup.exe /setup.exe.orig
403 rename /sources/setup.exe /sources/setup.exe.orig
408 if [ -n "$start_script" ]; then
409 stat_busy "Setting \"$start_script\" as the script to be executed when Windows PE boots"
410 start_script_base="$(basename "$start_script")"
411 cat > "$tmp_dir/__mkwinpeimg.winpeshl.ini" <<- EOF
413 %SYSTEMDRIVE%\\$start_script_base
416 add '$start_script' '/$start_script_base'
417 delete --force /Windows/System32/winpeshl.ini
418 add '$tmp_dir/__mkwinpeimg.winpeshl.ini' /Windows/System32/winpeshl.ini
423 if [ -n "$overlay" ]; then
424 stat_busy "Overlaying \"$overlay\" on the Windows PE filesystem"
433 stat_busy "Rebuilding WIM with changes made"
434 wimlib-imagex update "$boot_wim" --rebuild \
435 < "$tmp_dir/__mkwinpeimg.update.cmds" > /dev/null || stat_fail
442 # Make the ISO using the mkisofs command from cdrkit
444 stat_busy "Making ISO image \"$image\""
446 mkisofs -sysid "" -A "" -V "Microsoft Windows PE ($arch)" -d -N \
447 -b etfsboot.com -no-emul-boot -c boot.cat -hide etfsboot.com \
448 -hide boot.cat -quiet -o "$image" "$tmp_dir" 1>&4 || stat_fail
456 stat_busy "Making disk image \"$image\""
458 image_du=$(du -s -b "$tmp_dir" | cut -f 1)
459 image_size=$(( image_du + 10000000 ))
461 mtool_conf="$(mktemp)"
463 dd if=/dev/zero of="$image" count=$(( (image_size + 4095) / 4096)) \
466 cat > "$mtool_conf" <<- EOF
468 MTOOLS_FAT_COMPATIBILITY=1
473 export MTOOLSRC="$mtool_conf"
475 mformat -h 255 -s 63 -T $(( image_size / 512)) s: || stat_fail
476 mcopy -s "$tmp_dir"/* s: || stat_fail
478 syslinux --install "$image"
480 syslinuxdir="/usr/lib/syslinux"
482 if [ -d "$syslinuxdir/bios" ]; then
483 biosdir="$syslinuxdir/bios"
485 biosdir="$syslinuxdir"
488 mcopy "$biosdir/chain.c32" s: || stat_fail
489 if [ -e "$biosdir/libcom32.c32" ]; then
490 mcopy "$biosdir/libcom32.c32" s:
492 if [ -e "$biosdir/libutil.c32" ]; then
493 mcopy "$biosdir/libutil.c32" s:
495 mcopy - 's:syslinux.cfg' <<- EOF
499 APPEND ntldr=/bootmgr
506 tmp_dir="$(mktemp -d)"
507 process_command_line "$@"
509 if [ "$image" = "-" ] ; then
510 # Writing image to standard output
511 if [ "$make" != iso ]; then
512 echo 1>&2 "ERROR: Writing image to standard output is only supported in --iso mode!"
515 # We can't print anything to standard output except the ISO image
516 # itself. Play with the file descriptors.
518 exec 4>&1 # 4 is now the original standard output.
519 exec 1>&2 # Anything that goes to standard output now, by default,
520 # actually goes to standard error.
522 exec 4>&1 # 4 is now a copy of standard output
524 if [ -z "$waik_dir" ]; then
527 if [ -n "$start_script" -o -n "$overlay" -o -n "$remove_setup" ]; then
530 check_needed_programs
533 if [ $make != wim ]; then
534 mkdir -p "$tmp_dir"/{boot,sources}
535 get_primary_boot_files
538 if [ $make = wim ]; then
541 boot_wim="$tmp_dir"/sources/boot.wim
544 get_boot_wim "$boot_wim"
546 if [ -n "$modify_wim" ]; then
547 modify_boot_wim "$boot_wim" "$tmp_dir"
550 if [ $make = iso ]; then
551 make_iso_img "$image"
552 elif [ $make = disk ]; then
553 make_disk_img "$image"
556 if [ "$image" != "-" ]; then
557 echo "The image ($image) is $(stat -c %s "$image") bytes."