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