]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libfdisk/src/context.c
Merge branch 'maybe-for-v2.32' of https://github.com/rudimeier/util-linux
[thirdparty/util-linux.git] / libfdisk / src / context.c
1 #ifdef HAVE_LIBBLKID
2 # include <blkid.h>
3 #endif
4
5 #include "blkdev.h"
6 #ifdef __linux__
7 # include "partx.h"
8 #endif
9 #include "loopdev.h"
10 #include "fdiskP.h"
11
12
13 /**
14 * SECTION: context
15 * @title: Context
16 * @short_description: stores info about device, labels etc.
17 *
18 * The library distinguish between three types of partitioning objects.
19 *
20 * on-disk data
21 * - disk label specific
22 * - probed and read by disklabel drivers when assign device to the context
23 * or when switch to another disk label type
24 * - only fdisk_write_disklabel() modify on-disk data
25 *
26 * in-memory data
27 * - generic data and disklabel specific data stored in struct fdisk_label
28 * - all partitioning operations are based on in-memory data only
29 *
30 * struct fdisk_partition
31 * - provides abstraction to present partitions to users
32 * - fdisk_partition is possible to gather to fdisk_table container
33 * - used as unified template for new partitions
34 * - the struct fdisk_partition is always completely independent object and
35 * any change to the object has no effect to in-memory (or on-disk) label data
36 */
37
38 /**
39 * fdisk_new_context:
40 *
41 * Returns: newly allocated libfdisk handler
42 */
43 struct fdisk_context *fdisk_new_context(void)
44 {
45 struct fdisk_context *cxt;
46
47 cxt = calloc(1, sizeof(*cxt));
48 if (!cxt)
49 return NULL;
50
51 DBG(CXT, ul_debugobj(cxt, "alloc"));
52 cxt->dev_fd = -1;
53 cxt->refcount = 1;
54
55 INIT_LIST_HEAD(&cxt->wipes);
56
57 /*
58 * Allocate label specific structs.
59 *
60 * This is necessary (for example) to store label specific
61 * context setting.
62 */
63 cxt->labels[ cxt->nlabels++ ] = fdisk_new_gpt_label(cxt);
64 cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
65 cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
66 cxt->labels[ cxt->nlabels++ ] = fdisk_new_sgi_label(cxt);
67 cxt->labels[ cxt->nlabels++ ] = fdisk_new_sun_label(cxt);
68
69 bindtextdomain(LIBFDISK_TEXTDOMAIN, LOCALEDIR);
70
71 return cxt;
72 }
73
74 static int init_nested_from_parent(struct fdisk_context *cxt, int isnew)
75 {
76 struct fdisk_context *parent;
77
78 assert(cxt);
79 assert(cxt->parent);
80
81 parent = cxt->parent;
82
83 cxt->alignment_offset = parent->alignment_offset;
84 cxt->ask_cb = parent->ask_cb;
85 cxt->ask_data = parent->ask_data;
86 cxt->dev_fd = parent->dev_fd;
87 cxt->first_lba = parent->first_lba;
88 cxt->firstsector_bufsz = parent->firstsector_bufsz;
89 cxt->firstsector = parent->firstsector;
90 cxt->geom = parent->geom;
91 cxt->grain = parent->grain;
92 cxt->io_size = parent->io_size;
93 cxt->last_lba = parent->last_lba;
94 cxt->min_io_size = parent->min_io_size;
95 cxt->optimal_io_size = parent->optimal_io_size;
96 cxt->phy_sector_size = parent->phy_sector_size;
97 cxt->readonly = parent->readonly;
98 cxt->script = parent->script;
99 fdisk_ref_script(cxt->script);
100 cxt->sector_size = parent->sector_size;
101 cxt->total_sectors = parent->total_sectors;
102 cxt->user_geom = parent->user_geom;
103 cxt->user_log_sector = parent->user_log_sector;
104 cxt->user_pyh_sector = parent->user_pyh_sector;
105
106 /* parent <--> nested independent setting, initialize for new nested
107 * contexts only */
108 if (isnew) {
109 cxt->listonly = parent->listonly;
110 cxt->display_details = parent->display_details;
111 cxt->display_in_cyl_units = parent->display_in_cyl_units;
112 cxt->protect_bootbits = parent->protect_bootbits;
113 }
114
115 free(cxt->dev_path);
116 cxt->dev_path = NULL;
117
118 if (parent->dev_path) {
119 cxt->dev_path = strdup(parent->dev_path);
120 if (!cxt->dev_path)
121 return -ENOMEM;
122 }
123
124 INIT_LIST_HEAD(&cxt->wipes);
125
126 return 0;
127 }
128
129 /**
130 * fdisk_new_nested_context:
131 * @parent: parental context
132 * @name: optional label name (e.g. "bsd")
133 *
134 * Create a new nested fdisk context for nested disk labels (e.g. BSD or PMBR).
135 * The function also probes for the nested label on the device if device is
136 * already assigned to parent.
137 *
138 * The new context is initialized according to @parent and both context shares
139 * some settings and file descriptor to the device. The child propagate some
140 * changes (like fdisk_assign_device()) to parent, but it does not work
141 * vice-versa. The behavior is undefined if you assign another device to
142 * parent.
143 *
144 * Returns: new context for nested partition table.
145 */
146 struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent,
147 const char *name)
148 {
149 struct fdisk_context *cxt;
150 struct fdisk_label *lb = NULL;
151
152 assert(parent);
153
154 cxt = calloc(1, sizeof(*cxt));
155 if (!cxt)
156 return NULL;
157
158 DBG(CXT, ul_debugobj(parent, "alloc nested [%p] [name=%s]", cxt, name));
159 cxt->refcount = 1;
160
161 fdisk_ref_context(parent);
162 cxt->parent = parent;
163
164 if (init_nested_from_parent(cxt, 1) != 0) {
165 cxt->parent = NULL;
166 fdisk_unref_context(cxt);
167 return NULL;
168 }
169
170 if (name) {
171 if (strcasecmp(name, "bsd") == 0)
172 lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
173 else if (strcasecmp(name, "dos") == 0 || strcasecmp(name, "mbr") == 0)
174 lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
175 }
176
177 if (lb && parent->dev_fd >= 0) {
178 DBG(CXT, ul_debugobj(cxt, "probing for nested %s", lb->name));
179
180 cxt->label = lb;
181
182 if (lb->op->probe(cxt) == 1)
183 __fdisk_switch_label(cxt, lb);
184 else {
185 DBG(CXT, ul_debugobj(cxt, "not found %s label", lb->name));
186 if (lb->op->deinit)
187 lb->op->deinit(lb);
188 cxt->label = NULL;
189 }
190 }
191
192 return cxt;
193 }
194
195
196 /**
197 * fdisk_ref_context:
198 * @cxt: context pointer
199 *
200 * Increments reference counter.
201 */
202 void fdisk_ref_context(struct fdisk_context *cxt)
203 {
204 if (cxt)
205 cxt->refcount++;
206 }
207
208 /**
209 * fdisk_get_label:
210 * @cxt: context instance
211 * @name: label name (e.g. "gpt")
212 *
213 * If no @name specified then returns the current context label.
214 *
215 * The label is allocated and maintained within the context #cxt. There is
216 * nothing like reference counting for labels, you cannot deallocate the
217 * label.
218 *
219 * Returns: label struct or NULL in case of error.
220 */
221 struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name)
222 {
223 size_t i;
224
225 assert(cxt);
226
227 if (!name)
228 return cxt->label;
229 else if (strcasecmp(name, "mbr") == 0)
230 name = "dos";
231
232 for (i = 0; i < cxt->nlabels; i++)
233 if (cxt->labels[i]
234 && strcasecmp(cxt->labels[i]->name, name) == 0)
235 return cxt->labels[i];
236
237 DBG(CXT, ul_debugobj(cxt, "failed to found %s label driver", name));
238 return NULL;
239 }
240
241 /**
242 * fdisk_next_label:
243 * @cxt: context instance
244 * @lb: returns pointer to the next label
245 *
246 * <informalexample>
247 * <programlisting>
248 * // print all supported labels
249 * struct fdisk_context *cxt = fdisk_new_context();
250 * struct fdisk_label *lb = NULL;
251 *
252 * while (fdisk_next_label(cxt, &lb) == 0)
253 * print("label name: %s\n", fdisk_label_get_name(lb));
254 * fdisk_unref_context(cxt);
255 * </programlisting>
256 * </informalexample>
257 *
258 * Returns: <0 in case of error, 0 on success, 1 at the end.
259 */
260 int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb)
261 {
262 size_t i;
263 struct fdisk_label *res = NULL;
264
265 if (!lb || !cxt)
266 return -EINVAL;
267
268 if (!*lb)
269 res = cxt->labels[0];
270 else {
271 for (i = 1; i < cxt->nlabels; i++) {
272 if (*lb == cxt->labels[i - 1]) {
273 res = cxt->labels[i];
274 break;
275 }
276 }
277 }
278
279 *lb = res;
280 return res ? 0 : 1;
281 }
282
283 /**
284 * fdisk_get_nlabels:
285 * @cxt: context
286 *
287 * Returns: number of supported label types
288 */
289 size_t fdisk_get_nlabels(struct fdisk_context *cxt)
290 {
291 return cxt ? cxt->nlabels : 0;
292 }
293
294 int __fdisk_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb)
295 {
296 if (!lb || !cxt)
297 return -EINVAL;
298 if (lb->disabled) {
299 DBG(CXT, ul_debugobj(cxt, "*** attempt to switch to disabled label %s -- ignore!", lb->name));
300 return -EINVAL;
301 }
302 cxt->label = lb;
303 DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name));
304
305 fdisk_apply_label_device_properties(cxt);
306 return 0;
307 }
308
309 /**
310 * fdisk_has_label:
311 * @cxt: fdisk context
312 *
313 * Returns: return 1 if there is label on the device.
314 */
315 int fdisk_has_label(struct fdisk_context *cxt)
316 {
317 return cxt && cxt->label;
318 }
319
320 /**
321 * fdisk_has_protected_bootbits:
322 * @cxt: fdisk context
323 *
324 * Returns: return 1 if boot bits protection enabled.
325 */
326 int fdisk_has_protected_bootbits(struct fdisk_context *cxt)
327 {
328 return cxt && cxt->protect_bootbits;
329 }
330
331 /**
332 * fdisk_enable_bootbits_protection:
333 * @cxt: fdisk context
334 * @enable: 1 or 0
335 *
336 * The library zeroizes all the first sector when create a new disk label by
337 * default. This function allows to control this behavior. For now it's
338 * supported for MBR and GPT.
339 *
340 * Returns: 0 on success, < 0 on error.
341 */
342 int fdisk_enable_bootbits_protection(struct fdisk_context *cxt, int enable)
343 {
344 if (!cxt)
345 return -EINVAL;
346 cxt->protect_bootbits = enable ? 1 : 0;
347 return 0;
348 }
349 /**
350 * fdisk_disable_dialogs
351 * @cxt: fdisk context
352 * @enable: 1 or 0
353 *
354 * The library uses dialog driven partitioning by default.
355 *
356 * Returns: 0 on success, < 0 on error.
357 *
358 * Since: 2.31
359 */
360 int fdisk_disable_dialogs(struct fdisk_context *cxt, int disable)
361 {
362 if (!cxt)
363 return -EINVAL;
364
365 cxt->no_disalogs = disable;
366 return 0;
367 }
368
369 /**
370 * fdisk_has_dialogs
371 * @cxt: fdisk context
372 *
373 * See fdisk_disable_dialogs()
374 *
375 * Returns: 1 if dialog driven partitioning enabled (default), or 0.
376 *
377 * Since: 2.31
378 */
379 int fdisk_has_dialogs(struct fdisk_context *cxt)
380 {
381 return cxt->no_disalogs == 0;
382 }
383
384 /**
385 * fdisk_enable_wipe
386 * @cxt: fdisk context
387 * @enable: 1 or 0
388 *
389 * The library removes all PT/filesystem/RAID signatures before it writes
390 * partition table. The probing area where it looks for signatures is from
391 * the begin of the disk. The device is wiped by libblkid.
392 *
393 * See also fdisk_wipe_partition().
394 *
395 * Returns: 0 on success, < 0 on error.
396 */
397 int fdisk_enable_wipe(struct fdisk_context *cxt, int enable)
398 {
399 if (!cxt)
400 return -EINVAL;
401
402 fdisk_set_wipe_area(cxt, 0, cxt->total_sectors, enable);
403 return 0;
404 }
405
406 /**
407 * fdisk_has_wipe
408 * @cxt: fdisk context
409 *
410 * Returns the current wipe setting. See fdisk_enable_wipe().
411 *
412 * Returns: 0 on success, < 0 on error.
413 */
414 int fdisk_has_wipe(struct fdisk_context *cxt)
415 {
416 if (!cxt)
417 return 0;
418
419 return fdisk_has_wipe_area(cxt, 0, cxt->total_sectors);
420 }
421
422
423 /**
424 * fdisk_get_collision
425 * @cxt: fdisk context
426 *
427 * Returns: name of the filesystem or RAID detected on the device or NULL.
428 */
429 const char *fdisk_get_collision(struct fdisk_context *cxt)
430 {
431 return cxt->collision;
432 }
433
434 /**
435 * fdisk_is_ptcollision:
436 * @cxt: fdisk context
437 *
438 * The collision detected by libblkid (usually another partition table). Note
439 * that libfdisk does not support all partitions tables, so fdisk_has_label()
440 * may return false, but fdisk_is_ptcollision() may return true.
441 *
442 * Since: 2.30
443 *
444 * Returns: 0 or 1
445 */
446 int fdisk_is_ptcollision(struct fdisk_context *cxt)
447 {
448 return cxt->pt_collision;
449 }
450
451 /**
452 * fdisk_get_npartitions:
453 * @cxt: context
454 *
455 * The maximal number of the partitions depends on disklabel and does not
456 * have to describe the real limit of PT.
457 *
458 * For example the limit for MBR without extend partition is 4, with extended
459 * partition it's unlimited (so the function returns the current number of all
460 * partitions in this case).
461 *
462 * And for example for GPT it depends on space allocated on disk for array of
463 * entry records (usually 128).
464 *
465 * It's fine to use fdisk_get_npartitions() in loops, but don't forget that
466 * partition may be unused (see fdisk_is_partition_used()).
467 *
468 * <informalexample>
469 * <programlisting>
470 * struct fdisk_partition *pa = NULL;
471 * size_t i, nmax = fdisk_get_npartitions(cxt);
472 *
473 * for (i = 0; i < nmax; i++) {
474 * if (!fdisk_is_partition_used(cxt, i))
475 * continue;
476 * ... do something ...
477 * }
478 * </programlisting>
479 * </informalexample>
480 *
481 * Note that the recommended way to list partitions is to use
482 * fdisk_get_partitions() and struct fdisk_table than ask disk driver for each
483 * individual partitions.
484 *
485 * Returns: maximal number of partitions for the current label.
486 */
487 size_t fdisk_get_npartitions(struct fdisk_context *cxt)
488 {
489 return cxt && cxt->label ? cxt->label->nparts_max : 0;
490 }
491
492 /**
493 * fdisk_is_labeltype:
494 * @cxt: fdisk context
495 * @id: FDISK_DISKLABEL_*
496 *
497 * See also fdisk_is_label() macro in libfdisk.h.
498 *
499 * Returns: return 1 if the current label is @id
500 */
501 int fdisk_is_labeltype(struct fdisk_context *cxt, enum fdisk_labeltype id)
502 {
503 assert(cxt);
504
505 return cxt->label && (unsigned)fdisk_label_get_type(cxt->label) == id;
506 }
507
508 /**
509 * fdisk_get_parent:
510 * @cxt: nested fdisk context
511 *
512 * Returns: pointer to parental context, or NULL
513 */
514 struct fdisk_context *fdisk_get_parent(struct fdisk_context *cxt)
515 {
516 assert(cxt);
517 return cxt->parent;
518 }
519
520 static void reset_context(struct fdisk_context *cxt)
521 {
522 size_t i;
523
524 DBG(CXT, ul_debugobj(cxt, "*** resetting context"));
525
526 /* reset drives' private data */
527 for (i = 0; i < cxt->nlabels; i++)
528 fdisk_deinit_label(cxt->labels[i]);
529
530 if (cxt->parent) {
531 /* the first sector may be independent on parent */
532 if (cxt->parent->firstsector != cxt->firstsector)
533 free(cxt->firstsector);
534 } else {
535 /* we close device only in primary context */
536 if (cxt->dev_fd > -1)
537 close(cxt->dev_fd);
538 free(cxt->firstsector);
539 }
540
541 free(cxt->dev_path);
542 cxt->dev_path = NULL;
543
544 free(cxt->collision);
545 cxt->collision = NULL;
546
547 memset(&cxt->dev_st, 0, sizeof(cxt->dev_st));
548
549 cxt->dev_fd = -1;
550 cxt->firstsector = NULL;
551 cxt->firstsector_bufsz = 0;
552
553 fdisk_zeroize_device_properties(cxt);
554
555 fdisk_unref_script(cxt->script);
556 cxt->script = NULL;
557
558 cxt->label = NULL;
559
560 fdisk_free_wipe_areas(cxt);
561 }
562
563 /**
564 * fdisk_assign_device:
565 * @cxt: context
566 * @fname: path to the device to be handled
567 * @readonly: how to open the device
568 *
569 * Open the device, discovery topology, geometry, detect disklabel and switch
570 * the current label driver to reflect the probing result.
571 *
572 * Note that this function resets all generic setting in context. If the @cxt
573 * is nested context then the device is assigned to the parental context and
574 * necessary properties are copied to the @cxt. The change is propagated in
575 * child->parent direction only. It's impossible to use a different device for
576 * primary and nested contexts.
577 *
578 * Returns: 0 on success, < 0 on error.
579 */
580 int fdisk_assign_device(struct fdisk_context *cxt,
581 const char *fname, int readonly)
582 {
583 int fd;
584
585 DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname));
586 assert(cxt);
587
588 /* redirect request to parent */
589 if (cxt->parent) {
590 int rc, org = fdisk_is_listonly(cxt->parent);
591
592 /* assign_device() is sensitive to "listonly" mode, so let's
593 * follow the current context setting for the parent to avoid
594 * unwanted extra warnings. */
595 fdisk_enable_listonly(cxt->parent, fdisk_is_listonly(cxt));
596
597 rc = fdisk_assign_device(cxt->parent, fname, readonly);
598 fdisk_enable_listonly(cxt->parent, org);
599
600 if (!rc)
601 rc = init_nested_from_parent(cxt, 0);
602 if (!rc)
603 fdisk_probe_labels(cxt);
604 return rc;
605 }
606
607 reset_context(cxt);
608
609 fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC);
610 if (fd < 0)
611 goto fail;
612
613 if (fstat(fd, &cxt->dev_st) != 0)
614 goto fail;
615
616 cxt->readonly = readonly;
617 cxt->dev_fd = fd;
618 cxt->dev_path = strdup(fname);
619 if (!cxt->dev_path)
620 goto fail;
621
622 fdisk_discover_topology(cxt);
623 fdisk_discover_geometry(cxt);
624
625 fdisk_apply_user_device_properties(cxt);
626
627 if (fdisk_read_firstsector(cxt) < 0)
628 goto fail;
629
630 fdisk_probe_labels(cxt);
631
632 fdisk_apply_label_device_properties(cxt);
633
634 /* warn about obsolete stuff on the device if we aren't in
635 * list-only mode and there is not PT yet */
636 if (!fdisk_is_listonly(cxt) && !fdisk_has_label(cxt)
637 && fdisk_check_collisions(cxt) < 0)
638 goto fail;
639
640 DBG(CXT, ul_debugobj(cxt, "initialized for %s [%s]",
641 fname, readonly ? "READ-ONLY" : "READ-WRITE"));
642 return 0;
643 fail:
644 {
645 int rc = -errno;
646 if (fd >= 0)
647 close(fd);
648 DBG(CXT, ul_debugobj(cxt, "failed to assign device [rc=%d]", rc));
649 return rc;
650 }
651 }
652
653 /**
654 * fdisk_deassign_device:
655 * @cxt: context
656 * @nosync: disable fsync()
657 *
658 * Close device and call fsync(). If the @cxt is nested context than the
659 * request is redirected to the parent.
660 *
661 * Returns: 0 on success, < 0 on error.
662 */
663 int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
664 {
665 assert(cxt);
666 assert(cxt->dev_fd >= 0);
667
668 if (cxt->parent) {
669 int rc = fdisk_deassign_device(cxt->parent, nosync);
670
671 if (!rc)
672 rc = init_nested_from_parent(cxt, 0);
673 return rc;
674 }
675
676 DBG(CXT, ul_debugobj(cxt, "de-assigning device %s", cxt->dev_path));
677
678 if (cxt->readonly)
679 close(cxt->dev_fd);
680 else {
681 if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) {
682 fdisk_warn(cxt, _("%s: close device failed"),
683 cxt->dev_path);
684 return -errno;
685 }
686
687 if (!nosync) {
688 fdisk_info(cxt, _("Syncing disks."));
689 sync();
690 }
691 }
692
693 free(cxt->dev_path);
694 cxt->dev_path = NULL;
695
696 cxt->dev_fd = -1;
697
698 return 0;
699 }
700
701 /**
702 * fdisk_reassign_device:
703 * @cxt: context
704 *
705 * This function is "hard reset" of the context and it does not write anything
706 * to the device. All in-memory changes associated with the context will be
707 * lost. It's recommended to use this function after some fatal problem when the
708 * context (and label specific driver) is in an undefined state.
709 *
710 * Returns: 0 on success, < 0 on error.
711 */
712 int fdisk_reassign_device(struct fdisk_context *cxt)
713 {
714 char *devname;
715 int rdonly, rc;
716
717 assert(cxt);
718 assert(cxt->dev_fd >= 0);
719
720 DBG(CXT, ul_debugobj(cxt, "re-assigning device %s", cxt->dev_path));
721
722 devname = strdup(cxt->dev_path);
723 if (!devname)
724 return -ENOMEM;
725
726 rdonly = cxt->readonly;
727
728 fdisk_deassign_device(cxt, 1);
729 rc = fdisk_assign_device(cxt, devname, rdonly);
730 free(devname);
731
732 return rc;
733 }
734
735 /**
736 * fdisk_reread_partition_table:
737 * @cxt: context
738 *
739 * Force *kernel* to re-read partition table on block devices.
740 *
741 * Returns: 0 on success, < 0 in case of error.
742 */
743 int fdisk_reread_partition_table(struct fdisk_context *cxt)
744 {
745 int i = 0;
746
747 assert(cxt);
748 assert(cxt->dev_fd >= 0);
749
750 if (!S_ISBLK(cxt->dev_st.st_mode))
751 return 0;
752 else {
753 DBG(CXT, ul_debugobj(cxt, "calling re-read ioctl"));
754 sync();
755 #ifdef BLKRRPART
756 fdisk_info(cxt, _("Calling ioctl() to re-read partition table."));
757 i = ioctl(cxt->dev_fd, BLKRRPART);
758 #else
759 errno = ENOSYS;
760 i = 1;
761 #endif
762 }
763
764 if (i) {
765 fdisk_warn(cxt, _("Re-reading the partition table failed."));
766 fdisk_info(cxt, _(
767 "The kernel still uses the old table. The "
768 "new table will be used at the next reboot "
769 "or after you run partprobe(8) or kpartx(8)."));
770 return -errno;
771 }
772
773 return 0;
774 }
775
776 #ifdef __linux__
777 static inline int add_to_partitions_array(
778 struct fdisk_partition ***ary,
779 struct fdisk_partition *pa,
780 size_t *n, size_t nmax)
781 {
782 if (!*ary) {
783 *ary = calloc(nmax, sizeof(struct fdisk_partition *));
784 if (!*ary)
785 return -ENOMEM;
786 }
787 (*ary)[*n] = pa;
788 (*n)++;
789 return 0;
790 }
791 #endif
792
793 /**
794 * fdisk_reread_changes:
795 * @cxt: context
796 * @org: original layout (on disk)
797 *
798 * Like fdisk_reread_partition_table() but don't forces kernel re-read all
799 * partition table. The BLKPG_* ioctls are used for individual partitions. The
800 * advantage is that unmodified partitions maybe mounted.
801 *
802 * The function behavies like fdisk_reread_partition_table() on systems where
803 * are no available BLKPG_* ioctls.
804 *
805 * Returns: <0 on error, or 0.
806 */
807 #ifdef __linux__
808 int fdisk_reread_changes(struct fdisk_context *cxt, struct fdisk_table *org)
809 {
810 struct fdisk_table *tb = NULL;
811 struct fdisk_iter itr;
812 struct fdisk_partition *pa;
813 struct fdisk_partition **rem = NULL, **add = NULL, **upd = NULL;
814 int change, rc = 0, err = 0;
815 size_t nparts, i, nadds = 0, nupds = 0, nrems = 0;
816
817 DBG(CXT, ul_debugobj(cxt, "rereading changes"));
818
819 fdisk_reset_iter(&itr, FDISK_ITER_FORWARD);
820
821 /* the current layout */
822 fdisk_get_partitions(cxt, &tb);
823 /* maximal number of partitions */
824 nparts = max(fdisk_table_get_nents(tb), fdisk_table_get_nents(org));
825
826 while (fdisk_diff_tables(org, tb, &itr, &pa, &change) == 0) {
827 if (change == FDISK_DIFF_UNCHANGED)
828 continue;
829 switch (change) {
830 case FDISK_DIFF_REMOVED:
831 rc = add_to_partitions_array(&rem, pa, &nrems, nparts);
832 break;
833 case FDISK_DIFF_ADDED:
834 rc = add_to_partitions_array(&add, pa, &nadds, nparts);
835 break;
836 case FDISK_DIFF_RESIZED:
837 rc = add_to_partitions_array(&upd, pa, &nupds, nparts);
838 break;
839 case FDISK_DIFF_MOVED:
840 rc = add_to_partitions_array(&rem, pa, &nrems, nparts);
841 rc = add_to_partitions_array(&add, pa, &nadds, nparts);
842 break;
843 }
844 if (rc != 0)
845 goto done;
846 }
847
848 for (i = 0; i < nrems; i++) {
849 pa = rem[i];
850 DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_DEL_PARTITION", pa->partno));
851 if (partx_del_partition(cxt->dev_fd, pa->partno + 1) != 0) {
852 fdisk_warn(cxt, _("Failed to remove partition %zu from system"), pa->partno + 1);
853 err++;
854 }
855 }
856 for (i = 0; i < nupds; i++) {
857 pa = upd[i];
858 DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_RESIZE_PARTITION", pa->partno));
859 if (partx_resize_partition(cxt->dev_fd, pa->partno + 1, pa->start, pa->size) != 0) {
860 fdisk_warn(cxt, _("Failed to update system information about partition %zu"), pa->partno + 1);
861 err++;
862 }
863 }
864 for (i = 0; i < nadds; i++) {
865 pa = add[i];
866 DBG(PART, ul_debugobj(pa, "#%zu calling BLKPG_ADD_PARTITION", pa->partno));
867 if (partx_add_partition(cxt->dev_fd, pa->partno + 1, pa->start, pa->size) != 0) {
868 fdisk_warn(cxt, _("Failed to add partition %zu to system"), pa->partno + 1);
869 err++;
870 }
871 }
872 if (err)
873 fdisk_info(cxt, _(
874 "The kernel still uses the old partitions. The new "
875 "table will be used at the next reboot. "));
876 done:
877 free(rem);
878 free(add);
879 free(upd);
880 fdisk_unref_table(tb);
881 return rc;
882 }
883 #else
884 int fdisk_reread_changes(struct fdisk_context *cxt,
885 struct fdisk_table *org __attribute__((__unused__))) {
886 return fdisk_reread_partition_table(cxt);
887 }
888 #endif
889
890 /**
891 * fdisk_device_is_used:
892 * @cxt: context
893 *
894 * On systems where is no BLKRRPART ioctl the function returns zero and
895 * sets errno to ENOSYS.
896 *
897 * Returns: 1 if the device assigned to the context is used by system, or 0.
898 */
899 int fdisk_device_is_used(struct fdisk_context *cxt)
900 {
901 int rc = 0;
902
903 assert(cxt);
904 assert(cxt->dev_fd >= 0);
905
906 errno = 0;
907
908 #ifdef BLKRRPART
909 /* it seems kernel always return EINVAL for BLKRRPART on loopdevices */
910 if (S_ISBLK(cxt->dev_st.st_mode)
911 && major(cxt->dev_st.st_rdev) != LOOPDEV_MAJOR) {
912 DBG(CXT, ul_debugobj(cxt, "calling re-read ioctl"));
913 rc = ioctl(cxt->dev_fd, BLKRRPART) != 0;
914 }
915 #else
916 errno = ENOSYS;
917 #endif
918 DBG(CXT, ul_debugobj(cxt, "device used: %s [errno=%d]", rc ? "TRUE" : "FALSE", errno));
919 return rc;
920 }
921
922 /**
923 * fdisk_is_readonly:
924 * @cxt: context
925 *
926 * Returns: 1 if device open readonly
927 */
928 int fdisk_is_readonly(struct fdisk_context *cxt)
929 {
930 assert(cxt);
931 return cxt->readonly;
932 }
933
934 /**
935 * fdisk_is_regfile:
936 * @cxt: context
937 *
938 * Since: 2.30
939 *
940 * Returns: 1 if open file descriptor is regular file rather than a block device.
941 */
942 int fdisk_is_regfile(struct fdisk_context *cxt)
943 {
944 assert(cxt);
945 return S_ISREG(cxt->dev_st.st_mode);
946 }
947
948 /**
949 * fdisk_unref_context:
950 * @cxt: fdisk context
951 *
952 * Deallocates context struct.
953 */
954 void fdisk_unref_context(struct fdisk_context *cxt)
955 {
956 unsigned i;
957
958 if (!cxt)
959 return;
960
961 cxt->refcount--;
962 if (cxt->refcount <= 0) {
963 DBG(CXT, ul_debugobj(cxt, "freeing context %p for %s", cxt, cxt->dev_path));
964
965 reset_context(cxt); /* this is sensitive to parent<->child relationship! */
966
967 /* deallocate label's private stuff */
968 for (i = 0; i < cxt->nlabels; i++) {
969 if (!cxt->labels[i])
970 continue;
971 if (cxt->labels[i]->op->free)
972 cxt->labels[i]->op->free(cxt->labels[i]);
973 else
974 free(cxt->labels[i]);
975 }
976
977 fdisk_unref_context(cxt->parent);
978 cxt->parent = NULL;
979
980 free(cxt);
981 }
982 }
983
984
985 /**
986 * fdisk_enable_details:
987 * @cxt: context
988 * @enable: true/false
989 *
990 * Enables or disables "details" display mode. This function has effect to
991 * fdisk_partition_to_string() function.
992 *
993 * Returns: 0 on success, < 0 on error.
994 */
995 int fdisk_enable_details(struct fdisk_context *cxt, int enable)
996 {
997 assert(cxt);
998 cxt->display_details = enable ? 1 : 0;
999 return 0;
1000 }
1001
1002 /**
1003 * fdisk_is_details:
1004 * @cxt: context
1005 *
1006 * Returns: 1 if details are enabled
1007 */
1008 int fdisk_is_details(struct fdisk_context *cxt)
1009 {
1010 assert(cxt);
1011 return cxt->display_details == 1;
1012 }
1013
1014 /**
1015 * fdisk_enable_listonly:
1016 * @cxt: context
1017 * @enable: true/false
1018 *
1019 * Just list partition only, don't care about another details, mistakes, ...
1020 *
1021 * Returns: 0 on success, < 0 on error.
1022 */
1023 int fdisk_enable_listonly(struct fdisk_context *cxt, int enable)
1024 {
1025 assert(cxt);
1026 cxt->listonly = enable ? 1 : 0;
1027 return 0;
1028 }
1029
1030 /**
1031 * fdisk_is_listonly:
1032 * @cxt: context
1033 *
1034 * Returns: 1 if list-only mode enabled
1035 */
1036 int fdisk_is_listonly(struct fdisk_context *cxt)
1037 {
1038 assert(cxt);
1039 return cxt->listonly == 1;
1040 }
1041
1042
1043 /**
1044 * fdisk_set_unit:
1045 * @cxt: context
1046 * @str: "cylinder" or "sector".
1047 *
1048 * This is pure shit, unfortunately for example Sun addresses begin of the
1049 * partition by cylinders...
1050 *
1051 * Returns: 0 on success, <0 on error.
1052 */
1053 int fdisk_set_unit(struct fdisk_context *cxt, const char *str)
1054 {
1055 assert(cxt);
1056
1057 cxt->display_in_cyl_units = 0;
1058
1059 if (!str)
1060 return 0;
1061
1062 if (strcmp(str, "cylinder") == 0 || strcmp(str, "cylinders") == 0)
1063 cxt->display_in_cyl_units = 1;
1064
1065 else if (strcmp(str, "sector") == 0 || strcmp(str, "sectors") == 0)
1066 cxt->display_in_cyl_units = 0;
1067
1068 DBG(CXT, ul_debugobj(cxt, "display unit: %s", fdisk_get_unit(cxt, 0)));
1069 return 0;
1070 }
1071
1072 /**
1073 * fdisk_get_unit:
1074 * @cxt: context
1075 * @n: FDISK_PLURAL or FDISK_SINGULAR
1076 *
1077 * Returns: unit name.
1078 */
1079 const char *fdisk_get_unit(struct fdisk_context *cxt, int n)
1080 {
1081 assert(cxt);
1082
1083 if (fdisk_use_cylinders(cxt))
1084 return P_("cylinder", "cylinders", n);
1085 return P_("sector", "sectors", n);
1086 }
1087
1088 /**
1089 * fdisk_use_cylinders:
1090 * @cxt: context
1091 *
1092 * Returns: 1 if user wants to display in cylinders.
1093 */
1094 int fdisk_use_cylinders(struct fdisk_context *cxt)
1095 {
1096 assert(cxt);
1097 return cxt->display_in_cyl_units == 1;
1098 }
1099
1100 /**
1101 * fdisk_get_units_per_sector:
1102 * @cxt: context
1103 *
1104 * This is necessary only for brain dead situations when we use "cylinders";
1105 *
1106 * Returns: number of "units" per sector, default is 1 if display unit is sector.
1107 */
1108 unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt)
1109 {
1110 assert(cxt);
1111
1112 if (fdisk_use_cylinders(cxt)) {
1113 assert(cxt->geom.heads);
1114 return cxt->geom.heads * cxt->geom.sectors;
1115 }
1116 return 1;
1117 }
1118
1119 /**
1120 * fdisk_get_optimal_iosize:
1121 * @cxt: context
1122 *
1123 * The optimal I/O is optional and does not have to be provided by device,
1124 * anyway libfdisk never returns zero. If the optimal I/O size is not provided
1125 * then libfdisk returns minimal I/O size or sector size.
1126 *
1127 * Returns: optimal I/O size in bytes.
1128 */
1129 unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt)
1130 {
1131 assert(cxt);
1132 return cxt->optimal_io_size ? cxt->optimal_io_size : cxt->io_size;
1133 }
1134
1135 /**
1136 * fdisk_get_minimal_iosize:
1137 * @cxt: context
1138 *
1139 * Returns: minimal I/O size in bytes
1140 */
1141 unsigned long fdisk_get_minimal_iosize(struct fdisk_context *cxt)
1142 {
1143 assert(cxt);
1144 return cxt->min_io_size;
1145 }
1146
1147 /**
1148 * fdisk_get_physector_size:
1149 * @cxt: context
1150 *
1151 * Returns: physical sector size in bytes
1152 */
1153 unsigned long fdisk_get_physector_size(struct fdisk_context *cxt)
1154 {
1155 assert(cxt);
1156 return cxt->phy_sector_size;
1157 }
1158
1159 /**
1160 * fdisk_get_sector_size:
1161 * @cxt: context
1162 *
1163 * Returns: logical sector size in bytes
1164 */
1165 unsigned long fdisk_get_sector_size(struct fdisk_context *cxt)
1166 {
1167 assert(cxt);
1168 return cxt->sector_size;
1169 }
1170
1171 /**
1172 * fdisk_get_alignment_offset
1173 * @cxt: context
1174 *
1175 * The alignment offset is offset between logical and physical sectors. For
1176 * backward compatibility the first logical sector on 4K disks does no have to
1177 * start on the same place like physical sectors.
1178 *
1179 * Returns: alignment offset in bytes
1180 */
1181 unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt)
1182 {
1183 assert(cxt);
1184 return cxt->alignment_offset;
1185 }
1186
1187 /**
1188 * fdisk_get_grain_size:
1189 * @cxt: context
1190 *
1191 * Returns: grain in bytes used to align partitions (usually 1MiB)
1192 */
1193 unsigned long fdisk_get_grain_size(struct fdisk_context *cxt)
1194 {
1195 assert(cxt);
1196 return cxt->grain;
1197 }
1198
1199 /**
1200 * fdisk_get_first_lba:
1201 * @cxt: context
1202 *
1203 * Returns: first possible LBA on disk for data partitions.
1204 */
1205 fdisk_sector_t fdisk_get_first_lba(struct fdisk_context *cxt)
1206 {
1207 assert(cxt);
1208 return cxt->first_lba;
1209 }
1210
1211 /**
1212 * fdisk_set_first_lba:
1213 * @cxt: fdisk context
1214 * @lba: first possible logical sector for data
1215 *
1216 * It's strongly recommended to use the default library setting. The first LBA
1217 * is always reset by fdisk_assign_device(), fdisk_override_geometry()
1218 * and fdisk_reset_alignment(). This is very low level function and library
1219 * does not check if your setting makes any sense.
1220 *
1221 * This function is necessary only when you want to work with very unusual
1222 * partition tables like GPT protective MBR or hybrid partition tables on
1223 * bootable media where the first partition may start on very crazy offsets.
1224 *
1225 * Returns: 0 on success, <0 on error.
1226 */
1227 fdisk_sector_t fdisk_set_first_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
1228 {
1229 assert(cxt);
1230 DBG(CXT, ul_debugobj(cxt, "setting first LBA from %ju to %ju",
1231 (uintmax_t) cxt->first_lba, (uintmax_t) lba));
1232 cxt->first_lba = lba;
1233 return 0;
1234 }
1235
1236 /**
1237 * fdisk_get_last_lba:
1238 * @cxt: fdisk context
1239 *
1240 * Note that the device has to be already assigned.
1241 *
1242 * Returns: last possible LBA on device
1243 */
1244 fdisk_sector_t fdisk_get_last_lba(struct fdisk_context *cxt)
1245 {
1246 return cxt->last_lba;
1247 }
1248
1249 /**
1250 * fdisk_set_last_lba:
1251 * @cxt: fdisk context
1252 * @lba: last possible logical sector
1253 *
1254 * It's strongly recommended to use the default library setting. The last LBA
1255 * is always reset by fdisk_assign_device(), fdisk_override_geometry() and
1256 * fdisk_reset_alignment().
1257 *
1258 * The default is number of sectors on the device, but maybe modified by the
1259 * current disklabel driver (for example GPT uses the end of disk for backup
1260 * header, so last_lba is smaller than total number of sectors).
1261 *
1262 * Returns: 0 on success, <0 on error.
1263 */
1264 fdisk_sector_t fdisk_set_last_lba(struct fdisk_context *cxt, fdisk_sector_t lba)
1265 {
1266 assert(cxt);
1267
1268 if (lba > cxt->total_sectors - 1 || lba < 1)
1269 return -ERANGE;
1270 cxt->last_lba = lba;
1271 return 0;
1272 }
1273
1274 /**
1275 * fdisk_set_size_unit:
1276 * @cxt: fdisk context
1277 * @unit: FDISK_SIZEUNIT_*
1278 *
1279 * Sets unit for SIZE output field (see fdisk_partition_to_string()).
1280 *
1281 * Returns: 0 on success, <0 on error.
1282 */
1283 int fdisk_set_size_unit(struct fdisk_context *cxt, int unit)
1284 {
1285 assert(cxt);
1286 cxt->sizeunit = unit;
1287 return 0;
1288 }
1289
1290 /**
1291 * fdisk_get_size_unit:
1292 * @cxt: fdisk context
1293 *
1294 * Gets unit for SIZE output field (see fdisk_partition_to_string()).
1295 *
1296 * Returns: unit
1297 */
1298 int fdisk_get_size_unit(struct fdisk_context *cxt)
1299 {
1300 assert(cxt);
1301 return cxt->sizeunit;
1302 }
1303
1304 /**
1305 * fdisk_get_nsectors:
1306 * @cxt: context
1307 *
1308 * Returns: size of the device in logical sectors.
1309 */
1310 fdisk_sector_t fdisk_get_nsectors(struct fdisk_context *cxt)
1311 {
1312 assert(cxt);
1313 return cxt->total_sectors;
1314 }
1315
1316 /**
1317 * fdisk_get_devname:
1318 * @cxt: context
1319 *
1320 * Returns: device name.
1321 */
1322 const char *fdisk_get_devname(struct fdisk_context *cxt)
1323 {
1324 assert(cxt);
1325 return cxt->dev_path;
1326 }
1327
1328 /**
1329 * fdisk_get_devfd:
1330 * @cxt: context
1331 *
1332 * Returns: device file descriptor.
1333 */
1334 int fdisk_get_devfd(struct fdisk_context *cxt)
1335 {
1336 assert(cxt);
1337 return cxt->dev_fd;
1338 }
1339
1340 /**
1341 * fdisk_get_geom_heads:
1342 * @cxt: context
1343 *
1344 * Returns: number of geometry heads.
1345 */
1346 unsigned int fdisk_get_geom_heads(struct fdisk_context *cxt)
1347 {
1348 assert(cxt);
1349 return cxt->geom.heads;
1350 }
1351 /**
1352 * fdisk_get_geom_sectors:
1353 * @cxt: context
1354 *
1355 * Returns: number of geometry sectors.
1356 */
1357 fdisk_sector_t fdisk_get_geom_sectors(struct fdisk_context *cxt)
1358 {
1359 assert(cxt);
1360 return cxt->geom.sectors;
1361
1362 }
1363
1364 /**
1365 * fdisk_get_geom_cylinders:
1366 * @cxt: context
1367 *
1368 * Returns: number of geometry cylinders
1369 */
1370 fdisk_sector_t fdisk_get_geom_cylinders(struct fdisk_context *cxt)
1371 {
1372 assert(cxt);
1373 return cxt->geom.cylinders;
1374 }
1375
1376 int fdisk_missing_geometry(struct fdisk_context *cxt)
1377 {
1378 int rc;
1379
1380 if (!cxt || !cxt->label)
1381 return 0;
1382
1383 rc = (fdisk_label_require_geometry(cxt->label) &&
1384 (!cxt->geom.heads || !cxt->geom.sectors
1385 || !cxt->geom.cylinders));
1386
1387 if (rc && !fdisk_is_listonly(cxt))
1388 fdisk_warnx(cxt, _("Incomplete geometry setting."));
1389
1390 return rc;
1391 }
1392