Skip to content

Commit 12ea8c4

Browse files
committed
[cpio] Allow for construction of parent directories as needed
iPXE allows individual raw files to be automatically wrapped with suitable CPIO headers and injected into the magic initrd image as exposed to a booted Linux kernel. This feature is currently limited to placing files within directories that already exist in the initrd filesystem. Remove this limitation by adding the ability for iPXE to construct CPIO headers for parent directories as needed, under control of the "mkdir=<n>" command-line argument. For example: initrd config.ign /usr/share/oem/config.ign mkdir=1 will create CPIO headers for the "/usr/share/oem" directory as well as for the "/usr/share/oem/config.ign" file itself. This simplifies the process of booting operating systems such as Flatcar Linux, which otherwise require the single "config.ign" file to be manually wrapped up as a CPIO archive solely in order to create the relevant parent directory entries. The value <n> may be used to control the number of parent directory entries that are created. For example, "mkdir=2" would cause up to two parent directories to be created (i.e. "/usr/share" and "/usr/share/oem" in the above example). A negative value such as "mkdir=-1" may be used to create all parent directories up to the root of the tree. Do not create any parent directory entries by default, since doing so would potentially cause the modes and ownership information for existing directories to be overwritten. Signed-off-by: Michael Brown <[email protected]>
1 parent e7595fe commit 12ea8c4

File tree

4 files changed

+161
-52
lines changed

4 files changed

+161
-52
lines changed

src/arch/x86/image/bzimage.c

+24-13
Original file line numberDiff line numberDiff line change
@@ -353,24 +353,35 @@ static size_t bzimage_load_initrd ( struct image *image,
353353
const char *filename = cpio_name ( initrd );
354354
struct cpio_header cpio;
355355
size_t offset;
356+
size_t cpio_len;
356357
size_t pad_len;
358+
size_t len;
359+
unsigned int i;
357360

358361
/* Skip hidden images */
359362
if ( initrd->flags & IMAGE_HIDDEN )
360363
return 0;
361364

362-
/* Create cpio header for non-prebuilt images */
363-
offset = cpio_header ( initrd, &cpio );
365+
/* Determine length of cpio headers for non-prebuilt images */
366+
len = 0;
367+
for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ; i++ )
368+
len += ( cpio_len + cpio_pad_len ( cpio_len ) );
364369

