map.c revision 1.10 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.10 2015/11/29 14:12:35 christos 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
47 int lbawidth;
48
49 static map_t *mediamap;
50
51 static map_t *
52 mkmap(off_t start, off_t size, int type)
53 {
54 map_t *m;
55
56 m = calloc(1, sizeof(*m));
57 if (m == NULL)
58 return (NULL);
59 m->map_start = start;
60 m->map_size = size;
61 m->map_next = m->map_prev = NULL;
62 m->map_type = type;
63 m->map_index = 0;
64 m->map_data = NULL;
65 return (m);
66 }
67
68 static const char *maptypes[] = {
69 "unused",
70 "mbr",
71 "mbr partition",
72 "primary gpt header",
73 "secondary gpt header",
74 "primary gpt table",
75 "secondary gpt table",
76 "gpt partition",
77 "protective mbr",
78 };
79
80 static const char *
81 map_type(int t)
82 {
83 if ((size_t)t >= __arraycount(maptypes))
84 return "*unknown*";
85 return maptypes[t];
86 }
87
88 map_t *
89 map_add(off_t start, off_t size, int type, void *data)
90 {
91 map_t *m, *n, *p;
92
93 #ifdef DEBUG
94 printf("add: %s %#jx %#jx\n", map_type(type), (uintmax_t)start,
95 (uintmax_t)size);
96 for (n = mediamap; n; n = n->map_next)
97 printf("have: %s %#jx %#jx\n", map_type(n->map_type),
98 (uintmax_t)n->map_start, (uintmax_t)n->map_size);
99 #endif
100
101 n = mediamap;
102 while (n != NULL && n->map_start + n->map_size <= start)
103 n = n->map_next;
104 if (n == NULL) {
105 if (!quiet)
106 warnx("Can't find map");
107 return (NULL);
108 }
109
110 if (n->map_start + n->map_size < start + size) {
111 if (!quiet)
112 warnx("map entry doesn't fit media");
113 return (NULL);
114 }
115
116 if (n->map_start == start && n->map_size == size) {
117 if (n->map_type != MAP_TYPE_UNUSED) {
118 if (n->map_type != MAP_TYPE_MBR_PART ||
119 type != MAP_TYPE_GPT_PART) {
120 if (!quiet)
121 warnx("partition(%ju,%ju) mirrored",
122 (uintmax_t)start, (uintmax_t)size);
123 }
124 }
125 n->map_type = type;
126 n->map_data = data;
127 return (n);
128 }
129
130 if (n->map_type != MAP_TYPE_UNUSED) {
131 if (n->map_type != MAP_TYPE_MBR_PART ||
132 type != MAP_TYPE_GPT_PART) {
133 warnx("bogus map current=%s new=%s",
134 map_type(n->map_type), map_type(type));
135 return (NULL);
136 }
137 n->map_type = MAP_TYPE_UNUSED;
138 }
139
140 m = mkmap(start, size, type);
141 if (m == NULL)
142 return (NULL);
143
144 m->map_data = data;
145
146 if (start == n->map_start) {
147 m->map_prev = n->map_prev;
148 m->map_next = n;
149 if (m->map_prev != NULL)
150 m->map_prev->map_next = m;
151 else
152 mediamap = m;
153 n->map_prev = m;
154 n->map_start += size;
155 n->map_size -= size;
156 } else if (start + size == n->map_start + n->map_size) {
157 p = n;
158 m->map_next = p->map_next;
159 m->map_prev = p;
160 if (m->map_next != NULL)
161 m->map_next->map_prev = m;
162 p->map_next = m;
163 p->map_size -= size;
164 } else {
165 p = mkmap(n->map_start, start - n->map_start, n->map_type);
166 n->map_start += p->map_size + m->map_size;
167 n->map_size -= (p->map_size + m->map_size);
168 p->map_prev = n->map_prev;
169 m->map_prev = p;
170 n->map_prev = m;
171 m->map_next = n;
172 p->map_next = m;
173 if (p->map_prev != NULL)
174 p->map_prev->map_next = p;
175 else
176 mediamap = p;
177 }
178
179 return (m);
180 }
181
182 map_t *
183 map_alloc(off_t start, off_t size, off_t alignment)
184 {
185 off_t delta;
186 map_t *m;
187
188 if (alignment > 0) {
189 if ((start % alignment) != 0)
190 start = (start + alignment) / alignment * alignment;
191 if ((size % alignment) != 0)
192 size = (size + alignment) / alignment * alignment;
193 }
194
195 for (m = mediamap; m != NULL; m = m->map_next) {
196 if (m->map_type != MAP_TYPE_UNUSED || m->map_start < 2)
197 continue;
198 if (start != 0 && m->map_start > start)
199 return (NULL);
200
201 if (start != 0)
202 delta = start - m->map_start;
203 else if (alignment > 0 && m->map_start % alignment != 0)
204 delta = (m->map_start + alignment) /
205 alignment * alignment - m->map_start;
206 else
207 delta = 0;
208
209 if (size == 0 || m->map_size - delta >= size) {
210 if (m->map_size - delta < alignment)
211 continue;
212 if (size == 0) {
213 if (alignment > 0 &&
214 (m->map_size - delta) % alignment != 0)
215 size = (m->map_size - delta) /
216 alignment * alignment;
217 else
218 size = m->map_size - delta;
219 }
220 return map_add(m->map_start + delta, size,
221 MAP_TYPE_GPT_PART, NULL);
222 }
223 }
224
225 return NULL;
226 }
227
228 off_t
229 map_resize(map_t *m, off_t size, off_t alignment)
230 {
231 map_t *n, *o;
232 off_t alignsize, prevsize;
233
234 n = m->map_next;
235
236 if (size < 0 || alignment < 0) {
237 warnx("negative size or alignment");
238 return 0;
239 }
240 if (size == 0 && alignment == 0) {
241 if (n == NULL || n->map_type != MAP_TYPE_UNUSED)
242 return 0;
243 else {
244 size = m->map_size + n->map_size;
245 m->map_size = size;
246 m->map_next = n->map_next;
247 if (n->map_next != NULL)
248 n->map_next->map_prev = m;
249 if (n->map_data != NULL)
250 free(n->map_data);
251 free(n);
252 return size;
253 }
254 }
255
256 if (size == 0 && alignment > 0) {
257 if (n == NULL || n->map_type != MAP_TYPE_UNUSED)
258 return 0;
259 else {
260 prevsize = m->map_size;
261 size = (m->map_size + n->map_size) /
262 alignment * alignment;
263 if (size <= prevsize)
264 return 0;
265 m->map_size = size;
266 n->map_start += size - prevsize;
267 n->map_size -= size - prevsize;
268 if (n->map_size == 0) {
269 m->map_next = n->map_next;
270 if (n->map_next != NULL)
271 n->map_next->map_prev = m;
272 if (n->map_data != NULL)
273 free(n->map_data);
274 free(n);
275 }
276 return size;
277 }
278 }
279
280 alignsize = size;
281 if (alignment % size != 0)
282 alignsize = (size + alignment) / alignment * alignment;
283
284 if (alignsize < m->map_size) { /* shrinking */
285 prevsize = m->map_size;
286 m->map_size = alignsize;
287 if (n == NULL || n->map_type != MAP_TYPE_UNUSED) {
288 o = mkmap(m->map_start + alignsize,
289 prevsize - alignsize, MAP_TYPE_UNUSED);
290 m->map_next = o;
291 o->map_prev = m;
292 o->map_next = n;
293 if (n != NULL)
294 n->map_prev = o;
295 return alignsize;
296 } else {
297 n->map_start -= alignsize;
298 n->map_size += alignsize;
299 return alignsize;
300 }
301 } else if (alignsize > m->map_size) { /* expanding */
302 if (n == NULL || n->map_type != MAP_TYPE_UNUSED ||
303 n->map_size < alignsize - m->map_size) {
304 return 0;
305 }
306 n->map_size -= alignsize - m->map_size;
307 n->map_start += alignsize - m->map_size;
308 if (n->map_size == 0) {
309 m->map_next = n->map_next;
310 if (n->map_next != NULL)
311 n->map_next->map_prev = m;
312 if (n->map_data != NULL)
313 free(n->map_data);
314 free(n);
315 }
316 m->map_size = alignsize;
317 return alignsize;
318 } else /* correct size */
319 return alignsize;
320 }
321
322 map_t *
323 map_find(int type)
324 {
325 map_t *m;
326
327 m = mediamap;
328 while (m != NULL && m->map_type != type)
329 m = m->map_next;
330 return (m);
331 }
332
333 map_t *
334 map_first(void)
335 {
336 return mediamap;
337 }
338
339 map_t *
340 map_last(void)
341 {
342 map_t *m;
343
344 m = mediamap;
345 while (m != NULL && m->map_next != NULL)
346 m = m->map_next;
347 return (m);
348 }
349
350 off_t
351 map_free(off_t start, off_t size)
352 {
353 map_t *m;
354
355 m = mediamap;
356
357 while (m != NULL && m->map_start + m->map_size <= start)
358 m = m->map_next;
359 if (m == NULL || m->map_type != MAP_TYPE_UNUSED)
360 return (0LL);
361 if (size)
362 return ((m->map_start + m->map_size >= start + size) ? 1 : 0);
363 return (m->map_size - (start - m->map_start));
364 }
365
366 void
367 map_init(off_t size)
368 {
369 char buf[32];
370
371 mediamap = mkmap(0LL, size, MAP_TYPE_UNUSED);
372 lbawidth = snprintf(buf, sizeof(buf), "%ju", (uintmax_t)size);
373 if (lbawidth < 5)
374 lbawidth = 5;
375 }
376