/src/util-linux/libfdisk/src/table.c
Line | Count | Source |
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 | 3.65k | { |
25 | 3.65k | struct fdisk_table *tb = NULL; |
26 | | |
27 | 3.65k | tb = calloc(1, sizeof(*tb)); |
28 | 3.65k | if (!tb) |
29 | 0 | return NULL; |
30 | | |
31 | 3.65k | DBG(TAB, ul_debugobj(tb, "alloc")); |
32 | 3.65k | tb->refcount = 1; |
33 | 3.65k | INIT_LIST_HEAD(&tb->parts); |
34 | 3.65k | return tb; |
35 | 3.65k | } |
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 | 7.31k | { |
49 | 7.31k | if (!tb) |
50 | 0 | return -EINVAL; |
51 | | |
52 | 7.31k | DBG(TAB, ul_debugobj(tb, "reset")); |
53 | | |
54 | 25.2k | while (!list_empty(&tb->parts)) { |
55 | 17.9k | struct fdisk_partition *pa = list_entry(tb->parts.next, |
56 | 17.9k | struct fdisk_partition, parts); |
57 | 17.9k | fdisk_table_remove_partition(tb, pa); |
58 | 17.9k | } |
59 | | |
60 | 7.31k | tb->nents = 0; |
61 | 7.31k | return 0; |
62 | 7.31k | } |
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 | 0 | { |
72 | 0 | if (tb) |
73 | 0 | tb->refcount++; |
74 | 0 | } |
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 | 3.68k | { |
85 | 3.68k | if (!tb) |
86 | 31 | return; |
87 | | |
88 | 3.65k | tb->refcount--; |
89 | 3.65k | if (tb->refcount <= 0) { |
90 | 3.65k | fdisk_reset_table(tb); |
91 | | |
92 | 3.65k | DBG(TAB, ul_debugobj(tb, "free")); |
93 | 3.65k | free(tb); |
94 | 3.65k | } |
95 | 3.65k | } |
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 | 27.1k | { |
105 | 27.1k | return tb == NULL || list_empty(&tb->parts) ? 1 : 0; |
106 | 27.1k | } |
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 | 0 | { |
116 | 0 | return tb ? tb->nents : 0; |
117 | 0 | } |
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 | 19.8k | { |
141 | 19.8k | int rc = 1; |
142 | | |
143 | 19.8k | if (!tb || !itr || !pa) |
144 | 0 | return -EINVAL; |
145 | 19.8k | *pa = NULL; |
146 | | |
147 | 19.8k | if (!itr->head) |
148 | 1.91k | FDISK_ITER_INIT(itr, &tb->parts); |
149 | 19.8k | if (itr->p != itr->head) { |
150 | 17.9k | FDISK_ITER_ITERATE(itr, *pa, struct fdisk_partition, parts); |
151 | 17.9k | rc = 0; |
152 | 17.9k | } |
153 | | |
154 | 19.8k | return rc; |
155 | 19.8k | } |
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 | 0 | { |
168 | 0 | struct fdisk_partition *pa = NULL; |
169 | 0 | struct fdisk_iter itr; |
170 | |
|
171 | 0 | if (!tb) |
172 | 0 | return NULL; |
173 | | |
174 | 0 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); |
175 | |
|
176 | 0 | while (fdisk_table_next_partition(tb, &itr, &pa) == 0) { |
177 | 0 | if (n == 0) |
178 | 0 | return pa; |
179 | 0 | n--; |
180 | 0 | } |
181 | | |
182 | 0 | return NULL; |
183 | 0 | } |
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 | 0 | { |
196 | 0 | struct fdisk_partition *pa = NULL; |
197 | 0 | struct fdisk_iter itr; |
198 | |
|
199 | 0 | if (!tb) |
200 | 0 | return NULL; |
201 | | |
202 | 0 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); |
203 | |
|
204 | 0 | while (fdisk_table_next_partition(tb, &itr, &pa) == 0) { |
205 | 0 | if (pa->partno == partno) |
206 | 0 | return pa; |
207 | 0 | } |
208 | | |
209 | 0 | return NULL; |
210 | 0 | } |
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 | 17.9k | { |
225 | 17.9k | if (!tb || !pa) |
226 | 0 | return -EINVAL; |
227 | | |
228 | 17.9k | if (!list_empty(&pa->parts)) |
229 | 0 | return -EBUSY; |
230 | | |
231 | 17.9k | fdisk_ref_partition(pa); |
232 | 17.9k | list_add_tail(&pa->parts, &tb->parts); |
233 | 17.9k | tb->nents++; |
234 | | |
235 | 17.9k | DBG(TAB, ul_debugobj(tb, "add entry %p [start=%ju, end=%ju, size=%ju, %s %s %s]", |
236 | 17.9k | pa, |
237 | 17.9k | (uintmax_t) fdisk_partition_get_start(pa), |
238 | 17.9k | fdisk_partition_has_end(pa) ? (uintmax_t) fdisk_partition_get_end(pa) : 0, |
239 | 17.9k | fdisk_partition_has_size(pa) ? (uintmax_t) fdisk_partition_get_size(pa) : 0, |
240 | 17.9k | fdisk_partition_is_freespace(pa) ? "freespace" : "", |
241 | 17.9k | fdisk_partition_is_nested(pa) ? "nested" : "", |
242 | 17.9k | fdisk_partition_is_container(pa) ? "container" : "primary")); |
243 | 17.9k | return 0; |
244 | 17.9k | } |
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 | 0 | { |
252 | 0 | assert(tb); |
253 | 0 | assert(pa); |
254 | |
|
255 | 0 | fdisk_ref_partition(pa); |
256 | 0 | if (poz) |
257 | 0 | list_add(&pa->parts, &poz->parts); |
258 | 0 | else |
259 | 0 | list_add(&pa->parts, &tb->parts); |
260 | 0 | tb->nents++; |
261 | |
|
262 | 0 | DBG(TAB, ul_debugobj(tb, "insert entry %p pre=%p [start=%ju, end=%ju, size=%ju, %s %s %s]", |
263 | 0 | pa, poz ? poz : NULL, |
264 | 0 | (uintmax_t) fdisk_partition_get_start(pa), |
265 | 0 | (uintmax_t) fdisk_partition_get_end(pa), |
266 | 0 | (uintmax_t) fdisk_partition_get_size(pa), |
267 | 0 | fdisk_partition_is_freespace(pa) ? "freespace" : "", |
268 | 0 | fdisk_partition_is_nested(pa) ? "nested" : "", |
269 | 0 | fdisk_partition_is_container(pa) ? "container" : "")); |
270 | 0 | return 0; |
271 | 0 | } |
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 | 17.9k | { |
287 | 17.9k | if (!tb || !pa) |
288 | 0 | return -EINVAL; |
289 | | |
290 | 17.9k | DBG(TAB, ul_debugobj(tb, "remove entry %p", pa)); |
291 | 17.9k | list_del(&pa->parts); |
292 | 17.9k | INIT_LIST_HEAD(&pa->parts); |
293 | | |
294 | 17.9k | fdisk_unref_partition(pa); |
295 | 17.9k | tb->nents--; |
296 | | |
297 | 17.9k | return 0; |
298 | 17.9k | } |
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 | 0 | { |
312 | 0 | size_t i; |
313 | |
|
314 | 0 | if (!cxt || !cxt->label || !tb) |
315 | 0 | return -EINVAL; |
316 | 0 | if (!cxt->label->op->get_part) |
317 | 0 | return -ENOSYS; |
318 | | |
319 | 0 | DBG(CXT, ul_debugobj(cxt, " -- get table --")); |
320 | |
|
321 | 0 | if (!*tb && !(*tb = fdisk_new_table())) |
322 | 0 | return -ENOMEM; |
323 | | |
324 | 0 | for (i = 0; i < cxt->label->nparts_max; i++) { |
325 | 0 | struct fdisk_partition *pa = NULL; |
326 | |
|
327 | 0 | if (fdisk_get_partition(cxt, i, &pa) != 0) |
328 | 0 | continue; |
329 | 0 | if (fdisk_partition_is_used(pa)) |
330 | 0 | fdisk_table_add_partition(*tb, pa); |
331 | 0 | fdisk_unref_partition(pa); |
332 | 0 | } |
333 | |
|
334 | 0 | return 0; |
335 | 0 | } |
336 | | |
337 | | void fdisk_debug_print_table(struct fdisk_table *tb) |
338 | 0 | { |
339 | 0 | struct fdisk_iter itr; |
340 | 0 | struct fdisk_partition *pa; |
341 | |
|
342 | 0 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); |
343 | 0 | while (fdisk_table_next_partition(tb, &itr, &pa) == 0) |
344 | 0 | ul_debugobj(tb, "partition %p [partno=%zu, start=%ju, end=%ju, size=%ju%s%s%s] ", |
345 | 0 | pa, pa->partno, |
346 | 0 | (uintmax_t) fdisk_partition_get_start(pa), |
347 | 0 | (uintmax_t) fdisk_partition_get_end(pa), |
348 | 0 | (uintmax_t) fdisk_partition_get_size(pa), |
349 | 0 | fdisk_partition_is_nested(pa) ? " nested" : "", |
350 | 0 | fdisk_partition_is_freespace(pa) ? " freespace" : "", |
351 | 0 | fdisk_partition_is_container(pa) ? " container" : ""); |
352 | |
|
353 | 0 | } |
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 | 0 | { |
360 | 0 | struct fdisk_partition *pa = list_entry(a, struct fdisk_partition, parts), |
361 | 0 | *pb = list_entry(b, struct fdisk_partition, parts); |
362 | |
|
363 | 0 | fdisk_partcmp_t cmp = (fdisk_partcmp_t) data; |
364 | |
|
365 | 0 | return cmp(pa, pb); |
366 | 0 | } |
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 | 0 | { |
382 | 0 | if (!tb) |
383 | 0 | return -EINVAL; |
384 | | |
385 | | /* |
386 | | DBG(TAB, ul_debugobj(tb, "Before sort:")); |
387 | | ON_DBG(TAB, fdisk_debug_print_table(tb)); |
388 | | */ |
389 | | |
390 | 0 | 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 | 0 | return 0; |
398 | 0 | } |
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 | 0 | { |
407 | 0 | fdisk_sector_t aligned_start, size; |
408 | |
|
409 | 0 | assert(cxt); |
410 | 0 | assert(pa); |
411 | |
|
412 | 0 | *pa = NULL; |
413 | |
|
414 | 0 | if (start == end) |
415 | 0 | return 0; |
416 | | |
417 | 0 | assert(start >= cxt->first_lba); |
418 | 0 | assert(end); |
419 | 0 | assert(end > start); |
420 | |
|
421 | 0 | aligned_start = fdisk_align_lba_in_range(cxt, start, start, end); |
422 | 0 | size = end - aligned_start + 1ULL; |
423 | |
|
424 | 0 | if (size == 0) { |
425 | 0 | DBG(TAB, ul_debug("ignore freespace (aligned size is zero)")); |
426 | 0 | return 0; |
427 | 0 | } |
428 | | |
429 | 0 | *pa = fdisk_new_partition(); |
430 | 0 | if (!*pa) |
431 | 0 | return -ENOMEM; |
432 | | |
433 | 0 | (*pa)->freespace = 1; |
434 | 0 | (*pa)->start = aligned_start; |
435 | 0 | (*pa)->size = size; |
436 | |
|
437 | 0 | if (parent) |
438 | 0 | (*pa)->parent_partno = parent->partno; |
439 | 0 | return 0; |
440 | 0 | } |
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 | 0 | { |
450 | 0 | struct fdisk_partition *pa, *x, *real_parent = NULL, *best = NULL; |
451 | 0 | struct fdisk_iter itr; |
452 | 0 | int rc = 0; |
453 | |
|
454 | 0 | assert(tb); |
455 | |
|
456 | 0 | rc = new_freespace(cxt, start, end, parent, &pa); |
457 | 0 | if (rc) |
458 | 0 | return -ENOMEM; |
459 | 0 | if (!pa) |
460 | 0 | return 0; |
461 | | |
462 | 0 | assert(fdisk_partition_has_start(pa)); |
463 | 0 | assert(fdisk_partition_has_end(pa)); |
464 | |
|
465 | 0 | DBG(TAB, ul_debugobj(tb, "adding freespace")); |
466 | |
|
467 | 0 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); |
468 | 0 | if (parent && fdisk_partition_has_partno(parent)) { |
469 | 0 | while (fdisk_table_next_partition(tb, &itr, &x) == 0) { |
470 | 0 | if (!fdisk_partition_has_partno(x)) |
471 | 0 | continue; |
472 | 0 | if (x->partno == parent->partno) { |
473 | 0 | real_parent = x; |
474 | 0 | break; |
475 | 0 | } |
476 | 0 | } |
477 | 0 | if (!real_parent) { |
478 | 0 | DBG(TAB, ul_debugobj(tb, "not found freespace parent (partno=%zu)", |
479 | 0 | parent->partno)); |
480 | 0 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); |
481 | 0 | } |
482 | 0 | } |
483 | |
|
484 | 0 | while (fdisk_table_next_partition(tb, &itr, &x) == 0) { |
485 | 0 | fdisk_sector_t the_end, best_end = 0; |
486 | |
|
487 | 0 | if (!fdisk_partition_has_end(x)) |
488 | 0 | continue; |
489 | | |
490 | 0 | the_end = fdisk_partition_get_end(x); |
491 | 0 | if (best) |
492 | 0 | best_end = fdisk_partition_get_end(best); |
493 | |
|
494 | 0 | if (the_end < pa->start && (!best || best_end < the_end)) |
495 | 0 | best = x; |
496 | 0 | } |
497 | |
|
498 | 0 | if (!best && real_parent) |
499 | 0 | best = real_parent; |
500 | 0 | rc = table_insert_partition(tb, best, pa); |
501 | |
|
502 | 0 | fdisk_unref_partition(pa); |
503 | |
|
504 | 0 | DBG(TAB, ul_debugobj(tb, "adding freespace DONE [rc=%d]", rc)); |
505 | 0 | return rc; |
506 | 0 | } |
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 | 0 | { |
515 | 0 | struct fdisk_iter itr; |
516 | 0 | struct fdisk_partition *pa; |
517 | 0 | fdisk_sector_t x, last, grain, lastplusoff; |
518 | 0 | int rc = 0; |
519 | |
|
520 | 0 | assert(cxt); |
521 | 0 | assert(parts); |
522 | 0 | assert(tb); |
523 | 0 | assert(cont); |
524 | 0 | assert(fdisk_partition_has_start(cont)); |
525 | |
|
526 | 0 | DBG(TAB, ul_debugobj(tb, "analyze container 0x%p", cont)); |
527 | |
|
528 | 0 | last = fdisk_partition_get_start(cont); |
529 | 0 | grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1; |
530 | 0 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); |
531 | |
|
532 | 0 | DBG(CXT, ul_debugobj(cxt, "initialized: last=%ju, grain=%ju", |
533 | 0 | (uintmax_t)last, (uintmax_t)grain)); |
534 | |
|
535 | 0 | while (fdisk_table_next_partition(parts, &itr, &pa) == 0) { |
536 | |
|
537 | 0 | DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju", |
538 | 0 | pa->partno, (uintmax_t)pa->start)); |
539 | |
|
540 | 0 | if (!pa->used || !fdisk_partition_is_nested(pa) |
541 | 0 | || !fdisk_partition_has_start(pa)) |
542 | 0 | continue; |
543 | | |
544 | 0 | DBG(CXT, ul_debugobj(cxt, "freespace container analyze: partno=%zu, start=%ju, end=%ju", |
545 | 0 | pa->partno, |
546 | 0 | (uintmax_t) fdisk_partition_get_start(pa), |
547 | 0 | (uintmax_t) fdisk_partition_get_end(pa))); |
548 | |
|
549 | 0 | lastplusoff = last + cxt->first_lba; |
550 | 0 | if (pa->start > lastplusoff && pa->start - lastplusoff > grain) |
551 | 0 | rc = table_add_freespace(cxt, tb, lastplusoff, pa->start, cont); |
552 | 0 | if (rc) |
553 | 0 | goto done; |
554 | 0 | last = fdisk_partition_get_end(pa); |
555 | 0 | } |
556 | | |
557 | | /* free-space remaining in extended partition */ |
558 | 0 | x = fdisk_partition_get_start(cont) + fdisk_partition_get_size(cont) - 1; |
559 | 0 | lastplusoff = last + cxt->first_lba; |
560 | 0 | if (lastplusoff < x && x - lastplusoff > grain) { |
561 | 0 | DBG(TAB, ul_debugobj(tb, "add remaining space in container 0x%p", cont)); |
562 | 0 | rc = table_add_freespace(cxt, tb, lastplusoff, x, cont); |
563 | 0 | } |
564 | |
|
565 | 0 | done: |
566 | 0 | DBG(TAB, ul_debugobj(tb, "analyze container 0x%p DONE [rc=%d]", cont, rc)); |
567 | 0 | return rc; |
568 | 0 | } |
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 | 0 | { |
586 | 0 | int rc = 0; |
587 | 0 | size_t nparts = 0; |
588 | 0 | fdisk_sector_t last, grain; |
589 | 0 | struct fdisk_table *parts = NULL; |
590 | 0 | struct fdisk_partition *pa; |
591 | 0 | struct fdisk_iter itr; |
592 | |
|
593 | 0 | DBG(CXT, ul_debugobj(cxt, "-- get freespace --")); |
594 | |
|
595 | 0 | if (!cxt || !cxt->label || !tb) |
596 | 0 | return -EINVAL; |
597 | 0 | if (!*tb && !(*tb = fdisk_new_table())) |
598 | 0 | return -ENOMEM; |
599 | | |
600 | 0 | rc = fdisk_get_partitions(cxt, &parts); |
601 | 0 | if (rc) |
602 | 0 | goto done; |
603 | | |
604 | 0 | fdisk_table_sort_partitions(parts, fdisk_partition_cmp_start); |
605 | 0 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); |
606 | 0 | last = cxt->first_lba; |
607 | 0 | grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1; |
608 | |
|
609 | 0 | DBG(CXT, ul_debugobj(cxt, "initialized: last=%ju, grain=%ju", |
610 | 0 | (uintmax_t)last, (uintmax_t)grain)); |
611 | | |
612 | | /* analyze gaps between partitions */ |
613 | 0 | while (rc == 0 && fdisk_table_next_partition(parts, &itr, &pa) == 0) { |
614 | |
|
615 | 0 | DBG(CXT, ul_debugobj(cxt, "partno=%zu, start=%ju", |
616 | 0 | pa->partno, (uintmax_t)pa->start)); |
617 | |
|
618 | 0 | if (!pa->used || pa->wholedisk || fdisk_partition_is_nested(pa) |
619 | 0 | || !fdisk_partition_has_start(pa)) |
620 | 0 | continue; |
621 | 0 | DBG(CXT, ul_debugobj(cxt, "freespace analyze: partno=%zu, start=%ju, end=%ju", |
622 | 0 | pa->partno, |
623 | 0 | (uintmax_t) fdisk_partition_get_start(pa), |
624 | 0 | (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 | 0 | if (last + grain < pa->start |
630 | 0 | || (nparts == 0 && |
631 | 0 | (fdisk_align_lba(cxt, last, FDISK_ALIGN_UP) < |
632 | 0 | pa->start))) { |
633 | 0 | rc = table_add_freespace(cxt, *tb, |
634 | 0 | last + (nparts == 0 ? 0 : 1), |
635 | 0 | pa->start - 1, NULL); |
636 | 0 | } |
637 | | /* add gaps between logical partitions */ |
638 | 0 | if (fdisk_partition_is_container(pa)) |
639 | 0 | rc = check_container_freespace(cxt, parts, *tb, pa); |
640 | |
|
641 | 0 | if (fdisk_partition_has_end(pa)) { |
642 | 0 | fdisk_sector_t pa_end = fdisk_partition_get_end(pa); |
643 | 0 | if (pa_end > last) |
644 | 0 | last = fdisk_partition_get_end(pa); |
645 | 0 | } |
646 | 0 | nparts++; |
647 | 0 | } |
648 | | |
649 | | /* add free-space behind last partition to the end of the table (so |
650 | | * don't use table_add_freespace()) */ |
651 | 0 | if (rc == 0 && last + grain < cxt->last_lba - 1) { |
652 | 0 | DBG(CXT, ul_debugobj(cxt, "freespace behind last partition detected")); |
653 | 0 | rc = new_freespace(cxt, |
654 | 0 | last + (last > cxt->first_lba || nparts ? 1 : 0), |
655 | 0 | cxt->last_lba, NULL, &pa); |
656 | 0 | if (pa) { |
657 | 0 | fdisk_table_add_partition(*tb, pa); |
658 | 0 | fdisk_unref_partition(pa); |
659 | 0 | } |
660 | 0 | } |
661 | |
|
662 | 0 | done: |
663 | 0 | fdisk_unref_table(parts); |
664 | |
|
665 | 0 | DBG(CXT, ul_debugobj(cxt, "get freespace DONE [rc=%d]", rc)); |
666 | 0 | return rc; |
667 | 0 | } |
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 | 0 | { |
677 | 0 | struct fdisk_partition *pa; |
678 | 0 | struct fdisk_iter itr; |
679 | 0 | fdisk_sector_t last = 0; |
680 | |
|
681 | 0 | DBG(TAB, ul_debugobj(tb, "wrong older check")); |
682 | |
|
683 | 0 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); |
684 | 0 | while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) { |
685 | 0 | if (!fdisk_partition_has_start(pa) || fdisk_partition_is_wholedisk(pa)) |
686 | 0 | continue; |
687 | 0 | if (pa->start < last) |
688 | 0 | return 1; |
689 | 0 | last = pa->start; |
690 | 0 | } |
691 | 0 | return 0; |
692 | 0 | } |
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 | 0 | { |
708 | 0 | struct fdisk_partition *pa; |
709 | 0 | struct fdisk_iter itr; |
710 | 0 | int rc = 0; |
711 | |
|
712 | 0 | assert(cxt); |
713 | 0 | assert(tb); |
714 | |
|
715 | 0 | DBG(TAB, ul_debugobj(tb, "applying to context %p", cxt)); |
716 | |
|
717 | 0 | fdisk_reset_iter(&itr, FDISK_ITER_FORWARD); |
718 | 0 | while (tb && fdisk_table_next_partition(tb, &itr, &pa) == 0) { |
719 | 0 | if (!fdisk_partition_has_start(pa) && !pa->start_follow_default) |
720 | 0 | continue; |
721 | 0 | rc = fdisk_add_partition(cxt, pa, NULL); |
722 | 0 | if (rc) |
723 | 0 | break; |
724 | 0 | } |
725 | |
|
726 | 0 | return rc; |
727 | 0 | } |
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 | 0 | { |
733 | 0 | struct fdisk_partition *pa = NULL, *pb; |
734 | 0 | int rc = 1; |
735 | |
|
736 | 0 | assert(itr); |
737 | 0 | assert(res); |
738 | 0 | assert(change); |
739 | |
|
740 | 0 | DBG(TAB, ul_debugobj(a, "table diff [new table=%p]", b)); |
741 | |
|
742 | 0 | if (a && (itr->head == NULL || itr->head == &a->parts)) { |
743 | 0 | DBG(TAB, ul_debugobj(a, " scanning old table")); |
744 | 0 | do { |
745 | 0 | rc = fdisk_table_next_partition(a, itr, &pa); |
746 | 0 | if (rc != 0) |
747 | 0 | break; |
748 | 0 | } while (!fdisk_partition_has_partno(pa)); |
749 | 0 | } |
750 | |
|
751 | 0 | if (rc == 1 && b) { |
752 | 0 | DBG(TAB, ul_debugobj(a, " scanning new table")); |
753 | 0 | if (itr->head != &b->parts) { |
754 | 0 | DBG(TAB, ul_debugobj(a, " initialize to TAB=%p", b)); |
755 | 0 | fdisk_reset_iter(itr, FDISK_ITER_FORWARD); |
756 | 0 | } |
757 | |
|
758 | 0 | while (fdisk_table_next_partition(b, itr, &pb) == 0) { |
759 | 0 | if (!fdisk_partition_has_partno(pb)) |
760 | 0 | continue; |
761 | 0 | if (a == NULL || |
762 | 0 | fdisk_table_get_partition_by_partno(a, pb->partno) == NULL) { |
763 | 0 | DBG(TAB, ul_debugobj(a, " #%zu ADDED", pb->partno)); |
764 | 0 | *change = FDISK_DIFF_ADDED; |
765 | 0 | *res = pb; |
766 | 0 | return 0; |
767 | 0 | } |
768 | 0 | } |
769 | 0 | } |
770 | | |
771 | 0 | if (rc) { |
772 | 0 | DBG(TAB, ul_debugobj(a, "table diff done [rc=%d]", rc)); |
773 | 0 | return rc; /* error or done */ |
774 | 0 | } |
775 | | |
776 | 0 | pb = fdisk_table_get_partition_by_partno(b, pa->partno); |
777 | |
|
778 | 0 | if (!pb) { |
779 | 0 | DBG(TAB, ul_debugobj(a, " #%zu REMOVED", pa->partno)); |
780 | 0 | *change = FDISK_DIFF_REMOVED; |
781 | 0 | *res = pa; |
782 | 0 | } else if (pb->start != pa->start) { |
783 | 0 | DBG(TAB, ul_debugobj(a, " #%zu MOVED", pb->partno)); |
784 | 0 | *change = FDISK_DIFF_MOVED; |
785 | 0 | *res = pb; |
786 | 0 | } else if (pb->size != pa->size) { |
787 | 0 | DBG(TAB, ul_debugobj(a, " #%zu RESIZED", pb->partno)); |
788 | 0 | *change = FDISK_DIFF_RESIZED; |
789 | 0 | *res = pb; |
790 | 0 | } else { |
791 | | DBG(TAB, ul_debugobj(a, " #%zu UNCHANGED", pb->partno)); |
792 | 0 | *change = FDISK_DIFF_UNCHANGED; |
793 | 0 | *res = pa; |
794 | 0 | } |
795 | 0 | return 0; |
796 | 0 | } |
797 | | |