xref: /netbsd/sbin/gpt/map.c (revision b523cf42)
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