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