3 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
4 * 2012 Davidlohr Bueso <dave@gnu.org>
5 * 2021 Pali Rohár <pali.rohar@gmail.com>
7 * This is re-written version for libfdisk, the original was fdiskdoslabel.c
8 * 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 const struct fdisk_parttype dos_parttypes
[] = {
71 #include "pt-mbr-partnames.h"
74 static const struct fdisk_shortcut dos_parttype_cuts
[] =
76 { .shortcut
= "L", .alias
= "linux", .data
= "83" },
77 { .shortcut
= "S", .alias
= "swap", .data
= "82" },
78 { .shortcut
= "E", .alias
= "extended", .data
= "05", .deprecated
= 1 }, /* collision with 0x0e type */
79 { .shortcut
= "Ex",.alias
= "extended", .data
= "05" }, /* MBR extended */
80 { .shortcut
= "U", .alias
= "uefi", .data
= "EF" }, /* UEFI system */
81 { .shortcut
= "R", .alias
= "raid", .data
= "FD" }, /* Linux RAID */
82 { .shortcut
= "V", .alias
= "lvm", .data
= "8E" }, /* LVM */
83 { .shortcut
= "X", .alias
= "linuxex", .data
= "85" } /* Linux extended */
87 #define sector(s) ((s) & 0x3f)
88 #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
90 #define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
92 #define is_dos_compatible(_x) \
93 (fdisk_is_label(_x, DOS) && \
94 fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
96 #define cround(c, n) fdisk_cround(c, n)
99 static inline struct fdisk_dos_label
*self_label(struct fdisk_context
*cxt
)
103 assert(fdisk_is_label(cxt
, DOS
));
105 return (struct fdisk_dos_label
*) cxt
->label
;
108 static inline struct pte
*self_pte(struct fdisk_context
*cxt
, size_t i
)
110 struct fdisk_dos_label
*l
= self_label(cxt
);
112 if (i
>= ARRAY_SIZE(l
->ptes
))
118 static inline struct dos_partition
*self_partition(
119 struct fdisk_context
*cxt
,
122 struct pte
*pe
= self_pte(cxt
, i
);
123 return pe
? pe
->pt_entry
: NULL
;
126 struct dos_partition
*fdisk_dos_get_partition(
127 struct fdisk_context
*cxt
,
132 assert(fdisk_is_label(cxt
, DOS
));
134 return self_partition(cxt
, i
);
137 static struct fdisk_parttype
*dos_partition_parttype(
138 struct fdisk_context
*cxt
,
139 struct dos_partition
*p
)
141 struct fdisk_parttype
*t
142 = fdisk_label_get_parttype_from_code(cxt
->label
, p
->sys_ind
);
143 return t
? : fdisk_new_unknown_parttype(p
->sys_ind
, NULL
);
147 * Linux kernel cares about partition size only. Things like
148 * partition type or so are completely irrelevant -- kzak Nov-2013
150 static int is_used_partition(struct dos_partition
*p
)
152 return p
&& dos_partition_get_size(p
) != 0;
155 static void partition_set_changed(
156 struct fdisk_context
*cxt
,
160 struct pte
*pe
= self_pte(cxt
, i
);
165 DBG(LABEL
, ul_debug("DOS: setting %zu partition to %s", i
,
166 changed
? "changed" : "unchanged"));
168 pe
->changed
= changed
? 1 : 0;
170 fdisk_label_set_changed(cxt
->label
, 1);
173 static fdisk_sector_t
get_abs_partition_start(struct pte
*pe
)
176 assert(pe
->pt_entry
);
178 return pe
->offset
+ dos_partition_get_start(pe
->pt_entry
);
181 static fdisk_sector_t
get_abs_partition_end(struct pte
*pe
)
186 assert(pe
->pt_entry
);
188 size
= dos_partition_get_size(pe
->pt_entry
);
189 return get_abs_partition_start(pe
) + size
- (size
? 1 : 0);
192 static int is_cleared_partition(struct dos_partition
*p
)
194 return !(!p
|| p
->boot_ind
|| p
->bh
|| p
->bs
|| p
->bc
||
195 p
->sys_ind
|| p
->eh
|| p
->es
|| p
->ec
||
196 dos_partition_get_start(p
) || dos_partition_get_size(p
));
199 static int get_partition_unused_primary(struct fdisk_context
*cxt
,
200 struct fdisk_partition
*pa
,
210 org
= cxt
->label
->nparts_max
;
212 cxt
->label
->nparts_max
= 4;
213 rc
= fdisk_partition_next_partno(pa
, cxt
, &n
);
214 cxt
->label
->nparts_max
= org
;
217 fdisk_info(cxt
, _("All primary partitions have been defined already."));
219 } else if (rc
== -ERANGE
) {
220 fdisk_warnx(cxt
, _("Primary partition not available."));
227 static int seek_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
)
229 off_t offset
= (off_t
) secno
* cxt
->sector_size
;
231 return lseek(cxt
->dev_fd
, offset
, SEEK_SET
) == (off_t
) -1 ? -errno
: 0;
234 static int read_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
,
237 int rc
= seek_sector(cxt
, secno
);
243 r
= read(cxt
->dev_fd
, buf
, cxt
->sector_size
);
244 if (r
== (ssize_t
) cxt
->sector_size
)
251 /* Allocate a buffer and read a partition table sector */
252 static int read_pte(struct fdisk_context
*cxt
, size_t pno
, fdisk_sector_t offset
)
256 struct pte
*pe
= self_pte(cxt
, pno
);
261 buf
= calloc(1, cxt
->sector_size
);
265 DBG(LABEL
, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
266 pno
, (uintmax_t) offset
, buf
));
269 pe
->sectorbuffer
= buf
;
270 pe
->private_sectorbuffer
= 1;
272 rc
= read_sector(cxt
, offset
, pe
->sectorbuffer
);
274 fdisk_warn(cxt
, _("Failed to read extended partition table "
275 "(offset=%ju)"), (uintmax_t) offset
);
280 pe
->pt_entry
= pe
->ex_entry
= NULL
;
285 static void clear_partition(struct dos_partition
*p
)
297 dos_partition_set_start(p
,0);
298 dos_partition_set_size(p
,0);
301 static void dos_init(struct fdisk_context
*cxt
)
303 struct fdisk_dos_label
*l
= self_label(cxt
);
308 assert(fdisk_is_label(cxt
, DOS
));
310 DBG(LABEL
, ul_debug("DOS: initialize, first sector buffer %p", cxt
->firstsector
));
312 cxt
->label
->nparts_max
= 4; /* default, unlimited number of logical */
316 l
->non_pt_changed
= 0;
318 memset(l
->ptes
, 0, sizeof(l
->ptes
));
320 for (i
= 0; i
< 4; i
++) {
321 struct pte
*pe
= self_pte(cxt
, i
);
324 pe
->pt_entry
= mbr_get_partition(cxt
->firstsector
, i
);
327 pe
->sectorbuffer
= cxt
->firstsector
;
328 pe
->private_sectorbuffer
= 0;
331 DBG(LABEL
, ul_debug("DOS: initialize: #%zu start=%u size=%u sysid=%02x",
333 dos_partition_get_start(pe
->pt_entry
),
334 dos_partition_get_size(pe
->pt_entry
),
335 pe
->pt_entry
->sys_ind
));
338 if (fdisk_is_listonly(cxt
))
341 * Various warnings...
343 if (fdisk_missing_geometry(cxt
))
344 fdisk_warnx(cxt
, _("You can set geometry from the extra functions menu."));
346 if (is_dos_compatible(cxt
)) {
347 fdisk_warnx(cxt
, _("DOS-compatible mode is deprecated."));
349 if (cxt
->sector_size
!= cxt
->phy_sector_size
)
351 "The device presents a logical sector size that is smaller than "
352 "the physical sector size. Aligning to a physical sector (or optimal "
353 "I/O) size boundary is recommended, or performance may be impacted."));
356 if (fdisk_use_cylinders(cxt
))
357 fdisk_warnx(cxt
, _("Cylinders as display units are deprecated."));
359 if (cxt
->total_sectors
> UINT_MAX
) {
360 uint64_t bytes
= cxt
->total_sectors
* cxt
->sector_size
;
361 char *szstr
= size_to_human_string(SIZE_SUFFIX_SPACE
362 | SIZE_SUFFIX_3LETTER
, bytes
);
364 _("The size of this disk is %s (%ju bytes). DOS "
365 "partition table format cannot be used on drives for "
366 "volumes larger than %lu bytes for %lu-byte "
367 "sectors. Use GUID partition table format (GPT)."),
369 UINT_MAX
* cxt
->sector_size
,
375 /* callback called by libfdisk */
376 static void dos_deinit(struct fdisk_label
*lb
)
379 struct fdisk_dos_label
*l
= (struct fdisk_dos_label
*) lb
;
381 for (i
= 0; i
< ARRAY_SIZE(l
->ptes
); i
++) {
382 struct pte
*pe
= &l
->ptes
[i
];
384 if (pe
->private_sectorbuffer
&& pe
->sectorbuffer
) {
385 DBG(LABEL
, ul_debug("DOS: freeing pte %zu sector buffer %p",
386 i
, pe
->sectorbuffer
));
387 free(pe
->sectorbuffer
);
389 pe
->sectorbuffer
= NULL
;
390 pe
->private_sectorbuffer
= 0;
393 memset(l
->ptes
, 0, sizeof(l
->ptes
));
396 static void reset_pte(struct pte
*pe
)
400 if (pe
->private_sectorbuffer
) {
401 DBG(LABEL
, ul_debug(" --> freeing pte sector buffer %p",
403 free(pe
->sectorbuffer
);
405 memset(pe
, 0, sizeof(struct pte
));
408 static int delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
410 struct fdisk_dos_label
*l
;
412 struct dos_partition
*p
;
413 struct dos_partition
*q
;
417 assert(fdisk_is_label(cxt
, DOS
));
419 pe
= self_pte(cxt
, partnum
);
423 DBG(LABEL
, ul_debug("DOS: delete partition %zu (max=%zu)", partnum
,
424 cxt
->label
->nparts_max
));
430 /* Note that for the fifth partition (partnum == 4) we don't actually
431 decrement partitions. */
433 DBG(LABEL
, ul_debug("--> delete primary"));
434 if (IS_EXTENDED(p
->sys_ind
) && partnum
== l
->ext_index
) {
436 DBG(LABEL
, ul_debug(" --> delete extended"));
437 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
438 DBG(LABEL
, ul_debug(" --> delete logical #%zu", i
));
439 reset_pte(&l
->ptes
[i
]);
442 cxt
->label
->nparts_max
= 4;
443 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
447 partition_set_changed(cxt
, partnum
, 1);
449 } else if (!q
->sys_ind
&& partnum
> 4) {
450 DBG(LABEL
, ul_debug("--> delete logical [last in the chain]"));
451 reset_pte(&l
->ptes
[partnum
]);
452 --cxt
->label
->nparts_max
;
454 /* clear link to deleted partition */
455 clear_partition(l
->ptes
[partnum
].ex_entry
);
456 partition_set_changed(cxt
, partnum
, 1);
458 DBG(LABEL
, ul_debug("--> delete logical [move down]"));
460 DBG(LABEL
, ul_debug(" --> delete %zu logical link", partnum
));
461 p
= l
->ptes
[partnum
- 1].ex_entry
;
463 dos_partition_set_start(p
, dos_partition_get_start(q
));
464 dos_partition_set_size(p
, dos_partition_get_size(q
));
465 dos_partition_sync_chs(p
, pe
->offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
466 partition_set_changed(cxt
, partnum
- 1, 1);
468 } else if (cxt
->label
->nparts_max
> 5) {
469 DBG(LABEL
, ul_debug(" --> delete first logical link"));
470 pe
= &l
->ptes
[5]; /* second logical */
472 if (pe
->pt_entry
) /* prevent SEGFAULT */
473 dos_partition_set_start(pe
->pt_entry
,
474 get_abs_partition_start(pe
) -
476 pe
->offset
= l
->ext_offset
;
477 dos_partition_sync_chs(p
, pe
->offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
478 partition_set_changed(cxt
, 5, 1);
481 if (cxt
->label
->nparts_max
> 5) {
482 DBG(LABEL
, ul_debug(" --> move ptes"));
483 cxt
->label
->nparts_max
--;
484 reset_pte(&l
->ptes
[partnum
]);
485 while (partnum
< cxt
->label
->nparts_max
) {
486 DBG(LABEL
, ul_debug(" --> moving pte %zu <-- %zu", partnum
, partnum
+ 1));
487 l
->ptes
[partnum
] = l
->ptes
[partnum
+ 1];
490 memset(&l
->ptes
[partnum
], 0, sizeof(struct pte
));
492 DBG(LABEL
, ul_debug(" --> the only logical: clear only"));
493 clear_partition(l
->ptes
[partnum
].pt_entry
);
494 cxt
->label
->nparts_max
--;
497 DBG(LABEL
, ul_debug(" --> clear last logical"));
498 reset_pte(&l
->ptes
[partnum
]);
499 partition_set_changed(cxt
, l
->ext_index
, 1);
504 fdisk_label_set_changed(cxt
->label
, 1);
508 static int dos_delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
514 assert(fdisk_is_label(cxt
, DOS
));
516 pe
= self_pte(cxt
, partnum
);
517 if (!pe
|| !is_used_partition(pe
->pt_entry
))
520 return delete_partition(cxt
, partnum
);
523 static void read_extended(struct fdisk_context
*cxt
, size_t ext
)
526 struct pte
*pex
, *pe
;
527 struct dos_partition
*p
, *q
;
528 struct fdisk_dos_label
*l
= self_label(cxt
);
531 pex
= self_pte(cxt
, ext
);
533 DBG(LABEL
, ul_debug("DOS: uninitialized pointer to %zu pex", ext
));
536 pex
->ex_entry
= pex
->pt_entry
;
539 if (!dos_partition_get_start(p
)) {
540 fdisk_warnx(cxt
, _("Bad offset in primary extended partition."));
544 DBG(LABEL
, ul_debug("DOS: Reading extended %zu", ext
));
546 while (IS_EXTENDED (p
->sys_ind
)) {
547 if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
548 /* This is not a Linux restriction, but
549 this program uses arrays of size MAXIMUM_PARTS.
550 Do not try to `improve' this test. */
551 struct pte
*pre
= self_pte(cxt
,
552 cxt
->label
->nparts_max
- 1);
554 _("Omitting partitions after #%zu. They will be deleted "
555 "if you save this partition table."),
556 cxt
->label
->nparts_max
);
559 clear_partition(pre
->ex_entry
);
560 partition_set_changed(cxt
,
561 cxt
->label
->nparts_max
- 1, 1);
566 pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
570 if (read_pte(cxt
, cxt
->label
->nparts_max
, l
->ext_offset
+
571 dos_partition_get_start(p
)))
575 l
->ext_offset
= dos_partition_get_start(p
);
577 assert(pe
->sectorbuffer
);
578 q
= p
= mbr_get_partition(pe
->sectorbuffer
, 0);
580 for (i
= 0; i
< 4; i
++, p
++) {
581 if (!dos_partition_get_size(p
))
584 if (IS_EXTENDED (p
->sys_ind
)) {
587 "Extra link pointer in partition "
589 cxt
->label
->nparts_max
+ 1);
592 } else if (p
->sys_ind
) {
595 "Ignoring extra data in partition "
597 cxt
->label
->nparts_max
+ 1);
603 /* very strange code here... */
605 if (q
!= pe
->ex_entry
)
608 pe
->pt_entry
= q
+ 1;
611 if (q
!= pe
->pt_entry
)
614 pe
->ex_entry
= q
+ 1;
618 cxt
->label
->nparts_cur
= ++cxt
->label
->nparts_max
;
620 DBG(LABEL
, ul_debug("DOS: EBR[offset=%ju]: link: type=%x, start=%u, size=%u; "
621 " data: type=%x, start=%u, size=%u",
622 (uintmax_t) pe
->offset
,
623 pe
->ex_entry
->sys_ind
,
624 dos_partition_get_start(pe
->ex_entry
),
625 dos_partition_get_size(pe
->ex_entry
),
626 pe
->pt_entry
->sys_ind
,
627 dos_partition_get_start(pe
->pt_entry
),
628 dos_partition_get_size(pe
->pt_entry
)));
632 /* remove last empty EBR */
633 pe
= self_pte(cxt
, cxt
->label
->nparts_max
- 1);
635 is_cleared_partition(pe
->ex_entry
) &&
636 is_cleared_partition(pe
->pt_entry
)) {
637 DBG(LABEL
, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe
->offset
));
639 cxt
->label
->nparts_max
--;
640 cxt
->label
->nparts_cur
--;
643 /* remove empty links */
645 q
= self_partition(cxt
, 4);
646 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
647 p
= self_partition(cxt
, i
);
649 if (p
&& !dos_partition_get_size(p
) &&
650 (cxt
->label
->nparts_max
> 5 || (q
&& q
->sys_ind
))) {
651 fdisk_info(cxt
, _("omitting empty partition (%zu)"), i
+1);
652 delete_partition(cxt
, i
);
653 goto remove
; /* numbering changed */
657 DBG(LABEL
, ul_debug("DOS: nparts_max: %zu", cxt
->label
->nparts_max
));
660 static int dos_create_disklabel(struct fdisk_context
*cxt
)
664 struct fdisk_dos_label
*l
;
668 assert(fdisk_is_label(cxt
, DOS
));
670 DBG(LABEL
, ul_debug("DOS: creating new disklabel"));
674 const char *s
= fdisk_script_get_header(cxt
->script
, "label-id");
678 id
= strtoul(s
, &end
, 16);
679 if (!errno
&& end
&& s
< end
) {
681 DBG(LABEL
, ul_debug("DOS: re-use ID from script (0x%08x)", id
));
683 DBG(LABEL
, ul_debug("DOS: failed to parse label=id '%s'", s
));
687 /* random disk signature */
689 DBG(LABEL
, ul_debug("DOS: generate new ID"));
690 ul_random_get_bytes(&id
, sizeof(id
));
693 if (fdisk_has_protected_bootbits(cxt
))
694 rc
= fdisk_init_firstsector_buffer(cxt
, 0, MBR_PT_BOOTBITS_SIZE
);
696 rc
= fdisk_init_firstsector_buffer(cxt
, 0, 0);
703 /* Generate an MBR ID for this disk */
704 mbr_set_id(cxt
->firstsector
, id
);
705 l
->non_pt_changed
= 1;
706 fdisk_label_set_changed(cxt
->label
, 1);
708 /* Put MBR signature */
709 mbr_set_magic(cxt
->firstsector
);
711 fdisk_info(cxt
, _("Created a new DOS (MBR) disklabel with disk "
712 "identifier 0x%08x."), id
);
716 static int dos_set_disklabel_id(struct fdisk_context
*cxt
, const char *str
)
719 unsigned int id
, old
;
720 struct fdisk_dos_label
*l
;
725 assert(fdisk_is_label(cxt
, DOS
));
727 DBG(LABEL
, ul_debug("DOS: setting Id"));
730 old
= mbr_get_id(cxt
->firstsector
);
733 rc
= fdisk_ask_string(cxt
,
734 _("Enter the new disk identifier"), &buf
);
741 id
= strtoul(str
, &end
, 0);
742 if (errno
|| str
== end
|| (end
&& *end
)) {
743 fdisk_warnx(cxt
, _("Incorrect value."));
752 mbr_set_id(cxt
->firstsector
, id
);
753 l
->non_pt_changed
= 1;
754 fdisk_label_set_changed(cxt
->label
, 1);
756 fdisk_info(cxt
, _("Disk identifier changed from 0x%08x to 0x%08x."),
761 static unsigned int chs_div_minus(unsigned int a1
, unsigned int a2
, unsigned int b1
, unsigned int b2
)
763 if (a1
> a2
&& b1
> b2
) {
766 } else if (a2
> a1
&& b2
> b1
) {
777 static inline int chs_overflowed(unsigned int c
, unsigned int h
, unsigned int s
)
779 /* 1023/254/63 or 1023/255/63 indicates overflowed/invalid C/H/S values */
780 return (c
== 1023 && (h
== 254 || h
== 255) && s
== 63);
783 static inline int lba_overflowed(fdisk_sector_t start
, fdisk_sector_t sects
)
785 /* Check if the last LBA sector can be represented by unsigned 32bit int */
786 return (start
+ (sects
-1) > UINT32_MAX
);
789 static void get_partition_table_geometry(struct fdisk_context
*cxt
,
790 unsigned int *ph
, unsigned int *ps
)
792 unsigned char *bufp
= cxt
->firstsector
;
793 struct { unsigned int c
, h
, o
, v
; } t
[8];
794 unsigned int n1
, n2
, n3
, n4
, n5
, n6
;
795 const struct dos_partition
*p
;
796 unsigned int c
, h
, s
, l
;
801 #define chs_set_t(c, h, s, l, t, i) do { \
804 t[i].o = l - (s-1); \
805 t[i].v = (!chs_overflowed(c, h, s) && s && s-1 <= l); \
809 * Conversion from C/H/S to LBA is defined by formula:
810 * LBA = (c * N_heads + h) * N_sectors + (s - 1)
813 * Then formula can be expressed as:
814 * o = (c * N_heads + h) * N_sectors
815 * In general from two tuples (LBA1, c1, h1, s1), (LBA2, c2, h2, s2)
816 * we can derive formulas for N_heads and N_sectors:
817 * N_heads = (o1 * h2 - o2 * h1) / (o2 * c1 - o1 * c2)
818 * N_sectors = (o2 * c1 - o1 * c2) / (c1 * h2 - c2 * h1)
819 * MBR table contains for very partition start and end tuple.
820 * So we have up to 8 tuples which leads to up to 28 equations
821 * for calculating N_heads and N_sectors. Try to calculate
822 * N_heads and N_sectors from the first possible partition and
823 * if it fails then try also mixed tuples (beginning from first
824 * partition and end from second). Calculation may fail if both
825 * first and last sectors are on cylinder or head boundary
826 * (dividend or divisor is zero). It is possible that different
827 * partitions would have different C/H/S geometry. In this case
828 * we want geometry from the first partition as in most cases
829 * this partition is or was used by BIOS for booting.
833 for (i
= 0; i
< 4; i
++) {
834 p
= mbr_get_partition(bufp
, i
);
838 c
= cylinder(p
->bs
, p
->bc
);
841 l
= dos_partition_get_start(p
);
842 chs_set_t(c
, h
, s
, l
, t
, 2*i
);
844 sects
= dos_partition_get_size(p
);
845 if (!sects
|| lba_overflowed(l
, sects
))
848 c
= cylinder(p
->es
, p
->ec
);
852 chs_set_t(c
, h
, s
, l
, t
, 2*i
+1);
855 for (dif
= 1; dif
< 8; dif
++) {
856 for (i
= 0; i
+ dif
< 8; i
++) {
858 if (!t
[i
].v
|| !t
[j
].v
)
860 n1
= t
[i
].o
* t
[j
].h
;
861 n2
= t
[j
].o
* t
[i
].h
;
862 n3
= t
[j
].o
* t
[i
].c
;
863 n4
= t
[i
].o
* t
[j
].c
;
864 n5
= t
[i
].c
* t
[j
].h
;
865 n6
= t
[j
].c
* t
[i
].h
;
866 if (!hh
&& n1
!= n2
&& n3
!= n4
) {
867 h
= chs_div_minus(n1
, n2
, n3
, n4
);
868 if (h
> 0 && h
<= 256)
871 if (!ss
&& n3
!= n4
&& n5
!= n6
) {
872 s
= chs_div_minus(n3
, n4
, n5
, n6
);
873 if (s
> 0 && s
<= 63)
888 DBG(LABEL
, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph
, *ps
));
891 static int dos_reset_alignment(struct fdisk_context
*cxt
)
895 assert(fdisk_is_label(cxt
, DOS
));
897 /* overwrite necessary stuff by DOS deprecated stuff */
898 if (is_dos_compatible(cxt
)) {
899 DBG(LABEL
, ul_debug("DOS: resetting alignment for DOS-compatible PT"));
900 if (cxt
->geom
.sectors
)
901 cxt
->first_lba
= cxt
->geom
.sectors
; /* usually 63 */
903 cxt
->grain
= cxt
->sector_size
; /* usually 512 */
909 /* TODO: move to include/pt-dos.h and share with libblkid */
910 #define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
911 #define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
913 static int dos_probe_label(struct fdisk_context
*cxt
)
916 unsigned int h
= 0, s
= 0;
920 assert(fdisk_is_label(cxt
, DOS
));
922 /* ignore disks with AIX magic number */
923 if (memcmp(cxt
->firstsector
, AIX_MAGIC_STRING
, AIX_MAGIC_STRLEN
) == 0)
926 if (!mbr_is_valid_magic(cxt
->firstsector
))
931 get_partition_table_geometry(cxt
, &h
, &s
);
934 cxt
->geom
.sectors
= s
;
935 cxt
->geom
.cylinders
= cxt
->total_sectors
/
936 (cxt
->geom
.heads
* cxt
->geom
.sectors
);
938 if (fdisk_has_user_device_geometry(cxt
))
939 fdisk_apply_user_device_properties(cxt
);
942 for (i
= 0; i
< 4; i
++) {
943 struct pte
*pe
= self_pte(cxt
, i
);
946 if (is_used_partition(pe
->pt_entry
))
947 cxt
->label
->nparts_cur
++;
949 if (IS_EXTENDED (pe
->pt_entry
->sys_ind
)) {
950 if (cxt
->label
->nparts_max
!= 4)
952 "Ignoring extra extended partition %zu"),
955 read_extended(cxt
, i
);
959 for (i
= 3; i
< cxt
->label
->nparts_max
; i
++) {
960 struct pte
*pe
= self_pte(cxt
, i
);
961 struct fdisk_dos_label
*l
= self_label(cxt
);
964 if (!mbr_is_valid_magic(pe
->sectorbuffer
)) {
966 "Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
967 "be corrected by w(rite)."),
968 pe
->sectorbuffer
[510],
969 pe
->sectorbuffer
[511],
971 partition_set_changed(cxt
, i
, 1);
973 /* mark also extended as changed to update the first EBR
974 * in situation that there is no logical partitions at all */
975 partition_set_changed(cxt
, l
->ext_index
, 1);
982 static void set_partition(struct fdisk_context
*cxt
,
983 int i
, int doext
, fdisk_sector_t start
,
984 fdisk_sector_t stop
, int sysid
, int boot
)
986 struct pte
*pe
= self_pte(cxt
, i
);
987 struct dos_partition
*p
;
988 fdisk_sector_t offset
;
990 assert(!FDISK_IS_UNDEF(start
));
991 assert(!FDISK_IS_UNDEF(stop
));
995 struct fdisk_dos_label
*l
= self_label(cxt
);
997 offset
= l
->ext_offset
;
1000 offset
= pe
->offset
;
1003 DBG(LABEL
, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, size=%zu, sysid=%02x",
1004 i
, doext
? " [extended]" : "",
1006 (size_t) (start
- offset
),
1007 (size_t) (stop
- start
+ 1),
1010 p
->boot_ind
= boot
? ACTIVE_FLAG
: 0;
1012 dos_partition_set_start(p
, start
- offset
);
1013 dos_partition_set_size(p
, stop
- start
+ 1);
1014 dos_partition_sync_chs(p
, offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
1015 partition_set_changed(cxt
, i
, 1);
1019 static int get_start_from_user( struct fdisk_context
*cxt
,
1020 fdisk_sector_t
*start
,
1022 fdisk_sector_t dflt
,
1023 fdisk_sector_t limit
,
1024 struct fdisk_partition
*pa
)
1028 /* try to use template from 'pa' */
1029 if (pa
&& pa
->start_follow_default
)
1032 else if (pa
&& fdisk_partition_has_start(pa
)) {
1033 DBG(LABEL
, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
1034 (uintmax_t) pa
->start
, (uintmax_t) low
, (uintmax_t) limit
));
1036 if (*start
< low
|| *start
> limit
) {
1037 fdisk_warnx(cxt
, _("Start sector %ju out of range."),
1038 (uintmax_t) *start
);
1042 /* ask user by dialog */
1043 struct fdisk_ask
*ask
= fdisk_new_ask();
1048 fdisk_ask_set_query(ask
,
1049 fdisk_use_cylinders(cxt
) ?
1050 _("First cylinder") : _("First sector"));
1051 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_NUMBER
);
1052 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, low
));
1053 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, dflt
));
1054 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
1056 rc
= fdisk_do_ask(cxt
, ask
);
1057 *start
= fdisk_ask_number_get_result(ask
);
1058 fdisk_unref_ask(ask
);
1061 if (fdisk_use_cylinders(cxt
)) {
1062 *start
= (*start
- 1)
1063 * fdisk_get_units_per_sector(cxt
);
1069 DBG(LABEL
, ul_debug("DOS: start is %ju", (uintmax_t) *start
));
1073 /* Returns last available sector in the free space pointed to by start. */
1074 static int find_last_free(
1075 struct fdisk_context
*cxt
,
1077 fdisk_sector_t begin
,
1078 fdisk_sector_t stop
,
1079 fdisk_sector_t
*result
)
1081 fdisk_sector_t last
= stop
;
1083 size_t i
= logical
? 4 : 0;
1085 for ( ; i
< cxt
->label
->nparts_max
; i
++) {
1086 struct pte
*pe
= self_pte(cxt
, i
);
1089 fdisk_sector_t p_start
= get_abs_partition_start(pe
);
1090 fdisk_sector_t p_end
= get_abs_partition_end(pe
);
1092 if (is_cleared_partition(pe
->pt_entry
))
1094 /* count EBR and begin of the logical partition as used area */
1096 p_start
-= cxt
->first_lba
;
1098 if ((p_start
>= begin
&& p_start
<= last
) ||
1099 (p_end
>= begin
&& p_end
<= last
)) {
1103 DBG(LABEL
, ul_debug("no free space <%ju,%ju>",
1104 (uintmax_t) begin
, (uintmax_t) stop
));
1112 DBG(LABEL
, ul_debug("DOS: last free sector <%ju,%ju>: %ju",
1113 (uintmax_t) begin
, (uintmax_t) stop
, (uintmax_t) last
));
1119 static int find_last_free_sector_in_range(
1120 struct fdisk_context
*cxt
,
1122 fdisk_sector_t begin
,
1124 fdisk_sector_t
*result
)
1127 fdisk_sector_t last
= end
;
1130 size_t i
= logical
? 4 : 0;
1133 for ( ; i
< cxt
->label
->nparts_max
; i
++) {
1134 struct pte
*pe
= self_pte(cxt
, i
);
1137 fdisk_sector_t p_start
= get_abs_partition_start(pe
);
1138 fdisk_sector_t p_end
= get_abs_partition_end(pe
);
1140 if (is_cleared_partition(pe
->pt_entry
))
1143 /* count EBR and begin of the logical partition as used area */
1145 p_start
-= cxt
->first_lba
;
1147 if (last
>= p_start
&& last
<= p_end
) {
1152 DBG(LABEL
, ul_debug("DOS: last free out of range <%ju,%ju>: %ju",
1153 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) last
));
1159 } while (last_moved
== 1);
1161 DBG(LABEL
, ul_debug("DOS: last unused sector in range <%ju,%ju>: %ju",
1162 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) last
));
1168 static int find_first_free_sector_in_range(
1169 struct fdisk_context
*cxt
,
1171 fdisk_sector_t begin
,
1173 fdisk_sector_t
*result
)
1175 int first_moved
= 0;
1176 fdisk_sector_t first
= begin
;
1179 size_t i
= logical
? 4 : 0;
1182 for (; i
< cxt
->label
->nparts_max
; i
++) {
1183 struct pte
*pe
= self_pte(cxt
, i
);
1186 fdisk_sector_t p_start
= get_abs_partition_start(pe
);
1187 fdisk_sector_t p_end
= get_abs_partition_end(pe
);
1189 if (is_cleared_partition(pe
->pt_entry
))
1191 /* count EBR and begin of the logical partition as used area */
1193 p_start
-= cxt
->first_lba
;
1194 if (first
< p_start
)
1196 if (first
<= p_end
) {
1197 first
= p_end
+ 1 + (logical
? cxt
->first_lba
: 0);
1201 DBG(LABEL
, ul_debug("DOS: first free out of range <%ju,%ju>: %ju",
1202 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) first
));
1207 } while (first_moved
== 1);
1209 DBG(LABEL
, ul_debug("DOS: first unused sector in range <%ju,%ju>: %ju",
1210 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) first
));
1215 static int get_disk_ranges(struct fdisk_context
*cxt
, int logical
,
1216 fdisk_sector_t
*first
, fdisk_sector_t
*last
)
1219 /* logical partitions */
1220 struct fdisk_dos_label
*l
= self_label(cxt
);
1221 struct pte
*ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1226 *first
= l
->ext_offset
+ cxt
->first_lba
;
1227 *last
= get_abs_partition_end(ext_pe
);
1230 /* primary partitions */
1231 if (fdisk_use_cylinders(cxt
) || !cxt
->total_sectors
)
1232 *last
= cxt
->geom
.heads
* cxt
->geom
.sectors
* cxt
->geom
.cylinders
- 1;
1234 *last
= cxt
->total_sectors
- 1;
1236 if (*last
> UINT_MAX
)
1238 *first
= cxt
->first_lba
;
1244 /* first free sector on disk */
1245 static int find_first_free_sector(struct fdisk_context
*cxt
,
1247 fdisk_sector_t start
,
1248 fdisk_sector_t
*result
)
1250 fdisk_sector_t first
, last
;
1253 rc
= get_disk_ranges(cxt
, logical
, &first
, &last
);
1257 return find_first_free_sector_in_range(cxt
, logical
, start
, last
, result
);
1260 static int add_partition(struct fdisk_context
*cxt
, size_t n
,
1261 struct fdisk_partition
*pa
)
1263 int sys
, read
= 0, rc
, isrel
= 0, is_logical
;
1264 struct fdisk_dos_label
*l
= self_label(cxt
);
1265 struct dos_partition
*p
= self_partition(cxt
, n
);
1266 struct fdisk_ask
*ask
= NULL
;
1268 fdisk_sector_t start
, stop
, limit
, temp
;
1270 DBG(LABEL
, ul_debug("DOS: adding partition %zu", n
));
1272 sys
= pa
&& pa
->type
? pa
->type
->code
: MBR_LINUX_DATA_PARTITION
;
1273 is_logical
= n
>= 4;
1275 if (p
&& is_used_partition(p
)) {
1276 fdisk_warnx(cxt
, _("Partition %zu is already defined. "
1277 "Delete it before re-adding it."),
1282 rc
= get_disk_ranges(cxt
, is_logical
, &start
, &stop
);
1286 if (!is_logical
&& cxt
->parent
&& fdisk_is_label(cxt
->parent
, GPT
))
1287 start
= 1; /* Bad boy modifies hybrid MBR */
1289 rc
= find_last_free_sector_in_range(cxt
, is_logical
, start
, stop
, &limit
);
1291 fdisk_warnx(cxt
, _("No free sectors available."));
1295 if ((is_logical
|| !cxt
->parent
|| !fdisk_is_label(cxt
->parent
, GPT
))
1296 && cxt
->script
&& pa
&& fdisk_partition_has_start(pa
)
1297 && pa
->start
>= (is_logical
? l
->ext_offset
: 1)
1298 && pa
->start
< start
) {
1299 fdisk_set_first_lba(cxt
, 1);
1301 rc
= get_disk_ranges(cxt
, is_logical
, &start
, &stop
);
1302 if (rc
) /* won't happen, but checking to be proper */
1307 * Ask for first sector
1310 fdisk_sector_t dflt
, aligned
;
1314 DBG(LABEL
, ul_debug("DOS: >>> search for first free from %ju", start
));
1315 rc
= find_first_free_sector(cxt
, is_logical
, start
, &dflt
);
1317 fdisk_warnx(cxt
, _("No free sectors available."));
1322 if (n
>= 4 && pa
&& fdisk_partition_has_start(pa
) && cxt
->script
1323 && cxt
->first_lba
> 1
1324 && temp
== start
- cxt
->first_lba
) {
1325 fdisk_set_first_lba(cxt
, 1);
1329 /* the default sector should be aligned and unused */
1331 aligned
= fdisk_align_lba_in_range(cxt
, dflt
, dflt
, limit
);
1332 find_first_free_sector(cxt
, is_logical
, aligned
, &dflt
);
1333 } while (dflt
!= aligned
&& dflt
> aligned
&& dflt
< limit
);
1339 if (start
>= temp
+ fdisk_get_units_per_sector(cxt
)
1341 if (!pa
|| !pa
->start_follow_default
)
1342 fdisk_info(cxt
, _("Sector %ju is already allocated."),
1346 if (pa
&& fdisk_partition_has_start(pa
))
1350 if (!read
&& start
== temp
) {
1351 rc
= get_start_from_user(cxt
, &start
, temp
, dflt
, limit
, pa
);
1356 if (pa
&& fdisk_partition_has_size(pa
)) {
1357 fdisk_sector_t last
;
1359 rc
= find_last_free(cxt
, is_logical
, start
, limit
, &last
);
1360 if (rc
== 0 && last
- start
+ 1 < fdisk_partition_get_size(pa
)) {
1361 DBG(LABEL
, ul_debug("DOS: area <%ju,%ju> too small [wanted=%ju aval=%ju]",
1362 (uintmax_t) start
, (uintmax_t) last
,
1363 fdisk_partition_get_size(pa
),
1366 if (fdisk_partition_has_start(pa
)
1367 && fdisk_partition_get_start(pa
) <= last
)
1374 if (rc
== -ENOSPC
) {
1375 fdisk_warnx(cxt
, _("No free sectors available."));
1380 } while (start
!= temp
|| !read
);
1383 /* The first EBR is stored at begin of the extended partition */
1384 struct pte
*pe
= self_pte(cxt
, n
);
1387 pe
->offset
= l
->ext_offset
;
1389 /* The second (and another) EBR */
1390 struct pte
*pe
= self_pte(cxt
, n
);
1393 assert(start
>= cxt
->first_lba
);
1395 pe
->offset
= start
- cxt
->first_lba
;
1396 DBG(LABEL
, ul_debug("DOS: setting EBR offset to %ju [start=%ju]", pe
->offset
, start
));
1398 if (pe
->offset
== l
->ext_offset
) { /* must be corrected */
1400 if (cxt
->first_lba
== 1)
1405 rc
= find_last_free(cxt
, is_logical
, start
, limit
, &stop
);
1407 fdisk_warnx(cxt
, _("No free sectors available."));
1413 * Ask for last sector
1415 if (fdisk_cround(cxt
, start
) == fdisk_cround(cxt
, limit
))
1417 else if (pa
&& pa
->end_follow_default
)
1419 else if (pa
&& fdisk_partition_has_size(pa
)) {
1420 stop
= start
+ pa
->size
;
1421 isrel
= pa
->size_explicit
? 0 : 1;
1422 if ((!isrel
|| !alignment_required(cxt
)) && stop
> start
)
1425 /* ask user by dialog */
1428 ask
= fdisk_new_ask();
1430 fdisk_reset_ask(ask
);
1433 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_OFFSET
);
1435 if (fdisk_use_cylinders(cxt
)) {
1436 fdisk_ask_set_query(ask
, _("Last cylinder, +/-cylinders or +/-size{K,M,G,T,P}"));
1437 fdisk_ask_number_set_unit(ask
,
1439 fdisk_get_units_per_sector(cxt
));
1441 fdisk_ask_set_query(ask
, _("Last sector, +/-sectors or +/-size{K,M,G,T,P}"));
1442 fdisk_ask_number_set_unit(ask
,cxt
->sector_size
);
1445 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, start
));
1446 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, limit
));
1447 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
1448 fdisk_ask_number_set_base(ask
, fdisk_cround(cxt
, start
)); /* base for relative input */
1449 fdisk_ask_number_set_wrap_negative(ask
, 1); /* wrap negative around high */
1451 rc
= fdisk_do_ask(cxt
, ask
);
1455 stop
= fdisk_ask_number_get_result(ask
);
1456 isrel
= fdisk_ask_number_is_relative(ask
);
1457 if (fdisk_use_cylinders(cxt
)) {
1458 stop
= stop
* fdisk_get_units_per_sector(cxt
) - 1;
1463 if (stop
>= start
&& stop
<= limit
)
1465 fdisk_warnx(cxt
, _("Value out of range."));
1469 DBG(LABEL
, ul_debug("DOS: raw stop: %ju [limit %ju]", (uintmax_t) stop
, (uintmax_t) limit
));
1474 if (isrel
&& stop
- start
< (cxt
->grain
/ fdisk_get_sector_size(cxt
))) {
1475 /* Don't try to be smart on very small partitions and don't align so small sizes */
1477 DBG(LABEL
, ul_debug("DOS: don't align end of tiny partition [start=%ju, stop=%ju, grain=%lu]",
1478 (uintmax_t)start
, (uintmax_t)stop
, cxt
->grain
));
1481 if (stop
< limit
&& isrel
&& alignment_required(cxt
)) {
1482 /* the last sector has not been exactly requested (but
1483 * defined by +size{K,M,G} convention), so be smart and
1484 * align the end of the partition. The next partition
1485 * will start at phy.block boundary.
1487 stop
= fdisk_align_lba_in_range(cxt
, stop
, start
, limit
);
1489 stop
-= 1; /* end one sector before aligned offset */
1492 DBG(LABEL
, ul_debug("DOS: aligned stop: %ju", (uintmax_t) stop
));
1495 set_partition(cxt
, n
, 0, start
, stop
, sys
, fdisk_partition_is_bootable(pa
));
1497 struct pte
*pe
= self_pte(cxt
, n
);
1499 set_partition(cxt
, n
- 1, 1, pe
->offset
, stop
,
1500 MBR_DOS_EXTENDED_PARTITION
, 0);
1505 struct fdisk_parttype
*t
=
1506 fdisk_label_get_parttype_from_code(cxt
->label
, sys
);
1507 fdisk_info_new_partition(cxt
, n
+ 1, start
, stop
, t
);
1508 fdisk_unref_parttype(t
);
1512 if (IS_EXTENDED(sys
)) {
1513 struct pte
*pen
= self_pte(cxt
, n
);
1517 l
->ext_offset
= start
;
1521 fdisk_label_set_changed(cxt
->label
, 1);
1524 fdisk_unref_ask(ask
);
1528 static int add_logical(struct fdisk_context
*cxt
,
1529 struct fdisk_partition
*pa
,
1538 assert(self_label(cxt
)->ext_offset
);
1540 DBG(LABEL
, ul_debug("DOS: nparts max: %zu", cxt
->label
->nparts_max
));
1541 pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
1544 if (!pe
->sectorbuffer
) {
1545 pe
->sectorbuffer
= calloc(1, cxt
->sector_size
);
1546 if (!pe
->sectorbuffer
)
1548 DBG(LABEL
, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1549 cxt
->label
->nparts_max
, pe
->sectorbuffer
));
1550 pe
->private_sectorbuffer
= 1;
1552 pe
->pt_entry
= mbr_get_partition(pe
->sectorbuffer
, 0);
1553 pe
->ex_entry
= pe
->pt_entry
+ 1;
1555 partition_set_changed(cxt
, cxt
->label
->nparts_max
, 1);
1557 cxt
->label
->nparts_max
++;
1559 /* this message makes sense only when we use extended/primary/logical
1560 * dialog. The dialog is disable for scripts, see dos_add_partition() */
1562 fdisk_info(cxt
, _("Adding logical partition %zu"),
1563 cxt
->label
->nparts_max
);
1564 *partno
= cxt
->label
->nparts_max
- 1;
1565 rc
= add_partition(cxt
, *partno
, pa
);
1568 /* reset on error */
1569 cxt
->label
->nparts_max
--;
1570 pe
->pt_entry
= NULL
;
1571 pe
->ex_entry
= NULL
;
1579 static int check(struct fdisk_context
*cxt
, size_t n
,
1580 unsigned int h
, unsigned int s
, unsigned int c
,
1581 unsigned int lba_sector
)
1583 unsigned int chs_sector
, real_s
, real_c
;
1586 if (!is_dos_compatible(cxt
))
1589 real_s
= sector(s
) - 1;
1590 real_c
= cylinder(s
, c
);
1591 chs_sector
= (real_c
* cxt
->geom
.heads
+ h
) * cxt
->geom
.sectors
+ real_s
;
1594 fdisk_warnx(cxt
, _("Partition %zu: contains sector 0"), n
);
1597 if (h
>= cxt
->geom
.heads
) {
1598 fdisk_warnx(cxt
, _("Partition %zu: head %d greater than "
1599 "maximum %d"), n
, h
+ 1, cxt
->geom
.heads
);
1602 if (real_s
>= cxt
->geom
.sectors
) {
1603 fdisk_warnx(cxt
, _("Partition %zu: sector %d greater than "
1604 "maximum %ju"), n
, real_s
+ 1,
1605 (uintmax_t) cxt
->geom
.sectors
);
1608 if (real_c
>= cxt
->geom
.cylinders
) {
1609 fdisk_warnx(cxt
, _("Partition %zu: cylinder %d greater than "
1612 (uintmax_t) cxt
->geom
.cylinders
);
1615 if (lba_sector
/ (cxt
->geom
.heads
* cxt
->geom
.sectors
) < 1024 && lba_sector
!= chs_sector
) {
1616 fdisk_warnx(cxt
, _("Partition %zu: LBA sector %u "
1617 "disagrees with C/H/S calculated sector %u"),
1618 n
, lba_sector
, chs_sector
);
1625 /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1626 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1627 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1628 * Lubkin Oct. 1991). */
1631 long2chs(struct fdisk_context
*cxt
, unsigned long ls
,
1632 unsigned int *c
, unsigned int *h
, unsigned int *s
) {
1633 int spc
= cxt
->geom
.heads
* cxt
->geom
.sectors
;
1637 *h
= ls
/ cxt
->geom
.sectors
;
1638 *s
= ls
% cxt
->geom
.sectors
+ 1; /* sectors count from 1 */
1641 static int check_consistency(struct fdisk_context
*cxt
, struct dos_partition
*p
,
1644 unsigned int pbc
, pbh
, pbs
; /* physical beginning c, h, s */
1645 unsigned int pec
, peh
, pes
; /* physical ending c, h, s */
1646 unsigned int lbc
, lbh
, lbs
; /* logical beginning c, h, s */
1647 unsigned int lec
, leh
, les
; /* logical ending c, h, s */
1650 if (!is_dos_compatible(cxt
))
1653 if (!cxt
->geom
.heads
|| !cxt
->geom
.sectors
|| (partition
>= 4))
1654 return 0; /* do not check extended partitions */
1656 /* physical beginning c, h, s */
1657 pbc
= cylinder(p
->bs
, p
->bc
);
1659 pbs
= sector(p
->bs
);
1661 /* physical ending c, h, s */
1662 pec
= cylinder(p
->es
, p
->ec
);
1664 pes
= sector(p
->es
);
1666 /* compute logical beginning (c, h, s) */
1667 long2chs(cxt
, dos_partition_get_start(p
), &lbc
, &lbh
, &lbs
);
1669 /* compute logical ending (c, h, s) */
1670 long2chs(cxt
, dos_partition_get_start(p
) + dos_partition_get_size(p
) - 1, &lec
, &leh
, &les
);
1672 /* Same physical / logical beginning? */
1674 && (pbc
!= lbc
|| pbh
!= lbh
|| pbs
!= lbs
)) {
1675 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1676 "beginnings (non-Linux?): "
1677 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1684 /* Same physical / logical ending? */
1686 && (pec
!= lec
|| peh
!= leh
|| pes
!= les
)) {
1687 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1688 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1695 /* Ending on cylinder boundary? */
1696 if (peh
!= (cxt
->geom
.heads
- 1) || pes
!= cxt
->geom
.sectors
) {
1697 fdisk_warnx(cxt
, _("Partition %zu: does not end on "
1698 "cylinder boundary."),
1706 static void fill_bounds(struct fdisk_context
*cxt
,
1707 fdisk_sector_t
*first
, fdisk_sector_t
*last
)
1710 struct pte
*pe
= self_pte(cxt
, 0);
1711 struct dos_partition
*p
;
1714 for (i
= 0; i
< cxt
->label
->nparts_max
; pe
++,i
++) {
1716 if (is_cleared_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
1717 first
[i
] = SIZE_MAX
;
1720 first
[i
] = get_abs_partition_start(pe
);
1721 last
[i
] = get_abs_partition_end(pe
);
1726 static int dos_verify_disklabel(struct fdisk_context
*cxt
)
1729 fdisk_sector_t total
= 1, n_sectors
= cxt
->total_sectors
;
1730 fdisk_sector_t
*first
, *last
;
1731 struct dos_partition
*p
;
1732 struct fdisk_dos_label
*l
= self_label(cxt
);
1735 assert(fdisk_is_label(cxt
, DOS
));
1737 first
= calloc(cxt
->label
->nparts_max
, sizeof(*first
));
1738 last
= calloc(cxt
->label
->nparts_max
, sizeof(*first
));
1740 if (!first
|| !last
) {
1746 fill_bounds(cxt
, first
, last
);
1747 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
1748 struct pte
*pe
= self_pte(cxt
, i
);
1750 p
= self_partition(cxt
, i
);
1751 if (p
&& is_used_partition(p
) && !IS_EXTENDED(p
->sys_ind
)) {
1752 nerrors
+= check_consistency(cxt
, p
, i
);
1754 if (get_abs_partition_start(pe
) < first
[i
]) {
1756 "Partition %zu: bad start-of-data."),
1761 nerrors
+= check(cxt
, i
+ 1, p
->bh
, p
->bs
, p
->bc
, first
[i
]);
1762 nerrors
+= check(cxt
, i
+ 1, p
->eh
, p
->es
, p
->ec
, last
[i
]);
1763 total
+= last
[i
] + 1 - first
[i
];
1766 total
+= get_abs_partition_start(pe
) - 1;
1768 for (j
= 0; j
< i
; j
++) {
1769 if ((first
[i
] >= first
[j
] && first
[i
] <= last
[j
])
1770 || ((last
[i
] <= last
[j
] && last
[i
] >= first
[j
]))) {
1772 fdisk_warnx(cxt
, _("Partition %zu: "
1773 "overlaps partition %zu."),
1777 total
+= first
[i
] >= first
[j
] ?
1778 first
[i
] : first
[j
];
1779 total
-= last
[i
] <= last
[j
] ?
1786 if (l
->ext_offset
) {
1787 fdisk_sector_t e_last
;
1788 struct pte
*ext_pe
= self_pte(cxt
, l
->ext_index
);
1791 e_last
= get_abs_partition_end(ext_pe
);
1793 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1795 p
= self_partition(cxt
, i
);
1799 if (i
!= 4 || i
+ 1 < cxt
->label
->nparts_max
) {
1801 _("Partition %zu: empty."),
1805 } else if (first
[i
] < l
->ext_offset
1806 || last
[i
] > e_last
) {
1808 fdisk_warnx(cxt
, _("Logical partition %zu: "
1809 "not entirely in partition %zu."),
1810 i
+ 1, l
->ext_index
+ 1);
1817 fdisk_info(cxt
, _("No errors detected."));
1818 if (total
> n_sectors
)
1819 fdisk_info(cxt
, _("Total allocated sectors %ju greater "
1820 "than the maximum %ju."), (uintmax_t) total
, (uintmax_t) n_sectors
);
1821 else if (total
< n_sectors
)
1822 fdisk_info(cxt
, _("Remaining %ju unallocated %ld-byte "
1823 "sectors."), (uintmax_t) n_sectors
- total
, cxt
->sector_size
);
1826 P_("%d error detected.", "%d errors detected.", nerrors
),
1835 * Ask the user for new partition type information (logical, extended).
1836 * This function calls the actual partition adding logic - add_partition.
1840 static int dos_add_partition(struct fdisk_context
*cxt
,
1841 struct fdisk_partition
*pa
,
1845 uint8_t free_primary
= 0, free_sectors
= 0;
1846 fdisk_sector_t first
= 0, grain
;
1848 struct fdisk_dos_label
*l
;
1850 size_t res
= 0; /* partno */
1854 assert(fdisk_is_label(cxt
, DOS
));
1856 DBG(LABEL
, ul_debug("DOS: new partition wanted"));
1858 l
= self_label(cxt
);
1860 if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1861 fdisk_warnx(cxt
, _("The maximum number of partitions has "
1866 ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1869 * partition template (@pa) based partitioning
1872 /* A) template specifies start within extended partition; add logical */
1873 if (pa
&& fdisk_partition_has_start(pa
) && ext_pe
1874 && pa
->start
>= l
->ext_offset
1875 && pa
->start
<= get_abs_partition_end(ext_pe
)) {
1876 DBG(LABEL
, ul_debug("DOS: pa template %p: add logical (by offset)", pa
));
1878 if (fdisk_partition_has_partno(pa
) && fdisk_partition_get_partno(pa
) < 4) {
1879 DBG(LABEL
, ul_debug("DOS: pa template specifies partno<4 for logical partition"));
1882 rc
= add_logical(cxt
, pa
, &res
);
1885 /* B) template specifies start out of extended partition; add primary */
1886 } else if (pa
&& fdisk_partition_has_start(pa
) && ext_pe
) {
1887 DBG(LABEL
, ul_debug("DOS: pa template %p: add primary (by offset)", pa
));
1889 if (fdisk_partition_has_partno(pa
) && fdisk_partition_get_partno(pa
) >= 4) {
1890 DBG(LABEL
, ul_debug("DOS: pa template specifies partno>=4 for primary partition"));
1893 if (ext_pe
&& pa
->type
&& IS_EXTENDED(pa
->type
->code
)) {
1894 fdisk_warnx(cxt
, _("Extended partition already exists."));
1897 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1899 rc
= add_partition(cxt
, res
, pa
);
1902 /* C) template specifies start (or default), partno < 4; add primary */
1903 } else if (pa
&& (fdisk_partition_start_is_default(pa
) || fdisk_partition_has_start(pa
))
1904 && fdisk_partition_has_partno(pa
)
1905 && pa
->partno
< 4) {
1906 DBG(LABEL
, ul_debug("DOS: pa template %p: add primary (by partno)", pa
));
1908 if (ext_pe
&& pa
->type
&& IS_EXTENDED(pa
->type
->code
)) {
1909 fdisk_warnx(cxt
, _("Extended partition already exists."));
1912 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1914 rc
= add_partition(cxt
, res
, pa
);
1917 /* D) template specifies start (or default), partno >= 4; add logical */
1918 } else if (pa
&& (fdisk_partition_start_is_default(pa
) || fdisk_partition_has_start(pa
))
1919 && fdisk_partition_has_partno(pa
)
1920 && pa
->partno
>= 4) {
1921 DBG(LABEL
, ul_debug("DOS: pa template %p: add logical (by partno)", pa
));
1924 fdisk_warnx(cxt
, _("Extended partition does not exists. Failed to add logical partition."));
1928 if (fdisk_partition_has_start(pa
)
1929 && pa
->start
< l
->ext_offset
1930 && pa
->start
> get_abs_partition_end(ext_pe
)) {
1931 DBG(LABEL
, ul_debug("DOS: pa template specifies partno>=4, but start out of extended"));
1935 rc
= add_logical(cxt
, pa
, &res
);
1939 DBG(LABEL
, ul_debug("DOS: dialog driven partitioning"));
1940 /* Note @pa may be still used for things like partition type, etc */
1942 /* check if there is space for primary partition */
1943 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
1944 first
= cxt
->first_lba
;
1946 if (cxt
->parent
&& fdisk_is_label(cxt
->parent
, GPT
)) {
1947 /* modifying a hybrid MBR, which throws out the rules */
1952 /* set @first after the last used partition, set @free_sectors if there
1953 * is gap in front if the first partition or between used parrtitions. */
1954 for (i
= 0; i
< 4; i
++) {
1955 struct dos_partition
*p
= self_partition(cxt
, i
);
1957 if (p
&& is_used_partition(p
)) {
1958 fdisk_sector_t start
= dos_partition_get_start(p
);
1959 if (first
+ grain
<= start
)
1961 first
= start
+ dos_partition_get_size(p
);
1966 /* set @free_sectors if there is a space after the first usable sector */
1967 if (first
+ grain
- 1 <= cxt
->total_sectors
- 1)
1970 DBG(LABEL
, ul_debug("DOS: primary: first free: %ju, last on disk: %ju, "
1971 "free_sectors=%d, free_primary=%d",
1973 (uintmax_t) cxt
->total_sectors
- 1,
1974 free_sectors
, free_primary
));
1976 if (!free_primary
|| !free_sectors
) {
1977 DBG(LABEL
, ul_debug("DOS: primary impossible"));
1978 if (l
->ext_offset
) {
1979 if (!pa
|| fdisk_partition_has_start(pa
)) {
1980 /* See above case A); here we have start, but
1981 * out of extended partition */
1984 msg
= _("All primary partitions are in use.");
1986 msg
= _("All space for primary partitions is in use.");
1988 if (pa
&& fdisk_partition_has_start(pa
)) {
1989 fdisk_warnx(cxt
, "%s", msg
);
1992 fdisk_info(cxt
, "%s", msg
);
1994 DBG(LABEL
, ul_debug("DOS: trying logical"));
1995 rc
= add_logical(cxt
, pa
, &res
);
1998 fdisk_info(cxt
, _("All space for primary partitions is in use."));
2000 /* TRANSLATORS: Try to keep this within 80 characters. */
2001 fdisk_info(cxt
, _("To create more partitions, first replace "
2002 "a primary with an extended partition."));
2007 struct fdisk_ask
*ask
;
2010 /* the default layout for scripts is to create primary partitions */
2011 if (cxt
->script
|| !fdisk_has_dialogs(cxt
)) {
2012 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
2014 rc
= add_partition(cxt
, res
, pa
);
2018 ask
= fdisk_new_ask();
2021 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_MENU
);
2022 fdisk_ask_set_query(ask
, _("Partition type"));
2023 fdisk_ask_menu_set_default(ask
, free_primary
== 1
2024 && !l
->ext_offset
? 'e' : 'p');
2025 snprintf(hint
, sizeof(hint
),
2026 _("%u primary, %d extended, %u free"),
2027 4 - (l
->ext_offset
? 1 : 0) - free_primary
,
2028 l
->ext_offset
? 1 : 0,
2031 fdisk_ask_menu_add_item(ask
, 'p', _("primary"), hint
);
2033 fdisk_ask_menu_add_item(ask
, 'e', _("extended"), _("container for logical partitions"));
2035 fdisk_ask_menu_add_item(ask
, 'l', _("logical"), _("numbered from 5"));
2037 rc
= fdisk_do_ask(cxt
, ask
);
2039 fdisk_ask_menu_get_result(ask
, &c
);
2040 fdisk_unref_ask(ask
);
2045 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
2047 rc
= add_partition(cxt
, res
, pa
);
2049 } else if (c
== 'l' && l
->ext_offset
) {
2050 rc
= add_logical(cxt
, pa
, &res
);
2052 } else if (c
== 'e' && !l
->ext_offset
) {
2053 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
2055 struct fdisk_partition
*xpa
= NULL
;
2056 struct fdisk_parttype
*t
;
2058 t
= fdisk_label_get_parttype_from_code(cxt
->label
,
2059 MBR_DOS_EXTENDED_PARTITION
);
2061 pa
= xpa
= fdisk_new_partition();
2065 fdisk_partition_set_type(pa
, t
);
2066 rc
= add_partition(cxt
, res
, pa
);
2068 fdisk_unref_partition(xpa
);
2074 fdisk_warnx(cxt
, _("Invalid partition type `%c'."), c
);
2078 cxt
->label
->nparts_cur
++;
2085 static int write_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
,
2090 rc
= seek_sector(cxt
, secno
);
2092 fdisk_warn(cxt
, _("Cannot write sector %jd: seek failed"),
2097 DBG(LABEL
, ul_debug("DOS: writing to sector %ju", (uintmax_t) secno
));
2099 if (write(cxt
->dev_fd
, buf
, cxt
->sector_size
) != (ssize_t
) cxt
->sector_size
)
2104 static int dos_write_disklabel(struct fdisk_context
*cxt
)
2106 struct fdisk_dos_label
*l
= self_label(cxt
);
2108 int rc
= 0, mbr_changed
= 0;
2112 assert(fdisk_is_label(cxt
, DOS
));
2114 DBG(LABEL
, ul_debug("DOS: write PT requested [label-changed: %d, non-pt-changed: %d]",
2115 cxt
->label
->changed
, l
->non_pt_changed
));
2117 mbr_changed
= l
->non_pt_changed
;
2119 /* MBR (primary partitions) */
2121 for (i
= 0; i
< 4; i
++) {
2122 struct pte
*pe
= self_pte(cxt
, i
);
2130 DBG(LABEL
, ul_debug("DOS: MBR changed, writing"));
2131 mbr_set_magic(cxt
->firstsector
);
2132 rc
= write_sector(cxt
, 0, cxt
->firstsector
);
2137 if (cxt
->label
->nparts_max
<= 4 && l
->ext_offset
) {
2138 /* we have empty extended partition, check if the partition has
2139 * been modified and then cleanup possible remaining EBR */
2140 struct pte
*pe
= self_pte(cxt
, l
->ext_index
);
2141 unsigned char empty
[512] = { 0 };
2142 fdisk_sector_t off
= pe
? get_abs_partition_start(pe
) : 0;
2144 if (off
&& pe
->changed
) {
2145 mbr_set_magic(empty
);
2146 write_sector(cxt
, off
, empty
);
2150 /* EBR (logical partitions) */
2151 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2152 struct pte
*pe
= self_pte(cxt
, i
);
2155 if (!pe
->changed
|| !pe
->offset
|| !pe
->sectorbuffer
)
2158 mbr_set_magic(pe
->sectorbuffer
);
2159 rc
= write_sector(cxt
, pe
->offset
, pe
->sectorbuffer
);
2168 static int dos_locate_disklabel(struct fdisk_context
*cxt
, int n
,
2169 const char **name
, uint64_t *offset
, size_t *size
)
2184 /* extended partitions */
2185 if ((size_t)n
- 1 + 4 < cxt
->label
->nparts_max
) {
2186 struct pte
*pe
= self_pte(cxt
, n
- 1 + 4);
2189 assert(pe
->private_sectorbuffer
);
2192 *offset
= (uint64_t) pe
->offset
* cxt
->sector_size
;
2203 * Check whether partition entries are ordered by their starting positions.
2204 * Return 0 if OK. Return i if partition i should have been earlier.
2205 * Two separate checks: primary and logical partitions.
2207 static int wrong_p_order(struct fdisk_context
*cxt
, size_t *prev
)
2209 size_t last_p_start_pos
= 0, p_start_pos
;
2210 size_t i
, last_i
= 0;
2212 for (i
= 0 ; i
< cxt
->label
->nparts_max
; i
++) {
2214 struct pte
*pe
= self_pte(cxt
, i
);
2215 struct dos_partition
*p
;
2222 last_p_start_pos
= 0;
2224 if (is_used_partition(p
)) {
2225 p_start_pos
= get_abs_partition_start(pe
);
2227 if (last_p_start_pos
> p_start_pos
) {
2233 last_p_start_pos
= p_start_pos
;
2240 static int dos_get_disklabel_item(struct fdisk_context
*cxt
, struct fdisk_labelitem
*item
)
2246 assert(fdisk_is_label(cxt
, DOS
));
2249 case FDISK_LABELITEM_ID
:
2251 unsigned int num
= mbr_get_id(cxt
->firstsector
);
2252 item
->name
= _("Disk identifier");
2254 if (asprintf(&item
->data
.str
, "0x%08x", num
) < 0)
2259 if (item
->id
< __FDISK_NLABELITEMS
)
2260 rc
= 1; /* unsupported generic item */
2262 rc
= 2; /* out of range */
2270 static int dos_get_partition(struct fdisk_context
*cxt
, size_t n
,
2271 struct fdisk_partition
*pa
)
2273 struct dos_partition
*p
;
2275 struct fdisk_dos_label
*lb
;
2280 assert(fdisk_is_label(cxt
, DOS
));
2282 lb
= self_label(cxt
);
2284 pe
= self_pte(cxt
, n
);
2288 pa
->used
= !is_cleared_partition(p
);
2292 pa
->type
= dos_partition_parttype(cxt
, p
);
2293 pa
->boot
= p
->boot_ind
== ACTIVE_FLAG
? 1 : 0;
2294 pa
->start
= get_abs_partition_start(pe
);
2295 pa
->size
= dos_partition_get_size(p
);
2296 pa
->container
= lb
->ext_offset
&& n
== lb
->ext_index
;
2299 pa
->parent_partno
= lb
->ext_index
;
2301 if (p
->boot_ind
&& asprintf(&pa
->attrs
, "%02x", p
->boot_ind
) < 0)
2305 if (asprintf(&pa
->start_chs
, "%d/%d/%d",
2306 cylinder(p
->bs
, p
->bc
),
2312 if (asprintf(&pa
->end_chs
, "%d/%d/%d",
2313 cylinder(p
->es
, p
->ec
),
2321 static int has_logical(struct fdisk_context
*cxt
)
2324 struct fdisk_dos_label
*l
= self_label(cxt
);
2326 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2327 if (l
->ptes
[i
].pt_entry
)
2333 static int dos_set_partition(struct fdisk_context
*cxt
, size_t n
,
2334 struct fdisk_partition
*pa
)
2336 struct fdisk_dos_label
*l
;
2337 struct dos_partition
*p
;
2340 fdisk_sector_t start
, size
;
2345 assert(fdisk_is_label(cxt
, DOS
));
2347 if (n
>= cxt
->label
->nparts_max
)
2350 l
= self_label(cxt
);
2351 p
= self_partition(cxt
, n
);
2354 pe
= self_pte(cxt
, n
);
2358 orgtype
= p
->sys_ind
;
2361 if (IS_EXTENDED(pa
->type
->code
) && l
->ext_offset
&& l
->ext_index
!= n
) {
2362 fdisk_warnx(cxt
, _("Extended partition already exists."));
2366 if (!pa
->type
->code
)
2367 fdisk_warnx(cxt
, _("Type 0 means free space to many systems. "
2368 "Having partitions of type 0 is probably unwise."));
2370 if (IS_EXTENDED(p
->sys_ind
) && !IS_EXTENDED(pa
->type
->code
) && has_logical(cxt
)) {
2372 "Cannot change type of the extended partition which is "
2373 "already used by logical partitions. Delete logical "
2374 "partitions first."));
2379 FDISK_INIT_UNDEF(start
);
2380 FDISK_INIT_UNDEF(size
);
2382 if (fdisk_partition_has_start(pa
))
2384 if (fdisk_partition_has_size(pa
))
2387 if (!FDISK_IS_UNDEF(start
) || !FDISK_IS_UNDEF(size
)) {
2388 DBG(LABEL
, ul_debug("DOS: resize partition"));
2390 if (FDISK_IS_UNDEF(start
))
2391 start
= get_abs_partition_start(pe
);
2392 if (FDISK_IS_UNDEF(size
))
2393 size
= dos_partition_get_size(p
);
2395 set_partition(cxt
, n
, 0, start
, start
+ size
- 1,
2396 pa
->type
? pa
->type
->code
: p
->sys_ind
,
2397 FDISK_IS_UNDEF(pa
->boot
) ?
2398 p
->boot_ind
== ACTIVE_FLAG
:
2399 fdisk_partition_is_bootable(pa
));
2401 DBG(LABEL
, ul_debug("DOS: keep size, modify properties"));
2403 p
->sys_ind
= pa
->type
->code
;
2404 if (!FDISK_IS_UNDEF(pa
->boot
))
2405 p
->boot_ind
= fdisk_partition_is_bootable(pa
) ? ACTIVE_FLAG
: 0;
2409 if (IS_EXTENDED(pa
->type
->code
) && !IS_EXTENDED(orgtype
)) {
2410 /* new extended partition - create a reference */
2412 l
->ext_offset
= dos_partition_get_start(p
);
2414 } else if (IS_EXTENDED(orgtype
)) {
2415 /* remove extended partition */
2416 cxt
->label
->nparts_max
= 4;
2417 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
2423 partition_set_changed(cxt
, n
, 1);
2427 static void print_chain_of_logicals(struct fdisk_context
*cxt
)
2430 struct fdisk_dos_label
*l
= self_label(cxt
);
2432 fputc('\n', stdout
);
2434 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2435 struct pte
*pe
= self_pte(cxt
, i
);
2438 fprintf(stderr
, "#%02zu EBR [%10ju], "
2439 "data[start=%10ju (%10ju), size=%10ju], "
2440 "link[start=%10ju (%10ju), size=%10ju]\n",
2441 i
, (uintmax_t) pe
->offset
,
2443 (uintmax_t) dos_partition_get_start(pe
->pt_entry
),
2444 (uintmax_t) get_abs_partition_start(pe
),
2445 (uintmax_t) dos_partition_get_size(pe
->pt_entry
),
2447 (uintmax_t) dos_partition_get_start(pe
->ex_entry
),
2448 (uintmax_t) l
->ext_offset
+ dos_partition_get_start(pe
->ex_entry
),
2449 (uintmax_t) dos_partition_get_size(pe
->ex_entry
));
2453 static int cmp_ebr_offsets(const void *a
, const void *b
)
2455 const struct pte
*ae
= (const struct pte
*) a
,
2456 *be
= (const struct pte
*) b
;
2458 if (ae
->offset
== 0 && be
->offset
== 0)
2460 if (ae
->offset
== 0)
2462 if (be
->offset
== 0)
2465 return cmp_numbers(ae
->offset
, be
->offset
);
2469 * Fix the chain of logicals.
2471 * The function does not modify data partitions within EBR tables
2472 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
2473 * (pte->ex_entry) between EBR tables.
2476 static void fix_chain_of_logicals(struct fdisk_context
*cxt
)
2478 struct fdisk_dos_label
*l
= self_label(cxt
);
2482 DBG(LABEL
, print_chain_of_logicals(cxt
));
2484 /* Sort chain by EBR offsets */
2485 qsort(&l
->ptes
[4], cxt
->label
->nparts_max
- 4, sizeof(struct pte
),
2489 /* Sort data partitions by start */
2490 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
2491 struct pte
*cur
= self_pte(cxt
, i
),
2492 *nxt
= self_pte(cxt
, i
+ 1);
2497 if (get_abs_partition_start(cur
) >
2498 get_abs_partition_start(nxt
)) {
2500 struct dos_partition tmp
= *cur
->pt_entry
;
2501 fdisk_sector_t cur_start
= get_abs_partition_start(cur
),
2502 nxt_start
= get_abs_partition_start(nxt
);
2504 /* swap data partitions */
2505 *cur
->pt_entry
= *nxt
->pt_entry
;
2506 *nxt
->pt_entry
= tmp
;
2508 /* Recount starts according to EBR offsets, the absolute
2509 * address still has to be the same! */
2510 dos_partition_set_start(cur
->pt_entry
, nxt_start
- cur
->offset
);
2511 dos_partition_sync_chs(cur
->pt_entry
, cur
->offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
2512 dos_partition_set_start(nxt
->pt_entry
, cur_start
- nxt
->offset
);
2513 dos_partition_sync_chs(nxt
->pt_entry
, nxt
->offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
2515 partition_set_changed(cxt
, i
, 1);
2516 partition_set_changed(cxt
, i
+ 1, 1);
2521 /* Update EBR links */
2522 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
2523 struct pte
*cur
= self_pte(cxt
, i
),
2524 *nxt
= self_pte(cxt
, i
+ 1);
2529 fdisk_sector_t noff
= nxt
->offset
- l
->ext_offset
,
2530 ooff
= dos_partition_get_start(cur
->ex_entry
);
2535 DBG(LABEL
, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2536 (uintmax_t) cur
->offset
,
2537 (uintmax_t) ooff
, (uintmax_t) noff
));
2539 set_partition(cxt
, i
, 1, nxt
->offset
,
2540 get_abs_partition_end(nxt
),
2541 MBR_DOS_EXTENDED_PARTITION
, 0);
2544 /* always terminate the chain ! */
2545 last
= self_pte(cxt
, cxt
->label
->nparts_max
- 1);
2547 clear_partition(last
->ex_entry
);
2548 partition_set_changed(cxt
, cxt
->label
->nparts_max
- 1, 1);
2551 DBG(LABEL
, print_chain_of_logicals(cxt
));
2554 static int dos_reorder(struct fdisk_context
*cxt
)
2556 struct pte
*pei
, *pek
;
2559 if (!wrong_p_order(cxt
, NULL
))
2562 while ((i
= wrong_p_order(cxt
, &k
)) != 0 && i
< 4) {
2563 /* partition i should have come earlier, move it */
2564 /* We have to move data in the MBR */
2565 struct dos_partition
*pi
, *pk
, *pe
, pbuf
;
2566 pei
= self_pte(cxt
, i
);
2567 pek
= self_pte(cxt
, k
);
2573 pei
->ex_entry
= pek
->ex_entry
;
2579 memmove(&pbuf
, pi
, sizeof(struct dos_partition
));
2580 memmove(pi
, pk
, sizeof(struct dos_partition
));
2581 memmove(pk
, &pbuf
, sizeof(struct dos_partition
));
2583 partition_set_changed(cxt
, i
, 1);
2584 partition_set_changed(cxt
, k
, 1);
2588 fix_chain_of_logicals(cxt
);
2594 * fdisk_dos_fix_chs:
2595 * @cxt: fdisk context
2597 * Fix beginning and ending C/H/S values for every partition
2598 * according to LBA relative offset, relative beginning and
2599 * size and fdisk idea of disk geometry (sectors per track
2600 * and number of heads).
2602 * Returns: number of fixed (changed) partitions.
2604 int fdisk_dos_fix_chs(struct fdisk_context
*cxt
)
2606 unsigned int obc
, obh
, obs
; /* old beginning c, h, s */
2607 unsigned int oec
, oeh
, oes
; /* old ending c, h, s */
2608 unsigned int nbc
, nbh
, nbs
; /* new beginning c, h, s */
2609 unsigned int nec
, neh
, nes
; /* new ending c, h, s */
2610 fdisk_sector_t l
, sects
; /* lba beginning and size */
2611 struct dos_partition
*p
;
2616 assert(fdisk_is_label(cxt
, DOS
));
2618 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
2619 p
= self_partition(cxt
, i
);
2620 if (!p
|| !is_used_partition(p
))
2623 pe
= self_pte(cxt
, i
);
2625 /* old beginning c, h, s */
2626 obc
= cylinder(p
->bs
, p
->bc
);
2628 obs
= sector(p
->bs
);
2630 /* old ending c, h, s */
2631 oec
= cylinder(p
->es
, p
->ec
);
2633 oes
= sector(p
->es
);
2635 /* new beginning c, h, s */
2636 l
= get_abs_partition_start(pe
);
2637 long2chs(cxt
, l
, &nbc
, &nbh
, &nbs
);
2638 if (l
> UINT32_MAX
|| nbc
>= 1024) {
2640 nbh
= cxt
->geom
.heads
-1;
2641 nbs
= cxt
->geom
.sectors
;
2644 /* new ending c, h, s */
2645 sects
= dos_partition_get_size(p
);
2646 long2chs(cxt
, l
+ sects
- 1, &nec
, &neh
, &nes
);
2647 if (lba_overflowed(l
, sects
) || nec
>= 1024) {
2649 neh
= cxt
->geom
.heads
-1;
2650 nes
= cxt
->geom
.sectors
;
2653 if (obc
!= nbc
|| obh
!= nbh
|| obs
!= nbs
||
2654 oec
!= nec
|| oeh
!= neh
|| oes
!= nes
) {
2655 DBG(LABEL
, ul_debug("DOS: changing %zu partition CHS "
2656 "from (%d, %d, %d)-(%d, %d, %d) "
2657 "to (%d, %d, %d)-(%d, %d, %d)",
2658 i
, obc
, obh
, obs
, oec
, oeh
, oes
,
2659 nbc
, nbh
, nbs
, nec
, neh
, nes
));
2662 p
->bs
= nbs
| ((nbc
>> 2) & 0xc0);
2665 p
->es
= nes
| ((nec
>> 2) & 0xc0);
2666 partition_set_changed(cxt
, i
, 1);
2674 /* TODO: use fdisk_set_partition() API */
2675 int fdisk_dos_move_begin(struct fdisk_context
*cxt
, size_t i
)
2678 struct dos_partition
*p
;
2679 unsigned int new, free_start
, curr_start
, last
;
2685 assert(fdisk_is_label(cxt
, DOS
));
2687 pe
= self_pte(cxt
, i
);
2693 if (!is_used_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
2694 fdisk_warnx(cxt
, _("Partition %zu: no data area."), i
+ 1);
2698 /* The safe start is at the second sector, but some use-cases require
2699 * to have MBR within the first partition , so default to the first
2700 * sector of the disk or at the second sector of the extended partition
2702 free_start
= pe
->offset
? pe
->offset
+ 1 : 0;
2704 curr_start
= get_abs_partition_start(pe
);
2706 /* look for a free space before the current start of the partition */
2707 for (x
= 0; x
< cxt
->label
->nparts_max
; x
++) {
2709 struct pte
*prev_pe
= self_pte(cxt
, x
);
2710 struct dos_partition
*prev_p
;
2714 prev_p
= prev_pe
->pt_entry
;
2717 end
= get_abs_partition_start(prev_pe
)
2718 + dos_partition_get_size(prev_p
);
2720 if (is_used_partition(prev_p
) &&
2721 end
> free_start
&& end
<= curr_start
)
2725 last
= get_abs_partition_end(pe
);
2727 rc
= fdisk_ask_number(cxt
, free_start
, curr_start
, last
,
2728 _("New beginning of data"), &res
);
2732 new = res
- pe
->offset
;
2734 if (new != dos_partition_get_size(p
)) {
2735 unsigned int sects
= dos_partition_get_size(p
)
2736 + dos_partition_get_start(p
) - new;
2738 dos_partition_set_size(p
, sects
);
2739 dos_partition_set_start(p
, new);
2740 dos_partition_sync_chs(p
, pe
->offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
2742 partition_set_changed(cxt
, i
, 1);
2745 fdisk_info(cxt
, _("The new beginning of the partition overlaps the disk "
2746 "label area. Be very careful when using the partition. "
2747 "You can lose all your partitions on the disk."));
2753 static int dos_partition_is_used(
2754 struct fdisk_context
*cxt
,
2757 struct dos_partition
*p
;
2761 assert(fdisk_is_label(cxt
, DOS
));
2763 if (i
>= cxt
->label
->nparts_max
)
2766 p
= self_partition(cxt
, i
);
2768 return p
&& !is_cleared_partition(p
);
2771 static int dos_toggle_partition_flag(
2772 struct fdisk_context
*cxt
,
2776 struct dos_partition
*p
;
2780 assert(fdisk_is_label(cxt
, DOS
));
2782 if (i
>= cxt
->label
->nparts_max
)
2785 p
= self_partition(cxt
, i
);
2789 case DOS_FLAG_ACTIVE
:
2790 if (IS_EXTENDED(p
->sys_ind
) && !p
->boot_ind
)
2791 fdisk_warnx(cxt
, _("Partition %zu: is an extended "
2792 "partition."), i
+ 1);
2794 p
->boot_ind
= (p
->boot_ind
? 0 : ACTIVE_FLAG
);
2795 partition_set_changed(cxt
, i
, 1);
2796 fdisk_info(cxt
, p
->boot_ind
?
2797 _("The bootable flag on partition %zu is enabled now.") :
2798 _("The bootable flag on partition %zu is disabled now."),
2808 static const struct fdisk_field dos_fields
[] =
2811 { FDISK_FIELD_DEVICE
, N_("Device"), 10, 0 },
2812 { FDISK_FIELD_BOOT
, N_("Boot"), 1, 0 },
2813 { FDISK_FIELD_START
, N_("Start"), 5, FDISK_FIELDFL_NUMBER
},
2814 { FDISK_FIELD_END
, N_("End"), 5, FDISK_FIELDFL_NUMBER
},
2815 { FDISK_FIELD_SECTORS
, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER
},
2816 { FDISK_FIELD_CYLINDERS
,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER
},
2817 { FDISK_FIELD_SIZE
, N_("Size"), 5, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_EYECANDY
},
2818 { FDISK_FIELD_TYPEID
, N_("Id"), 2, FDISK_FIELDFL_NUMBER
},
2819 { FDISK_FIELD_TYPE
, N_("Type"), 0.1, 0 },
2822 { FDISK_FIELD_SADDR
, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2823 { FDISK_FIELD_EADDR
, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2824 { FDISK_FIELD_ATTR
, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
}
2828 static const struct fdisk_label_operations dos_operations
=
2830 .probe
= dos_probe_label
,
2831 .write
= dos_write_disklabel
,
2832 .verify
= dos_verify_disklabel
,
2833 .create
= dos_create_disklabel
,
2834 .locate
= dos_locate_disklabel
,
2835 .get_item
= dos_get_disklabel_item
,
2836 .set_id
= dos_set_disklabel_id
,
2838 .get_part
= dos_get_partition
,
2839 .set_part
= dos_set_partition
,
2840 .add_part
= dos_add_partition
,
2841 .del_part
= dos_delete_partition
,
2842 .reorder
= dos_reorder
,
2844 .part_toggle_flag
= dos_toggle_partition_flag
,
2845 .part_is_used
= dos_partition_is_used
,
2847 .reset_alignment
= dos_reset_alignment
,
2849 .deinit
= dos_deinit
,
2853 * allocates DOS in-memory stuff
2855 struct fdisk_label
*fdisk_new_dos_label(struct fdisk_context
*cxt
__attribute__ ((__unused__
)))
2857 struct fdisk_label
*lb
;
2858 struct fdisk_dos_label
*dos
;
2860 dos
= calloc(1, sizeof(*dos
));
2864 /* initialize generic part of the driver */
2865 lb
= (struct fdisk_label
*) dos
;
2867 lb
->id
= FDISK_DISKLABEL_DOS
;
2868 lb
->op
= &dos_operations
;
2870 lb
->parttypes
= dos_parttypes
;
2871 lb
->nparttypes
= ARRAY_SIZE(dos_parttypes
) - 1;
2872 lb
->parttype_cuts
= dos_parttype_cuts
;
2873 lb
->nparttype_cuts
= ARRAY_SIZE(dos_parttype_cuts
);
2875 lb
->fields
= dos_fields
;
2876 lb
->nfields
= ARRAY_SIZE(dos_fields
);
2878 lb
->geom_min
.sectors
= 1;
2879 lb
->geom_min
.heads
= 1;
2880 lb
->geom_min
.cylinders
= 1;
2882 lb
->geom_max
.sectors
= 63;
2883 lb
->geom_max
.heads
= 255;
2884 lb
->geom_max
.cylinders
= 1048576;
2886 /* return calloc() result to keep static anaylizers happy */
2887 return (struct fdisk_label
*) dos
;
2891 * fdisk_dos_enable_compatible:
2892 * @lb: DOS label (see fdisk_get_label())
2895 * Enables deprecated DOS compatible mode, in this mode library checks for
2896 * cylinders boundary, cases about CHS addressing and another obscure things.
2898 * Returns: 0 on success, <0 on error.
2900 int fdisk_dos_enable_compatible(struct fdisk_label
*lb
, int enable
)
2902 struct fdisk_dos_label
*dos
= (struct fdisk_dos_label
*) lb
;
2907 dos
->compatible
= enable
;
2909 lb
->flags
|= FDISK_LABEL_FL_REQUIRE_GEOMETRY
;
2914 * fdisk_dos_is_compatible:
2917 * Returns: 0 if DOS compatibility disabled, 1 if enabled
2919 int fdisk_dos_is_compatible(struct fdisk_label
*lb
)
2921 return ((struct fdisk_dos_label
*) lb
)->compatible
;