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
66228753Smmint
67228753Smmarchive_read_support_compression_rpm(struct archive *_a)
68228753Smm{
69228753Smm	struct archive_read *a = (struct archive_read *)_a;
70228753Smm	struct archive_read_filter_bidder *bidder;
71228753Smm
72228753Smm	bidder = __archive_read_get_bidder(a);
73228753Smm	archive_clear_error(_a);
74228753Smm	if (bidder == NULL)
75228753Smm		return (ARCHIVE_FATAL);
76228753Smm
77228753Smm	bidder->data = NULL;
78228753Smm	bidder->bid = rpm_bidder_bid;
79228753Smm	bidder->init = rpm_bidder_init;
80228753Smm	bidder->options = NULL;
81228753Smm	bidder->free = NULL;
82228753Smm	return (ARCHIVE_OK);
83228753Smm}
84228753Smm
85228753Smmstatic int
86228753Smmrpm_bidder_bid(struct archive_read_filter_bidder *self,
87228753Smm    struct archive_read_filter *filter)
88228753Smm{
89228753Smm	const unsigned char *b;
90228753Smm	ssize_t avail;
91228753Smm	int bits_checked;
92228753Smm
93228753Smm	(void)self; /* UNUSED */
94228753Smm
95228753Smm	b = __archive_read_filter_ahead(filter, 8, &avail);
96228753Smm	if (b == NULL)
97228753Smm		return (0);
98228753Smm
99228753Smm	bits_checked = 0;
100228753Smm	/*
101228753Smm	 * Verify Header Magic Bytes : 0xed 0xab 0xee 0xdb
102228753Smm	 */
103228753Smm	if (b[0] != 0xed)
104228753Smm		return (0);
105228753Smm	bits_checked += 8;
106228753Smm	if (b[1] != 0xab)
107228753Smm		return (0);
108228753Smm	bits_checked += 8;
109228753Smm	if (b[2] != 0xee)
110228753Smm		return (0);
111228753Smm	bits_checked += 8;
112228753Smm	if (b[3] != 0xdb)
113228753Smm		return (0);
114228753Smm	bits_checked += 8;
115228753Smm	/*
116228753Smm	 * Check major version.
117228753Smm	 */
118228753Smm	if (b[4] != 3 && b[4] != 4)
119228753Smm		return (0);
120228753Smm	bits_checked += 8;
121228753Smm	/*
122228753Smm	 * Check package type; binary or source.
123228753Smm	 */
124228753Smm	if (b[6] != 0)
125228753Smm		return (0);
126228753Smm	bits_checked += 8;
127228753Smm	if (b[7] != 0 && b[7] != 1)
128228753Smm		return (0);
129228753Smm	bits_checked += 8;
130228753Smm
131228753Smm	return (bits_checked);
132228753Smm}
133228753Smm
134228753Smmstatic int
135228753Smmrpm_bidder_init(struct archive_read_filter *self)
136228753Smm{
137228753Smm	struct rpm   *rpm;
138228753Smm
139228753Smm	self->code = ARCHIVE_COMPRESSION_RPM;
140228753Smm	self->name = "rpm";
141228753Smm	self->read = rpm_filter_read;
142228753Smm	self->skip = NULL; /* not supported */
143228753Smm	self->close = rpm_filter_close;
144228753Smm
145228753Smm	rpm = (struct rpm *)calloc(sizeof(*rpm), 1);
146228753Smm	if (rpm == NULL) {
147228753Smm		archive_set_error(&self->archive->archive, ENOMEM,
148228753Smm		    "Can't allocate data for rpm");
149228753Smm		return (ARCHIVE_FATAL);
150228753Smm	}
151228753Smm
152228753Smm	self->data = rpm;
153228753Smm	rpm->state = ST_LEAD;
154228753Smm
155228753Smm	return (ARCHIVE_OK);
156228753Smm}
157228753Smm
158228753Smmstatic ssize_t
159228753Smmrpm_filter_read(struct archive_read_filter *self, const void **buff)
160228753Smm{
161228753Smm	struct rpm *rpm;
162228753Smm	const unsigned char *b;
163228753Smm	ssize_t avail_in, total;
164228753Smm	size_t used, n;
165228753Smm	uint32_t section;
166228753Smm	uint32_t bytes;
167228753Smm
168228753Smm	rpm = (struct rpm *)self->data;
169228753Smm	*buff = NULL;
170228753Smm	total = avail_in = 0;
171228753Smm	b = NULL;
172228753Smm	used = 0;
173228753Smm	do {
174228753Smm		if (b == NULL) {
175228753Smm			b = __archive_read_filter_ahead(self->upstream, 1,
176228753Smm			    &avail_in);
177228753Smm			if (b == NULL) {
178228753Smm				if (avail_in < 0)
179228753Smm					return (ARCHIVE_FATAL);
180228753Smm				else
181228753Smm					break;
182228753Smm			}
183228753Smm		}
184228753Smm
185228753Smm		switch (rpm->state) {
186228753Smm		case ST_LEAD:
187228753Smm			if (rpm->total_in + avail_in < RPM_LEAD_SIZE)
188228753Smm				used += avail_in;
189228753Smm			else {
190228753Smm				n = RPM_LEAD_SIZE - rpm->total_in;
191228753Smm				used += n;
192228753Smm				b += n;
193228753Smm				rpm->state = ST_HEADER;
194228753Smm				rpm->hpos = 0;
195228753Smm				rpm->hlen = 0;
196228753Smm				rpm->first_header = 1;
197228753Smm			}
198228753Smm			break;
199228753Smm		case ST_HEADER:
200228753Smm			n = 16 - rpm->hpos;
201228753Smm			if (n > avail_in - used)
202228753Smm				n = avail_in - used;
203228753Smm			memcpy(rpm->header+rpm->hpos, b, n);
204228753Smm			b += n;
205228753Smm			used += n;
206228753Smm			rpm->hpos += n;
207228753Smm
208228753Smm			if (rpm->hpos == 16) {
209228753Smm				if (rpm->header[0] != 0x8e ||
210228753Smm				    rpm->header[1] != 0xad ||
211228753Smm				    rpm->header[2] != 0xe8 ||
212228753Smm				    rpm->header[3] != 0x01) {
213228753Smm					if (rpm->first_header) {
214228753Smm						archive_set_error(
215228753Smm						    &self->archive->archive,
216228753Smm						    ARCHIVE_ERRNO_FILE_FORMAT,
217228753Smm						    "Unrecoginized rpm header");
218228753Smm						return (ARCHIVE_FATAL);
219228753Smm					}
220228753Smm					rpm->state = ST_ARCHIVE;
221228753Smm					*buff = rpm->header;
222228753Smm					total = rpm->hpos;
223228753Smm					break;
224228753Smm				}
225228753Smm				/* Calculate 'Header' length. */
226228753Smm				section = archive_be32dec(rpm->header+8);
227228753Smm				bytes = archive_be32dec(rpm->header+12);
228228753Smm				rpm->hlen = 16 + section * 16 + bytes;
229228753Smm				rpm->state = ST_HEADER_DATA;
230228753Smm				rpm->first_header = 0;
231228753Smm			}
232228753Smm			break;
233228753Smm		case ST_HEADER_DATA:
234228753Smm			n = rpm->hlen - rpm->hpos;
235228753Smm			if (n > avail_in - used)
236228753Smm				n = avail_in - used;
237228753Smm			b += n;
238228753Smm			used += n;
239228753Smm			rpm->hpos += n;
240228753Smm			if (rpm->hpos == rpm->hlen)
241228753Smm				rpm->state = ST_PADDING;
242228753Smm			break;
243228753Smm		case ST_PADDING:
244228753Smm			while (used < (size_t)avail_in) {
245228753Smm				if (*b != 0) {
246228753Smm					/* Read next header. */
247228753Smm					rpm->state = ST_HEADER;
248228753Smm					rpm->hpos = 0;
249228753Smm					rpm->hlen = 0;
250228753Smm					break;
251228753Smm				}
252228753Smm				b++;
253228753Smm				used++;
254228753Smm			}
255228753Smm			break;
256228753Smm		case ST_ARCHIVE:
257228753Smm			*buff = b;
258228753Smm			total = avail_in;
259228753Smm			used = avail_in;
260228753Smm			break;
261228753Smm		}
262228753Smm		if (used == (size_t)avail_in) {
263228753Smm			rpm->total_in += used;
264228753Smm			__archive_read_filter_consume(self->upstream, used);
265228753Smm			b = NULL;
266228753Smm			used = 0;
267228753Smm		}
268228753Smm	} while (total == 0 && avail_in > 0);
269228753Smm
270228753Smm	if (used > 0 && b != NULL) {
271228753Smm		rpm->total_in += used;
272228753Smm		__archive_read_filter_consume(self->upstream, used);
273228753Smm	}
274228753Smm	return (total);
275228753Smm}
276228753Smm
277228753Smmstatic int
278228753Smmrpm_filter_close(struct archive_read_filter *self)
279228753Smm{
280228753Smm	struct rpm *rpm;
281228753Smm
282228753Smm	rpm = (struct rpm *)self->data;
283228753Smm	free(rpm);
284228753Smm
285228753Smm	return (ARCHIVE_OK);
286228753Smm}
287228753Smm
288