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
91 installation ISO image. If not specified, then
92 the script tries the following locations:
93 /mnt/windows, /mnt/windows7, /mnt/windows8,
95 -A, --waik-dir=DIR Get the boot files and boot.wim from the ISO image
96 of the Windows Automated Installation Kit (WAIK)
97 mounted on DIR instead of from a Windows
98 installation ISO. This also works if the mounted
99 ISO is for the WAIK supplement rather than for the
101 -s, --start-script=FILE Add FILE to the root directory of Windows PE image
102 and adjust \Windows\System32\winpeshl.ini to
103 execute FILE when Windows PE starts up.
104 -w, --wim=WIM Use WIM as the boot.wim file. This defaults to the
105 appropriate WIM file from the Windows or WAIK
107 -O, --overlay=DIR Adds all the files in DIR to the Windows PE image.
108 -t, --tmp-dir=DIR Use DIR as the temporary base of the ISO filesystem.
109 Defaults to making one using "mktemp -d".
110 -a, --arch=ARCH Use the Windows PE version from the WAIK that has
111 the CPU architecture ARCH. Possible values:
112 "x86" or "amd64". Default is "x86".
113 -h, --help Display this information.
114 -v, --version Show version information.
116 See \`man mkwinpeimg' for more information.
121 echo "$script_name (distributed with wimlib $WIMLIB_VERSION)"
127 process_command_line() {
129 if ! options=$(getopt -o oiw:W:s:O:t:A:a:hv -l \
130 only-wim,iso,wim:,windows-dir:,start-script:,overlay:,tmp-dir:,waik-dir:,arch:,help,version \
140 eval set -- "$options"
141 while [ $# -gt 0 ]; do
151 windows_dir_specified=yes
152 if [ -n "$waik_dir" ]; then
153 echo 1>&2 "ERROR: Cannot specify both --windows-dir and --waik-dir!"
160 if [ -n "$windows_dir" ]; then
161 echo 1>&2 "ERROR: Cannot specify both --windows-dir and --waik-dir!"
184 if [ "$2" == "x86" ]; then
187 # Need to test Itanium images before making it an
188 # option. Note: syslinux is x86 only so can't be used
189 # for the Itanium disk image.
190 #elif [ "$2" == "ia64" ]; then
193 elif [ "$2" == "amd64" ]; then
197 echo 1>&2 "ERROR: $2 is not a valid arch (x86/amd64)"
214 echo 1>&2 "Invalid option \"$1\""
222 if [ $# -ne 1 ]; then
223 echo 1>&2 "You must specify the name of the image file to create!"
224 echo 1>&2 "Run \"$script_name -h\" to see usage information."
232 if [ -z "$windows_dir_specified" ]; then
233 for windows_dir in /mnt/windows /mnt/windows7 \
234 /mnt/windows8 /mnt/windows10; \
236 if [ -d "$windows_dir"/sources ]; then
241 if [ ! -d "$windows_dir" ]; then
242 if [ -z "$windows_dir_specified" ]; then
244 ERROR: Could not find the directory that the Windows (Vista or later) ISO image
245 is mounted on! Please specify this directory using the --windows-dir option.
248 echo 1>&2 "ERROR: Could not find the directory \"$windows_dir\"!"
252 if [ ! -d "$windows_dir/sources" ]; then
254 ERROR: The directory "$windows_dir" exists, but it seems that a Windows
255 (Vista or later) installation ISO image is not mounted on it. Please mount
256 the image to continue.
262 check_needed_programs() {
263 if [ -z "$waik_dir" -o -n "$modify_wim" ]; then
264 if ! type -P wimlib-imagex &> /dev/null ; then
266 ERROR: To make a customized image of Windows PE, we need the wimlib-imagex program
267 from "wimlib" so that we can modify the boot.wim file. However, wimlib-imagex
268 doesn't seem to be installed. Please install "wimlib" to continue.
274 if [ $make = iso ]; then
275 if ! type -P mkisofs &> /dev/null ; then
277 ERROR: To make a bootable ISO image of Windows PE, we need the "mkisofs"
278 program, but it doesn't seem to be installed. Please install the "cdrkit"
279 package to continue, or try omitting the --iso option to make a disk image
280 instead of an ISO image.
284 elif [ $make = disk ] ; then
285 if ! type -P syslinux &> /dev/null ; then
287 ERROR: To make a bootable disk image of Windows PE, we need the "syslinux"
288 program, but it doesn't seem to be installed. Please install the "syslinux"
289 package to continue, or try using the --iso option to make an ISO image instead
295 if ! type -P mformat mcopy &> /dev/null; then
297 ERROR: To make a bootable disk image of Windows PE, we need the "mformat" and
298 "mcopy" programs from the "mtools" package. These programs allow us to
299 format a FAT filesystem and copy files to it without needing root privileges.
300 Please install "mtools" if you want to make a disk image of Windows PE. Or,
301 try using the --iso option to make an ISO image instead of a disk image.
306 if [ -n "$waik_dir" ] && [ -f "$waik_dir"/wAIK${arch}.msi ]; then
307 if ! type -P cabextract &> /dev/null ; then
309 ERROR: The boot files in the Windows Automated Installation Kit (WAIK) are
310 inside cabinet archives. To extract these files, we need the "cabextract"
311 program, but it doesn't seem to be installed. Please install "cabextract" to
320 get_primary_boot_files() {
321 if [ -n "$waik_dir" ]; then
322 # Get boot files from the WAIK.
323 stat_busy "Copying primary boot files from the Windows Automated Installation Kit ($waik_dir, $arch)"
325 if [ -f "$waik_dir"/wAIK${arch}.msi ]; then
326 if [ $make = iso ]; then
327 cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_etfsboot.com -p \
328 > "$tmp_dir"/etfsboot.com || stat_fail
330 cabextract "$waik_dir"/wAIK${arch}.msi -F F${arch_id}_BOOTMGR -p \
331 > "$tmp_dir"/bootmgr || stat_fail
332 cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_boot.sdi -p \
333 > "$tmp_dir"/boot/boot.sdi || stat_fail
334 cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_bcd -p \
335 > "$tmp_dir"/boot/bcd || stat_fail
336 # The WAIK supplement disc has a different structure
338 # Note: fuseiso, mount default to map=normal i.e. lowercase
339 if [ $make = iso ]; then
340 cp "$waik_dir"/${arch,,}/boot/etfsboot.com $tmp_dir/etfsboot.com || stat_fail
342 cp "$waik_dir"/${arch,,}/bootmgr $tmp_dir/bootmgr || stat_fail
343 cp "$waik_dir"/${arch,,}/boot/boot.sdi $tmp_dir/boot/boot.sdi || stat_fail
344 cp "$waik_dir"/${arch,,}/boot/bcd $tmp_dir/boot/bcd || stat_fail
348 # Get boot files from the Windows ISO
350 stat_busy "Copying primary boot files from mounted Windows ISO ($windows_dir)"
351 if [ $make = iso ]; then
352 cp "$windows_dir"/boot/etfsboot.com "$tmp_dir" || stat_fail
354 cp "$windows_dir"/bootmgr "$tmp_dir" || stat_fail
355 cp "$windows_dir"/boot/{bcd,boot.sdi} "$tmp_dir"/boot || stat_fail
362 # Copy the WIM over, or export the 2nd image in the WIM in the case of boot.wim
363 # from the Windows ISO.
365 if [ -z "$wim" ]; then
367 # WIM file unspecified- grab it from the WAIK or the Windows ISO
368 if [ -n "$waik_dir" ]; then
370 if [ -f "$waik_dir/WinPE.cab" ]; then
371 stat_busy "Extracting boot.wim from \"$waik_dir/WinPE.cab\""
372 cabextract "$waik_dir/WinPE.cab" -F F${arch_id}_WINPE.WIM -p \
373 > "$boot_wim" 2>/dev/null || stat_fail
374 # WAIK supplement has different layout
376 stat_busy "Copying boot.wim from \"${waik_dir}/${arch,,}/winpe.wim\""
377 cp "$waik_dir"/${arch,,}/winpe.wim "$boot_wim" || stat_fail
384 wim="$windows_dir/sources/boot.wim"
385 stat_busy "Exporting image from \"$wim\""
386 wimlib-imagex export "$windows_dir"/sources/boot.wim 2 \
387 --boot "$boot_wim" || stat_fail
392 stat_busy "Copying $wim to temporary directory"
393 cp "$wim" "$boot_wim"|| stat_fail
398 # Make modifications to the WIM.
403 exec 3>"$tmp_dir/__mkwinpeimg.update.cmds"
405 if [ -n "$remove_setup" ]; then
406 stat_busy "Renaming setup.exe to prevent it from bothering us"
408 rename /setup.exe /setup.exe.orig
409 rename /sources/setup.exe /sources/setup.exe.orig
414 if [ -n "$start_script" ]; then
415 stat_busy "Setting \"$start_script\" as the script to be executed when Windows PE boots"
416 start_script_base="$(basename "$start_script")"
417 cat > "$tmp_dir/__mkwinpeimg.winpeshl.ini" <<- EOF
419 %SYSTEMDRIVE%\\$start_script_base
422 add '$start_script' '/$start_script_base'
423 delete --force /Windows/System32/winpeshl.ini
424 add '$tmp_dir/__mkwinpeimg.winpeshl.ini' /Windows/System32/winpeshl.ini
429 if [ -n "$overlay" ]; then
430 stat_busy "Overlaying \"$overlay\" on the Windows PE filesystem"
439 stat_busy "Rebuilding WIM with changes made"
440 # Use case-insensitive mode; some Windows PE images contain a "windows"
441 # directory instead of a "Windows" directory...
442 WIMLIB_IMAGEX_IGNORE_CASE=1 wimlib-imagex update "$boot_wim" --rebuild \
443 < "$tmp_dir/__mkwinpeimg.update.cmds" > /dev/null || stat_fail
450 # Make the ISO using the mkisofs command from cdrkit
452 stat_busy "Making ISO image \"$image\""
454 mkisofs -sysid "" -A "" -V "Microsoft Windows PE ($arch)" -d -N \
455 -b etfsboot.com -no-emul-boot -c boot.cat -hide etfsboot.com \
456 -hide boot.cat -quiet -o "$image" "$tmp_dir" 1>&4 || stat_fail
464 stat_busy "Making disk image \"$image\""
466 image_du=$(du -s -b "$tmp_dir" | cut -f 1)
467 image_size=$(( image_du + 10000000 ))
469 mtool_conf="$(mktemp)"
471 dd if=/dev/zero of="$image" count=$(( (image_size + 4095) / 4096)) \
474 cat > "$mtool_conf" <<- EOF
476 MTOOLS_FAT_COMPATIBILITY=1
481 export MTOOLSRC="$mtool_conf"
483 mformat -h 255 -s 63 -T $(( image_size / 512)) s: || stat_fail
484 mcopy -s "$tmp_dir"/* s: || stat_fail
486 syslinux --install "$image"
489 /usr/lib/syslinux/modules/bios \
490 /usr/lib/syslinux/bios \
493 if [ -e "$biosdir/chain.c32" ]; then
498 mcopy "$biosdir/chain.c32" s: || stat_fail
499 if [ -e "$biosdir/libcom32.c32" ]; then
500 mcopy "$biosdir/libcom32.c32" s:
502 if [ -e "$biosdir/libutil.c32" ]; then
503 mcopy "$biosdir/libutil.c32" s:
505 mcopy - 's:syslinux.cfg' <<- EOF
509 APPEND ntldr=/bootmgr
516 tmp_dir="$(mktemp -d)"
517 process_command_line "$@"
519 if [ "$image" = "-" ] ; then
520 # Writing image to standard output
521 if [ "$make" != iso ]; then
522 echo 1>&2 "ERROR: Writing image to standard output is only supported in --iso mode!"
525 # We can't print anything to standard output except the ISO image
526 # itself. Play with the file descriptors.
528 exec 4>&1 # 4 is now the original standard output.
529 exec 1>&2 # Anything that goes to standard output now, by default,
530 # actually goes to standard error.
532 exec 4>&1 # 4 is now a copy of standard output
534 if [ -z "$waik_dir" ]; then
537 if [ -n "$start_script" -o -n "$overlay" -o -n "$remove_setup" ]; then
540 check_needed_programs
543 if [ $make != wim ]; then
544 mkdir -p "$tmp_dir"/{boot,sources}
545 get_primary_boot_files
548 if [ $make = wim ]; then
551 boot_wim="$tmp_dir"/sources/boot.wim
554 get_boot_wim "$boot_wim"
556 if [ -n "$modify_wim" ]; then
557 modify_boot_wim "$boot_wim" "$tmp_dir"
560 if [ $make = iso ]; then
561 make_iso_img "$image"
562 elif [ $make = disk ]; then
563 make_disk_img "$image"
566 if [ "$image" != "-" ]; then
567 echo "The image ($image) is $(stat -c %s "$image") bytes."