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.15 2020/05/24 14:42:44 jmcneill 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
map_create(off_t start,off_t size,int type)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
map_destroy(map_t m)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 *
map_type(int t)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
map_add(gpt_t gpt,off_t start,off_t size,int type,void * data,int alloc)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
map_alloc(gpt_t gpt,off_t start,off_t size,off_t alignment)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
map_resize(gpt_t gpt,map_t m,off_t size,off_t alignment)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 m->map_size = size;
285 return size;
286 } else if (size < prevsize) {
287 gpt_warnx(gpt, "Can't coalesce %ju <= %ju",
288 (uintmax_t)prevsize, (uintmax_t)size);
289 return -1;
290 }
291 m->map_size = size;
292 n->map_start += size - prevsize;
293 n->map_size -= size - prevsize;
294 if (n->map_size == 0) {
295 m->map_next = n->map_next;
296 if (n->map_next != NULL)
297 n->map_next->map_prev = m;
298 map_destroy(n);
299 }
300 return size;
301 }
302 }
303
304 alignsize = size;
305 if (alignment % size != 0)
306 alignsize = (size + alignment) / alignment * alignment;
307
308 if (alignsize < m->map_size) { /* shrinking */
309 prevsize = m->map_size;
310 m->map_size = alignsize;
311 if (n == NULL || n->map_type != MAP_TYPE_UNUSED) {
312 o = map_create(m->map_start + alignsize,
313 prevsize - alignsize, MAP_TYPE_UNUSED);
314 if (o == NULL) {
315 gpt_warn(gpt, "Can't create map");
316 return -1;
317 }
318 m->map_next = o;
319 o->map_prev = m;
320 o->map_next = n;
321 if (n != NULL)
322 n->map_prev = o;
323 return alignsize;
324 } else {
325 n->map_start -= alignsize;
326 n->map_size += alignsize;
327 return alignsize;
328 }
329 } else if (alignsize > m->map_size) { /* expanding */
330 if (n == NULL) {
331 gpt_warnx(gpt, "Can't expand map, no space after it");
332 return -1;
333 }
334 if (n->map_type != MAP_TYPE_UNUSED) {
335 gpt_warnx(gpt,
336 "Can't expand map, next map after it in use");
337 return -1;
338 }
339 if (n->map_size < alignsize - m->map_size) {
340 gpt_warnx(gpt,
341 "Can't expand map, not enough space in the"
342 " next map after it");
343 return -1;
344 }
345 n->map_size -= alignsize - m->map_size;
346 n->map_start += alignsize - m->map_size;
347 if (n->map_size == 0) {
348 m->map_next = n->map_next;
349 if (n->map_next != NULL)
350 n->map_next->map_prev = m;
351 map_destroy(n);
352 }
353 m->map_size = alignsize;
354 return alignsize;
355 } else /* correct size */
356 return alignsize;
357 }
358
359 map_t
map_find(gpt_t gpt,int type)360 map_find(gpt_t gpt, int type)
361 {
362 map_t m;
363
364 m = gpt->mediamap;
365 while (m != NULL && m->map_type != type)
366 m = m->map_next;
367 return m;
368 }
369
370 map_t
map_first(gpt_t gpt)371 map_first(gpt_t gpt)
372 {
373 return gpt->mediamap;
374 }
375
376 map_t
map_last(gpt_t gpt)377 map_last(gpt_t gpt)
378 {
379 map_t m;
380
381 m = gpt->mediamap;
382 while (m != NULL && m->map_next != NULL)
383 m = m->map_next;
384 return m;
385 }
386
387 off_t
map_free(gpt_t gpt,off_t start,off_t size)388 map_free(gpt_t gpt, off_t start, off_t size)
389 {
390 map_t m;
391
392 m = gpt->mediamap;
393
394 while (m != NULL && m->map_start + m->map_size <= start)
395 m = m->map_next;
396 if (m == NULL || m->map_type != MAP_TYPE_UNUSED)
397 return 0LL;
398 if (size)
399 return (m->map_start + m->map_size >= start + size) ? 1 : 0;
400 return m->map_size - (start - m->map_start);
401 }
402
403 int
map_init(gpt_t gpt,off_t size)404 map_init(gpt_t gpt, off_t size)
405 {
406 char buf[32];
407
408 gpt->mediamap = map_create(0LL, size, MAP_TYPE_UNUSED);
409 if (gpt->mediamap == NULL) {
410 gpt_warn(gpt, "Can't create map");
411 return -1;
412 }
413 gpt->lbawidth = snprintf(buf, sizeof(buf), "%ju", (uintmax_t)size);
414 if (gpt->lbawidth < 5)
415 gpt->lbawidth = 5;
416 return 0;
417 }
418