archive_read_support_filter_rpm.c revision 238856
1/*-
2 * Copyright (c) 2009 Michihiro NAKAJIMA
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "archive_platform.h"
27
28#ifdef HAVE_ERRNO_H
29#include <errno.h>
30#endif
31#ifdef HAVE_STDLIB_H
32#include <stdlib.h>
33#endif
34
35#include "archive.h"
36#include "archive_endian.h"
37#include "archive_private.h"
38#include "archive_read_private.h"
39
40struct rpm {
41	int64_t		 total_in;
42	size_t		 hpos;
43	size_t		 hlen;
44	unsigned char	 header[16];
45	enum {
46		ST_LEAD,	/* Skipping 'Lead' section. */
47		ST_HEADER,	/* Reading 'Header' section;
48				 * first 16 bytes. */
49		ST_HEADER_DATA,	/* Skipping 'Header' section. */
50		ST_PADDING,	/* Skipping padding data after the
51				 * 'Header' section. */
52		ST_ARCHIVE	/* Reading 'Archive' section. */
53	}		 state;
54	int		 first_header;
55};
56#define RPM_LEAD_SIZE	96	/* Size of 'Lead' section. */
57
58static int	rpm_bidder_bid(struct archive_read_filter_bidder *,
59		    struct archive_read_filter *);
60static int	rpm_bidder_init(struct archive_read_filter *);
61
62static ssize_t	rpm_filter_read(struct archive_read_filter *,
63		    const void **);
64static int	rpm_filter_close(struct archive_read_filter *);
65
66#if ARCHIVE_VERSION_NUMBER < 4000000
67/* Deprecated; remove in libarchive 4.0 */
68int
69archive_read_support_compression_rpm(struct archive *a)
70{
71	return archive_read_support_filter_rpm(a);
72}
73#endif
74
75int
76archive_read_support_filter_rpm(struct archive *_a)
77{
78	struct archive_read *a = (struct archive_read *)_a;
79	struct archive_read_filter_bidder *bidder;
80
81	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
82	    ARCHIVE_STATE_NEW, "archive_read_support_filter_rpm");
83
84	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
85		return (ARCHIVE_FATAL);
86
87	bidder->data = NULL;
88	bidder->bid = rpm_bidder_bid;
89	bidder->init = rpm_bidder_init;
90	bidder->options = NULL;
91	bidder->free = NULL;
92	return (ARCHIVE_OK);
93}
94
95static int
96rpm_bidder_bid(struct archive_read_filter_bidder *self,
97    struct archive_read_filter *filter)
98{
99	const unsigned char *b;
100	ssize_t avail;
101	int bits_checked;
102
103	(void)self; /* UNUSED */
104
105	b = __archive_read_filter_ahead(filter, 8, &avail);
106	if (b == NULL)
107		return (0);
108
109	bits_checked = 0;
110	/*
111	 * Verify Header Magic Bytes : 0XED 0XAB 0XEE 0XDB
112	 */
113	if (memcmp(b, "\xED\xAB\xEE\xDB", 4) != 0)
114		return (0);
115	bits_checked += 32;
116	/*
117	 * Check major version.
118	 */
119	if (b[4] != 3 && b[4] != 4)
120		return (0);
121	bits_checked += 8;
122	/*
123	 * Check package type; binary or source.
124	 */
125	if (b[6] != 0)
126		return (0);
127	bits_checked += 8;
128	if (b[7] != 0 && b[7] != 1)
129		return (0);
130	bits_checked += 8;
131
132	return (bits_checked);
133}
134
135static int
136rpm_bidder_init(struct archive_read_filter *self)
137{
138	struct rpm   *rpm;
139
140	self->code = ARCHIVE_COMPRESSION_RPM;
141	self->name = "rpm";
142	self->read = rpm_filter_read;
143	self->skip = NULL; /* not supported */
144	self->close = rpm_filter_close;
145
146	rpm = (struct rpm *)calloc(sizeof(*rpm), 1);
147	if (rpm == NULL) {
148		archive_set_error(&self->archive->archive, ENOMEM,
149		    "Can't allocate data for rpm");
150		return (ARCHIVE_FATAL);
151	}
152
153	self->data = rpm;
154	rpm->state = ST_LEAD;
155
156	return (ARCHIVE_OK);
157}
158
159static ssize_t
160rpm_filter_read(struct archive_read_filter *self, const void **buff)
161{
162	struct rpm *rpm;
163	const unsigned char *b;
164	ssize_t avail_in, total;
165	size_t used, n;
166	uint32_t section;
167	uint32_t bytes;
168
169	rpm = (struct rpm *)self->data;
170	*buff = NULL;
171	total = avail_in = 0;
172	b = NULL;
173	used = 0;
174	do {
175		if (b == NULL) {
176			b = __archive_read_filter_ahead(self->upstream, 1,
177			    &avail_in);
178			if (b == NULL) {
179				if (avail_in < 0)
180					return (ARCHIVE_FATAL);
181				else
182					break;
183			}
184		}
185
186		switch (rpm->state) {
187		case ST_LEAD:
188			if (rpm->total_in + avail_in < RPM_LEAD_SIZE)
189				used += avail_in;
190			else {
191				n = (size_t)(RPM_LEAD_SIZE - rpm->total_in);
192				used += n;
193				b += n;
194				rpm->state = ST_HEADER;
195				rpm->hpos = 0;
196				rpm->hlen = 0;
197				rpm->first_header = 1;
198			}
199			break;
200		case ST_HEADER:
201			n = 16 - rpm->hpos;
202			if (n > avail_in - used)
203				n = avail_in - used;
204			memcpy(rpm->header+rpm->hpos, b, n);
205			b += n;
206			used += n;
207			rpm->hpos += n;
208
209			if (rpm->hpos == 16) {
210				if (rpm->header[0] != 0x8e ||
211				    rpm->header[1] != 0xad ||
212				    rpm->header[2] != 0xe8 ||
213				    rpm->header[3] != 0x01) {
214					if (rpm->first_header) {
215						archive_set_error(
216						    &self->archive->archive,
217						    ARCHIVE_ERRNO_FILE_FORMAT,
218						    "Unrecoginized rpm header");
219						return (ARCHIVE_FATAL);
220					}
221					rpm->state = ST_ARCHIVE;
222					*buff = rpm->header;
223					total = rpm->hpos;
224					break;
225				}
226				/* Calculate 'Header' length. */
227				section = archive_be32dec(rpm->header+8);
228				bytes = archive_be32dec(rpm->header+12);
229				rpm->hlen = 16 + section * 16 + bytes;
230				rpm->state = ST_HEADER_DATA;
231				rpm->first_header = 0;
232			}
233			break;
234		case ST_HEADER_DATA:
235			n = rpm->hlen - rpm->hpos;
236			if (n > avail_in - used)
237				n = avail_in - used;
238			b += n;
239			used += n;
240			rpm->hpos += n;
241			if (rpm->hpos == rpm->hlen)
242				rpm->state = ST_PADDING;
243			break;
244		case ST_PADDING:
245			while (used < (size_t)avail_in) {
246				if (*b != 0) {
247					/* Read next header. */
248					rpm->state = ST_HEADER;
249					rpm->hpos = 0;
250					rpm->hlen = 0;
251					break;
252				}
253				b++;
254				used++;
255			}
256			break;
257		case ST_ARCHIVE:
258			*buff = b;
259			total = avail_in;
260			used = avail_in;
261			break;
262		}
263		if (used == (size_t)avail_in) {
264			rpm->total_in += used;
265			__archive_read_filter_consume(self->upstream, used);
266			b = NULL;
267			used = 0;
268		}
269	} while (total == 0 && avail_in > 0);
270
271	if (used > 0 && b != NULL) {
272		rpm->total_in += used;
273		__archive_read_filter_consume(self->upstream, used);
274	}
275	return (total);
276}
277
278static int
279rpm_filter_close(struct archive_read_filter *self)
280{
281	struct rpm *rpm;
282
283	rpm = (struct rpm *)self->data;
284	free(rpm);
285
286	return (ARCHIVE_OK);
287}
288
289