365-
/* Copy in initrd image body (and cpio header if applicable) */
370+
/* Copy in initrd image body and construct any cpio headers */
366371
if ( address ) {
367-
memmove_user ( address, offset, initrd->data, 0, initrd->len );
368-
if ( offset ) {
369-
memset_user ( address, 0, 0, offset );
370-
copy_to_user ( address, 0, &cpio, sizeof ( cpio ) );
371-
copy_to_user ( address, sizeof ( cpio ), filename,
372-
cpio_name_len ( initrd ) );
372+
memmove_user ( address, len, initrd->data, 0, initrd->len );
373+
memset_user ( address, 0, 0, len );
374+
offset = 0;
375+
for ( i = 0 ; ( cpio_len = cpio_header ( initrd, i, &cpio ) ) ;
376+
i++ ) {
377+
copy_to_user ( address, offset, &cpio,
378+
sizeof ( cpio ) );
379+
copy_to_user ( address, ( offset + sizeof ( cpio ) ),
380+
filename,
381+
( cpio_len - sizeof ( cpio ) ) );
382+
offset += ( cpio_len + cpio_pad_len ( cpio_len ) );
373383
}
384+
assert ( offset == len );
374385
DBGC ( image, "bzImage %p initrd %p [%#08lx,%#08lx,%#08lx)"
375386
"%s%s\n", image, initrd, user_to_phys ( address, 0 ),
376387
user_to_phys ( address, offset ),
@@ -379,14 +390,14 @@ static size_t bzimage_load_initrd ( struct image *image,
379390
DBGC2_MD5A ( image, user_to_phys ( address, offset ),
380391
user_to_virt ( address, offset ), initrd->len );
381392
}
382-
offset += initrd->len;
393+
len += initrd->len;
383394

384395
/* Zero-pad to next INITRD_ALIGN boundary */
385-
pad_len = ( ( -offset ) & ( INITRD_ALIGN - 1 ) );
396+
pad_len = ( ( -len ) & ( INITRD_ALIGN - 1 ) );
386397
if ( address )
387-
memset_user ( address, offset, 0, pad_len );
398+
memset_user ( address, len, 0, pad_len );
388399

389-
return offset;
400+
return len;
390401
}
391402

392403
/**

src/core/cpio.c

+106-29
Original file line numberDiff line numberDiff line change
@@ -34,37 +34,69 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
3434
#include <string.h>
3535
#include <ipxe/cpio.h>
3636

37+
/** CPIO default file mode */
38+
#define CPIO_DEFAULT_MODE 0644
39+
40+
/** CPIO directory mode */
41+
#define CPIO_DEFAULT_DIR_MODE 0755
42+
3743
/**
3844
* Set field within a CPIO header
3945
*
4046
* @v field Field within CPIO header
4147
* @v value Value to set
4248
*/
43-
void cpio_set_field ( char *field, unsigned long value ) {
49+
static void cpio_set_field ( char *field, unsigned long value ) {
4450
char buf[9];
4551

4652
snprintf ( buf, sizeof ( buf ), "%08lx", value );
4753
memcpy ( field, buf, 8 );
4854
}
4955

5056
/**
51-
* Get CPIO image filename
57+
* Get maximum number of CPIO headers (i.e. number of path components)
5258
*
5359
* @v image Image
54-
* @ret len CPIO filename length (0 for no filename)
60+
* @ret max Maximum number of CPIO headers
5561
*/
56-
size_t cpio_name_len ( struct image *image ) {
62+
static unsigned int cpio_max ( struct image *image ) {
5763
const char *name = cpio_name ( image );
58-
char *sep;
59-
size_t len;
64+
unsigned int max = 0;
65+
char c;
6066

6167
/* Check for existence of CPIO filename */
6268
if ( ! name )
6369
return 0;
6470

65-
/* Locate separator (if any) */
66-
sep = strchr ( name, ' ' );
67-
len = ( sep ? ( ( size_t ) ( sep - name ) ) : strlen ( name ) );
71+
/* Count number of path separators */
72+
while ( ( ( c = *(name++) ) ) && ( c != ' ' ) ) {
73+
if ( c == '/' )
74+
max++;
75+
}
76+
77+
return max;
78+
}
79+
80+
/**
81+
* Get CPIO image filename
82+
*
83+
* @v image Image
84+
* @v depth Path depth
85+
* @ret len Filename length
86+
*/
87+
static size_t cpio_name_len ( struct image *image, unsigned int depth ) {
88+
const char *name = cpio_name ( image );
89+
size_t len;
90+
char c;
91+
92+
/* Sanity check */
93+
assert ( name != NULL );
94+
95+
/* Calculate length up to specified path depth */
96+
for ( len = 0 ; ( ( ( c = name[len] ) ) && ( c != ' ' ) ) ; len++ ) {
97+
if ( ( c == '/' ) && ( depth-- == 0 ) )
98+
break;
99+
}
68100

69101
return len;
70102
}
@@ -73,55 +105,100 @@ size_t cpio_name_len ( struct image *image ) {
73105
* Parse CPIO image parameters
74106
*
75107
* @v image Image
76-
* @v cpio CPIO header to fill in
108+
* @v mode Mode to fill in
109+
* @v count Number of CPIO headers to fill in
77110
*/
78-
static void cpio_parse_cmdline ( struct image *image,
79-
struct cpio_header *cpio ) {
111+
static void cpio_parse_cmdline ( struct image *image, unsigned int *mode,
112+
unsigned int *count ) {
80113
const char *arg;
81114
char *end;
82-
unsigned int mode;
83115

84-
/* Look for "mode=" */
116+
/* Set default values */
117+
*mode = CPIO_DEFAULT_MODE;
118+
*count = 1;
119+
120+
/* Parse "mode=...", if present */
85121
if ( ( arg = image_argument ( image, "mode=" ) ) ) {
86-
mode = strtoul ( arg, &end, 8 /* Octal for file mode */ );
122+
*mode = strtoul ( arg, &end, 8 /* Octal for file mode */ );
87123
if ( *end && ( *end != ' ' ) ) {
88124
DBGC ( image, "CPIO %p strange \"mode=\" "
89125
"terminator '%c'\n", image, *end );
90126
}
91-
cpio_set_field ( cpio->c_mode, ( 0100000 | mode ) );
92127
}
128+
129+
/* Parse "mkdir=...", if present */
130+
if ( ( arg = image_argument ( image, "mkdir=" ) ) ) {
131+
*count += strtoul ( arg, &end, 10 );
132+
if ( *end && ( *end != ' ' ) ) {
133+
DBGC ( image, "CPIO %p strange \"mkdir=\" "
134+
"terminator '%c'\n", image, *end );
135+
}
136+
}
137+
138+
/* Allow "mkdir=-1" to request creation of full directory tree */
139+
if ( ! *count )
140+
*count = -1U;
93141
}
94142

95143
/**
96144
* Construct CPIO header for image, if applicable
97145
*
98146
* @v image Image
147+
* @v index CPIO header index
99148
* @v cpio CPIO header to fill in
100-
* @ret len Length of magic CPIO header (including filename)
149+
* @ret len Length of CPIO header (including name, excluding NUL)
101150
*/
102-
size_t cpio_header ( struct image *image, struct cpio_header *cpio ) {
151+
size_t cpio_header ( struct image *image, unsigned int index,
152+
struct cpio_header *cpio ) {
153+
const char *name = cpio_name ( image );
154+
unsigned int mode;
155+
unsigned int count;
156+
unsigned int max;
157+
unsigned int depth;
158+
unsigned int i;
103159
size_t name_len;
104160
size_t len;
105161

106-
/* Get filename length */
107-
name_len = cpio_name_len ( image );
162+
/* Parse command line arguments */
163+
cpio_parse_cmdline ( image, &mode, &count );
108164

109-
/* Images with no filename are assumed to already be CPIO archives */
110-
if ( ! name_len )
165+
/* Determine number of CPIO headers to be constructed */
166+
max = cpio_max ( image );
167+
if ( count > max )
168+
count = max;
169+
170+
/* Determine path depth of this CPIO header */
171+
if ( index >= count )
111172
return 0;
173+
depth = ( max - count + index + 1 );
174+
175+
/* Get filename length */
176+
name_len = cpio_name_len ( image, depth );
177+
178+
/* Calculate mode and length */
179+
if ( depth < max ) {
180+
/* Directory */
181+
mode = ( CPIO_MODE_DIR | CPIO_DEFAULT_DIR_MODE );
182+
len = 0;
183+
} else {
184+
/* File */
185+
mode |= CPIO_MODE_FILE;
186+
len = image->len;
187+
}
112188

113189
/* Construct CPIO header */
114190
memset ( cpio, '0', sizeof ( *cpio ) );
115191
memcpy ( cpio->c_magic, CPIO_MAGIC, sizeof ( cpio->c_magic ) );
116-
cpio_set_field ( cpio->c_mode, 0100644 );
192+
cpio_set_field ( cpio->c_mode, mode );
117193
cpio_set_field ( cpio->c_nlink, 1 );
118-
cpio_set_field ( cpio->c_filesize, image->len );
194+
cpio_set_field ( cpio->c_filesize, len );
119195
cpio_set_field ( cpio->c_namesize, ( name_len + 1 /* NUL */ ) );
120-
cpio_parse_cmdline ( image, cpio );
196+
DBGC ( image, "CPIO %s %d/%d \"", image->name, depth, max );
197+
for ( i = 0 ; i < name_len ; i++ )
198+
DBGC ( image, "%c", name[i] );
199+
DBGC ( image, "\"\n" );
200+
DBGC2_HDA ( image, 0, cpio, sizeof ( *cpio ) );
121201

122202
/* Calculate total length */
123-
len = ( ( sizeof ( *cpio ) + name_len + 1 /* NUL */ + CPIO_ALIGN - 1 )
124-
& ~( CPIO_ALIGN - 1 ) );
125-
126-
return len;
203+
return ( sizeof ( *cpio ) + name_len );
127204
}

src/include/ipxe/cpio.h

+22-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
1111

12+
#include <stdint.h>
1213
#include <ipxe/image.h>
1314

1415
/** A CPIO archive header
@@ -50,6 +51,12 @@ struct cpio_header {
5051
/** CPIO magic */
5152
#define CPIO_MAGIC "070701"
5253

54+
/** CPIO type for regular files */
55+
#define CPIO_MODE_FILE 0100000
56+
57+
/** CPIO type for directories */
58+
#define CPIO_MODE_DIR 0040000
59+
5360
/** CPIO header length alignment */
5461
#define CPIO_ALIGN 4
5562

@@ -67,8 +74,20 @@ cpio_name ( struct image *image ) {
6774
return image->cmdline;
6875
}
6976

70-
extern void cpio_set_field ( char *field, unsigned long value );
71-
extern size_t cpio_name_len ( struct image *image );
72-
extern size_t cpio_header ( struct image *image, struct cpio_header *cpio );
77+
/**
78+
* Get CPIO header zero-padding length
79+
*
80+
* @v len Length of CPIO header (including name, excluding NUL)
81+
* @ret pad_len Padding length
82+
*/
83+
static inline __attribute__ (( always_inline )) size_t
84+
cpio_pad_len ( size_t len ) {
85+
86+
/* Pad by at least one byte (for name's terminating NUL) */
87+
return ( CPIO_ALIGN - ( len % CPIO_ALIGN ) );
88+
}
89+
90+
extern size_t cpio_header ( struct image *image, unsigned int index,
91+
struct cpio_header *cpio );
7392

7493
#endif /* _IPXE_CPIO_H */

src/interface/efi/efi_file.c

+9-7
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,7 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) {
245245
size_t cpio_len;
246246
size_t name_len;
247247
size_t len;
248+
unsigned int i;
248249

249250
/* Read from file */
250251
len = 0;
@@ -263,15 +264,16 @@ static size_t efi_file_read_initrd ( struct efi_file_reader *reader ) {
263264
}
264265
len += efi_file_read_chunk ( reader, UNULL, pad_len );
265266

266-
/* Read CPIO header, if applicable */
267-
cpio_len = cpio_header ( image, &cpio );
268-
if ( cpio_len ) {
269-
name = cpio_name ( image );
270-
name_len = cpio_name_len ( image );
271-
pad_len = ( cpio_len - sizeof ( cpio ) - name_len );
267+
/* Read CPIO header(s), if applicable */
268+
name = cpio_name ( image );
269+
for ( i = 0 ; ( cpio_len = cpio_header ( image, i, &cpio ) );
270+
i++ ) {
271+
name_len = ( cpio_len - sizeof ( cpio ) );
272+
pad_len = cpio_pad_len ( cpio_len );
272273
DBGC ( file, "EFIFILE %s [%#08zx,%#08zx) %s header\n",
273274
efi_file_name ( file ), reader->pos,
274-
( reader->pos + cpio_len ), image->name );
275+
( reader->pos + cpio_len + pad_len ),
276+
image->name );
275277
len += efi_file_read_chunk ( reader,
276278
virt_to_user ( &cpio ),
277279
sizeof ( cpio ) );

0 commit comments

Comments
 (0)