3 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
4 * 2012 Davidlohr Bueso <dave@gnu.org>
6 * This is re-written version for libfdisk, the original was fdiskdoslabel.c
7 * from util-linux fdisk.
11 #include "randutils.h"
19 #define MAXIMUM_PARTS 60
20 #define ACTIVE_FLAG 0x80
25 * @short_description: disk label specific functions
30 #define IS_EXTENDED(i) \
31 ((i) == MBR_DOS_EXTENDED_PARTITION \
32 || (i) == MBR_W95_EXTENDED_PARTITION \
33 || (i) == MBR_LINUX_EXTENDED_PARTITION)
36 * per partition table entry data
38 * The four primary partitions have the same sectorbuffer
39 * and have NULL ex_entry.
41 * Each logical partition table entry has two pointers, one for the
42 * partition and one link to the next one.
45 struct dos_partition
*pt_entry
; /* on-disk MBR entry */
46 struct dos_partition
*ex_entry
; /* on-disk EBR entry */
47 fdisk_sector_t offset
; /* disk sector number */
48 unsigned char *sectorbuffer
; /* disk sector contents */
50 unsigned int changed
: 1,
51 private_sectorbuffer
: 1;
55 * in-memory fdisk GPT stuff
57 struct fdisk_dos_label
{
58 struct fdisk_label head
; /* generic part */
60 struct pte ptes
[MAXIMUM_PARTS
]; /* partition */
61 fdisk_sector_t ext_offset
; /* start of the ext.partition */
62 size_t ext_index
; /* ext.partition index (if ext_offset is set) */
63 unsigned int compatible
: 1, /* is DOS compatible? */
64 non_pt_changed
: 1; /* MBR, but no PT changed */
70 static struct fdisk_parttype dos_parttypes
[] = {
71 #include "pt-mbr-partnames.h"
74 #define set_hsc(h,s,c,sector) { \
75 s = sector % cxt->geom.sectors + 1; \
76 sector /= cxt->geom.sectors; \
77 h = sector % cxt->geom.heads; \
78 sector /= cxt->geom.heads; \
80 s |= (sector >> 2) & 0xc0; \
84 #define sector(s) ((s) & 0x3f)
85 #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
87 #define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
89 #define is_dos_compatible(_x) \
90 (fdisk_is_label(_x, DOS) && \
91 fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
93 #define cround(c, n) fdisk_cround(c, n)
96 static inline struct fdisk_dos_label
*self_label(struct fdisk_context
*cxt
)
100 assert(fdisk_is_label(cxt
, DOS
));
102 return (struct fdisk_dos_label
*) cxt
->label
;
105 static inline struct pte
*self_pte(struct fdisk_context
*cxt
, size_t i
)
107 struct fdisk_dos_label
*l
= self_label(cxt
);
109 if (i
>= ARRAY_SIZE(l
->ptes
))
115 static inline struct dos_partition
*self_partition(
116 struct fdisk_context
*cxt
,
119 struct pte
*pe
= self_pte(cxt
, i
);
120 return pe
? pe
->pt_entry
: NULL
;
123 struct dos_partition
*fdisk_dos_get_partition(
124 struct fdisk_context
*cxt
,
129 assert(fdisk_is_label(cxt
, DOS
));
131 return self_partition(cxt
, i
);
134 static struct fdisk_parttype
*dos_partition_parttype(
135 struct fdisk_context
*cxt
,
136 struct dos_partition
*p
)
138 struct fdisk_parttype
*t
139 = fdisk_label_get_parttype_from_code(cxt
->label
, p
->sys_ind
);
140 return t
? : fdisk_new_unknown_parttype(p
->sys_ind
, NULL
);
144 * Linux kernel cares about partition size only. Things like
145 * partition type or so are completely irrelevant -- kzak Nov-2013
147 static int is_used_partition(struct dos_partition
*p
)
149 return p
&& dos_partition_get_size(p
) != 0;
152 static void partition_set_changed(
153 struct fdisk_context
*cxt
,
157 struct pte
*pe
= self_pte(cxt
, i
);
162 DBG(LABEL
, ul_debug("DOS: setting %zu partition to %s", i
,
163 changed
? "changed" : "unchanged"));
165 pe
->changed
= changed
? 1 : 0;
167 fdisk_label_set_changed(cxt
->label
, 1);
170 static fdisk_sector_t
get_abs_partition_start(struct pte
*pe
)
173 assert(pe
->pt_entry
);
175 return pe
->offset
+ dos_partition_get_start(pe
->pt_entry
);
178 static fdisk_sector_t
get_abs_partition_end(struct pte
*pe
)
183 assert(pe
->pt_entry
);
185 size
= dos_partition_get_size(pe
->pt_entry
);
186 return get_abs_partition_start(pe
) + size
- (size
? 1 : 0);
189 static int is_cleared_partition(struct dos_partition
*p
)
191 return !(!p
|| p
->boot_ind
|| p
->bh
|| p
->bs
|| p
->bc
||
192 p
->sys_ind
|| p
->eh
|| p
->es
|| p
->ec
||
193 dos_partition_get_start(p
) || dos_partition_get_size(p
));
196 static int get_partition_unused_primary(struct fdisk_context
*cxt
,
197 struct fdisk_partition
*pa
,
207 org
= cxt
->label
->nparts_max
;
209 cxt
->label
->nparts_max
= 4;
210 rc
= fdisk_partition_next_partno(pa
, cxt
, &n
);
211 cxt
->label
->nparts_max
= org
;
214 fdisk_info(cxt
, _("All primary partitions have been defined already."));
221 static int seek_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
)
223 off_t offset
= (off_t
) secno
* cxt
->sector_size
;
225 return lseek(cxt
->dev_fd
, offset
, SEEK_SET
) == (off_t
) -1 ? -errno
: 0;
228 static int read_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
,
231 int rc
= seek_sector(cxt
, secno
);
237 r
= read(cxt
->dev_fd
, buf
, cxt
->sector_size
);
238 if (r
== (ssize_t
) cxt
->sector_size
)
245 /* Allocate a buffer and read a partition table sector */
246 static int read_pte(struct fdisk_context
*cxt
, size_t pno
, fdisk_sector_t offset
)
250 struct pte
*pe
= self_pte(cxt
, pno
);
252 buf
= calloc(1, cxt
->sector_size
);
256 DBG(LABEL
, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
257 pno
, (uintmax_t) offset
, buf
));
260 pe
->sectorbuffer
= buf
;
261 pe
->private_sectorbuffer
= 1;
263 rc
= read_sector(cxt
, offset
, pe
->sectorbuffer
);
265 fdisk_warn(cxt
, _("Failed to read extended partition table "
266 "(offset=%ju)"), (uintmax_t) offset
);
271 pe
->pt_entry
= pe
->ex_entry
= NULL
;
276 static void clear_partition(struct dos_partition
*p
)
288 dos_partition_set_start(p
,0);
289 dos_partition_set_size(p
,0);
292 static void dos_init(struct fdisk_context
*cxt
)
294 struct fdisk_dos_label
*l
= self_label(cxt
);
299 assert(fdisk_is_label(cxt
, DOS
));
301 DBG(LABEL
, ul_debug("DOS: initialize, first sector buffer %p", cxt
->firstsector
));
303 cxt
->label
->nparts_max
= 4; /* default, unlimited number of logical */
307 l
->non_pt_changed
= 0;
309 memset(l
->ptes
, 0, sizeof(l
->ptes
));
311 for (i
= 0; i
< 4; i
++) {
312 struct pte
*pe
= self_pte(cxt
, i
);
314 pe
->pt_entry
= mbr_get_partition(cxt
->firstsector
, i
);
317 pe
->sectorbuffer
= cxt
->firstsector
;
318 pe
->private_sectorbuffer
= 0;
322 if (fdisk_is_listonly(cxt
))
325 * Various warnings...
327 if (fdisk_missing_geometry(cxt
))
328 fdisk_warnx(cxt
, _("You can set geometry from the extra functions menu."));
330 if (is_dos_compatible(cxt
)) {
331 fdisk_warnx(cxt
, _("DOS-compatible mode is deprecated."));
333 if (cxt
->sector_size
!= cxt
->phy_sector_size
)
335 "The device presents a logical sector size that is smaller than "
336 "the physical sector size. Aligning to a physical sector (or optimal "
337 "I/O) size boundary is recommended, or performance may be impacted."));
340 if (fdisk_use_cylinders(cxt
))
341 fdisk_warnx(cxt
, _("Cylinders as display units are deprecated."));
343 if (cxt
->total_sectors
> UINT_MAX
) {
344 uint64_t bytes
= cxt
->total_sectors
* cxt
->sector_size
;
345 char *szstr
= size_to_human_string(SIZE_SUFFIX_SPACE
346 | SIZE_SUFFIX_3LETTER
, bytes
);
348 _("The size of this disk is %s (%ju bytes). DOS "
349 "partition table format can not be used on drives for "
350 "volumes larger than %lu bytes for %lu-byte "
351 "sectors. Use GUID partition table format (GPT)."),
353 UINT_MAX
* cxt
->sector_size
,
359 /* callback called by libfdisk */
360 static void dos_deinit(struct fdisk_label
*lb
)
363 struct fdisk_dos_label
*l
= (struct fdisk_dos_label
*) lb
;
365 for (i
= 0; i
< ARRAY_SIZE(l
->ptes
); i
++) {
366 struct pte
*pe
= &l
->ptes
[i
];
368 if (pe
->private_sectorbuffer
&& pe
->sectorbuffer
) {
369 DBG(LABEL
, ul_debug("DOS: freeing pte %zu sector buffer %p",
370 i
, pe
->sectorbuffer
));
371 free(pe
->sectorbuffer
);
373 pe
->sectorbuffer
= NULL
;
374 pe
->private_sectorbuffer
= 0;
377 memset(l
->ptes
, 0, sizeof(l
->ptes
));
380 static void reset_pte(struct pte
*pe
)
384 if (pe
->private_sectorbuffer
) {
385 DBG(LABEL
, ul_debug(" --> freeing pte sector buffer %p",
387 free(pe
->sectorbuffer
);
389 memset(pe
, 0, sizeof(struct pte
));
392 static int dos_delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
394 struct fdisk_dos_label
*l
;
396 struct dos_partition
*p
;
397 struct dos_partition
*q
;
401 assert(fdisk_is_label(cxt
, DOS
));
403 pe
= self_pte(cxt
, partnum
);
407 DBG(LABEL
, ul_debug("DOS: delete partiton %zu (max=%zu)", partnum
,
408 cxt
->label
->nparts_max
));
414 /* Note that for the fifth partition (partnum == 4) we don't actually
415 decrement partitions. */
417 DBG(LABEL
, ul_debug("--> delete primary"));
418 if (IS_EXTENDED(p
->sys_ind
) && partnum
== l
->ext_index
) {
420 DBG(LABEL
, ul_debug(" --> delete extended"));
421 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
422 DBG(LABEL
, ul_debug(" --> delete logical #%zu", i
));
423 reset_pte(&l
->ptes
[i
]);
426 cxt
->label
->nparts_max
= 4;
427 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
431 partition_set_changed(cxt
, partnum
, 1);
433 } else if (!q
->sys_ind
&& partnum
> 4) {
434 DBG(LABEL
, ul_debug("--> delete logical [last in the chain]"));
435 reset_pte(&l
->ptes
[partnum
]);
436 --cxt
->label
->nparts_max
;
438 /* clear link to deleted partition */
439 clear_partition(l
->ptes
[partnum
].ex_entry
);
440 partition_set_changed(cxt
, partnum
, 1);
442 DBG(LABEL
, ul_debug("--> delete logical [move down]"));
444 DBG(LABEL
, ul_debug(" --> delete %zu logical link", partnum
));
445 p
= l
->ptes
[partnum
- 1].ex_entry
;
447 dos_partition_set_start(p
, dos_partition_get_start(q
));
448 dos_partition_set_size(p
, dos_partition_get_size(q
));
449 partition_set_changed(cxt
, partnum
- 1, 1);
451 } else if (cxt
->label
->nparts_max
> 5) {
452 DBG(LABEL
, ul_debug(" --> delete first logical link"));
453 pe
= &l
->ptes
[5]; /* second logical */
455 if (pe
->pt_entry
) /* prevent SEGFAULT */
456 dos_partition_set_start(pe
->pt_entry
,
457 get_abs_partition_start(pe
) -
459 pe
->offset
= l
->ext_offset
;
460 partition_set_changed(cxt
, 5, 1);
463 if (cxt
->label
->nparts_max
> 5) {
464 DBG(LABEL
, ul_debug(" --> move ptes"));
465 cxt
->label
->nparts_max
--;
466 reset_pte(&l
->ptes
[partnum
]);
467 while (partnum
< cxt
->label
->nparts_max
) {
468 DBG(LABEL
, ul_debug(" --> moving pte %zu <-- %zu", partnum
, partnum
+ 1));
469 l
->ptes
[partnum
] = l
->ptes
[partnum
+ 1];
472 memset(&l
->ptes
[partnum
], 0, sizeof(struct pte
));
474 DBG(LABEL
, ul_debug(" --> the only logical: clear only"));
475 clear_partition(l
->ptes
[partnum
].pt_entry
);
476 cxt
->label
->nparts_max
--;
479 DBG(LABEL
, ul_debug(" --> clear last logical"));
480 reset_pte(&l
->ptes
[partnum
]);
481 partition_set_changed(cxt
, l
->ext_index
, 1);
486 fdisk_label_set_changed(cxt
->label
, 1);
490 static void read_extended(struct fdisk_context
*cxt
, size_t ext
)
493 struct pte
*pex
, *pe
;
494 struct dos_partition
*p
, *q
;
495 struct fdisk_dos_label
*l
= self_label(cxt
);
498 pex
= self_pte(cxt
, ext
);
499 pex
->ex_entry
= pex
->pt_entry
;
502 if (!dos_partition_get_start(p
)) {
503 fdisk_warnx(cxt
, _("Bad offset in primary extended partition."));
507 DBG(LABEL
, ul_debug("DOS: Reading extended %zu", ext
));
509 while (IS_EXTENDED (p
->sys_ind
)) {
510 pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
512 if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
513 /* This is not a Linux restriction, but
514 this program uses arrays of size MAXIMUM_PARTS.
515 Do not try to `improve' this test. */
516 struct pte
*pre
= self_pte(cxt
,
517 cxt
->label
->nparts_max
- 1);
519 _("Omitting partitions after #%zu. They will be deleted "
520 "if you save this partition table."),
521 cxt
->label
->nparts_max
);
523 clear_partition(pre
->ex_entry
);
524 partition_set_changed(cxt
,
525 cxt
->label
->nparts_max
- 1, 1);
529 if (read_pte(cxt
, cxt
->label
->nparts_max
, l
->ext_offset
+
530 dos_partition_get_start(p
)))
534 l
->ext_offset
= dos_partition_get_start(p
);
536 assert(pe
->sectorbuffer
);
537 q
= p
= mbr_get_partition(pe
->sectorbuffer
, 0);
539 for (i
= 0; i
< 4; i
++, p
++) {
540 if (!dos_partition_get_size(p
))
543 if (IS_EXTENDED (p
->sys_ind
)) {
546 "Extra link pointer in partition "
548 cxt
->label
->nparts_max
+ 1);
551 } else if (p
->sys_ind
) {
554 "Ignoring extra data in partition "
556 cxt
->label
->nparts_max
+ 1);
562 /* very strange code here... */
564 if (q
!= pe
->ex_entry
)
567 pe
->pt_entry
= q
+ 1;
570 if (q
!= pe
->pt_entry
)
573 pe
->ex_entry
= q
+ 1;
577 cxt
->label
->nparts_cur
= ++cxt
->label
->nparts_max
;
579 DBG(LABEL
, ul_debug("DOS: EBR[offset=%ju]: link: type=%x, start=%u, size=%u; "
580 " data: type=%x, start=%u, size=%u",
581 (uintmax_t) pe
->offset
,
582 pe
->ex_entry
->sys_ind
,
583 dos_partition_get_start(pe
->ex_entry
),
584 dos_partition_get_size(pe
->ex_entry
),
585 pe
->pt_entry
->sys_ind
,
586 dos_partition_get_start(pe
->pt_entry
),
587 dos_partition_get_size(pe
->pt_entry
)));
591 /* remove last empty EBR */
592 pe
= self_pte(cxt
, cxt
->label
->nparts_max
- 1);
593 if (is_cleared_partition(pe
->ex_entry
) &&
594 is_cleared_partition(pe
->pt_entry
)) {
595 DBG(LABEL
, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe
->offset
));
597 cxt
->label
->nparts_max
--;
598 cxt
->label
->nparts_cur
--;
601 /* remove empty links */
603 q
= self_partition(cxt
, 4);
604 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
605 p
= self_partition(cxt
, i
);
607 if (!dos_partition_get_size(p
) &&
608 (cxt
->label
->nparts_max
> 5 || q
->sys_ind
)) {
609 fdisk_info(cxt
, _("omitting empty partition (%zu)"), i
+1);
610 dos_delete_partition(cxt
, i
);
611 goto remove
; /* numbering changed */
615 DBG(LABEL
, ul_debug("DOS: nparts_max: %zu", cxt
->label
->nparts_max
));
618 static int dos_create_disklabel(struct fdisk_context
*cxt
)
622 struct fdisk_dos_label
*l
;
626 assert(fdisk_is_label(cxt
, DOS
));
628 DBG(LABEL
, ul_debug("DOS: creating new disklabel"));
632 const char *s
= fdisk_script_get_header(cxt
->script
, "label-id");
636 id
= strtol(s
, &end
, 16);
637 if (!errno
&& end
&& s
< end
)
642 /* random disk signature */
644 random_get_bytes(&id
, sizeof(id
));
646 if (fdisk_has_protected_bootbits(cxt
))
647 rc
= fdisk_init_firstsector_buffer(cxt
, 0, MBR_PT_BOOTBITS_SIZE
);
649 rc
= fdisk_init_firstsector_buffer(cxt
, 0, 0);
656 /* Generate an MBR ID for this disk */
657 mbr_set_id(cxt
->firstsector
, id
);
658 l
->non_pt_changed
= 1;
659 fdisk_label_set_changed(cxt
->label
, 1);
661 /* Put MBR signature */
662 mbr_set_magic(cxt
->firstsector
);
664 fdisk_info(cxt
, _("Created a new DOS disklabel with disk "
665 "identifier 0x%08x."), id
);
669 static int dos_set_disklabel_id(struct fdisk_context
*cxt
)
671 char *end
= NULL
, *str
= NULL
;
672 unsigned int id
, old
;
673 struct fdisk_dos_label
*l
;
678 assert(fdisk_is_label(cxt
, DOS
));
680 DBG(LABEL
, ul_debug("DOS: setting Id"));
683 old
= mbr_get_id(cxt
->firstsector
);
684 rc
= fdisk_ask_string(cxt
,
685 _("Enter the new disk identifier"), &str
);
690 id
= strtoul(str
, &end
, 0);
691 if (errno
|| str
== end
|| (end
&& *end
)) {
692 fdisk_warnx(cxt
, _("Incorrect value."));
697 mbr_set_id(cxt
->firstsector
, id
);
698 l
->non_pt_changed
= 1;
699 fdisk_label_set_changed(cxt
->label
, 1);
701 fdisk_info(cxt
, _("Disk identifier changed from 0x%08x to 0x%08x."),
706 static void get_partition_table_geometry(struct fdisk_context
*cxt
,
707 unsigned int *ph
, unsigned int *ps
)
709 unsigned char *bufp
= cxt
->firstsector
;
710 struct dos_partition
*p
;
716 for (i
= 0; i
< 4; i
++) {
717 p
= mbr_get_partition(bufp
, i
);
718 if (p
->sys_ind
!= 0) {
725 } else if (hh
!= h
|| ss
!= s
)
730 if (!first
&& !bad
) {
735 DBG(LABEL
, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph
, *ps
));
738 static int dos_reset_alignment(struct fdisk_context
*cxt
)
742 assert(fdisk_is_label(cxt
, DOS
));
744 /* overwrite necessary stuff by DOS deprecated stuff */
745 if (is_dos_compatible(cxt
)) {
746 DBG(LABEL
, ul_debug("DOS: reseting alignemnt for DOS-comaptiblem PT"));
747 if (cxt
->geom
.sectors
)
748 cxt
->first_lba
= cxt
->geom
.sectors
; /* usually 63 */
750 cxt
->grain
= cxt
->sector_size
; /* usually 512 */
756 /* TODO: move to include/pt-dos.h and share with libblkid */
757 #define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
758 #define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
760 static int dos_probe_label(struct fdisk_context
*cxt
)
763 unsigned int h
= 0, s
= 0;
767 assert(fdisk_is_label(cxt
, DOS
));
769 /* ignore disks with AIX magic number */
770 if (memcmp(cxt
->firstsector
, AIX_MAGIC_STRING
, AIX_MAGIC_STRLEN
) == 0)
773 if (!mbr_is_valid_magic(cxt
->firstsector
))
778 get_partition_table_geometry(cxt
, &h
, &s
);
781 cxt
->geom
.sectors
= s
;
784 for (i
= 0; i
< 4; i
++) {
785 struct pte
*pe
= self_pte(cxt
, i
);
787 if (is_used_partition(pe
->pt_entry
))
788 cxt
->label
->nparts_cur
++;
790 if (IS_EXTENDED (pe
->pt_entry
->sys_ind
)) {
791 if (cxt
->label
->nparts_max
!= 4)
793 "Ignoring extra extended partition %zu"),
796 read_extended(cxt
, i
);
800 for (i
= 3; i
< cxt
->label
->nparts_max
; i
++) {
801 struct pte
*pe
= self_pte(cxt
, i
);
802 struct fdisk_dos_label
*l
= self_label(cxt
);
804 if (!mbr_is_valid_magic(pe
->sectorbuffer
)) {
806 "Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
807 "be corrected by w(rite)."),
808 pe
->sectorbuffer
[510],
809 pe
->sectorbuffer
[511],
811 partition_set_changed(cxt
, i
, 1);
813 /* mark also extended as changed to update the first EBR
814 * in situation that there is no logical partitions at all */
815 partition_set_changed(cxt
, l
->ext_index
, 1);
822 static void set_partition(struct fdisk_context
*cxt
,
823 int i
, int doext
, fdisk_sector_t start
,
824 fdisk_sector_t stop
, int sysid
, int boot
)
826 struct pte
*pe
= self_pte(cxt
, i
);
827 struct dos_partition
*p
;
828 fdisk_sector_t offset
;
830 assert(!FDISK_IS_UNDEF(start
));
831 assert(!FDISK_IS_UNDEF(stop
));
834 struct fdisk_dos_label
*l
= self_label(cxt
);
836 offset
= l
->ext_offset
;
842 DBG(LABEL
, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, size=%zu, sysid=%02x",
843 i
, doext
? " [extended]" : "",
845 (size_t) (start
- offset
),
846 (size_t) (stop
- start
+ 1),
849 p
->boot_ind
= boot
? ACTIVE_FLAG
: 0;
851 dos_partition_set_start(p
, start
- offset
);
852 dos_partition_set_size(p
, stop
- start
+ 1);
854 if (is_dos_compatible(cxt
) && (start
/(cxt
->geom
.sectors
*cxt
->geom
.heads
) > 1023))
855 start
= cxt
->geom
.heads
*cxt
->geom
.sectors
*1024 - 1;
856 set_hsc(p
->bh
, p
->bs
, p
->bc
, start
);
857 if (is_dos_compatible(cxt
) && (stop
/(cxt
->geom
.sectors
*cxt
->geom
.heads
) > 1023))
858 stop
= cxt
->geom
.heads
*cxt
->geom
.sectors
*1024 - 1;
859 set_hsc(p
->eh
, p
->es
, p
->ec
, stop
);
860 partition_set_changed(cxt
, i
, 1);
863 static fdisk_sector_t
get_unused_start(struct fdisk_context
*cxt
,
864 int part_n
, fdisk_sector_t start
,
865 fdisk_sector_t first
[], fdisk_sector_t last
[])
869 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
870 fdisk_sector_t lastplusoff
;
871 struct pte
*pe
= self_pte(cxt
, i
);
873 if (start
== pe
->offset
)
874 start
+= cxt
->first_lba
;
875 lastplusoff
= last
[i
] + ((part_n
< 4) ? 0 : cxt
->first_lba
);
876 if (start
>= first
[i
] && start
<= lastplusoff
)
877 start
= lastplusoff
+ 1;
883 static void fill_bounds(struct fdisk_context
*cxt
,
884 fdisk_sector_t
*first
, fdisk_sector_t
*last
)
887 struct pte
*pe
= self_pte(cxt
, 0);
888 struct dos_partition
*p
;
890 for (i
= 0; i
< cxt
->label
->nparts_max
; pe
++,i
++) {
892 if (is_cleared_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
893 first
[i
] = 0xffffffff;
896 first
[i
] = get_abs_partition_start(pe
);
897 last
[i
] = get_abs_partition_end(pe
);
902 static int get_start_from_user( struct fdisk_context
*cxt
,
903 fdisk_sector_t
*start
,
906 fdisk_sector_t limit
,
907 struct fdisk_partition
*pa
)
911 /* try to use tepmlate from 'pa' */
912 if (pa
&& pa
->start_follow_default
)
915 else if (pa
&& fdisk_partition_has_start(pa
)) {
916 DBG(LABEL
, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
917 (uintmax_t) pa
->start
, (uintmax_t) low
, (uintmax_t) limit
));
919 if (*start
< low
|| *start
> limit
) {
920 fdisk_warnx(cxt
, _("Start sector %ju out of range."),
925 /* ask user by dialog */
926 struct fdisk_ask
*ask
= fdisk_new_ask();
931 fdisk_ask_set_query(ask
,
932 fdisk_use_cylinders(cxt
) ?
933 _("First cylinder") : _("First sector"));
934 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_NUMBER
);
935 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, low
));
936 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, dflt
));
937 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
939 rc
= fdisk_do_ask(cxt
, ask
);
940 *start
= fdisk_ask_number_get_result(ask
);
941 fdisk_unref_ask(ask
);
944 if (fdisk_use_cylinders(cxt
)) {
945 *start
= (*start
- 1)
946 * fdisk_get_units_per_sector(cxt
);
952 DBG(LABEL
, ul_debug("DOS: start is %ju", (uintmax_t) *start
));
956 static fdisk_sector_t
get_possible_last(struct fdisk_context
*cxt
, size_t n
)
958 fdisk_sector_t limit
;
961 /* logical partitions */
962 struct fdisk_dos_label
*l
= self_label(cxt
);
963 struct pte
*ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
967 limit
= get_abs_partition_end(ext_pe
);
969 /* primary partitions */
970 if (fdisk_use_cylinders(cxt
) || !cxt
->total_sectors
)
971 limit
= cxt
->geom
.heads
* cxt
->geom
.sectors
* cxt
->geom
.cylinders
- 1;
973 limit
= cxt
->total_sectors
- 1;
975 if (limit
> UINT_MAX
)
979 DBG(LABEL
, ul_debug("DOS: last possible sector for #%zu is %ju",
980 n
, (uintmax_t) limit
));
984 /* returns last free sector for area addressed by @start, the first[] and
985 * last[] are fill_bounds() results */
986 static fdisk_sector_t
get_unused_last(struct fdisk_context
*cxt
, size_t n
,
987 fdisk_sector_t start
,
988 fdisk_sector_t first
[])
991 fdisk_sector_t limit
= get_possible_last(cxt
, n
);
993 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
994 struct pte
*pe
= self_pte(cxt
, i
);
996 if (start
< pe
->offset
&& limit
>= pe
->offset
)
997 limit
= pe
->offset
- 1;
998 if (start
< first
[i
] && limit
>= first
[i
])
999 limit
= first
[i
] - 1;
1002 DBG(LABEL
, ul_debug("DOS: unused sector for #%zu is %ju",
1003 n
, (uintmax_t) limit
));
1007 static int add_partition(struct fdisk_context
*cxt
, size_t n
,
1008 struct fdisk_partition
*pa
)
1010 int sys
, read
= 0, rc
, isrel
= 0;
1012 struct fdisk_dos_label
*l
= self_label(cxt
);
1013 struct dos_partition
*p
= self_partition(cxt
, n
);
1014 struct pte
*ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1015 struct fdisk_ask
*ask
= NULL
;
1017 fdisk_sector_t start
, stop
= 0, limit
, temp
,
1018 first
[cxt
->label
->nparts_max
],
1019 last
[cxt
->label
->nparts_max
];
1021 DBG(LABEL
, ul_debug("DOS: adding partition %zu", n
));
1023 sys
= pa
&& pa
->type
? pa
->type
->code
: MBR_LINUX_DATA_PARTITION
;
1025 if (is_used_partition(p
)) {
1026 fdisk_warnx(cxt
, _("Partition %zu is already defined. "
1027 "Delete it before re-adding it."),
1031 fill_bounds(cxt
, first
, last
);
1032 limit
= get_possible_last(cxt
, n
);
1035 if (cxt
->parent
&& fdisk_is_label(cxt
->parent
, GPT
))
1036 start
= 1; /* Bad boy modifies hybrid MBR */
1038 if (cxt
->script
&& pa
&& fdisk_partition_has_start(pa
)
1039 && pa
->start
< cxt
->first_lba
1041 fdisk_set_first_lba(cxt
, 1);
1043 start
= cxt
->first_lba
;
1046 if (l
->ext_offset
) {
1048 first
[l
->ext_index
] = l
->ext_offset
;
1049 last
[l
->ext_index
] = get_abs_partition_end(ext_pe
);
1054 if (cxt
->script
&& pa
&& fdisk_partition_has_start(pa
)
1055 && pa
->start
>= l
->ext_offset
1056 && pa
->start
< l
->ext_offset
+ cxt
->first_lba
)
1057 fdisk_set_first_lba(cxt
, 1);
1059 start
= l
->ext_offset
+ cxt
->first_lba
;
1062 if (fdisk_use_cylinders(cxt
))
1063 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
1064 first
[i
] = (fdisk_cround(cxt
, first
[i
]) - 1)
1065 * fdisk_get_units_per_sector(cxt
);
1069 * Ask for first sector
1072 fdisk_sector_t dflt
, aligned
;
1075 dflt
= start
= get_unused_start(cxt
, n
, start
, first
, last
);
1077 if (n
>= 4 && pa
&& fdisk_partition_has_start(pa
) && cxt
->script
1078 && cxt
->first_lba
> 1
1079 && temp
== start
- cxt
->first_lba
) {
1080 fdisk_set_first_lba(cxt
, 1);
1084 /* the default sector should be aligned and unused */
1086 aligned
= fdisk_align_lba_in_range(cxt
, dflt
, dflt
, limit
);
1087 dflt
= get_unused_start(cxt
, n
, aligned
, first
, last
);
1088 } while (dflt
!= aligned
&& dflt
> aligned
&& dflt
< limit
);
1094 if (start
>= temp
+ fdisk_get_units_per_sector(cxt
)
1096 fdisk_info(cxt
, _("Sector %llu is already allocated."),
1100 if (pa
&& (fdisk_partition_has_start(pa
) ||
1101 pa
->start_follow_default
))
1105 if (!read
&& start
== temp
) {
1106 rc
= get_start_from_user(cxt
, &start
, temp
, dflt
, limit
, pa
);
1111 } while (start
!= temp
|| !read
);
1114 /* The first EBR is stored at begin of the extended partition */
1115 struct pte
*pe
= self_pte(cxt
, n
);
1116 pe
->offset
= l
->ext_offset
;
1119 /* The second (and another) EBR */
1120 struct pte
*pe
= self_pte(cxt
, n
);
1122 pe
->offset
= start
- cxt
->first_lba
;
1123 if (pe
->offset
== l
->ext_offset
) { /* must be corrected */
1125 if (cxt
->first_lba
== 1)
1130 limit
= get_unused_last(cxt
, n
, start
, first
);
1132 if (start
> limit
) {
1133 fdisk_warnx(cxt
, _("No free sectors available."));
1138 * Ask for last sector
1140 if (fdisk_cround(cxt
, start
) == fdisk_cround(cxt
, limit
))
1142 else if (pa
&& pa
->end_follow_default
)
1144 else if (pa
&& fdisk_partition_has_size(pa
)) {
1145 stop
= start
+ pa
->size
;
1146 isrel
= pa
->size_explicit
? 0 : 1;
1147 if (!isrel
&& stop
> start
)
1150 /* ask user by dialog */
1153 ask
= fdisk_new_ask();
1155 fdisk_reset_ask(ask
);
1158 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_OFFSET
);
1160 if (fdisk_use_cylinders(cxt
)) {
1161 fdisk_ask_set_query(ask
, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
1162 fdisk_ask_number_set_unit(ask
,
1164 fdisk_get_units_per_sector(cxt
));
1166 fdisk_ask_set_query(ask
, _("Last sector, +sectors or +size{K,M,G,T,P}"));
1167 fdisk_ask_number_set_unit(ask
,cxt
->sector_size
);
1170 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, start
));
1171 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, limit
));
1172 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
1173 fdisk_ask_number_set_base(ask
, fdisk_cround(cxt
, start
)); /* base for relative input */
1175 rc
= fdisk_do_ask(cxt
, ask
);
1179 stop
= fdisk_ask_number_get_result(ask
);
1180 isrel
= fdisk_ask_number_is_relative(ask
);
1181 if (fdisk_use_cylinders(cxt
)) {
1182 stop
= stop
* fdisk_get_units_per_sector(cxt
) - 1;
1187 if (stop
>= start
&& stop
<= limit
)
1189 fdisk_warnx(cxt
, _("Value out of range."));
1193 DBG(LABEL
, ul_debug("DOS: raw stop: %ju", (uintmax_t) stop
));
1198 if (isrel
&& stop
- start
< (cxt
->grain
/ fdisk_get_sector_size(cxt
))) {
1199 /* Don't try to be smart on very small partitions and don't align so small sizes */
1203 DBG(LABEL
, ul_debug("DOS: don't align end os tiny partition [start=%ju, stop=%ju, grain=%lu]",
1204 (uintmax_t)start
, (uintmax_t)stop
, cxt
->grain
));
1208 if (isrel
&& alignment_required(cxt
)) {
1209 /* the last sector has not been exactly requested (but
1210 * defined by +size{K,M,G} convention), so be smart and
1211 * align the end of the partition. The next partition
1212 * will start at phy.block boundary.
1214 stop
= fdisk_align_lba_in_range(cxt
, stop
, start
, limit
);
1219 DBG(LABEL
, ul_debug("DOS: aligned stop: %ju", (uintmax_t) stop
));
1223 set_partition(cxt
, n
, 0, start
, stop
, sys
, fdisk_partition_is_bootable(pa
));
1225 struct pte
*pe
= self_pte(cxt
, n
);
1226 set_partition(cxt
, n
- 1, 1, pe
->offset
, stop
,
1227 MBR_DOS_EXTENDED_PARTITION
, 0);
1232 struct fdisk_parttype
*t
=
1233 fdisk_label_get_parttype_from_code(cxt
->label
, sys
);
1234 fdisk_info_new_partition(cxt
, n
+ 1, start
, stop
, t
);
1235 fdisk_unref_parttype(t
);
1239 if (IS_EXTENDED(sys
)) {
1240 struct pte
*pen
= self_pte(cxt
, n
);
1243 l
->ext_offset
= start
;
1247 fdisk_label_set_changed(cxt
->label
, 1);
1250 fdisk_unref_ask(ask
);
1254 static int add_logical(struct fdisk_context
*cxt
,
1255 struct fdisk_partition
*pa
,
1264 assert(self_label(cxt
)->ext_offset
);
1266 DBG(LABEL
, ul_debug("DOS: nparts max: %zu", cxt
->label
->nparts_max
));
1267 pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
1269 if (!pe
->sectorbuffer
) {
1270 pe
->sectorbuffer
= calloc(1, cxt
->sector_size
);
1271 if (!pe
->sectorbuffer
)
1273 DBG(LABEL
, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1274 cxt
->label
->nparts_max
, pe
->sectorbuffer
));
1275 pe
->private_sectorbuffer
= 1;
1277 pe
->pt_entry
= mbr_get_partition(pe
->sectorbuffer
, 0);
1278 pe
->ex_entry
= pe
->pt_entry
+ 1;
1280 partition_set_changed(cxt
, cxt
->label
->nparts_max
, 1);
1282 cxt
->label
->nparts_max
++;
1284 /* this message makes sense only when we use extended/primary/logical
1285 * dialog. The dialog is disable for scripts, see dos_add_partition() */
1287 fdisk_info(cxt
, _("Adding logical partition %zu"),
1288 cxt
->label
->nparts_max
);
1289 *partno
= cxt
->label
->nparts_max
- 1;
1290 rc
= add_partition(cxt
, *partno
, pa
);
1293 /* reset on error */
1294 cxt
->label
->nparts_max
--;
1295 pe
->pt_entry
= NULL
;
1296 pe
->ex_entry
= NULL
;
1304 static void check(struct fdisk_context
*cxt
, size_t n
,
1305 unsigned int h
, unsigned int s
, unsigned int c
,
1308 unsigned int total
, real_s
, real_c
;
1310 if (!is_dos_compatible(cxt
))
1313 real_s
= sector(s
) - 1;
1314 real_c
= cylinder(s
, c
);
1315 total
= (real_c
* cxt
->geom
.heads
+ h
) * cxt
->geom
.sectors
+ real_s
;
1318 fdisk_warnx(cxt
, _("Partition %zu: contains sector 0"), n
);
1319 if (h
>= cxt
->geom
.heads
)
1320 fdisk_warnx(cxt
, _("Partition %zu: head %d greater than "
1321 "maximum %d"), n
, h
+ 1, cxt
->geom
.heads
);
1322 if (real_s
>= cxt
->geom
.sectors
)
1323 fdisk_warnx(cxt
, _("Partition %zu: sector %d greater than "
1324 "maximum %llu"), n
, s
, cxt
->geom
.sectors
);
1325 if (real_c
>= cxt
->geom
.cylinders
)
1326 fdisk_warnx(cxt
, _("Partition %zu: cylinder %d greater than "
1329 cxt
->geom
.cylinders
);
1331 if (cxt
->geom
.cylinders
<= 1024 && start
!= total
)
1332 fdisk_warnx(cxt
, _("Partition %zu: previous sectors %u "
1333 "disagrees with total %u"), n
, start
, total
);
1336 /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1337 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1338 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1339 * Lubkin Oct. 1991). */
1342 long2chs(struct fdisk_context
*cxt
, unsigned long ls
,
1343 unsigned int *c
, unsigned int *h
, unsigned int *s
) {
1344 int spc
= cxt
->geom
.heads
* cxt
->geom
.sectors
;
1348 *h
= ls
/ cxt
->geom
.sectors
;
1349 *s
= ls
% cxt
->geom
.sectors
+ 1; /* sectors count from 1 */
1352 static void check_consistency(struct fdisk_context
*cxt
, struct dos_partition
*p
,
1355 unsigned int pbc
, pbh
, pbs
; /* physical beginning c, h, s */
1356 unsigned int pec
, peh
, pes
; /* physical ending c, h, s */
1357 unsigned int lbc
, lbh
, lbs
; /* logical beginning c, h, s */
1358 unsigned int lec
, leh
, les
; /* logical ending c, h, s */
1360 if (!is_dos_compatible(cxt
))
1363 if (!cxt
->geom
.heads
|| !cxt
->geom
.sectors
|| (partition
>= 4))
1364 return; /* do not check extended partitions */
1366 /* physical beginning c, h, s */
1367 pbc
= (p
->bc
& 0xff) | ((p
->bs
<< 2) & 0x300);
1371 /* physical ending c, h, s */
1372 pec
= (p
->ec
& 0xff) | ((p
->es
<< 2) & 0x300);
1376 /* compute logical beginning (c, h, s) */
1377 long2chs(cxt
, dos_partition_get_start(p
), &lbc
, &lbh
, &lbs
);
1379 /* compute logical ending (c, h, s) */
1380 long2chs(cxt
, dos_partition_get_start(p
) + dos_partition_get_size(p
) - 1, &lec
, &leh
, &les
);
1382 /* Same physical / logical beginning? */
1383 if (cxt
->geom
.cylinders
<= 1024
1384 && (pbc
!= lbc
|| pbh
!= lbh
|| pbs
!= lbs
)) {
1385 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1386 "beginnings (non-Linux?): "
1387 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1393 /* Same physical / logical ending? */
1394 if (cxt
->geom
.cylinders
<= 1024
1395 && (pec
!= lec
|| peh
!= leh
|| pes
!= les
)) {
1396 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1397 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1403 /* Ending on cylinder boundary? */
1404 if (peh
!= (cxt
->geom
.heads
- 1) || pes
!= cxt
->geom
.sectors
) {
1405 fdisk_warnx(cxt
, _("Partition %zu: does not end on "
1406 "cylinder boundary."),
1411 static int dos_verify_disklabel(struct fdisk_context
*cxt
)
1414 fdisk_sector_t total
= 1, n_sectors
= cxt
->total_sectors
;
1415 fdisk_sector_t first
[cxt
->label
->nparts_max
],
1416 last
[cxt
->label
->nparts_max
];
1417 struct dos_partition
*p
;
1418 struct fdisk_dos_label
*l
= self_label(cxt
);
1420 assert(fdisk_is_label(cxt
, DOS
));
1422 fill_bounds(cxt
, first
, last
);
1423 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
1424 struct pte
*pe
= self_pte(cxt
, i
);
1426 p
= self_partition(cxt
, i
);
1427 if (is_used_partition(p
) && !IS_EXTENDED(p
->sys_ind
)) {
1428 check_consistency(cxt
, p
, i
);
1429 if (get_abs_partition_start(pe
) < first
[i
])
1431 "Partition %zu: bad start-of-data."),
1434 check(cxt
, i
+ 1, p
->eh
, p
->es
, p
->ec
, last
[i
]);
1435 total
+= last
[i
] + 1 - first
[i
];
1438 total
+= get_abs_partition_start(pe
) - 1;
1440 for (j
= 0; j
< i
; j
++) {
1441 if ((first
[i
] >= first
[j
] && first
[i
] <= last
[j
])
1442 || ((last
[i
] <= last
[j
] && last
[i
] >= first
[j
]))) {
1444 fdisk_warnx(cxt
, _("Partition %zu: "
1445 "overlaps partition %zu."),
1448 total
+= first
[i
] >= first
[j
] ?
1449 first
[i
] : first
[j
];
1450 total
-= last
[i
] <= last
[j
] ?
1457 if (l
->ext_offset
) {
1458 fdisk_sector_t e_last
;
1459 struct pte
*ext_pe
= self_pte(cxt
, l
->ext_index
);
1461 e_last
= get_abs_partition_end(ext_pe
);
1463 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1465 p
= self_partition(cxt
, i
);
1468 if (i
!= 4 || i
+ 1 < cxt
->label
->nparts_max
)
1470 _("Partition %zu: empty."),
1472 } else if (first
[i
] < l
->ext_offset
1473 || last
[i
] > e_last
) {
1475 fdisk_warnx(cxt
, _("Logical partition %zu: "
1476 "not entirely in partition %zu."),
1477 i
+ 1, l
->ext_index
+ 1);
1482 if (total
> n_sectors
)
1483 fdisk_warnx(cxt
, _("Total allocated sectors %llu greater "
1484 "than the maximum %llu."), total
, n_sectors
);
1485 else if (total
< n_sectors
)
1486 fdisk_warnx(cxt
, _("Remaining %lld unallocated %ld-byte "
1487 "sectors."), n_sectors
- total
, cxt
->sector_size
);
1493 * Ask the user for new partition type information (logical, extended).
1494 * This function calls the actual partition adding logic - add_partition.
1498 static int dos_add_partition(struct fdisk_context
*cxt
,
1499 struct fdisk_partition
*pa
,
1502 size_t i
, free_primary
= 0, free_sectors
= 0;
1503 fdisk_sector_t last
= 0, grain
;
1505 struct fdisk_dos_label
*l
;
1507 size_t res
; /* partno */
1511 assert(fdisk_is_label(cxt
, DOS
));
1513 DBG(LABEL
, ul_debug("DOS: new partition wanted"));
1515 l
= self_label(cxt
);
1516 ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1519 * partition template (@pa) based partitioning
1522 /* pa specifies start within extended partition, add logical */
1523 if (pa
&& fdisk_partition_has_start(pa
) && ext_pe
1524 && pa
->start
>= l
->ext_offset
1525 && pa
->start
<= get_abs_partition_end(ext_pe
)) {
1526 DBG(LABEL
, ul_debug("DOS: pa template %p: add logical", pa
));
1527 rc
= add_logical(cxt
, pa
, &res
);
1530 /* pa specifies that extended partition is wanted */
1531 } else if (pa
&& pa
->type
&& IS_EXTENDED(pa
->type
->code
)) {
1532 DBG(LABEL
, ul_debug("DOS: pa template %p: add extened", pa
));
1533 if (l
->ext_offset
) {
1534 fdisk_warnx(cxt
, _("Extended partition already exists."));
1537 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1539 rc
= add_partition(cxt
, res
, pa
);
1543 /* pa specifies start, but outside extended partition */
1544 } else if (pa
&& fdisk_partition_has_start(pa
) && l
->ext_offset
) {
1545 DBG(LABEL
, ul_debug("DOS: pa template %p: add primary", pa
));
1546 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1548 rc
= add_partition(cxt
, res
, pa
);
1554 * dialog driven partitioning (it does not mean that @pa template is
1555 * completely ignored!)
1558 /* check if there is space for primary partition */
1559 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
1560 last
= cxt
->first_lba
;
1562 for (i
= 0; i
< 4; i
++) {
1563 struct dos_partition
*p
= self_partition(cxt
, i
);
1565 if (is_used_partition(p
)) {
1566 fdisk_sector_t start
= dos_partition_get_start(p
);
1567 if (last
+ grain
<= start
)
1569 last
= start
+ dos_partition_get_size(p
);
1573 if (last
+ grain
< cxt
->total_sectors
- 1)
1576 if (!free_primary
&& cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1577 fdisk_info(cxt
, _("The maximum number of partitions has "
1583 if (!free_primary
|| !free_sectors
) {
1584 DBG(LABEL
, ul_debug("DOS: primary impossible, add logical"));
1585 if (l
->ext_offset
) {
1586 if (!pa
|| fdisk_partition_has_start(pa
)) {
1589 msg
= _("All primary partitions are in use.");
1591 msg
= _("All space for primary partitions is in use.");
1593 if (pa
&& fdisk_partition_has_start(pa
)) {
1594 fdisk_warnx(cxt
, msg
);
1597 fdisk_info(cxt
, msg
);
1599 rc
= add_logical(cxt
, pa
, &res
);
1602 fdisk_info(cxt
, _("All space for primary partitions is in use."));
1604 /* TRANSLATORS: Try to keep this within 80 characters. */
1605 fdisk_info(cxt
, _("To create more partitions, first replace "
1606 "a primary with an extended partition."));
1609 } else if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1610 fdisk_info(cxt
, _("All logical partitions are in use. "
1611 "Adding a primary partition."));
1612 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1614 rc
= add_partition(cxt
, res
, pa
);
1617 struct fdisk_ask
*ask
;
1620 /* the default layout for scripts is to create primary partitions */
1622 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1624 rc
= add_partition(cxt
, res
, pa
);
1628 ask
= fdisk_new_ask();
1631 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_MENU
);
1632 fdisk_ask_set_query(ask
, _("Partition type"));
1633 fdisk_ask_menu_set_default(ask
, free_primary
== 1
1634 && !l
->ext_offset
? 'e' : 'p');
1635 snprintf(hint
, sizeof(hint
),
1636 _("%zu primary, %d extended, %zu free"),
1637 4 - (l
->ext_offset
? 1 : 0) - free_primary
,
1638 l
->ext_offset
? 1 : 0,
1641 fdisk_ask_menu_add_item(ask
, 'p', _("primary"), hint
);
1643 fdisk_ask_menu_add_item(ask
, 'e', _("extended"), _("container for logical partitions"));
1645 fdisk_ask_menu_add_item(ask
, 'l', _("logical"), _("numbered from 5"));
1647 rc
= fdisk_do_ask(cxt
, ask
);
1650 fdisk_ask_menu_get_result(ask
, &c
);
1651 fdisk_unref_ask(ask
);
1654 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1656 rc
= add_partition(cxt
, res
, pa
);
1658 } else if (c
== 'l' && l
->ext_offset
) {
1659 rc
= add_logical(cxt
, pa
, &res
);
1661 } else if (c
== 'e' && !l
->ext_offset
) {
1662 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1664 struct fdisk_partition
*xpa
= NULL
;
1665 struct fdisk_parttype
*t
;
1667 t
= fdisk_label_get_parttype_from_code(cxt
->label
,
1668 MBR_DOS_EXTENDED_PARTITION
);
1670 pa
= xpa
= fdisk_new_partition();
1674 fdisk_partition_set_type(pa
, t
);
1675 rc
= add_partition(cxt
, res
, pa
);
1677 fdisk_unref_partition(xpa
);
1683 fdisk_warnx(cxt
, _("Invalid partition type `%c'."), c
);
1687 cxt
->label
->nparts_cur
++;
1694 static int write_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
,
1699 rc
= seek_sector(cxt
, secno
);
1701 fdisk_warn(cxt
, _("Cannot write sector %jd: seek failed"),
1706 DBG(LABEL
, ul_debug("DOS: writting to sector %ju", (uintmax_t) secno
));
1708 if (write(cxt
->dev_fd
, buf
, cxt
->sector_size
) != (ssize_t
) cxt
->sector_size
)
1713 static int dos_write_disklabel(struct fdisk_context
*cxt
)
1715 struct fdisk_dos_label
*l
= self_label(cxt
);
1717 int rc
= 0, mbr_changed
= 0;
1721 assert(fdisk_is_label(cxt
, DOS
));
1723 DBG(LABEL
, ul_debug("DOS: write PT requested [label-changed: %d, non-pt-changed: %d]",
1724 cxt
->label
->changed
, l
->non_pt_changed
));
1726 mbr_changed
= l
->non_pt_changed
;
1728 /* MBR (primary partitions) */
1730 for (i
= 0; i
< 4; i
++) {
1731 struct pte
*pe
= self_pte(cxt
, i
);
1737 DBG(LABEL
, ul_debug("DOS: MBR changed, writting"));
1738 mbr_set_magic(cxt
->firstsector
);
1739 rc
= write_sector(cxt
, 0, cxt
->firstsector
);
1744 if (cxt
->label
->nparts_max
<= 4 && l
->ext_offset
) {
1745 /* we have empty extended partition, check if the partition has
1746 * been modified and then cleanup possible remaining EBR */
1747 struct pte
*pe
= self_pte(cxt
, l
->ext_index
);
1748 unsigned char empty
[512] = { 0 };
1749 fdisk_sector_t off
= pe
? get_abs_partition_start(pe
) : 0;
1751 if (off
&& pe
->changed
) {
1752 mbr_set_magic(empty
);
1753 write_sector(cxt
, off
, empty
);
1757 /* EBR (logical partitions) */
1758 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1759 struct pte
*pe
= self_pte(cxt
, i
);
1761 if (!pe
->changed
|| !pe
->offset
|| !pe
->sectorbuffer
)
1764 mbr_set_magic(pe
->sectorbuffer
);
1765 rc
= write_sector(cxt
, pe
->offset
, pe
->sectorbuffer
);
1774 static int dos_locate_disklabel(struct fdisk_context
*cxt
, int n
,
1775 const char **name
, uint64_t *offset
, size_t *size
)
1790 /* extended partitions */
1791 if ((size_t)n
- 1 + 4 < cxt
->label
->nparts_max
) {
1792 struct pte
*pe
= self_pte(cxt
, n
- 1 + 4);
1794 assert(pe
->private_sectorbuffer
);
1797 *offset
= (uint64_t) pe
->offset
* cxt
->sector_size
;
1808 * Check whether partition entries are ordered by their starting positions.
1809 * Return 0 if OK. Return i if partition i should have been earlier.
1810 * Two separate checks: primary and logical partitions.
1812 static int wrong_p_order(struct fdisk_context
*cxt
, size_t *prev
)
1814 size_t last_p_start_pos
= 0, p_start_pos
;
1815 size_t i
, last_i
= 0;
1817 for (i
= 0 ; i
< cxt
->label
->nparts_max
; i
++) {
1819 struct pte
*pe
= self_pte(cxt
, i
);
1820 struct dos_partition
*p
= pe
->pt_entry
;
1824 last_p_start_pos
= 0;
1826 if (is_used_partition(p
)) {
1827 p_start_pos
= get_abs_partition_start(pe
);
1829 if (last_p_start_pos
> p_start_pos
) {
1835 last_p_start_pos
= p_start_pos
;
1842 static int dos_get_disklabel_item(struct fdisk_context
*cxt
, struct fdisk_labelitem
*item
)
1848 assert(fdisk_is_label(cxt
, DOS
));
1851 case FDISK_LABELITEM_ID
:
1853 unsigned int num
= mbr_get_id(cxt
->firstsector
);
1854 item
->name
= _("Disk identifier");
1856 if (asprintf(&item
->data
.str
, "0x%08x", num
) < 0)
1861 if (item
->id
< __FDISK_NLABELITEMS
)
1862 rc
= 1; /* unssupported generic item */
1864 rc
= 2; /* out of range */
1872 static int dos_get_partition(struct fdisk_context
*cxt
, size_t n
,
1873 struct fdisk_partition
*pa
)
1875 struct dos_partition
*p
;
1877 struct fdisk_dos_label
*lb
;
1882 assert(fdisk_is_label(cxt
, DOS
));
1884 lb
= self_label(cxt
);
1885 pe
= self_pte(cxt
, n
);
1887 pa
->used
= !is_cleared_partition(p
);
1891 pa
->type
= dos_partition_parttype(cxt
, p
);
1892 pa
->boot
= p
->boot_ind
== ACTIVE_FLAG
? 1 : 0;
1893 pa
->start
= get_abs_partition_start(pe
);
1894 pa
->size
= dos_partition_get_size(p
);
1895 pa
->container
= lb
->ext_offset
&& n
== lb
->ext_index
;
1898 pa
->parent_partno
= lb
->ext_index
;
1900 if (p
->boot_ind
&& asprintf(&pa
->attrs
, "%02x", p
->boot_ind
) < 0)
1904 if (asprintf(&pa
->start_chs
, "%d/%d/%d",
1905 cylinder(p
->bs
, p
->bc
),
1911 if (asprintf(&pa
->end_chs
, "%d/%d/%d",
1912 cylinder(p
->es
, p
->ec
),
1920 static int has_logical(struct fdisk_context
*cxt
)
1923 struct fdisk_dos_label
*l
= self_label(cxt
);
1925 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1926 if (l
->ptes
[i
].pt_entry
)
1932 static int dos_set_partition(struct fdisk_context
*cxt
, size_t n
,
1933 struct fdisk_partition
*pa
)
1935 struct fdisk_dos_label
*l
;
1936 struct dos_partition
*p
;
1939 fdisk_sector_t start
, size
;
1944 assert(fdisk_is_label(cxt
, DOS
));
1946 if (n
>= cxt
->label
->nparts_max
)
1949 l
= self_label(cxt
);
1950 p
= self_partition(cxt
, n
);
1951 pe
= self_pte(cxt
, n
);
1952 orgtype
= p
->sys_ind
;
1955 if (IS_EXTENDED(pa
->type
->code
) && l
->ext_offset
) {
1956 fdisk_warnx(cxt
, _("Extended partition already exists."));
1960 if (!pa
->type
->code
)
1961 fdisk_warnx(cxt
, _("Type 0 means free space to many systems. "
1962 "Having partitions of type 0 is probably unwise."));
1964 if (IS_EXTENDED(p
->sys_ind
) && !IS_EXTENDED(pa
->type
->code
) && has_logical(cxt
)) {
1966 "Cannot change type of the extended partition which is "
1967 "already used by logical partitions. Delete logical "
1968 "partitions first."));
1973 FDISK_INIT_UNDEF(start
);
1974 FDISK_INIT_UNDEF(size
);
1976 if (fdisk_partition_has_start(pa
))
1978 if (fdisk_partition_has_size(pa
))
1981 if (!FDISK_IS_UNDEF(start
) || !FDISK_IS_UNDEF(size
)) {
1982 DBG(LABEL
, ul_debug("DOS: resize partition"));
1984 if (FDISK_IS_UNDEF(start
))
1985 start
= get_abs_partition_start(pe
);
1986 if (FDISK_IS_UNDEF(size
))
1987 size
= dos_partition_get_size(p
);
1989 set_partition(cxt
, n
, 0, start
, start
+ size
- 1,
1990 pa
->type
? pa
->type
->code
: p
->sys_ind
,
1991 FDISK_IS_UNDEF(pa
->boot
) ?
1992 p
->boot_ind
== ACTIVE_FLAG
:
1993 fdisk_partition_is_bootable(pa
));
1995 DBG(LABEL
, ul_debug("DOS: keep size, modify properties"));
1997 p
->sys_ind
= pa
->type
->code
;
1998 if (!FDISK_IS_UNDEF(pa
->boot
))
1999 p
->boot_ind
= fdisk_partition_is_bootable(pa
) ? ACTIVE_FLAG
: 0;
2003 if (IS_EXTENDED(pa
->type
->code
) && !IS_EXTENDED(orgtype
)) {
2004 /* new extended partition - create a reference */
2006 l
->ext_offset
= dos_partition_get_start(p
);
2008 } else if (IS_EXTENDED(orgtype
)) {
2009 /* remove extended partition */
2010 cxt
->label
->nparts_max
= 4;
2011 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
2017 partition_set_changed(cxt
, n
, 1);
2021 static void print_chain_of_logicals(struct fdisk_context
*cxt
)
2024 struct fdisk_dos_label
*l
= self_label(cxt
);
2026 fputc('\n', stdout
);
2028 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2029 struct pte
*pe
= self_pte(cxt
, i
);
2031 fprintf(stderr
, "#%02zu EBR [%10ju], "
2032 "data[start=%10ju (%10ju), size=%10ju], "
2033 "link[start=%10ju (%10ju), size=%10ju]\n",
2034 i
, (uintmax_t) pe
->offset
,
2036 (uintmax_t) dos_partition_get_start(pe
->pt_entry
),
2037 (uintmax_t) get_abs_partition_start(pe
),
2038 (uintmax_t) dos_partition_get_size(pe
->pt_entry
),
2040 (uintmax_t) dos_partition_get_start(pe
->ex_entry
),
2041 (uintmax_t) l
->ext_offset
+ dos_partition_get_start(pe
->ex_entry
),
2042 (uintmax_t) dos_partition_get_size(pe
->ex_entry
));
2046 static int cmp_ebr_offsets(const void *a
, const void *b
)
2048 struct pte
*ae
= (struct pte
*) a
,
2049 *be
= (struct pte
*) b
;
2051 if (ae
->offset
== 0 && be
->offset
== 0)
2053 if (ae
->offset
== 0)
2055 if (be
->offset
== 0)
2058 return cmp_numbers(ae
->offset
, be
->offset
);
2062 * Fix the chain of logicals.
2064 * The function does not modify data partitions within EBR tables
2065 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
2066 * (pte->ex_entry) between EBR tables.
2069 static void fix_chain_of_logicals(struct fdisk_context
*cxt
)
2071 struct fdisk_dos_label
*l
= self_label(cxt
);
2075 DBG(LABEL
, print_chain_of_logicals(cxt
));
2077 /* Sort chain by EBR offsets */
2078 qsort(&l
->ptes
[4], cxt
->label
->nparts_max
- 4, sizeof(struct pte
),
2082 /* Sort data partitions by start */
2083 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
2084 struct pte
*cur
= self_pte(cxt
, i
),
2085 *nxt
= self_pte(cxt
, i
+ 1);
2087 if (get_abs_partition_start(cur
) >
2088 get_abs_partition_start(nxt
)) {
2090 struct dos_partition tmp
= *cur
->pt_entry
;
2091 fdisk_sector_t cur_start
= get_abs_partition_start(cur
),
2092 nxt_start
= get_abs_partition_start(nxt
);
2094 /* swap data partitions */
2095 *cur
->pt_entry
= *nxt
->pt_entry
;
2096 *nxt
->pt_entry
= tmp
;
2098 /* Recount starts according to EBR offsets, the absolute
2099 * address tas to be still the same! */
2100 dos_partition_set_start(cur
->pt_entry
, nxt_start
- cur
->offset
);
2101 dos_partition_set_start(nxt
->pt_entry
, cur_start
- nxt
->offset
);
2103 partition_set_changed(cxt
, i
, 1);
2104 partition_set_changed(cxt
, i
+ 1, 1);
2109 /* Update EBR links */
2110 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
2111 struct pte
*cur
= self_pte(cxt
, i
),
2112 *nxt
= self_pte(cxt
, i
+ 1);
2114 fdisk_sector_t noff
= nxt
->offset
- l
->ext_offset
,
2115 ooff
= dos_partition_get_start(cur
->ex_entry
);
2120 DBG(LABEL
, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2121 (uintmax_t) cur
->offset
,
2122 (uintmax_t) ooff
, (uintmax_t) noff
));
2124 set_partition(cxt
, i
, 1, nxt
->offset
,
2125 get_abs_partition_end(nxt
),
2126 MBR_DOS_EXTENDED_PARTITION
, 0);
2129 /* always terminate the chain ! */
2130 last
= self_pte(cxt
, cxt
->label
->nparts_max
- 1);
2132 clear_partition(last
->ex_entry
);
2133 partition_set_changed(cxt
, cxt
->label
->nparts_max
- 1, 1);
2136 DBG(LABEL
, print_chain_of_logicals(cxt
));
2139 static int dos_reorder(struct fdisk_context
*cxt
)
2141 struct pte
*pei
, *pek
;
2144 if (!wrong_p_order(cxt
, NULL
)) {
2145 fdisk_info(cxt
, _("Nothing to do. Ordering is correct already."));
2149 while ((i
= wrong_p_order(cxt
, &k
)) != 0 && i
< 4) {
2150 /* partition i should have come earlier, move it */
2151 /* We have to move data in the MBR */
2152 struct dos_partition
*pi
, *pk
, *pe
, pbuf
;
2153 pei
= self_pte(cxt
, i
);
2154 pek
= self_pte(cxt
, k
);
2157 pei
->ex_entry
= pek
->ex_entry
;
2163 memmove(&pbuf
, pi
, sizeof(struct dos_partition
));
2164 memmove(pi
, pk
, sizeof(struct dos_partition
));
2165 memmove(pk
, &pbuf
, sizeof(struct dos_partition
));
2167 partition_set_changed(cxt
, i
, 1);
2168 partition_set_changed(cxt
, k
, 1);
2172 fix_chain_of_logicals(cxt
);
2177 /* TODO: use fdisk_set_partition() API */
2178 int fdisk_dos_move_begin(struct fdisk_context
*cxt
, size_t i
)
2181 struct dos_partition
*p
;
2182 unsigned int new, free_start
, curr_start
, last
;
2188 assert(fdisk_is_label(cxt
, DOS
));
2190 pe
= self_pte(cxt
, i
);
2193 if (!is_used_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
2194 fdisk_warnx(cxt
, _("Partition %zu: no data area."), i
+ 1);
2198 /* the default start is at the second sector of the disk or at the
2199 * second sector of the extended partition
2201 free_start
= pe
->offset
? pe
->offset
+ 1 : 1;
2203 curr_start
= get_abs_partition_start(pe
);
2205 /* look for a free space before the current start of the partition */
2206 for (x
= 0; x
< cxt
->label
->nparts_max
; x
++) {
2208 struct pte
*prev_pe
= self_pte(cxt
, x
);
2209 struct dos_partition
*prev_p
= prev_pe
->pt_entry
;
2213 end
= get_abs_partition_start(prev_pe
)
2214 + dos_partition_get_size(prev_p
);
2216 if (is_used_partition(prev_p
) &&
2217 end
> free_start
&& end
<= curr_start
)
2221 last
= get_abs_partition_end(pe
);
2223 rc
= fdisk_ask_number(cxt
, free_start
, curr_start
, last
,
2224 _("New beginning of data"), &res
);
2228 new = res
- pe
->offset
;
2230 if (new != dos_partition_get_size(p
)) {
2231 unsigned int sects
= dos_partition_get_size(p
)
2232 + dos_partition_get_start(p
) - new;
2234 dos_partition_set_size(p
, sects
);
2235 dos_partition_set_start(p
, new);
2237 partition_set_changed(cxt
, i
, 1);
2243 static int dos_partition_is_used(
2244 struct fdisk_context
*cxt
,
2247 struct dos_partition
*p
;
2251 assert(fdisk_is_label(cxt
, DOS
));
2253 if (i
>= cxt
->label
->nparts_max
)
2256 p
= self_partition(cxt
, i
);
2258 return p
&& !is_cleared_partition(p
);
2261 static int dos_toggle_partition_flag(
2262 struct fdisk_context
*cxt
,
2266 struct dos_partition
*p
;
2270 assert(fdisk_is_label(cxt
, DOS
));
2272 if (i
>= cxt
->label
->nparts_max
)
2275 p
= self_partition(cxt
, i
);
2278 case DOS_FLAG_ACTIVE
:
2279 if (IS_EXTENDED(p
->sys_ind
) && !p
->boot_ind
)
2280 fdisk_warnx(cxt
, _("Partition %zu: is an extended "
2281 "partition."), i
+ 1);
2283 p
->boot_ind
= (p
->boot_ind
? 0 : ACTIVE_FLAG
);
2284 partition_set_changed(cxt
, i
, 1);
2285 fdisk_info(cxt
, p
->boot_ind
?
2286 _("The bootable flag on partition %zu is enabled now.") :
2287 _("The bootable flag on partition %zu is disabled now."),
2297 static const struct fdisk_field dos_fields
[] =
2300 { FDISK_FIELD_DEVICE
, N_("Device"), 10, 0 },
2301 { FDISK_FIELD_BOOT
, N_("Boot"), 1, 0 },
2302 { FDISK_FIELD_START
, N_("Start"), 5, FDISK_FIELDFL_NUMBER
},
2303 { FDISK_FIELD_END
, N_("End"), 5, FDISK_FIELDFL_NUMBER
},
2304 { FDISK_FIELD_SECTORS
, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER
},
2305 { FDISK_FIELD_CYLINDERS
,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER
},
2306 { FDISK_FIELD_SIZE
, N_("Size"), 5, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_EYECANDY
},
2307 { FDISK_FIELD_TYPEID
, N_("Id"), 2, FDISK_FIELDFL_NUMBER
},
2308 { FDISK_FIELD_TYPE
, N_("Type"), 0.1, 0 },
2311 { FDISK_FIELD_SADDR
, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2312 { FDISK_FIELD_EADDR
, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2313 { FDISK_FIELD_ATTR
, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
}
2317 static const struct fdisk_label_operations dos_operations
=
2319 .probe
= dos_probe_label
,
2320 .write
= dos_write_disklabel
,
2321 .verify
= dos_verify_disklabel
,
2322 .create
= dos_create_disklabel
,
2323 .locate
= dos_locate_disklabel
,
2324 .get_item
= dos_get_disklabel_item
,
2325 .set_id
= dos_set_disklabel_id
,
2327 .get_part
= dos_get_partition
,
2328 .set_part
= dos_set_partition
,
2329 .add_part
= dos_add_partition
,
2330 .del_part
= dos_delete_partition
,
2331 .reorder
= dos_reorder
,
2333 .part_toggle_flag
= dos_toggle_partition_flag
,
2334 .part_is_used
= dos_partition_is_used
,
2336 .reset_alignment
= dos_reset_alignment
,
2338 .deinit
= dos_deinit
,
2342 * allocates DOS in-memory stuff
2344 struct fdisk_label
*fdisk_new_dos_label(struct fdisk_context
*cxt
)
2346 struct fdisk_label
*lb
;
2347 struct fdisk_dos_label
*dos
;
2351 dos
= calloc(1, sizeof(*dos
));
2355 /* initialize generic part of the driver */
2356 lb
= (struct fdisk_label
*) dos
;
2358 lb
->id
= FDISK_DISKLABEL_DOS
;
2359 lb
->op
= &dos_operations
;
2360 lb
->parttypes
= dos_parttypes
;
2361 lb
->nparttypes
= ARRAY_SIZE(dos_parttypes
) - 1;
2362 lb
->fields
= dos_fields
;
2363 lb
->nfields
= ARRAY_SIZE(dos_fields
);
2369 * fdisk_dos_enable_compatible:
2370 * @lb: DOS label (see fdisk_get_label())
2373 * Enables deprecated DOS compatible mode, in this mode library checks for
2374 * cylinders boundary, cases about CHS addressing and another obscure things.
2376 * Returns: 0 on success, <0 on error.
2378 int fdisk_dos_enable_compatible(struct fdisk_label
*lb
, int enable
)
2380 struct fdisk_dos_label
*dos
= (struct fdisk_dos_label
*) lb
;
2385 dos
->compatible
= enable
;
2387 lb
->flags
|= FDISK_LABEL_FL_REQUIRE_GEOMETRY
;
2392 * fdisk_dos_is_compatible:
2395 * Returns: 0 if DOS compatibility disabled, 1 if enabled
2397 int fdisk_dos_is_compatible(struct fdisk_label
*lb
)
2399 return ((struct fdisk_dos_label
*) lb
)->compatible
;