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