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