1228753Smm/*-
2228753Smm * Copyright (c) 2009 Michihiro NAKAJIMA
3228753Smm * All rights reserved.
4228753Smm *
5228753Smm * Redistribution and use in source and binary forms, with or without
6228753Smm * modification, are permitted provided that the following conditions
7228753Smm * are met:
8228753Smm * 1. Redistributions of source code must retain the above copyright
9228753Smm *    notice, this list of conditions and the following disclaimer.
10228753Smm * 2. Redistributions in binary form must reproduce the above copyright
11228753Smm *    notice, this list of conditions and the following disclaimer in the
12228753Smm *    documentation and/or other materials provided with the distribution.
13228753Smm *
14228753Smm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15228753Smm * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16228753Smm * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17228753Smm * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18228753Smm * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19228753Smm * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20228753Smm * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21228753Smm * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22228753Smm * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23228753Smm * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24228753Smm */
25228753Smm
26228753Smm#include "archive_platform.h"
27228753Smm
28228753Smm#ifdef HAVE_ERRNO_H
29228753Smm#include <errno.h>
30228753Smm#endif
31228753Smm#ifdef HAVE_STDLIB_H
32228753Smm#include <stdlib.h>
33228753Smm#endif
34228753Smm
35228753Smm#include "archive.h"
36228753Smm#include "archive_endian.h"
37228753Smm#include "archive_private.h"
38228753Smm#include "archive_read_private.h"
39228753Smm
40228753Smmstruct rpm {
41228753Smm	int64_t		 total_in;
42228753Smm	size_t		 hpos;
43228753Smm	size_t		 hlen;
44228753Smm	unsigned char	 header[16];
45228753Smm	enum {
46228753Smm		ST_LEAD,	/* Skipping 'Lead' section. */
47228753Smm		ST_HEADER,	/* Reading 'Header' section;
48228753Smm				 * first 16 bytes. */
49228753Smm		ST_HEADER_DATA,	/* Skipping 'Header' section. */
50228753Smm		ST_PADDING,	/* Skipping padding data after the
51228753Smm				 * 'Header' section. */
52228753Smm		ST_ARCHIVE	/* Reading 'Archive' section. */
53228753Smm	}		 state;
54228753Smm	int		 first_header;
55228753Smm};
56228753Smm#define RPM_LEAD_SIZE	96	/* Size of 'Lead' section. */
57228753Smm
58228753Smmstatic int	rpm_bidder_bid(struct archive_read_filter_bidder *,
59228753Smm		    struct archive_read_filter *);
60228753Smmstatic int	rpm_bidder_init(struct archive_read_filter *);
61228753Smm
62228753Smmstatic ssize_t	rpm_filter_read(struct archive_read_filter *,
63228753Smm		    const void **);
64228753Smmstatic int	rpm_filter_close(struct archive_read_filter *);
65228753Smm
66231200Smm#if ARCHIVE_VERSION_NUMBER < 4000000
67231200Smm/* Deprecated; remove in libarchive 4.0 */
68228753Smmint
69231200Smmarchive_read_support_compression_rpm(struct archive *a)
70228753Smm{
71231200Smm	return archive_read_support_filter_rpm(a);
72231200Smm}
73231200Smm#endif
74231200Smm
75231200Smmint
76231200Smmarchive_read_support_filter_rpm(struct archive *_a)
77231200Smm{
78228753Smm	struct archive_read *a = (struct archive_read *)_a;
79228753Smm	struct archive_read_filter_bidder *bidder;
80228753Smm
81231200Smm	archive_check_magic(_a, ARCHIVE_READ_MAGIC,
82231200Smm	    ARCHIVE_STATE_NEW, "archive_read_support_filter_rpm");
83231200Smm
84231200Smm	if (__archive_read_get_bidder(a, &bidder) != ARCHIVE_OK)
85228753Smm		return (ARCHIVE_FATAL);
86228753Smm
87228753Smm	bidder->data = NULL;
88248616Smm	bidder->name = "rpm";
89228753Smm	bidder->bid = rpm_bidder_bid;
90228753Smm	bidder->init = rpm_bidder_init;
91228753Smm	bidder->options = NULL;
92228753Smm	bidder->free = NULL;
93228753Smm	return (ARCHIVE_OK);
94228753Smm}
95228753Smm
96228753Smmstatic int
97228753Smmrpm_bidder_bid(struct archive_read_filter_bidder *self,
98228753Smm    struct archive_read_filter *filter)
99228753Smm{
100228753Smm	const unsigned char *b;
101228753Smm	ssize_t avail;
102228753Smm	int bits_checked;
103228753Smm
104228753Smm	(void)self; /* UNUSED */
105228753Smm
106228753Smm	b = __archive_read_filter_ahead(filter, 8, &avail);
107228753Smm	if (b == NULL)
108228753Smm		return (0);
109228753Smm
110228753Smm	bits_checked = 0;
111228753Smm	/*
112231200Smm	 * Verify Header Magic Bytes : 0XED 0XAB 0XEE 0XDB
113228753Smm	 */
114231200Smm	if (memcmp(b, "\xED\xAB\xEE\xDB", 4) != 0)
115228753Smm		return (0);
116231200Smm	bits_checked += 32;
117228753Smm	/*
118228753Smm	 * Check major version.
119228753Smm	 */
120228753Smm	if (b[4] != 3 && b[4] != 4)
121228753Smm		return (0);
122228753Smm	bits_checked += 8;
123228753Smm	/*
124228753Smm	 * Check package type; binary or source.
125228753Smm	 */
126228753Smm	if (b[6] != 0)
127228753Smm		return (0);
128228753Smm	bits_checked += 8;
129228753Smm	if (b[7] != 0 && b[7] != 1)
130228753Smm		return (0);
131228753Smm	bits_checked += 8;
132228753Smm
133228753Smm	return (bits_checked);
134228753Smm}
135228753Smm
136228753Smmstatic int
137228753Smmrpm_bidder_init(struct archive_read_filter *self)
138228753Smm{
139228753Smm	struct rpm   *rpm;
140228753Smm
141248616Smm	self->code = ARCHIVE_FILTER_RPM;
142228753Smm	self->name = "rpm";
143228753Smm	self->read = rpm_filter_read;
144228753Smm	self->skip = NULL; /* not supported */
145228753Smm	self->close = rpm_filter_close;
146228753Smm
147228753Smm	rpm = (struct rpm *)calloc(sizeof(*rpm), 1);
148228753Smm	if (rpm == NULL) {
149228753Smm		archive_set_error(&self->archive->archive, ENOMEM,
150228753Smm		    "Can't allocate data for rpm");
151228753Smm		return (ARCHIVE_FATAL);
152228753Smm	}
153228753Smm
154228753Smm	self->data = rpm;
155228753Smm	rpm->state = ST_LEAD;
156228753Smm
157228753Smm	return (ARCHIVE_OK);
158228753Smm}
159228753Smm
160228753Smmstatic ssize_t
161228753Smmrpm_filter_read(struct archive_read_filter *self, const void **buff)
162228753Smm{
163228753Smm	struct rpm *rpm;
164228753Smm	const unsigned char *b;
165228753Smm	ssize_t avail_in, total;
166228753Smm	size_t used, n;
167228753Smm	uint32_t section;
168228753Smm	uint32_t bytes;
169228753Smm
170228753Smm	rpm = (struct rpm *)self->data;
171228753Smm	*buff = NULL;
172228753Smm	total = avail_in = 0;
173228753Smm	b = NULL;
174228753Smm	used = 0;
175228753Smm	do {
176228753Smm		if (b == NULL) {
177228753Smm			b = __archive_read_filter_ahead(self->upstream, 1,
178228753Smm			    &avail_in);
179228753Smm			if (b == NULL) {
180228753Smm				if (avail_in < 0)
181228753Smm					return (ARCHIVE_FATAL);
182228753Smm				else
183228753Smm					break;
184228753Smm			}
185228753Smm		}
186228753Smm
187228753Smm		switch (rpm->state) {
188228753Smm		case ST_LEAD:
189228753Smm			if (rpm->total_in + avail_in < RPM_LEAD_SIZE)
190228753Smm				used += avail_in;
191228753Smm			else {
192238856Smm				n = (size_t)(RPM_LEAD_SIZE - rpm->total_in);
193228753Smm				used += n;
194228753Smm				b += n;
195228753Smm				rpm->state = ST_HEADER;
196228753Smm				rpm->hpos = 0;
197228753Smm				rpm->hlen = 0;
198228753Smm				rpm->first_header = 1;
199228753Smm			}
200228753Smm			break;
201228753Smm		case ST_HEADER:
202228753Smm			n = 16 - rpm->hpos;
203228753Smm			if (n > avail_in - used)
204228753Smm				n = avail_in - used;
205228753Smm			memcpy(rpm->header+rpm->hpos, b, n);
206228753Smm			b += n;
207228753Smm			used += n;
208228753Smm			rpm->hpos += n;
209228753Smm
210228753Smm			if (rpm->hpos == 16) {
211228753Smm				if (rpm->header[0] != 0x8e ||
212228753Smm				    rpm->header[1] != 0xad ||
213228753Smm				    rpm->header[2] != 0xe8 ||
214228753Smm				    rpm->header[3] != 0x01) {
215228753Smm					if (rpm->first_header) {
216228753Smm						archive_set_error(
217228753Smm						    &self->archive->archive,
218228753Smm						    ARCHIVE_ERRNO_FILE_FORMAT,
219370535Sgit2svn						    "Unrecognized rpm header");
220228753Smm						return (ARCHIVE_FATAL);
221228753Smm					}
222228753Smm					rpm->state = ST_ARCHIVE;
223228753Smm					*buff = rpm->header;
224228753Smm					total = rpm->hpos;
225228753Smm					break;
226228753Smm				}
227228753Smm				/* Calculate 'Header' length. */
228228753Smm				section = archive_be32dec(rpm->header+8);
229228753Smm				bytes = archive_be32dec(rpm->header+12);
230228753Smm				rpm->hlen = 16 + section * 16 + bytes;
231228753Smm				rpm->state = ST_HEADER_DATA;
232228753Smm				rpm->first_header = 0;
233228753Smm			}
234228753Smm			break;
235228753Smm		case ST_HEADER_DATA:
236228753Smm			n = rpm->hlen - rpm->hpos;
237228753Smm			if (n > avail_in - used)
238228753Smm				n = avail_in - used;
239228753Smm			b += n;
240228753Smm			used += n;
241228753Smm			rpm->hpos += n;
242228753Smm			if (rpm->hpos == rpm->hlen)
243228753Smm				rpm->state = ST_PADDING;
244228753Smm			break;
245228753Smm		case ST_PADDING:
246228753Smm			while (used < (size_t)avail_in) {
247228753Smm				if (*b != 0) {
248228753Smm					/* Read next header. */
249228753Smm					rpm->state = ST_HEADER;
250228753Smm					rpm->hpos = 0;
251228753Smm					rpm->hlen = 0;
252228753Smm					break;
253228753Smm				}
254228753Smm				b++;
255228753Smm				used++;
256228753Smm			}
257228753Smm			break;
258228753Smm		case ST_ARCHIVE:
259228753Smm			*buff = b;
260228753Smm			total = avail_in;
261228753Smm			used = avail_in;
262228753Smm			break;
263228753Smm		}
264228753Smm		if (used == (size_t)avail_in) {
265228753Smm			rpm->total_in += used;
266228753Smm			__archive_read_filter_consume(self->upstream, used);
267228753Smm			b = NULL;
268228753Smm			used = 0;
269228753Smm		}
270228753Smm	} while (total == 0 && avail_in > 0);
271228753Smm
272228753Smm	if (used > 0 && b != NULL) {
273228753Smm		rpm->total_in += used;
274228753Smm		__archive_read_filter_consume(self->upstream, used);
275228753Smm	}
276228753Smm	return (total);
277228753Smm}
278228753Smm
279228753Smmstatic int
280228753Smmrpm_filter_close(struct archive_read_filter *self)
281228753Smm{
282228753Smm	struct rpm *rpm;
283228753Smm
284228753Smm	rpm = (struct rpm *)self->data;
285228753Smm	free(rpm);
286228753Smm
287228753Smm	return (ARCHIVE_OK);
288228753Smm}
289228753Smm
290