Home | History | Annotate | Line # | Download | only in gpt
      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.16   tsutsui __RCSID("$NetBSD: map.c,v 1.16 2023/12/05 17:23:19 tsutsui 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.11  christos #include "gpt_private.h"
     47   1.1  christos 
     48  1.11  christos static map_t
     49  1.13  christos map_create(off_t start, off_t size, int type)
     50   1.1  christos {
     51  1.11  christos 	map_t m;
     52   1.1  christos 
     53   1.8  christos 	m = calloc(1, sizeof(*m));
     54   1.1  christos 	if (m == NULL)
     55  1.12  christos 		return NULL;
     56   1.1  christos 	m->map_start = start;
     57   1.1  christos 	m->map_size = size;
     58   1.1  christos 	m->map_next = m->map_prev = NULL;
     59   1.1  christos 	m->map_type = type;
     60   1.1  christos 	m->map_index = 0;
     61   1.1  christos 	m->map_data = NULL;
     62  1.13  christos 	m->map_alloc = 0;
     63  1.12  christos 	return m;
     64   1.1  christos }
     65   1.1  christos 
     66  1.13  christos static void
     67  1.13  christos map_destroy(map_t m)
     68  1.13  christos {
     69  1.13  christos 	if (m == NULL)
     70  1.13  christos 		return;
     71  1.13  christos 	if (m->map_alloc)
     72  1.13  christos 		free(m->map_data);
     73  1.13  christos 	free(m);
     74  1.13  christos }
     75  1.13  christos 
     76  1.10  christos static const char *maptypes[] = {
     77   1.9  christos 	"unused",
     78   1.9  christos 	"mbr",
     79   1.9  christos 	"mbr partition",
     80   1.9  christos 	"primary gpt header",
     81   1.9  christos 	"secondary gpt header",
     82   1.9  christos 	"primary gpt table",
     83   1.9  christos 	"secondary gpt table",
     84   1.9  christos 	"gpt partition",
     85   1.9  christos 	"protective mbr",
     86   1.9  christos };
     87   1.9  christos 
     88   1.9  christos static const char *
     89   1.9  christos map_type(int t)
     90   1.9  christos {
     91   1.9  christos 	if ((size_t)t >= __arraycount(maptypes))
     92   1.9  christos 		return "*unknown*";
     93   1.9  christos 	return maptypes[t];
     94   1.9  christos }
     95   1.9  christos 
     96  1.11  christos map_t
     97  1.13  christos map_add(gpt_t gpt, off_t start, off_t size, int type, void *data, int alloc)
     98   1.1  christos {
     99  1.11  christos 	map_t m, n, p;
    100   1.1  christos 
    101  1.10  christos #ifdef DEBUG
    102  1.10  christos 	printf("add: %s %#jx %#jx\n", map_type(type), (uintmax_t)start,
    103  1.10  christos 	    (uintmax_t)size);
    104  1.11  christos 	for (n = gpt->mediamap; n; n = n->map_next)
    105  1.10  christos 		printf("have: %s %#jx %#jx\n", map_type(n->map_type),
    106  1.10  christos 		    (uintmax_t)n->map_start, (uintmax_t)n->map_size);
    107  1.10  christos #endif
    108  1.10  christos 
    109  1.11  christos 	n = gpt->mediamap;
    110   1.1  christos 	while (n != NULL && n->map_start + n->map_size <= start)
    111   1.1  christos 		n = n->map_next;
    112   1.8  christos 	if (n == NULL) {
    113  1.11  christos 		if (!(gpt->flags & GPT_QUIET))
    114  1.11  christos 			gpt_warnx(gpt, "Can't find map");
    115  1.12  christos 		return NULL;
    116   1.8  christos 	}
    117   1.1  christos 
    118   1.1  christos 	if (n->map_start + n->map_size < start + size) {
    119  1.11  christos 		if (!(gpt->flags & GPT_QUIET))
    120  1.14       mrg 			gpt_warnx(gpt, "map entry doesn't fit media: "
    121  1.14       mrg 			    "new start + new size < start + size\n"
    122  1.14       mrg 			    "(%jx + %jx < %jx + %jx)",
    123  1.14       mrg 			    n->map_start, n->map_size, start, size);
    124  1.12  christos 		return NULL;
    125   1.1  christos 	}
    126   1.1  christos 
    127   1.1  christos 	if (n->map_start == start && n->map_size == size) {
    128   1.1  christos 		if (n->map_type != MAP_TYPE_UNUSED) {
    129   1.1  christos 			if (n->map_type != MAP_TYPE_MBR_PART ||
    130   1.1  christos 			    type != MAP_TYPE_GPT_PART) {
    131  1.11  christos 				if (!(gpt->flags & GPT_QUIET))
    132  1.12  christos 					gpt_warnx(gpt,
    133  1.12  christos 					    "partition(%ju,%ju) mirrored",
    134   1.8  christos 					    (uintmax_t)start, (uintmax_t)size);
    135   1.1  christos 			}
    136   1.1  christos 		}
    137   1.1  christos 		n->map_type = type;
    138   1.1  christos 		n->map_data = data;
    139  1.13  christos 		n->map_alloc = alloc;
    140  1.12  christos 		return n;
    141   1.1  christos 	}
    142   1.1  christos 
    143   1.9  christos 	if (n->map_type != MAP_TYPE_UNUSED) {
    144   1.9  christos 		if (n->map_type != MAP_TYPE_MBR_PART ||
    145   1.9  christos 		    type != MAP_TYPE_GPT_PART) {
    146  1.11  christos 			gpt_warnx(gpt, "bogus map current=%s new=%s",
    147   1.9  christos 			    map_type(n->map_type), map_type(type));
    148  1.12  christos 			return NULL;
    149   1.9  christos 		}
    150   1.1  christos 		n->map_type = MAP_TYPE_UNUSED;
    151   1.1  christos 	}
    152   1.1  christos 
    153  1.13  christos 	m = map_create(start, size, type);
    154   1.1  christos 	if (m == NULL)
    155  1.12  christos 		goto oomem;
    156   1.1  christos 
    157   1.1  christos 	m->map_data = data;
    158  1.13  christos 	m->map_alloc = alloc;
    159   1.1  christos 
    160   1.1  christos 	if (start == n->map_start) {
    161   1.1  christos 		m->map_prev = n->map_prev;
    162   1.1  christos 		m->map_next = n;
    163   1.1  christos 		if (m->map_prev != NULL)
    164   1.1  christos 			m->map_prev->map_next = m;
    165   1.1  christos 		else
    166  1.11  christos 			gpt->mediamap = m;
    167   1.1  christos 		n->map_prev = m;
    168   1.1  christos 		n->map_start += size;
    169   1.1  christos 		n->map_size -= size;
    170   1.1  christos 	} else if (start + size == n->map_start + n->map_size) {
    171   1.1  christos 		p = n;
    172   1.1  christos 		m->map_next = p->map_next;
    173   1.1  christos 		m->map_prev = p;
    174   1.1  christos 		if (m->map_next != NULL)
    175   1.1  christos 			m->map_next->map_prev = m;
    176   1.1  christos 		p->map_next = m;
    177   1.1  christos 		p->map_size -= size;
    178   1.1  christos 	} else {
    179  1.13  christos 		p = map_create(n->map_start, start - n->map_start, n->map_type);
    180  1.12  christos 		if (p == NULL)
    181  1.12  christos 			goto oomem;
    182   1.1  christos 		n->map_start += p->map_size + m->map_size;
    183   1.1  christos 		n->map_size -= (p->map_size + m->map_size);
    184   1.1  christos 		p->map_prev = n->map_prev;
    185   1.1  christos 		m->map_prev = p;
    186   1.1  christos 		n->map_prev = m;
    187   1.1  christos 		m->map_next = n;
    188   1.1  christos 		p->map_next = m;
    189   1.1  christos 		if (p->map_prev != NULL)
    190   1.1  christos 			p->map_prev->map_next = p;
    191   1.1  christos 		else
    192  1.11  christos 			gpt->mediamap = p;
    193   1.1  christos 	}
    194   1.1  christos 
    195  1.12  christos 	return m;
    196  1.12  christos oomem:
    197  1.13  christos 	map_destroy(m);
    198  1.12  christos 	gpt_warn(gpt, "Can't create map");
    199  1.12  christos 	return NULL;
    200   1.1  christos }
    201   1.1  christos 
    202  1.11  christos map_t
    203  1.11  christos map_alloc(gpt_t gpt, off_t start, off_t size, off_t alignment)
    204   1.1  christos {
    205   1.1  christos 	off_t delta;
    206  1.11  christos 	map_t m;
    207   1.1  christos 
    208   1.4   jnemeth 	if (alignment > 0) {
    209   1.4   jnemeth 		if ((start % alignment) != 0)
    210   1.4   jnemeth 			start = (start + alignment) / alignment * alignment;
    211   1.4   jnemeth 		if ((size % alignment) != 0)
    212   1.4   jnemeth 			size = (size + alignment) / alignment * alignment;
    213   1.4   jnemeth 	}
    214   1.4   jnemeth 
    215  1.11  christos 	for (m = gpt->mediamap; m != NULL; m = m->map_next) {
    216   1.1  christos 		if (m->map_type != MAP_TYPE_UNUSED || m->map_start < 2)
    217   1.1  christos 			continue;
    218   1.1  christos 		if (start != 0 && m->map_start > start)
    219  1.12  christos 			return NULL;
    220   1.4   jnemeth 
    221   1.4   jnemeth 		if (start != 0)
    222   1.4   jnemeth 			delta = start - m->map_start;
    223   1.4   jnemeth 		else if (alignment > 0 && m->map_start % alignment != 0)
    224   1.4   jnemeth 			delta = (m->map_start + alignment) /
    225   1.4   jnemeth 			        alignment * alignment - m->map_start;
    226   1.4   jnemeth 		else
    227   1.4   jnemeth 			delta = 0;
    228   1.4   jnemeth 
    229   1.4   jnemeth                 if (size == 0 || m->map_size - delta >= size) {
    230   1.4   jnemeth 			if (m->map_size - delta < alignment)
    231   1.1  christos 				continue;
    232   1.4   jnemeth 			if (size == 0) {
    233   1.4   jnemeth 				if (alignment > 0 &&
    234   1.4   jnemeth 				    (m->map_size - delta) % alignment != 0)
    235   1.4   jnemeth 					size = (m->map_size - delta) /
    236   1.4   jnemeth 					    alignment * alignment;
    237   1.4   jnemeth 				else
    238   1.4   jnemeth 					size = m->map_size - delta;
    239   1.4   jnemeth 			}
    240  1.11  christos 			return map_add(gpt, m->map_start + delta, size,
    241  1.13  christos 			    MAP_TYPE_GPT_PART, NULL, 0);
    242   1.1  christos 		}
    243   1.1  christos 	}
    244   1.1  christos 
    245   1.4   jnemeth 	return NULL;
    246   1.1  christos }
    247   1.1  christos 
    248   1.5   jnemeth off_t
    249  1.11  christos map_resize(gpt_t gpt, map_t m, off_t size, off_t alignment)
    250   1.5   jnemeth {
    251  1.11  christos 	map_t n, o;
    252   1.5   jnemeth 	off_t alignsize, prevsize;
    253   1.5   jnemeth 
    254   1.5   jnemeth 	n = m->map_next;
    255   1.5   jnemeth 
    256   1.6  christos 	if (size < 0 || alignment < 0) {
    257  1.11  christos 		gpt_warnx(gpt, "negative size or alignment");
    258  1.12  christos 		return -1;
    259   1.6  christos 	}
    260  1.16   tsutsui 	/* Size == 0 means to use whole region of the following unused map */
    261  1.12  christos 	if (size == 0) {
    262  1.12  christos 		if (n == NULL) {
    263  1.12  christos 			// XXX: we could just turn the map to UNUSED!
    264  1.12  christos 			gpt_warnx(gpt, "Can't delete, next map is not found");
    265  1.12  christos 			return -1;
    266  1.12  christos 		}
    267  1.12  christos 		if (n->map_type != MAP_TYPE_UNUSED) {
    268  1.12  christos 			gpt_warnx(gpt, "Can't delete, next map is in use");
    269  1.12  christos 			return -1;
    270  1.12  christos 		}
    271  1.12  christos 		if (alignment == 0) {
    272   1.5   jnemeth 			size = m->map_size + n->map_size;
    273   1.5   jnemeth 			m->map_size = size;
    274   1.5   jnemeth 			m->map_next = n->map_next;
    275   1.5   jnemeth 			if (n->map_next != NULL)
    276   1.5   jnemeth 				n->map_next->map_prev = m;
    277  1.13  christos 			map_destroy(n);
    278   1.5   jnemeth 			return size;
    279  1.12  christos 		} else { /* alignment > 0 */
    280   1.5   jnemeth 			prevsize = m->map_size;
    281  1.12  christos 			size = ((m->map_size + n->map_size) / alignment)
    282  1.12  christos 			    * alignment;
    283  1.15  jmcneill 			if (size == prevsize) {
    284  1.15  jmcneill 				m->map_size = size;
    285  1.15  jmcneill 				return size;
    286  1.15  jmcneill 			} else if (size < prevsize) {
    287  1.12  christos 				gpt_warnx(gpt, "Can't coalesce %ju <= %ju",
    288  1.12  christos 				    (uintmax_t)prevsize, (uintmax_t)size);
    289  1.12  christos 				return -1;
    290  1.12  christos 			}
    291   1.5   jnemeth 			m->map_size = size;
    292   1.5   jnemeth 			n->map_start += size - prevsize;
    293   1.5   jnemeth 			n->map_size -= size - prevsize;
    294   1.5   jnemeth 			if (n->map_size == 0) {
    295   1.5   jnemeth 				m->map_next = n->map_next;
    296   1.5   jnemeth 				if (n->map_next != NULL)
    297   1.5   jnemeth 					n->map_next->map_prev = m;
    298  1.13  christos 				map_destroy(n);
    299   1.5   jnemeth 			}
    300   1.5   jnemeth 			return size;
    301   1.5   jnemeth 		}
    302   1.5   jnemeth 	}
    303   1.5   jnemeth 
    304   1.5   jnemeth 	alignsize = size;
    305   1.5   jnemeth 	if (alignment % size != 0)
    306   1.5   jnemeth 		alignsize = (size + alignment) / alignment * alignment;
    307   1.5   jnemeth 
    308   1.5   jnemeth 	if (alignsize < m->map_size) {		/* shrinking */
    309   1.5   jnemeth 		prevsize = m->map_size;
    310   1.5   jnemeth 		m->map_size = alignsize;
    311   1.5   jnemeth 		if (n == NULL || n->map_type != MAP_TYPE_UNUSED) {
    312  1.13  christos 			o = map_create(m->map_start + alignsize,
    313   1.5   jnemeth 				  prevsize - alignsize, MAP_TYPE_UNUSED);
    314  1.12  christos 			if (o == NULL) {
    315  1.12  christos 				gpt_warn(gpt, "Can't create map");
    316  1.12  christos 				return -1;
    317  1.12  christos 			}
    318   1.5   jnemeth 			m->map_next = o;
    319   1.5   jnemeth 			o->map_prev = m;
    320   1.5   jnemeth 			o->map_next = n;
    321   1.5   jnemeth 			if (n != NULL)
    322   1.5   jnemeth 				n->map_prev = o;
    323   1.5   jnemeth 			return alignsize;
    324   1.5   jnemeth 		} else {
    325   1.5   jnemeth 			n->map_start -= alignsize;
    326   1.5   jnemeth 			n->map_size += alignsize;
    327   1.5   jnemeth 			return alignsize;
    328   1.5   jnemeth 		}
    329   1.5   jnemeth 	} else if (alignsize > m->map_size) {		/* expanding */
    330  1.12  christos 		if (n == NULL) {
    331  1.12  christos 			gpt_warnx(gpt, "Can't expand map, no space after it");
    332  1.12  christos 			return -1;
    333  1.12  christos 		}
    334  1.12  christos 		if (n->map_type != MAP_TYPE_UNUSED) {
    335  1.12  christos 			gpt_warnx(gpt,
    336  1.12  christos 			    "Can't expand map, next map after it in use");
    337  1.12  christos 			return -1;
    338  1.12  christos 		}
    339  1.12  christos 		if (n->map_size < alignsize - m->map_size) {
    340  1.12  christos 			gpt_warnx(gpt,
    341  1.12  christos 			    "Can't expand map, not enough space in the"
    342  1.12  christos 			    " next map after it");
    343  1.12  christos 			return -1;
    344   1.5   jnemeth 		}
    345   1.5   jnemeth 		n->map_size -= alignsize - m->map_size;
    346   1.5   jnemeth 		n->map_start += alignsize - m->map_size;
    347   1.5   jnemeth 		if (n->map_size == 0) {
    348   1.5   jnemeth 			m->map_next = n->map_next;
    349   1.5   jnemeth 			if (n->map_next != NULL)
    350   1.5   jnemeth 				n->map_next->map_prev = m;
    351  1.13  christos 			map_destroy(n);
    352   1.5   jnemeth 		}
    353   1.5   jnemeth 		m->map_size = alignsize;
    354   1.5   jnemeth 		return alignsize;
    355   1.5   jnemeth 	} else						/* correct size */
    356   1.5   jnemeth 		return alignsize;
    357   1.5   jnemeth }
    358   1.5   jnemeth 
    359  1.11  christos map_t
    360  1.11  christos map_find(gpt_t gpt, int type)
    361   1.1  christos {
    362  1.11  christos 	map_t m;
    363   1.1  christos 
    364  1.11  christos 	m = gpt->mediamap;
    365   1.1  christos 	while (m != NULL && m->map_type != type)
    366   1.1  christos 		m = m->map_next;
    367  1.12  christos 	return m;
    368   1.1  christos }
    369   1.1  christos 
    370  1.11  christos map_t
    371  1.11  christos map_first(gpt_t gpt)
    372   1.1  christos {
    373  1.11  christos 	return gpt->mediamap;
    374   1.1  christos }
    375   1.1  christos 
    376  1.11  christos map_t
    377  1.11  christos map_last(gpt_t gpt)
    378   1.1  christos {
    379  1.11  christos 	map_t m;
    380   1.1  christos 
    381  1.11  christos 	m = gpt->mediamap;
    382   1.1  christos 	while (m != NULL && m->map_next != NULL)
    383   1.1  christos 		m = m->map_next;
    384  1.12  christos 	return m;
    385   1.1  christos }
    386   1.1  christos 
    387   1.1  christos off_t
    388  1.11  christos map_free(gpt_t gpt, off_t start, off_t size)
    389   1.1  christos {
    390  1.11  christos 	map_t m;
    391   1.1  christos 
    392  1.11  christos 	m = gpt->mediamap;
    393   1.1  christos 
    394   1.1  christos 	while (m != NULL && m->map_start + m->map_size <= start)
    395   1.1  christos 		m = m->map_next;
    396   1.1  christos 	if (m == NULL || m->map_type != MAP_TYPE_UNUSED)
    397  1.12  christos 		return 0LL;
    398   1.1  christos 	if (size)
    399  1.12  christos 		return (m->map_start + m->map_size >= start + size) ? 1 : 0;
    400  1.12  christos 	return m->map_size - (start - m->map_start);
    401   1.1  christos }
    402   1.1  christos 
    403  1.12  christos int
    404  1.11  christos map_init(gpt_t gpt, off_t size)
    405   1.1  christos {
    406   1.1  christos 	char buf[32];
    407   1.1  christos 
    408  1.13  christos 	gpt->mediamap = map_create(0LL, size, MAP_TYPE_UNUSED);
    409  1.12  christos 	if (gpt->mediamap == NULL) {
    410  1.12  christos 		gpt_warn(gpt, "Can't create map");
    411  1.12  christos 		return -1;
    412  1.12  christos 	}
    413  1.11  christos 	gpt->lbawidth = snprintf(buf, sizeof(buf), "%ju", (uintmax_t)size);
    414  1.11  christos 	if (gpt->lbawidth < 5)
    415  1.11  christos 		gpt->lbawidth = 5;
    416  1.12  christos 	return 0;
    417   1.1  christos }
    418