aboutsummaryrefslogtreecommitdiff
path: root/src/core/cpio.c
blob: 27aee75817c4707ad1a9e77c1bb86742821565d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of the
 * License, or any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 *
 * You can also choose to distribute this program under the terms of
 * the Unmodified Binary Distribution Licence (as given in the file
 * COPYING.UBDL), provided that you have satisfied its requirements.
 */

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

/** @file
 *
 * CPIO archives
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ipxe/cpio.h>

/**
 * 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 ) {
	char buf[9];

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

/**
 * Get CPIO image filename
 *
 * @v image		Image
 * @ret len		CPIO filename length (0 for no filename)
 */
size_t cpio_name_len ( struct image *image ) {
	const char *name = cpio_name ( image );
	char *sep;
	size_t len;

	/* 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 ) );

	return len;
}

/**
 * Parse CPIO image parameters
 *
 * @v image		Image
 * @v cpio		CPIO header to fill in
 */
static void cpio_parse_cmdline ( struct image *image,
				 struct cpio_header *cpio ) {
	const char *cmdline;
	char *arg;
	char *end;
	unsigned int mode;

	/* Skip image filename */
	cmdline = ( cpio_name ( image ) + cpio_name_len ( image ) );

	/* Look for "mode=" */
	if ( ( arg = strstr ( cmdline, "mode=" ) ) ) {
		arg += 5;
		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 ) );
	}
}

/**
 * Construct CPIO header for image, if applicable
 *
 * @v image		Image
 * @v cpio		CPIO header to fill in
 * @ret len		Length of magic CPIO header (including filename)
 */
size_t cpio_header ( struct image *image, struct cpio_header *cpio ) {
	size_t name_len;
	size_t len;

	/* Get filename length */
	name_len = cpio_name_len ( image );

	/* Images with no filename are assumed to already be CPIO archives */
	if ( ! name_len )
		return 0;

	/* 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_nlink, 1 );
	cpio_set_field ( cpio->c_filesize, image->len );
	cpio_set_field ( cpio->c_namesize, ( name_len + 1 /* NUL */ ) );
	cpio_parse_cmdline ( image, cpio );

	/* Calculate total length */
	len = ( ( sizeof ( *cpio ) + name_len + 1 /* NUL */ + CPIO_ALIGN - 1 )
		& ~( CPIO_ALIGN - 1 ) );

	return len;
}