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 ),
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;
}
/**
#include <string.h>
#include <ipxe/cpio.h>
+/** 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 );
}
/**
- * 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;
}
* 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 );
}