xref: /aosp_15_r20/external/kmod/libkmod/libkmod-file.c (revision cc4ad7da8cefe208cb129ac2aa9a357c7c72deb2)
1*cc4ad7daSAndroid Build Coastguard Worker /*
2*cc4ad7daSAndroid Build Coastguard Worker  * libkmod - interface to kernel module operations
3*cc4ad7daSAndroid Build Coastguard Worker  *
4*cc4ad7daSAndroid Build Coastguard Worker  * Copyright (C) 2011-2013  ProFUSION embedded systems
5*cc4ad7daSAndroid Build Coastguard Worker  *
6*cc4ad7daSAndroid Build Coastguard Worker  * This library is free software; you can redistribute it and/or
7*cc4ad7daSAndroid Build Coastguard Worker  * modify it under the terms of the GNU Lesser General Public
8*cc4ad7daSAndroid Build Coastguard Worker  * License as published by the Free Software Foundation; either
9*cc4ad7daSAndroid Build Coastguard Worker  * version 2.1 of the License, or (at your option) any later version.
10*cc4ad7daSAndroid Build Coastguard Worker  *
11*cc4ad7daSAndroid Build Coastguard Worker  * This library is distributed in the hope that it will be useful,
12*cc4ad7daSAndroid Build Coastguard Worker  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13*cc4ad7daSAndroid Build Coastguard Worker  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14*cc4ad7daSAndroid Build Coastguard Worker  * Lesser General Public License for more details.
15*cc4ad7daSAndroid Build Coastguard Worker  *
16*cc4ad7daSAndroid Build Coastguard Worker  * You should have received a copy of the GNU Lesser General Public
17*cc4ad7daSAndroid Build Coastguard Worker  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18*cc4ad7daSAndroid Build Coastguard Worker  */
19*cc4ad7daSAndroid Build Coastguard Worker 
20*cc4ad7daSAndroid Build Coastguard Worker #include <errno.h>
21*cc4ad7daSAndroid Build Coastguard Worker #include <stdbool.h>
22*cc4ad7daSAndroid Build Coastguard Worker #include <stdio.h>
23*cc4ad7daSAndroid Build Coastguard Worker #include <stdlib.h>
24*cc4ad7daSAndroid Build Coastguard Worker #include <string.h>
25*cc4ad7daSAndroid Build Coastguard Worker #include <sys/mman.h>
26*cc4ad7daSAndroid Build Coastguard Worker #include <sys/stat.h>
27*cc4ad7daSAndroid Build Coastguard Worker #include <sys/types.h>
28*cc4ad7daSAndroid Build Coastguard Worker #include <unistd.h>
29*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_ZSTD
30*cc4ad7daSAndroid Build Coastguard Worker #include <zstd.h>
31*cc4ad7daSAndroid Build Coastguard Worker #endif
32*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_XZ
33*cc4ad7daSAndroid Build Coastguard Worker #include <lzma.h>
34*cc4ad7daSAndroid Build Coastguard Worker #endif
35*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_ZLIB
36*cc4ad7daSAndroid Build Coastguard Worker #include <zlib.h>
37*cc4ad7daSAndroid Build Coastguard Worker #endif
38*cc4ad7daSAndroid Build Coastguard Worker 
39*cc4ad7daSAndroid Build Coastguard Worker #include <shared/util.h>
40*cc4ad7daSAndroid Build Coastguard Worker 
41*cc4ad7daSAndroid Build Coastguard Worker #include "libkmod.h"
42*cc4ad7daSAndroid Build Coastguard Worker #include "libkmod-internal.h"
43*cc4ad7daSAndroid Build Coastguard Worker 
44*cc4ad7daSAndroid Build Coastguard Worker struct kmod_file;
45*cc4ad7daSAndroid Build Coastguard Worker struct file_ops {
46*cc4ad7daSAndroid Build Coastguard Worker 	int (*load)(struct kmod_file *file);
47*cc4ad7daSAndroid Build Coastguard Worker 	void (*unload)(struct kmod_file *file);
48*cc4ad7daSAndroid Build Coastguard Worker };
49*cc4ad7daSAndroid Build Coastguard Worker 
50*cc4ad7daSAndroid Build Coastguard Worker struct kmod_file {
51*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_ZSTD
52*cc4ad7daSAndroid Build Coastguard Worker 	bool zstd_used;
53*cc4ad7daSAndroid Build Coastguard Worker #endif
54*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_XZ
55*cc4ad7daSAndroid Build Coastguard Worker 	bool xz_used;
56*cc4ad7daSAndroid Build Coastguard Worker #endif
57*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_ZLIB
58*cc4ad7daSAndroid Build Coastguard Worker 	gzFile gzf;
59*cc4ad7daSAndroid Build Coastguard Worker #endif
60*cc4ad7daSAndroid Build Coastguard Worker 	int fd;
61*cc4ad7daSAndroid Build Coastguard Worker 	enum kmod_file_compression_type compression;
62*cc4ad7daSAndroid Build Coastguard Worker 	off_t size;
63*cc4ad7daSAndroid Build Coastguard Worker 	void *memory;
64*cc4ad7daSAndroid Build Coastguard Worker 	const struct file_ops *ops;
65*cc4ad7daSAndroid Build Coastguard Worker 	const struct kmod_ctx *ctx;
66*cc4ad7daSAndroid Build Coastguard Worker 	struct kmod_elf *elf;
67*cc4ad7daSAndroid Build Coastguard Worker };
68*cc4ad7daSAndroid Build Coastguard Worker 
69*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_ZSTD
zstd_read_block(struct kmod_file * file,size_t block_size,ZSTD_inBuffer * input,size_t * input_capacity)70*cc4ad7daSAndroid Build Coastguard Worker static int zstd_read_block(struct kmod_file *file, size_t block_size,
71*cc4ad7daSAndroid Build Coastguard Worker 			   ZSTD_inBuffer *input, size_t *input_capacity)
72*cc4ad7daSAndroid Build Coastguard Worker {
73*cc4ad7daSAndroid Build Coastguard Worker 	ssize_t rdret;
74*cc4ad7daSAndroid Build Coastguard Worker 	int ret;
75*cc4ad7daSAndroid Build Coastguard Worker 
76*cc4ad7daSAndroid Build Coastguard Worker 	if (*input_capacity < block_size) {
77*cc4ad7daSAndroid Build Coastguard Worker 		free((void *)input->src);
78*cc4ad7daSAndroid Build Coastguard Worker 		input->src = malloc(block_size);
79*cc4ad7daSAndroid Build Coastguard Worker 		if (input->src == NULL) {
80*cc4ad7daSAndroid Build Coastguard Worker 			ret = -errno;
81*cc4ad7daSAndroid Build Coastguard Worker 			ERR(file->ctx, "zstd: %m\n");
82*cc4ad7daSAndroid Build Coastguard Worker 			return ret;
83*cc4ad7daSAndroid Build Coastguard Worker 		}
84*cc4ad7daSAndroid Build Coastguard Worker 		*input_capacity = block_size;
85*cc4ad7daSAndroid Build Coastguard Worker 	}
86*cc4ad7daSAndroid Build Coastguard Worker 
87*cc4ad7daSAndroid Build Coastguard Worker 	rdret = read(file->fd, (void *)input->src, block_size);
88*cc4ad7daSAndroid Build Coastguard Worker 	if (rdret < 0) {
89*cc4ad7daSAndroid Build Coastguard Worker 		ret = -errno;
90*cc4ad7daSAndroid Build Coastguard Worker 		ERR(file->ctx, "zstd: %m\n");
91*cc4ad7daSAndroid Build Coastguard Worker 		return ret;
92*cc4ad7daSAndroid Build Coastguard Worker 	}
93*cc4ad7daSAndroid Build Coastguard Worker 
94*cc4ad7daSAndroid Build Coastguard Worker 	input->pos = 0;
95*cc4ad7daSAndroid Build Coastguard Worker 	input->size = rdret;
96*cc4ad7daSAndroid Build Coastguard Worker 	return 0;
97*cc4ad7daSAndroid Build Coastguard Worker }
98*cc4ad7daSAndroid Build Coastguard Worker 
zstd_ensure_outbuffer_space(ZSTD_outBuffer * buffer,size_t min_free)99*cc4ad7daSAndroid Build Coastguard Worker static int zstd_ensure_outbuffer_space(ZSTD_outBuffer *buffer, size_t min_free)
100*cc4ad7daSAndroid Build Coastguard Worker {
101*cc4ad7daSAndroid Build Coastguard Worker 	uint8_t *old_buffer = buffer->dst;
102*cc4ad7daSAndroid Build Coastguard Worker 	int ret = 0;
103*cc4ad7daSAndroid Build Coastguard Worker 
104*cc4ad7daSAndroid Build Coastguard Worker 	if (buffer->size - buffer->pos >= min_free)
105*cc4ad7daSAndroid Build Coastguard Worker 		return 0;
106*cc4ad7daSAndroid Build Coastguard Worker 
107*cc4ad7daSAndroid Build Coastguard Worker 	buffer->size += min_free;
108*cc4ad7daSAndroid Build Coastguard Worker 	buffer->dst = realloc(buffer->dst, buffer->size);
109*cc4ad7daSAndroid Build Coastguard Worker 	if (buffer->dst == NULL) {
110*cc4ad7daSAndroid Build Coastguard Worker 		ret = -errno;
111*cc4ad7daSAndroid Build Coastguard Worker 		free(old_buffer);
112*cc4ad7daSAndroid Build Coastguard Worker 	}
113*cc4ad7daSAndroid Build Coastguard Worker 
114*cc4ad7daSAndroid Build Coastguard Worker 	return ret;
115*cc4ad7daSAndroid Build Coastguard Worker }
116*cc4ad7daSAndroid Build Coastguard Worker 
zstd_decompress_block(struct kmod_file * file,ZSTD_DStream * dstr,ZSTD_inBuffer * input,ZSTD_outBuffer * output,size_t * next_block_size)117*cc4ad7daSAndroid Build Coastguard Worker static int zstd_decompress_block(struct kmod_file *file, ZSTD_DStream *dstr,
118*cc4ad7daSAndroid Build Coastguard Worker 				 ZSTD_inBuffer *input, ZSTD_outBuffer *output,
119*cc4ad7daSAndroid Build Coastguard Worker 				 size_t *next_block_size)
120*cc4ad7daSAndroid Build Coastguard Worker {
121*cc4ad7daSAndroid Build Coastguard Worker 	size_t out_buf_min_size = ZSTD_DStreamOutSize();
122*cc4ad7daSAndroid Build Coastguard Worker 	int ret = 0;
123*cc4ad7daSAndroid Build Coastguard Worker 
124*cc4ad7daSAndroid Build Coastguard Worker 	do {
125*cc4ad7daSAndroid Build Coastguard Worker 		ssize_t dsret;
126*cc4ad7daSAndroid Build Coastguard Worker 
127*cc4ad7daSAndroid Build Coastguard Worker 		ret = zstd_ensure_outbuffer_space(output, out_buf_min_size);
128*cc4ad7daSAndroid Build Coastguard Worker 		if (ret) {
129*cc4ad7daSAndroid Build Coastguard Worker 			ERR(file->ctx, "zstd: %s\n", strerror(-ret));
130*cc4ad7daSAndroid Build Coastguard Worker 			break;
131*cc4ad7daSAndroid Build Coastguard Worker 		}
132*cc4ad7daSAndroid Build Coastguard Worker 
133*cc4ad7daSAndroid Build Coastguard Worker 		dsret = ZSTD_decompressStream(dstr, output, input);
134*cc4ad7daSAndroid Build Coastguard Worker 		if (ZSTD_isError(dsret)) {
135*cc4ad7daSAndroid Build Coastguard Worker 			ret = -EINVAL;
136*cc4ad7daSAndroid Build Coastguard Worker 			ERR(file->ctx, "zstd: %s\n", ZSTD_getErrorName(dsret));
137*cc4ad7daSAndroid Build Coastguard Worker 			break;
138*cc4ad7daSAndroid Build Coastguard Worker 		}
139*cc4ad7daSAndroid Build Coastguard Worker 		if (dsret > 0)
140*cc4ad7daSAndroid Build Coastguard Worker 			*next_block_size = (size_t)dsret;
141*cc4ad7daSAndroid Build Coastguard Worker 	} while (input->pos < input->size
142*cc4ad7daSAndroid Build Coastguard Worker 		 || output->pos > output->size
143*cc4ad7daSAndroid Build Coastguard Worker 		 || output->size - output->pos < out_buf_min_size);
144*cc4ad7daSAndroid Build Coastguard Worker 
145*cc4ad7daSAndroid Build Coastguard Worker 	return ret;
146*cc4ad7daSAndroid Build Coastguard Worker }
147*cc4ad7daSAndroid Build Coastguard Worker 
load_zstd(struct kmod_file * file)148*cc4ad7daSAndroid Build Coastguard Worker static int load_zstd(struct kmod_file *file)
149*cc4ad7daSAndroid Build Coastguard Worker {
150*cc4ad7daSAndroid Build Coastguard Worker 	ZSTD_DStream *dstr;
151*cc4ad7daSAndroid Build Coastguard Worker 	size_t next_block_size;
152*cc4ad7daSAndroid Build Coastguard Worker 	size_t zst_inb_capacity = 0;
153*cc4ad7daSAndroid Build Coastguard Worker 	ZSTD_inBuffer zst_inb = { 0 };
154*cc4ad7daSAndroid Build Coastguard Worker 	ZSTD_outBuffer zst_outb = { 0 };
155*cc4ad7daSAndroid Build Coastguard Worker 	int ret;
156*cc4ad7daSAndroid Build Coastguard Worker 
157*cc4ad7daSAndroid Build Coastguard Worker 	dstr = ZSTD_createDStream();
158*cc4ad7daSAndroid Build Coastguard Worker 	if (dstr == NULL) {
159*cc4ad7daSAndroid Build Coastguard Worker 		ret = -EINVAL;
160*cc4ad7daSAndroid Build Coastguard Worker 		ERR(file->ctx, "zstd: Failed to create decompression stream\n");
161*cc4ad7daSAndroid Build Coastguard Worker 		goto out;
162*cc4ad7daSAndroid Build Coastguard Worker 	}
163*cc4ad7daSAndroid Build Coastguard Worker 
164*cc4ad7daSAndroid Build Coastguard Worker 	next_block_size = ZSTD_initDStream(dstr);
165*cc4ad7daSAndroid Build Coastguard Worker 
166*cc4ad7daSAndroid Build Coastguard Worker 	while (true) {
167*cc4ad7daSAndroid Build Coastguard Worker 		ret = zstd_read_block(file, next_block_size, &zst_inb,
168*cc4ad7daSAndroid Build Coastguard Worker 				      &zst_inb_capacity);
169*cc4ad7daSAndroid Build Coastguard Worker 		if (ret != 0)
170*cc4ad7daSAndroid Build Coastguard Worker 			goto out;
171*cc4ad7daSAndroid Build Coastguard Worker 		if (zst_inb.size == 0) /* EOF */
172*cc4ad7daSAndroid Build Coastguard Worker 			break;
173*cc4ad7daSAndroid Build Coastguard Worker 
174*cc4ad7daSAndroid Build Coastguard Worker 		ret = zstd_decompress_block(file, dstr, &zst_inb, &zst_outb,
175*cc4ad7daSAndroid Build Coastguard Worker 					    &next_block_size);
176*cc4ad7daSAndroid Build Coastguard Worker 		if (ret != 0)
177*cc4ad7daSAndroid Build Coastguard Worker 			goto out;
178*cc4ad7daSAndroid Build Coastguard Worker 	}
179*cc4ad7daSAndroid Build Coastguard Worker 
180*cc4ad7daSAndroid Build Coastguard Worker 	ZSTD_freeDStream(dstr);
181*cc4ad7daSAndroid Build Coastguard Worker 	free((void *)zst_inb.src);
182*cc4ad7daSAndroid Build Coastguard Worker 	file->zstd_used = true;
183*cc4ad7daSAndroid Build Coastguard Worker 	file->memory = zst_outb.dst;
184*cc4ad7daSAndroid Build Coastguard Worker 	file->size = zst_outb.pos;
185*cc4ad7daSAndroid Build Coastguard Worker 	return 0;
186*cc4ad7daSAndroid Build Coastguard Worker out:
187*cc4ad7daSAndroid Build Coastguard Worker 	if (dstr != NULL)
188*cc4ad7daSAndroid Build Coastguard Worker 		ZSTD_freeDStream(dstr);
189*cc4ad7daSAndroid Build Coastguard Worker 	free((void *)zst_inb.src);
190*cc4ad7daSAndroid Build Coastguard Worker 	free((void *)zst_outb.dst);
191*cc4ad7daSAndroid Build Coastguard Worker 	return ret;
192*cc4ad7daSAndroid Build Coastguard Worker }
193*cc4ad7daSAndroid Build Coastguard Worker 
unload_zstd(struct kmod_file * file)194*cc4ad7daSAndroid Build Coastguard Worker static void unload_zstd(struct kmod_file *file)
195*cc4ad7daSAndroid Build Coastguard Worker {
196*cc4ad7daSAndroid Build Coastguard Worker 	if (!file->zstd_used)
197*cc4ad7daSAndroid Build Coastguard Worker 		return;
198*cc4ad7daSAndroid Build Coastguard Worker 	free(file->memory);
199*cc4ad7daSAndroid Build Coastguard Worker }
200*cc4ad7daSAndroid Build Coastguard Worker 
201*cc4ad7daSAndroid Build Coastguard Worker static const char magic_zstd[] = {0x28, 0xB5, 0x2F, 0xFD};
202*cc4ad7daSAndroid Build Coastguard Worker #endif
203*cc4ad7daSAndroid Build Coastguard Worker 
204*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_XZ
xz_uncompress_belch(struct kmod_file * file,lzma_ret ret)205*cc4ad7daSAndroid Build Coastguard Worker static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
206*cc4ad7daSAndroid Build Coastguard Worker {
207*cc4ad7daSAndroid Build Coastguard Worker 	switch (ret) {
208*cc4ad7daSAndroid Build Coastguard Worker 	case LZMA_MEM_ERROR:
209*cc4ad7daSAndroid Build Coastguard Worker 		ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
210*cc4ad7daSAndroid Build Coastguard Worker 		break;
211*cc4ad7daSAndroid Build Coastguard Worker 	case LZMA_FORMAT_ERROR:
212*cc4ad7daSAndroid Build Coastguard Worker 		ERR(file->ctx, "xz: File format not recognized\n");
213*cc4ad7daSAndroid Build Coastguard Worker 		break;
214*cc4ad7daSAndroid Build Coastguard Worker 	case LZMA_OPTIONS_ERROR:
215*cc4ad7daSAndroid Build Coastguard Worker 		ERR(file->ctx, "xz: Unsupported compression options\n");
216*cc4ad7daSAndroid Build Coastguard Worker 		break;
217*cc4ad7daSAndroid Build Coastguard Worker 	case LZMA_DATA_ERROR:
218*cc4ad7daSAndroid Build Coastguard Worker 		ERR(file->ctx, "xz: File is corrupt\n");
219*cc4ad7daSAndroid Build Coastguard Worker 		break;
220*cc4ad7daSAndroid Build Coastguard Worker 	case LZMA_BUF_ERROR:
221*cc4ad7daSAndroid Build Coastguard Worker 		ERR(file->ctx, "xz: Unexpected end of input\n");
222*cc4ad7daSAndroid Build Coastguard Worker 		break;
223*cc4ad7daSAndroid Build Coastguard Worker 	default:
224*cc4ad7daSAndroid Build Coastguard Worker 		ERR(file->ctx, "xz: Internal error (bug)\n");
225*cc4ad7daSAndroid Build Coastguard Worker 		break;
226*cc4ad7daSAndroid Build Coastguard Worker 	}
227*cc4ad7daSAndroid Build Coastguard Worker }
228*cc4ad7daSAndroid Build Coastguard Worker 
xz_uncompress(lzma_stream * strm,struct kmod_file * file)229*cc4ad7daSAndroid Build Coastguard Worker static int xz_uncompress(lzma_stream *strm, struct kmod_file *file)
230*cc4ad7daSAndroid Build Coastguard Worker {
231*cc4ad7daSAndroid Build Coastguard Worker 	uint8_t in_buf[BUFSIZ], out_buf[BUFSIZ];
232*cc4ad7daSAndroid Build Coastguard Worker 	lzma_action action = LZMA_RUN;
233*cc4ad7daSAndroid Build Coastguard Worker 	lzma_ret ret;
234*cc4ad7daSAndroid Build Coastguard Worker 	void *p = NULL;
235*cc4ad7daSAndroid Build Coastguard Worker 	size_t total = 0;
236*cc4ad7daSAndroid Build Coastguard Worker 
237*cc4ad7daSAndroid Build Coastguard Worker 	strm->avail_in  = 0;
238*cc4ad7daSAndroid Build Coastguard Worker 	strm->next_out  = out_buf;
239*cc4ad7daSAndroid Build Coastguard Worker 	strm->avail_out = sizeof(out_buf);
240*cc4ad7daSAndroid Build Coastguard Worker 
241*cc4ad7daSAndroid Build Coastguard Worker 	while (true) {
242*cc4ad7daSAndroid Build Coastguard Worker 		if (strm->avail_in == 0) {
243*cc4ad7daSAndroid Build Coastguard Worker 			ssize_t rdret = read(file->fd, in_buf, sizeof(in_buf));
244*cc4ad7daSAndroid Build Coastguard Worker 			if (rdret < 0) {
245*cc4ad7daSAndroid Build Coastguard Worker 				ret = -errno;
246*cc4ad7daSAndroid Build Coastguard Worker 				goto out;
247*cc4ad7daSAndroid Build Coastguard Worker 			}
248*cc4ad7daSAndroid Build Coastguard Worker 			strm->next_in  = in_buf;
249*cc4ad7daSAndroid Build Coastguard Worker 			strm->avail_in = rdret;
250*cc4ad7daSAndroid Build Coastguard Worker 			if (rdret == 0)
251*cc4ad7daSAndroid Build Coastguard Worker 				action = LZMA_FINISH;
252*cc4ad7daSAndroid Build Coastguard Worker 		}
253*cc4ad7daSAndroid Build Coastguard Worker 		ret = lzma_code(strm, action);
254*cc4ad7daSAndroid Build Coastguard Worker 		if (strm->avail_out == 0 || ret != LZMA_OK) {
255*cc4ad7daSAndroid Build Coastguard Worker 			size_t write_size = BUFSIZ - strm->avail_out;
256*cc4ad7daSAndroid Build Coastguard Worker 			char *tmp = realloc(p, total + write_size);
257*cc4ad7daSAndroid Build Coastguard Worker 			if (tmp == NULL) {
258*cc4ad7daSAndroid Build Coastguard Worker 				ret = -errno;
259*cc4ad7daSAndroid Build Coastguard Worker 				goto out;
260*cc4ad7daSAndroid Build Coastguard Worker 			}
261*cc4ad7daSAndroid Build Coastguard Worker 			memcpy(tmp + total, out_buf, write_size);
262*cc4ad7daSAndroid Build Coastguard Worker 			total += write_size;
263*cc4ad7daSAndroid Build Coastguard Worker 			p = tmp;
264*cc4ad7daSAndroid Build Coastguard Worker 			strm->next_out = out_buf;
265*cc4ad7daSAndroid Build Coastguard Worker 			strm->avail_out = BUFSIZ;
266*cc4ad7daSAndroid Build Coastguard Worker 		}
267*cc4ad7daSAndroid Build Coastguard Worker 		if (ret == LZMA_STREAM_END)
268*cc4ad7daSAndroid Build Coastguard Worker 			break;
269*cc4ad7daSAndroid Build Coastguard Worker 		if (ret != LZMA_OK) {
270*cc4ad7daSAndroid Build Coastguard Worker 			xz_uncompress_belch(file, ret);
271*cc4ad7daSAndroid Build Coastguard Worker 			ret = -EINVAL;
272*cc4ad7daSAndroid Build Coastguard Worker 			goto out;
273*cc4ad7daSAndroid Build Coastguard Worker 		}
274*cc4ad7daSAndroid Build Coastguard Worker 	}
275*cc4ad7daSAndroid Build Coastguard Worker 	file->xz_used = true;
276*cc4ad7daSAndroid Build Coastguard Worker 	file->memory = p;
277*cc4ad7daSAndroid Build Coastguard Worker 	file->size = total;
278*cc4ad7daSAndroid Build Coastguard Worker 	return 0;
279*cc4ad7daSAndroid Build Coastguard Worker  out:
280*cc4ad7daSAndroid Build Coastguard Worker 	free(p);
281*cc4ad7daSAndroid Build Coastguard Worker 	return ret;
282*cc4ad7daSAndroid Build Coastguard Worker }
283*cc4ad7daSAndroid Build Coastguard Worker 
load_xz(struct kmod_file * file)284*cc4ad7daSAndroid Build Coastguard Worker static int load_xz(struct kmod_file *file)
285*cc4ad7daSAndroid Build Coastguard Worker {
286*cc4ad7daSAndroid Build Coastguard Worker 	lzma_stream strm = LZMA_STREAM_INIT;
287*cc4ad7daSAndroid Build Coastguard Worker 	lzma_ret lzret;
288*cc4ad7daSAndroid Build Coastguard Worker 	int ret;
289*cc4ad7daSAndroid Build Coastguard Worker 
290*cc4ad7daSAndroid Build Coastguard Worker 	lzret = lzma_stream_decoder(&strm, UINT64_MAX, LZMA_CONCATENATED);
291*cc4ad7daSAndroid Build Coastguard Worker 	if (lzret == LZMA_MEM_ERROR) {
292*cc4ad7daSAndroid Build Coastguard Worker 		ERR(file->ctx, "xz: %s\n", strerror(ENOMEM));
293*cc4ad7daSAndroid Build Coastguard Worker 		return -ENOMEM;
294*cc4ad7daSAndroid Build Coastguard Worker 	} else if (lzret != LZMA_OK) {
295*cc4ad7daSAndroid Build Coastguard Worker 		ERR(file->ctx, "xz: Internal error (bug)\n");
296*cc4ad7daSAndroid Build Coastguard Worker 		return -EINVAL;
297*cc4ad7daSAndroid Build Coastguard Worker 	}
298*cc4ad7daSAndroid Build Coastguard Worker 	ret = xz_uncompress(&strm, file);
299*cc4ad7daSAndroid Build Coastguard Worker 	lzma_end(&strm);
300*cc4ad7daSAndroid Build Coastguard Worker 	return ret;
301*cc4ad7daSAndroid Build Coastguard Worker }
302*cc4ad7daSAndroid Build Coastguard Worker 
unload_xz(struct kmod_file * file)303*cc4ad7daSAndroid Build Coastguard Worker static void unload_xz(struct kmod_file *file)
304*cc4ad7daSAndroid Build Coastguard Worker {
305*cc4ad7daSAndroid Build Coastguard Worker 	if (!file->xz_used)
306*cc4ad7daSAndroid Build Coastguard Worker 		return;
307*cc4ad7daSAndroid Build Coastguard Worker 	free(file->memory);
308*cc4ad7daSAndroid Build Coastguard Worker }
309*cc4ad7daSAndroid Build Coastguard Worker 
310*cc4ad7daSAndroid Build Coastguard Worker static const char magic_xz[] = {0xfd, '7', 'z', 'X', 'Z', 0};
311*cc4ad7daSAndroid Build Coastguard Worker #endif
312*cc4ad7daSAndroid Build Coastguard Worker 
313*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_ZLIB
314*cc4ad7daSAndroid Build Coastguard Worker #define READ_STEP (4 * 1024 * 1024)
load_zlib(struct kmod_file * file)315*cc4ad7daSAndroid Build Coastguard Worker static int load_zlib(struct kmod_file *file)
316*cc4ad7daSAndroid Build Coastguard Worker {
317*cc4ad7daSAndroid Build Coastguard Worker 	int err = 0;
318*cc4ad7daSAndroid Build Coastguard Worker 	off_t did = 0, total = 0;
319*cc4ad7daSAndroid Build Coastguard Worker 	_cleanup_free_ unsigned char *p = NULL;
320*cc4ad7daSAndroid Build Coastguard Worker 
321*cc4ad7daSAndroid Build Coastguard Worker 	errno = 0;
322*cc4ad7daSAndroid Build Coastguard Worker 	file->gzf = gzdopen(file->fd, "rb");
323*cc4ad7daSAndroid Build Coastguard Worker 	if (file->gzf == NULL)
324*cc4ad7daSAndroid Build Coastguard Worker 		return -errno;
325*cc4ad7daSAndroid Build Coastguard Worker 	file->fd = -1; /* now owned by gzf due gzdopen() */
326*cc4ad7daSAndroid Build Coastguard Worker 
327*cc4ad7daSAndroid Build Coastguard Worker 	for (;;) {
328*cc4ad7daSAndroid Build Coastguard Worker 		int r;
329*cc4ad7daSAndroid Build Coastguard Worker 
330*cc4ad7daSAndroid Build Coastguard Worker 		if (did == total) {
331*cc4ad7daSAndroid Build Coastguard Worker 			void *tmp = realloc(p, total + READ_STEP);
332*cc4ad7daSAndroid Build Coastguard Worker 			if (tmp == NULL) {
333*cc4ad7daSAndroid Build Coastguard Worker 				err = -errno;
334*cc4ad7daSAndroid Build Coastguard Worker 				goto error;
335*cc4ad7daSAndroid Build Coastguard Worker 			}
336*cc4ad7daSAndroid Build Coastguard Worker 			total += READ_STEP;
337*cc4ad7daSAndroid Build Coastguard Worker 			p = tmp;
338*cc4ad7daSAndroid Build Coastguard Worker 		}
339*cc4ad7daSAndroid Build Coastguard Worker 
340*cc4ad7daSAndroid Build Coastguard Worker 		r = gzread(file->gzf, p + did, total - did);
341*cc4ad7daSAndroid Build Coastguard Worker 		if (r == 0)
342*cc4ad7daSAndroid Build Coastguard Worker 			break;
343*cc4ad7daSAndroid Build Coastguard Worker 		else if (r < 0) {
344*cc4ad7daSAndroid Build Coastguard Worker 			int gzerr;
345*cc4ad7daSAndroid Build Coastguard Worker 			const char *gz_errmsg = gzerror(file->gzf, &gzerr);
346*cc4ad7daSAndroid Build Coastguard Worker 
347*cc4ad7daSAndroid Build Coastguard Worker 			ERR(file->ctx, "gzip: %s\n", gz_errmsg);
348*cc4ad7daSAndroid Build Coastguard Worker 
349*cc4ad7daSAndroid Build Coastguard Worker 			/* gzip might not set errno here */
350*cc4ad7daSAndroid Build Coastguard Worker 			err = gzerr == Z_ERRNO ? -errno : -EINVAL;
351*cc4ad7daSAndroid Build Coastguard Worker 			goto error;
352*cc4ad7daSAndroid Build Coastguard Worker 		}
353*cc4ad7daSAndroid Build Coastguard Worker 		did += r;
354*cc4ad7daSAndroid Build Coastguard Worker 	}
355*cc4ad7daSAndroid Build Coastguard Worker 
356*cc4ad7daSAndroid Build Coastguard Worker 	file->memory = p;
357*cc4ad7daSAndroid Build Coastguard Worker 	file->size = did;
358*cc4ad7daSAndroid Build Coastguard Worker 	p = NULL;
359*cc4ad7daSAndroid Build Coastguard Worker 	return 0;
360*cc4ad7daSAndroid Build Coastguard Worker 
361*cc4ad7daSAndroid Build Coastguard Worker error:
362*cc4ad7daSAndroid Build Coastguard Worker 	gzclose(file->gzf);
363*cc4ad7daSAndroid Build Coastguard Worker 	return err;
364*cc4ad7daSAndroid Build Coastguard Worker }
365*cc4ad7daSAndroid Build Coastguard Worker 
unload_zlib(struct kmod_file * file)366*cc4ad7daSAndroid Build Coastguard Worker static void unload_zlib(struct kmod_file *file)
367*cc4ad7daSAndroid Build Coastguard Worker {
368*cc4ad7daSAndroid Build Coastguard Worker 	if (file->gzf == NULL)
369*cc4ad7daSAndroid Build Coastguard Worker 		return;
370*cc4ad7daSAndroid Build Coastguard Worker 	free(file->memory);
371*cc4ad7daSAndroid Build Coastguard Worker 	gzclose(file->gzf); /* closes file->fd */
372*cc4ad7daSAndroid Build Coastguard Worker }
373*cc4ad7daSAndroid Build Coastguard Worker 
374*cc4ad7daSAndroid Build Coastguard Worker static const char magic_zlib[] = {0x1f, 0x8b};
375*cc4ad7daSAndroid Build Coastguard Worker #endif
376*cc4ad7daSAndroid Build Coastguard Worker 
377*cc4ad7daSAndroid Build Coastguard Worker static const struct comp_type {
378*cc4ad7daSAndroid Build Coastguard Worker 	size_t magic_size;
379*cc4ad7daSAndroid Build Coastguard Worker 	enum kmod_file_compression_type compression;
380*cc4ad7daSAndroid Build Coastguard Worker 	const char *magic_bytes;
381*cc4ad7daSAndroid Build Coastguard Worker 	const struct file_ops ops;
382*cc4ad7daSAndroid Build Coastguard Worker } comp_types[] = {
383*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_ZSTD
384*cc4ad7daSAndroid Build Coastguard Worker 	{sizeof(magic_zstd),	KMOD_FILE_COMPRESSION_ZSTD, magic_zstd, {load_zstd, unload_zstd}},
385*cc4ad7daSAndroid Build Coastguard Worker #endif
386*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_XZ
387*cc4ad7daSAndroid Build Coastguard Worker 	{sizeof(magic_xz),	KMOD_FILE_COMPRESSION_XZ, magic_xz, {load_xz, unload_xz}},
388*cc4ad7daSAndroid Build Coastguard Worker #endif
389*cc4ad7daSAndroid Build Coastguard Worker #ifdef ENABLE_ZLIB
390*cc4ad7daSAndroid Build Coastguard Worker 	{sizeof(magic_zlib),	KMOD_FILE_COMPRESSION_ZLIB, magic_zlib, {load_zlib, unload_zlib}},
391*cc4ad7daSAndroid Build Coastguard Worker #endif
392*cc4ad7daSAndroid Build Coastguard Worker 	{0,			KMOD_FILE_COMPRESSION_NONE, NULL, {NULL, NULL}}
393*cc4ad7daSAndroid Build Coastguard Worker };
394*cc4ad7daSAndroid Build Coastguard Worker 
load_reg(struct kmod_file * file)395*cc4ad7daSAndroid Build Coastguard Worker static int load_reg(struct kmod_file *file)
396*cc4ad7daSAndroid Build Coastguard Worker {
397*cc4ad7daSAndroid Build Coastguard Worker 	struct stat st;
398*cc4ad7daSAndroid Build Coastguard Worker 
399*cc4ad7daSAndroid Build Coastguard Worker 	if (fstat(file->fd, &st) < 0)
400*cc4ad7daSAndroid Build Coastguard Worker 		return -errno;
401*cc4ad7daSAndroid Build Coastguard Worker 
402*cc4ad7daSAndroid Build Coastguard Worker 	file->size = st.st_size;
403*cc4ad7daSAndroid Build Coastguard Worker 	file->memory = mmap(NULL, file->size, PROT_READ, MAP_PRIVATE,
404*cc4ad7daSAndroid Build Coastguard Worker 			    file->fd, 0);
405*cc4ad7daSAndroid Build Coastguard Worker 	if (file->memory == MAP_FAILED)
406*cc4ad7daSAndroid Build Coastguard Worker 		return -errno;
407*cc4ad7daSAndroid Build Coastguard Worker 
408*cc4ad7daSAndroid Build Coastguard Worker 	return 0;
409*cc4ad7daSAndroid Build Coastguard Worker }
410*cc4ad7daSAndroid Build Coastguard Worker 
unload_reg(struct kmod_file * file)411*cc4ad7daSAndroid Build Coastguard Worker static void unload_reg(struct kmod_file *file)
412*cc4ad7daSAndroid Build Coastguard Worker {
413*cc4ad7daSAndroid Build Coastguard Worker 	munmap(file->memory, file->size);
414*cc4ad7daSAndroid Build Coastguard Worker }
415*cc4ad7daSAndroid Build Coastguard Worker 
416*cc4ad7daSAndroid Build Coastguard Worker static const struct file_ops reg_ops = {
417*cc4ad7daSAndroid Build Coastguard Worker 	load_reg, unload_reg
418*cc4ad7daSAndroid Build Coastguard Worker };
419*cc4ad7daSAndroid Build Coastguard Worker 
kmod_file_get_elf(struct kmod_file * file)420*cc4ad7daSAndroid Build Coastguard Worker struct kmod_elf *kmod_file_get_elf(struct kmod_file *file)
421*cc4ad7daSAndroid Build Coastguard Worker {
422*cc4ad7daSAndroid Build Coastguard Worker 	if (file->elf)
423*cc4ad7daSAndroid Build Coastguard Worker 		return file->elf;
424*cc4ad7daSAndroid Build Coastguard Worker 
425*cc4ad7daSAndroid Build Coastguard Worker 	kmod_file_load_contents(file);
426*cc4ad7daSAndroid Build Coastguard Worker 	file->elf = kmod_elf_new(file->memory, file->size);
427*cc4ad7daSAndroid Build Coastguard Worker 	return file->elf;
428*cc4ad7daSAndroid Build Coastguard Worker }
429*cc4ad7daSAndroid Build Coastguard Worker 
kmod_file_open(const struct kmod_ctx * ctx,const char * filename)430*cc4ad7daSAndroid Build Coastguard Worker struct kmod_file *kmod_file_open(const struct kmod_ctx *ctx,
431*cc4ad7daSAndroid Build Coastguard Worker 						const char *filename)
432*cc4ad7daSAndroid Build Coastguard Worker {
433*cc4ad7daSAndroid Build Coastguard Worker 	struct kmod_file *file = calloc(1, sizeof(struct kmod_file));
434*cc4ad7daSAndroid Build Coastguard Worker 	const struct comp_type *itr;
435*cc4ad7daSAndroid Build Coastguard Worker 	size_t magic_size_max = 0;
436*cc4ad7daSAndroid Build Coastguard Worker 	int err = 0;
437*cc4ad7daSAndroid Build Coastguard Worker 
438*cc4ad7daSAndroid Build Coastguard Worker 	if (file == NULL)
439*cc4ad7daSAndroid Build Coastguard Worker 		return NULL;
440*cc4ad7daSAndroid Build Coastguard Worker 
441*cc4ad7daSAndroid Build Coastguard Worker 	file->fd = open(filename, O_RDONLY|O_CLOEXEC);
442*cc4ad7daSAndroid Build Coastguard Worker 	if (file->fd < 0) {
443*cc4ad7daSAndroid Build Coastguard Worker 		err = -errno;
444*cc4ad7daSAndroid Build Coastguard Worker 		goto error;
445*cc4ad7daSAndroid Build Coastguard Worker 	}
446*cc4ad7daSAndroid Build Coastguard Worker 
447*cc4ad7daSAndroid Build Coastguard Worker 	for (itr = comp_types; itr->ops.load != NULL; itr++) {
448*cc4ad7daSAndroid Build Coastguard Worker 		if (magic_size_max < itr->magic_size)
449*cc4ad7daSAndroid Build Coastguard Worker 			magic_size_max = itr->magic_size;
450*cc4ad7daSAndroid Build Coastguard Worker 	}
451*cc4ad7daSAndroid Build Coastguard Worker 
452*cc4ad7daSAndroid Build Coastguard Worker 	if (magic_size_max > 0) {
453*cc4ad7daSAndroid Build Coastguard Worker 		char *buf = alloca(magic_size_max + 1);
454*cc4ad7daSAndroid Build Coastguard Worker 		ssize_t sz;
455*cc4ad7daSAndroid Build Coastguard Worker 
456*cc4ad7daSAndroid Build Coastguard Worker 		if (buf == NULL) {
457*cc4ad7daSAndroid Build Coastguard Worker 			err = -errno;
458*cc4ad7daSAndroid Build Coastguard Worker 			goto error;
459*cc4ad7daSAndroid Build Coastguard Worker 		}
460*cc4ad7daSAndroid Build Coastguard Worker 		sz = read_str_safe(file->fd, buf, magic_size_max + 1);
461*cc4ad7daSAndroid Build Coastguard Worker 		lseek(file->fd, 0, SEEK_SET);
462*cc4ad7daSAndroid Build Coastguard Worker 		if (sz != (ssize_t)magic_size_max) {
463*cc4ad7daSAndroid Build Coastguard Worker 			if (sz < 0)
464*cc4ad7daSAndroid Build Coastguard Worker 				err = sz;
465*cc4ad7daSAndroid Build Coastguard Worker 			else
466*cc4ad7daSAndroid Build Coastguard Worker 				err = -EINVAL;
467*cc4ad7daSAndroid Build Coastguard Worker 			goto error;
468*cc4ad7daSAndroid Build Coastguard Worker 		}
469*cc4ad7daSAndroid Build Coastguard Worker 
470*cc4ad7daSAndroid Build Coastguard Worker 		for (itr = comp_types; itr->ops.load != NULL; itr++) {
471*cc4ad7daSAndroid Build Coastguard Worker 			if (memcmp(buf, itr->magic_bytes, itr->magic_size) == 0) {
472*cc4ad7daSAndroid Build Coastguard Worker 				file->ops = &itr->ops;
473*cc4ad7daSAndroid Build Coastguard Worker 				file->compression = itr->compression;
474*cc4ad7daSAndroid Build Coastguard Worker 				break;
475*cc4ad7daSAndroid Build Coastguard Worker 			}
476*cc4ad7daSAndroid Build Coastguard Worker 		}
477*cc4ad7daSAndroid Build Coastguard Worker 	}
478*cc4ad7daSAndroid Build Coastguard Worker 
479*cc4ad7daSAndroid Build Coastguard Worker 	if (file->ops == NULL) {
480*cc4ad7daSAndroid Build Coastguard Worker 		file->ops = &reg_ops;
481*cc4ad7daSAndroid Build Coastguard Worker 		file->compression = KMOD_FILE_COMPRESSION_NONE;
482*cc4ad7daSAndroid Build Coastguard Worker 	}
483*cc4ad7daSAndroid Build Coastguard Worker 
484*cc4ad7daSAndroid Build Coastguard Worker 	file->ctx = ctx;
485*cc4ad7daSAndroid Build Coastguard Worker 
486*cc4ad7daSAndroid Build Coastguard Worker error:
487*cc4ad7daSAndroid Build Coastguard Worker 	if (err < 0) {
488*cc4ad7daSAndroid Build Coastguard Worker 		if (file->fd >= 0)
489*cc4ad7daSAndroid Build Coastguard Worker 			close(file->fd);
490*cc4ad7daSAndroid Build Coastguard Worker 		free(file);
491*cc4ad7daSAndroid Build Coastguard Worker 		errno = -err;
492*cc4ad7daSAndroid Build Coastguard Worker 		return NULL;
493*cc4ad7daSAndroid Build Coastguard Worker 	}
494*cc4ad7daSAndroid Build Coastguard Worker 
495*cc4ad7daSAndroid Build Coastguard Worker 	return file;
496*cc4ad7daSAndroid Build Coastguard Worker }
497*cc4ad7daSAndroid Build Coastguard Worker 
498*cc4ad7daSAndroid Build Coastguard Worker /*
499*cc4ad7daSAndroid Build Coastguard Worker  *  Callers should just check file->memory got updated
500*cc4ad7daSAndroid Build Coastguard Worker  */
kmod_file_load_contents(struct kmod_file * file)501*cc4ad7daSAndroid Build Coastguard Worker void kmod_file_load_contents(struct kmod_file *file)
502*cc4ad7daSAndroid Build Coastguard Worker {
503*cc4ad7daSAndroid Build Coastguard Worker 	if (file->memory)
504*cc4ad7daSAndroid Build Coastguard Worker 		return;
505*cc4ad7daSAndroid Build Coastguard Worker 
506*cc4ad7daSAndroid Build Coastguard Worker 	/*  The load functions already log possible errors. */
507*cc4ad7daSAndroid Build Coastguard Worker 	file->ops->load(file);
508*cc4ad7daSAndroid Build Coastguard Worker }
509*cc4ad7daSAndroid Build Coastguard Worker 
kmod_file_get_contents(const struct kmod_file * file)510*cc4ad7daSAndroid Build Coastguard Worker void *kmod_file_get_contents(const struct kmod_file *file)
511*cc4ad7daSAndroid Build Coastguard Worker {
512*cc4ad7daSAndroid Build Coastguard Worker 	return file->memory;
513*cc4ad7daSAndroid Build Coastguard Worker }
514*cc4ad7daSAndroid Build Coastguard Worker 
kmod_file_get_size(const struct kmod_file * file)515*cc4ad7daSAndroid Build Coastguard Worker off_t kmod_file_get_size(const struct kmod_file *file)
516*cc4ad7daSAndroid Build Coastguard Worker {
517*cc4ad7daSAndroid Build Coastguard Worker 	return file->size;
518*cc4ad7daSAndroid Build Coastguard Worker }
519*cc4ad7daSAndroid Build Coastguard Worker 
kmod_file_get_compression(const struct kmod_file * file)520*cc4ad7daSAndroid Build Coastguard Worker enum kmod_file_compression_type kmod_file_get_compression(const struct kmod_file *file)
521*cc4ad7daSAndroid Build Coastguard Worker {
522*cc4ad7daSAndroid Build Coastguard Worker 	return file->compression;
523*cc4ad7daSAndroid Build Coastguard Worker }
524*cc4ad7daSAndroid Build Coastguard Worker 
kmod_file_get_fd(const struct kmod_file * file)525*cc4ad7daSAndroid Build Coastguard Worker int kmod_file_get_fd(const struct kmod_file *file)
526*cc4ad7daSAndroid Build Coastguard Worker {
527*cc4ad7daSAndroid Build Coastguard Worker 	return file->fd;
528*cc4ad7daSAndroid Build Coastguard Worker }
529*cc4ad7daSAndroid Build Coastguard Worker 
kmod_file_unref(struct kmod_file * file)530*cc4ad7daSAndroid Build Coastguard Worker void kmod_file_unref(struct kmod_file *file)
531*cc4ad7daSAndroid Build Coastguard Worker {
532*cc4ad7daSAndroid Build Coastguard Worker 	if (file->elf)
533*cc4ad7daSAndroid Build Coastguard Worker 		kmod_elf_unref(file->elf);
534*cc4ad7daSAndroid Build Coastguard Worker 
535*cc4ad7daSAndroid Build Coastguard Worker 	if (file->memory)
536*cc4ad7daSAndroid Build Coastguard Worker 		file->ops->unload(file);
537*cc4ad7daSAndroid Build Coastguard Worker 
538*cc4ad7daSAndroid Build Coastguard Worker 	if (file->fd >= 0)
539*cc4ad7daSAndroid Build Coastguard Worker 		close(file->fd);
540*cc4ad7daSAndroid Build Coastguard Worker 	free(file);
541*cc4ad7daSAndroid Build Coastguard Worker }
542