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