]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libfdisk/src/context.c
libfdisk: clean up API (context.c)
[thirdparty/util-linux.git] / libfdisk / src / context.c
1 #ifdef HAVE_LIBBLKID
2 # include <blkid.h>
3 #endif
4
5 #include "fdiskP.h"
6
7 /**
8 * fdisk_new_context:
9 *
10 * Returns: newly allocated libfdisk handler
11 */
12 struct fdisk_context *fdisk_new_context(void)
13 {
14 struct fdisk_context *cxt;
15
16 cxt = calloc(1, sizeof(*cxt));
17 if (!cxt)
18 return NULL;
19
20 DBG(CXT, ul_debugobj(cxt, "alloc"));
21 cxt->dev_fd = -1;
22
23 /*
24 * Allocate label specific structs.
25 *
26 * This is necessary (for example) to store label specific
27 * context setting.
28 */
29 cxt->labels[ cxt->nlabels++ ] = fdisk_new_gpt_label(cxt);
30 cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
31 cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
32 cxt->labels[ cxt->nlabels++ ] = fdisk_new_sgi_label(cxt);
33 cxt->labels[ cxt->nlabels++ ] = fdisk_new_sun_label(cxt);
34
35 return cxt;
36 }
37
38 /**
39 * fdisk_new_nested_context:
40 * @parent: parental context
41 * @name: optional label name (e.g. "bsd")
42 *
43 * This is supported for MBR+BSD and GPT+pMBR only.
44 *
45 * Returns: new context for nested partiton table.
46 */
47 struct fdisk_context *fdisk_new_nested_context(struct fdisk_context *parent,
48 const char *name)
49 {
50 struct fdisk_context *cxt;
51 struct fdisk_label *lb = NULL;
52
53 assert(parent);
54
55 cxt = calloc(1, sizeof(*cxt));
56 if (!cxt)
57 return NULL;
58
59 DBG(CXT, ul_debugobj(parent, "alloc nested [%p]", cxt));
60 cxt->dev_fd = parent->dev_fd;
61 cxt->parent = parent;
62
63 cxt->io_size = parent->io_size;
64 cxt->optimal_io_size = parent->optimal_io_size;
65 cxt->min_io_size = parent->min_io_size;
66 cxt->phy_sector_size = parent->phy_sector_size;
67 cxt->sector_size = parent->sector_size;
68 cxt->alignment_offset = parent->alignment_offset;
69 cxt->grain = parent->grain;
70 cxt->first_lba = parent->first_lba;
71 cxt->total_sectors = parent->total_sectors;
72 cxt->firstsector = parent->firstsector;
73
74 cxt->ask_cb = parent->ask_cb;
75 cxt->ask_data = parent->ask_data;
76
77 cxt->geom = parent->geom;
78
79 if (name) {
80 if (strcmp(name, "bsd") == 0)
81 lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_bsd_label(cxt);
82 else if (strcmp(name, "dos") == 0)
83 lb = cxt->labels[ cxt->nlabels++ ] = fdisk_new_dos_label(cxt);
84 }
85
86 if (lb) {
87 DBG(CXT, ul_debugobj(cxt, "probing for nested %s", lb->name));
88
89 cxt->label = lb;
90
91 if (lb->op->probe(cxt) == 1)
92 __fdisk_switch_label(cxt, lb);
93 else {
94 DBG(CXT, ul_debugobj(cxt, "not found %s label", lb->name));
95 if (lb->op->deinit)
96 lb->op->deinit(lb);
97 cxt->label = NULL;
98 }
99 }
100
101 return cxt;
102 }
103
104
105 /**
106 * fdisk_get_label:
107 * @cxt: context instance
108 * @name: label name (e.g. "gpt")
109 *
110 * If no @name specified then returns the current context label.
111 *
112 * Returns: label struct or NULL in case of error.
113 */
114 struct fdisk_label *fdisk_get_label(struct fdisk_context *cxt, const char *name)
115 {
116 size_t i;
117
118 assert(cxt);
119
120 if (!name)
121 return cxt->label;
122
123 for (i = 0; i < cxt->nlabels; i++)
124 if (cxt->labels[i]
125 && strcmp(cxt->labels[i]->name, name) == 0)
126 return cxt->labels[i];
127
128 DBG(CXT, ul_debugobj(cxt, "failed to found %s label driver", name));
129 return NULL;
130 }
131
132 /**
133 * fdisk_next_label:
134 * @cxt: context instance
135 * @lb: returns pointer to the next label
136 *
137 * <informalexample>
138 * <programlisting>
139 * // print all supported labels
140 * struct fdisk_context *cxt = fdisk_new_context();
141 * struct fdisk_label *lb = NULL;
142 *
143 * while (fdisk_next_label(cxt, &lb) == 0)
144 * print("label name: %s\n", fdisk_label_get_name(lb));
145 * fdisk_free_context(cxt);
146 * </programlisting>
147 * </informalexample>
148 *
149 * Returns: <0 in case of error, 0 on success, 1 at the end.
150 */
151 int fdisk_next_label(struct fdisk_context *cxt, struct fdisk_label **lb)
152 {
153 size_t i;
154 struct fdisk_label *res = NULL;
155
156 if (!lb || !cxt)
157 return -EINVAL;
158
159 if (!*lb)
160 res = cxt->labels[0];
161 else {
162 for (i = 1; i < cxt->nlabels; i++) {
163 if (*lb == cxt->labels[i - 1]) {
164 res = cxt->labels[i];
165 break;
166 }
167 }
168 }
169
170 *lb = res;
171 return res ? 0 : 1;
172 }
173
174 /**
175 * fdisk_get_nlabels:
176 * @cxt: context
177 *
178 * Returns: number of supported label types
179 */
180 size_t fdisk_get_nlabels(struct fdisk_context *cxt)
181 {
182 return cxt ? cxt->nlabels : 0;
183 }
184
185 int __fdisk_switch_label(struct fdisk_context *cxt, struct fdisk_label *lb)
186 {
187 if (!lb || !cxt)
188 return -EINVAL;
189 if (lb->disabled) {
190 DBG(CXT, ul_debugobj(cxt, "*** attempt to switch to disabled label %s -- ignore!", lb->name));
191 return -EINVAL;
192 }
193 cxt->label = lb;
194 DBG(CXT, ul_debugobj(cxt, "--> switching context to %s!", lb->name));
195 return 0;
196 }
197
198 /**
199 * fdisk_switch_label:
200 * @cxt: context
201 * @name: label name (e.g. "gpt")
202 *
203 * Forces libfdisk to use the label driver.
204 *
205 * Returns: 0 on succes, <0 in case of error.
206 */
207 int fdisk_switch_label(struct fdisk_context *cxt, const char *name)
208 {
209 return __fdisk_switch_label(cxt, fdisk_get_label(cxt, name));
210 }
211
212
213 static void reset_context(struct fdisk_context *cxt)
214 {
215 size_t i;
216
217 DBG(CXT, ul_debugobj(cxt, "*** resetting context"));
218
219 /* reset drives' private data */
220 for (i = 0; i < cxt->nlabels; i++)
221 fdisk_deinit_label(cxt->labels[i]);
222
223 /* free device specific stuff */
224 if (!cxt->parent && cxt->dev_fd > -1)
225 close(cxt->dev_fd);
226 free(cxt->dev_path);
227
228 if (cxt->parent == NULL || cxt->parent->firstsector != cxt->firstsector)
229 free(cxt->firstsector);
230
231 /* initialize */
232 cxt->dev_fd = -1;
233 cxt->dev_path = NULL;
234 cxt->firstsector = NULL;
235 cxt->firstsector_bufsz = 0;
236
237 fdisk_zeroize_device_properties(cxt);
238
239 cxt->label = NULL;
240 }
241
242 /*
243 * This function prints a warning if the device is not wiped (e.g. wipefs(8).
244 * Please don't call this function if there is already a PT.
245 *
246 * Returns: 0 if nothing found, < 0 on error, 1 if found a signature
247 */
248 static int warn_wipe(struct fdisk_context *cxt)
249 {
250 #ifdef HAVE_LIBBLKID
251 blkid_probe pr;
252 #endif
253 int rc = 0;
254
255 assert(cxt);
256
257 if (fdisk_dev_has_disklabel(cxt) || cxt->dev_fd < 0)
258 return -EINVAL;
259 #ifdef HAVE_LIBBLKID
260 DBG(CXT, ul_debugobj(cxt, "wipe check: initialize libblkid prober"));
261
262 pr = blkid_new_probe();
263 if (!pr)
264 return -ENOMEM;
265 rc = blkid_probe_set_device(pr, cxt->dev_fd, 0, 0);
266 if (rc)
267 return rc;
268
269 blkid_probe_enable_superblocks(pr, 1);
270 blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
271 blkid_probe_enable_partitions(pr, 1);
272
273 /* we care about the first found FS/raid, so don't call blkid_do_probe()
274 * in loop or don't use blkid_do_fullprobe() ... */
275 rc = blkid_do_probe(pr);
276 if (rc == 0) {
277 const char *name = NULL;
278
279 if (blkid_probe_lookup_value(pr, "TYPE", &name, 0) == 0 ||
280 blkid_probe_lookup_value(pr, "PTTYPE", &name, 0) == 0) {
281 fdisk_warnx(cxt, _(
282 "%s: device contains a valid '%s' signature, it's "
283 "strongly recommended to wipe the device by command wipefs(8) "
284 "if this setup is unexpected to avoid "
285 "possible collisions."), cxt->dev_path, name);
286 rc = 1;
287 }
288 }
289
290 blkid_free_probe(pr);
291 #endif
292 return rc;
293 }
294
295 /**
296 * fdisk_assign_device:
297 * @fname: path to the device to be handled
298 * @readonly: how to open the device
299 *
300 * Open the device, discovery topology, geometry, and detect disklabel.
301 *
302 * Returns: 0 on success, < 0 on error.
303 */
304 int fdisk_assign_device(struct fdisk_context *cxt,
305 const char *fname, int readonly)
306 {
307 int fd;
308
309 DBG(CXT, ul_debugobj(cxt, "assigning device %s", fname));
310 assert(cxt);
311
312 reset_context(cxt);
313
314 fd = open(fname, (readonly ? O_RDONLY : O_RDWR ) | O_CLOEXEC);
315 if (fd < 0)
316 return -errno;
317
318 cxt->readonly = readonly;
319 cxt->dev_fd = fd;
320 cxt->dev_path = strdup(fname);
321 if (!cxt->dev_path)
322 goto fail;
323
324 fdisk_discover_topology(cxt);
325 fdisk_discover_geometry(cxt);
326
327 if (fdisk_read_firstsector(cxt) < 0)
328 goto fail;
329
330 /* detect labels and apply labes specific stuff (e.g geomery)
331 * to the context */
332 fdisk_probe_labels(cxt);
333
334 /* let's apply user geometry *after* label prober
335 * to make it possible to override in-label setting */
336 fdisk_apply_user_device_properties(cxt);
337
338 /* warn about obsolete stuff on the device if we aren't in
339 * list-only mode and there is not PT yet */
340 if (!fdisk_is_listonly(cxt) && !fdisk_dev_has_disklabel(cxt))
341 warn_wipe(cxt);
342
343 DBG(CXT, ul_debugobj(cxt, "initialized for %s [%s]",
344 fname, readonly ? "READ-ONLY" : "READ-WRITE"));
345 return 0;
346 fail:
347 DBG(CXT, ul_debugobj(cxt, "failed to assign device"));
348 return -errno;
349 }
350
351 int fdisk_deassign_device(struct fdisk_context *cxt, int nosync)
352 {
353 assert(cxt);
354 assert(cxt->dev_fd >= 0);
355
356 if (cxt->readonly || nosync)
357 close(cxt->dev_fd);
358
359 else {
360 if (fsync(cxt->dev_fd) || close(cxt->dev_fd)) {
361 fdisk_warn(cxt, _("%s: close device failed"),
362 cxt->dev_path);
363 return -errno;
364 }
365
366 fdisk_info(cxt, _("Syncing disks."));
367 sync();
368 }
369 cxt->dev_fd = -1;
370 return 0;
371 }
372
373 int fdisk_is_readonly(struct fdisk_context *cxt)
374 {
375 assert(cxt);
376 return cxt->readonly;
377 }
378
379 /**
380 * fdisk_free_context:
381 * @cxt: fdisk context
382 *
383 * Deallocates context struct.
384 */
385 void fdisk_free_context(struct fdisk_context *cxt)
386 {
387 int i;
388
389 if (!cxt)
390 return;
391
392 DBG(CXT, ul_debugobj(cxt, "freeing context %p for %s", cxt, cxt->dev_path));
393 reset_context(cxt);
394
395 /* deallocate label's private stuff */
396 for (i = 0; i < cxt->nlabels; i++) {
397 if (!cxt->labels[i])
398 continue;
399 if (cxt->labels[i]->op->free)
400 cxt->labels[i]->op->free(cxt->labels[i]);
401 else
402 free(cxt->labels[i]);
403 }
404
405 free(cxt);
406 }
407
408 /**
409 * fdisk_set_ask:
410 * @cxt: context
411 * @ask_cb: callback
412 * @data: callback data
413 *
414 * Set callbacks for dialog driven partitioning and library warnings/errors.
415 *
416 * Returns: 0 on success, < 0 on error.
417 */
418 int fdisk_set_ask(struct fdisk_context *cxt,
419 int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
420 void *data)
421 {
422 assert(cxt);
423
424 cxt->ask_cb = ask_cb;
425 cxt->ask_data = data;
426 return 0;
427 }
428
429 /**
430 * fdisk_enable_details:
431 * @cxt: context
432 * @enable: true/flase
433 *
434 * Enables or disables "details" display mode. This function has effect to
435 * fdisk_partition_to_string() function.
436 *
437 * Returns: 0 on success, < 0 on error.
438 */
439 int fdisk_enable_details(struct fdisk_context *cxt, int enable)
440 {
441 assert(cxt);
442 cxt->display_details = enable ? 1 : 0;
443 return 0;
444 }
445
446 /**
447 * fdisk_is_details:
448 * @cxt: context
449 *
450 * Returns: 1 if details are enabled
451 */
452 int fdisk_is_details(struct fdisk_context *cxt)
453 {
454 assert(cxt);
455 return cxt->display_details == 1;
456 }
457
458 /**
459 * fdisk_enable_listonly:
460 * @cxt: context
461 * @enable: true/flase
462 *
463 * Just list partition only, don't care about another details, mistakes, ...
464 *
465 * Returns: 0 on success, < 0 on error.
466 */
467 int fdisk_enable_listonly(struct fdisk_context *cxt, int enable)
468 {
469 assert(cxt);
470 cxt->listonly = enable ? 1 : 0;
471 return 0;
472 }
473
474 /**
475 * fdisk_is_listonly:
476 * @cxt: context
477 *
478 * Returns: 1 if list-only mode enabled
479 */
480 int fdisk_is_listonly(struct fdisk_context *cxt)
481 {
482 assert(cxt);
483 return cxt->listonly == 1;
484 }
485
486
487 /**
488 * fdisk_set_unit:
489 * @cxt: context
490 * @str: "cylinder" or "sector".
491 *
492 * This is pure shit, unfortunately for example Sun addresses begin of the
493 * partition by cylinders...
494 *
495 * Returns: 0 on succes, <0 on error.
496 */
497 int fdisk_set_unit(struct fdisk_context *cxt, const char *str)
498 {
499 assert(cxt);
500
501 cxt->display_in_cyl_units = 0;
502
503 if (!str)
504 return 0;
505
506 if (strcmp(str, "cylinder") == 0 || strcmp(str, "cylinders") == 0)
507 cxt->display_in_cyl_units = 1;
508
509 else if (strcmp(str, "sector") == 0 || strcmp(str, "sectors") == 0)
510 cxt->display_in_cyl_units = 0;
511
512 DBG(CXT, ul_debugobj(cxt, "display unit: %s", fdisk_get_unit(cxt, 0)));
513 return 0;
514 }
515
516 /**
517 * fdisk_get_unit:
518 * @cxt: context
519 *
520 * Returns: unit name.
521 */
522 const char *fdisk_get_unit(struct fdisk_context *cxt, int n)
523 {
524 assert(cxt);
525
526 if (fdisk_use_cylinders(cxt))
527 return P_("cylinder", "cylinders", n);
528 return P_("sector", "sectors", n);
529 }
530
531 /**
532 * fdisk_use_cylinders:
533 * @@cxt: context
534 *
535 * Returns 1 if user wants to display in cylinders.
536 */
537 int fdisk_use_cylinders(struct fdisk_context *cxt)
538 {
539 assert(cxt);
540 return cxt->display_in_cyl_units == 1;
541 }
542
543 /**
544 * fdisk_get_units_per_sector:
545 * @cxt: context
546 *
547 * This is neccessary only for brain dead situations when we use "cylinders";
548 *
549 * Returns: number of "units" per sector, default is 1 if display unit is sector.
550 */
551 unsigned int fdisk_get_units_per_sector(struct fdisk_context *cxt)
552 {
553 assert(cxt);
554
555 if (fdisk_use_cylinders(cxt)) {
556 assert(cxt->geom.heads);
557 return cxt->geom.heads * cxt->geom.sectors;
558 }
559 return 1;
560 }
561
562 /**
563 * fdisk_get_optimal_iosize:
564 * @cxt: context
565 *
566 * Returns: optimal I/O size
567 */
568 unsigned long fdisk_get_optimal_iosize(struct fdisk_context *cxt)
569 {
570 assert(cxt);
571 return cxt->optimal_io_size;
572 }
573
574 /**
575 * fdisk_get_minimal_iosize:
576 * @cxt: context
577 *
578 * Returns: minimal I/O size
579 */
580 unsigned long fdisk_get_minimal_size(struct fdisk_context *cxt)
581 {
582 assert(cxt);
583 return cxt->min_io_size;
584 }
585
586 /**
587 * fdisk_get_physector_size:
588 * @cxt: context
589 *
590 * Returns: physical sector size
591 */
592 unsigned long fdisk_get_physector_size(struct fdisk_context *cxt)
593 {
594 assert(cxt);
595 return cxt->phy_sector_size;
596 }
597
598 /**
599 * fdisk_get_sector_size:
600 * @cxt: context
601 *
602 * Returns: sector size
603 */
604 unsigned long fdisk_get_sector_size(struct fdisk_context *cxt)
605 {
606 assert(cxt);
607 return cxt->sector_size;
608 }
609
610 /**
611 * fdisk_get_alignment_offset
612 * @cxt: context
613 *
614 * Returns: alignment offset (used by 4K disks for backward compatibility with DOS tools).
615 */
616 unsigned long fdisk_get_alignment_offset(struct fdisk_context *cxt)
617 {
618 assert(cxt);
619 return cxt->alignment_offset;
620 }
621
622 /**
623 * fdisk_get_grain_size:
624 * @cxt: context
625 *
626 * Returns: usual grain used to align partitions
627 */
628 unsigned long fdisk_get_grain_size(struct fdisk_context *cxt)
629 {
630 assert(cxt);
631 return cxt->grain;
632 }
633
634 /**
635 * fdisk_get_first_lba:
636 * @cxt: context
637 *
638 * Returns: first possible LBA on disk for data partitions.
639 */
640 unsigned long fdisk_get_first_lba(struct fdisk_context *cxt)
641 {
642 assert(cxt);
643 return cxt->first_lba;
644 }
645
646 /**
647 * fdisk_get_nsectors:
648 * @cxt: context
649 *
650 * Returns: size of the device in (real) sectors.
651 */
652 unsigned long fdisk_get_nsectors(struct fdisk_context *cxt)
653 {
654 assert(cxt);
655 return cxt->total_sectors;
656 }
657
658 /**
659 * fdisk_get_devname:
660 * @cxt: context
661 *
662 * Returns: device name.
663 */
664 const char *fdisk_get_devname(struct fdisk_context *cxt)
665 {
666 assert(cxt);
667 return cxt->dev_path;
668 }