On Thu, Apr 28, 2011 at 6:13 AM, hai wu <[email protected]> wrote:
> Is there a tool to auto-extract livecd/ISO file from initrd.img (which was
> generated from livecd-iso-to-pxeboot command)?
-to-pxeboot appends gziped cpio archive with ISO file to the original
initrd cpio archive, so if you know original initrd size, you could
extract appendix using e.g. dd. In general case, tool is needed to
search for the end-of-cpio-archive marker ("TRAILER!!!") which is what
attached Richard's initrdsplitter.c is doing, Read comments for usage.
Alan
/* Program to split concatenated cpio files.
*
* These files can be created for use with initrd, but the usual tools
* can't split them back into individual cpio archives easily.
* See also: Documentation/early-userspace/buffer-format.txt
*
* By Richard W.M. Jones <[email protected]>
* Major parts copied from: init/initramfs.c
*
* Usage:
* gunzip initrd.gz
* initrdsplitter initrd output
* ==> produces output.0 output.1 ... etc
* You can use "-" as the input filename to mean stdin.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <limits.h>
#include <ctype.h>
static int cpio_skip_file (unsigned long *position, int fd, int ofd);
static int create_output_file (const char *prefix, int n);
static void unlink_output_file (const char *prefix, int n);
static int debug = 1;
int
main (int argc, char *argv[])
{
int fd = 0; /* stdin */
int ofd;
int output;
unsigned long position = 0;
if (argc != 3) {
fprintf (stderr, "initrdsplitter (-|initrd) output\n");
exit (1);
}
if (strcmp (argv[1], "-") != 0) {
fd = open (argv[1], O_RDONLY);
if (fd == -1) { perror (argv[1]); exit (1); }
}
/* Create the first output file. */
output = 0;
ofd = create_output_file (argv[2], output);
for (;;) {
switch (cpio_skip_file (&position, fd, ofd)) {
case 0: /* normal header/file skipped */
break;
case 1: /* TRAILER!!!\0 seen */
if (close (ofd) == -1) { perror ("close"); exit (1); }
output++;
ofd = create_output_file (argv[2], output);
position = 0;
break;
case 2: /* end of input file */
if (fd != 0 && close (fd) == -1) { perror ("close"); exit (1); }
if (close (ofd) == -1) { perror ("close"); exit (1); }
/* If nothing was written to the final file, delete it. */
if (position == 0) unlink_output_file (argv[2], output);
exit (0);
}
}
}
#define N_ALIGN(len) ((((len) + 1) & ~3) + 2)
static void really_read (int fd, void *buf, size_t count, const char *errmsg);
static void really_write (int fd, void *buf, size_t count);
static void parse_header (char *);
static unsigned long ino, major, minor, nlink;
static mode_t mode;
static unsigned long body_len, name_len;
static uid_t uid;
static gid_t gid;
/*static unsigned rdev;*/
static char header[110];
static char filename[PATH_MAX+1];
static char buffer[4096];
static int
cpio_skip_file (unsigned long *position, int fd, int ofd)
{
unsigned long n;
ssize_t r;
/* Aligning ourself with the start of the next file is significantly
* hard. Firstly, we're expecting a header, starting "07...", and
* the header is always aligned to 4 bytes in the input file.
*
* However, arbitrary amounts of ASCII NIL bytes can be added, between
* files. In theory we might have concatenated a non-aligned file
* containing NILs at the end with an aligned file. So allow
* headers to be non-aligned to take care of this.
*
* So, we're just going to search for the first '0', ignoring '\0'.
*/
for (;;) {
again:
r = read (fd, header, 1);
if (r == 0)
return 2; /* end of input */
if (r == -1) {
if (errno == EAGAIN || errno == EINTR) goto again;
perror ("read");
exit (1);
}
/* .. but DON'T copy to output */
if (header[0] == '0')
break;
if (header[0] != '\0') {
fprintf (stderr, "initrdsplitter: couldn't synchronize with start of next cpio header. Probably the input is not a valid cpio archive.\n");
exit (1);
}
}
/* Read the cpio file header. */
really_read (fd, header+1, 109, "not a valid cpio archive");
/* Is it a cpio archive header? */
if (memcmp (header, "070707", 6) == 0) {
fprintf (stderr, "initrdsplitter: old-style cpio archive. Use -H newc option to cpio\n");
exit (1);
}
if (memcmp (header, "070701", 6) != 0) {
int i;
fprintf (stderr, "initrdsplitter: incorrect file header: not a valid cpio archive\n");
fprintf (stderr, "--- header ---\n");
/* Dump out the actual file header that we read. */
for (i = 0; i < 110; ++i) {
if (isprint (header[i]))
printf ("%c", header[i]);
else
printf ("\\x%02x", header[i]);
if ((i & 31) == 0) printf ("\n");
}
fprintf (stderr, "\n--- end of header ---\n");
exit (1);
}
/* Parse the header and check fields look reasonable. */
parse_header (header);
if (name_len <= 0 || name_len > PATH_MAX) {
fprintf (stderr, "initrdsplitter: incorrect name length\n");
exit (1);
}
/* XXX I think you may need to treat symlinks specially. */
/* Read the filename. */
really_read (fd, filename, N_ALIGN (name_len), "not a valid cpio archive");
filename[N_ALIGN(name_len)] = '\0';
if (debug)
printf ("0%o %s, %lu name, %lu body\n",
mode, filename, name_len, body_len);
/* Write the header and filename to the output. */
really_write (ofd, header, 110);
really_write (ofd, filename, N_ALIGN (name_len));
/* Copy the body. */
n = body_len;
while (n > 0) {
int nb = n < sizeof (buffer) ? n : sizeof (buffer);
really_read (fd, buffer, nb, "short read");
really_write (ofd, buffer, nb);
n -= nb;
}
/* Update our position in the output file. */
*position += 110 + N_ALIGN (name_len) + body_len;
/* We need to make sure the next header (or the whole file) has
* position multiple of 4 by writing \0 characters.
*/
while (((*position) & 3) != 0) {
header[0] = '\0';
really_write (ofd, header, 1);
(*position)++;
}
/* Was that the TRAILER file? */
if (strcmp (filename, "TRAILER!!!") == 0)
return 1;
return 0;
}
static void
parse_header (char *s)
{
unsigned long parsed[12];
char buf[9];
int i;
buf[8] = '\0';
for (i = 0, s += 6; i < 12; i++, s += 8) {
memcpy(buf, s, 8);
parsed[i] = strtoul (buf, NULL, 16);
}
ino = parsed[0];
mode = parsed[1];
uid = parsed[2];
gid = parsed[3];
nlink = parsed[4];
body_len = parsed[6];
major = parsed[7];
minor = parsed[8];
/*rdev = new_encode_dev(MKDEV(parsed[9], parsed[10]));*/
name_len = parsed[11];
}
static int
create_output_file (const char *prefix, int n)
{
char filename [PATH_MAX+1];
int ofd;
snprintf (filename, PATH_MAX+1, "%s.%d", prefix, n);
ofd = open (filename, O_CREAT|O_TRUNC|O_WRONLY, 0644);
if (ofd == -1) {
perror (filename);
exit (1);
}
return ofd;
}
static void
unlink_output_file (const char *prefix, int n)
{
char filename [PATH_MAX+1];
snprintf (filename, PATH_MAX+1, "%s.%d", prefix, n);
unlink (filename);
}
static void
really_read (int fd, void *buffer, size_t count, const char *errmsg)
{
ssize_t r;
while (count > 0) {
r = read (fd, buffer, count);
if (r == -1) {
if (errno == EINTR || errno == EAGAIN) continue;
perror ("read");
exit (1);
}
if (r == 0) {
fprintf (stderr, "initrdsplitter: read: %s\n", errmsg);
exit (1);
}
buffer += r;
count -= r;
}
}
static void
really_write (int fd, void *buffer, size_t count)
{
ssize_t r;
while (count > 0) {
r = write (fd, buffer, count);
if (r == -1) {
if (errno == EINTR || errno == EAGAIN) continue;
perror ("write");
exit (1);
}
buffer += r;
count -= r;
}
}
--
livecd mailing list
[email protected]
https://admin.fedoraproject.org/mailman/listinfo/livecd