diff --git a/src/arch/x86/image/bzimage.c b/src/arch/x86/image/bzimage.c index 2c776147d..d2534bdad 100644 --- a/src/arch/x86/image/bzimage.c +++ b/src/arch/x86/image/bzimage.c @@ -353,24 +353,35 @@ static size_t bzimage_load_initrd ( struct image *image, const char *filename = cpio_name ( initrd ); struct cpio_header cpio; size_t offset; + size_t cpio_len; size_t pad_len; + size_t len; + unsigned int i; /* Skip hidden images */ if ( initrd->flags & IMAGE_HIDDEN ) return 0; - /* Create cpio header for non-prebuilt images */ - offset = cpio_header ( initrd, &cpio ); + /* Determine length of cpio headers for non-prebuilt images */ + len = 0; + for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ; i++ ) + len += ( cpio_len + cpio_pad_len ( cpio_len ) ); - /* Copy in initrd image body (and cpio header if applicable) */ + /* Copy in initrd image body and construct any cpio headers */ if ( address ) { - memmove_user ( address, offset, initrd->data, 0, initrd->len ); - if ( offset ) { - memset_user ( address, 0, 0, offset ); - copy_to_user ( address, 0, &cpio, sizeof ( cpio ) ); - copy_to_user ( address, sizeof ( cpio ), filename, - cpio_name_len ( initrd ) ); + memmove_user ( address, len, initrd->data, 0, initrd->len ); + memset_user ( address, 0, 0, len ); + offset = 0; + for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ; + i++ ) { + copy_to_user ( address, offset, &cpio, + sizeof ( cpio ) ); + copy_to_user ( address, ( offset + sizeof ( cpio ) ), + filename, + ( cpio_len - sizeof ( cpio ) ) ); + offset += ( cpio_len + cpio_pad_len ( cpio_len ) ); } + assert ( offset == len ); DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)" "%s%s\n", image, initrd, user_to_phys ( address, 0 ), user_to_phys ( address, offset ), @@ -379,14 +390,14 @@ static size_t bzimage_load_initrd ( struct image *image, DBGC2_MD5A ( image, user_to_phys ( address, offset ), user_to_virt ( address, offset ), initrd->len ); } - offset += initrd->len; + len += initrd->len; /* Zero-pad to next INITRD_ALIGN boundary */ - pad_len = ( ( -offset ) & ( INITRD_ALIGN - 1 ) ); + pad_len = ( ( -len ) & ( INITRD_ALIGN - 1 ) ); if ( address ) - memset_user ( address, offset, 0, pad_len ); + memset_user ( address, len, 0, pad_len ); - return offset; + return len; } /** diff --git a/src/core/cpio.c b/src/core/cpio.c index 4b607e260..4a7061960 100644 --- a/src/core/cpio.c +++ b/src/core/cpio.c @@ -34,13 +34,19 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include +/** CPIO default file mode */ +#define CPIO_DEFAULT_MODE 0644 + +/** CPIO directory mode */ +#define CPIO_DEFAULT_DIR_MODE 0755 + /** * Set field within a CPIO header * * @v field Field within CPIO header * @v value Value to set */ -void cpio_set_field ( char *field, unsigned long value ) { +static void cpio_set_field ( char *field, unsigned long value ) { char buf[9]; snprintf ( buf, sizeof ( buf ), "%08lx", value ); @@ -48,23 +54,49 @@ void cpio_set_field ( char *field, unsigned long value ) { } /** - * Get CPIO image filename + * Get maximum number of CPIO headers (i.e. number of path components) * * @v image Image - * @ret len CPIO filename length (0 for no filename) + * @ret max Maximum number of CPIO headers */ -size_t cpio_name_len ( struct image *image ) { +static unsigned int cpio_max ( struct image *image ) { const char *name = cpio_name ( image ); - char *sep; - size_t len; + unsigned int max = 0; + char c; /* Check for existence of CPIO filename */ if ( ! name ) return 0; - /* Locate separator (if any) */ - sep = strchr ( name, ' ' ); - len = ( sep ? ( ( size_t ) ( sep - name ) ) : strlen ( name ) ); + /* Count number of path separators */ + while ( ( ( c = *(name++) ) ) && ( c != ' ' ) ) { + if ( c == '/' ) + max++; + } + + return max; +} + +/** + * Get CPIO image filename + * + * @v image Image + * @v depth Path depth + * @ret len Filename length + */ +static size_t cpio_name_len ( struct image *image, unsigned int depth ) { + const char *name = cpio_name ( image ); + size_t len; + char c; + + /* Sanity check */ + assert ( name != NULL ); + + /* Calculate length up to specified path depth */ + for ( len = 0 ; ( ( ( c = name[len] ) ) && ( c != ' ' ) ) ; len++ ) { + if ( ( c == '/' ) && ( depth-- == 0 ) ) + break; + } return len; } @@ -73,55 +105,100 @@ size_t cpio_name_len ( struct image *image ) { * Parse CPIO image parameters * * @v image Image - * @v cpio CPIO header to fill in + * @v mode Mode to fill in + * @v count Number of CPIO headers to fill in */ -static void cpio_parse_cmdline ( struct image *image, - struct cpio_header *cpio ) { +static void cpio_parse_cmdline ( struct image *image, unsigned int *mode, + unsigned int *count ) { const char *arg; char *end; - unsigned int mode; - /* Look for "mode=" */ + /* Set default values */ + *mode = CPIO_DEFAULT_MODE; + *count = 1; + + /* Parse "mode=...", if present */ if ( ( arg = image_argument ( image, "mode=" ) ) ) { - mode = strtoul ( arg, &end, 8 /* Octal for file mode */ ); + *mode = strtoul ( arg, &end, 8 /* Octal for file mode */ ); if ( *end && ( *end != ' ' ) ) { DBGC ( image, "CPIO %p strange \"mode=\" " "terminator '%c'\n", image, *end ); } - cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) ); } + + /* Parse "mkdir=...", if present */ + if ( ( arg = image_argument ( image, "mkdir=" ) ) ) { + *count += strtoul ( arg, &end, 10 ); + if ( *end && ( *end != ' ' ) ) { + DBGC ( image, "CPIO %p strange \"mkdir=\" " + "terminator '%c'\n", image, *end ); + } + } + + /* Allow "mkdir=-1" to request creation of full directory tree */ + if ( ! *count ) + *count = -1U; } /** * Construct CPIO header for image, if applicable * * @v image Image + * @v index CPIO header index * @v cpio CPIO header to fill in - * @ret len Length of magic CPIO header (including filename) + * @ret len Length of CPIO header (including name, excluding NUL) */ -size_t cpio_header ( struct image *image, struct cpio_header *cpio ) { +size_t cpio_header ( struct image *image, unsigned int index, + struct cpio_header *cpio ) { + const char *name = cpio_name ( image ); + unsigned int mode; + unsigned int count; + unsigned int max; + unsigned int depth; + unsigned int i; size_t name_len; size_t len; - /* Get filename length */ - name_len = cpio_name_len ( image ); + /* Parse command line arguments */ + cpio_parse_cmdline ( image, &mode, &count ); - /* Images with no filename are assumed to already be CPIO archives */ - if ( ! name_len ) + /* Determine number of CPIO headers to be constructed */ + max = cpio_max ( image ); + if ( count > max ) + count = max; + + /* Determine path depth of this CPIO header */ + if ( index >= count ) return 0; + depth = ( max - count + index + 1 ); + + /* Get filename length */ + name_len = cpio_name_len ( image, depth ); + + /* Calculate mode and length */ + if ( depth < max ) { + /* Directory */ + mode = ( CPIO_MODE_DIR | CPIO_DEFAULT_DIR_MODE ); + len = 0; + } else { + /* File */ + mode |= CPIO_MODE_FILE; + len = image->len; + } /* Construct CPIO header */ memset ( cpio, '0', sizeof ( *cpio ) ); memcpy ( cpio->c_magic, CPIO_MAGIC, sizeof ( cpio->c_magic ) ); - cpio_set_field ( cpio->c_mode, 0100644 ); + cpio_set_field ( cpio->c_mode, mode ); cpio_set_field ( cpio->c_nlink, 1 ); - cpio_set_field ( cpio->c_filesize, image->len ); + cpio_set_field ( cpio->c_filesize, len ); cpio_set_field ( cpio->c_namesize, ( name_len + 1 /* NUL */ ) ); - cpio_parse_cmdline ( image, cpio ); + DBGC ( image, "CPIO %s %d/%d \"", image->name, depth, max ); + for ( i = 0 ; i < name_len ; i++ ) + DBGC ( image, "%c", name[i] ); + DBGC ( image, "\"\n" ); + DBGC2_HDA ( image, 0, cpio, sizeof ( *cpio ) ); /* Calculate total length */ - len = ( ( sizeof ( *cpio ) + name_len + 1 /* NUL */ + CPIO_ALIGN - 1 ) - & ~( CPIO_ALIGN - 1 ) ); - - return len; + return ( sizeof ( *cpio ) + name_len ); } diff --git a/src/include/ipxe/cpio.h b/src/include/ipxe/cpio.h index 9c5e22d5a..c45c12b11 100644 --- a/src/include/ipxe/cpio.h +++ b/src/include/ipxe/cpio.h @@ -9,6 +9,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include #include /** A CPIO archive header @@ -50,6 +51,12 @@ struct cpio_header { /** CPIO magic */ #define CPIO_MAGIC "070701" +/** CPIO type for regular files */ +#define CPIO_MODE_FILE 0100000 + +/** CPIO type for directories */ +#define CPIO_MODE_DIR 0040000 + /** CPIO header length alignment */ #define CPIO_ALIGN 4 @@ -67,8 +74,20 @@ cpio_name ( struct image *image ) { return image->cmdline; } -extern void cpio_set_field ( char *field, unsigned long value ); -extern size_t cpio_name_len ( struct image *image ); -extern size_t cpio_header ( struct image *image, struct cpio_header *cpio ); +/** + * Get CPIO header zero-padding length + * + * @v len Length of CPIO header (including name, excluding NUL) + * @ret pad_len Padding length + */ +static inline __attribute__ (( always_inline )) size_t +cpio_pad_len ( size_t len ) { + + /* Pad by at least one byte (for name's terminating NUL) */ + return ( CPIO_ALIGN - ( len % CPIO_ALIGN ) ); +} + +extern size_t cpio_header ( struct image *image, unsigned int index, + struct cpio_header *cpio ); #endif /* _IPXE_CPIO_H */ diff --git a/src/interface/efi/efi_file.c b/src/interface/efi/efi_file.c index 2ae3a0cb4..e10c2ec4e 100644 --- a/src/interface/efi/efi_file.c +++ b/src/interface/efi/efi_file.c @@ -245,6 +245,7 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { size_t cpio_len; size_t name_len; size_t len; + unsigned int i; /* Read from file */ len = 0; @@ -263,15 +264,16 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) { } len += efi_file_read_chunk ( reader, UNULL, pad_len ); - /* Read CPIO header, if applicable */ - cpio_len = cpio_header ( image, &cpio ); - if ( cpio_len ) { - name = cpio_name ( image ); - name_len = cpio_name_len ( image ); - pad_len = ( cpio_len - sizeof ( cpio ) - name_len ); + /* Read CPIO header(s), if applicable */ + name = cpio_name ( image ); + for ( i = 0 ; ( cpio_len = cpio_header ( image, i, &cpio ) ); + i++ ) { + name_len = ( cpio_len - sizeof ( cpio ) ); + pad_len = cpio_pad_len ( cpio_len ); DBGC ( file, "EFIFILE %s [%#08zx,%#08zx) %s header\n", efi_file_name ( file ), reader->pos, - ( reader->pos + cpio_len ), image->name ); + ( reader->pos + cpio_len + pad_len ), + image->name ); len += efi_file_read_chunk ( reader, virt_to_user ( &cpio ), sizeof ( cpio ) );