xref: /freebsd-src/usr.bin/mkimg/image.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1f0e9dcedSMarcel Moolenaar /*-
2f0e9dcedSMarcel Moolenaar  * Copyright (c) 2014 Juniper Networks, Inc.
3f0e9dcedSMarcel Moolenaar  * All rights reserved.
4f0e9dcedSMarcel Moolenaar  *
5f0e9dcedSMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
6f0e9dcedSMarcel Moolenaar  * modification, are permitted provided that the following conditions
7f0e9dcedSMarcel Moolenaar  * are met:
8f0e9dcedSMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
9f0e9dcedSMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
10f0e9dcedSMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
11f0e9dcedSMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
12f0e9dcedSMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
13f0e9dcedSMarcel Moolenaar  *
14f0e9dcedSMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15f0e9dcedSMarcel Moolenaar  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16f0e9dcedSMarcel Moolenaar  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17f0e9dcedSMarcel Moolenaar  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18f0e9dcedSMarcel Moolenaar  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19f0e9dcedSMarcel Moolenaar  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20f0e9dcedSMarcel Moolenaar  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21f0e9dcedSMarcel Moolenaar  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22f0e9dcedSMarcel Moolenaar  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23f0e9dcedSMarcel Moolenaar  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24f0e9dcedSMarcel Moolenaar  * SUCH DAMAGE.
25f0e9dcedSMarcel Moolenaar  */
26f0e9dcedSMarcel Moolenaar 
27f0e9dcedSMarcel Moolenaar #include <sys/cdefs.h>
28852a0932SMarcel Moolenaar #include <sys/mman.h>
29852a0932SMarcel Moolenaar #include <sys/stat.h>
30f0e9dcedSMarcel Moolenaar #include <assert.h>
31cc7f01a5SMark Johnston #include <err.h>
32f0e9dcedSMarcel Moolenaar #include <errno.h>
33aa30ba04SMarcel Moolenaar #include <limits.h>
34aa30ba04SMarcel Moolenaar #include <paths.h>
35852a0932SMarcel Moolenaar #include <stdint.h>
36aa30ba04SMarcel Moolenaar #include <stdio.h>
37f0e9dcedSMarcel Moolenaar #include <stdlib.h>
38852a0932SMarcel Moolenaar #include <string.h>
39f0e9dcedSMarcel Moolenaar #include <unistd.h>
40f0e9dcedSMarcel Moolenaar 
41f0e9dcedSMarcel Moolenaar #include "image.h"
42f0e9dcedSMarcel Moolenaar #include "mkimg.h"
43f0e9dcedSMarcel Moolenaar 
447b5a53eaSMarcel Moolenaar #ifndef MAP_NOCORE
457b5a53eaSMarcel Moolenaar #define	MAP_NOCORE	0
467b5a53eaSMarcel Moolenaar #endif
477b5a53eaSMarcel Moolenaar #ifndef MAP_NOSYNC
487b5a53eaSMarcel Moolenaar #define	MAP_NOSYNC	0
497b5a53eaSMarcel Moolenaar #endif
507b5a53eaSMarcel Moolenaar 
517b5a53eaSMarcel Moolenaar #ifndef SEEK_DATA
527b5a53eaSMarcel Moolenaar #define	SEEK_DATA	-1
537b5a53eaSMarcel Moolenaar #endif
547b5a53eaSMarcel Moolenaar #ifndef SEEK_HOLE
557b5a53eaSMarcel Moolenaar #define	SEEK_HOLE	-1
567b5a53eaSMarcel Moolenaar #endif
577b5a53eaSMarcel Moolenaar 
58852a0932SMarcel Moolenaar struct chunk {
591080fb19SMarcel Moolenaar 	TAILQ_ENTRY(chunk) ch_list;
60852a0932SMarcel Moolenaar 	size_t	ch_size;		/* Size of chunk in bytes. */
61852a0932SMarcel Moolenaar 	lba_t	ch_block;		/* Block address in image. */
62852a0932SMarcel Moolenaar 	union {
63852a0932SMarcel Moolenaar 		struct {
64852a0932SMarcel Moolenaar 			off_t	ofs;	/* Offset in backing file. */
65852a0932SMarcel Moolenaar 			int	fd;	/* FD of backing file. */
66852a0932SMarcel Moolenaar 		} file;
67852a0932SMarcel Moolenaar 		struct {
68852a0932SMarcel Moolenaar 			void	*ptr;	/* Pointer to data in memory */
69852a0932SMarcel Moolenaar 		} mem;
70852a0932SMarcel Moolenaar 	} ch_u;
71852a0932SMarcel Moolenaar 	u_int	ch_type;
72852a0932SMarcel Moolenaar #define	CH_TYPE_ZEROES		0	/* Chunk is a gap (no data). */
73852a0932SMarcel Moolenaar #define	CH_TYPE_FILE		1	/* File-backed chunk. */
74852a0932SMarcel Moolenaar #define	CH_TYPE_MEMORY		2	/* Memory-backed chunk */
75852a0932SMarcel Moolenaar };
76f0e9dcedSMarcel Moolenaar 
771080fb19SMarcel Moolenaar static TAILQ_HEAD(chunk_head, chunk) image_chunks;
78852a0932SMarcel Moolenaar static u_int image_nchunks;
79852a0932SMarcel Moolenaar 
80852a0932SMarcel Moolenaar static char image_swap_file[PATH_MAX];
81852a0932SMarcel Moolenaar static int image_swap_fd = -1;
82852a0932SMarcel Moolenaar static u_int image_swap_pgsz;
83852a0932SMarcel Moolenaar static off_t image_swap_size;
84852a0932SMarcel Moolenaar 
85f0e9dcedSMarcel Moolenaar static lba_t image_size;
86f0e9dcedSMarcel Moolenaar 
87852a0932SMarcel Moolenaar static int
is_empty_sector(void * buf)88852a0932SMarcel Moolenaar is_empty_sector(void *buf)
89f0e9dcedSMarcel Moolenaar {
90852a0932SMarcel Moolenaar 	uint64_t *p = buf;
91852a0932SMarcel Moolenaar 	size_t n, max;
92f0e9dcedSMarcel Moolenaar 
93852a0932SMarcel Moolenaar 	assert(((uintptr_t)p & 3) == 0);
94852a0932SMarcel Moolenaar 
95852a0932SMarcel Moolenaar 	max = secsz / sizeof(uint64_t);
96852a0932SMarcel Moolenaar 	for (n = 0; n < max; n++) {
97852a0932SMarcel Moolenaar 		if (p[n] != 0UL)
98852a0932SMarcel Moolenaar 			return (0);
99852a0932SMarcel Moolenaar 	}
100852a0932SMarcel Moolenaar 	return (1);
101852a0932SMarcel Moolenaar }
102852a0932SMarcel Moolenaar 
103852a0932SMarcel Moolenaar /*
104852a0932SMarcel Moolenaar  * Swap file handlng.
105852a0932SMarcel Moolenaar  */
106852a0932SMarcel Moolenaar 
107852a0932SMarcel Moolenaar static off_t
image_swap_alloc(size_t size)108852a0932SMarcel Moolenaar image_swap_alloc(size_t size)
109852a0932SMarcel Moolenaar {
110852a0932SMarcel Moolenaar 	off_t ofs;
111852a0932SMarcel Moolenaar 	size_t unit;
112852a0932SMarcel Moolenaar 
113852a0932SMarcel Moolenaar 	unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz;
114852a0932SMarcel Moolenaar 	assert((unit & (unit - 1)) == 0);
115852a0932SMarcel Moolenaar 
116852a0932SMarcel Moolenaar 	size = (size + unit - 1) & ~(unit - 1);
117852a0932SMarcel Moolenaar 
118852a0932SMarcel Moolenaar 	ofs = image_swap_size;
119852a0932SMarcel Moolenaar 	image_swap_size += size;
120852a0932SMarcel Moolenaar 	if (ftruncate(image_swap_fd, image_swap_size) == -1) {
121852a0932SMarcel Moolenaar 		image_swap_size = ofs;
122852a0932SMarcel Moolenaar 		ofs = -1LL;
123852a0932SMarcel Moolenaar 	}
124852a0932SMarcel Moolenaar 	return (ofs);
125852a0932SMarcel Moolenaar }
126852a0932SMarcel Moolenaar 
127852a0932SMarcel Moolenaar /*
128852a0932SMarcel Moolenaar  * Image chunk handling.
129852a0932SMarcel Moolenaar  */
130852a0932SMarcel Moolenaar 
131852a0932SMarcel Moolenaar static struct chunk *
image_chunk_find(lba_t blk)132852a0932SMarcel Moolenaar image_chunk_find(lba_t blk)
133852a0932SMarcel Moolenaar {
134852a0932SMarcel Moolenaar 	static struct chunk *last = NULL;
135852a0932SMarcel Moolenaar 	struct chunk *ch;
136852a0932SMarcel Moolenaar 
137852a0932SMarcel Moolenaar 	ch = (last != NULL && last->ch_block <= blk)
1381080fb19SMarcel Moolenaar 	    ? last : TAILQ_FIRST(&image_chunks);
139852a0932SMarcel Moolenaar 	while (ch != NULL) {
140852a0932SMarcel Moolenaar 		if (ch->ch_block <= blk &&
141852a0932SMarcel Moolenaar 		    (lba_t)(ch->ch_block + (ch->ch_size / secsz)) > blk) {
142852a0932SMarcel Moolenaar 			last = ch;
143852a0932SMarcel Moolenaar 			break;
144852a0932SMarcel Moolenaar 		}
1451080fb19SMarcel Moolenaar 		ch = TAILQ_NEXT(ch, ch_list);
146852a0932SMarcel Moolenaar 	}
147852a0932SMarcel Moolenaar 	return (ch);
148852a0932SMarcel Moolenaar }
149852a0932SMarcel Moolenaar 
150852a0932SMarcel Moolenaar static size_t
image_chunk_grow(struct chunk * ch,size_t sz)151852a0932SMarcel Moolenaar image_chunk_grow(struct chunk *ch, size_t sz)
152852a0932SMarcel Moolenaar {
153852a0932SMarcel Moolenaar 	size_t dsz, newsz;
154852a0932SMarcel Moolenaar 
155852a0932SMarcel Moolenaar 	newsz = ch->ch_size + sz;
156852a0932SMarcel Moolenaar 	if (newsz > ch->ch_size) {
157852a0932SMarcel Moolenaar 		ch->ch_size = newsz;
158852a0932SMarcel Moolenaar 		return (0);
159852a0932SMarcel Moolenaar 	}
160852a0932SMarcel Moolenaar 	/* We would overflow -- create new chunk for remainder. */
161852a0932SMarcel Moolenaar 	dsz = SIZE_MAX - ch->ch_size;
162852a0932SMarcel Moolenaar 	assert(dsz < sz);
163852a0932SMarcel Moolenaar 	ch->ch_size = SIZE_MAX;
164852a0932SMarcel Moolenaar 	return (sz - dsz);
165852a0932SMarcel Moolenaar }
166852a0932SMarcel Moolenaar 
167852a0932SMarcel Moolenaar static struct chunk *
image_chunk_memory(struct chunk * ch,lba_t blk)168852a0932SMarcel Moolenaar image_chunk_memory(struct chunk *ch, lba_t blk)
169852a0932SMarcel Moolenaar {
170852a0932SMarcel Moolenaar 	struct chunk *new;
171852a0932SMarcel Moolenaar 	void *ptr;
172852a0932SMarcel Moolenaar 
173852a0932SMarcel Moolenaar 	ptr = calloc(1, secsz);
174852a0932SMarcel Moolenaar 	if (ptr == NULL)
175852a0932SMarcel Moolenaar 		return (NULL);
176852a0932SMarcel Moolenaar 
177852a0932SMarcel Moolenaar 	if (ch->ch_block < blk) {
178852a0932SMarcel Moolenaar 		new = malloc(sizeof(*new));
179852a0932SMarcel Moolenaar 		if (new == NULL) {
180852a0932SMarcel Moolenaar 			free(ptr);
181852a0932SMarcel Moolenaar 			return (NULL);
182852a0932SMarcel Moolenaar 		}
183852a0932SMarcel Moolenaar 		memcpy(new, ch, sizeof(*new));
184852a0932SMarcel Moolenaar 		ch->ch_size = (blk - ch->ch_block) * secsz;
185852a0932SMarcel Moolenaar 		new->ch_block = blk;
186852a0932SMarcel Moolenaar 		new->ch_size -= ch->ch_size;
1871080fb19SMarcel Moolenaar 		TAILQ_INSERT_AFTER(&image_chunks, ch, new, ch_list);
188852a0932SMarcel Moolenaar 		image_nchunks++;
189852a0932SMarcel Moolenaar 		ch = new;
190852a0932SMarcel Moolenaar 	}
191852a0932SMarcel Moolenaar 
192852a0932SMarcel Moolenaar 	if (ch->ch_size > secsz) {
193852a0932SMarcel Moolenaar 		new = malloc(sizeof(*new));
194852a0932SMarcel Moolenaar 		if (new == NULL) {
195852a0932SMarcel Moolenaar 			free(ptr);
196852a0932SMarcel Moolenaar 			return (NULL);
197852a0932SMarcel Moolenaar 		}
198852a0932SMarcel Moolenaar 		memcpy(new, ch, sizeof(*new));
199852a0932SMarcel Moolenaar 		ch->ch_size = secsz;
200852a0932SMarcel Moolenaar 		new->ch_block++;
201852a0932SMarcel Moolenaar 		new->ch_size -= secsz;
2021080fb19SMarcel Moolenaar 		TAILQ_INSERT_AFTER(&image_chunks, ch, new, ch_list);
203852a0932SMarcel Moolenaar 		image_nchunks++;
204852a0932SMarcel Moolenaar 	}
205852a0932SMarcel Moolenaar 
206852a0932SMarcel Moolenaar 	ch->ch_type = CH_TYPE_MEMORY;
207852a0932SMarcel Moolenaar 	ch->ch_u.mem.ptr = ptr;
208852a0932SMarcel Moolenaar 	return (ch);
209852a0932SMarcel Moolenaar }
210852a0932SMarcel Moolenaar 
211852a0932SMarcel Moolenaar static int
image_chunk_skipto(lba_t to)212852a0932SMarcel Moolenaar image_chunk_skipto(lba_t to)
213852a0932SMarcel Moolenaar {
214852a0932SMarcel Moolenaar 	struct chunk *ch;
215852a0932SMarcel Moolenaar 	lba_t from;
216852a0932SMarcel Moolenaar 	size_t sz;
217852a0932SMarcel Moolenaar 
2181080fb19SMarcel Moolenaar 	ch = TAILQ_LAST(&image_chunks, chunk_head);
219852a0932SMarcel Moolenaar 	from = (ch != NULL) ? ch->ch_block + (ch->ch_size / secsz) : 0LL;
220852a0932SMarcel Moolenaar 
221852a0932SMarcel Moolenaar 	assert(from <= to);
222852a0932SMarcel Moolenaar 
223852a0932SMarcel Moolenaar 	/* Nothing to do? */
224852a0932SMarcel Moolenaar 	if (from == to)
225852a0932SMarcel Moolenaar 		return (0);
226852a0932SMarcel Moolenaar 	/* Avoid bugs due to overflows. */
227852a0932SMarcel Moolenaar 	if ((uintmax_t)(to - from) > (uintmax_t)(SIZE_MAX / secsz))
228852a0932SMarcel Moolenaar 		return (EFBIG);
229852a0932SMarcel Moolenaar 	sz = (to - from) * secsz;
230852a0932SMarcel Moolenaar 	if (ch != NULL && ch->ch_type == CH_TYPE_ZEROES) {
231852a0932SMarcel Moolenaar 		sz = image_chunk_grow(ch, sz);
232852a0932SMarcel Moolenaar 		if (sz == 0)
233852a0932SMarcel Moolenaar 			return (0);
234852a0932SMarcel Moolenaar 		from = ch->ch_block + (ch->ch_size / secsz);
235852a0932SMarcel Moolenaar 	}
236852a0932SMarcel Moolenaar 	ch = malloc(sizeof(*ch));
237852a0932SMarcel Moolenaar 	if (ch == NULL)
238852a0932SMarcel Moolenaar 		return (ENOMEM);
239852a0932SMarcel Moolenaar 	memset(ch, 0, sizeof(*ch));
240852a0932SMarcel Moolenaar 	ch->ch_block = from;
241852a0932SMarcel Moolenaar 	ch->ch_size = sz;
242852a0932SMarcel Moolenaar 	ch->ch_type = CH_TYPE_ZEROES;
2431080fb19SMarcel Moolenaar 	TAILQ_INSERT_TAIL(&image_chunks, ch, ch_list);
244852a0932SMarcel Moolenaar 	image_nchunks++;
245852a0932SMarcel Moolenaar 	return (0);
246852a0932SMarcel Moolenaar }
247852a0932SMarcel Moolenaar 
248852a0932SMarcel Moolenaar static int
image_chunk_append(lba_t blk,size_t sz,off_t ofs,int fd)249852a0932SMarcel Moolenaar image_chunk_append(lba_t blk, size_t sz, off_t ofs, int fd)
250852a0932SMarcel Moolenaar {
251852a0932SMarcel Moolenaar 	struct chunk *ch;
252852a0932SMarcel Moolenaar 
2531080fb19SMarcel Moolenaar 	ch = TAILQ_LAST(&image_chunks, chunk_head);
254852a0932SMarcel Moolenaar 	if (ch != NULL && ch->ch_type == CH_TYPE_FILE) {
255852a0932SMarcel Moolenaar 		if (fd == ch->ch_u.file.fd &&
256852a0932SMarcel Moolenaar 		    blk == (lba_t)(ch->ch_block + (ch->ch_size / secsz)) &&
257852a0932SMarcel Moolenaar 		    ofs == (off_t)(ch->ch_u.file.ofs + ch->ch_size)) {
258852a0932SMarcel Moolenaar 			sz = image_chunk_grow(ch, sz);
259852a0932SMarcel Moolenaar 			if (sz == 0)
260852a0932SMarcel Moolenaar 				return (0);
261852a0932SMarcel Moolenaar 			blk = ch->ch_block + (ch->ch_size / secsz);
262852a0932SMarcel Moolenaar 			ofs = ch->ch_u.file.ofs + ch->ch_size;
263852a0932SMarcel Moolenaar 		}
264852a0932SMarcel Moolenaar 	}
265852a0932SMarcel Moolenaar 	ch = malloc(sizeof(*ch));
266852a0932SMarcel Moolenaar 	if (ch == NULL)
267852a0932SMarcel Moolenaar 		return (ENOMEM);
268852a0932SMarcel Moolenaar 	memset(ch, 0, sizeof(*ch));
269852a0932SMarcel Moolenaar 	ch->ch_block = blk;
270852a0932SMarcel Moolenaar 	ch->ch_size = sz;
271852a0932SMarcel Moolenaar 	ch->ch_type = CH_TYPE_FILE;
272852a0932SMarcel Moolenaar 	ch->ch_u.file.ofs = ofs;
273852a0932SMarcel Moolenaar 	ch->ch_u.file.fd = fd;
2741080fb19SMarcel Moolenaar 	TAILQ_INSERT_TAIL(&image_chunks, ch, ch_list);
275852a0932SMarcel Moolenaar 	image_nchunks++;
276852a0932SMarcel Moolenaar 	return (0);
277852a0932SMarcel Moolenaar }
278852a0932SMarcel Moolenaar 
279852a0932SMarcel Moolenaar static int
image_chunk_copyin(lba_t blk,void * buf,size_t sz,off_t ofs,int fd)280852a0932SMarcel Moolenaar image_chunk_copyin(lba_t blk, void *buf, size_t sz, off_t ofs, int fd)
281852a0932SMarcel Moolenaar {
282852a0932SMarcel Moolenaar 	uint8_t *p = buf;
283852a0932SMarcel Moolenaar 	int error;
284852a0932SMarcel Moolenaar 
285852a0932SMarcel Moolenaar 	error = 0;
286852a0932SMarcel Moolenaar 	sz = (sz + secsz - 1) & ~(secsz - 1);
287852a0932SMarcel Moolenaar 	while (!error && sz > 0) {
288852a0932SMarcel Moolenaar 		if (is_empty_sector(p))
289852a0932SMarcel Moolenaar 			error = image_chunk_skipto(blk + 1);
290852a0932SMarcel Moolenaar 		else
291852a0932SMarcel Moolenaar 			error = image_chunk_append(blk, secsz, ofs, fd);
292852a0932SMarcel Moolenaar 		blk++;
293852a0932SMarcel Moolenaar 		p += secsz;
294852a0932SMarcel Moolenaar 		sz -= secsz;
295852a0932SMarcel Moolenaar 		ofs += secsz;
296852a0932SMarcel Moolenaar 	}
297852a0932SMarcel Moolenaar 	return (error);
298852a0932SMarcel Moolenaar }
299852a0932SMarcel Moolenaar 
300852a0932SMarcel Moolenaar /*
301852a0932SMarcel Moolenaar  * File mapping support.
302852a0932SMarcel Moolenaar  */
303852a0932SMarcel Moolenaar 
304852a0932SMarcel Moolenaar static void *
image_file_map(int fd,off_t ofs,size_t sz,off_t * iofp)305*baf4abfcSSimon J. Gerraty image_file_map(int fd, off_t ofs, size_t sz, off_t *iofp)
306852a0932SMarcel Moolenaar {
307852a0932SMarcel Moolenaar 	void *ptr;
308852a0932SMarcel Moolenaar 	size_t unit;
309852a0932SMarcel Moolenaar 	int flags, prot;
310*baf4abfcSSimon J. Gerraty 	off_t x;
311852a0932SMarcel Moolenaar 
312*baf4abfcSSimon J. Gerraty 	/* On Linux anyway ofs must also be page aligned */
313*baf4abfcSSimon J. Gerraty 	if ((x = (ofs % image_swap_pgsz)) != 0) {
314*baf4abfcSSimon J. Gerraty 	    ofs -= x;
315*baf4abfcSSimon J. Gerraty 	    sz += x;
316*baf4abfcSSimon J. Gerraty 	    *iofp = x;
317*baf4abfcSSimon J. Gerraty 	} else
318*baf4abfcSSimon J. Gerraty 	    *iofp = 0;
319852a0932SMarcel Moolenaar 	unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz;
320852a0932SMarcel Moolenaar 	assert((unit & (unit - 1)) == 0);
321852a0932SMarcel Moolenaar 
322852a0932SMarcel Moolenaar 	flags = MAP_NOCORE | MAP_NOSYNC | MAP_SHARED;
323852a0932SMarcel Moolenaar 	/* Allow writing to our swap file only. */
324852a0932SMarcel Moolenaar 	prot = PROT_READ | ((fd == image_swap_fd) ? PROT_WRITE : 0);
325852a0932SMarcel Moolenaar 	sz = (sz + unit - 1) & ~(unit - 1);
326852a0932SMarcel Moolenaar 	ptr = mmap(NULL, sz, prot, flags, fd, ofs);
327852a0932SMarcel Moolenaar 	return ((ptr == MAP_FAILED) ? NULL : ptr);
328852a0932SMarcel Moolenaar }
329852a0932SMarcel Moolenaar 
330852a0932SMarcel Moolenaar static int
image_file_unmap(void * buffer,size_t sz)331852a0932SMarcel Moolenaar image_file_unmap(void *buffer, size_t sz)
332852a0932SMarcel Moolenaar {
333852a0932SMarcel Moolenaar 	size_t unit;
334852a0932SMarcel Moolenaar 
335852a0932SMarcel Moolenaar 	unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz;
336852a0932SMarcel Moolenaar 	sz = (sz + unit - 1) & ~(unit - 1);
337cc7f01a5SMark Johnston 	if (madvise(buffer, sz, MADV_DONTNEED) != 0)
338cc7f01a5SMark Johnston 		warn("madvise");
339852a0932SMarcel Moolenaar 	munmap(buffer, sz);
340852a0932SMarcel Moolenaar 	return (0);
341852a0932SMarcel Moolenaar }
342852a0932SMarcel Moolenaar 
343852a0932SMarcel Moolenaar /*
344852a0932SMarcel Moolenaar  * Input/source file handling.
345852a0932SMarcel Moolenaar  */
346852a0932SMarcel Moolenaar 
347852a0932SMarcel Moolenaar static int
image_copyin_stream(lba_t blk,int fd,uint64_t * sizep)348852a0932SMarcel Moolenaar image_copyin_stream(lba_t blk, int fd, uint64_t *sizep)
349852a0932SMarcel Moolenaar {
350852a0932SMarcel Moolenaar 	char *buffer;
351852a0932SMarcel Moolenaar 	uint64_t bytesize;
352852a0932SMarcel Moolenaar 	off_t swofs;
353852a0932SMarcel Moolenaar 	size_t iosz;
354852a0932SMarcel Moolenaar 	ssize_t rdsz;
355852a0932SMarcel Moolenaar 	int error;
356*baf4abfcSSimon J. Gerraty 	off_t iof;
357852a0932SMarcel Moolenaar 
358852a0932SMarcel Moolenaar 	/*
359852a0932SMarcel Moolenaar 	 * This makes sure we're doing I/O in multiples of the page
360852a0932SMarcel Moolenaar 	 * size as well as of the sector size. 2MB is the minimum
361852a0932SMarcel Moolenaar 	 * by virtue of secsz at least 512 bytes and the page size
362852a0932SMarcel Moolenaar 	 * at least 4K bytes.
363852a0932SMarcel Moolenaar 	 */
364852a0932SMarcel Moolenaar 	iosz = secsz * image_swap_pgsz;
365852a0932SMarcel Moolenaar 
366852a0932SMarcel Moolenaar 	bytesize = 0;
367852a0932SMarcel Moolenaar 	do {
368852a0932SMarcel Moolenaar 		swofs = image_swap_alloc(iosz);
369852a0932SMarcel Moolenaar 		if (swofs == -1LL)
370852a0932SMarcel Moolenaar 			return (errno);
371*baf4abfcSSimon J. Gerraty 		buffer = image_file_map(image_swap_fd, swofs, iosz, &iof);
372852a0932SMarcel Moolenaar 		if (buffer == NULL)
373852a0932SMarcel Moolenaar 			return (errno);
374*baf4abfcSSimon J. Gerraty 		rdsz = read(fd, &buffer[iof], iosz);
375852a0932SMarcel Moolenaar 		if (rdsz > 0)
376*baf4abfcSSimon J. Gerraty 			error = image_chunk_copyin(blk, &buffer[iof], rdsz, swofs,
377852a0932SMarcel Moolenaar 			    image_swap_fd);
378852a0932SMarcel Moolenaar 		else if (rdsz < 0)
379852a0932SMarcel Moolenaar 			error = errno;
380852a0932SMarcel Moolenaar 		else
381852a0932SMarcel Moolenaar 			error = 0;
382852a0932SMarcel Moolenaar 		image_file_unmap(buffer, iosz);
383852a0932SMarcel Moolenaar 		/* XXX should we relinguish unused swap space? */
384852a0932SMarcel Moolenaar 		if (error)
385852a0932SMarcel Moolenaar 			return (error);
386852a0932SMarcel Moolenaar 
387852a0932SMarcel Moolenaar 		bytesize += rdsz;
388852a0932SMarcel Moolenaar 		blk += (rdsz + secsz - 1) / secsz;
389852a0932SMarcel Moolenaar 	} while (rdsz > 0);
390852a0932SMarcel Moolenaar 
391852a0932SMarcel Moolenaar 	if (sizep != NULL)
392852a0932SMarcel Moolenaar 		*sizep = bytesize;
393852a0932SMarcel Moolenaar 	return (0);
394852a0932SMarcel Moolenaar }
395852a0932SMarcel Moolenaar 
396852a0932SMarcel Moolenaar static int
image_copyin_mapped(lba_t blk,int fd,uint64_t * sizep)397852a0932SMarcel Moolenaar image_copyin_mapped(lba_t blk, int fd, uint64_t *sizep)
398852a0932SMarcel Moolenaar {
399*baf4abfcSSimon J. Gerraty 	off_t cur, data, end, hole, pos, iof;
400*baf4abfcSSimon J. Gerraty 	void *mp;
401*baf4abfcSSimon J. Gerraty 	char *buf;
402852a0932SMarcel Moolenaar 	uint64_t bytesize;
403852a0932SMarcel Moolenaar 	size_t iosz, sz;
404852a0932SMarcel Moolenaar 	int error;
405852a0932SMarcel Moolenaar 
406852a0932SMarcel Moolenaar 	/*
407852a0932SMarcel Moolenaar 	 * We'd like to know the size of the file and we must
408852a0932SMarcel Moolenaar 	 * be able to seek in order to mmap(2). If this isn't
409852a0932SMarcel Moolenaar 	 * possible, then treat the file as a stream/pipe.
410852a0932SMarcel Moolenaar 	 */
411852a0932SMarcel Moolenaar 	end = lseek(fd, 0L, SEEK_END);
412852a0932SMarcel Moolenaar 	if (end == -1L)
413852a0932SMarcel Moolenaar 		return (image_copyin_stream(blk, fd, sizep));
414852a0932SMarcel Moolenaar 
415852a0932SMarcel Moolenaar 	/*
416852a0932SMarcel Moolenaar 	 * We need the file opened for the duration and our
417852a0932SMarcel Moolenaar 	 * caller is going to close the file. Make a dup(2)
418852a0932SMarcel Moolenaar 	 * so that control the faith of the descriptor.
419852a0932SMarcel Moolenaar 	 */
420852a0932SMarcel Moolenaar 	fd = dup(fd);
421852a0932SMarcel Moolenaar 	if (fd == -1)
422852a0932SMarcel Moolenaar 		return (errno);
423852a0932SMarcel Moolenaar 
424852a0932SMarcel Moolenaar 	iosz = secsz * image_swap_pgsz;
425852a0932SMarcel Moolenaar 
426852a0932SMarcel Moolenaar 	bytesize = 0;
427852a0932SMarcel Moolenaar 	cur = pos = 0;
428852a0932SMarcel Moolenaar 	error = 0;
429852a0932SMarcel Moolenaar 	while (!error && cur < end) {
430852a0932SMarcel Moolenaar 		hole = lseek(fd, cur, SEEK_HOLE);
4319ba57342SMarcel Moolenaar 		if (hole == -1)
4329ba57342SMarcel Moolenaar 			hole = end;
433852a0932SMarcel Moolenaar 		data = lseek(fd, cur, SEEK_DATA);
4349ba57342SMarcel Moolenaar 		if (data == -1)
4359ba57342SMarcel Moolenaar 			data = end;
436852a0932SMarcel Moolenaar 
437852a0932SMarcel Moolenaar 		/*
438852a0932SMarcel Moolenaar 		 * Treat the entire file as data if sparse files
439852a0932SMarcel Moolenaar 		 * are not supported by the underlying file system.
440852a0932SMarcel Moolenaar 		 */
4419ba57342SMarcel Moolenaar 		if (hole == end && data == end)
442852a0932SMarcel Moolenaar 			data = cur;
443852a0932SMarcel Moolenaar 
444852a0932SMarcel Moolenaar 		if (cur == hole && data > hole) {
445852a0932SMarcel Moolenaar 			hole = pos;
446852a0932SMarcel Moolenaar 			pos = data & ~((uint64_t)secsz - 1);
447852a0932SMarcel Moolenaar 
448852a0932SMarcel Moolenaar 			blk += (pos - hole) / secsz;
449852a0932SMarcel Moolenaar 			error = image_chunk_skipto(blk);
450852a0932SMarcel Moolenaar 
451852a0932SMarcel Moolenaar 			bytesize += pos - hole;
452852a0932SMarcel Moolenaar 			cur = data;
453852a0932SMarcel Moolenaar 		} else if (cur == data && hole > data) {
454852a0932SMarcel Moolenaar 			data = pos;
455852a0932SMarcel Moolenaar 			pos = (hole + secsz - 1) & ~((uint64_t)secsz - 1);
456852a0932SMarcel Moolenaar 
457852a0932SMarcel Moolenaar 			while (data < pos) {
458852a0932SMarcel Moolenaar 				sz = (pos - data > (off_t)iosz)
459852a0932SMarcel Moolenaar 				    ? iosz : (size_t)(pos - data);
460852a0932SMarcel Moolenaar 
461*baf4abfcSSimon J. Gerraty 				buf = mp = image_file_map(fd, data, sz, &iof);
462*baf4abfcSSimon J. Gerraty 				if (mp != NULL) {
463*baf4abfcSSimon J. Gerraty 					buf += iof;
464852a0932SMarcel Moolenaar 					error = image_chunk_copyin(blk, buf,
465852a0932SMarcel Moolenaar 					    sz, data, fd);
466*baf4abfcSSimon J. Gerraty 					image_file_unmap(mp, sz);
467852a0932SMarcel Moolenaar 				} else
468852a0932SMarcel Moolenaar 					error = errno;
469852a0932SMarcel Moolenaar 
470852a0932SMarcel Moolenaar 				blk += sz / secsz;
471852a0932SMarcel Moolenaar 				bytesize += sz;
472852a0932SMarcel Moolenaar 				data += sz;
473852a0932SMarcel Moolenaar 			}
474852a0932SMarcel Moolenaar 			cur = hole;
475852a0932SMarcel Moolenaar 		} else {
476852a0932SMarcel Moolenaar 			/*
477852a0932SMarcel Moolenaar 			 * I don't know what this means or whether it
478852a0932SMarcel Moolenaar 			 * can happen at all...
479852a0932SMarcel Moolenaar 			 */
4804039ea7cSMarcel Moolenaar 			assert(0);
481852a0932SMarcel Moolenaar 		}
482852a0932SMarcel Moolenaar 	}
483852a0932SMarcel Moolenaar 	if (error)
484852a0932SMarcel Moolenaar 		close(fd);
485852a0932SMarcel Moolenaar 	if (!error && sizep != NULL)
486852a0932SMarcel Moolenaar 		*sizep = bytesize;
487852a0932SMarcel Moolenaar 	return (error);
488f0e9dcedSMarcel Moolenaar }
489f0e9dcedSMarcel Moolenaar 
490f0e9dcedSMarcel Moolenaar int
image_copyin(lba_t blk,int fd,uint64_t * sizep)491f0e9dcedSMarcel Moolenaar image_copyin(lba_t blk, int fd, uint64_t *sizep)
492f0e9dcedSMarcel Moolenaar {
493852a0932SMarcel Moolenaar 	struct stat sb;
494852a0932SMarcel Moolenaar 	int error;
495f0e9dcedSMarcel Moolenaar 
496852a0932SMarcel Moolenaar 	error = image_chunk_skipto(blk);
497852a0932SMarcel Moolenaar 	if (!error) {
498852a0932SMarcel Moolenaar 		if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode))
499852a0932SMarcel Moolenaar 			error = image_copyin_stream(blk, fd, sizep);
500852a0932SMarcel Moolenaar 		else
501852a0932SMarcel Moolenaar 			error = image_copyin_mapped(blk, fd, sizep);
502f0e9dcedSMarcel Moolenaar 	}
503f0e9dcedSMarcel Moolenaar 	return (error);
504f0e9dcedSMarcel Moolenaar }
505f0e9dcedSMarcel Moolenaar 
506852a0932SMarcel Moolenaar /*
507852a0932SMarcel Moolenaar  * Output/sink file handling.
508852a0932SMarcel Moolenaar  */
509852a0932SMarcel Moolenaar 
510f0e9dcedSMarcel Moolenaar int
image_copyout(int fd)511f0e9dcedSMarcel Moolenaar image_copyout(int fd)
512f0e9dcedSMarcel Moolenaar {
513f3582a72SMarcel Moolenaar 	int error;
514f3582a72SMarcel Moolenaar 
515f3582a72SMarcel Moolenaar 	error = image_copyout_region(fd, 0, image_size);
516a0f136e1SMarcel Moolenaar 	if (!error)
517a0f136e1SMarcel Moolenaar 		error = image_copyout_done(fd);
518f3582a72SMarcel Moolenaar 	return (error);
519a0f136e1SMarcel Moolenaar }
520a0f136e1SMarcel Moolenaar 
521a0f136e1SMarcel Moolenaar int
image_copyout_done(int fd)522a0f136e1SMarcel Moolenaar image_copyout_done(int fd)
523a0f136e1SMarcel Moolenaar {
524a0f136e1SMarcel Moolenaar 	off_t ofs;
525a0f136e1SMarcel Moolenaar 	int error;
526f3582a72SMarcel Moolenaar 
527f3582a72SMarcel Moolenaar 	ofs = lseek(fd, 0L, SEEK_CUR);
528f3582a72SMarcel Moolenaar 	if (ofs == -1)
529f3582a72SMarcel Moolenaar 		return (0);
530f3582a72SMarcel Moolenaar 	error = (ftruncate(fd, ofs) == -1) ? errno : 0;
531f3582a72SMarcel Moolenaar 	return (error);
532f3582a72SMarcel Moolenaar }
533f3582a72SMarcel Moolenaar 
534852a0932SMarcel Moolenaar static int
image_copyout_memory(int fd,size_t size,void * ptr)535852a0932SMarcel Moolenaar image_copyout_memory(int fd, size_t size, void *ptr)
536852a0932SMarcel Moolenaar {
537852a0932SMarcel Moolenaar 
538852a0932SMarcel Moolenaar 	if (write(fd, ptr, size) == -1)
539852a0932SMarcel Moolenaar 		return (errno);
540852a0932SMarcel Moolenaar 	return (0);
541852a0932SMarcel Moolenaar }
542852a0932SMarcel Moolenaar 
543ddaceed2SMarcel Moolenaar int
image_copyout_zeroes(int fd,size_t count)544ddaceed2SMarcel Moolenaar image_copyout_zeroes(int fd, size_t count)
545852a0932SMarcel Moolenaar {
546852a0932SMarcel Moolenaar 	static uint8_t *zeroes = NULL;
547852a0932SMarcel Moolenaar 	size_t sz;
548852a0932SMarcel Moolenaar 	int error;
549852a0932SMarcel Moolenaar 
550ddaceed2SMarcel Moolenaar 	if (lseek(fd, (off_t)count, SEEK_CUR) != -1)
551852a0932SMarcel Moolenaar 		return (0);
552852a0932SMarcel Moolenaar 
553852a0932SMarcel Moolenaar 	/*
554852a0932SMarcel Moolenaar 	 * If we can't seek, we must write.
555852a0932SMarcel Moolenaar 	 */
556852a0932SMarcel Moolenaar 
557852a0932SMarcel Moolenaar 	if (zeroes == NULL) {
558852a0932SMarcel Moolenaar 		zeroes = calloc(1, secsz);
559852a0932SMarcel Moolenaar 		if (zeroes == NULL)
560852a0932SMarcel Moolenaar 			return (ENOMEM);
561852a0932SMarcel Moolenaar 	}
562852a0932SMarcel Moolenaar 
563ddaceed2SMarcel Moolenaar 	while (count > 0) {
564ddaceed2SMarcel Moolenaar 		sz = (count > secsz) ? secsz : count;
565852a0932SMarcel Moolenaar 		error = image_copyout_memory(fd, sz, zeroes);
566852a0932SMarcel Moolenaar 		if (error)
567852a0932SMarcel Moolenaar 			return (error);
568ddaceed2SMarcel Moolenaar 		count -= sz;
569852a0932SMarcel Moolenaar 	}
570852a0932SMarcel Moolenaar 	return (0);
571852a0932SMarcel Moolenaar }
572852a0932SMarcel Moolenaar 
573852a0932SMarcel Moolenaar static int
image_copyout_file(int fd,size_t size,int ifd,off_t iofs)574852a0932SMarcel Moolenaar image_copyout_file(int fd, size_t size, int ifd, off_t iofs)
575852a0932SMarcel Moolenaar {
576*baf4abfcSSimon J. Gerraty 	void *mp;
577*baf4abfcSSimon J. Gerraty 	char *buf;
578852a0932SMarcel Moolenaar 	size_t iosz, sz;
579852a0932SMarcel Moolenaar 	int error;
580*baf4abfcSSimon J. Gerraty 	off_t iof;
581852a0932SMarcel Moolenaar 
582852a0932SMarcel Moolenaar 	iosz = secsz * image_swap_pgsz;
583852a0932SMarcel Moolenaar 
584852a0932SMarcel Moolenaar 	while (size > 0) {
585852a0932SMarcel Moolenaar 		sz = (size > iosz) ? iosz : size;
586*baf4abfcSSimon J. Gerraty 		buf = mp = image_file_map(ifd, iofs, sz, &iof);
587852a0932SMarcel Moolenaar 		if (buf == NULL)
588852a0932SMarcel Moolenaar 			return (errno);
589*baf4abfcSSimon J. Gerraty 		buf += iof;
590852a0932SMarcel Moolenaar 		error = image_copyout_memory(fd, sz, buf);
591*baf4abfcSSimon J. Gerraty 		image_file_unmap(mp, sz);
592852a0932SMarcel Moolenaar 		if (error)
593852a0932SMarcel Moolenaar 			return (error);
594852a0932SMarcel Moolenaar 		size -= sz;
595852a0932SMarcel Moolenaar 		iofs += sz;
596852a0932SMarcel Moolenaar 	}
597852a0932SMarcel Moolenaar 	return (0);
598852a0932SMarcel Moolenaar }
599852a0932SMarcel Moolenaar 
600f3582a72SMarcel Moolenaar int
image_copyout_region(int fd,lba_t blk,lba_t size)601f3582a72SMarcel Moolenaar image_copyout_region(int fd, lba_t blk, lba_t size)
602f3582a72SMarcel Moolenaar {
603852a0932SMarcel Moolenaar 	struct chunk *ch;
604852a0932SMarcel Moolenaar 	size_t ofs, sz;
605f0e9dcedSMarcel Moolenaar 	int error;
606f0e9dcedSMarcel Moolenaar 
607f3582a72SMarcel Moolenaar 	size *= secsz;
608852a0932SMarcel Moolenaar 
6097b5a53eaSMarcel Moolenaar 	error = 0;
6107b5a53eaSMarcel Moolenaar 	while (!error && size > 0) {
611852a0932SMarcel Moolenaar 		ch = image_chunk_find(blk);
6127b5a53eaSMarcel Moolenaar 		if (ch == NULL) {
6137b5a53eaSMarcel Moolenaar 			error = EINVAL;
6147b5a53eaSMarcel Moolenaar 			break;
6157b5a53eaSMarcel Moolenaar 		}
616852a0932SMarcel Moolenaar 		ofs = (blk - ch->ch_block) * secsz;
617852a0932SMarcel Moolenaar 		sz = ch->ch_size - ofs;
618852a0932SMarcel Moolenaar 		sz = ((lba_t)sz < size) ? sz : (size_t)size;
619852a0932SMarcel Moolenaar 		switch (ch->ch_type) {
620852a0932SMarcel Moolenaar 		case CH_TYPE_ZEROES:
621852a0932SMarcel Moolenaar 			error = image_copyout_zeroes(fd, sz);
622f0e9dcedSMarcel Moolenaar 			break;
623852a0932SMarcel Moolenaar 		case CH_TYPE_FILE:
624852a0932SMarcel Moolenaar 			error = image_copyout_file(fd, sz, ch->ch_u.file.fd,
625852a0932SMarcel Moolenaar 			    ch->ch_u.file.ofs + ofs);
626f0e9dcedSMarcel Moolenaar 			break;
627852a0932SMarcel Moolenaar 		case CH_TYPE_MEMORY:
628852a0932SMarcel Moolenaar 			error = image_copyout_memory(fd, sz, ch->ch_u.mem.ptr);
629852a0932SMarcel Moolenaar 			break;
630852a0932SMarcel Moolenaar 		default:
6314039ea7cSMarcel Moolenaar 			assert(0);
632f0e9dcedSMarcel Moolenaar 		}
633852a0932SMarcel Moolenaar 		size -= sz;
634852a0932SMarcel Moolenaar 		blk += sz / secsz;
635f0e9dcedSMarcel Moolenaar 	}
6367b5a53eaSMarcel Moolenaar 	return (error);
637f0e9dcedSMarcel Moolenaar }
638f0e9dcedSMarcel Moolenaar 
639a0f136e1SMarcel Moolenaar int
image_data(lba_t blk,lba_t size)640a0f136e1SMarcel Moolenaar image_data(lba_t blk, lba_t size)
641a0f136e1SMarcel Moolenaar {
642852a0932SMarcel Moolenaar 	struct chunk *ch;
643852a0932SMarcel Moolenaar 	lba_t lim;
644a0f136e1SMarcel Moolenaar 
645852a0932SMarcel Moolenaar 	while (1) {
646852a0932SMarcel Moolenaar 		ch = image_chunk_find(blk);
647852a0932SMarcel Moolenaar 		if (ch == NULL)
648852a0932SMarcel Moolenaar 			return (0);
649852a0932SMarcel Moolenaar 		if (ch->ch_type != CH_TYPE_ZEROES)
650a0f136e1SMarcel Moolenaar 			return (1);
651852a0932SMarcel Moolenaar 		lim = ch->ch_block + (ch->ch_size / secsz);
652852a0932SMarcel Moolenaar 		if (lim >= blk + size)
653852a0932SMarcel Moolenaar 			return (0);
654852a0932SMarcel Moolenaar 		size -= lim - blk;
655852a0932SMarcel Moolenaar 		blk = lim;
656a0f136e1SMarcel Moolenaar 	}
657852a0932SMarcel Moolenaar 	/*NOTREACHED*/
658a0f136e1SMarcel Moolenaar }
659a0f136e1SMarcel Moolenaar 
660f0e9dcedSMarcel Moolenaar lba_t
image_get_size(void)661f0e9dcedSMarcel Moolenaar image_get_size(void)
662f0e9dcedSMarcel Moolenaar {
663f0e9dcedSMarcel Moolenaar 
664f0e9dcedSMarcel Moolenaar 	return (image_size);
665f0e9dcedSMarcel Moolenaar }
666f0e9dcedSMarcel Moolenaar 
667f0e9dcedSMarcel Moolenaar int
image_set_size(lba_t blk)668f0e9dcedSMarcel Moolenaar image_set_size(lba_t blk)
669f0e9dcedSMarcel Moolenaar {
670852a0932SMarcel Moolenaar 	int error;
671f0e9dcedSMarcel Moolenaar 
672852a0932SMarcel Moolenaar 	error = image_chunk_skipto(blk);
673852a0932SMarcel Moolenaar 	if (!error)
674f0e9dcedSMarcel Moolenaar 		image_size = blk;
675852a0932SMarcel Moolenaar 	return (error);
676f0e9dcedSMarcel Moolenaar }
677f0e9dcedSMarcel Moolenaar 
678f0e9dcedSMarcel Moolenaar int
image_write(lba_t blk,void * buf,ssize_t len)679f0e9dcedSMarcel Moolenaar image_write(lba_t blk, void *buf, ssize_t len)
680f0e9dcedSMarcel Moolenaar {
681852a0932SMarcel Moolenaar 	struct chunk *ch;
682f0e9dcedSMarcel Moolenaar 
683852a0932SMarcel Moolenaar 	while (len > 0) {
684852a0932SMarcel Moolenaar 		if (!is_empty_sector(buf)) {
685852a0932SMarcel Moolenaar 			ch = image_chunk_find(blk);
686852a0932SMarcel Moolenaar 			if (ch == NULL)
687852a0932SMarcel Moolenaar 				return (ENXIO);
688852a0932SMarcel Moolenaar 			/* We may not be able to write to files. */
689852a0932SMarcel Moolenaar 			if (ch->ch_type == CH_TYPE_FILE)
690852a0932SMarcel Moolenaar 				return (EINVAL);
691852a0932SMarcel Moolenaar 			if (ch->ch_type == CH_TYPE_ZEROES) {
692852a0932SMarcel Moolenaar 				ch = image_chunk_memory(ch, blk);
693852a0932SMarcel Moolenaar 				if (ch == NULL)
694852a0932SMarcel Moolenaar 					return (ENOMEM);
695852a0932SMarcel Moolenaar 			}
696852a0932SMarcel Moolenaar 			assert(ch->ch_type == CH_TYPE_MEMORY);
697852a0932SMarcel Moolenaar 			memcpy(ch->ch_u.mem.ptr, buf, secsz);
698852a0932SMarcel Moolenaar 		}
699852a0932SMarcel Moolenaar 		blk++;
700852a0932SMarcel Moolenaar 		buf = (char *)buf + secsz;
701852a0932SMarcel Moolenaar 		len--;
702852a0932SMarcel Moolenaar 	}
703f0e9dcedSMarcel Moolenaar 	return (0);
704f0e9dcedSMarcel Moolenaar }
705f0e9dcedSMarcel Moolenaar 
706852a0932SMarcel Moolenaar static void
image_cleanup(void)707852a0932SMarcel Moolenaar image_cleanup(void)
708852a0932SMarcel Moolenaar {
709852a0932SMarcel Moolenaar 	struct chunk *ch;
710852a0932SMarcel Moolenaar 
7111080fb19SMarcel Moolenaar 	while ((ch = TAILQ_FIRST(&image_chunks)) != NULL) {
712852a0932SMarcel Moolenaar 		switch (ch->ch_type) {
713852a0932SMarcel Moolenaar 		case CH_TYPE_FILE:
714852a0932SMarcel Moolenaar 			/* We may be closing the same file multiple times. */
715852a0932SMarcel Moolenaar 			if (ch->ch_u.file.fd != -1)
716852a0932SMarcel Moolenaar 				close(ch->ch_u.file.fd);
717852a0932SMarcel Moolenaar 			break;
718852a0932SMarcel Moolenaar 		case CH_TYPE_MEMORY:
719852a0932SMarcel Moolenaar 			free(ch->ch_u.mem.ptr);
720852a0932SMarcel Moolenaar 			break;
721852a0932SMarcel Moolenaar 		default:
722852a0932SMarcel Moolenaar 			break;
723852a0932SMarcel Moolenaar 		}
7241080fb19SMarcel Moolenaar 		TAILQ_REMOVE(&image_chunks, ch, ch_list);
725852a0932SMarcel Moolenaar 		free(ch);
726852a0932SMarcel Moolenaar 	}
727852a0932SMarcel Moolenaar 	if (image_swap_fd != -1)
728852a0932SMarcel Moolenaar 		close(image_swap_fd);
729852a0932SMarcel Moolenaar 	unlink(image_swap_file);
730852a0932SMarcel Moolenaar }
731852a0932SMarcel Moolenaar 
732f0e9dcedSMarcel Moolenaar int
image_init(void)733f0e9dcedSMarcel Moolenaar image_init(void)
734f0e9dcedSMarcel Moolenaar {
735aa30ba04SMarcel Moolenaar 	const char *tmpdir;
736f0e9dcedSMarcel Moolenaar 
7371080fb19SMarcel Moolenaar 	TAILQ_INIT(&image_chunks);
738852a0932SMarcel Moolenaar 	image_nchunks = 0;
739852a0932SMarcel Moolenaar 
740852a0932SMarcel Moolenaar 	image_swap_size = 0;
741852a0932SMarcel Moolenaar 	image_swap_pgsz = getpagesize();
742852a0932SMarcel Moolenaar 
743852a0932SMarcel Moolenaar 	if (atexit(image_cleanup) == -1)
744f0e9dcedSMarcel Moolenaar 		return (errno);
745aa30ba04SMarcel Moolenaar 	if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
746aa30ba04SMarcel Moolenaar 		tmpdir = _PATH_TMP;
747852a0932SMarcel Moolenaar 	snprintf(image_swap_file, sizeof(image_swap_file), "%s/mkimg-XXXXXX",
748aa30ba04SMarcel Moolenaar 	    tmpdir);
749852a0932SMarcel Moolenaar 	image_swap_fd = mkstemp(image_swap_file);
750852a0932SMarcel Moolenaar 	if (image_swap_fd == -1)
751f0e9dcedSMarcel Moolenaar 		return (errno);
752f0e9dcedSMarcel Moolenaar 	return (0);
753f0e9dcedSMarcel Moolenaar }
754