Home | History | Annotate | Line # | Download | only in gpt
map.c revision 1.14
      1 /*-
      2  * Copyright (c) 2002 Marcel Moolenaar
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  *
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #if HAVE_NBTOOL_CONFIG_H
     28 #include "nbtool_config.h"
     29 #endif
     30 
     31 #include <sys/cdefs.h>
     32 #ifdef __FBSDID
     33 __FBSDID("$FreeBSD: src/sbin/gpt/map.c,v 1.6 2005/08/31 01:47:19 marcel Exp $");
     34 #endif
     35 #ifdef __RCSID
     36 __RCSID("$NetBSD: map.c,v 1.14 2018/04/11 07:14:23 mrg Exp $");
     37 #endif
     38 
     39 #include <sys/types.h>
     40 #include <err.h>
     41 #include <stdio.h>
     42 #include <stdlib.h>
     43 
     44 #include "map.h"
     45 #include "gpt.h"
     46 #include "gpt_private.h"
     47 
     48 static map_t
     49 map_create(off_t start, off_t size, int type)
     50 {
     51 	map_t m;
     52 
     53 	m = calloc(1, sizeof(*m));
     54 	if (m == NULL)
     55 		return NULL;
     56 	m->map_start = start;
     57 	m->map_size = size;
     58 	m->map_next = m->map_prev = NULL;
     59 	m->map_type = type;
     60 	m->map_index = 0;
     61 	m->map_data = NULL;
     62 	m->map_alloc = 0;
     63 	return m;
     64 }
     65 
     66 static void
     67 map_destroy(map_t m)
     68 {
     69 	if (m == NULL)
     70 		return;
     71 	if (m->map_alloc)
     72 		free(m->map_data);
     73 	free(m);
     74 }
     75 
     76 static const char *maptypes[] = {
     77 	"unused",
     78 	"mbr",
     79 	"mbr partition",
     80 	"primary gpt header",
     81 	"secondary gpt header",
     82 	"primary gpt table",
     83 	"secondary gpt table",
     84 	"gpt partition",
     85 	"protective mbr",
     86 };
     87 
     88 static const char *
     89 map_type(int t)
     90 {
     91 	if ((size_t)t >= __arraycount(maptypes))
     92 		return "*unknown*";
     93 	return maptypes[t];
     94 }
     95 
     96 map_t
     97 map_add(gpt_t gpt, off_t start, off_t size, int type, void *data, int alloc)
     98 {
     99 	map_t m, n, p;
    100 
    101 #ifdef DEBUG
    102 	printf("add: %s %#jx %#jx\n", map_type(type), (uintmax_t)start,
    103 	    (uintmax_t)size);
    104 	for (n = gpt->mediamap; n; n = n->map_next)
    105 		printf("have: %s %#jx %#jx\n", map_type(n->map_type),
    106 		    (uintmax_t)n->map_start, (uintmax_t)n->map_size);
    107 #endif
    108 
    109 	n = gpt->mediamap;
    110 	while (n != NULL && n->map_start + n->map_size <= start)
    111 		n = n->map_next;
    112 	if (n == NULL) {
    113 		if (!(gpt->flags & GPT_QUIET))
    114 			gpt_warnx(gpt, "Can't find map");
    115 		return NULL;
    116 	}
    117 
    118 	if (n->map_start + n->map_size < start + size) {
    119 		if (!(gpt->flags & GPT_QUIET))
    120 			gpt_warnx(gpt, "map entry doesn't fit media: "
    121 			    "new start + new size < start + size\n"
    122 			    "(%jx + %jx < %jx + %jx)",
    123 			    n->map_start, n->map_size, start, size);
    124 		return NULL;
    125 	}
    126 
    127 	if (n->map_start == start && n->map_size == size) {
    128 		if (n->map_type != MAP_TYPE_UNUSED) {
    129 			if (n->map_type != MAP_TYPE_MBR_PART ||
    130 			    type != MAP_TYPE_GPT_PART) {
    131 				if (!(gpt->flags & GPT_QUIET))
    132 					gpt_warnx(gpt,
    133 					    "partition(%ju,%ju) mirrored",
    134 					    (uintmax_t)start, (uintmax_t)size);
    135 			}
    136 		}
    137 		n->map_type = type;
    138 		n->map_data = data;
    139 		n->map_alloc = alloc;
    140 		return n;
    141 	}
    142 
    143 	if (n->map_type != MAP_TYPE_UNUSED) {
    144 		if (n->map_type != MAP_TYPE_MBR_PART ||
    145 		    type != MAP_TYPE_GPT_PART) {
    146 			gpt_warnx(gpt, "bogus map current=%s new=%s",
    147 			    map_type(n->map_type), map_type(type));
    148 			return NULL;
    149 		}
    150 		n->map_type = MAP_TYPE_UNUSED;
    151 	}
    152 
    153 	m = map_create(start, size, type);
    154 	if (m == NULL)
    155 		goto oomem;
    156 
    157 	m->map_data = data;
    158 	m->map_alloc = alloc;
    159 
    160 	if (start == n->map_start) {
    161 		m->map_prev = n->map_prev;
    162 		m->map_next = n;
    163 		if (m->map_prev != NULL)
    164 			m->map_prev->map_next = m;
    165 		else
    166 			gpt->mediamap = m;
    167 		n->map_prev = m;
    168 		n->map_start += size;
    169 		n->map_size -= size;
    170 	} else if (start + size == n->map_start + n->map_size) {
    171 		p = n;
    172 		m->map_next = p->map_next;
    173 		m->map_prev = p;
    174 		if (m->map_next != NULL)
    175 			m->map_next->map_prev = m;
    176 		p->map_next = m;
    177 		p->map_size -= size;
    178 	} else {
    179 		p = map_create(n->map_start, start - n->map_start, n->map_type);
    180 		if (p == NULL)
    181 			goto oomem;
    182 		n->map_start += p->map_size + m->map_size;
    183 		n->map_size -= (p->map_size + m->map_size);
    184 		p->map_prev = n->map_prev;
    185 		m->map_prev = p;
    186 		n->map_prev = m;
    187 		m->map_next = n;
    188 		p->map_next = m;
    189 		if (p->map_prev != NULL)
    190 			p->map_prev->map_next = p;
    191 		else
    192 			gpt->mediamap = p;
    193 	}
    194 
    195 	return m;
    196 oomem:
    197 	map_destroy(m);
    198 	gpt_warn(gpt, "Can't create map");
    199 	return NULL;
    200 }
    201 
    202 map_t
    203 map_alloc(gpt_t gpt, off_t start, off_t size, off_t alignment)
    204 {
    205 	off_t delta;
    206 	map_t m;
    207 
    208 	if (alignment > 0) {
    209 		if ((start % alignment) != 0)
    210 			start = (start + alignment) / alignment * alignment;
    211 		if ((size % alignment) != 0)
    212 			size = (size + alignment) / alignment * alignment;
    213 	}
    214 
    215 	for (m = gpt->mediamap; m != NULL; m = m->map_next) {
    216 		if (m->map_type != MAP_TYPE_UNUSED || m->map_start < 2)
    217 			continue;
    218 		if (start != 0 && m->map_start > start)
    219 			return NULL;
    220 
    221 		if (start != 0)
    222 			delta = start - m->map_start;
    223 		else if (alignment > 0 && m->map_start % alignment != 0)
    224 			delta = (m->map_start + alignment) /
    225 			        alignment * alignment - m->map_start;
    226 		else
    227 			delta = 0;
    228 
    229                 if (size == 0 || m->map_size - delta >= size) {
    230 			if (m->map_size - delta < alignment)
    231 				continue;
    232 			if (size == 0) {
    233 				if (alignment > 0 &&
    234 				    (m->map_size - delta) % alignment != 0)
    235 					size = (m->map_size - delta) /
    236 					    alignment * alignment;
    237 				else
    238 					size = m->map_size - delta;
    239 			}
    240 			return map_add(gpt, m->map_start + delta, size,
    241 			    MAP_TYPE_GPT_PART, NULL, 0);
    242 		}
    243 	}
    244 
    245 	return NULL;
    246 }
    247 
    248 off_t
    249 map_resize(gpt_t gpt, map_t m, off_t size, off_t alignment)
    250 {
    251 	map_t n, o;
    252 	off_t alignsize, prevsize;
    253 
    254 	n = m->map_next;
    255 
    256 	if (size < 0 || alignment < 0) {
    257 		gpt_warnx(gpt, "negative size or alignment");
    258 		return -1;
    259 	}
    260 	/* Size == 0 means delete, if the next map is unused */
    261 	if (size == 0) {
    262 		if (n == NULL) {
    263 			// XXX: we could just turn the map to UNUSED!
    264 			gpt_warnx(gpt, "Can't delete, next map is not found");
    265 			return -1;
    266 		}
    267 		if (n->map_type != MAP_TYPE_UNUSED) {
    268 			gpt_warnx(gpt, "Can't delete, next map is in use");
    269 			return -1;
    270 		}
    271 		if (alignment == 0) {
    272 			size = m->map_size + n->map_size;
    273 			m->map_size = size;
    274 			m->map_next = n->map_next;
    275 			if (n->map_next != NULL)
    276 				n->map_next->map_prev = m;
    277 			map_destroy(n);
    278 			return size;
    279 		} else { /* alignment > 0 */
    280 			prevsize = m->map_size;
    281 			size = ((m->map_size + n->map_size) / alignment)
    282 			    * alignment;
    283 			if (size <= prevsize) {
    284 				gpt_warnx(gpt, "Can't coalesce %ju <= %ju",
    285 				    (uintmax_t)prevsize, (uintmax_t)size);
    286 				return -1;
    287 			}
    288 			m->map_size = size;
    289 			n->map_start += size - prevsize;
    290 			n->map_size -= size - prevsize;
    291 			if (n->map_size == 0) {
    292 				m->map_next = n->map_next;
    293 				if (n->map_next != NULL)
    294 					n->map_next->map_prev = m;
    295 				map_destroy(n);
    296 			}
    297 			return size;
    298 		}
    299 	}
    300 
    301 	alignsize = size;
    302 	if (alignment % size != 0)
    303 		alignsize = (size + alignment) / alignment * alignment;
    304 
    305 	if (alignsize < m->map_size) {		/* shrinking */
    306 		prevsize = m->map_size;
    307 		m->map_size = alignsize;
    308 		if (n == NULL || n->map_type != MAP_TYPE_UNUSED) {
    309 			o = map_create(m->map_start + alignsize,
    310 				  prevsize - alignsize, MAP_TYPE_UNUSED);
    311 			if (o == NULL) {
    312 				gpt_warn(gpt, "Can't create map");
    313 				return -1;
    314 			}
    315 			m->map_next = o;
    316 			o->map_prev = m;
    317 			o->map_next = n;
    318 			if (n != NULL)
    319 				n->map_prev = o;
    320 			return alignsize;
    321 		} else {
    322 			n->map_start -= alignsize;
    323 			n->map_size += alignsize;
    324 			return alignsize;
    325 		}
    326 	} else if (alignsize > m->map_size) {		/* expanding */
    327 		if (n == NULL) {
    328 			gpt_warnx(gpt, "Can't expand map, no space after it");
    329 			return -1;
    330 		}
    331 		if (n->map_type != MAP_TYPE_UNUSED) {
    332 			gpt_warnx(gpt,
    333 			    "Can't expand map, next map after it in use");
    334 			return -1;
    335 		}
    336 		if (n->map_size < alignsize - m->map_size) {
    337 			gpt_warnx(gpt,
    338 			    "Can't expand map, not enough space in the"
    339 			    " next map after it");
    340 			return -1;
    341 		}
    342 		n->map_size -= alignsize - m->map_size;
    343 		n->map_start += alignsize - m->map_size;
    344 		if (n->map_size == 0) {
    345 			m->map_next = n->map_next;
    346 			if (n->map_next != NULL)
    347 				n->map_next->map_prev = m;
    348 			map_destroy(n);
    349 		}
    350 		m->map_size = alignsize;
    351 		return alignsize;
    352 	} else						/* correct size */
    353 		return alignsize;
    354 }
    355 
    356 map_t
    357 map_find(gpt_t gpt, int type)
    358 {
    359 	map_t m;
    360 
    361 	m = gpt->mediamap;
    362 	while (m != NULL && m->map_type != type)
    363 		m = m->map_next;
    364 	return m;
    365 }
    366 
    367 map_t
    368 map_first(gpt_t gpt)
    369 {
    370 	return gpt->mediamap;
    371 }
    372 
    373 map_t
    374 map_last(gpt_t gpt)
    375 {
    376 	map_t m;
    377 
    378 	m = gpt->mediamap;
    379 	while (m != NULL && m->map_next != NULL)
    380 		m = m->map_next;
    381 	return m;
    382 }
    383 
    384 off_t
    385 map_free(gpt_t gpt, off_t start, off_t size)
    386 {
    387 	map_t m;
    388 
    389 	m = gpt->mediamap;
    390 
    391 	while (m != NULL && m->map_start + m->map_size <= start)
    392 		m = m->map_next;
    393 	if (m == NULL || m->map_type != MAP_TYPE_UNUSED)
    394 		return 0LL;
    395 	if (size)
    396 		return (m->map_start + m->map_size >= start + size) ? 1 : 0;
    397 	return m->map_size - (start - m->map_start);
    398 }
    399 
    400 int
    401 map_init(gpt_t gpt, off_t size)
    402 {
    403 	char buf[32];
    404 
    405 	gpt->mediamap = map_create(0LL, size, MAP_TYPE_UNUSED);
    406 	if (gpt->mediamap == NULL) {
    407 		gpt_warn(gpt, "Can't create map");
    408 		return -1;
    409 	}
    410 	gpt->lbawidth = snprintf(buf, sizeof(buf), "%ju", (uintmax_t)size);
    411 	if (gpt->lbawidth < 5)
    412 		gpt->lbawidth = 5;
    413 	return 0;
    414 }
    415