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