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