image.c revision 301090
1265574Smarcel/*-
2265574Smarcel * Copyright (c) 2014 Juniper Networks, Inc.
3265574Smarcel * All rights reserved.
4265574Smarcel *
5265574Smarcel * Redistribution and use in source and binary forms, with or without
6265574Smarcel * modification, are permitted provided that the following conditions
7265574Smarcel * are met:
8265574Smarcel * 1. Redistributions of source code must retain the above copyright
9265574Smarcel *    notice, this list of conditions and the following disclaimer.
10265574Smarcel * 2. Redistributions in binary form must reproduce the above copyright
11265574Smarcel *    notice, this list of conditions and the following disclaimer in the
12265574Smarcel *    documentation and/or other materials provided with the distribution.
13265574Smarcel *
14265574Smarcel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15265574Smarcel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16265574Smarcel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17265574Smarcel * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18265574Smarcel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19265574Smarcel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20265574Smarcel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21265574Smarcel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22265574Smarcel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23265574Smarcel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24265574Smarcel * SUCH DAMAGE.
25265574Smarcel */
26265574Smarcel
27265574Smarcel#include <sys/cdefs.h>
28265574Smarcel__FBSDID("$FreeBSD: head/usr.bin/mkimg/image.c 301090 2016-06-01 02:30:06Z markj $");
29265574Smarcel
30272384Smarcel#include <sys/mman.h>
31272384Smarcel#include <sys/queue.h>
32272384Smarcel#include <sys/stat.h>
33265574Smarcel#include <sys/types.h>
34265574Smarcel#include <assert.h>
35301090Smarkj#include <err.h>
36265574Smarcel#include <errno.h>
37266556Smarcel#include <limits.h>
38266556Smarcel#include <paths.h>
39272384Smarcel#include <stdint.h>
40266556Smarcel#include <stdio.h>
41265574Smarcel#include <stdlib.h>
42272384Smarcel#include <string.h>
43265574Smarcel#include <unistd.h>
44265574Smarcel
45265579Smarcel#include "image.h"
46265574Smarcel#include "mkimg.h"
47265574Smarcel
48272384Smarcelstruct chunk {
49272384Smarcel	STAILQ_ENTRY(chunk) ch_list;
50272384Smarcel	size_t	ch_size;		/* Size of chunk in bytes. */
51272384Smarcel	lba_t	ch_block;		/* Block address in image. */
52272384Smarcel	union {
53272384Smarcel		struct {
54272384Smarcel			off_t	ofs;	/* Offset in backing file. */
55272384Smarcel			int	fd;	/* FD of backing file. */
56272384Smarcel		} file;
57272384Smarcel		struct {
58272384Smarcel			void	*ptr;	/* Pointer to data in memory */
59272384Smarcel		} mem;
60272384Smarcel	} ch_u;
61272384Smarcel	u_int	ch_type;
62272384Smarcel#define	CH_TYPE_ZEROES		0	/* Chunk is a gap (no data). */
63272384Smarcel#define	CH_TYPE_FILE		1	/* File-backed chunk. */
64272384Smarcel#define	CH_TYPE_MEMORY		2	/* Memory-backed chunk */
65272384Smarcel};
66265574Smarcel
67272384Smarcelstatic STAILQ_HEAD(chunk_head, chunk) image_chunks;
68272384Smarcelstatic u_int image_nchunks;
69272384Smarcel
70272384Smarcelstatic char image_swap_file[PATH_MAX];
71272384Smarcelstatic int image_swap_fd = -1;
72272384Smarcelstatic u_int image_swap_pgsz;
73272384Smarcelstatic off_t image_swap_size;
74272384Smarcel
75265725Smarcelstatic lba_t image_size;
76265618Smarcel
77272384Smarcelstatic int
78272384Smarcelis_empty_sector(void *buf)
79265618Smarcel{
80272384Smarcel	uint64_t *p = buf;
81272384Smarcel	size_t n, max;
82265618Smarcel
83272384Smarcel	assert(((uintptr_t)p & 3) == 0);
84272384Smarcel
85272384Smarcel	max = secsz / sizeof(uint64_t);
86272384Smarcel	for (n = 0; n < max; n++) {
87272384Smarcel		if (p[n] != 0UL)
88272384Smarcel			return (0);
89272384Smarcel	}
90272384Smarcel	return (1);
91265618Smarcel}
92265618Smarcel
93272384Smarcel/*
94272384Smarcel * Swap file handlng.
95272384Smarcel */
96272384Smarcel
97272384Smarcelstatic off_t
98272384Smarcelimage_swap_alloc(size_t size)
99265574Smarcel{
100272384Smarcel	off_t ofs;
101272384Smarcel	size_t unit;
102272384Smarcel
103272384Smarcel	unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz;
104272384Smarcel	assert((unit & (unit - 1)) == 0);
105272384Smarcel
106272384Smarcel	size = (size + unit - 1) & ~(unit - 1);
107272384Smarcel
108272384Smarcel	ofs = image_swap_size;
109272384Smarcel	image_swap_size += size;
110272384Smarcel	if (ftruncate(image_swap_fd, image_swap_size) == -1) {
111272384Smarcel		image_swap_size = ofs;
112272384Smarcel		ofs = -1LL;
113272384Smarcel	}
114272384Smarcel	return (ofs);
115272384Smarcel}
116272384Smarcel
117272384Smarcel/*
118272384Smarcel * Image chunk handling.
119272384Smarcel */
120272384Smarcel
121272384Smarcelstatic struct chunk *
122272384Smarcelimage_chunk_find(lba_t blk)
123272384Smarcel{
124272384Smarcel	static struct chunk *last = NULL;
125272384Smarcel	struct chunk *ch;
126272384Smarcel
127272384Smarcel	ch = (last != NULL && last->ch_block <= blk)
128272384Smarcel	    ? last : STAILQ_FIRST(&image_chunks);
129272384Smarcel	while (ch != NULL) {
130272384Smarcel		if (ch->ch_block <= blk &&
131272384Smarcel		    (lba_t)(ch->ch_block + (ch->ch_size / secsz)) > blk) {
132272384Smarcel			last = ch;
133272384Smarcel			break;
134272384Smarcel		}
135272384Smarcel		ch = STAILQ_NEXT(ch, ch_list);
136272384Smarcel	}
137272384Smarcel	return (ch);
138272384Smarcel}
139272384Smarcel
140272384Smarcelstatic size_t
141272384Smarcelimage_chunk_grow(struct chunk *ch, size_t sz)
142272384Smarcel{
143272384Smarcel	size_t dsz, newsz;
144272384Smarcel
145272384Smarcel	newsz = ch->ch_size + sz;
146272384Smarcel	if (newsz > ch->ch_size) {
147272384Smarcel		ch->ch_size = newsz;
148272384Smarcel		return (0);
149272384Smarcel	}
150272384Smarcel	/* We would overflow -- create new chunk for remainder. */
151272384Smarcel	dsz = SIZE_MAX - ch->ch_size;
152272384Smarcel	assert(dsz < sz);
153272384Smarcel	ch->ch_size = SIZE_MAX;
154272384Smarcel	return (sz - dsz);
155272384Smarcel}
156272384Smarcel
157272384Smarcelstatic struct chunk *
158272384Smarcelimage_chunk_memory(struct chunk *ch, lba_t blk)
159272384Smarcel{
160272384Smarcel	struct chunk *new;
161272384Smarcel	void *ptr;
162272384Smarcel
163272384Smarcel	ptr = calloc(1, secsz);
164272384Smarcel	if (ptr == NULL)
165272384Smarcel		return (NULL);
166272384Smarcel
167272384Smarcel	if (ch->ch_block < blk) {
168272384Smarcel		new = malloc(sizeof(*new));
169272384Smarcel		if (new == NULL) {
170272384Smarcel			free(ptr);
171272384Smarcel			return (NULL);
172272384Smarcel		}
173272384Smarcel		memcpy(new, ch, sizeof(*new));
174272384Smarcel		ch->ch_size = (blk - ch->ch_block) * secsz;
175272384Smarcel		new->ch_block = blk;
176272384Smarcel		new->ch_size -= ch->ch_size;
177272384Smarcel		STAILQ_INSERT_AFTER(&image_chunks, ch, new, ch_list);
178272384Smarcel		image_nchunks++;
179272384Smarcel		ch = new;
180272384Smarcel	}
181272384Smarcel
182272384Smarcel	if (ch->ch_size > secsz) {
183272384Smarcel		new = malloc(sizeof(*new));
184272384Smarcel		if (new == NULL) {
185272384Smarcel			free(ptr);
186272384Smarcel			return (NULL);
187272384Smarcel		}
188272384Smarcel		memcpy(new, ch, sizeof(*new));
189272384Smarcel		ch->ch_size = secsz;
190272384Smarcel		new->ch_block++;
191272384Smarcel		new->ch_size -= secsz;
192272384Smarcel		STAILQ_INSERT_AFTER(&image_chunks, ch, new, ch_list);
193272384Smarcel		image_nchunks++;
194272384Smarcel	}
195272384Smarcel
196272384Smarcel	ch->ch_type = CH_TYPE_MEMORY;
197272384Smarcel	ch->ch_u.mem.ptr = ptr;
198272384Smarcel	return (ch);
199272384Smarcel}
200272384Smarcel
201272384Smarcelstatic int
202272384Smarcelimage_chunk_skipto(lba_t to)
203272384Smarcel{
204272384Smarcel	struct chunk *ch;
205272384Smarcel	lba_t from;
206272384Smarcel	size_t sz;
207272384Smarcel
208272384Smarcel	ch = STAILQ_LAST(&image_chunks, chunk, ch_list);
209272384Smarcel	from = (ch != NULL) ? ch->ch_block + (ch->ch_size / secsz) : 0LL;
210272384Smarcel
211272384Smarcel	assert(from <= to);
212272384Smarcel
213272384Smarcel	/* Nothing to do? */
214272384Smarcel	if (from == to)
215272384Smarcel		return (0);
216272384Smarcel	/* Avoid bugs due to overflows. */
217272384Smarcel	if ((uintmax_t)(to - from) > (uintmax_t)(SIZE_MAX / secsz))
218272384Smarcel		return (EFBIG);
219272384Smarcel	sz = (to - from) * secsz;
220272384Smarcel	if (ch != NULL && ch->ch_type == CH_TYPE_ZEROES) {
221272384Smarcel		sz = image_chunk_grow(ch, sz);
222272384Smarcel		if (sz == 0)
223272384Smarcel			return (0);
224272384Smarcel		from = ch->ch_block + (ch->ch_size / secsz);
225272384Smarcel	}
226272384Smarcel	ch = malloc(sizeof(*ch));
227272384Smarcel	if (ch == NULL)
228272384Smarcel		return (ENOMEM);
229272384Smarcel	memset(ch, 0, sizeof(*ch));
230272384Smarcel	ch->ch_block = from;
231272384Smarcel	ch->ch_size = sz;
232272384Smarcel	ch->ch_type = CH_TYPE_ZEROES;
233272384Smarcel	STAILQ_INSERT_TAIL(&image_chunks, ch, ch_list);
234272384Smarcel	image_nchunks++;
235272384Smarcel	return (0);
236272384Smarcel}
237272384Smarcel
238272384Smarcelstatic int
239272384Smarcelimage_chunk_append(lba_t blk, size_t sz, off_t ofs, int fd)
240272384Smarcel{
241272384Smarcel	struct chunk *ch;
242272384Smarcel
243272384Smarcel	ch = STAILQ_LAST(&image_chunks, chunk, ch_list);
244272384Smarcel	if (ch != NULL && ch->ch_type == CH_TYPE_FILE) {
245272384Smarcel		if (fd == ch->ch_u.file.fd &&
246272384Smarcel		    blk == (lba_t)(ch->ch_block + (ch->ch_size / secsz)) &&
247272384Smarcel		    ofs == (off_t)(ch->ch_u.file.ofs + ch->ch_size)) {
248272384Smarcel			sz = image_chunk_grow(ch, sz);
249272384Smarcel			if (sz == 0)
250272384Smarcel				return (0);
251272384Smarcel			blk = ch->ch_block + (ch->ch_size / secsz);
252272384Smarcel			ofs = ch->ch_u.file.ofs + ch->ch_size;
253272384Smarcel		}
254272384Smarcel	}
255272384Smarcel	ch = malloc(sizeof(*ch));
256272384Smarcel	if (ch == NULL)
257272384Smarcel		return (ENOMEM);
258272384Smarcel	memset(ch, 0, sizeof(*ch));
259272384Smarcel	ch->ch_block = blk;
260272384Smarcel	ch->ch_size = sz;
261272384Smarcel	ch->ch_type = CH_TYPE_FILE;
262272384Smarcel	ch->ch_u.file.ofs = ofs;
263272384Smarcel	ch->ch_u.file.fd = fd;
264272384Smarcel	STAILQ_INSERT_TAIL(&image_chunks, ch, ch_list);
265272384Smarcel	image_nchunks++;
266272384Smarcel	return (0);
267272384Smarcel}
268272384Smarcel
269272384Smarcelstatic int
270272384Smarcelimage_chunk_copyin(lba_t blk, void *buf, size_t sz, off_t ofs, int fd)
271272384Smarcel{
272272384Smarcel	uint8_t *p = buf;
273272384Smarcel	int error;
274272384Smarcel
275272384Smarcel	error = 0;
276272384Smarcel	sz = (sz + secsz - 1) & ~(secsz - 1);
277272384Smarcel	while (!error && sz > 0) {
278272384Smarcel		if (is_empty_sector(p))
279272384Smarcel			error = image_chunk_skipto(blk + 1);
280272384Smarcel		else
281272384Smarcel			error = image_chunk_append(blk, secsz, ofs, fd);
282272384Smarcel		blk++;
283272384Smarcel		p += secsz;
284272384Smarcel		sz -= secsz;
285272384Smarcel		ofs += secsz;
286272384Smarcel	}
287272384Smarcel	return (error);
288272384Smarcel}
289272384Smarcel
290272384Smarcel/*
291272384Smarcel * File mapping support.
292272384Smarcel */
293272384Smarcel
294272384Smarcelstatic void *
295272384Smarcelimage_file_map(int fd, off_t ofs, size_t sz)
296272384Smarcel{
297272384Smarcel	void *ptr;
298272384Smarcel	size_t unit;
299272384Smarcel	int flags, prot;
300272384Smarcel
301272384Smarcel	unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz;
302272384Smarcel	assert((unit & (unit - 1)) == 0);
303272384Smarcel
304272384Smarcel	flags = MAP_NOCORE | MAP_NOSYNC | MAP_SHARED;
305272384Smarcel	/* Allow writing to our swap file only. */
306272384Smarcel	prot = PROT_READ | ((fd == image_swap_fd) ? PROT_WRITE : 0);
307272384Smarcel	sz = (sz + unit - 1) & ~(unit - 1);
308272384Smarcel	ptr = mmap(NULL, sz, prot, flags, fd, ofs);
309272384Smarcel	return ((ptr == MAP_FAILED) ? NULL : ptr);
310272384Smarcel}
311272384Smarcel
312272384Smarcelstatic int
313272384Smarcelimage_file_unmap(void *buffer, size_t sz)
314272384Smarcel{
315272384Smarcel	size_t unit;
316272384Smarcel
317272384Smarcel	unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz;
318272384Smarcel	sz = (sz + unit - 1) & ~(unit - 1);
319301090Smarkj	if (madvise(buffer, sz, MADV_DONTNEED) != 0)
320301090Smarkj		warn("madvise");
321272384Smarcel	munmap(buffer, sz);
322272384Smarcel	return (0);
323272384Smarcel}
324272384Smarcel
325272384Smarcel/*
326272384Smarcel * Input/source file handling.
327272384Smarcel */
328272384Smarcel
329272384Smarcelstatic int
330272384Smarcelimage_copyin_stream(lba_t blk, int fd, uint64_t *sizep)
331272384Smarcel{
332265574Smarcel	char *buffer;
333265574Smarcel	uint64_t bytesize;
334272384Smarcel	off_t swofs;
335272384Smarcel	size_t iosz;
336272384Smarcel	ssize_t rdsz;
337272384Smarcel	int error;
338265574Smarcel
339272384Smarcel	/*
340272384Smarcel	 * This makes sure we're doing I/O in multiples of the page
341272384Smarcel	 * size as well as of the sector size. 2MB is the minimum
342272384Smarcel	 * by virtue of secsz at least 512 bytes and the page size
343272384Smarcel	 * at least 4K bytes.
344272384Smarcel	 */
345272384Smarcel	iosz = secsz * image_swap_pgsz;
346265574Smarcel
347265574Smarcel	bytesize = 0;
348272384Smarcel	do {
349272384Smarcel		swofs = image_swap_alloc(iosz);
350272384Smarcel		if (swofs == -1LL)
351272384Smarcel			return (errno);
352272384Smarcel		buffer = image_file_map(image_swap_fd, swofs, iosz);
353272384Smarcel		if (buffer == NULL)
354272384Smarcel			return (errno);
355272384Smarcel		rdsz = read(fd, buffer, iosz);
356272384Smarcel		if (rdsz > 0)
357272384Smarcel			error = image_chunk_copyin(blk, buffer, rdsz, swofs,
358272384Smarcel			    image_swap_fd);
359272384Smarcel		else if (rdsz < 0)
360272384Smarcel			error = errno;
361272384Smarcel		else
362272384Smarcel			error = 0;
363272384Smarcel		image_file_unmap(buffer, iosz);
364272384Smarcel		/* XXX should we relinguish unused swap space? */
365272384Smarcel		if (error)
366272384Smarcel			return (error);
367272384Smarcel
368272384Smarcel		bytesize += rdsz;
369272384Smarcel		blk += (rdsz + secsz - 1) / secsz;
370272384Smarcel	} while (rdsz > 0);
371272384Smarcel
372272384Smarcel	if (sizep != NULL)
373272384Smarcel		*sizep = bytesize;
374272384Smarcel	return (0);
375272384Smarcel}
376272384Smarcel
377272384Smarcelstatic int
378272384Smarcelimage_copyin_mapped(lba_t blk, int fd, uint64_t *sizep)
379272384Smarcel{
380272384Smarcel	off_t cur, data, end, hole, pos;
381272384Smarcel	void *buf;
382272384Smarcel	uint64_t bytesize;
383272384Smarcel	size_t iosz, sz;
384272384Smarcel	int error;
385272384Smarcel
386272384Smarcel	/*
387272384Smarcel	 * We'd like to know the size of the file and we must
388272384Smarcel	 * be able to seek in order to mmap(2). If this isn't
389272384Smarcel	 * possible, then treat the file as a stream/pipe.
390272384Smarcel	 */
391272384Smarcel	end = lseek(fd, 0L, SEEK_END);
392272384Smarcel	if (end == -1L)
393272384Smarcel		return (image_copyin_stream(blk, fd, sizep));
394272384Smarcel
395272384Smarcel	/*
396272384Smarcel	 * We need the file opened for the duration and our
397272384Smarcel	 * caller is going to close the file. Make a dup(2)
398272384Smarcel	 * so that control the faith of the descriptor.
399272384Smarcel	 */
400272384Smarcel	fd = dup(fd);
401272384Smarcel	if (fd == -1)
402272384Smarcel		return (errno);
403272384Smarcel
404272384Smarcel	iosz = secsz * image_swap_pgsz;
405272384Smarcel
406272384Smarcel	bytesize = 0;
407272384Smarcel	cur = pos = 0;
408272384Smarcel	error = 0;
409272384Smarcel	while (!error && cur < end) {
410272384Smarcel		hole = lseek(fd, cur, SEEK_HOLE);
411274410Smarcel		if (hole == -1)
412274410Smarcel			hole = end;
413272384Smarcel		data = lseek(fd, cur, SEEK_DATA);
414274410Smarcel		if (data == -1)
415274410Smarcel			data = end;
416272384Smarcel
417272384Smarcel		/*
418272384Smarcel		 * Treat the entire file as data if sparse files
419272384Smarcel		 * are not supported by the underlying file system.
420272384Smarcel		 */
421274410Smarcel		if (hole == end && data == end)
422272384Smarcel			data = cur;
423272384Smarcel
424272384Smarcel		if (cur == hole && data > hole) {
425272384Smarcel			hole = pos;
426272384Smarcel			pos = data & ~((uint64_t)secsz - 1);
427272384Smarcel
428272384Smarcel			blk += (pos - hole) / secsz;
429272384Smarcel			error = image_chunk_skipto(blk);
430272384Smarcel
431272384Smarcel			bytesize += pos - hole;
432272384Smarcel			cur = data;
433272384Smarcel		} else if (cur == data && hole > data) {
434272384Smarcel			data = pos;
435272384Smarcel			pos = (hole + secsz - 1) & ~((uint64_t)secsz - 1);
436272384Smarcel
437272384Smarcel			while (data < pos) {
438272384Smarcel				sz = (pos - data > (off_t)iosz)
439272384Smarcel				    ? iosz : (size_t)(pos - data);
440272384Smarcel
441272384Smarcel				buf = image_file_map(fd, data, sz);
442272384Smarcel				if (buf != NULL) {
443272384Smarcel					error = image_chunk_copyin(blk, buf,
444272384Smarcel					    sz, data, fd);
445272384Smarcel					image_file_unmap(buf, sz);
446272384Smarcel				} else
447272384Smarcel					error = errno;
448272384Smarcel
449272384Smarcel				blk += sz / secsz;
450272384Smarcel				bytesize += sz;
451272384Smarcel				data += sz;
452272384Smarcel			}
453272384Smarcel			cur = hole;
454272384Smarcel		} else {
455272384Smarcel			/*
456272384Smarcel			 * I don't know what this means or whether it
457272384Smarcel			 * can happen at all...
458272384Smarcel			 */
459272384Smarcel			error = EDOOFUS;
460265574Smarcel			break;
461265574Smarcel		}
462265574Smarcel	}
463272384Smarcel	if (error)
464272384Smarcel		close(fd);
465272384Smarcel	if (!error && sizep != NULL)
466265574Smarcel		*sizep = bytesize;
467265574Smarcel	return (error);
468265574Smarcel}
469265574Smarcel
470265574Smarcelint
471272384Smarcelimage_copyin(lba_t blk, int fd, uint64_t *sizep)
472272384Smarcel{
473272384Smarcel	struct stat sb;
474272384Smarcel	int error;
475272384Smarcel
476272384Smarcel	error = image_chunk_skipto(blk);
477272384Smarcel	if (!error) {
478272384Smarcel		if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode))
479272384Smarcel			error = image_copyin_stream(blk, fd, sizep);
480272384Smarcel		else
481272384Smarcel			error = image_copyin_mapped(blk, fd, sizep);
482272384Smarcel	}
483272384Smarcel	return (error);
484272384Smarcel}
485272384Smarcel
486272384Smarcel/*
487272384Smarcel * Output/sink file handling.
488272384Smarcel */
489272384Smarcel
490272384Smarcelint
491265618Smarcelimage_copyout(int fd)
492265574Smarcel{
493268236Smarcel	int error;
494268236Smarcel
495268236Smarcel	error = image_copyout_region(fd, 0, image_size);
496268646Smarcel	if (!error)
497268646Smarcel		error = image_copyout_done(fd);
498268646Smarcel	return (error);
499268646Smarcel}
500268236Smarcel
501268646Smarcelint
502268646Smarcelimage_copyout_done(int fd)
503268646Smarcel{
504268646Smarcel	off_t ofs;
505268646Smarcel	int error;
506268646Smarcel
507268236Smarcel	ofs = lseek(fd, 0L, SEEK_CUR);
508268236Smarcel	if (ofs == -1)
509268236Smarcel		return (0);
510268236Smarcel	error = (ftruncate(fd, ofs) == -1) ? errno : 0;
511268236Smarcel	return (error);
512268236Smarcel}
513268236Smarcel
514272384Smarcelstatic int
515272384Smarcelimage_copyout_memory(int fd, size_t size, void *ptr)
516272384Smarcel{
517272384Smarcel
518272384Smarcel	if (write(fd, ptr, size) == -1)
519272384Smarcel		return (errno);
520272384Smarcel	return (0);
521272384Smarcel}
522272384Smarcel
523286215Smarcelint
524286215Smarcelimage_copyout_zeroes(int fd, size_t count)
525272384Smarcel{
526272384Smarcel	static uint8_t *zeroes = NULL;
527272384Smarcel	size_t sz;
528272384Smarcel	int error;
529272384Smarcel
530286215Smarcel	if (lseek(fd, (off_t)count, SEEK_CUR) != -1)
531272384Smarcel		return (0);
532272384Smarcel
533272384Smarcel	/*
534272384Smarcel	 * If we can't seek, we must write.
535272384Smarcel	 */
536272384Smarcel
537272384Smarcel	if (zeroes == NULL) {
538272384Smarcel		zeroes = calloc(1, secsz);
539272384Smarcel		if (zeroes == NULL)
540272384Smarcel			return (ENOMEM);
541272384Smarcel	}
542272384Smarcel
543286215Smarcel	while (count > 0) {
544286215Smarcel		sz = (count > secsz) ? secsz : count;
545272384Smarcel		error = image_copyout_memory(fd, sz, zeroes);
546272384Smarcel		if (error)
547272384Smarcel			return (error);
548286215Smarcel		count -= sz;
549272384Smarcel	}
550272384Smarcel	return (0);
551272384Smarcel}
552272384Smarcel
553272384Smarcelstatic int
554272384Smarcelimage_copyout_file(int fd, size_t size, int ifd, off_t iofs)
555272384Smarcel{
556272384Smarcel	void *buf;
557272384Smarcel	size_t iosz, sz;
558272384Smarcel	int error;
559272384Smarcel
560272384Smarcel	iosz = secsz * image_swap_pgsz;
561272384Smarcel
562272384Smarcel	while (size > 0) {
563272384Smarcel		sz = (size > iosz) ? iosz : size;
564272384Smarcel		buf = image_file_map(ifd, iofs, sz);
565272384Smarcel		if (buf == NULL)
566272384Smarcel			return (errno);
567272384Smarcel		error = image_copyout_memory(fd, sz, buf);
568272384Smarcel		image_file_unmap(buf, sz);
569272384Smarcel		if (error)
570272384Smarcel			return (error);
571272384Smarcel		size -= sz;
572272384Smarcel		iofs += sz;
573272384Smarcel	}
574272384Smarcel	return (0);
575272384Smarcel}
576272384Smarcel
577268236Smarcelint
578268236Smarcelimage_copyout_region(int fd, lba_t blk, lba_t size)
579268236Smarcel{
580272384Smarcel	struct chunk *ch;
581272384Smarcel	size_t ofs, sz;
582265618Smarcel	int error;
583265574Smarcel
584272384Smarcel	size *= secsz;
585265618Smarcel
586268236Smarcel	while (size > 0) {
587272384Smarcel		ch = image_chunk_find(blk);
588272384Smarcel		if (ch == NULL)
589272384Smarcel			return (EINVAL);
590272384Smarcel		ofs = (blk - ch->ch_block) * secsz;
591272384Smarcel		sz = ch->ch_size - ofs;
592272384Smarcel		sz = ((lba_t)sz < size) ? sz : (size_t)size;
593272384Smarcel		switch (ch->ch_type) {
594272384Smarcel		case CH_TYPE_ZEROES:
595272384Smarcel			error = image_copyout_zeroes(fd, sz);
596265618Smarcel			break;
597272384Smarcel		case CH_TYPE_FILE:
598272384Smarcel			error = image_copyout_file(fd, sz, ch->ch_u.file.fd,
599272384Smarcel			    ch->ch_u.file.ofs + ofs);
600265618Smarcel			break;
601272384Smarcel		case CH_TYPE_MEMORY:
602272384Smarcel			error = image_copyout_memory(fd, sz, ch->ch_u.mem.ptr);
603272384Smarcel			break;
604272384Smarcel		default:
605272384Smarcel			return (EDOOFUS);
606265618Smarcel		}
607272384Smarcel		size -= sz;
608272384Smarcel		blk += sz / secsz;
609265618Smarcel	}
610272384Smarcel	return (0);
611265618Smarcel}
612265618Smarcel
613268646Smarcelint
614268646Smarcelimage_data(lba_t blk, lba_t size)
615268646Smarcel{
616272384Smarcel	struct chunk *ch;
617272384Smarcel	lba_t lim;
618268646Smarcel
619272384Smarcel	while (1) {
620272384Smarcel		ch = image_chunk_find(blk);
621272384Smarcel		if (ch == NULL)
622272384Smarcel			return (0);
623272384Smarcel		if (ch->ch_type != CH_TYPE_ZEROES)
624272384Smarcel			return (1);
625272384Smarcel		lim = ch->ch_block + (ch->ch_size / secsz);
626272384Smarcel		if (lim >= blk + size)
627272384Smarcel			return (0);
628272384Smarcel		size -= lim - blk;
629272384Smarcel		blk = lim;
630268646Smarcel	}
631272384Smarcel	/*NOTREACHED*/
632268646Smarcel}
633268646Smarcel
634265725Smarcellba_t
635265725Smarcelimage_get_size(void)
636265725Smarcel{
637265725Smarcel
638265725Smarcel	return (image_size);
639265725Smarcel}
640265725Smarcel
641265618Smarcelint
642265618Smarcelimage_set_size(lba_t blk)
643265618Smarcel{
644272384Smarcel	int error;
645265618Smarcel
646272384Smarcel	error = image_chunk_skipto(blk);
647272384Smarcel	if (!error)
648272384Smarcel		image_size = blk;
649272384Smarcel	return (error);
650265574Smarcel}
651265574Smarcel
652265574Smarcelint
653265618Smarcelimage_write(lba_t blk, void *buf, ssize_t len)
654265574Smarcel{
655272384Smarcel	struct chunk *ch;
656265574Smarcel
657272384Smarcel	while (len > 0) {
658272384Smarcel		if (!is_empty_sector(buf)) {
659272384Smarcel			ch = image_chunk_find(blk);
660272384Smarcel			if (ch == NULL)
661272384Smarcel				return (ENXIO);
662272384Smarcel			/* We may not be able to write to files. */
663272384Smarcel			if (ch->ch_type == CH_TYPE_FILE)
664272384Smarcel				return (EINVAL);
665272384Smarcel			if (ch->ch_type == CH_TYPE_ZEROES) {
666272384Smarcel				ch = image_chunk_memory(ch, blk);
667272384Smarcel				if (ch == NULL)
668272384Smarcel					return (ENOMEM);
669272384Smarcel			}
670272384Smarcel			assert(ch->ch_type == CH_TYPE_MEMORY);
671272384Smarcel			memcpy(ch->ch_u.mem.ptr, buf, secsz);
672272384Smarcel		}
673272384Smarcel		blk++;
674272384Smarcel		buf = (char *)buf + secsz;
675272384Smarcel		len--;
676272384Smarcel	}
677265574Smarcel	return (0);
678265574Smarcel}
679265618Smarcel
680272384Smarcelstatic void
681272384Smarcelimage_cleanup(void)
682272384Smarcel{
683272384Smarcel	struct chunk *ch;
684272384Smarcel
685272384Smarcel	while ((ch = STAILQ_FIRST(&image_chunks)) != NULL) {
686272384Smarcel		switch (ch->ch_type) {
687272384Smarcel		case CH_TYPE_FILE:
688272384Smarcel			/* We may be closing the same file multiple times. */
689272384Smarcel			if (ch->ch_u.file.fd != -1)
690272384Smarcel				close(ch->ch_u.file.fd);
691272384Smarcel			break;
692272384Smarcel		case CH_TYPE_MEMORY:
693272384Smarcel			free(ch->ch_u.mem.ptr);
694272384Smarcel			break;
695272384Smarcel		default:
696272384Smarcel			break;
697272384Smarcel		}
698272384Smarcel		STAILQ_REMOVE_HEAD(&image_chunks, ch_list);
699272384Smarcel		free(ch);
700272384Smarcel	}
701272384Smarcel	if (image_swap_fd != -1)
702272384Smarcel		close(image_swap_fd);
703272384Smarcel	unlink(image_swap_file);
704272384Smarcel}
705272384Smarcel
706265618Smarcelint
707265618Smarcelimage_init(void)
708265618Smarcel{
709266556Smarcel	const char *tmpdir;
710265618Smarcel
711272384Smarcel	STAILQ_INIT(&image_chunks);
712272384Smarcel	image_nchunks = 0;
713272384Smarcel
714272384Smarcel	image_swap_size = 0;
715272384Smarcel	image_swap_pgsz = getpagesize();
716272384Smarcel
717272384Smarcel	if (atexit(image_cleanup) == -1)
718265618Smarcel		return (errno);
719266556Smarcel	if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
720266556Smarcel		tmpdir = _PATH_TMP;
721272384Smarcel	snprintf(image_swap_file, sizeof(image_swap_file), "%s/mkimg-XXXXXX",
722266556Smarcel	    tmpdir);
723272384Smarcel	image_swap_fd = mkstemp(image_swap_file);
724272384Smarcel	if (image_swap_fd == -1)
725265618Smarcel		return (errno);
726265618Smarcel	return (0);
727265618Smarcel}
728