Home | History | Annotate | Line # | Download | only in gpt
map.c revision 1.11
      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.11 2015/12/01 09:05:33 christos 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 mkmap(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 	return (m);
     63 }
     64 
     65 static const char *maptypes[] = {
     66 	"unused",
     67 	"mbr",
     68 	"mbr partition",
     69 	"primary gpt header",
     70 	"secondary gpt header",
     71 	"primary gpt table",
     72 	"secondary gpt table",
     73 	"gpt partition",
     74 	"protective mbr",
     75 };
     76 
     77 static const char *
     78 map_type(int t)
     79 {
     80 	if ((size_t)t >= __arraycount(maptypes))
     81 		return "*unknown*";
     82 	return maptypes[t];
     83 }
     84 
     85 map_t
     86 map_add(gpt_t gpt, off_t start, off_t size, int type, void *data)
     87 {
     88 	map_t m, n, p;
     89 
     90 #ifdef DEBUG
     91 	printf("add: %s %#jx %#jx\n", map_type(type), (uintmax_t)start,
     92 	    (uintmax_t)size);
     93 	for (n = gpt->mediamap; n; n = n->map_next)
     94 		printf("have: %s %#jx %#jx\n", map_type(n->map_type),
     95 		    (uintmax_t)n->map_start, (uintmax_t)n->map_size);
     96 #endif
     97 
     98 	n = gpt->mediamap;
     99 	while (n != NULL && n->map_start + n->map_size <= start)
    100 		n = n->map_next;
    101 	if (n == NULL) {
    102 		if (!(gpt->flags & GPT_QUIET))
    103 			gpt_warnx(gpt, "Can't find map");
    104 		return (NULL);
    105 	}
    106 
    107 	if (n->map_start + n->map_size < start + size) {
    108 		if (!(gpt->flags & GPT_QUIET))
    109 			gpt_warnx(gpt, "map entry doesn't fit media");
    110 		return (NULL);
    111 	}
    112 
    113 	if (n->map_start == start && n->map_size == size) {
    114 		if (n->map_type != MAP_TYPE_UNUSED) {
    115 			if (n->map_type != MAP_TYPE_MBR_PART ||
    116 			    type != MAP_TYPE_GPT_PART) {
    117 				if (!(gpt->flags & GPT_QUIET))
    118 					gpt_warnx(gpt, "partition(%ju,%ju) mirrored",
    119 					    (uintmax_t)start, (uintmax_t)size);
    120 			}
    121 		}
    122 		n->map_type = type;
    123 		n->map_data = data;
    124 		return (n);
    125 	}
    126 
    127 	if (n->map_type != MAP_TYPE_UNUSED) {
    128 		if (n->map_type != MAP_TYPE_MBR_PART ||
    129 		    type != MAP_TYPE_GPT_PART) {
    130 			gpt_warnx(gpt, "bogus map current=%s new=%s",
    131 			    map_type(n->map_type), map_type(type));
    132 			return (NULL);
    133 		}
    134 		n->map_type = MAP_TYPE_UNUSED;
    135 	}
    136 
    137 	m = mkmap(start, size, type);
    138 	if (m == NULL)
    139 		return (NULL);
    140 
    141 	m->map_data = data;
    142 
    143 	if (start == n->map_start) {
    144 		m->map_prev = n->map_prev;
    145 		m->map_next = n;
    146 		if (m->map_prev != NULL)
    147 			m->map_prev->map_next = m;
    148 		else
    149 			gpt->mediamap = m;
    150 		n->map_prev = m;
    151 		n->map_start += size;
    152 		n->map_size -= size;
    153 	} else if (start + size == n->map_start + n->map_size) {
    154 		p = n;
    155 		m->map_next = p->map_next;
    156 		m->map_prev = p;
    157 		if (m->map_next != NULL)
    158 			m->map_next->map_prev = m;
    159 		p->map_next = m;
    160 		p->map_size -= size;
    161 	} else {
    162 		p = mkmap(n->map_start, start - n->map_start, n->map_type);
    163 		n->map_start += p->map_size + m->map_size;
    164 		n->map_size -= (p->map_size + m->map_size);
    165 		p->map_prev = n->map_prev;
    166 		m->map_prev = p;
    167 		n->map_prev = m;
    168 		m->map_next = n;
    169 		p->map_next = m;
    170 		if (p->map_prev != NULL)
    171 			p->map_prev->map_next = p;
    172 		else
    173 			gpt->mediamap = p;
    174 	}
    175 
    176 	return (m);
    177 }
    178 
    179 map_t
    180 map_alloc(gpt_t gpt, off_t start, off_t size, off_t alignment)
    181 {
    182 	off_t delta;
    183 	map_t m;
    184 
    185 	if (alignment > 0) {
    186 		if ((start % alignment) != 0)
    187 			start = (start + alignment) / alignment * alignment;
    188 		if ((size % alignment) != 0)
    189 			size = (size + alignment) / alignment * alignment;
    190 	}
    191 
    192 	for (m = gpt->mediamap; m != NULL; m = m->map_next) {
    193 		if (m->map_type != MAP_TYPE_UNUSED || m->map_start < 2)
    194 			continue;
    195 		if (start != 0 && m->map_start > start)
    196 			return (NULL);
    197 
    198 		if (start != 0)
    199 			delta = start - m->map_start;
    200 		else if (alignment > 0 && m->map_start % alignment != 0)
    201 			delta = (m->map_start + alignment) /
    202 			        alignment * alignment - m->map_start;
    203 		else
    204 			delta = 0;
    205 
    206                 if (size == 0 || m->map_size - delta >= size) {
    207 			if (m->map_size - delta < alignment)
    208 				continue;
    209 			if (size == 0) {
    210 				if (alignment > 0 &&
    211 				    (m->map_size - delta) % alignment != 0)
    212 					size = (m->map_size - delta) /
    213 					    alignment * alignment;
    214 				else
    215 					size = m->map_size - delta;
    216 			}
    217 			return map_add(gpt, m->map_start + delta, size,
    218 				    MAP_TYPE_GPT_PART, NULL);
    219 		}
    220 	}
    221 
    222 	return NULL;
    223 }
    224 
    225 off_t
    226 map_resize(gpt_t gpt, map_t m, off_t size, off_t alignment)
    227 {
    228 	map_t n, o;
    229 	off_t alignsize, prevsize;
    230 
    231 	n = m->map_next;
    232 
    233 	if (size < 0 || alignment < 0) {
    234 		gpt_warnx(gpt, "negative size or alignment");
    235 		return 0;
    236 	}
    237 	if (size == 0 && alignment == 0) {
    238 		if (n == NULL || n->map_type != MAP_TYPE_UNUSED)
    239 			return 0;
    240 		else {
    241 			size = m->map_size + n->map_size;
    242 			m->map_size = size;
    243 			m->map_next = n->map_next;
    244 			if (n->map_next != NULL)
    245 				n->map_next->map_prev = m;
    246 			if (n->map_data != NULL)
    247 				free(n->map_data);
    248 			free(n);
    249 			return size;
    250 		}
    251 	}
    252 
    253 	if (size == 0 && alignment > 0) {
    254 		if (n == NULL || n->map_type != MAP_TYPE_UNUSED)
    255 			return 0;
    256 		else {
    257 			prevsize = m->map_size;
    258 			size = (m->map_size + n->map_size) /
    259 			       alignment * alignment;
    260 			if (size <= prevsize)
    261 				return 0;
    262 			m->map_size = size;
    263 			n->map_start += size - prevsize;
    264 			n->map_size -= size - prevsize;
    265 			if (n->map_size == 0) {
    266 				m->map_next = n->map_next;
    267 				if (n->map_next != NULL)
    268 					n->map_next->map_prev = m;
    269 				if (n->map_data != NULL)
    270 					free(n->map_data);
    271 				free(n);
    272 			}
    273 			return size;
    274 		}
    275 	}
    276 
    277 	alignsize = size;
    278 	if (alignment % size != 0)
    279 		alignsize = (size + alignment) / alignment * alignment;
    280 
    281 	if (alignsize < m->map_size) {		/* shrinking */
    282 		prevsize = m->map_size;
    283 		m->map_size = alignsize;
    284 		if (n == NULL || n->map_type != MAP_TYPE_UNUSED) {
    285 			o = mkmap(m->map_start + alignsize,
    286 				  prevsize - alignsize, MAP_TYPE_UNUSED);
    287 			m->map_next = o;
    288 			o->map_prev = m;
    289 			o->map_next = n;
    290 			if (n != NULL)
    291 				n->map_prev = o;
    292 			return alignsize;
    293 		} else {
    294 			n->map_start -= alignsize;
    295 			n->map_size += alignsize;
    296 			return alignsize;
    297 		}
    298 	} else if (alignsize > m->map_size) {		/* expanding */
    299 		if (n == NULL || n->map_type != MAP_TYPE_UNUSED ||
    300 		    n->map_size < alignsize - m->map_size) {
    301 			return 0;
    302 		}
    303 		n->map_size -= alignsize - m->map_size;
    304 		n->map_start += alignsize - m->map_size;
    305 		if (n->map_size == 0) {
    306 			m->map_next = n->map_next;
    307 			if (n->map_next != NULL)
    308 				n->map_next->map_prev = m;
    309 			if (n->map_data != NULL)
    310 				free(n->map_data);
    311 			free(n);
    312 		}
    313 		m->map_size = alignsize;
    314 		return alignsize;
    315 	} else						/* correct size */
    316 		return alignsize;
    317 }
    318 
    319 map_t
    320 map_find(gpt_t gpt, int type)
    321 {
    322 	map_t m;
    323 
    324 	m = gpt->mediamap;
    325 	while (m != NULL && m->map_type != type)
    326 		m = m->map_next;
    327 	return (m);
    328 }
    329 
    330 map_t
    331 map_first(gpt_t gpt)
    332 {
    333 	return gpt->mediamap;
    334 }
    335 
    336 map_t
    337 map_last(gpt_t gpt)
    338 {
    339 	map_t m;
    340 
    341 	m = gpt->mediamap;
    342 	while (m != NULL && m->map_next != NULL)
    343 		m = m->map_next;
    344 	return (m);
    345 }
    346 
    347 off_t
    348 map_free(gpt_t gpt, off_t start, off_t size)
    349 {
    350 	map_t m;
    351 
    352 	m = gpt->mediamap;
    353 
    354 	while (m != NULL && m->map_start + m->map_size <= start)
    355 		m = m->map_next;
    356 	if (m == NULL || m->map_type != MAP_TYPE_UNUSED)
    357 		return (0LL);
    358 	if (size)
    359 		return ((m->map_start + m->map_size >= start + size) ? 1 : 0);
    360 	return (m->map_size - (start - m->map_start));
    361 }
    362 
    363 void
    364 map_init(gpt_t gpt, off_t size)
    365 {
    366 	char buf[32];
    367 
    368 	gpt->mediamap = mkmap(0LL, size, MAP_TYPE_UNUSED);
    369 	gpt->lbawidth = snprintf(buf, sizeof(buf), "%ju", (uintmax_t)size);
    370 	if (gpt->lbawidth < 5)
    371 		gpt->lbawidth = 5;
    372 }
    373