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$"); 29265574Smarcel 30272775Smarcel#include <sys/mman.h> 31272775Smarcel#include <sys/queue.h> 32272775Smarcel#include <sys/stat.h> 33265574Smarcel#include <sys/types.h> 34265574Smarcel#include <assert.h> 35302924Smarkj#include <err.h> 36265574Smarcel#include <errno.h> 37268161Smarcel#include <limits.h> 38268161Smarcel#include <paths.h> 39272775Smarcel#include <stdint.h> 40268161Smarcel#include <stdio.h> 41265574Smarcel#include <stdlib.h> 42272775Smarcel#include <string.h> 43265574Smarcel#include <unistd.h> 44265574Smarcel 45265579Smarcel#include "image.h" 46265574Smarcel#include "mkimg.h" 47265574Smarcel 48272775Smarcelstruct chunk { 49272775Smarcel STAILQ_ENTRY(chunk) ch_list; 50272775Smarcel size_t ch_size; /* Size of chunk in bytes. */ 51272775Smarcel lba_t ch_block; /* Block address in image. */ 52272775Smarcel union { 53272775Smarcel struct { 54272775Smarcel off_t ofs; /* Offset in backing file. */ 55272775Smarcel int fd; /* FD of backing file. */ 56272775Smarcel } file; 57272775Smarcel struct { 58272775Smarcel void *ptr; /* Pointer to data in memory */ 59272775Smarcel } mem; 60272775Smarcel } ch_u; 61272775Smarcel u_int ch_type; 62272775Smarcel#define CH_TYPE_ZEROES 0 /* Chunk is a gap (no data). */ 63272775Smarcel#define CH_TYPE_FILE 1 /* File-backed chunk. */ 64272775Smarcel#define CH_TYPE_MEMORY 2 /* Memory-backed chunk */ 65272775Smarcel}; 66265574Smarcel 67272775Smarcelstatic STAILQ_HEAD(chunk_head, chunk) image_chunks; 68272775Smarcelstatic u_int image_nchunks; 69272775Smarcel 70272775Smarcelstatic char image_swap_file[PATH_MAX]; 71272775Smarcelstatic int image_swap_fd = -1; 72272775Smarcelstatic u_int image_swap_pgsz; 73272775Smarcelstatic off_t image_swap_size; 74272775Smarcel 75265725Smarcelstatic lba_t image_size; 76265618Smarcel 77272775Smarcelstatic int 78272775Smarcelis_empty_sector(void *buf) 79265618Smarcel{ 80272775Smarcel uint64_t *p = buf; 81272775Smarcel size_t n, max; 82265618Smarcel 83272775Smarcel assert(((uintptr_t)p & 3) == 0); 84272775Smarcel 85272775Smarcel max = secsz / sizeof(uint64_t); 86272775Smarcel for (n = 0; n < max; n++) { 87272775Smarcel if (p[n] != 0UL) 88272775Smarcel return (0); 89272775Smarcel } 90272775Smarcel return (1); 91265618Smarcel} 92265618Smarcel 93272775Smarcel/* 94272775Smarcel * Swap file handlng. 95272775Smarcel */ 96272775Smarcel 97272775Smarcelstatic off_t 98272775Smarcelimage_swap_alloc(size_t size) 99265574Smarcel{ 100272775Smarcel off_t ofs; 101272775Smarcel size_t unit; 102272775Smarcel 103272775Smarcel unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz; 104272775Smarcel assert((unit & (unit - 1)) == 0); 105272775Smarcel 106272775Smarcel size = (size + unit - 1) & ~(unit - 1); 107272775Smarcel 108272775Smarcel ofs = image_swap_size; 109272775Smarcel image_swap_size += size; 110272775Smarcel if (ftruncate(image_swap_fd, image_swap_size) == -1) { 111272775Smarcel image_swap_size = ofs; 112272775Smarcel ofs = -1LL; 113272775Smarcel } 114272775Smarcel return (ofs); 115272775Smarcel} 116272775Smarcel 117272775Smarcel/* 118272775Smarcel * Image chunk handling. 119272775Smarcel */ 120272775Smarcel 121272775Smarcelstatic struct chunk * 122272775Smarcelimage_chunk_find(lba_t blk) 123272775Smarcel{ 124272775Smarcel static struct chunk *last = NULL; 125272775Smarcel struct chunk *ch; 126272775Smarcel 127272775Smarcel ch = (last != NULL && last->ch_block <= blk) 128272775Smarcel ? last : STAILQ_FIRST(&image_chunks); 129272775Smarcel while (ch != NULL) { 130272775Smarcel if (ch->ch_block <= blk && 131272775Smarcel (lba_t)(ch->ch_block + (ch->ch_size / secsz)) > blk) { 132272775Smarcel last = ch; 133272775Smarcel break; 134272775Smarcel } 135272775Smarcel ch = STAILQ_NEXT(ch, ch_list); 136272775Smarcel } 137272775Smarcel return (ch); 138272775Smarcel} 139272775Smarcel 140272775Smarcelstatic size_t 141272775Smarcelimage_chunk_grow(struct chunk *ch, size_t sz) 142272775Smarcel{ 143272775Smarcel size_t dsz, newsz; 144272775Smarcel 145272775Smarcel newsz = ch->ch_size + sz; 146272775Smarcel if (newsz > ch->ch_size) { 147272775Smarcel ch->ch_size = newsz; 148272775Smarcel return (0); 149272775Smarcel } 150272775Smarcel /* We would overflow -- create new chunk for remainder. */ 151272775Smarcel dsz = SIZE_MAX - ch->ch_size; 152272775Smarcel assert(dsz < sz); 153272775Smarcel ch->ch_size = SIZE_MAX; 154272775Smarcel return (sz - dsz); 155272775Smarcel} 156272775Smarcel 157272775Smarcelstatic struct chunk * 158272775Smarcelimage_chunk_memory(struct chunk *ch, lba_t blk) 159272775Smarcel{ 160272775Smarcel struct chunk *new; 161272775Smarcel void *ptr; 162272775Smarcel 163272775Smarcel ptr = calloc(1, secsz); 164272775Smarcel if (ptr == NULL) 165272775Smarcel return (NULL); 166272775Smarcel 167272775Smarcel if (ch->ch_block < blk) { 168272775Smarcel new = malloc(sizeof(*new)); 169272775Smarcel if (new == NULL) { 170272775Smarcel free(ptr); 171272775Smarcel return (NULL); 172272775Smarcel } 173272775Smarcel memcpy(new, ch, sizeof(*new)); 174272775Smarcel ch->ch_size = (blk - ch->ch_block) * secsz; 175272775Smarcel new->ch_block = blk; 176272775Smarcel new->ch_size -= ch->ch_size; 177272775Smarcel STAILQ_INSERT_AFTER(&image_chunks, ch, new, ch_list); 178272775Smarcel image_nchunks++; 179272775Smarcel ch = new; 180272775Smarcel } 181272775Smarcel 182272775Smarcel if (ch->ch_size > secsz) { 183272775Smarcel new = malloc(sizeof(*new)); 184272775Smarcel if (new == NULL) { 185272775Smarcel free(ptr); 186272775Smarcel return (NULL); 187272775Smarcel } 188272775Smarcel memcpy(new, ch, sizeof(*new)); 189272775Smarcel ch->ch_size = secsz; 190272775Smarcel new->ch_block++; 191272775Smarcel new->ch_size -= secsz; 192272775Smarcel STAILQ_INSERT_AFTER(&image_chunks, ch, new, ch_list); 193272775Smarcel image_nchunks++; 194272775Smarcel } 195272775Smarcel 196272775Smarcel ch->ch_type = CH_TYPE_MEMORY; 197272775Smarcel ch->ch_u.mem.ptr = ptr; 198272775Smarcel return (ch); 199272775Smarcel} 200272775Smarcel 201272775Smarcelstatic int 202272775Smarcelimage_chunk_skipto(lba_t to) 203272775Smarcel{ 204272775Smarcel struct chunk *ch; 205272775Smarcel lba_t from; 206272775Smarcel size_t sz; 207272775Smarcel 208272775Smarcel ch = STAILQ_LAST(&image_chunks, chunk, ch_list); 209272775Smarcel from = (ch != NULL) ? ch->ch_block + (ch->ch_size / secsz) : 0LL; 210272775Smarcel 211272775Smarcel assert(from <= to); 212272775Smarcel 213272775Smarcel /* Nothing to do? */ 214272775Smarcel if (from == to) 215272775Smarcel return (0); 216272775Smarcel /* Avoid bugs due to overflows. */ 217272775Smarcel if ((uintmax_t)(to - from) > (uintmax_t)(SIZE_MAX / secsz)) 218272775Smarcel return (EFBIG); 219272775Smarcel sz = (to - from) * secsz; 220272775Smarcel if (ch != NULL && ch->ch_type == CH_TYPE_ZEROES) { 221272775Smarcel sz = image_chunk_grow(ch, sz); 222272775Smarcel if (sz == 0) 223272775Smarcel return (0); 224272775Smarcel from = ch->ch_block + (ch->ch_size / secsz); 225272775Smarcel } 226272775Smarcel ch = malloc(sizeof(*ch)); 227272775Smarcel if (ch == NULL) 228272775Smarcel return (ENOMEM); 229272775Smarcel memset(ch, 0, sizeof(*ch)); 230272775Smarcel ch->ch_block = from; 231272775Smarcel ch->ch_size = sz; 232272775Smarcel ch->ch_type = CH_TYPE_ZEROES; 233272775Smarcel STAILQ_INSERT_TAIL(&image_chunks, ch, ch_list); 234272775Smarcel image_nchunks++; 235272775Smarcel return (0); 236272775Smarcel} 237272775Smarcel 238272775Smarcelstatic int 239272775Smarcelimage_chunk_append(lba_t blk, size_t sz, off_t ofs, int fd) 240272775Smarcel{ 241272775Smarcel struct chunk *ch; 242272775Smarcel 243272775Smarcel ch = STAILQ_LAST(&image_chunks, chunk, ch_list); 244272775Smarcel if (ch != NULL && ch->ch_type == CH_TYPE_FILE) { 245272775Smarcel if (fd == ch->ch_u.file.fd && 246272775Smarcel blk == (lba_t)(ch->ch_block + (ch->ch_size / secsz)) && 247272775Smarcel ofs == (off_t)(ch->ch_u.file.ofs + ch->ch_size)) { 248272775Smarcel sz = image_chunk_grow(ch, sz); 249272775Smarcel if (sz == 0) 250272775Smarcel return (0); 251272775Smarcel blk = ch->ch_block + (ch->ch_size / secsz); 252272775Smarcel ofs = ch->ch_u.file.ofs + ch->ch_size; 253272775Smarcel } 254272775Smarcel } 255272775Smarcel ch = malloc(sizeof(*ch)); 256272775Smarcel if (ch == NULL) 257272775Smarcel return (ENOMEM); 258272775Smarcel memset(ch, 0, sizeof(*ch)); 259272775Smarcel ch->ch_block = blk; 260272775Smarcel ch->ch_size = sz; 261272775Smarcel ch->ch_type = CH_TYPE_FILE; 262272775Smarcel ch->ch_u.file.ofs = ofs; 263272775Smarcel ch->ch_u.file.fd = fd; 264272775Smarcel STAILQ_INSERT_TAIL(&image_chunks, ch, ch_list); 265272775Smarcel image_nchunks++; 266272775Smarcel return (0); 267272775Smarcel} 268272775Smarcel 269272775Smarcelstatic int 270272775Smarcelimage_chunk_copyin(lba_t blk, void *buf, size_t sz, off_t ofs, int fd) 271272775Smarcel{ 272272775Smarcel uint8_t *p = buf; 273272775Smarcel int error; 274272775Smarcel 275272775Smarcel error = 0; 276272775Smarcel sz = (sz + secsz - 1) & ~(secsz - 1); 277272775Smarcel while (!error && sz > 0) { 278272775Smarcel if (is_empty_sector(p)) 279272775Smarcel error = image_chunk_skipto(blk + 1); 280272775Smarcel else 281272775Smarcel error = image_chunk_append(blk, secsz, ofs, fd); 282272775Smarcel blk++; 283272775Smarcel p += secsz; 284272775Smarcel sz -= secsz; 285272775Smarcel ofs += secsz; 286272775Smarcel } 287272775Smarcel return (error); 288272775Smarcel} 289272775Smarcel 290272775Smarcel/* 291272775Smarcel * File mapping support. 292272775Smarcel */ 293272775Smarcel 294272775Smarcelstatic void * 295272775Smarcelimage_file_map(int fd, off_t ofs, size_t sz) 296272775Smarcel{ 297272775Smarcel void *ptr; 298272775Smarcel size_t unit; 299272775Smarcel int flags, prot; 300272775Smarcel 301272775Smarcel unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz; 302272775Smarcel assert((unit & (unit - 1)) == 0); 303272775Smarcel 304272775Smarcel flags = MAP_NOCORE | MAP_NOSYNC | MAP_SHARED; 305272775Smarcel /* Allow writing to our swap file only. */ 306272775Smarcel prot = PROT_READ | ((fd == image_swap_fd) ? PROT_WRITE : 0); 307272775Smarcel sz = (sz + unit - 1) & ~(unit - 1); 308272775Smarcel ptr = mmap(NULL, sz, prot, flags, fd, ofs); 309272775Smarcel return ((ptr == MAP_FAILED) ? NULL : ptr); 310272775Smarcel} 311272775Smarcel 312272775Smarcelstatic int 313272775Smarcelimage_file_unmap(void *buffer, size_t sz) 314272775Smarcel{ 315272775Smarcel size_t unit; 316272775Smarcel 317272775Smarcel unit = (secsz > image_swap_pgsz) ? secsz : image_swap_pgsz; 318272775Smarcel sz = (sz + unit - 1) & ~(unit - 1); 319302924Smarkj if (madvise(buffer, sz, MADV_DONTNEED) != 0) 320302924Smarkj warn("madvise"); 321272775Smarcel munmap(buffer, sz); 322272775Smarcel return (0); 323272775Smarcel} 324272775Smarcel 325272775Smarcel/* 326272775Smarcel * Input/source file handling. 327272775Smarcel */ 328272775Smarcel 329272775Smarcelstatic int 330272775Smarcelimage_copyin_stream(lba_t blk, int fd, uint64_t *sizep) 331272775Smarcel{ 332265574Smarcel char *buffer; 333265574Smarcel uint64_t bytesize; 334272775Smarcel off_t swofs; 335272775Smarcel size_t iosz; 336272775Smarcel ssize_t rdsz; 337272775Smarcel int error; 338265574Smarcel 339272775Smarcel /* 340272775Smarcel * This makes sure we're doing I/O in multiples of the page 341272775Smarcel * size as well as of the sector size. 2MB is the minimum 342272775Smarcel * by virtue of secsz at least 512 bytes and the page size 343272775Smarcel * at least 4K bytes. 344272775Smarcel */ 345272775Smarcel iosz = secsz * image_swap_pgsz; 346265574Smarcel 347265574Smarcel bytesize = 0; 348272775Smarcel do { 349272775Smarcel swofs = image_swap_alloc(iosz); 350272775Smarcel if (swofs == -1LL) 351272775Smarcel return (errno); 352272775Smarcel buffer = image_file_map(image_swap_fd, swofs, iosz); 353272775Smarcel if (buffer == NULL) 354272775Smarcel return (errno); 355272775Smarcel rdsz = read(fd, buffer, iosz); 356272775Smarcel if (rdsz > 0) 357272775Smarcel error = image_chunk_copyin(blk, buffer, rdsz, swofs, 358272775Smarcel image_swap_fd); 359272775Smarcel else if (rdsz < 0) 360272775Smarcel error = errno; 361272775Smarcel else 362272775Smarcel error = 0; 363272775Smarcel image_file_unmap(buffer, iosz); 364272775Smarcel /* XXX should we relinguish unused swap space? */ 365272775Smarcel if (error) 366272775Smarcel return (error); 367272775Smarcel 368272775Smarcel bytesize += rdsz; 369272775Smarcel blk += (rdsz + secsz - 1) / secsz; 370272775Smarcel } while (rdsz > 0); 371272775Smarcel 372272775Smarcel if (sizep != NULL) 373272775Smarcel *sizep = bytesize; 374272775Smarcel return (0); 375272775Smarcel} 376272775Smarcel 377272775Smarcelstatic int 378272775Smarcelimage_copyin_mapped(lba_t blk, int fd, uint64_t *sizep) 379272775Smarcel{ 380272775Smarcel off_t cur, data, end, hole, pos; 381272775Smarcel void *buf; 382272775Smarcel uint64_t bytesize; 383272775Smarcel size_t iosz, sz; 384272775Smarcel int error; 385272775Smarcel 386272775Smarcel /* 387272775Smarcel * We'd like to know the size of the file and we must 388272775Smarcel * be able to seek in order to mmap(2). If this isn't 389272775Smarcel * possible, then treat the file as a stream/pipe. 390272775Smarcel */ 391272775Smarcel end = lseek(fd, 0L, SEEK_END); 392272775Smarcel if (end == -1L) 393272775Smarcel return (image_copyin_stream(blk, fd, sizep)); 394272775Smarcel 395272775Smarcel /* 396272775Smarcel * We need the file opened for the duration and our 397272775Smarcel * caller is going to close the file. Make a dup(2) 398272775Smarcel * so that control the faith of the descriptor. 399272775Smarcel */ 400272775Smarcel fd = dup(fd); 401272775Smarcel if (fd == -1) 402272775Smarcel return (errno); 403272775Smarcel 404272775Smarcel iosz = secsz * image_swap_pgsz; 405272775Smarcel 406272775Smarcel bytesize = 0; 407272775Smarcel cur = pos = 0; 408272775Smarcel error = 0; 409272775Smarcel while (!error && cur < end) { 410272775Smarcel hole = lseek(fd, cur, SEEK_HOLE); 411274634Smarcel if (hole == -1) 412274634Smarcel hole = end; 413272775Smarcel data = lseek(fd, cur, SEEK_DATA); 414274634Smarcel if (data == -1) 415274634Smarcel data = end; 416272775Smarcel 417272775Smarcel /* 418272775Smarcel * Treat the entire file as data if sparse files 419272775Smarcel * are not supported by the underlying file system. 420272775Smarcel */ 421274634Smarcel if (hole == end && data == end) 422272775Smarcel data = cur; 423272775Smarcel 424272775Smarcel if (cur == hole && data > hole) { 425272775Smarcel hole = pos; 426272775Smarcel pos = data & ~((uint64_t)secsz - 1); 427272775Smarcel 428272775Smarcel blk += (pos - hole) / secsz; 429272775Smarcel error = image_chunk_skipto(blk); 430272775Smarcel 431272775Smarcel bytesize += pos - hole; 432272775Smarcel cur = data; 433272775Smarcel } else if (cur == data && hole > data) { 434272775Smarcel data = pos; 435272775Smarcel pos = (hole + secsz - 1) & ~((uint64_t)secsz - 1); 436272775Smarcel 437272775Smarcel while (data < pos) { 438272775Smarcel sz = (pos - data > (off_t)iosz) 439272775Smarcel ? iosz : (size_t)(pos - data); 440272775Smarcel 441272775Smarcel buf = image_file_map(fd, data, sz); 442272775Smarcel if (buf != NULL) { 443272775Smarcel error = image_chunk_copyin(blk, buf, 444272775Smarcel sz, data, fd); 445272775Smarcel image_file_unmap(buf, sz); 446272775Smarcel } else 447272775Smarcel error = errno; 448272775Smarcel 449272775Smarcel blk += sz / secsz; 450272775Smarcel bytesize += sz; 451272775Smarcel data += sz; 452272775Smarcel } 453272775Smarcel cur = hole; 454272775Smarcel } else { 455272775Smarcel /* 456272775Smarcel * I don't know what this means or whether it 457272775Smarcel * can happen at all... 458272775Smarcel */ 459272775Smarcel error = EDOOFUS; 460265574Smarcel break; 461265574Smarcel } 462265574Smarcel } 463272775Smarcel if (error) 464272775Smarcel close(fd); 465272775Smarcel if (!error && sizep != NULL) 466265574Smarcel *sizep = bytesize; 467265574Smarcel return (error); 468265574Smarcel} 469265574Smarcel 470265574Smarcelint 471272775Smarcelimage_copyin(lba_t blk, int fd, uint64_t *sizep) 472272775Smarcel{ 473272775Smarcel struct stat sb; 474272775Smarcel int error; 475272775Smarcel 476272775Smarcel error = image_chunk_skipto(blk); 477272775Smarcel if (!error) { 478272775Smarcel if (fstat(fd, &sb) == -1 || !S_ISREG(sb.st_mode)) 479272775Smarcel error = image_copyin_stream(blk, fd, sizep); 480272775Smarcel else 481272775Smarcel error = image_copyin_mapped(blk, fd, sizep); 482272775Smarcel } 483272775Smarcel return (error); 484272775Smarcel} 485272775Smarcel 486272775Smarcel/* 487272775Smarcel * Output/sink file handling. 488272775Smarcel */ 489272775Smarcel 490272775Smarcelint 491265618Smarcelimage_copyout(int fd) 492265574Smarcel{ 493269177Smarcel int error; 494269177Smarcel 495269177Smarcel error = image_copyout_region(fd, 0, image_size); 496269177Smarcel if (!error) 497269177Smarcel error = image_copyout_done(fd); 498269177Smarcel return (error); 499269177Smarcel} 500269177Smarcel 501269177Smarcelint 502269177Smarcelimage_copyout_done(int fd) 503269177Smarcel{ 504269177Smarcel off_t ofs; 505269177Smarcel int error; 506269177Smarcel 507269177Smarcel ofs = lseek(fd, 0L, SEEK_CUR); 508269177Smarcel if (ofs == -1) 509269177Smarcel return (0); 510269177Smarcel error = (ftruncate(fd, ofs) == -1) ? errno : 0; 511269177Smarcel return (error); 512269177Smarcel} 513269177Smarcel 514272775Smarcelstatic int 515272775Smarcelimage_copyout_memory(int fd, size_t size, void *ptr) 516272775Smarcel{ 517272775Smarcel 518272775Smarcel if (write(fd, ptr, size) == -1) 519272775Smarcel return (errno); 520272775Smarcel return (0); 521272775Smarcel} 522272775Smarcel 523287122Smarcelint 524287122Smarcelimage_copyout_zeroes(int fd, size_t count) 525272775Smarcel{ 526272775Smarcel static uint8_t *zeroes = NULL; 527272775Smarcel size_t sz; 528272775Smarcel int error; 529272775Smarcel 530287122Smarcel if (lseek(fd, (off_t)count, SEEK_CUR) != -1) 531272775Smarcel return (0); 532272775Smarcel 533272775Smarcel /* 534272775Smarcel * If we can't seek, we must write. 535272775Smarcel */ 536272775Smarcel 537272775Smarcel if (zeroes == NULL) { 538272775Smarcel zeroes = calloc(1, secsz); 539272775Smarcel if (zeroes == NULL) 540272775Smarcel return (ENOMEM); 541272775Smarcel } 542272775Smarcel 543287122Smarcel while (count > 0) { 544287122Smarcel sz = (count > secsz) ? secsz : count; 545272775Smarcel error = image_copyout_memory(fd, sz, zeroes); 546272775Smarcel if (error) 547272775Smarcel return (error); 548287122Smarcel count -= sz; 549272775Smarcel } 550272775Smarcel return (0); 551272775Smarcel} 552272775Smarcel 553272775Smarcelstatic int 554272775Smarcelimage_copyout_file(int fd, size_t size, int ifd, off_t iofs) 555272775Smarcel{ 556272775Smarcel void *buf; 557272775Smarcel size_t iosz, sz; 558272775Smarcel int error; 559272775Smarcel 560272775Smarcel iosz = secsz * image_swap_pgsz; 561272775Smarcel 562272775Smarcel while (size > 0) { 563272775Smarcel sz = (size > iosz) ? iosz : size; 564272775Smarcel buf = image_file_map(ifd, iofs, sz); 565272775Smarcel if (buf == NULL) 566272775Smarcel return (errno); 567272775Smarcel error = image_copyout_memory(fd, sz, buf); 568272775Smarcel image_file_unmap(buf, sz); 569272775Smarcel if (error) 570272775Smarcel return (error); 571272775Smarcel size -= sz; 572272775Smarcel iofs += sz; 573272775Smarcel } 574272775Smarcel return (0); 575272775Smarcel} 576272775Smarcel 577269177Smarcelint 578269177Smarcelimage_copyout_region(int fd, lba_t blk, lba_t size) 579269177Smarcel{ 580272775Smarcel struct chunk *ch; 581272775Smarcel size_t ofs, sz; 582265618Smarcel int error; 583265574Smarcel 584272775Smarcel size *= secsz; 585265618Smarcel 586269177Smarcel while (size > 0) { 587272775Smarcel ch = image_chunk_find(blk); 588272775Smarcel if (ch == NULL) 589272775Smarcel return (EINVAL); 590272775Smarcel ofs = (blk - ch->ch_block) * secsz; 591272775Smarcel sz = ch->ch_size - ofs; 592272775Smarcel sz = ((lba_t)sz < size) ? sz : (size_t)size; 593272775Smarcel switch (ch->ch_type) { 594272775Smarcel case CH_TYPE_ZEROES: 595272775Smarcel error = image_copyout_zeroes(fd, sz); 596265618Smarcel break; 597272775Smarcel case CH_TYPE_FILE: 598272775Smarcel error = image_copyout_file(fd, sz, ch->ch_u.file.fd, 599272775Smarcel ch->ch_u.file.ofs + ofs); 600265618Smarcel break; 601272775Smarcel case CH_TYPE_MEMORY: 602272775Smarcel error = image_copyout_memory(fd, sz, ch->ch_u.mem.ptr); 603272775Smarcel break; 604272775Smarcel default: 605272775Smarcel return (EDOOFUS); 606265618Smarcel } 607272775Smarcel size -= sz; 608272775Smarcel blk += sz / secsz; 609265618Smarcel } 610272775Smarcel return (0); 611265618Smarcel} 612265618Smarcel 613269177Smarcelint 614269177Smarcelimage_data(lba_t blk, lba_t size) 615269177Smarcel{ 616272775Smarcel struct chunk *ch; 617272775Smarcel lba_t lim; 618269177Smarcel 619272775Smarcel while (1) { 620272775Smarcel ch = image_chunk_find(blk); 621272775Smarcel if (ch == NULL) 622272775Smarcel return (0); 623272775Smarcel if (ch->ch_type != CH_TYPE_ZEROES) 624272775Smarcel return (1); 625272775Smarcel lim = ch->ch_block + (ch->ch_size / secsz); 626272775Smarcel if (lim >= blk + size) 627272775Smarcel return (0); 628272775Smarcel size -= lim - blk; 629272775Smarcel blk = lim; 630269177Smarcel } 631272775Smarcel /*NOTREACHED*/ 632269177Smarcel} 633269177Smarcel 634265725Smarcellba_t 635265725Smarcelimage_get_size(void) 636265725Smarcel{ 637265725Smarcel 638265725Smarcel return (image_size); 639265725Smarcel} 640265725Smarcel 641265618Smarcelint 642265618Smarcelimage_set_size(lba_t blk) 643265618Smarcel{ 644272775Smarcel int error; 645265618Smarcel 646272775Smarcel error = image_chunk_skipto(blk); 647272775Smarcel if (!error) 648272775Smarcel image_size = blk; 649272775Smarcel return (error); 650265574Smarcel} 651265574Smarcel 652265574Smarcelint 653265618Smarcelimage_write(lba_t blk, void *buf, ssize_t len) 654265574Smarcel{ 655272775Smarcel struct chunk *ch; 656265574Smarcel 657272775Smarcel while (len > 0) { 658272775Smarcel if (!is_empty_sector(buf)) { 659272775Smarcel ch = image_chunk_find(blk); 660272775Smarcel if (ch == NULL) 661272775Smarcel return (ENXIO); 662272775Smarcel /* We may not be able to write to files. */ 663272775Smarcel if (ch->ch_type == CH_TYPE_FILE) 664272775Smarcel return (EINVAL); 665272775Smarcel if (ch->ch_type == CH_TYPE_ZEROES) { 666272775Smarcel ch = image_chunk_memory(ch, blk); 667272775Smarcel if (ch == NULL) 668272775Smarcel return (ENOMEM); 669272775Smarcel } 670272775Smarcel assert(ch->ch_type == CH_TYPE_MEMORY); 671272775Smarcel memcpy(ch->ch_u.mem.ptr, buf, secsz); 672272775Smarcel } 673272775Smarcel blk++; 674272775Smarcel buf = (char *)buf + secsz; 675272775Smarcel len--; 676272775Smarcel } 677265574Smarcel return (0); 678265574Smarcel} 679265618Smarcel 680272775Smarcelstatic void 681272775Smarcelimage_cleanup(void) 682272775Smarcel{ 683272775Smarcel struct chunk *ch; 684272775Smarcel 685272775Smarcel while ((ch = STAILQ_FIRST(&image_chunks)) != NULL) { 686272775Smarcel switch (ch->ch_type) { 687272775Smarcel case CH_TYPE_FILE: 688272775Smarcel /* We may be closing the same file multiple times. */ 689272775Smarcel if (ch->ch_u.file.fd != -1) 690272775Smarcel close(ch->ch_u.file.fd); 691272775Smarcel break; 692272775Smarcel case CH_TYPE_MEMORY: 693272775Smarcel free(ch->ch_u.mem.ptr); 694272775Smarcel break; 695272775Smarcel default: 696272775Smarcel break; 697272775Smarcel } 698272775Smarcel STAILQ_REMOVE_HEAD(&image_chunks, ch_list); 699272775Smarcel free(ch); 700272775Smarcel } 701272775Smarcel if (image_swap_fd != -1) 702272775Smarcel close(image_swap_fd); 703272775Smarcel unlink(image_swap_file); 704272775Smarcel} 705272775Smarcel 706265618Smarcelint 707265618Smarcelimage_init(void) 708265618Smarcel{ 709268161Smarcel const char *tmpdir; 710265618Smarcel 711272775Smarcel STAILQ_INIT(&image_chunks); 712272775Smarcel image_nchunks = 0; 713272775Smarcel 714272775Smarcel image_swap_size = 0; 715272775Smarcel image_swap_pgsz = getpagesize(); 716272775Smarcel 717272775Smarcel if (atexit(image_cleanup) == -1) 718265618Smarcel return (errno); 719268161Smarcel if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') 720268161Smarcel tmpdir = _PATH_TMP; 721272775Smarcel snprintf(image_swap_file, sizeof(image_swap_file), "%s/mkimg-XXXXXX", 722268161Smarcel tmpdir); 723272775Smarcel image_swap_fd = mkstemp(image_swap_file); 724272775Smarcel if (image_swap_fd == -1) 725265618Smarcel return (errno); 726265618Smarcel return (0); 727265618Smarcel} 728