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