summaryrefslogtreecommitdiffhomepage
path: root/rpmunpack.c
blob: 2178a247dd72a27ecc611d2931b8050c353be833 (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
133
134
135
136
/*
 * rpmunpack for busybox
 *
 * rpmunpack.c  -  Utility program to unpack an RPM archive
 *
 * Gero Kuhlmann <gero@gkminix.han.de> 1998
 *
 *  This program is public domain software; you can do whatever you like
 *  with this source, including modifying and redistributing it.
 *
 *  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.
 */
 
#include "busybox.h" 
#include <fcntl.h>

/*
 * Some general definitions
 */
#define BUFSIZE		512
#define RPM_MAGIC	"\355\253\356\333"
#define GZ_MAGIC_1	'\037'
#define GZ_MAGIC_2	'\213'

/*
 * Global variables
 */
static char buffer[BUFSIZE];
static char *progname;
static int infile, outfile;

/*
 * Read a specified number of bytes from input file
 */
static void myread(int num)
{
  int err;

  if ((err = read(infile, buffer, num)) != num) {
	if (err < 0)
		perror(progname);
	else
		fprintf(stderr, "Unexpected end of input file!\n");
	exit(1);
  }
}

/*
 * Main program
 */
int rpmunpack_main(int argc, char **argv)
{
  int len, status = 0;

  /* Get our own program name */
  if ((progname = strrchr(argv[0], '/')) == NULL)
	progname = argv[0];
  else
	progname++;

  /* Check for command line parameters */
	if (argc>=2 && *argv[1]=='-') {
           usage(rpmunpack_usage);
	}

  /* Open input file */
  if (argc == 1)
	infile = STDIN_FILENO;
  else if ((infile = open(argv[1], O_RDONLY)) < 0) {
	perror(progname);
	exit(1);
  }

  /* Read magic ID and output filename */
  myread(4);
  if (strncmp(buffer, RPM_MAGIC, 4)) {
	fprintf(stderr, "Input file is not in RPM format!\n");
	exit(1);
  }
  myread(6);		/* Skip flags */
  myread(64);
  buffer[64] = '\0';

  /* Open output file */
  strcat(buffer, ".cpio.gz");
  if (infile == STDIN_FILENO)
	outfile = STDOUT_FILENO;
  else if ((outfile = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, 0644)) < 0) {
	perror(progname);
	exit(1);
  }

  /*
   * Now search for the GZIP signature. This is rather awkward, but I don't
   * know any other way how to find out the exact starting position of the
   * archive within the input file. There are a couple of data structures
   * and texts (obviously descriptions, installation shell scripts etc.)
   * coming before the archive, but even they start at different offsets
   * with different RPM files. However, it looks like the GZIP signature
   * never appears before offset 0x200, so we skip these first couple of
   * bytes to make the signature scan a little more reliable.
   */
  myread(0x200 - 74);
  while (status < 2) {
	myread(1);
	if (status == 0 && buffer[0] == GZ_MAGIC_1)
		status++;
	else if (status == 1 && buffer[0] == GZ_MAGIC_2)
		status++;
	else
		status = 0;
  }
  buffer[0] = GZ_MAGIC_1;
  buffer[1] = GZ_MAGIC_2;
  if (write(outfile, buffer, 2) < 0) {
	perror(progname);
	exit(1);
  }

  /* Now simply copy the GZIP archive into the output file */
  while ((len = read(infile, buffer, BUFSIZE)) > 0) {
	if (write(outfile, buffer, len) < 0) {
		perror(progname);
		exit(1);
	}
  }
  if (len < 0) {
	perror(progname);
	exit(1);
  }
  close(outfile);
  close(infile);
  exit(0);
}