Skip to content

Commit

Permalink
[patch] Patch the WIM image to include additional files
Browse files Browse the repository at this point in the history
Add the ability to dynamically patch the WIM file to cause additional
files to appear within the filesystem as seen by Windows.

We do this by creating a new boot image metadata resource in which the
directory \Windows\System32 includes additional directory entries for
each file visible to wimboot (excluding any BCD, .sdi, or .wim files).

This allows the boot process to be modified using e.g. a winpeshl.ini
file dynamically generated by a web server.  For example:

  #!ipxe
  kernel /wimboot
  initrd /media/Boot/BCD BCD
  initrd /media/Boot/boot.sdi boot.sdi
  initrd /media/sources/boot.wim boot.wim boot.wim
  initrd /cgi-bin/winpeshl.ini?mac=${mac} winpeshl.ini
  boot

Signed-off-by: Michael Brown <mbrown@fensystems.co.uk>
  • Loading branch information
mcb30 committed May 10, 2017
1 parent 9285750 commit 7f42b27
Show file tree
Hide file tree
Showing 9 changed files with 862 additions and 57 deletions.
5 changes: 5 additions & 0 deletions src/cmdline.c
Expand Up @@ -35,6 +35,9 @@
/** Use raw (unpatched) BCD files */
int cmdline_rawbcd;

/** Use raw (unpatched) WIM files */
int cmdline_rawwim;

/** Allow graphical output from bootmgr/bootmgfw */
int cmdline_gui;

