]>
Commit | Line | Data |
---|---|---|
1 | ||
2 | #include "fdiskP.h" | |
3 | ||
4 | /** | |
5 | * SECTION: table | |
6 | * @title: Table | |
7 | * @short_description: container for fdisk partitions | |
8 | * | |
9 | * The fdisk_table is simple container for fdisk_partitions. The table is no | |
10 | * directly connected to label data (partition table), and table changes don't | |
11 | * affect in-memory or on-disk data. | |
12 | */ | |
13 | ||
14 | /** | |
15 | * fdisk_new_table: | |
16 | * | |
17 | * The table is a container for struct fdisk_partition entries. The container | |
18 | * does not have any real connection with label (partition table) and with | |
19 | * real on-disk data. | |
20 | * | |
21 | * Returns: newly allocated table struct. | |
22 | */ | |
23 | struct fdisk_table *fdisk_new_table(void) | |
24 | { | |
25 | struct fdisk_table *tb = NULL; | |
26 | ||
27 | tb = calloc(1, sizeof(*tb)); | |
28 | if (!tb) | |
29 | return NULL; | |
30 | ||
31 | DBG(TAB, ul_debugobj(tb, "alloc")); | |
32 | tb->refcount = 1; | |
33 | INIT_LIST_HEAD(&tb->parts); | |
34 | return tb; | |
35 | } | |
36 | ||
37 | /** | |
38 | * fdisk_reset_table: | |
39 | * @tb: tab pointer | |
40 | * | |
41 | * Removes all entries (partitions) from the table. The partitions with zero | |
42 | * reference count will be deallocated. This function does not modify partition | |
43 | * table. | |
44 | * | |
45 | * Returns: 0 on success or negative number in case of error. | |
46 | */ | |
47 | int fdisk_reset_table(struct fdisk_table *tb) | |
48 | { | |
49 | if (!tb) | |
50 | return -EINVAL; | |
51 | ||
52 | DBG(TAB, ul_debugobj(tb, "reset")); | |
53 | ||
54 | while (!list_empty(&tb->parts)) { | |
55 | struct fdisk_partition *pa = list_entry(tb->parts.next, | |
56 | struct fdisk_partition, parts); | |
57 | fdisk_table_remove_partition(tb, pa); | |
58 | } | |
59 | ||
60 | tb->nents = 0; | |
61 | return 0; | |
62 | } | |
63 | ||
64 | /** | |
65 | * fdisk_ref_table: | |
66 | * @tb: table pointer | |
67 | * | |
68 | * Increments reference counter. | |
69 | */ | |
70 | void fdisk_ref_table(struct fdisk_table *tb) | |
71 | { | |
72 | if (tb) | |
73 | tb->refcount++; | |
74 | } | |
75 | ||
76 | /** | |
77 | * fdisk_unref_table: | |
78 | * @tb: table pointer | |
79 | * | |
80 | * Decrements reference counter, on zero the @tb is automatically | |
81 | * deallocated. | |
82 | */ | |
83 | void fdisk_unref_table(struct fdisk_table *tb) | |
84 | { | |
85 | if (!tb) | |
86 | return; | |
87 | ||
88 | tb->refcount--; | |
89 | if (tb->refcount <= 0) { | |
90 | fdisk_reset_table(tb); | |
91 | ||
92 | DBG(TAB, ul_debugobj(tb, "free")); | |
93 | free(tb); | |
94 | } | |
95 | } | |
96 | ||
97 | /** | |
98 | * fdisk_table_is_empty: | |
99 | * @tb: pointer to tab | |
100 | * | |
101 | * Returns: 1 if the table is without filesystems, or 0. | |
102 | */ | |
103 | int fdisk_table_is_empty(struct fdisk_table *tb) | |
104 | { | |
105 | return tb == NULL || list_empty(&tb->parts) ? 1 : 0; | |
106 | } | |
107 | ||
108 | /** | |
109 | * fdisk_table_get_nents: | |
110 | * @tb: pointer to tab | |
111 | * | |
112 | * Returns: number of entries in table. | |
113 | */ | |
114 | size_t fdisk_table_get_nents(struct fdisk_table *tb) | |
115 | { | |
116 | return tb ? tb->nents : 0; | |
117 | } | |
118 | ||
119 | /** | |
120 | * fdisk_table_next_partition: | |
121 | * @tb: tab pointer | |
122 | * @itr: iterator | |
123 | * @pa: returns the next tab entry | |
124 | * | |
125 | * Returns: 0 on success, negative number in case of error or 1 at the end of list. | |
126 | * | |
127 | * Example: | |
128 | * <informalexample> | |
129 | * <programlisting> | |
130 | * while(fdisk_table_next_partition(tb, itr, &pa) == 0) { | |
131 | * ... | |
132 | * } | |
133 | * </programlisting> | |
134 | * </informalexample> | |
135 | */ | |
136 | int fdisk_table_next_partition( | |
137 | struct fdisk_table *tb, | |
138 | struct fdisk_iter *itr, | |
139 | struct fdisk_partition **pa) | |
140 | { | |
141 | int rc = 1; | |
142 | ||
143 | if (!tb || !itr || !pa) | |
144 | return -EINVAL; | |
145 | *pa = NULL; | |
146 | ||
147 | if (!itr->head) | |
148 | FDISK_ITER_INIT(itr, &tb->parts); | |
149 | if (itr->p != itr->head) { | |
150 | FDISK_ITER_ITERATE(itr, *pa, struct fdisk_partition, parts); | |
151 | rc = 0; | |
152 | } | |
153 | ||
154 | return rc; | |
155 | } | |
156 | ||
157 | /** | |
158 | * fdisk_table_get_partition: | |
159 | * @tb: tab pointer | |
160 | * @n: number of entry in table | |
161 | * | |
162 | * Returns: n-th entry from table or NULL | |
163 | */ | |
164 | struct fdisk_partition *fdisk_table_get_partition( | |
165 | struct fdisk_table *tb, | |
166 | size_t n) | |
167 | { | |
168 | struct fdisk_partition *pa = NULL; | |
169 | struct fdisk_iter itr; | |
170 | ||
171 | if (!tb) | |
172 | return NULL; | |
173 | ||
174 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); | |
175 | ||
176 | while (fdisk_table_next_partition(tb, &itr, &pa) == 0) { | |
177 | if (n == 0) | |
178 | return pa; | |
179 | n--; | |
180 | } | |
181 | ||
182 | return NULL; | |
183 | } | |
184 | ||
185 | /** | |
186 | * fdisk_table_get_partition_by_partno: | |
187 | * @tb: tab pointer | |
188 | * @partno: partition number | |
189 | * | |
190 | * Returns: partition with @partno or NULL. | |
191 | */ | |
192 | struct fdisk_partition *fdisk_table_get_partition_by_partno( | |
193 | struct fdisk_table *tb, | |
194 | size_t partno) | |
195 | { | |
196 | struct fdisk_partition *pa = NULL; | |
197 | struct fdisk_iter itr; | |
198 | ||
199 | if (!tb) | |
200 | return NULL; | |
201 | ||
202 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); | |
203 | ||
204 | while (fdisk_table_next_partition(tb, &itr, &pa) == 0) { | |
205 | if (pa->partno == partno) | |
206 | return pa; | |
207 | } | |
208 | ||
209 | return NULL; | |
210 | } | |
211 | ||
212 | /** | |
213 | * fdisk_table_add_partition | |
214 | * @tb: tab pointer | |
215 | * @pa: new entry | |
216 | * | |
217 | * Adds a new entry to table and increment @pa reference counter. Don't forget to | |
218 | * use fdisk_unref_partition() after fdisk_table_add_partition() if you want to keep | |
219 | * the @pa referenced by the table only. | |
220 | * | |
221 | * Returns: 0 on success or negative number in case of error. | |
222 | */ | |
223 | int fdisk_table_add_partition(struct fdisk_table *tb, struct fdisk_partition *pa) | |
224 | { | |
225 | if (!tb || !pa) | |
226 | return -EINVAL; | |
227 | ||
228 | if (!list_empty(&pa->parts)) | |
229 | return -EBUSY; | |
230 | ||
231 | fdisk_ref_partition(pa); | |
232 | list_add_tail(&pa->parts, &tb->parts); | |
233 | tb->nents++; | |
234 | ||
235 | DBG(TAB, ul_debugobj(tb, "add entry %p [start=%ju, end=%ju, size=%ju, %s %s %s]", | |
236 | pa, | |
237 | (uintmax_t) fdisk_partition_get_start(pa), | |
238 | fdisk_partition_has_end(pa) ? (uintmax_t) fdisk_partition_get_end(pa) : 0, | |
239 | fdisk_partition_has_size(pa) ? (uintmax_t) fdisk_partition_get_size(pa) : 0, | |
240 | fdisk_partition_is_freespace(pa) ? "freespace" : "", | |
241 | fdisk_partition_is_nested(pa) ? "nested" : "", | |
242 | fdisk_partition_is_container(pa) ? "container" : "primary")); | |
243 | return 0; | |
244 | } | |
245 | ||
246 | /* inserts @pa after @poz */ | |
247 | static int table_insert_partition( | |
248 | struct fdisk_table *tb, | |
249 | struct fdisk_partition *poz, | |
250 | struct fdisk_partition *pa) | |
251 | { | |
252 | assert(tb); | |
253 | assert(pa); | |
254 | ||
255 | fdisk_ref_partition(pa); | |
256 | if (poz) | |
257 | list_add(&pa->parts, &poz->parts); | |
258 | else | |
259 | list_add(&pa->parts, &tb->parts); | |
260 | tb->nents++; | |
261 | ||
262 | DBG(TAB, ul_debugobj(tb, "insert entry %p pre=%p [start=%ju, end=%ju, size=%ju, %s %s %s]", | |
263 | pa, poz ? poz : NULL, | |
264 | (uintmax_t) fdisk_partition_get_start(pa), | |
265 | (uintmax_t) fdisk_partition_get_end(pa), | |
266 | (uintmax_t) fdisk_partition_get_size(pa), | |
267 | fdisk_partition_is_freespace(pa) ? "freespace" : "", | |
268 | fdisk_partition_is_nested(pa) ? "nested" : "", | |
269 | fdisk_partition_is_container(pa) ? "container" : "")); | |
270 | return 0; | |
271 | } | |
272 | ||
273 | /** | |
274 | * fdisk_table_remove_partition | |
275 | * @tb: tab pointer | |
276 | * @pa: new entry | |
277 | * | |
278 | * Removes the @pa from the table and de-increment reference counter of the @pa. The | |
279 | * partition with zero reference counter will be deallocated. Don't forget to use | |
280 | * fdisk_ref_partition() before call fdisk_table_remove_partition() if you want | |
281 | * to use @pa later. | |
282 | * | |
283 | * Returns: 0 on success or negative number in case of error. | |
284 | */ | |
285 | int fdisk_table_remove_partition(struct fdisk_table *tb, struct fdisk_partition *pa) | |
286 | { | |
287 | if (!tb || !pa) | |
288 | return -EINVAL; | |
289 | ||
290 | DBG(TAB, ul_debugobj(tb, "remove entry %p", pa)); | |
291 | list_del(&pa->parts); | |
292 | INIT_LIST_HEAD(&pa->parts); | |
293 | ||
294 | fdisk_unref_partition(pa); | |
295 | tb->nents--; | |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
300 | /** | |
301 | * fdisk_get_partitions | |
302 | * @cxt: fdisk context | |
303 | * @tb: returns table | |
304 | * | |
305 | * This function adds partitions from disklabel to @table, it allocates a new | |
306 | * table if @table points to NULL. | |
307 | * | |
308 | * Returns: 0 on success, otherwise, a corresponding error. | |
309 | */ | |
310 | int fdisk_get_partitions(struct fdisk_context *cxt, struct fdisk_table **tb) | |
311 | { | |
312 | size_t i; | |
313 | ||
314 | if (!cxt || !cxt->label || !tb) | |
315 | return -EINVAL; | |
316 | if (!cxt->label->op->get_part) | |
317 | return -ENOSYS; | |
318 | ||
319 | DBG(CXT, ul_debugobj(cxt, " -- get table --")); | |
320 | ||
321 | if (!*tb && !(*tb = fdisk_new_table())) | |
322 | return -ENOMEM; | |
323 | ||
324 | for (i = 0; i < cxt->label->nparts_max; i++) { | |
325 | struct fdisk_partition *pa = NULL; | |
326 | ||
327 | if (fdisk_get_partition(cxt, i, &pa) != 0) | |
328 | continue; | |
329 | if (fdisk_partition_is_used(pa)) | |
330 | fdisk_table_add_partition(*tb, pa); | |
331 | fdisk_unref_partition(pa); | |
332 | } | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | void fdisk_debug_print_table(struct fdisk_table *tb) | |
338 | { | |
339 | struct fdisk_iter itr; | |
340 | struct fdisk_partition *pa; | |
341 | ||
342 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); | |
343 | while (fdisk_table_next_partition(tb, &itr, &pa) == 0) | |
344 | ul_debugobj(tb, "partition %p [partno=%zu, start=%ju, end=%ju, size=%ju%s%s%s] ", | |
345 | pa, pa->partno, | |
346 | (uintmax_t) fdisk_partition_get_start(pa), | |
347 | (uintmax_t) fdisk_partition_get_end(pa), | |
348 | (uintmax_t) fdisk_partition_get_size(pa), | |
349 | fdisk_partition_is_nested(pa) ? " nested" : "", | |
350 | fdisk_partition_is_freespace(pa) ? " freespace" : "", | |
351 | fdisk_partition_is_container(pa) ? " container" : ""); | |
352 | ||
353 | } | |
354 | ||
355 | ||
356 | typedef int (*fdisk_partcmp_t)(struct fdisk_partition *, struct fdisk_partition *); | |
357 | ||
358 | static int cmp_parts_wrapper(struct list_head *a, struct list_head *b, void *data) | |
359 | { | |
360 | struct fdisk_partition *pa = list_entry(a, struct fdisk_partition, parts), | |
361 | *pb = list_entry(b, struct fdisk_partition, parts); | |
362 | ||
363 | fdisk_partcmp_t cmp = (fdisk_partcmp_t) data; | |
364 | ||
365 | return cmp(pa, pb); | |
366 | } | |
367 | ||
368 | ||
369 | /** | |
370 | * fdisk_table_sort_partitions: | |
371 | * @tb: table | |
372 | * @cmp: compare function | |
373 | * | |
374 | * Sort partition in the table. | |
375 | * | |
376 | * Returns: 0 on success, <0 on error. | |
377 | */ | |
378 | int fdisk_table_sort_partitions(struct fdisk_table *tb, | |
379 | int (*cmp)(struct fdisk_partition *, | |
380 | struct fdisk_partition *)) | |
381 | { | |
382 | if (!tb) | |
383 | return -EINVAL; | |
384 | ||
385 | /* | |
386 | DBG(TAB, ul_debugobj(tb, "Before sort:")); | |
387 | ON_DBG(TAB, fdisk_debug_print_table(tb)); | |
388 | */ | |
389 | ||
390 | list_sort(&tb->parts, cmp_parts_wrapper, (void *) cmp); | |
391 | ||
392 | /* | |
393 | DBG(TAB, ul_debugobj(tb, "After sort:")); | |
394 | ON_DBG(TAB, fdisk_debug_print_table(tb)); | |
395 | */ | |
396 | ||
397 | return 0; | |
398 | } | |
399 | ||
400 | /* allocates a new freespace description */ | |
401 | static int new_freespace(struct fdisk_context *cxt, | |
402 | fdisk_sector_t start, | |
403 | fdisk_sector_t end, | |
404 | struct fdisk_partition *parent, | |
405 | struct fdisk_partition **pa) | |
406 | { | |
407 | fdisk_sector_t aligned_start, size; | |
408 | ||
409 | assert(cxt); | |
410 | assert(pa); | |
411 | ||
412 | *pa = NULL; | |
413 | ||
414 | if (start == end) | |
415 | return 0; | |
416 | ||
417 | assert(start >= cxt->first_lba); | |
418 | assert(end); | |
419 | assert(end > start); | |
420 | ||
421 | aligned_start = fdisk_align_lba_in_range(cxt, start, start, end); | |
422 | size = end - aligned_start + 1ULL; | |
423 | ||
424 | if (size == 0) { | |
425 | DBG(TAB, ul_debug("ignore freespace (aligned size is zero)")); | |
426 | return 0; | |
427 | } | |
428 | ||
429 | *pa = fdisk_new_partition(); | |
430 | if (!*pa) | |
431 | return -ENOMEM; | |
432 | ||
433 | (*pa)->freespace = 1; | |
434 | (*pa)->start = aligned_start; | |
435 | (*pa)->size = size; | |
436 | ||
437 | if (parent) | |
438 | (*pa)->parent_partno = parent->partno; | |
439 | return 0; | |
440 | } | |
441 | ||
442 | /* add freespace description to the right place within @tb */ | |
443 | static int table_add_freespace( | |
444 | struct fdisk_context *cxt, | |
445 | struct fdisk_table *tb, | |
446 | fdisk_sector_t start, | |
447 | fdisk_sector_t end, | |
448 | struct fdisk_partition *parent) | |
449 | { | |
450 | struct fdisk_partition *pa, *x, *real_parent = NULL, *best = NULL; | |
451 | struct fdisk_iter itr; | |
452 | int rc = 0; | |
453 | ||
454 | assert(tb); | |
455 | ||
456 | rc = new_freespace(cxt, start, end, parent, &pa); | |
457 | if (rc) | |
458 | return -ENOMEM; | |
459 | if (!pa) | |
460 | return 0; | |
461 | ||
462 | assert(fdisk_partition_has_start(pa)); | |
463 | assert(fdisk_partition_has_end(pa)); | |
464 | ||
465 | DBG(TAB, ul_debugobj(tb, "adding freespace")); | |
466 | ||
467 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); | |
468 | if (parent && fdisk_partition_has_partno(parent)) { | |
469 | while (fdisk_table_next_partition(tb, &itr, &x) == 0) { | |
470 | if (!fdisk_partition_has_partno(x)) | |
471 | continue; | |
472 | if (x->partno == parent->partno) { | |
473 | real_parent = x; | |
474 | break; | |
475 | } | |
476 | } | |
477 | if (!real_parent) { | |
478 | DBG(TAB, ul_debugobj(tb, "not found freespace parent (partno=%zu)", | |
479 | parent->partno)); | |
480 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); | |
481 | } | |
482 | } | |
483 | ||
484 | while (fdisk_table_next_partition(tb, &itr, &x) == 0) { | |
485 | fdisk_sector_t the_end, best_end = 0; | |
486 | ||
487 | if (!fdisk_partition_has_end(x)) | |
488 | continue; | |
489 | ||
490 | the_end = fdisk_partition_get_end(x); | |
491 | if (best) | |
492 | best_end = fdisk_partition_get_end(best); | |
493 | ||
494 | if (the_end < pa->start && (!best || best_end < the_end)) | |
495 | best = x; | |
496 | } | |
497 | ||
498 | if (!best && real_parent) | |
499 | best = real_parent; | |
500 | rc = table_insert_partition(tb, best, pa); | |
501 | ||
502 | fdisk_unref_partition(pa); | |
503 | ||
504 | DBG(TAB, ul_debugobj(tb, "adding freespace DONE [rc=%d]", rc)); | |
505 | return rc; | |
506 | } | |
507 | ||
508 | /* analyze @cont(ainer) in @parts and add all detected freespace into @tb, note | |
509 | * that @parts has to be sorted by partition starts */ | |
510 | static int check_container_freespace(struct fdisk_context *cxt, | |
511 | struct fdisk_table *parts, | |
512 | struct fdisk_table *tb, | |
513 | struct fdisk_partition *cont) | |
514 | { | |
515 | struct fdisk_iter itr; | |
516 | struct fdisk_partition *pa; | |
517 | fdisk_sector_t x, last, grain, lastplusoff; | |
518 | int rc = 0; | |
519 | ||
520 | assert(cxt); | |
521 | assert(parts); | |
522 | assert(tb); | |
523 | assert(cont); | |
524 | assert(fdisk_partition_has_start(cont)); | |
525 | ||
526 | DBG(TAB, ul_debugobj(tb, "analyze container 0x%p", cont)); | |
527 | ||
528 | last = fdisk_partition_get_start(cont); | |
529 | grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1; | |
530 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); | |
531 | ||
532 | DBG(CXT, ul_debugobj(cxt, "initialized: last=%ju, grain=%ju", | |
533 | (uintmax_t)last, (uintmax_t)grain)); | |
534 | ||
535 | while (fdisk_table_next_partition(parts, &itr, &pa) == 0) { | |
536 | ||
537 | DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju", | |
538 | pa->partno, (uintmax_t)pa->start)); | |
539 | ||
540 | if (!pa->used || !fdisk_partition_is_nested(pa) | |
541 | || !fdisk_partition_has_start(pa)) | |
542 | continue; | |
543 | ||
544 | DBG(CXT, ul_debugobj(cxt, "freespace container analyze: partno=%zu, start=%ju, end=%ju", | |
545 | pa->partno, | |
546 | (uintmax_t) fdisk_partition_get_start(pa), | |
547 | (uintmax_t) fdisk_partition_get_end(pa))); | |
548 | ||
549 | lastplusoff = last + cxt->first_lba; | |
550 | if (pa->start > lastplusoff && pa->start - lastplusoff > grain) | |
551 | rc = table_add_freespace(cxt, tb, lastplusoff, pa->start, cont); | |
552 | if (rc) | |
553 | goto done; | |
554 | last = fdisk_partition_get_end(pa); | |
555 | } | |
556 | ||
557 | /* free-space remaining in extended partition */ | |
558 | x = fdisk_partition_get_start(cont) + fdisk_partition_get_size(cont) - 1; | |
559 | lastplusoff = last + cxt->first_lba; | |
560 | if (lastplusoff < x && x - lastplusoff > grain) { | |
561 | DBG(TAB, ul_debugobj(tb, "add remaining space in container 0x%p", cont)); | |
562 | rc = table_add_freespace(cxt, tb, lastplusoff, x, cont); | |
563 | } | |
564 | ||
565 | done: | |
566 | DBG(TAB, ul_debugobj(tb, "analyze container 0x%p DONE [rc=%d]", cont, rc)); | |
567 | return rc; | |
568 | } | |
569 | ||
570 | ||
571 | /** | |
572 | * fdisk_get_freespaces | |
573 | * @cxt: fdisk context | |
574 | * @tb: returns table | |
575 | * | |
576 | * This function adds freespace (described by fdisk_partition) to @table, it | |
577 | * allocates a new table if the @table points to NULL. | |
578 | * | |
579 | * Note that free space smaller than grain (see fdisk_get_grain_size()) is | |
580 | * ignored. | |
581 | * | |
582 | * Returns: 0 on success, otherwise, a corresponding error. | |
583 | */ | |
584 | int fdisk_get_freespaces(struct fdisk_context *cxt, struct fdisk_table **tb) | |
585 | { | |
586 | int rc = 0; | |
587 | size_t nparts = 0; | |
588 | fdisk_sector_t last, grain; | |
589 | struct fdisk_table *parts = NULL; | |
590 | struct fdisk_partition *pa; | |
591 | struct fdisk_iter itr; | |
592 | ||
593 | DBG(CXT, ul_debugobj(cxt, "-- get freespace --")); | |
594 | ||
595 | if (!cxt || !cxt->label || !tb) | |
596 | return -EINVAL; | |
597 | if (!*tb && !(*tb = fdisk_new_table())) | |
598 | return -ENOMEM; | |
599 | ||
600 | rc = fdisk_get_partitions(cxt, &parts); | |
601 | if (rc) | |
602 | goto done; | |
603 | ||
604 | fdisk_table_sort_partitions(parts, fdisk_partition_cmp_start); | |
605 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); | |
606 | last = cxt->first_lba; | |
607 | grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1; | |
608 | ||
609 | DBG(CXT, ul_debugobj(cxt, "initialized: last=%ju, grain=%ju", | |
610 | (uintmax_t)last, (uintmax_t)grain)); | |
611 | ||
612 | /* analyze gaps between partitions */ | |
613 | while (rc == 0 && fdisk_table_next_partition(parts, &itr, &pa) == 0) { | |
614 | ||
615 | DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju", | |
616 | pa->partno, (uintmax_t)pa->start)); | |
617 | ||
618 | if (!pa->used || pa->wholedisk || fdisk_partition_is_nested(pa) | |
619 | || !fdisk_partition_has_start(pa)) | |
620 | continue; | |
621 | DBG(CXT, ul_debugobj(cxt, "freespace analyze: partno=%zu, start=%ju, end=%ju", | |
622 | pa->partno, | |
623 | (uintmax_t) fdisk_partition_get_start(pa), | |
624 | (uintmax_t) fdisk_partition_get_end(pa))); | |
625 | ||
626 | /* We ignore small free spaces (smaller than grain) to keep partitions | |
627 | * aligned, the exception is space before the first partition when | |
628 | * cxt->first_lba is aligned. */ | |
629 | if (last + grain < pa->start | |
630 | || (nparts == 0 && | |
631 | (fdisk_align_lba(cxt, last, FDISK_ALIGN_UP) < | |
632 | pa->start))) { | |
633 | rc = table_add_freespace(cxt, *tb, | |
634 | last + (nparts == 0 ? 0 : 1), | |
635 | pa->start - 1, NULL); | |
636 | } | |
637 | /* add gaps between logical partitions */ | |
638 | if (fdisk_partition_is_container(pa)) | |
639 | rc = check_container_freespace(cxt, parts, *tb, pa); | |
640 | ||
641 | if (fdisk_partition_has_end(pa)) { | |
642 | fdisk_sector_t pa_end = fdisk_partition_get_end(pa); | |
643 | if (pa_end > last) | |
644 | last = fdisk_partition_get_end(pa); | |
645 | } | |
646 | nparts++; | |
647 | } | |
648 | ||
649 | /* add free-space behind last partition to the end of the table (so | |
650 | * don't use table_add_freespace()) */ | |
651 | if (rc == 0 && last + grain < cxt->last_lba - 1) { | |
652 | DBG(CXT, ul_debugobj(cxt, "freespace behind last partition detected")); | |
653 | rc = new_freespace(cxt, | |
654 | last + (last > cxt->first_lba || nparts ? 1 : 0), | |
655 | cxt->last_lba, NULL, &pa); | |
656 | if (pa) { | |
657 | fdisk_table_add_partition(*tb, pa); | |
658 | fdisk_unref_partition(pa); | |
659 | } | |
660 | } | |
661 | ||
662 | done: | |
663 | fdisk_unref_table(parts); | |
664 | ||
665 | DBG(CXT, ul_debugobj(cxt, "get freespace DONE [rc=%d]", rc)); | |
666 | return rc; | |
667 | } | |
668 | ||
669 | /** | |
670 | * fdisk_table_wrong_order: | |
671 | * @tb: table | |
672 | * | |
673 | * Returns: 1 of the table is not in disk order | |
674 | */ | |
675 | int fdisk_table_wrong_order(struct fdisk_table *tb) | |
676 | { | |
677 | struct fdisk_partition *pa; | |
678 | struct fdisk_iter itr; | |
679 | fdisk_sector_t last = 0; | |
680 | ||
681 | DBG(TAB, ul_debugobj(tb, "wrong older check")); | |
682 | ||
683 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); | |
684 | while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) { | |
685 | if (!fdisk_partition_has_start(pa) || fdisk_partition_is_wholedisk(pa)) | |
686 | continue; | |
687 | if (pa->start < last) | |
688 | return 1; | |
689 | last = pa->start; | |
690 | } | |
691 | return 0; | |
692 | } | |
693 | ||
694 | /** | |
695 | * fdisk_apply_table: | |
696 | * @cxt: context | |
697 | * @tb: table | |
698 | * | |
699 | * Add partitions from table @tb to the in-memory disk label. See | |
700 | * fdisk_add_partition(), fdisk_delete_all_partitions(). The partitions | |
701 | * that does not define start (or does not follow the default start) | |
702 | * are ignored. | |
703 | * | |
704 | * Returns: 0 on success, <0 on error. | |
705 | */ | |
706 | int fdisk_apply_table(struct fdisk_context *cxt, struct fdisk_table *tb) | |
707 | { | |
708 | struct fdisk_partition *pa; | |
709 | struct fdisk_iter itr; | |
710 | int rc = 0; | |
711 | ||
712 | assert(cxt); | |
713 | assert(tb); | |
714 | ||
715 | DBG(TAB, ul_debugobj(tb, "applying to context %p", cxt)); | |
716 | ||
717 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); | |
718 | while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) { | |
719 | if (!fdisk_partition_has_start(pa) && !pa->start_follow_default) | |
720 | continue; | |
721 | rc = fdisk_add_partition(cxt, pa, NULL); | |
722 | if (rc) | |
723 | break; | |
724 | } | |
725 | ||
726 | return rc; | |
727 | } | |
728 | ||
729 | int fdisk_diff_tables(struct fdisk_table *a, struct fdisk_table *b, | |
730 | struct fdisk_iter *itr, | |
731 | struct fdisk_partition **res, int *change) | |
732 | { | |
733 | struct fdisk_partition *pa = NULL, *pb; | |
734 | int rc = 1; | |
735 | ||
736 | assert(itr); | |
737 | assert(res); | |
738 | assert(change); | |
739 | ||
740 | DBG(TAB, ul_debugobj(a, "table diff [new table=%p]", b)); | |
741 | ||
742 | if (a && (itr->head == NULL || itr->head == &a->parts)) { | |
743 | DBG(TAB, ul_debugobj(a, " scanning old table")); | |
744 | do { | |
745 | rc = fdisk_table_next_partition(a, itr, &pa); | |
746 | if (rc != 0) | |
747 | break; | |
748 | } while (!fdisk_partition_has_partno(pa)); | |
749 | } | |
750 | ||
751 | if (rc == 1 && b) { | |
752 | DBG(TAB, ul_debugobj(a, " scanning new table")); | |
753 | if (itr->head != &b->parts) { | |
754 | DBG(TAB, ul_debugobj(a, " initialize to TAB=%p", b)); | |
755 | fdisk_reset_iter(itr, FDISK_ITER_FORWARD); | |
756 | } | |
757 | ||
758 | while (fdisk_table_next_partition(b, itr, &pb) == 0) { | |
759 | if (!fdisk_partition_has_partno(pb)) | |
760 | continue; | |
761 | if (a == NULL || | |
762 | fdisk_table_get_partition_by_partno(a, pb->partno) == NULL) { | |
763 | DBG(TAB, ul_debugobj(a, " #%zu ADDED", pb->partno)); | |
764 | *change = FDISK_DIFF_ADDED; | |
765 | *res = pb; | |
766 | return 0; | |
767 | } | |
768 | } | |
769 | } | |
770 | ||
771 | if (rc) { | |
772 | DBG(TAB, ul_debugobj(a, "table diff done [rc=%d]", rc)); | |
773 | return rc; /* error or done */ | |
774 | } | |
775 | ||
776 | pb = fdisk_table_get_partition_by_partno(b, pa->partno); | |
777 | ||
778 | if (!pb) { | |
779 | DBG(TAB, ul_debugobj(a, " #%zu REMOVED", pa->partno)); | |
780 | *change = FDISK_DIFF_REMOVED; | |
781 | *res = pa; | |
782 | } else if (pb->start != pa->start) { | |
783 | DBG(TAB, ul_debugobj(a, " #%zu MOVED", pb->partno)); | |
784 | *change = FDISK_DIFF_MOVED; | |
785 | *res = pb; | |
786 | } else if (pb->size != pa->size) { | |
787 | DBG(TAB, ul_debugobj(a, " #%zu RESIZED", pb->partno)); | |
788 | *change = FDISK_DIFF_RESIZED; | |
789 | *res = pb; | |
790 | } else { | |
791 | DBG(TAB, ul_debugobj(a, " #%zu UNCHANGED", pb->partno)); | |
792 | *change = FDISK_DIFF_UNCHANGED; | |
793 | *res = pa; | |
794 | } | |
795 | return 0; | |
796 | } | |
797 |