xref: /freebsd/contrib/libarchive/libarchive/archive_read_support_filter_rpm.c (revision bd66c1b43e33540205dbc1187c2f2a15c58b57ba)
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 
40 struct rpm {
41 	int64_t		 total_in;
42 	uint64_t	 hpos;
43 	uint64_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 #define RPM_MIN_HEAD_SIZE	16	/* Minimum size of 'Head'. */
58 
59 static int	rpm_bidder_bid(struct archive_read_filter_bidder *,
60 		    struct archive_read_filter *);
61 static int	rpm_bidder_init(struct archive_read_filter *);
62 
63 static ssize_t	rpm_filter_read(struct archive_read_filter *,
64 		    const void **);
65 static int	rpm_filter_close(struct archive_read_filter *);
66 
67 static inline size_t rpm_limit_bytes(uint64_t, size_t);
68 
69 #if ARCHIVE_VERSION_NUMBER < 4000000
70 /* Deprecated; remove in libarchive 4.0 */
71 int
archive_read_support_compression_rpm(struct archive * a)72 archive_read_support_compression_rpm(struct archive *a)
73 {
74 	return archive_read_support_filter_rpm(a);
75 }
76 #endif
77 
78 static const struct archive_read_filter_bidder_vtable
79 rpm_bidder_vtable = {
80 	.bid = rpm_bidder_bid,
81 	.init = rpm_bidder_init,
82 };
83 
84 int
archive_read_support_filter_rpm(struct archive * _a)85 archive_read_support_filter_rpm(struct archive *_a)
86 {
87 	struct archive_read *a = (struct archive_read *)_a;
88 
89 	return __archive_read_register_bidder(a, NULL, "rpm",
90 			&rpm_bidder_vtable);
91 }
92 
93 static int
rpm_bidder_bid(struct archive_read_filter_bidder * self,struct archive_read_filter * filter)94 rpm_bidder_bid(struct archive_read_filter_bidder *self,
95     struct archive_read_filter *filter)
96 {
97 	const unsigned char *b;
98 	ssize_t avail;
99 	int bits_checked;
100 
101 	(void)self; /* UNUSED */
102 
103 	b = __archive_read_filter_ahead(filter, 8, &avail);
104 	if (b == NULL)
105 		return (0);
106 
107 	bits_checked = 0;
108 	/*
109 	 * Verify Header Magic Bytes : 0XED 0XAB 0XEE 0XDB
110 	 */
111 	if (memcmp(b, "\xED\xAB\xEE\xDB", 4) != 0)
112 		return (0);
113 	bits_checked += 32;
114 	/*
115 	 * Check major version.
116 	 */
117 	if (b[4] != 3 && b[4] != 4)
118 		return (0);
119 	bits_checked += 8;
120 	/*
121 	 * Check package type; binary or source.
122 	 */
123 	if (b[6] != 0)
124 		return (0);
125 	bits_checked += 8;
126 	if (b[7] != 0 && b[7] != 1)
127 		return (0);
128 	bits_checked += 8;
129 
130 	return (bits_checked);
131 }
132 
133 static const struct archive_read_filter_vtable
134 rpm_reader_vtable = {
135 	.read = rpm_filter_read,
136 	.close = rpm_filter_close,
137 };
138 
139 static int
rpm_bidder_init(struct archive_read_filter * self)140 rpm_bidder_init(struct archive_read_filter *self)
141 {
142 	struct rpm   *rpm;
143 
144 	self->code = ARCHIVE_FILTER_RPM;
145 	self->name = "rpm";
146 
147 	rpm = calloc(1, sizeof(*rpm));
148 	if (rpm == NULL) {
149 		archive_set_error(&self->archive->archive, ENOMEM,
150 		    "Can't allocate data for rpm");
151 		return (ARCHIVE_FATAL);
152 	}
153 
154 	self->data = rpm;
155 	rpm->state = ST_LEAD;
156 	self->vtable = &rpm_reader_vtable;
157 
158 	return (ARCHIVE_OK);
159 }
160 
161 static inline size_t
rpm_limit_bytes(uint64_t bytes,size_t max)162 rpm_limit_bytes(uint64_t bytes, size_t max)
163 {
164 	return (bytes > max ? max : (size_t)bytes);
165 }
166 
167 static ssize_t
rpm_filter_read(struct archive_read_filter * self,const void ** buff)168 rpm_filter_read(struct archive_read_filter *self, const void **buff)
169 {
170 	struct rpm *rpm;
171 	const unsigned char *b;
172 	ssize_t avail_in, total, used;
173 	size_t n;
174 	uint64_t section;
175 	uint64_t bytes;
176 
177 	rpm = (struct rpm *)self->data;
178 	*buff = NULL;
179 	total = avail_in = 0;
180 	b = NULL;
181 	used = 0;
182 	do {
183 		if (b == NULL) {
184 			b = __archive_read_filter_ahead(self->upstream, 1,
185 			    &avail_in);
186 			if (b == NULL) {
187 				if (avail_in < 0)
188 					return (ARCHIVE_FATAL);
189 				else
190 					break;
191 			}
192 		}
193 
194 		switch (rpm->state) {
195 		case ST_LEAD:
196 			if (rpm->total_in + avail_in < RPM_LEAD_SIZE)
197 				used += avail_in;
198 			else {
199 				n = (size_t)(RPM_LEAD_SIZE - rpm->total_in);
200 				used += n;
201 				b += n;
202 				rpm->state = ST_HEADER;
203 				rpm->hpos = 0;
204 				rpm->hlen = 0;
205 				rpm->first_header = 1;
206 			}
207 			break;
208 		case ST_HEADER:
209 			n = rpm_limit_bytes(RPM_MIN_HEAD_SIZE - rpm->hpos,
210 			    avail_in - used);
211 			memcpy(rpm->header+rpm->hpos, b, n);
212 			b += n;
213 			used += n;
214 			rpm->hpos += n;
215 
216 			if (rpm->hpos == RPM_MIN_HEAD_SIZE) {
217 				if (rpm->header[0] != 0x8e ||
218 				    rpm->header[1] != 0xad ||
219 				    rpm->header[2] != 0xe8 ||
220 				    rpm->header[3] != 0x01) {
221 					if (rpm->first_header) {
222 						archive_set_error(
223 						    &self->archive->archive,
224 						    ARCHIVE_ERRNO_FILE_FORMAT,
225 						    "Unrecognized rpm header");
226 						return (ARCHIVE_FATAL);
227 					}
228 					rpm->state = ST_ARCHIVE;
229 					*buff = rpm->header;
230 					total = RPM_MIN_HEAD_SIZE;
231 					break;
232 				}
233 				/* Calculate 'Header' length. */
234 				section = archive_be32dec(rpm->header+8);
235 				bytes = archive_be32dec(rpm->header+12);
236 				rpm->hlen = rpm->hpos + section * 16 + bytes;
237 				rpm->state = ST_HEADER_DATA;
238 				rpm->first_header = 0;
239 			}
240 			break;
241 		case ST_HEADER_DATA:
242 			n = rpm_limit_bytes(rpm->hlen - rpm->hpos,
243 			    avail_in - used);
244 			b += n;
245 			used += n;
246 			rpm->hpos += n;
247 			if (rpm->hpos == rpm->hlen)
248 				rpm->state = ST_PADDING;
249 			break;
250 		case ST_PADDING:
251 			while (used < avail_in) {
252 				if (*b != 0) {
253 					/* Read next header. */
254 					rpm->state = ST_HEADER;
255 					rpm->hpos = 0;
256 					rpm->hlen = 0;
257 					break;
258 				}
259 				b++;
260 				used++;
261 			}
262 			break;
263 		case ST_ARCHIVE:
264 			*buff = b;
265 			total = avail_in;
266 			used = avail_in;
267 			break;
268 		}
269 		if (used == avail_in) {
270 			rpm->total_in += used;
271 			__archive_read_filter_consume(self->upstream, used);
272 			b = NULL;
273 			used = 0;
274 		}
275 	} while (total == 0 && avail_in > 0);
276 
277 	if (used > 0 && b != NULL) {
278 		rpm->total_in += used;
279 		__archive_read_filter_consume(self->upstream, used);
280 	}
281 	return (total);
282 }
283 
284 static int
rpm_filter_close(struct archive_read_filter * self)285 rpm_filter_close(struct archive_read_filter *self)
286 {
287 	struct rpm *rpm;
288 
289 	rpm = (struct rpm *)self->data;
290 	free(rpm);
291 
292 	return (ARCHIVE_OK);
293 }
294 
295