3 # This script can make a customized bootable image of Windows PE.
6 # Copyright (C) 2012 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)"
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}"
80 if mountpoint -q "$mnt_dir" ; then
81 imagex unmount "$mnt_dir"
88 Usage: $script_name [OPTIONS] IMAGE
90 -i, --iso Make an ISO image instead of a disk image.
91 -o, --only-wim Make neither a disk image nor an ISO image;
92 instead, only make a modified boot.wim file.
93 -W, --windows-dir=DIR Use DIR as the location of the mounted Windows 7
94 or Windows 8 DVD. Default is /mnt/windows,
95 then /mnt/windows7, then /mnt/windows8.
96 -A, --waik-dir=DIR Get the boot files and boot.wim from the ISO of the
97 Windows Automated Installation Kit mounted on DIR
98 instead of from the Windows 7 or Windows 8 DVD.
99 -s, --start-script=FILE Add FILE to the root directory of Windows PE image
100 and adjust \Windows\System32\winpeshl.ini to
101 execute FILE when Windows PE starts up.
102 -w, --wim=WIM Use WIM as the boot.wim file. Defaults to
103 sources/boot.wim in the Windows DVD directory, or
104 F1_WINPE.WIM from the WAIK if --waik-dir is
106 -O, --overlay=DIR Adds all the files in DIR to the Windows PE image.
107 -t, --tmp-dir=DIR Use DIR as the temporary base of the ISO filesystem.
108 Defaults to making one using "mktemp -d".
109 -a, --arch=ARCH Use the Windows PE version from the WAIK that has
110 the CPU architecture ARCH. Possible values:
111 "x86" or "amd64". Default is "x86".
112 -h, --help Display this information.
113 -v, --version Show version information.
115 See \`man mkwinpeimg' for more information.
120 echo "$script_name (wimlib $WIMLIB_VERSION)"
126 process_command_line() {
128 if ! options=$(getopt -o oiw:W:s:O:t:A:a:hv -l \
129 only-wim,iso,wim:,windows-dir:,start-script:,overlay:,tmp-dir:,waik-dir:,arch:,help,version \
139 eval set -- "$options"
140 while [ $# -gt 0 ]; do
150 windows_dir_specified=yes
151 if [ -n "$waik_dir" ]; then
152 echo "ERROR: Cannot specify both --windows-dir and --waik-dir!"
159 if [ -n "$windows_dir" ]; then
160 echo "ERROR: Cannot specify both --windows-dir and --waik-dir!"
183 if [ "$2" == "x86" ]; then
186 # Need to test Itanium images before making it an
187 # option. Note: syslinux is x86 only so can't be used
188 # for the Itanium disk image.
189 #elif [ "$2" == "ia64" ]; then
192 elif [ "$2" == "amd64" ]; then
196 echo "ERROR: $2 is not a valid arch (x86/amd64)"
213 echo "Invalid option \"$1\""
221 if [ $# -ne 1 ]; then
222 echo "You must specify the name of the image file to create!"
223 echo "Run \"$script_name -h\" to see usage information."
231 if [ -z "$windows_dir_specified" ]; then
232 for windows_dir in /mnt/windows /mnt/windows7 /mnt/windows8; do
233 if [ -d "$windows_dir"/sources ]; then
238 if [ ! -d "$windows_dir" ]; then
239 if [ -z "$windows_dir_specified" ]; then
241 ERROR: Could not find the directory that the Windows 7 or 8 ISO image is mounted
242 on! Please specify this directory using the --windows-dir option.
245 echo "ERROR: Could not find the directory \"$windows_dir\"!"
249 if [ ! -d "$windows_dir/sources" ]; then
251 ERROR: The directory "$windows_dir" exists, but it seems that the Windows 7 or 8
252 ISO image is not mounted on it. Please mount the image to continue.
258 check_needed_programs() {
259 if [ -z "$waik_dir" -o -n "$modify_wim" ]; then
260 if ! type -P imagex &> /dev/null ; then
262 ERROR: To make a customized image of Windows PE, we need the "imagex" program
263 from WIMLIB so that we can modify the boot.wim file. However, "imagex"
264 doesn't seem to be installed. Please install WIMLIB to continue.
270 if [ $make = iso ]; then
271 if ! type -P mkisofs &> /dev/null ; then
273 ERROR: To make a bootable ISO image of Windows PE, we need the "mkisofs"
274 program, but it doesn't seem to be installed. Please install the "cdrkit"
275 package to continue, or try omitting the --iso option to make a disk image
276 instead of an ISO image.
280 elif [ $make = disk ] ; then
281 if ! type -P syslinux &> /dev/null ; then
283 ERROR: To make a bootable disk image of Windows PE, we need the "syslinux"
284 program, but it doesn't seem to be installed. Please install the "syslinux"
285 package to continue, or try using the --iso option to make an ISO image instead
291 if ! type -P mformat mcopy &> /dev/null; then
293 ERROR: To make a bootable disk image of Windows PE, we need the "mformat" and
294 "mcopy" programs from the "mtools" package. These programs allow us to
295 format a FAT filesystem and copy files to it without needing root privileges.
296 Please install "mtools" if you want to make a disk image of Windows PE. Or,
297 try using the --iso option to make an ISO image instead of a disk image.
302 if [ -n "$waik_dir" ]; then
303 if ! type -P cabextract &> /dev/null ; then
305 ERROR: The boot files in the Windows Automated Installation Kit (WAIK) are
306 inside cabinet archives. To extract these files, we need the "cabextract"
307 program, but it doesn't seem to be installed. Please install "cabextract" to
316 get_primary_boot_files() {
317 if [ -n "$waik_dir" ]; then
318 # Get boot files from the WAIK.
320 stat_busy "Copying primary boot files from the Windows Automated Installation Kit ($waik_dir, $arch)"
321 if [ $make = iso ]; then
322 cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_etfsboot.com -p \
323 > "$tmp_dir"/etfsboot.com || stat_fail
325 cabextract "$waik_dir"/wAIK${arch}.msi -F F${arch_id}_BOOTMGR -p \
326 > "$tmp_dir"/bootmgr || stat_fail
327 cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_boot.sdi -p \
328 > "$tmp_dir"/boot/boot.sdi || stat_fail
329 cabextract "$waik_dir"/wAIK${arch}.msi -F F_WINPE_${arch}_bcd -p \
330 > "$tmp_dir"/boot/bcd || stat_fail
333 # Get boot files from the Windows ISO
335 stat_busy "Copying primary boot files from mounted Windows DVD ($windows_dir)"
336 if [ $make = iso ]; then
337 cp "$windows_dir"/boot/etfsboot.com "$tmp_dir" || stat_fail
339 cp "$windows_dir"/bootmgr "$tmp_dir" || stat_fail
340 cp "$windows_dir"/boot/{bcd,boot.sdi} "$tmp_dir"/boot || stat_fail
347 # Copy the WIM over, or export the 2nd image in the WIM in the case of boot.wim
348 # from the Windows DVD.
350 if [ -z "$wim" ]; then
352 # WIM file unspecified- grab it from the WAIK or the Windows DVD
353 if [ -n "$waik_dir" ]; then
355 stat_busy "Extracting boot.wim from \"$waik_dir/WinPE.cab\""
356 cabextract "$waik_dir/WinPE.cab" -F F${arch_id}_WINPE.WIM -p \
357 > "$boot_wim" 2>/dev/null || stat_fail
362 wim="$windows_dir/sources/boot.wim"
363 stat_busy "Exporting image from \"$wim\""
364 imagex export "$windows_dir"/sources/boot.wim 2 \
365 --boot "$boot_wim" || stat_fail
370 stat_busy "Copying $wim to temporary directory"
371 cp "$wim" "$boot_wim"|| stat_fail
380 # Make modifications to the WIM.
381 stat_busy "Mounting "$1" read-write"
383 mkdir -p "$mnt_dir" || stat_fail
384 imagex mountrw "$boot_wim" "$mnt_dir"|| stat_fail
388 if [ -n "$remove_setup" ]; then
389 stat_busy "Renaming setup.exe to prevent it from bothering us"
390 mv "$mnt_dir"/setup.exe{,.bkup} || stat_fail
391 mv "$mnt_dir"/sources/setup.exe{,.bkup} || stat_fail
395 if [ -n "$start_script" ]; then
396 stat_busy "Setting \"$start_script\" as the script to be executed"\
397 "when Windows PE boots"
398 cp "$start_script" "$mnt_dir"|| stat_fail
399 cat > "$mnt_dir/Windows/System32/winpeshl.ini" << EOF
401 %SYSTEMDRIVE%\\$start_script
406 if [ -n "$overlay" ]; then
407 stat_busy "Overlaying \"$overlay\" on the Windows PE filesystem"
408 cp -r "$overlay"/* "$mnt_dir" || stat_fail
412 stat_busy "Rebuilding WIM with changes made"
413 imagex unmount --commit "$mnt_dir" || stat_fail
422 # Make the ISO using the mkisofs command from cdrkit
424 stat_busy "Making ISO image \"$image\""
426 mkisofs -sysid "" -A "" -V "Microsoft Windows PE ($arch)" -d -N \
427 -b etfsboot.com -no-emul-boot -c boot.cat -hide etfsboot.com \
428 -hide boot.cat -quiet -o "$image" "$tmp_dir" || stat_fail
436 stat_busy "Making disk image \"$image\""
438 image_du=$(du -s -b "$tmp_dir" | cut -f 1)
439 image_size=$(( image_du + 10000000 ))
441 mtool_conf="$(mktemp)"
443 dd if=/dev/zero of="$image" count=$(( (image_size + 4095) / 4096)) \
446 cat > "$mtool_conf" << EOF
448 MTOOLS_FAT_COMPATIBILITY=1
453 export MTOOLSRC="$mtool_conf"
455 mformat -h 255 -s 63 -T $(( image_size / 512)) s:
456 mcopy -s "$tmp_dir"/* s:
458 syslinux --install "$image"
459 mcopy /usr/lib/syslinux/chain.c32 s:
460 mcopy - 's:syslinux.cfg' << EOF
464 APPEND ntldr=/bootmgr
471 tmp_dir="$(mktemp -d)"
472 mnt_dir="$tmp_dir"/.boot.wim.mount
473 process_command_line "$@"
474 if [ -z "$waik_dir" ]; then
477 if [ -n "$start_script" -o -n "$overlay" -o -n "$remove_setup" ]; then
480 check_needed_programs
483 if [ $make != wim ]; then
484 mkdir -p "$tmp_dir"/{boot,sources}
485 get_primary_boot_files
488 if [ $make = wim ]; then
491 boot_wim="$tmp_dir"/sources/boot.wim
494 get_boot_wim "$boot_wim"
496 if [ -n "$modify_wim" ]; then
497 modify_boot_wim "$boot_wim" "$mnt_dir"
500 if [ $make = iso ]; then
501 make_iso_img "$image"
502 elif [ $make = disk ]; then
503 make_disk_img "$image"
506 echo "The image ($image) is $(stat -c %s "$image") bytes."