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