Expand Down Expand Up @@ -88,6 +91,8 @@ void process_cmdline ( char *cmdline ) {
/* Process this argument */
if ( strcmp ( key, "rawbcd" ) == 0 ) {
cmdline_rawbcd = 1;
} else if ( strcmp ( key, "rawwim" ) == 0 ) {
cmdline_rawwim = 1;
} else if ( strcmp ( key, "gui" ) == 0 ) {
cmdline_gui = 1;
} else if ( strcmp ( key, "pause" ) == 0 ) {
Expand Down
1 change: 1 addition & 0 deletions src/cmdline.h
Expand Up @@ -28,6 +28,7 @@
*/

extern int cmdline_rawbcd;
extern int cmdline_rawwim;
extern int cmdline_gui;
extern int cmdline_pause;
extern int cmdline_pause_quiet;
Expand Down
12 changes: 5 additions & 7 deletions src/script.lds
Expand Up @@ -67,23 +67,21 @@ SECTIONS {

/* bootmgr.exe hardcodes the address 0x30000 for use as a
* buffer accessible by real-mode code. We can't fit our
* code, data, and stack below this region, so explicitly
* place the stack higher in memory.
* .text, .data, and .bss below this region, so explicitly
* place the .bss higher in memory.
*/
_forbidden_start = 0x30000;
_forbidden_end = 0x40000;

/* Uninitialised data section */
.bss ( NOLOAD ) : {
_bss = .;
*(.bss)
*(.bss.*)
*(COMMON)

ASSERT ( ABSOLUTE ( . ) <= ABSOLUTE ( _forbidden_start ),
"Binary is too large" );
. = ABSOLUTE ( _forbidden_end );

*(.bss)
*(.bss.*)
*(COMMON)
*(.stack)
*(.stack.*)
. = ALIGN ( alignment );
Expand Down
2 changes: 1 addition & 1 deletion src/vdisk.c
Expand Up @@ -32,7 +32,7 @@
#include "vdisk.h"

/** Virtual files */
static struct vdisk_file vdisk_files[VDISK_MAX_FILES];
struct vdisk_file vdisk_files[VDISK_MAX_FILES];

/**
* Read from virtual Master Boot Record
Expand Down
2 changes: 2 additions & 0 deletions src/vdisk.h
Expand Up @@ -600,6 +600,8 @@ struct vdisk_file {
size_t len );
};

extern struct vdisk_file vdisk_files[VDISK_MAX_FILES];

extern void vdisk_read ( uint64_t lba, unsigned int count, void *data );
extern struct vdisk_file *
vdisk_add_file ( const char *name, void *opaque, size_t len,
Expand Down
134 changes: 112 additions & 22 deletions src/wim.c
Expand Up @@ -285,10 +285,45 @@ int wim_read ( struct vdisk_file *file, struct wim_header *header,
return 0;
}

/**
* Get number of images
*
* @v file Virtual file
* @v header WIM header
* @v count Count of images to fill in
* @ret rc Return status code
*/
int wim_count ( struct vdisk_file *file, struct wim_header *header,
unsigned int *count ) {
struct wim_lookup_entry entry;
size_t offset;
int rc;

/* Count metadata entries */
for ( offset = 0 ; ( offset + sizeof ( entry ) ) <= header->lookup.len ;
offset += sizeof ( entry ) ) {

/* Read entry */
if ( ( rc = wim_read ( file, header, &header->lookup, &entry,
offset, sizeof ( entry ) ) ) != 0 )
return rc;

/* Check for metadata entries */
if ( entry.resource.zlen__flags & WIM_RESHDR_METADATA ) {
(*count)++;
DBG2 ( "...found image %d metadata at +%#zx\n",
*count, offset );
}
}

return 0;
}

/**
* Get WIM image metadata
*
* @v file Virtual file
* @v header WIM header
* @v index Image index, or 0 to use boot image
* @v meta Metadata to fill in
* @ret rc Return status code
Expand Down Expand Up @@ -318,8 +353,8 @@ int wim_metadata ( struct vdisk_file *file, struct wim_header *header,
/* Look for our target entry */
if ( entry.resource.zlen__flags & WIM_RESHDR_METADATA ) {
found++;
DBG ( "...found image %d metadata at +%#zx\n",
found, offset );
DBG2 ( "...found image %d metadata at +%#zx\n",
found, offset );
if ( found == index ) {
memcpy ( meta, &entry.resource,
sizeof ( *meta ) );
Expand All @@ -339,23 +374,23 @@ int wim_metadata ( struct vdisk_file *file, struct wim_header *header,
* @v file Virtual file
* @v header WIM header
* @v meta Metadata
* @v offset Directory offset
* @v name Name
* @v offset Directory offset (will be updated)
* @v direntry Directory entry to fill in
* @ret rc Return status code
*/
static int wim_direntry ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, size_t offset,
const wchar_t *name,
struct wim_resource_header *meta,
const wchar_t *name, size_t *offset,
struct wim_directory_entry *direntry ) {
wchar_t name_buf[ wcslen ( name ) + 1 /* NUL */ ];
int rc;

/* Search directory */
for ( ; ; offset += direntry->len ) {
for ( ; ; *offset += direntry->len ) {

/* Read length field */
if ( ( rc = wim_read ( file, header, meta, direntry, offset,
if ( ( rc = wim_read ( file, header, meta, direntry, *offset,
sizeof ( direntry->len ) ) ) != 0 )
return rc;

Expand All @@ -366,7 +401,7 @@ static int wim_direntry ( struct vdisk_file *file, struct wim_header *header,
}

/* Read fixed-length portion of directory entry */
if ( ( rc = wim_read ( file, header, meta, direntry, offset,
if ( ( rc = wim_read ( file, header, meta, direntry, *offset,
sizeof ( *direntry ) ) ) != 0 )
return rc;

Expand All @@ -376,7 +411,7 @@ static int wim_direntry ( struct vdisk_file *file, struct wim_header *header,

/* Read name */
if ( ( rc = wim_read ( file, header, meta, &name_buf,
( offset + sizeof ( *direntry ) ),
( *offset + sizeof ( *direntry ) ),
sizeof ( name_buf ) ) ) != 0 )
return rc;

Expand All @@ -390,23 +425,21 @@ static int wim_direntry ( struct vdisk_file *file, struct wim_header *header,
}

/**
* Get file resource
* Get directory entry for a path
*
* @v file Virtual file
* @v header WIM header
* @v meta Metadata
* @v path Path to file
* @v resource File resource to fill in
* @v path Path to file/directory
* @v offset Directory entry offset to fill in
* @v direntry Directory entry to fill in
* @ret rc Return status code
*/
int wim_file ( struct vdisk_file *file, struct wim_header *header,
int wim_path ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, const wchar_t *path,
struct wim_resource_header *resource ) {
size_t *offset, struct wim_directory_entry *direntry ) {
wchar_t path_copy[ wcslen ( path ) + 1 /* WNUL */ ];
struct wim_security_header security;
struct wim_directory_entry direntry;
struct wim_lookup_entry entry;
size_t offset;
wchar_t *name;
wchar_t *next;
int rc;
Expand All @@ -417,22 +450,48 @@ int wim_file ( struct vdisk_file *file, struct wim_header *header,
return rc;

/* Get root directory offset */
offset = ( ( security.len + sizeof ( uint64_t ) - 1 ) &
~( sizeof ( uint64_t ) - 1 ) );
direntry->subdir = ( ( security.len + sizeof ( uint64_t ) - 1 ) &
~( sizeof ( uint64_t ) - 1 ) );

/* Find directory entry */
name = memcpy ( path_copy, path, sizeof ( path_copy ) );
do {
next = wcschr ( name, L'\\' );
if ( next )
*next = L'\0';
if ( ( rc = wim_direntry ( file, header, meta, offset, name,
&direntry ) ) != 0 )
*offset = direntry->subdir;
if ( ( rc = wim_direntry ( file, header, meta, name, offset,
direntry ) ) != 0 )
return rc;
offset = direntry.subdir;
name = ( next + 1 );
} while ( next );

return 0;
}

/**
* Get file resource
*
* @v file Virtual file
* @v header WIM header
* @v meta Metadata
* @v path Path to file
* @v resource File resource to fill in
* @ret rc Return status code
*/
int wim_file ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, const wchar_t *path,
struct wim_resource_header *resource ) {
struct wim_directory_entry direntry;
struct wim_lookup_entry entry;
size_t offset;
int rc;

/* Find directory entry */
if ( ( rc = wim_path ( file, header, meta, path, &offset,
&direntry ) ) != 0 )
return rc;

/* File matching file entry */
for ( offset = 0 ; ( offset + sizeof ( entry ) ) <= header->lookup.len ;
offset += sizeof ( entry ) ) {
Expand All @@ -455,3 +514,34 @@ int wim_file ( struct vdisk_file *file, struct wim_header *header,
DBG ( "Cannot find file %ls\n", path );
return -1;
}

/**
* Get length of a directory
*
* @v file Virtual file
* @v header WIM header
* @v meta Metadata
* @v offset Directory offset
* @v len Directory length to fill in (excluding terminator)
* @ret rc Return status code
*/
int wim_dir_len ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, size_t offset,
size_t *len ) {
struct wim_directory_entry direntry;
int rc;

/* Search directory */
for ( *len = 0 ; ; *len += direntry.len ) {

/* Read length field */
if ( ( rc = wim_read ( file, header, meta, &direntry,
( offset + *len ),
sizeof ( direntry.len ) ) ) != 0 )
return rc;

/* Check for end of this directory */
if ( ! direntry.len )
return 0;
}
}
17 changes: 17 additions & 0 deletions src/wim.h
Expand Up @@ -166,14 +166,31 @@ struct wim_directory_entry {
uint16_t name_len;
} __attribute__ (( packed ));

/** Normal file */
#define WIM_ATTR_NORMAL 0x00000080UL

/** No security information exists for this file */
#define WIM_NO_SECURITY 0xffffffffUL

/** Windows complains if the time fields are left at zero */
#define WIM_MAGIC_TIME 0x1a7b83d2ad93000ULL

extern int wim_header ( struct vdisk_file *file, struct wim_header *header );
extern int wim_count ( struct vdisk_file *file, struct wim_header *header,
unsigned int *count );
extern int wim_metadata ( struct vdisk_file *file, struct wim_header *header,
unsigned int index, struct wim_resource_header *meta);
extern int wim_read ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *resource, void *data,
size_t offset, size_t len );
extern int wim_path ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, const wchar_t *path,
size_t *offset, struct wim_directory_entry *direntry );
extern int wim_file ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, const wchar_t *path,
struct wim_resource_header *resource );
extern int wim_dir_len ( struct vdisk_file *file, struct wim_header *header,
struct wim_resource_header *meta, size_t offset,
size_t *len );

#endif /* _WIM_H */
4 changes: 4 additions & 0 deletions src/wimboot.h
Expand Up @@ -62,6 +62,10 @@
#include <stdint.h>
#include <bootapp.h>

/** Construct wide-character version of a string constant */
#define L( x ) _L ( x )
#define _L( x ) L ## x

/** Page size */
#define PAGE_SIZE 4096

Expand Down

0 comments on commit 7f42b27

Please sign in to comment.