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: stable/11/usr.bin/mkimg/image.c 329059 2018-02-09 09:15:43Z manu $");
29265574Smarcel
30272384Smarcel#include <sys/mman.h>
31272384Smarcel#include <sys/stat.h>
32265574Smarcel#include <assert.h>
33301090Smarkj#include <err.h>
34265574Smarcel#include <errno.h>
35266556Smarcel#include <limits.h>
36266556Smarcel#include <paths.h>
37272384Smarcel#include <stdint.h>
38266556Smarcel#include <stdio.h>
39265574Smarcel#include <stdlib.h>
40272384Smarcel#include <string.h>
41265574Smarcel#include <unistd.h>
42265574Smarcel
43265579Smarcel#include "image.h"
44265574Smarcel#include "mkimg.h"
45265574Smarcel
46329059Smanu#ifndef MAP_NOCORE
47329059Smanu#define	MAP_NOCORE	0
48329059Smanu#endif
49329059Smanu#ifndef MAP_NOSYNC
50329059Smanu#define	MAP_NOSYNC	0
51329059Smanu#endif
52329059Smanu
53329059Smanu#ifndef SEEK_DATA
54329059Smanu#define	SEEK_DATA	-1
55329059Smanu#endif
56329059Smanu#ifndef SEEK_HOLE
57329059Smanu#define	SEEK_HOLE	-1
58329059Smanu#endif
59329059Smanu
60272384Smarcelstruct chunk {
61329059Smanu	TAILQ_ENTRY(chunk) ch_list;
62272384Smarcel	size_t	ch_size;		/* Size of chunk in bytes. */
63272384Smarcel	lba_t	ch_block;		/* Block address in image. */
64272384Smarcel	union {
65272384Smarcel		struct {
66272384Smarcel			off_t	ofs;	/* Offset in backing file. */
67272384Smarcel			int	fd;	/* FD of backing file. */
68272384Smarcel		} file;
69272384Smarcel		struct {
70272384Smarcel			void	*ptr;	/* Pointer to data in memory */
71272384Smarcel		} mem;
72272384Smarcel	} ch_u;
73272384Smarcel	u_int	ch_type;
74272384Smarcel#define	CH_TYPE_ZEROES		0	/* Chunk is a gap (no data). */
75272384Smarcel#define	CH_TYPE_FILE		1	/* File-backed chunk. */
76272384Smarcel#define	CH_TYPE_MEMORY		2	/* Memory-backed chunk */
77272384Smarcel};
78265574Smarcel
79329059Smanustatic TAILQ_HEAD(chunk_head, chunk) image_chunks;
80272384Smarcelstatic u_int image_nchunks;
81272384Smarcel
82272384Smarcelstatic char image_swap_file[PATH_MAX];
83272384Smarcelstatic int image_swap_fd = -1;
84272384Smarcelstatic u_int image_swap_pgsz;
85272384Smarcelstatic off_t image_swap_size;
86272384Smarcel
87265725Smarcelstatic lba_t image_size;
88265618Smarcel
89272384Smarcelstatic int
90272384Smarcelis_empty_sector(void *buf)
91265618Smarcel{
92272384Smarcel	uint64_t *p = buf;
93272384Smarcel	size_t n, max;
94265618Smarcel
95272384Smarcel	assert(((uintptr_t)p & 3) == 0);
96272384Smarcel
97272384Smarcel	max = secsz / sizeof(uint64_t);
98272384Smarcel	for (n = 0; n < max; n++) {
99272384Smarcel		if (p[n] != 0UL)
100272384Smarcel			return (0);
101272384Smarcel	}
102272384Smarcel	return (1);
103265618Smarcel}
104265618Smarcel
105272384Smarcel/*
106272384Smarcel * Swap file handlng.
107272384Smarcel */
108272384Smarcel
109272384Smarcelstatic off_t
110272384Smarcelimage_swap_alloc(size_t size)
111265574Smarcel{
112272384Smarcel	off_t ofs;
113272384Smarcel	size_t unit;
114272384Smarcel
115272384Smarcel	unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz;
116272384Smarcel	assert((unit & (unit - 1)) == 0);
117272384Smarcel
118272384Smarcel	size = (size + unit - 1) & ~(unit - 1);
119272384Smarcel
120272384Smarcel	ofs = image_swap_size;
121272384Smarcel	image_swap_size += size;
122272384Smarcel	if (ftruncate(image_swap_fd, image_swap_size) == -1) {
123272384Smarcel		image_swap_size = ofs;
124272384Smarcel		ofs = -1LL;
125272384Smarcel	}
126272384Smarcel	return (ofs);
127272384Smarcel}
128272384Smarcel
129272384Smarcel/*
130272384Smarcel * Image chunk handling.
131272384Smarcel */
132272384Smarcel
133272384Smarcelstatic struct chunk *
134272384Smarcelimage_chunk_find(lba_t blk)
135272384Smarcel{
136272384Smarcel	static struct chunk *last = NULL;
137272384Smarcel	struct chunk *ch;
138272384Smarcel
139272384Smarcel	ch = (last != NULL && last->ch_block <= blk)
140329059Smanu	    ? last : TAILQ_FIRST(&image_chunks);
141272384Smarcel	while (ch != NULL) {
142272384Smarcel		if (ch->ch_block <= blk &&
143272384Smarcel		    (lba_t)(ch->ch_block + (ch->ch_size / secsz)) > blk) {
144272384Smarcel			last = ch;
145272384Smarcel			break;
146272384Smarcel		}
147329059Smanu		ch = TAILQ_NEXT(ch, ch_list);
148272384Smarcel	}
149272384Smarcel	return (ch);
150272384Smarcel}
151272384Smarcel
152272384Smarcelstatic size_t
153272384Smarcelimage_chunk_grow(struct chunk *ch, size_t sz)
154272384Smarcel{
155272384Smarcel	size_t dsz, newsz;
156272384Smarcel
157272384Smarcel	newsz = ch->ch_size + sz;
158272384Smarcel	if (newsz > ch->ch_size) {
159272384Smarcel		ch->ch_size = newsz;
160272384Smarcel		return (0);
161272384Smarcel	}
162272384Smarcel	/* We would overflow -- create new chunk for remainder. */
163272384Smarcel	dsz = SIZE_MAX - ch->ch_size;
164272384Smarcel	assert(dsz < sz);
165272384Smarcel	ch->ch_size = SIZE_MAX;
166272384Smarcel	return (sz - dsz);
167272384Smarcel}
168272384Smarcel
169272384Smarcelstatic struct chunk *
170272384Smarcelimage_chunk_memory(struct chunk *ch, lba_t blk)
171272384Smarcel{
172272384Smarcel	struct chunk *new;
173272384Smarcel	void *ptr;
174272384Smarcel
175272384Smarcel	ptr = calloc(1, secsz);
176272384Smarcel	if (ptr == NULL)
177272384Smarcel		return (NULL);
178272384Smarcel
179272384Smarcel	if (ch->ch_block < blk) {
180272384Smarcel		new = malloc(sizeof(*new));
181272384Smarcel		if (new == NULL) {
182272384Smarcel			free(ptr);
183272384Smarcel			return (NULL);
184272384Smarcel		}
185272384Smarcel		memcpy(new, ch, sizeof(*new));
186272384Smarcel		ch->ch_size = (blk - ch->ch_block) * secsz;
187272384Smarcel		new->ch_block = blk;
188272384Smarcel		new->ch_size -= ch->ch_size;
189329059Smanu		TAILQ_INSERT_AFTER(&image_chunks, ch, new, ch_list);
190272384Smarcel		image_nchunks++;
191272384Smarcel		ch = new;
192272384Smarcel	}
193272384Smarcel
194272384Smarcel	if (ch->ch_size > secsz) {
195272384Smarcel		new = malloc(sizeof(*new));
196272384Smarcel		if (new == NULL) {
197272384Smarcel			free(ptr);
198272384Smarcel			return (NULL);
199272384Smarcel		}
200272384Smarcel		memcpy(new, ch, sizeof(*new));
201272384Smarcel		ch->ch_size = secsz;
202272384Smarcel		new->ch_block++;
203272384Smarcel		new->ch_size -= secsz;
204329059Smanu		TAILQ_INSERT_AFTER(&image_chunks, ch, new, ch_list);
205272384Smarcel		image_nchunks++;
206272384Smarcel	}
207272384Smarcel
208272384Smarcel	ch->ch_type = CH_TYPE_MEMORY;
209272384Smarcel	ch->ch_u.mem.ptr = ptr;
210272384Smarcel	return (ch);
211272384Smarcel}
212272384Smarcel
213272384Smarcelstatic int
214272384Smarcelimage_chunk_skipto(lba_t to)
215272384Smarcel{
216272384Smarcel	struct chunk *ch;
217272384Smarcel	lba_t from;
218272384Smarcel	size_t sz;
219272384Smarcel
220329059Smanu	ch = TAILQ_LAST(&image_chunks, chunk_head);
221272384Smarcel	from = (ch != NULL) ? ch->ch_block + (ch->ch_size / secsz) : 0LL;
222272384Smarcel
223272384Smarcel	assert(from <= to);
224272384Smarcel
225272384Smarcel	/* Nothing to do? */
226272384Smarcel	if (from == to)
227272384Smarcel		return (0);
228272384Smarcel	/* Avoid bugs due to overflows. */
229272384Smarcel	if ((uintmax_t)(to - from) > (uintmax_t)(SIZE_MAX / secsz))
230272384Smarcel		return (EFBIG);
231272384Smarcel	sz = (to - from) * secsz;
232272384Smarcel	if (ch != NULL && ch->ch_type == CH_TYPE_ZEROES) {
233272384Smarcel		sz = image_chunk_grow(ch, sz);
234272384Smarcel		if (sz == 0)
235272384Smarcel			return (0);
236272384Smarcel		from = ch->ch_block + (ch->ch_size / secsz);
237272384Smarcel	}
238272384Smarcel	ch = malloc(sizeof(*ch));
239272384Smarcel	if (ch == NULL)
240272384Smarcel		return (ENOMEM);
241272384Smarcel	memset(ch, 0, sizeof(*ch));
242272384Smarcel	ch->ch_block = from;
243272384Smarcel	ch->ch_size = sz;
244272384Smarcel	ch->ch_type = CH_TYPE_ZEROES;
245329059Smanu	TAILQ_INSERT_TAIL(&image_chunks, ch, ch_list);
246272384Smarcel	image_nchunks++;
247272384Smarcel	return (0);
248272384Smarcel}
249272384Smarcel
250272384Smarcelstatic int
251272384Smarcelimage_chunk_append(lba_t blk, size_t sz, off_t ofs, int fd)
252272384Smarcel{
253272384Smarcel	struct chunk *ch;
254272384Smarcel
255329059Smanu	ch = TAILQ_LAST(&image_chunks, chunk_head);
256272384Smarcel	if (ch != NULL && ch->ch_type == CH_TYPE_FILE) {
257272384Smarcel		if (fd == ch->ch_u.file.fd &&
258272384Smarcel		    blk == (lba_t)(ch->ch_block + (ch->ch_size / secsz)) &&
259272384Smarcel		    ofs == (off_t)(ch->ch_u.file.ofs + ch->ch_size)) {
260272384Smarcel			sz = image_chunk_grow(ch, sz);
261272384Smarcel			if (sz == 0)
262272384Smarcel				return (0);
263272384Smarcel			blk = ch->ch_block + (ch->ch_size / secsz);
264272384Smarcel			ofs = ch->ch_u.file.ofs + ch->ch_size;
265272384Smarcel		}
266272384Smarcel	}
267272384Smarcel	ch = malloc(sizeof(*ch));
268272384Smarcel	if (ch == NULL)
269272384Smarcel		return (ENOMEM);
270272384Smarcel	memset(ch, 0, sizeof(*ch));
271272384Smarcel	ch->ch_block = blk;
272272384Smarcel	ch->ch_size = sz;
273272384Smarcel	ch->ch_type = CH_TYPE_FILE;
274272384Smarcel	ch->ch_u.file.ofs = ofs;
275272384Smarcel	ch->ch_u.file.fd = fd;
276329059Smanu	TAILQ_INSERT_TAIL(&image_chunks, ch, ch_list);
277272384Smarcel	image_nchunks++;
278272384Smarcel	return (0);
279272384Smarcel}
280272384Smarcel
281272384Smarcelstatic int
282272384Smarcelimage_chunk_copyin(lba_t blk, void *buf, size_t sz, off_t ofs, int fd)
283272384Smarcel{
284272384Smarcel	uint8_t *p = buf;
285272384Smarcel	int error;
286272384Smarcel
287272384Smarcel	error = 0;
288272384Smarcel	sz = (sz + secsz - 1) & ~(secsz - 1);
289272384Smarcel	while (!error && sz > 0) {
290272384Smarcel		if (is_empty_sector(p))
291272384Smarcel			error = image_chunk_skipto(blk + 1);
292272384Smarcel		else
293272384Smarcel			error = image_chunk_append(blk, secsz, ofs, fd);
294272384Smarcel		blk++;
295272384Smarcel		p += secsz;
296272384Smarcel		sz -= secsz;
297272384Smarcel		ofs += secsz;
298272384Smarcel	}
299272384Smarcel	return (error);
300272384Smarcel}
301272384Smarcel
302272384Smarcel/*
303272384Smarcel * File mapping support.
304272384Smarcel */
305272384Smarcel
306272384Smarcelstatic void *
307272384Smarcelimage_file_map(int fd, off_t ofs, size_t sz)
308272384Smarcel{
309272384Smarcel	void *ptr;
310272384Smarcel	size_t unit;
311272384Smarcel	int flags, prot;
312272384Smarcel
313272384Smarcel	unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz;
314272384Smarcel	assert((unit & (unit - 1)) == 0);
315272384Smarcel
316272384Smarcel	flags = MAP_NOCORE | MAP_NOSYNC | MAP_SHARED;
317272384Smarcel	/* Allow writing to our swap file only. */
318272384Smarcel	prot = PROT_READ | ((fd == image_swap_fd) ? PROT_WRITE : 0);
319272384Smarcel	sz = (sz + unit - 1) & ~(unit - 1);
320272384Smarcel	ptr = mmap(NULL, sz, prot, flags, fd, ofs);
321272384Smarcel	return ((ptr == MAP_FAILED) ? NULL : ptr);
322272384Smarcel}
323272384Smarcel
324272384Smarcelstatic int
325272384Smarcelimage_file_unmap(void *buffer, size_t sz)
326272384Smarcel{
327272384Smarcel	size_t unit;
328272384Smarcel
329272384Smarcel	unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz;
330272384Smarcel	sz = (sz + unit - 1) & ~(unit - 1);
331301090Smarkj	if (madvise(buffer, sz, MADV_DONTNEED) != 0)
332301090Smarkj		warn("madvise");
333272384Smarcel	munmap(buffer, sz);
334272384Smarcel	return (0);
335272384Smarcel}
336272384Smarcel
337272384Smarcel/*
338272384Smarcel * Input/source file handling.
339272384Smarcel */
340272384Smarcel
341272384Smarcelstatic int
342272384Smarcelimage_copyin_stream(lba_t blk, int fd, uint64_t *sizep)
343272384Smarcel{
344265574Smarcel	char *buffer;
345265574Smarcel	uint64_t bytesize;
346272384Smarcel	off_t swofs;
347272384Smarcel	size_t iosz;
348272384Smarcel	ssize_t rdsz;
349272384Smarcel	int error;
350265574Smarcel
351272384Smarcel	/*
352272384Smarcel	 * This makes sure we're doing I/O in multiples of the page
353272384Smarcel	 * size as well as of the sector size. 2MB is the minimum
354272384Smarcel	 * by virtue of secsz at least 512 bytes and the page size
355272384Smarcel	 * at least 4K bytes.
356272384Smarcel	 */
357272384Smarcel	iosz = secsz * image_swap_pgsz;
358265574Smarcel
359265574Smarcel	bytesize = 0;
360272384Smarcel	do {
361272384Smarcel		swofs = image_swap_alloc(iosz);
362272384Smarcel		if (swofs == -1LL)
363272384Smarcel			return (errno);
364272384Smarcel		buffer = image_file_map(image_swap_fd, swofs, iosz);
365272384Smarcel		if (buffer == NULL)
366272384Smarcel			return (errno);
367272384Smarcel		rdsz = read(fd, buffer, iosz);
368272384Smarcel		if (rdsz > 0)
369272384Smarcel			error = image_chunk_copyin(blk, buffer, rdsz, swofs,
370272384Smarcel			    image_swap_fd);
371272384Smarcel		else if (rdsz < 0)
372272384Smarcel			error = errno;
373272384Smarcel		else
374272384Smarcel			error = 0;
375272384Smarcel		image_file_unmap(buffer, iosz);
376272384Smarcel		/* XXX should we relinguish unused swap space? */
377272384Smarcel		if (error)
378272384Smarcel			return (error);
379272384Smarcel
380272384Smarcel		bytesize += rdsz;
381272384Smarcel		blk += (rdsz + secsz - 1) / secsz;
382272384Smarcel	} while (rdsz > 0);
383272384Smarcel
384272384Smarcel	if (sizep != NULL)
385272384Smarcel		*sizep = bytesize;
386272384Smarcel	return (0);
387272384Smarcel}
388272384Smarcel
389272384Smarcelstatic int
390272384Smarcelimage_copyin_mapped(lba_t blk, int fd, uint64_t *sizep)
391272384Smarcel{
392272384Smarcel	off_t cur, data, end, hole, pos;
393272384Smarcel	void *buf;
394272384Smarcel	uint64_t bytesize;
395272384Smarcel	size_t iosz, sz;
396272384Smarcel	int error;
397272384Smarcel
398272384Smarcel	/*
399272384Smarcel	 * We'd like to know the size of the file and we must
400272384Smarcel	 * be able to seek in order to mmap(2). If this isn't
401272384Smarcel	 * possible, then treat the file as a stream/pipe.
402272384Smarcel	 */
403272384Smarcel	end = lseek(fd, 0L, SEEK_END);
404272384Smarcel	if (end == -1L)
405272384Smarcel		return (image_copyin_stream(blk, fd, sizep));
406272384Smarcel
407272384Smarcel	/*
408272384Smarcel	 * We need the file opened for the duration and our
409272384Smarcel	 * caller is going to close the file. Make a dup(2)
410272384Smarcel	 * so that control the faith of the descriptor.
411272384Smarcel	 */
412272384Smarcel	fd = dup(fd);
413272384Smarcel	if (fd == -1)
414272384Smarcel		return (errno);
415272384Smarcel
416272384Smarcel	iosz = secsz * image_swap_pgsz;
417272384Smarcel
418272384Smarcel	bytesize = 0;
419272384Smarcel	cur = pos = 0;
420272384Smarcel	error = 0;
421272384Smarcel	while (!error && cur < end) {
422272384Smarcel		hole = lseek(fd, cur, SEEK_HOLE);
423274410Smarcel		if (hole == -1)
424274410Smarcel			hole = end;
425272384Smarcel		data = lseek(fd, cur, SEEK_DATA);
426274410Smarcel		if (data == -1)
427274410Smarcel			data = end;
428272384Smarcel
429272384Smarcel		/*
430272384Smarcel		 * Treat the entire file as data if sparse files
431272384Smarcel		 * are not supported by the underlying file system.
432272384Smarcel		 */
433274410Smarcel		if (hole == end && data == end)
434272384Smarcel			data = cur;
435272384Smarcel
436272384Smarcel		if (cur == hole && data > hole) {
437272384Smarcel			hole = pos;
438272384Smarcel			pos = data & ~((uint64_t)secsz - 1);
439272384Smarcel
440272384Smarcel			blk += (pos - hole) / secsz;
441272384Smarcel			error = image_chunk_skipto(blk);
442272384Smarcel
443272384Smarcel			bytesize += pos - hole;
444272384Smarcel			cur = data;
445272384Smarcel		} else if (cur == data && hole > data) {
446272384Smarcel			data = pos;
447272384Smarcel			pos = (hole + secsz - 1) & ~((uint64_t)secsz - 1);
448272384Smarcel
449272384Smarcel			while (data < pos) {
450272384Smarcel				sz = (pos - data > (off_t)iosz)
451272384Smarcel				    ? iosz : (size_t)(pos - data);
452272384Smarcel
453272384Smarcel				buf = image_file_map(fd, data, sz);
454272384Smarcel				if (buf != NULL) {
455272384Smarcel					error = image_chunk_copyin(blk, buf,
456272384Smarcel					    sz, data, fd);
457272384Smarcel					image_file_unmap(buf, sz);
458272384Smarcel				} else
459272384Smarcel					error = errno;
460272384Smarcel
461272384Smarcel				blk += sz / secsz;
462272384Smarcel				bytesize += sz;
463272384Smarcel				data += sz;
464272384Smarcel			}
465272384Smarcel			cur = hole;
466272384Smarcel		} else {
467272384Smarcel			/*
468272384Smarcel			 * I don't know what this means or whether it
469272384Smarcel			 * can happen at all...
470272384Smarcel			 */
471329059Smanu			assert(0);
472265574Smarcel		}
473265574Smarcel	}
474272384Smarcel	if (error)
475272384Smarcel		close(fd);
476272384Smarcel	if (!error && sizep != NULL)
477265574Smarcel		*sizep = bytesize;
478265574Smarcel	return (error);
479265574Smarcel}
480265574Smarcel
481265574Smarcelint
482272384Smarcelimage_copyin(lba_t blk, int fd, uint64_t *sizep)
483272384Smarcel{
484272384Smarcel	struct stat sb;
485272384Smarcel	int error;
486272384Smarcel
487272384Smarcel	error = image_chunk_skipto(blk);
488272384Smarcel	if (!error) {
489272384Smarcel		if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode))
490272384Smarcel			error = image_copyin_stream(blk, fd, sizep);
491272384Smarcel		else
492272384Smarcel			error = image_copyin_mapped(blk, fd, sizep);
493272384Smarcel	}
494272384Smarcel	return (error);
495272384Smarcel}
496272384Smarcel
497272384Smarcel/*
498272384Smarcel * Output/sink file handling.
499272384Smarcel */
500272384Smarcel
501272384Smarcelint
502265618Smarcelimage_copyout(int fd)
503265574Smarcel{
504268236Smarcel	int error;
505268236Smarcel
506268236Smarcel	error = image_copyout_region(fd, 0, image_size);
507268646Smarcel	if (!error)
508268646Smarcel		error = image_copyout_done(fd);
509268646Smarcel	return (error);
510268646Smarcel}
511268236Smarcel
512268646Smarcelint
513268646Smarcelimage_copyout_done(int fd)
514268646Smarcel{
515268646Smarcel	off_t ofs;
516268646Smarcel	int error;
517268646Smarcel
518268236Smarcel	ofs = lseek(fd, 0L, SEEK_CUR);
519268236Smarcel	if (ofs == -1)
520268236Smarcel		return (0);
521268236Smarcel	error = (ftruncate(fd, ofs) == -1) ? errno : 0;
522268236Smarcel	return (error);
523268236Smarcel}
524268236Smarcel
525272384Smarcelstatic int
526272384Smarcelimage_copyout_memory(int fd, size_t size, void *ptr)
527272384Smarcel{
528272384Smarcel
529272384Smarcel	if (write(fd, ptr, size) == -1)
530272384Smarcel		return (errno);
531272384Smarcel	return (0);
532272384Smarcel}
533272384Smarcel
534286215Smarcelint
535286215Smarcelimage_copyout_zeroes(int fd, size_t count)
536272384Smarcel{
537272384Smarcel	static uint8_t *zeroes = NULL;
538272384Smarcel	size_t sz;
539272384Smarcel	int error;
540272384Smarcel
541286215Smarcel	if (lseek(fd, (off_t)count, SEEK_CUR) != -1)
542272384Smarcel		return (0);
543272384Smarcel
544272384Smarcel	/*
545272384Smarcel	 * If we can't seek, we must write.
546272384Smarcel	 */
547272384Smarcel
548272384Smarcel	if (zeroes == NULL) {
549272384Smarcel		zeroes = calloc(1, secsz);
550272384Smarcel		if (zeroes == NULL)
551272384Smarcel			return (ENOMEM);
552272384Smarcel	}
553272384Smarcel
554286215Smarcel	while (count > 0) {
555286215Smarcel		sz = (count > secsz) ? secsz : count;
556272384Smarcel		error = image_copyout_memory(fd, sz, zeroes);
557272384Smarcel		if (error)
558272384Smarcel			return (error);
559286215Smarcel		count -= sz;
560272384Smarcel	}
561272384Smarcel	return (0);
562272384Smarcel}
563272384Smarcel
564272384Smarcelstatic int
565272384Smarcelimage_copyout_file(int fd, size_t size, int ifd, off_t iofs)
566272384Smarcel{
567272384Smarcel	void *buf;
568272384Smarcel	size_t iosz, sz;
569272384Smarcel	int error;
570272384Smarcel
571272384Smarcel	iosz = secsz * image_swap_pgsz;
572272384Smarcel
573272384Smarcel	while (size > 0) {
574272384Smarcel		sz = (size > iosz) ? iosz : size;
575272384Smarcel		buf = image_file_map(ifd, iofs, sz);
576272384Smarcel		if (buf == NULL)
577272384Smarcel			return (errno);
578272384Smarcel		error = image_copyout_memory(fd, sz, buf);
579272384Smarcel		image_file_unmap(buf, sz);
580272384Smarcel		if (error)
581272384Smarcel			return (error);
582272384Smarcel		size -= sz;
583272384Smarcel		iofs += sz;
584272384Smarcel	}
585272384Smarcel	return (0);
586272384Smarcel}
587272384Smarcel
588268236Smarcelint
589268236Smarcelimage_copyout_region(int fd, lba_t blk, lba_t size)
590268236Smarcel{
591272384Smarcel	struct chunk *ch;
592272384Smarcel	size_t ofs, sz;
593265618Smarcel	int error;
594265574Smarcel
595272384Smarcel	size *= secsz;
596265618Smarcel
597329059Smanu	error = 0;
598329059Smanu	while (!error && size > 0) {
599272384Smarcel		ch = image_chunk_find(blk);
600329059Smanu		if (ch == NULL) {
601329059Smanu			error = EINVAL;
602329059Smanu			break;
603329059Smanu		}
604272384Smarcel		ofs = (blk - ch->ch_block) * secsz;
605272384Smarcel		sz = ch->ch_size - ofs;
606272384Smarcel		sz = ((lba_t)sz < size) ? sz : (size_t)size;
607272384Smarcel		switch (ch->ch_type) {
608272384Smarcel		case CH_TYPE_ZEROES:
609272384Smarcel			error = image_copyout_zeroes(fd, sz);
610265618Smarcel			break;
611272384Smarcel		case CH_TYPE_FILE:
612272384Smarcel			error = image_copyout_file(fd, sz, ch->ch_u.file.fd,
613272384Smarcel			    ch->ch_u.file.ofs + ofs);
614265618Smarcel			break;
615272384Smarcel		case CH_TYPE_MEMORY:
616272384Smarcel			error = image_copyout_memory(fd, sz, ch->ch_u.mem.ptr);
617272384Smarcel			break;
618272384Smarcel		default:
619329059Smanu			assert(0);
620265618Smarcel		}
621272384Smarcel		size -= sz;
622272384Smarcel		blk += sz / secsz;
623265618Smarcel	}
624329059Smanu	return (error);
625265618Smarcel}
626265618Smarcel
627268646Smarcelint
628268646Smarcelimage_data(lba_t blk, lba_t size)
629268646Smarcel{
630272384Smarcel	struct chunk *ch;
631272384Smarcel	lba_t lim;
632268646Smarcel
633272384Smarcel	while (1) {
634272384Smarcel		ch = image_chunk_find(blk);
635272384Smarcel		if (ch == NULL)
636272384Smarcel			return (0);
637272384Smarcel		if (ch->ch_type != CH_TYPE_ZEROES)
638272384Smarcel			return (1);
639272384Smarcel		lim = ch->ch_block + (ch->ch_size / secsz);
640272384Smarcel		if (lim >= blk + size)
641272384Smarcel			return (0);
642272384Smarcel		size -= lim - blk;
643272384Smarcel		blk = lim;
644268646Smarcel	}
645272384Smarcel	/*NOTREACHED*/
646268646Smarcel}
647268646Smarcel
648265725Smarcellba_t
649265725Smarcelimage_get_size(void)
650265725Smarcel{
651265725Smarcel
652265725Smarcel	return (image_size);
653265725Smarcel}
654265725Smarcel
655265618Smarcelint
656265618Smarcelimage_set_size(lba_t blk)
657265618Smarcel{
658272384Smarcel	int error;
659265618Smarcel
660272384Smarcel	error = image_chunk_skipto(blk);
661272384Smarcel	if (!error)
662272384Smarcel		image_size = blk;
663272384Smarcel	return (error);
664265574Smarcel}
665265574Smarcel
666265574Smarcelint
667265618Smarcelimage_write(lba_t blk, void *buf, ssize_t len)
668265574Smarcel{
669272384Smarcel	struct chunk *ch;
670265574Smarcel
671272384Smarcel	while (len > 0) {
672272384Smarcel		if (!is_empty_sector(buf)) {
673272384Smarcel			ch = image_chunk_find(blk);
674272384Smarcel			if (ch == NULL)
675272384Smarcel				return (ENXIO);
676272384Smarcel			/* We may not be able to write to files. */
677272384Smarcel			if (ch->ch_type == CH_TYPE_FILE)
678272384Smarcel				return (EINVAL);
679272384Smarcel			if (ch->ch_type == CH_TYPE_ZEROES) {
680272384Smarcel				ch = image_chunk_memory(ch, blk);
681272384Smarcel				if (ch == NULL)
682272384Smarcel					return (ENOMEM);
683272384Smarcel			}
684272384Smarcel			assert(ch->ch_type == CH_TYPE_MEMORY);
685272384Smarcel			memcpy(ch->ch_u.mem.ptr, buf, secsz);
686272384Smarcel		}
687272384Smarcel		blk++;
688272384Smarcel		buf = (char *)buf + secsz;
689272384Smarcel		len--;
690272384Smarcel	}
691265574Smarcel	return (0);
692265574Smarcel}
693265618Smarcel
694272384Smarcelstatic void
695272384Smarcelimage_cleanup(void)
696272384Smarcel{
697272384Smarcel	struct chunk *ch;
698272384Smarcel
699329059Smanu	while ((ch = TAILQ_FIRST(&image_chunks)) != NULL) {
700272384Smarcel		switch (ch->ch_type) {
701272384Smarcel		case CH_TYPE_FILE:
702272384Smarcel			/* We may be closing the same file multiple times. */
703272384Smarcel			if (ch->ch_u.file.fd != -1)
704272384Smarcel				close(ch->ch_u.file.fd);
705272384Smarcel			break;
706272384Smarcel		case CH_TYPE_MEMORY:
707272384Smarcel			free(ch->ch_u.mem.ptr);
708272384Smarcel			break;
709272384Smarcel		default:
710272384Smarcel			break;
711272384Smarcel		}
712329059Smanu		TAILQ_REMOVE(&image_chunks, ch, ch_list);
713272384Smarcel		free(ch);
714272384Smarcel	}
715272384Smarcel	if (image_swap_fd != -1)
716272384Smarcel		close(image_swap_fd);
717272384Smarcel	unlink(image_swap_file);
718272384Smarcel}
719272384Smarcel
720265618Smarcelint
721265618Smarcelimage_init(void)
722265618Smarcel{
723266556Smarcel	const char *tmpdir;
724265618Smarcel
725329059Smanu	TAILQ_INIT(&image_chunks);
726272384Smarcel	image_nchunks = 0;
727272384Smarcel
728272384Smarcel	image_swap_size = 0;
729272384Smarcel	image_swap_pgsz = getpagesize();
730272384Smarcel
731272384Smarcel	if (atexit(image_cleanup) == -1)
732265618Smarcel		return (errno);
733266556Smarcel	if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0')
734266556Smarcel		tmpdir = _PATH_TMP;
735272384Smarcel	snprintf(image_swap_file, sizeof(image_swap_file), "%s/mkimg-XXXXXX",
736266556Smarcel	    tmpdir);
737272384Smarcel	image_swap_fd = mkstemp(image_swap_file);
738272384Smarcel	if (image_swap_fd == -1)
739265618Smarcel		return (errno);
740265618Smarcel	return (0);
741265618Smarcel}
742