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"
20 #define MAXIMUM_PARTS 60
21 #define ACTIVE_FLAG 0x80
26 * @short_description: disk label specific functions
31 #define IS_EXTENDED(i) \
32 ((i) == MBR_DOS_EXTENDED_PARTITION \
33 || (i) == MBR_W95_EXTENDED_PARTITION \
34 || (i) == MBR_LINUX_EXTENDED_PARTITION)
37 * per partition table entry data
39 * The four primary partitions have the same sectorbuffer
40 * and have NULL ex_entry.
42 * Each logical partition table entry has two pointers, one for the
43 * partition and one link to the next one.
46 struct dos_partition
*pt_entry
; /* on-disk MBR entry */
47 struct dos_partition
*ex_entry
; /* on-disk EBR entry */
48 fdisk_sector_t offset
; /* disk sector number */
49 unsigned char *sectorbuffer
; /* disk sector contents */
51 unsigned int changed
: 1,
52 private_sectorbuffer
: 1;
56 * in-memory fdisk GPT stuff
58 struct fdisk_dos_label
{
59 struct fdisk_label head
; /* generic part */
61 struct pte ptes
[MAXIMUM_PARTS
]; /* partition */
62 fdisk_sector_t ext_offset
; /* start of the ext.partition */
63 size_t ext_index
; /* ext.partition index (if ext_offset is set) */
64 unsigned int compatible
: 1, /* is DOS compatible? */
65 non_pt_changed
: 1; /* MBR, but no PT changed */
71 static const struct fdisk_parttype dos_parttypes
[] = {
72 #include "pt-mbr-partnames.h"
75 static const struct fdisk_shortcut dos_parttype_cuts
[] =
77 { .shortcut
= "L", .alias
= "linux", .data
= "83" },
78 { .shortcut
= "S", .alias
= "swap", .data
= "82" },
79 { .shortcut
= "E", .alias
= "extended", .data
= "05", .deprecated
= 1 }, /* collision with 0x0e type */
80 { .shortcut
= "Ex",.alias
= "extended", .data
= "05" }, /* MBR extended */
81 { .shortcut
= "U", .alias
= "uefi", .data
= "EF" }, /* UEFI system */
82 { .shortcut
= "R", .alias
= "raid", .data
= "FD" }, /* Linux RAID */
83 { .shortcut
= "V", .alias
= "lvm", .data
= "8E" }, /* LVM */
84 { .shortcut
= "X", .alias
= "linuxex", .data
= "85" } /* Linux extended */
88 #define sector(s) ((s) & 0x3f)
89 #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
91 #define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
93 #define is_dos_compatible(_x) \
94 (fdisk_is_label(_x, DOS) && \
95 fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
97 #define cround(c, n) fdisk_cround(c, n)
100 static inline struct fdisk_dos_label
*self_label(struct fdisk_context
*cxt
)
104 assert(fdisk_is_label(cxt
, DOS
));
106 return (struct fdisk_dos_label
*) cxt
->label
;
109 static inline struct pte
*self_pte(struct fdisk_context
*cxt
, size_t i
)
111 struct fdisk_dos_label
*l
= self_label(cxt
);
113 if (i
>= ARRAY_SIZE(l
->ptes
))
119 static inline struct dos_partition
*self_partition(
120 struct fdisk_context
*cxt
,
123 struct pte
*pe
= self_pte(cxt
, i
);
124 return pe
? pe
->pt_entry
: NULL
;
127 struct dos_partition
*fdisk_dos_get_partition(
128 struct fdisk_context
*cxt
,
133 assert(fdisk_is_label(cxt
, DOS
));
135 return self_partition(cxt
, i
);
138 static struct fdisk_parttype
*dos_partition_parttype(
139 struct fdisk_context
*cxt
,
140 struct dos_partition
*p
)
142 struct fdisk_parttype
*t
143 = fdisk_label_get_parttype_from_code(cxt
->label
, p
->sys_ind
);
144 return t
? : fdisk_new_unknown_parttype(p
->sys_ind
, NULL
);
148 * Linux kernel cares about partition size only. Things like
149 * partition type or so are completely irrelevant -- kzak Nov-2013
151 static int is_used_partition(struct dos_partition
*p
)
153 return p
&& dos_partition_get_size(p
) != 0;
156 static void partition_set_changed(
157 struct fdisk_context
*cxt
,
161 struct pte
*pe
= self_pte(cxt
, i
);
166 DBG(LABEL
, ul_debug("DOS: setting %zu partition to %s", i
,
167 changed
? "changed" : "unchanged"));
169 pe
->changed
= changed
? 1 : 0;
171 fdisk_label_set_changed(cxt
->label
, 1);
174 static fdisk_sector_t
get_abs_partition_start(struct pte
*pe
)
177 assert(pe
->pt_entry
);
179 return pe
->offset
+ dos_partition_get_start(pe
->pt_entry
);
182 static fdisk_sector_t
get_abs_partition_end(struct pte
*pe
)
187 assert(pe
->pt_entry
);
189 size
= dos_partition_get_size(pe
->pt_entry
);
190 return get_abs_partition_start(pe
) + size
- (size
? 1 : 0);
193 static int is_cleared_partition(struct dos_partition
*p
)
195 return !(!p
|| p
->boot_ind
|| p
->bh
|| p
->bs
|| p
->bc
||
196 p
->sys_ind
|| p
->eh
|| p
->es
|| p
->ec
||
197 dos_partition_get_start(p
) || dos_partition_get_size(p
));
200 static int get_partition_unused_primary(struct fdisk_context
*cxt
,
201 struct fdisk_partition
*pa
,
211 org
= cxt
->label
->nparts_max
;
213 cxt
->label
->nparts_max
= 4;
214 rc
= fdisk_partition_next_partno(pa
, cxt
, &n
);
215 cxt
->label
->nparts_max
= org
;
218 fdisk_info(cxt
, _("All primary partitions have been defined already."));
220 } else if (rc
== -ERANGE
) {
221 fdisk_warnx(cxt
, _("Primary partition not available."));
228 static int seek_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
)
230 off_t offset
= (off_t
) secno
* cxt
->sector_size
;
232 return lseek(cxt
->dev_fd
, offset
, SEEK_SET
) == (off_t
) -1 ? -errno
: 0;
235 static int read_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
,
238 int rc
= seek_sector(cxt
, secno
);
244 r
= read(cxt
->dev_fd
, buf
, cxt
->sector_size
);
245 if (r
== (ssize_t
) cxt
->sector_size
)
252 /* Allocate a buffer and read a partition table sector */
253 static int read_pte(struct fdisk_context
*cxt
, size_t pno
, fdisk_sector_t offset
)
257 struct pte
*pe
= self_pte(cxt
, pno
);
262 buf
= calloc(1, cxt
->sector_size
);
266 DBG(LABEL
, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
267 pno
, (uintmax_t) offset
, buf
));
270 pe
->sectorbuffer
= buf
;
271 pe
->private_sectorbuffer
= 1;
273 rc
= read_sector(cxt
, offset
, pe
->sectorbuffer
);
275 fdisk_warn(cxt
, _("Failed to read extended partition table "
276 "(offset=%ju)"), (uintmax_t) offset
);
281 pe
->pt_entry
= pe
->ex_entry
= NULL
;
286 static void clear_partition(struct dos_partition
*p
)
298 dos_partition_set_start(p
,0);
299 dos_partition_set_size(p
,0);
302 static void dos_init(struct fdisk_context
*cxt
)
304 struct fdisk_dos_label
*l
= self_label(cxt
);
309 assert(fdisk_is_label(cxt
, DOS
));
311 DBG(LABEL
, ul_debug("DOS: initialize, first sector buffer %p", cxt
->firstsector
));
313 cxt
->label
->nparts_max
= 4; /* default, unlimited number of logical */
317 l
->non_pt_changed
= 0;
319 memset(l
->ptes
, 0, sizeof(l
->ptes
));
321 for (i
= 0; i
< 4; i
++) {
322 struct pte
*pe
= self_pte(cxt
, i
);
325 pe
->pt_entry
= mbr_get_partition(cxt
->firstsector
, i
);
328 pe
->sectorbuffer
= cxt
->firstsector
;
329 pe
->private_sectorbuffer
= 0;
332 DBG(LABEL
, ul_debug("DOS: initialize: #%zu start=%u size=%u sysid=%02x",
334 dos_partition_get_start(pe
->pt_entry
),
335 dos_partition_get_size(pe
->pt_entry
),
336 pe
->pt_entry
->sys_ind
));
339 if (fdisk_is_listonly(cxt
))
342 * Various warnings...
344 if (fdisk_missing_geometry(cxt
))
345 fdisk_warnx(cxt
, _("You can set geometry from the extra functions menu."));
347 if (is_dos_compatible(cxt
)) {
348 fdisk_warnx(cxt
, _("DOS-compatible mode is deprecated."));
350 if (cxt
->sector_size
!= cxt
->phy_sector_size
)
352 "The device presents a logical sector size that is smaller than "
353 "the physical sector size. Aligning to a physical sector (or optimal "
354 "I/O) size boundary is recommended, or performance may be impacted."));
357 if (fdisk_use_cylinders(cxt
))
358 fdisk_warnx(cxt
, _("Cylinders as display units are deprecated."));
360 if (cxt
->total_sectors
> UINT_MAX
) {
361 uint64_t bytes
= cxt
->total_sectors
* cxt
->sector_size
;
362 char *szstr
= size_to_human_string(SIZE_SUFFIX_SPACE
363 | SIZE_SUFFIX_3LETTER
, bytes
);
365 _("The size of this disk is %s (%ju bytes). DOS "
366 "partition table format cannot be used on drives for "
367 "volumes larger than %lu bytes for %lu-byte "
368 "sectors. Use GUID partition table format (GPT)."),
370 UINT_MAX
* cxt
->sector_size
,
376 /* callback called by libfdisk */
377 static void dos_deinit(struct fdisk_label
*lb
)
380 struct fdisk_dos_label
*l
= (struct fdisk_dos_label
*) lb
;
382 for (i
= 0; i
< ARRAY_SIZE(l
->ptes
); i
++) {
383 struct pte
*pe
= &l
->ptes
[i
];
385 if (pe
->private_sectorbuffer
&& pe
->sectorbuffer
) {
386 DBG(LABEL
, ul_debug("DOS: freeing pte %zu sector buffer %p",
387 i
, pe
->sectorbuffer
));
388 free(pe
->sectorbuffer
);
390 pe
->sectorbuffer
= NULL
;
391 pe
->private_sectorbuffer
= 0;
394 memset(l
->ptes
, 0, sizeof(l
->ptes
));
397 static void reset_pte(struct pte
*pe
)
401 if (pe
->private_sectorbuffer
) {
402 DBG(LABEL
, ul_debug(" --> freeing pte sector buffer %p",
404 free(pe
->sectorbuffer
);
406 memset(pe
, 0, sizeof(struct pte
));
409 static int delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
411 struct fdisk_dos_label
*l
;
413 struct dos_partition
*p
;
414 struct dos_partition
*q
;
418 assert(fdisk_is_label(cxt
, DOS
));
420 pe
= self_pte(cxt
, partnum
);
424 DBG(LABEL
, ul_debug("DOS: delete partition %zu (max=%zu)", partnum
,
425 cxt
->label
->nparts_max
));
431 /* Note that for the fifth partition (partnum == 4) we don't actually
432 decrement partitions. */
434 DBG(LABEL
, ul_debug("--> delete primary"));
435 if (IS_EXTENDED(p
->sys_ind
) && partnum
== l
->ext_index
) {
437 DBG(LABEL
, ul_debug(" --> delete extended"));
438 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
439 DBG(LABEL
, ul_debug(" --> delete logical #%zu", i
));
440 reset_pte(&l
->ptes
[i
]);
443 cxt
->label
->nparts_max
= 4;
444 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
448 partition_set_changed(cxt
, partnum
, 1);
450 } else if (!q
->sys_ind
&& partnum
> 4) {
451 DBG(LABEL
, ul_debug("--> delete logical [last in the chain]"));
452 reset_pte(&l
->ptes
[partnum
]);
453 --cxt
->label
->nparts_max
;
455 /* clear link to deleted partition */
456 clear_partition(l
->ptes
[partnum
].ex_entry
);
457 partition_set_changed(cxt
, partnum
, 1);
459 DBG(LABEL
, ul_debug("--> delete logical [move down]"));
461 DBG(LABEL
, ul_debug(" --> delete %zu logical link", partnum
));
462 p
= l
->ptes
[partnum
- 1].ex_entry
;
464 dos_partition_set_start(p
, dos_partition_get_start(q
));
465 dos_partition_set_size(p
, dos_partition_get_size(q
));
466 dos_partition_sync_chs(p
, pe
->offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
467 partition_set_changed(cxt
, partnum
- 1, 1);
469 } else if (cxt
->label
->nparts_max
> 5) {
470 DBG(LABEL
, ul_debug(" --> delete first logical link"));
471 pe
= &l
->ptes
[5]; /* second logical */
473 if (pe
->pt_entry
) /* prevent SEGFAULT */
474 dos_partition_set_start(pe
->pt_entry
,
475 get_abs_partition_start(pe
) -
477 pe
->offset
= l
->ext_offset
;
478 dos_partition_sync_chs(p
, pe
->offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
479 partition_set_changed(cxt
, 5, 1);
482 if (cxt
->label
->nparts_max
> 5) {
483 DBG(LABEL
, ul_debug(" --> move ptes"));
484 cxt
->label
->nparts_max
--;
485 reset_pte(&l
->ptes
[partnum
]);
486 while (partnum
< cxt
->label
->nparts_max
) {
487 DBG(LABEL
, ul_debug(" --> moving pte %zu <-- %zu", partnum
, partnum
+ 1));
488 l
->ptes
[partnum
] = l
->ptes
[partnum
+ 1];
491 memset(&l
->ptes
[partnum
], 0, sizeof(struct pte
));
493 DBG(LABEL
, ul_debug(" --> the only logical: clear only"));
494 clear_partition(l
->ptes
[partnum
].pt_entry
);
495 cxt
->label
->nparts_max
--;
498 DBG(LABEL
, ul_debug(" --> clear last logical"));
499 reset_pte(&l
->ptes
[partnum
]);
500 partition_set_changed(cxt
, l
->ext_index
, 1);
505 fdisk_label_set_changed(cxt
->label
, 1);
509 static int dos_delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
515 assert(fdisk_is_label(cxt
, DOS
));
517 pe
= self_pte(cxt
, partnum
);
518 if (!pe
|| !is_used_partition(pe
->pt_entry
))
521 return delete_partition(cxt
, partnum
);
524 static void read_extended(struct fdisk_context
*cxt
, size_t ext
)
527 struct pte
*pex
, *pe
;
528 struct dos_partition
*p
, *q
;
529 struct fdisk_dos_label
*l
= self_label(cxt
);
531 if (fdisk_is_listonly(cxt
) &&
532 !sysfs_devno_is_wholedisk(fdisk_get_devno(cxt
))) {
533 DBG(LABEL
, ul_debug("DOS: unable to gather logical partition chain "
534 "when running on a non-whole disk device."));
539 pex
= self_pte(cxt
, ext
);
541 DBG(LABEL
, ul_debug("DOS: uninitialized pointer to %zu pex", ext
));
544 pex
->ex_entry
= pex
->pt_entry
;
547 if (!dos_partition_get_start(p
)) {
548 fdisk_warnx(cxt
, _("Bad offset in primary extended partition."));
552 DBG(LABEL
, ul_debug("DOS: Reading extended %zu", ext
));
554 while (IS_EXTENDED (p
->sys_ind
)) {
555 if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
556 /* This is not a Linux restriction, but
557 this program uses arrays of size MAXIMUM_PARTS.
558 Do not try to `improve' this test. */
559 struct pte
*pre
= self_pte(cxt
,
560 cxt
->label
->nparts_max
- 1);
562 _("Omitting partitions after #%zu. They will be deleted "
563 "if you save this partition table."),
564 cxt
->label
->nparts_max
);
567 clear_partition(pre
->ex_entry
);
568 partition_set_changed(cxt
,
569 cxt
->label
->nparts_max
- 1, 1);
574 pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
578 if (read_pte(cxt
, cxt
->label
->nparts_max
, l
->ext_offset
+
579 dos_partition_get_start(p
)))
583 l
->ext_offset
= dos_partition_get_start(p
);
585 assert(pe
->sectorbuffer
);
586 q
= p
= mbr_get_partition(pe
->sectorbuffer
, 0);
588 for (i
= 0; i
< 4; i
++, p
++) {
589 if (!dos_partition_get_size(p
))
592 if (IS_EXTENDED (p
->sys_ind
)) {
595 "Extra link pointer in partition "
597 cxt
->label
->nparts_max
+ 1);
600 } else if (p
->sys_ind
) {
603 "Ignoring extra data in partition "
605 cxt
->label
->nparts_max
+ 1);
611 /* very strange code here... */
613 if (q
!= pe
->ex_entry
)
616 pe
->pt_entry
= q
+ 1;
619 if (q
!= pe
->pt_entry
)
622 pe
->ex_entry
= q
+ 1;
626 cxt
->label
->nparts_cur
= ++cxt
->label
->nparts_max
;
628 DBG(LABEL
, ul_debug("DOS: EBR[offset=%ju]: link: type=%x, start=%u, size=%u; "
629 " data: type=%x, start=%u, size=%u",
630 (uintmax_t) pe
->offset
,
631 pe
->ex_entry
->sys_ind
,
632 dos_partition_get_start(pe
->ex_entry
),
633 dos_partition_get_size(pe
->ex_entry
),
634 pe
->pt_entry
->sys_ind
,
635 dos_partition_get_start(pe
->pt_entry
),
636 dos_partition_get_size(pe
->pt_entry
)));
640 /* remove last empty EBR */
641 pe
= self_pte(cxt
, cxt
->label
->nparts_max
- 1);
643 is_cleared_partition(pe
->ex_entry
) &&
644 is_cleared_partition(pe
->pt_entry
)) {
645 DBG(LABEL
, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe
->offset
));
647 cxt
->label
->nparts_max
--;
648 cxt
->label
->nparts_cur
--;
651 /* remove empty links */
653 q
= self_partition(cxt
, 4);
654 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
655 p
= self_partition(cxt
, i
);
657 if (p
&& !dos_partition_get_size(p
) &&
658 (cxt
->label
->nparts_max
> 5 || (q
&& q
->sys_ind
))) {
659 fdisk_info(cxt
, _("omitting empty partition (%zu)"), i
+1);
660 delete_partition(cxt
, i
);
661 goto remove
; /* numbering changed */
665 DBG(LABEL
, ul_debug("DOS: nparts_max: %zu", cxt
->label
->nparts_max
));
668 static int dos_create_disklabel(struct fdisk_context
*cxt
)
672 struct fdisk_dos_label
*l
;
676 assert(fdisk_is_label(cxt
, DOS
));
678 DBG(LABEL
, ul_debug("DOS: creating new disklabel"));
682 const char *s
= fdisk_script_get_header(cxt
->script
, "label-id");
686 id
= strtoul(s
, &end
, 16);
687 if (!errno
&& end
&& s
< end
) {
689 DBG(LABEL
, ul_debug("DOS: re-use ID from script (0x%08x)", id
));
691 DBG(LABEL
, ul_debug("DOS: failed to parse label=id '%s'", s
));
695 /* random disk signature */
697 DBG(LABEL
, ul_debug("DOS: generate new ID"));
698 ul_random_get_bytes(&id
, sizeof(id
));
701 if (fdisk_has_protected_bootbits(cxt
))
702 rc
= fdisk_init_firstsector_buffer(cxt
, 0, MBR_PT_BOOTBITS_SIZE
);
704 rc
= fdisk_init_firstsector_buffer(cxt
, 0, 0);
711 /* Generate an MBR ID for this disk */
712 mbr_set_id(cxt
->firstsector
, id
);
713 l
->non_pt_changed
= 1;
714 fdisk_label_set_changed(cxt
->label
, 1);
716 /* Put MBR signature */
717 mbr_set_magic(cxt
->firstsector
);
719 fdisk_info(cxt
, _("Created a new DOS (MBR) disklabel with disk "
720 "identifier 0x%08x."), id
);
724 static int dos_set_disklabel_id(struct fdisk_context
*cxt
, const char *str
)
727 unsigned int id
, old
;
728 struct fdisk_dos_label
*l
;
733 assert(fdisk_is_label(cxt
, DOS
));
735 DBG(LABEL
, ul_debug("DOS: setting Id"));
738 old
= mbr_get_id(cxt
->firstsector
);
741 rc
= fdisk_ask_string(cxt
,
742 _("Enter the new disk identifier"), &buf
);
749 id
= strtoul(str
, &end
, 0);
750 if (errno
|| str
== end
|| (end
&& *end
)) {
751 fdisk_warnx(cxt
, _("Incorrect value."));
760 mbr_set_id(cxt
->firstsector
, id
);
761 l
->non_pt_changed
= 1;
762 fdisk_label_set_changed(cxt
->label
, 1);
764 fdisk_info(cxt
, _("Disk identifier changed from 0x%08x to 0x%08x."),
769 static unsigned int chs_div_minus(unsigned int a1
, unsigned int a2
, unsigned int b1
, unsigned int b2
)
771 if (a1
> a2
&& b1
> b2
) {
774 } else if (a2
> a1
&& b2
> b1
) {
785 static inline int chs_overflowed(unsigned int c
, unsigned int h
, unsigned int s
)
787 /* 1023/254/63 or 1023/255/63 indicates overflowed/invalid C/H/S values */
788 return (c
== 1023 && (h
== 254 || h
== 255) && s
== 63);
791 static inline int lba_overflowed(fdisk_sector_t start
, fdisk_sector_t sects
)
793 /* Check if the last LBA sector can be represented by unsigned 32bit int */
794 return (start
+ (sects
-1) > UINT32_MAX
);
797 static void get_partition_table_geometry(struct fdisk_context
*cxt
,
798 unsigned int *ph
, unsigned int *ps
)
800 unsigned char *bufp
= cxt
->firstsector
;
801 struct { unsigned int c
, h
, o
, v
; } t
[8] = { 0 };
802 unsigned int n1
, n2
, n3
, n4
, n5
, n6
;
803 const struct dos_partition
*p
;
804 unsigned int c
, h
, s
, l
;
809 #define chs_set_t(c, h, s, l, t, i) do { \
812 t[i].o = l - (s-1); \
813 t[i].v = (!chs_overflowed(c, h, s) && s && s-1 <= l); \
817 * Conversion from C/H/S to LBA is defined by formula:
818 * LBA = (c * N_heads + h) * N_sectors + (s - 1)
821 * Then formula can be expressed as:
822 * o = (c * N_heads + h) * N_sectors
823 * In general from two tuples (LBA1, c1, h1, s1), (LBA2, c2, h2, s2)
824 * we can derive formulas for N_heads and N_sectors:
825 * N_heads = (o1 * h2 - o2 * h1) / (o2 * c1 - o1 * c2)
826 * N_sectors = (o2 * c1 - o1 * c2) / (c1 * h2 - c2 * h1)
827 * MBR table contains for very partition start and end tuple.
828 * So we have up to 8 tuples which leads to up to 28 equations
829 * for calculating N_heads and N_sectors. Try to calculate
830 * N_heads and N_sectors from the first possible partition and
831 * if it fails then try also mixed tuples (beginning from first
832 * partition and end from second). Calculation may fail if both
833 * first and last sectors are on cylinder or head boundary
834 * (dividend or divisor is zero). It is possible that different
835 * partitions would have different C/H/S geometry. In this case
836 * we want geometry from the first partition as in most cases
837 * this partition is or was used by BIOS for booting.
841 for (i
= 0; i
< 4; i
++) {
842 p
= mbr_get_partition(bufp
, i
);
846 c
= cylinder(p
->bs
, p
->bc
);
849 l
= dos_partition_get_start(p
);
850 chs_set_t(c
, h
, s
, l
, t
, 2*i
);
852 sects
= dos_partition_get_size(p
);
853 if (!sects
|| lba_overflowed(l
, sects
))
856 c
= cylinder(p
->es
, p
->ec
);
860 chs_set_t(c
, h
, s
, l
, t
, 2*i
+1);
863 for (dif
= 1; dif
< 8; dif
++) {
864 for (i
= 0; i
+ dif
< 8; i
++) {
866 if (!t
[i
].v
|| !t
[j
].v
)
868 n1
= t
[i
].o
* t
[j
].h
;
869 n2
= t
[j
].o
* t
[i
].h
;
870 n3
= t
[j
].o
* t
[i
].c
;
871 n4
= t
[i
].o
* t
[j
].c
;
872 n5
= t
[i
].c
* t
[j
].h
;
873 n6
= t
[j
].c
* t
[i
].h
;
874 if (!hh
&& n1
!= n2
&& n3
!= n4
) {
875 h
= chs_div_minus(n1
, n2
, n3
, n4
);
876 if (h
> 0 && h
<= 256)
879 if (!ss
&& n3
!= n4
&& n5
!= n6
) {
880 s
= chs_div_minus(n3
, n4
, n5
, n6
);
881 if (s
> 0 && s
<= 63)
896 DBG(LABEL
, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph
, *ps
));
899 static int dos_reset_alignment(struct fdisk_context
*cxt
)
903 assert(fdisk_is_label(cxt
, DOS
));
905 /* overwrite necessary stuff by DOS deprecated stuff */
906 if (is_dos_compatible(cxt
)) {
907 DBG(LABEL
, ul_debug("DOS: resetting alignment for DOS-compatible PT"));
908 if (cxt
->geom
.sectors
)
909 cxt
->first_lba
= cxt
->geom
.sectors
; /* usually 63 */
911 cxt
->grain
= cxt
->sector_size
; /* usually 512 */
917 /* TODO: move to include/pt-dos.h and share with libblkid */
918 #define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
919 #define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
921 static int dos_probe_label(struct fdisk_context
*cxt
)
924 unsigned int h
= 0, s
= 0;
928 assert(fdisk_is_label(cxt
, DOS
));
930 /* ignore disks with AIX magic number */
931 if (memcmp(cxt
->firstsector
, AIX_MAGIC_STRING
, AIX_MAGIC_STRLEN
) == 0)
934 if (!mbr_is_valid_magic(cxt
->firstsector
))
939 get_partition_table_geometry(cxt
, &h
, &s
);
942 cxt
->geom
.sectors
= s
;
943 cxt
->geom
.cylinders
= cxt
->total_sectors
/
944 (cxt
->geom
.heads
* cxt
->geom
.sectors
);
946 if (fdisk_has_user_device_geometry(cxt
))
947 fdisk_apply_user_device_properties(cxt
);
950 for (i
= 0; i
< 4; i
++) {
951 struct pte
*pe
= self_pte(cxt
, i
);
954 if (is_used_partition(pe
->pt_entry
))
955 cxt
->label
->nparts_cur
++;
957 if (IS_EXTENDED (pe
->pt_entry
->sys_ind
)) {
958 if (cxt
->label
->nparts_max
!= 4)
960 "Ignoring extra extended partition %zu"),
963 read_extended(cxt
, i
);
967 for (i
= 3; i
< cxt
->label
->nparts_max
; i
++) {
968 struct pte
*pe
= self_pte(cxt
, i
);
969 struct fdisk_dos_label
*l
= self_label(cxt
);
972 if (!mbr_is_valid_magic(pe
->sectorbuffer
)) {
974 "Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
975 "be corrected by w(rite)."),
976 pe
->sectorbuffer
[510],
977 pe
->sectorbuffer
[511],
979 partition_set_changed(cxt
, i
, 1);
981 /* mark also extended as changed to update the first EBR
982 * in situation that there is no logical partitions at all */
983 partition_set_changed(cxt
, l
->ext_index
, 1);
990 static void set_partition(struct fdisk_context
*cxt
,
991 int i
, int doext
, fdisk_sector_t start
,
992 fdisk_sector_t stop
, int sysid
, int boot
)
994 struct pte
*pe
= self_pte(cxt
, i
);
995 struct dos_partition
*p
;
996 fdisk_sector_t offset
;
998 assert(!FDISK_IS_UNDEF(start
));
999 assert(!FDISK_IS_UNDEF(stop
));
1003 struct fdisk_dos_label
*l
= self_label(cxt
);
1005 offset
= l
->ext_offset
;
1008 offset
= pe
->offset
;
1011 DBG(LABEL
, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, size=%zu, sysid=%02x",
1012 i
, doext
? " [extended]" : "",
1014 (size_t) (start
- offset
),
1015 (size_t) (stop
- start
+ 1),
1018 p
->boot_ind
= boot
? ACTIVE_FLAG
: 0;
1020 dos_partition_set_start(p
, start
- offset
);
1021 dos_partition_set_size(p
, stop
- start
+ 1);
1022 dos_partition_sync_chs(p
, offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
1023 partition_set_changed(cxt
, i
, 1);
1027 static int get_start_from_user( struct fdisk_context
*cxt
,
1028 fdisk_sector_t
*start
,
1030 fdisk_sector_t dflt
,
1031 fdisk_sector_t limit
,
1032 struct fdisk_partition
*pa
)
1036 /* try to use template from 'pa' */
1037 if (pa
&& pa
->start_follow_default
)
1040 else if (pa
&& fdisk_partition_has_start(pa
)) {
1041 DBG(LABEL
, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
1042 (uintmax_t) pa
->start
, (uintmax_t) low
, (uintmax_t) limit
));
1044 if (*start
< low
|| *start
> limit
) {
1045 fdisk_warnx(cxt
, _("Start sector %ju out of range."),
1046 (uintmax_t) *start
);
1050 /* ask user by dialog */
1051 struct fdisk_ask
*ask
= fdisk_new_ask();
1056 fdisk_ask_set_query(ask
,
1057 fdisk_use_cylinders(cxt
) ?
1058 _("First cylinder") : _("First sector"));
1059 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_NUMBER
);
1060 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, low
));
1061 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, dflt
));
1062 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
1064 rc
= fdisk_do_ask(cxt
, ask
);
1065 *start
= fdisk_ask_number_get_result(ask
);
1066 fdisk_unref_ask(ask
);
1069 if (fdisk_use_cylinders(cxt
)) {
1070 *start
= (*start
- 1)
1071 * fdisk_get_units_per_sector(cxt
);
1077 DBG(LABEL
, ul_debug("DOS: start is %ju", (uintmax_t) *start
));
1081 /* Returns last available sector in the free space pointed to by start. */
1082 static int find_last_free(
1083 struct fdisk_context
*cxt
,
1085 fdisk_sector_t begin
,
1086 fdisk_sector_t stop
,
1087 fdisk_sector_t
*result
)
1089 fdisk_sector_t last
= stop
;
1091 size_t i
= logical
? 4 : 0;
1093 for ( ; i
< cxt
->label
->nparts_max
; i
++) {
1094 struct pte
*pe
= self_pte(cxt
, i
);
1097 fdisk_sector_t p_start
= get_abs_partition_start(pe
);
1098 fdisk_sector_t p_end
= get_abs_partition_end(pe
);
1100 if (is_cleared_partition(pe
->pt_entry
))
1102 /* count EBR and begin of the logical partition as used area */
1104 p_start
-= cxt
->first_lba
;
1106 if ((p_start
>= begin
&& p_start
<= last
) ||
1107 (p_end
>= begin
&& p_end
<= last
)) {
1111 DBG(LABEL
, ul_debug("no free space <%ju,%ju>",
1112 (uintmax_t) begin
, (uintmax_t) stop
));
1120 DBG(LABEL
, ul_debug("DOS: last free sector <%ju,%ju>: %ju",
1121 (uintmax_t) begin
, (uintmax_t) stop
, (uintmax_t) last
));
1127 static int find_last_free_sector_in_range(
1128 struct fdisk_context
*cxt
,
1130 fdisk_sector_t begin
,
1132 fdisk_sector_t
*result
)
1135 fdisk_sector_t last
= end
;
1138 size_t i
= logical
? 4 : 0;
1141 for ( ; i
< cxt
->label
->nparts_max
; i
++) {
1142 struct pte
*pe
= self_pte(cxt
, i
);
1145 fdisk_sector_t p_start
= get_abs_partition_start(pe
);
1146 fdisk_sector_t p_end
= get_abs_partition_end(pe
);
1148 if (is_cleared_partition(pe
->pt_entry
))
1151 /* count EBR and begin of the logical partition as used area */
1153 p_start
-= cxt
->first_lba
;
1155 if (last
>= p_start
&& last
<= p_end
) {
1160 DBG(LABEL
, ul_debug("DOS: last free out of range <%ju,%ju>: %ju",
1161 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) last
));
1167 } while (last_moved
== 1);
1169 DBG(LABEL
, ul_debug("DOS: last unused sector in range <%ju,%ju>: %ju",
1170 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) last
));
1176 static int find_first_free_sector_in_range(
1177 struct fdisk_context
*cxt
,
1179 fdisk_sector_t begin
,
1181 fdisk_sector_t
*result
)
1183 int first_moved
= 0;
1184 fdisk_sector_t first
= begin
;
1187 size_t i
= logical
? 4 : 0;
1190 for (; i
< cxt
->label
->nparts_max
; i
++) {
1191 struct pte
*pe
= self_pte(cxt
, i
);
1194 fdisk_sector_t p_start
= get_abs_partition_start(pe
);
1195 fdisk_sector_t p_end
= get_abs_partition_end(pe
);
1197 if (is_cleared_partition(pe
->pt_entry
))
1199 /* count EBR and begin of the logical partition as used area */
1201 p_start
-= cxt
->first_lba
;
1202 if (first
< p_start
)
1204 if (first
<= p_end
) {
1205 first
= p_end
+ 1 + (logical
? cxt
->first_lba
: 0);
1209 DBG(LABEL
, ul_debug("DOS: first free out of range <%ju,%ju>: %ju",
1210 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) first
));
1215 } while (first_moved
== 1);
1217 DBG(LABEL
, ul_debug("DOS: first unused sector in range <%ju,%ju>: %ju",
1218 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) first
));
1223 static int get_disk_ranges(struct fdisk_context
*cxt
, int logical
,
1224 fdisk_sector_t
*first
, fdisk_sector_t
*last
)
1227 /* logical partitions */
1228 struct fdisk_dos_label
*l
= self_label(cxt
);
1229 struct pte
*ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1234 *first
= l
->ext_offset
+ cxt
->first_lba
;
1235 *last
= get_abs_partition_end(ext_pe
);
1238 /* primary partitions */
1239 if (fdisk_use_cylinders(cxt
) || !cxt
->total_sectors
)
1240 *last
= cxt
->geom
.heads
* cxt
->geom
.sectors
* cxt
->geom
.cylinders
- 1;
1242 *last
= cxt
->total_sectors
- 1;
1244 if (*last
> UINT_MAX
)
1246 *first
= cxt
->first_lba
;
1252 /* first free sector on disk */
1253 static int find_first_free_sector(struct fdisk_context
*cxt
,
1255 fdisk_sector_t start
,
1256 fdisk_sector_t
*result
)
1258 fdisk_sector_t first
, last
;
1261 rc
= get_disk_ranges(cxt
, logical
, &first
, &last
);
1265 return find_first_free_sector_in_range(cxt
, logical
, start
, last
, result
);
1268 static int add_partition(struct fdisk_context
*cxt
, size_t n
,
1269 struct fdisk_partition
*pa
)
1271 int sys
, read
= 0, rc
, isrel
= 0, is_logical
;
1272 struct fdisk_dos_label
*l
= self_label(cxt
);
1273 struct dos_partition
*p
= self_partition(cxt
, n
);
1274 struct fdisk_ask
*ask
= NULL
;
1276 fdisk_sector_t start
, stop
, limit
, temp
;
1278 DBG(LABEL
, ul_debug("DOS: adding partition %zu", n
));
1280 sys
= pa
&& pa
->type
? pa
->type
->code
: MBR_LINUX_DATA_PARTITION
;
1281 is_logical
= n
>= 4;
1283 if (p
&& is_used_partition(p
)) {
1284 fdisk_warnx(cxt
, _("Partition %zu is already defined. "
1285 "Delete it before re-adding it."),
1290 rc
= get_disk_ranges(cxt
, is_logical
, &start
, &stop
);
1294 if (!is_logical
&& cxt
->parent
&& fdisk_is_label(cxt
->parent
, GPT
))
1295 start
= 1; /* Bad boy modifies hybrid MBR */
1297 rc
= find_last_free_sector_in_range(cxt
, is_logical
, start
, stop
, &limit
);
1299 fdisk_warnx(cxt
, _("No free sectors available."));
1303 if ((is_logical
|| !cxt
->parent
|| !fdisk_is_label(cxt
->parent
, GPT
))
1304 && cxt
->script
&& pa
&& fdisk_partition_has_start(pa
)
1305 && pa
->start
>= (is_logical
? l
->ext_offset
: 1)
1306 && pa
->start
< start
) {
1307 fdisk_set_first_lba(cxt
, 1);
1309 rc
= get_disk_ranges(cxt
, is_logical
, &start
, &stop
);
1310 if (rc
) /* won't happen, but checking to be proper */
1315 * Ask for first sector
1318 fdisk_sector_t dflt
, aligned
;
1322 DBG(LABEL
, ul_debug("DOS: >>> search for first free from %ju", start
));
1323 rc
= find_first_free_sector(cxt
, is_logical
, start
, &dflt
);
1325 fdisk_warnx(cxt
, _("No free sectors available."));
1330 if (n
>= 4 && pa
&& fdisk_partition_has_start(pa
) && cxt
->script
1331 && cxt
->first_lba
> 1
1332 && temp
== start
- cxt
->first_lba
) {
1333 fdisk_set_first_lba(cxt
, 1);
1337 /* the default sector should be aligned and unused */
1339 aligned
= fdisk_align_lba_in_range(cxt
, dflt
, dflt
, limit
);
1340 find_first_free_sector(cxt
, is_logical
, aligned
, &dflt
);
1341 } while (dflt
!= aligned
&& dflt
> aligned
&& dflt
< limit
);
1347 if (start
>= temp
+ fdisk_get_units_per_sector(cxt
)
1349 if (!pa
|| !pa
->start_follow_default
)
1350 fdisk_info(cxt
, _("Sector %ju is already allocated."),
1354 if (pa
&& fdisk_partition_has_start(pa
))
1358 if (!read
&& start
== temp
) {
1359 rc
= get_start_from_user(cxt
, &start
, temp
, dflt
, limit
, pa
);
1364 if (pa
&& fdisk_partition_has_size(pa
)) {
1365 fdisk_sector_t last
;
1367 rc
= find_last_free(cxt
, is_logical
, start
, limit
, &last
);
1368 if (rc
== 0 && last
- start
+ 1 < fdisk_partition_get_size(pa
)) {
1369 DBG(LABEL
, ul_debug("DOS: area <%ju,%ju> too small [wanted=%ju aval=%ju]",
1370 (uintmax_t) start
, (uintmax_t) last
,
1371 fdisk_partition_get_size(pa
),
1374 if (fdisk_partition_has_start(pa
)
1375 && fdisk_partition_get_start(pa
) <= last
)
1382 if (rc
== -ENOSPC
) {
1383 fdisk_warnx(cxt
, _("No free sectors available."));
1388 } while (start
!= temp
|| !read
);
1391 /* The first EBR is stored at begin of the extended partition */
1392 struct pte
*pe
= self_pte(cxt
, n
);
1395 pe
->offset
= l
->ext_offset
;
1397 /* The second (and another) EBR */
1398 struct pte
*pe
= self_pte(cxt
, n
);
1401 assert(start
>= cxt
->first_lba
);
1403 pe
->offset
= start
- cxt
->first_lba
;
1404 DBG(LABEL
, ul_debug("DOS: setting EBR offset to %ju [start=%ju]", pe
->offset
, start
));
1406 if (pe
->offset
== l
->ext_offset
) { /* must be corrected */
1408 if (cxt
->first_lba
== 1)
1413 rc
= find_last_free(cxt
, is_logical
, start
, limit
, &stop
);
1415 fdisk_warnx(cxt
, _("No free sectors available."));
1421 * Ask for last sector
1423 if (fdisk_cround(cxt
, start
) == fdisk_cround(cxt
, limit
))
1425 else if (pa
&& pa
->end_follow_default
)
1427 else if (pa
&& fdisk_partition_has_size(pa
)) {
1428 stop
= start
+ pa
->size
;
1429 isrel
= pa
->size_explicit
? 0 : 1;
1430 if ((!isrel
|| !alignment_required(cxt
)) && stop
> start
)
1433 /* ask user by dialog */
1436 ask
= fdisk_new_ask();
1438 fdisk_reset_ask(ask
);
1441 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_OFFSET
);
1443 if (fdisk_use_cylinders(cxt
)) {
1444 fdisk_ask_set_query(ask
, _("Last cylinder, +/-cylinders or +/-size{K,M,G,T,P}"));
1445 fdisk_ask_number_set_unit(ask
,
1447 fdisk_get_units_per_sector(cxt
));
1449 fdisk_ask_set_query(ask
, _("Last sector, +/-sectors or +/-size{K,M,G,T,P}"));
1450 fdisk_ask_number_set_unit(ask
,cxt
->sector_size
);
1453 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, start
));
1454 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, limit
));
1455 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
1456 fdisk_ask_number_set_base(ask
, fdisk_cround(cxt
, start
)); /* base for relative input */
1457 fdisk_ask_number_set_wrap_negative(ask
, 1); /* wrap negative around high */
1459 rc
= fdisk_do_ask(cxt
, ask
);
1463 stop
= fdisk_ask_number_get_result(ask
);
1464 isrel
= fdisk_ask_number_is_relative(ask
);
1465 if (fdisk_use_cylinders(cxt
)) {
1466 stop
= stop
* fdisk_get_units_per_sector(cxt
) - 1;
1471 if (stop
>= start
&& stop
<= limit
)
1473 fdisk_warnx(cxt
, _("Value out of range."));
1477 DBG(LABEL
, ul_debug("DOS: raw stop: %ju [limit %ju]", (uintmax_t) stop
, (uintmax_t) limit
));
1482 if (isrel
&& stop
- start
< (cxt
->grain
/ fdisk_get_sector_size(cxt
))) {
1483 /* Don't try to be smart on very small partitions and don't align so small sizes */
1485 DBG(LABEL
, ul_debug("DOS: don't align end of tiny partition [start=%ju, stop=%ju, grain=%lu]",
1486 (uintmax_t)start
, (uintmax_t)stop
, cxt
->grain
));
1489 if (stop
< limit
&& isrel
&& alignment_required(cxt
)) {
1490 /* the last sector has not been exactly requested (but
1491 * defined by +size{K,M,G} convention), so be smart and
1492 * align the end of the partition. The next partition
1493 * will start at phy.block boundary.
1495 stop
= fdisk_align_lba_in_range(cxt
, stop
, start
, limit
);
1497 stop
-= 1; /* end one sector before aligned offset */
1500 DBG(LABEL
, ul_debug("DOS: aligned stop: %ju", (uintmax_t) stop
));
1503 set_partition(cxt
, n
, 0, start
, stop
, sys
, fdisk_partition_is_bootable(pa
));
1505 struct pte
*pe
= self_pte(cxt
, n
);
1507 set_partition(cxt
, n
- 1, 1, pe
->offset
, stop
,
1508 MBR_DOS_EXTENDED_PARTITION
, 0);
1513 struct fdisk_parttype
*t
=
1514 fdisk_label_get_parttype_from_code(cxt
->label
, sys
);
1515 fdisk_info_new_partition(cxt
, n
+ 1, start
, stop
, t
);
1516 fdisk_unref_parttype(t
);
1520 if (IS_EXTENDED(sys
)) {
1521 struct pte
*pen
= self_pte(cxt
, n
);
1525 l
->ext_offset
= start
;
1529 fdisk_label_set_changed(cxt
->label
, 1);
1532 fdisk_unref_ask(ask
);
1536 static int add_logical(struct fdisk_context
*cxt
,
1537 struct fdisk_partition
*pa
,
1546 assert(self_label(cxt
)->ext_offset
);
1548 DBG(LABEL
, ul_debug("DOS: nparts max: %zu", cxt
->label
->nparts_max
));
1549 pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
1552 if (!pe
->sectorbuffer
) {
1553 pe
->sectorbuffer
= calloc(1, cxt
->sector_size
);
1554 if (!pe
->sectorbuffer
)
1556 DBG(LABEL
, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1557 cxt
->label
->nparts_max
, pe
->sectorbuffer
));
1558 pe
->private_sectorbuffer
= 1;
1560 pe
->pt_entry
= mbr_get_partition(pe
->sectorbuffer
, 0);
1561 pe
->ex_entry
= pe
->pt_entry
+ 1;
1563 partition_set_changed(cxt
, cxt
->label
->nparts_max
, 1);
1565 cxt
->label
->nparts_max
++;
1567 /* this message makes sense only when we use extended/primary/logical
1568 * dialog. The dialog is disable for scripts, see dos_add_partition() */
1570 fdisk_info(cxt
, _("Adding logical partition %zu"),
1571 cxt
->label
->nparts_max
);
1572 *partno
= cxt
->label
->nparts_max
- 1;
1573 rc
= add_partition(cxt
, *partno
, pa
);
1576 /* reset on error */
1577 cxt
->label
->nparts_max
--;
1578 pe
->pt_entry
= NULL
;
1579 pe
->ex_entry
= NULL
;
1587 static int check(struct fdisk_context
*cxt
, size_t n
,
1588 unsigned int h
, unsigned int s
, unsigned int c
,
1589 unsigned int lba_sector
)
1591 unsigned int chs_sector
, real_s
, real_c
;
1594 if (!is_dos_compatible(cxt
))
1597 real_s
= sector(s
) - 1;
1598 real_c
= cylinder(s
, c
);
1599 chs_sector
= (real_c
* cxt
->geom
.heads
+ h
) * cxt
->geom
.sectors
+ real_s
;
1602 fdisk_warnx(cxt
, _("Partition %zu: contains sector 0"), n
);
1605 if (h
>= cxt
->geom
.heads
) {
1606 fdisk_warnx(cxt
, _("Partition %zu: head %d greater than "
1607 "maximum %d"), n
, h
+ 1, cxt
->geom
.heads
);
1610 if (real_s
>= cxt
->geom
.sectors
) {
1611 fdisk_warnx(cxt
, _("Partition %zu: sector %d greater than "
1612 "maximum %ju"), n
, real_s
+ 1,
1613 (uintmax_t) cxt
->geom
.sectors
);
1616 if (real_c
>= cxt
->geom
.cylinders
) {
1617 fdisk_warnx(cxt
, _("Partition %zu: cylinder %d greater than "
1620 (uintmax_t) cxt
->geom
.cylinders
);
1623 if (lba_sector
/ (cxt
->geom
.heads
* cxt
->geom
.sectors
) < 1024 && lba_sector
!= chs_sector
) {
1624 fdisk_warnx(cxt
, _("Partition %zu: LBA sector %u "
1625 "disagrees with C/H/S calculated sector %u"),
1626 n
, lba_sector
, chs_sector
);
1633 /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1634 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1635 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1636 * Lubkin Oct. 1991). */
1639 long2chs(struct fdisk_context
*cxt
, unsigned long ls
,
1640 unsigned int *c
, unsigned int *h
, unsigned int *s
) {
1641 int spc
= cxt
->geom
.heads
* cxt
->geom
.sectors
;
1645 *h
= ls
/ cxt
->geom
.sectors
;
1646 *s
= ls
% cxt
->geom
.sectors
+ 1; /* sectors count from 1 */
1649 static int check_consistency(struct fdisk_context
*cxt
, struct dos_partition
*p
,
1652 unsigned int pbc
, pbh
, pbs
; /* physical beginning c, h, s */
1653 unsigned int pec
, peh
, pes
; /* physical ending c, h, s */
1654 unsigned int lbc
, lbh
, lbs
; /* logical beginning c, h, s */
1655 unsigned int lec
, leh
, les
; /* logical ending c, h, s */
1658 if (!is_dos_compatible(cxt
))
1661 if (!cxt
->geom
.heads
|| !cxt
->geom
.sectors
|| (partition
>= 4))
1662 return 0; /* do not check extended partitions */
1664 /* physical beginning c, h, s */
1665 pbc
= cylinder(p
->bs
, p
->bc
);
1667 pbs
= sector(p
->bs
);
1669 /* physical ending c, h, s */
1670 pec
= cylinder(p
->es
, p
->ec
);
1672 pes
= sector(p
->es
);
1674 /* compute logical beginning (c, h, s) */
1675 long2chs(cxt
, dos_partition_get_start(p
), &lbc
, &lbh
, &lbs
);
1677 /* compute logical ending (c, h, s) */
1678 long2chs(cxt
, dos_partition_get_start(p
) + dos_partition_get_size(p
) - 1, &lec
, &leh
, &les
);
1680 /* Same physical / logical beginning? */
1682 && (pbc
!= lbc
|| pbh
!= lbh
|| pbs
!= lbs
)) {
1683 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1684 "beginnings (non-Linux?): "
1685 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1692 /* Same physical / logical ending? */
1694 && (pec
!= lec
|| peh
!= leh
|| pes
!= les
)) {
1695 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1696 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1703 /* Ending on cylinder boundary? */
1704 if (peh
!= (cxt
->geom
.heads
- 1) || pes
!= cxt
->geom
.sectors
) {
1705 fdisk_warnx(cxt
, _("Partition %zu: does not end on "
1706 "cylinder boundary."),
1714 static void fill_bounds(struct fdisk_context
*cxt
,
1715 fdisk_sector_t
*first
, fdisk_sector_t
*last
)
1718 struct pte
*pe
= self_pte(cxt
, 0);
1719 struct dos_partition
*p
;
1722 for (i
= 0; i
< cxt
->label
->nparts_max
; pe
++,i
++) {
1724 if (is_cleared_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
1725 first
[i
] = SIZE_MAX
;
1728 first
[i
] = get_abs_partition_start(pe
);
1729 last
[i
] = get_abs_partition_end(pe
);
1734 static int dos_verify_disklabel(struct fdisk_context
*cxt
)
1737 fdisk_sector_t total
= 1, n_sectors
= cxt
->total_sectors
;
1738 fdisk_sector_t
*first
, *last
;
1739 struct dos_partition
*p
;
1740 struct fdisk_dos_label
*l
= self_label(cxt
);
1743 assert(fdisk_is_label(cxt
, DOS
));
1745 first
= calloc(cxt
->label
->nparts_max
, sizeof(*first
));
1746 last
= calloc(cxt
->label
->nparts_max
, sizeof(*first
));
1748 if (!first
|| !last
) {
1754 fill_bounds(cxt
, first
, last
);
1755 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
1756 struct pte
*pe
= self_pte(cxt
, i
);
1758 p
= self_partition(cxt
, i
);
1759 if (p
&& is_used_partition(p
) && !IS_EXTENDED(p
->sys_ind
)) {
1760 nerrors
+= check_consistency(cxt
, p
, i
);
1762 if (get_abs_partition_start(pe
) < first
[i
]) {
1764 "Partition %zu: bad start-of-data."),
1769 nerrors
+= check(cxt
, i
+ 1, p
->bh
, p
->bs
, p
->bc
, first
[i
]);
1770 nerrors
+= check(cxt
, i
+ 1, p
->eh
, p
->es
, p
->ec
, last
[i
]);
1771 total
+= last
[i
] + 1 - first
[i
];
1774 total
+= get_abs_partition_start(pe
) - 1;
1776 for (j
= 0; j
< i
; j
++) {
1777 if ((first
[i
] >= first
[j
] && first
[i
] <= last
[j
])
1778 || ((last
[i
] <= last
[j
] && last
[i
] >= first
[j
]))) {
1780 fdisk_warnx(cxt
, _("Partition %zu: "
1781 "overlaps partition %zu."),
1785 total
+= first
[i
] >= first
[j
] ?
1786 first
[i
] : first
[j
];
1787 total
-= last
[i
] <= last
[j
] ?
1794 if (l
->ext_offset
) {
1795 fdisk_sector_t e_last
;
1796 struct pte
*ext_pe
= self_pte(cxt
, l
->ext_index
);
1799 e_last
= get_abs_partition_end(ext_pe
);
1801 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1803 p
= self_partition(cxt
, i
);
1807 if (i
!= 4 || i
+ 1 < cxt
->label
->nparts_max
) {
1809 _("Partition %zu: empty."),
1813 } else if (first
[i
] < l
->ext_offset
1814 || last
[i
] > e_last
) {
1816 fdisk_warnx(cxt
, _("Logical partition %zu: "
1817 "not entirely in partition %zu."),
1818 i
+ 1, l
->ext_index
+ 1);
1825 fdisk_info(cxt
, _("No errors detected."));
1826 if (total
> n_sectors
)
1827 fdisk_info(cxt
, _("Total allocated sectors %ju greater "
1828 "than the maximum %ju."), (uintmax_t) total
, (uintmax_t) n_sectors
);
1829 else if (total
< n_sectors
)
1830 fdisk_info(cxt
, _("Remaining %ju unallocated %ld-byte "
1831 "sectors."), (uintmax_t) n_sectors
- total
, cxt
->sector_size
);
1834 P_("%d error detected.", "%d errors detected.", nerrors
),
1843 * Ask the user for new partition type information (logical, extended).
1844 * This function calls the actual partition adding logic - add_partition.
1848 static int dos_add_partition(struct fdisk_context
*cxt
,
1849 struct fdisk_partition
*pa
,
1853 uint8_t free_primary
= 0, free_sectors
= 0;
1854 fdisk_sector_t first
= 0, grain
;
1856 struct fdisk_dos_label
*l
;
1858 size_t res
= 0; /* partno */
1862 assert(fdisk_is_label(cxt
, DOS
));
1864 DBG(LABEL
, ul_debug("DOS: new partition wanted"));
1866 l
= self_label(cxt
);
1868 if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1869 fdisk_warnx(cxt
, _("The maximum number of partitions has "
1874 ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1877 * partition template (@pa) based partitioning
1880 /* A) template specifies start within extended partition; add logical */
1881 if (pa
&& fdisk_partition_has_start(pa
) && ext_pe
1882 && pa
->start
>= l
->ext_offset
1883 && pa
->start
<= get_abs_partition_end(ext_pe
)) {
1884 DBG(LABEL
, ul_debug("DOS: pa template %p: add logical (by offset)", pa
));
1886 if (fdisk_partition_has_partno(pa
) && fdisk_partition_get_partno(pa
) < 4) {
1887 DBG(LABEL
, ul_debug("DOS: pa template specifies partno<4 for logical partition"));
1890 rc
= add_logical(cxt
, pa
, &res
);
1893 /* B) template specifies start out of extended partition; add primary */
1894 } else if (pa
&& fdisk_partition_has_start(pa
) && ext_pe
) {
1895 DBG(LABEL
, ul_debug("DOS: pa template %p: add primary (by offset)", pa
));
1897 if (fdisk_partition_has_partno(pa
) && fdisk_partition_get_partno(pa
) >= 4) {
1898 DBG(LABEL
, ul_debug("DOS: pa template specifies partno>=4 for primary partition"));
1901 if (ext_pe
&& pa
->type
&& IS_EXTENDED(pa
->type
->code
)) {
1902 fdisk_warnx(cxt
, _("Extended partition already exists."));
1905 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1907 rc
= add_partition(cxt
, res
, pa
);
1910 /* C) template specifies start (or default), partno < 4; add primary */
1911 } else if (pa
&& (fdisk_partition_start_is_default(pa
) || fdisk_partition_has_start(pa
))
1912 && fdisk_partition_has_partno(pa
)
1913 && pa
->partno
< 4) {
1914 DBG(LABEL
, ul_debug("DOS: pa template %p: add primary (by partno)", pa
));
1916 if (ext_pe
&& pa
->type
&& IS_EXTENDED(pa
->type
->code
)) {
1917 fdisk_warnx(cxt
, _("Extended partition already exists."));
1920 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1922 rc
= add_partition(cxt
, res
, pa
);
1925 /* D) template specifies start (or default), partno >= 4; add logical */
1926 } else if (pa
&& (fdisk_partition_start_is_default(pa
) || fdisk_partition_has_start(pa
))
1927 && fdisk_partition_has_partno(pa
)
1928 && pa
->partno
>= 4) {
1929 DBG(LABEL
, ul_debug("DOS: pa template %p: add logical (by partno)", pa
));
1932 fdisk_warnx(cxt
, _("Extended partition does not exists. Failed to add logical partition."));
1936 if (fdisk_partition_has_start(pa
)
1937 && pa
->start
< l
->ext_offset
1938 && pa
->start
> get_abs_partition_end(ext_pe
)) {
1939 DBG(LABEL
, ul_debug("DOS: pa template specifies partno>=4, but start out of extended"));
1943 rc
= add_logical(cxt
, pa
, &res
);
1947 DBG(LABEL
, ul_debug("DOS: dialog driven partitioning"));
1948 /* Note @pa may be still used for things like partition type, etc */
1950 /* check if there is space for primary partition */
1951 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
1952 first
= cxt
->first_lba
;
1954 if (cxt
->parent
&& fdisk_is_label(cxt
->parent
, GPT
)) {
1955 /* modifying a hybrid MBR, which throws out the rules */
1960 /* set @first after the last used partition, set @free_sectors if there
1961 * is gap in front if the first partition or between used parrtitions. */
1962 for (i
= 0; i
< 4; i
++) {
1963 struct dos_partition
*p
= self_partition(cxt
, i
);
1965 if (p
&& is_used_partition(p
)) {
1966 fdisk_sector_t start
= dos_partition_get_start(p
);
1967 if (first
+ grain
<= start
)
1969 first
= start
+ dos_partition_get_size(p
);
1974 /* set @free_sectors if there is a space after the first usable sector */
1975 if (first
+ grain
- 1 <= cxt
->total_sectors
- 1)
1978 DBG(LABEL
, ul_debug("DOS: primary: first free: %ju, last on disk: %ju, "
1979 "free_sectors=%d, free_primary=%d",
1981 (uintmax_t) cxt
->total_sectors
- 1,
1982 free_sectors
, free_primary
));
1984 if (!free_primary
|| !free_sectors
) {
1985 DBG(LABEL
, ul_debug("DOS: primary impossible"));
1986 if (l
->ext_offset
) {
1987 if (!pa
|| fdisk_partition_has_start(pa
)) {
1988 /* See above case A); here we have start, but
1989 * out of extended partition */
1992 msg
= _("All primary partitions are in use.");
1994 msg
= _("All space for primary partitions is in use.");
1996 if (pa
&& fdisk_partition_has_start(pa
)) {
1997 fdisk_warnx(cxt
, "%s", msg
);
2000 fdisk_info(cxt
, "%s", msg
);
2002 DBG(LABEL
, ul_debug("DOS: trying logical"));
2003 rc
= add_logical(cxt
, pa
, &res
);
2006 fdisk_info(cxt
, _("All space for primary partitions is in use."));
2008 /* TRANSLATORS: Try to keep this within 80 characters. */
2009 fdisk_info(cxt
, _("To create more partitions, first replace "
2010 "a primary with an extended partition."));
2015 struct fdisk_ask
*ask
;
2018 /* the default layout for scripts is to create primary partitions */
2019 if (cxt
->script
|| !fdisk_has_dialogs(cxt
)) {
2020 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
2022 rc
= add_partition(cxt
, res
, pa
);
2026 ask
= fdisk_new_ask();
2029 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_MENU
);
2030 fdisk_ask_set_query(ask
, _("Partition type"));
2031 fdisk_ask_menu_set_default(ask
, free_primary
== 1
2032 && !l
->ext_offset
? 'e' : 'p');
2033 snprintf(hint
, sizeof(hint
),
2034 _("%u primary, %d extended, %u free"),
2035 4 - (l
->ext_offset
? 1 : 0) - free_primary
,
2036 l
->ext_offset
? 1 : 0,
2039 fdisk_ask_menu_add_item(ask
, 'p', _("primary"), hint
);
2041 fdisk_ask_menu_add_item(ask
, 'e', _("extended"), _("container for logical partitions"));
2043 fdisk_ask_menu_add_item(ask
, 'l', _("logical"), _("numbered from 5"));
2045 rc
= fdisk_do_ask(cxt
, ask
);
2047 fdisk_ask_menu_get_result(ask
, &c
);
2048 fdisk_unref_ask(ask
);
2053 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
2055 rc
= add_partition(cxt
, res
, pa
);
2057 } else if (c
== 'l' && l
->ext_offset
) {
2058 rc
= add_logical(cxt
, pa
, &res
);
2060 } else if (c
== 'e' && !l
->ext_offset
) {
2061 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
2063 struct fdisk_partition
*xpa
= NULL
;
2064 struct fdisk_parttype
*t
;
2066 t
= fdisk_label_get_parttype_from_code(cxt
->label
,
2067 MBR_DOS_EXTENDED_PARTITION
);
2069 pa
= xpa
= fdisk_new_partition();
2073 fdisk_partition_set_type(pa
, t
);
2074 rc
= add_partition(cxt
, res
, pa
);
2076 fdisk_unref_partition(xpa
);
2082 fdisk_warnx(cxt
, _("Invalid partition type `%c'."), c
);
2086 cxt
->label
->nparts_cur
++;
2093 static int write_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
,
2098 rc
= seek_sector(cxt
, secno
);
2100 fdisk_warn(cxt
, _("Cannot write sector %jd: seek failed"),
2105 DBG(LABEL
, ul_debug("DOS: writing to sector %ju", (uintmax_t) secno
));
2107 if (write(cxt
->dev_fd
, buf
, cxt
->sector_size
) != (ssize_t
) cxt
->sector_size
)
2112 static int dos_write_disklabel(struct fdisk_context
*cxt
)
2114 struct fdisk_dos_label
*l
= self_label(cxt
);
2116 int rc
= 0, mbr_changed
= 0;
2120 assert(fdisk_is_label(cxt
, DOS
));
2122 DBG(LABEL
, ul_debug("DOS: write PT requested [label-changed: %d, non-pt-changed: %d]",
2123 cxt
->label
->changed
, l
->non_pt_changed
));
2125 mbr_changed
= l
->non_pt_changed
;
2127 /* MBR (primary partitions) */
2129 for (i
= 0; i
< 4; i
++) {
2130 struct pte
*pe
= self_pte(cxt
, i
);
2138 DBG(LABEL
, ul_debug("DOS: MBR changed, writing"));
2139 mbr_set_magic(cxt
->firstsector
);
2140 rc
= write_sector(cxt
, 0, cxt
->firstsector
);
2145 if (cxt
->label
->nparts_max
<= 4 && l
->ext_offset
) {
2146 /* we have empty extended partition, check if the partition has
2147 * been modified and then cleanup possible remaining EBR */
2148 struct pte
*pe
= self_pte(cxt
, l
->ext_index
);
2149 unsigned char empty
[512] = { 0 };
2150 fdisk_sector_t off
= pe
? get_abs_partition_start(pe
) : 0;
2152 if (off
&& pe
->changed
) {
2153 mbr_set_magic(empty
);
2154 write_sector(cxt
, off
, empty
);
2158 /* EBR (logical partitions) */
2159 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2160 struct pte
*pe
= self_pte(cxt
, i
);
2163 if (!pe
->changed
|| !pe
->offset
|| !pe
->sectorbuffer
)
2166 mbr_set_magic(pe
->sectorbuffer
);
2167 rc
= write_sector(cxt
, pe
->offset
, pe
->sectorbuffer
);
2176 static int dos_locate_disklabel(struct fdisk_context
*cxt
, int n
,
2177 const char **name
, uint64_t *offset
, size_t *size
)
2192 /* extended partitions */
2193 if ((size_t)n
- 1 + 4 < cxt
->label
->nparts_max
) {
2194 struct pte
*pe
= self_pte(cxt
, n
- 1 + 4);
2197 assert(pe
->private_sectorbuffer
);
2200 *offset
= (uint64_t) pe
->offset
* cxt
->sector_size
;
2211 * Check whether partition entries are ordered by their starting positions.
2212 * Return 0 if OK. Return i if partition i should have been earlier.
2213 * Two separate checks: primary and logical partitions.
2215 static int wrong_p_order(struct fdisk_context
*cxt
, size_t *prev
)
2217 size_t last_p_start_pos
= 0, p_start_pos
;
2218 size_t i
, last_i
= 0;
2220 for (i
= 0 ; i
< cxt
->label
->nparts_max
; i
++) {
2222 struct pte
*pe
= self_pte(cxt
, i
);
2223 struct dos_partition
*p
;
2230 last_p_start_pos
= 0;
2232 if (is_used_partition(p
)) {
2233 p_start_pos
= get_abs_partition_start(pe
);
2235 if (last_p_start_pos
> p_start_pos
) {
2241 last_p_start_pos
= p_start_pos
;
2248 static int dos_get_disklabel_item(struct fdisk_context
*cxt
, struct fdisk_labelitem
*item
)
2254 assert(fdisk_is_label(cxt
, DOS
));
2257 case FDISK_LABELITEM_ID
:
2259 unsigned int num
= mbr_get_id(cxt
->firstsector
);
2260 item
->name
= _("Disk identifier");
2262 if (asprintf(&item
->data
.str
, "0x%08x", num
) < 0)
2267 if (item
->id
< __FDISK_NLABELITEMS
)
2268 rc
= 1; /* unsupported generic item */
2270 rc
= 2; /* out of range */
2278 static int dos_get_partition(struct fdisk_context
*cxt
, size_t n
,
2279 struct fdisk_partition
*pa
)
2281 struct dos_partition
*p
;
2283 struct fdisk_dos_label
*lb
;
2288 assert(fdisk_is_label(cxt
, DOS
));
2290 lb
= self_label(cxt
);
2292 pe
= self_pte(cxt
, n
);
2296 pa
->used
= !is_cleared_partition(p
);
2300 pa
->type
= dos_partition_parttype(cxt
, p
);
2301 pa
->boot
= p
->boot_ind
== ACTIVE_FLAG
? 1 : 0;
2302 pa
->start
= get_abs_partition_start(pe
);
2303 pa
->size
= dos_partition_get_size(p
);
2304 pa
->container
= lb
->ext_offset
&& n
== lb
->ext_index
;
2307 pa
->parent_partno
= lb
->ext_index
;
2309 if (p
->boot_ind
&& asprintf(&pa
->attrs
, "%02x", p
->boot_ind
) < 0)
2313 if (asprintf(&pa
->start_chs
, "%d/%d/%d",
2314 cylinder(p
->bs
, p
->bc
),
2320 if (asprintf(&pa
->end_chs
, "%d/%d/%d",
2321 cylinder(p
->es
, p
->ec
),
2329 static int has_logical(struct fdisk_context
*cxt
)
2332 struct fdisk_dos_label
*l
= self_label(cxt
);
2334 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2335 if (l
->ptes
[i
].pt_entry
)
2341 static int dos_set_partition(struct fdisk_context
*cxt
, size_t n
,
2342 struct fdisk_partition
*pa
)
2344 struct fdisk_dos_label
*l
;
2345 struct dos_partition
*p
;
2348 fdisk_sector_t start
, size
;
2353 assert(fdisk_is_label(cxt
, DOS
));
2355 if (n
>= cxt
->label
->nparts_max
)
2358 l
= self_label(cxt
);
2359 p
= self_partition(cxt
, n
);
2362 pe
= self_pte(cxt
, n
);
2366 orgtype
= p
->sys_ind
;
2369 if (IS_EXTENDED(pa
->type
->code
) && l
->ext_offset
&& l
->ext_index
!= n
) {
2370 fdisk_warnx(cxt
, _("Extended partition already exists."));
2374 if (!pa
->type
->code
)
2375 fdisk_warnx(cxt
, _("Type 0 means free space to many systems. "
2376 "Having partitions of type 0 is probably unwise."));
2378 if (IS_EXTENDED(p
->sys_ind
) && !IS_EXTENDED(pa
->type
->code
) && has_logical(cxt
)) {
2380 "Cannot change type of the extended partition which is "
2381 "already used by logical partitions. Delete logical "
2382 "partitions first."));
2387 FDISK_INIT_UNDEF(start
);
2388 FDISK_INIT_UNDEF(size
);
2390 if (fdisk_partition_has_start(pa
))
2392 if (fdisk_partition_has_size(pa
))
2395 if (!FDISK_IS_UNDEF(start
) || !FDISK_IS_UNDEF(size
)) {
2396 DBG(LABEL
, ul_debug("DOS: resize partition"));
2398 if (FDISK_IS_UNDEF(start
))
2399 start
= get_abs_partition_start(pe
);
2400 if (FDISK_IS_UNDEF(size
))
2401 size
= dos_partition_get_size(p
);
2403 set_partition(cxt
, n
, 0, start
, start
+ size
- 1,
2404 pa
->type
? pa
->type
->code
: p
->sys_ind
,
2405 FDISK_IS_UNDEF(pa
->boot
) ?
2406 p
->boot_ind
== ACTIVE_FLAG
:
2407 fdisk_partition_is_bootable(pa
));
2409 DBG(LABEL
, ul_debug("DOS: keep size, modify properties"));
2411 p
->sys_ind
= pa
->type
->code
;
2412 if (!FDISK_IS_UNDEF(pa
->boot
))
2413 p
->boot_ind
= fdisk_partition_is_bootable(pa
) ? ACTIVE_FLAG
: 0;
2417 if (IS_EXTENDED(pa
->type
->code
) && !IS_EXTENDED(orgtype
)) {
2418 /* new extended partition - create a reference */
2420 l
->ext_offset
= dos_partition_get_start(p
);
2422 } else if (IS_EXTENDED(orgtype
)) {
2423 /* remove extended partition */
2424 cxt
->label
->nparts_max
= 4;
2425 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
2431 partition_set_changed(cxt
, n
, 1);
2435 static void print_chain_of_logicals(struct fdisk_context
*cxt
)
2438 struct fdisk_dos_label
*l
= self_label(cxt
);
2440 fputc('\n', stdout
);
2442 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2443 struct pte
*pe
= self_pte(cxt
, i
);
2446 fprintf(stderr
, "#%02zu EBR [%10ju], "
2447 "data[start=%10ju (%10ju), size=%10ju], "
2448 "link[start=%10ju (%10ju), size=%10ju]\n",
2449 i
, (uintmax_t) pe
->offset
,
2451 (uintmax_t) dos_partition_get_start(pe
->pt_entry
),
2452 (uintmax_t) get_abs_partition_start(pe
),
2453 (uintmax_t) dos_partition_get_size(pe
->pt_entry
),
2455 (uintmax_t) dos_partition_get_start(pe
->ex_entry
),
2456 (uintmax_t) l
->ext_offset
+ dos_partition_get_start(pe
->ex_entry
),
2457 (uintmax_t) dos_partition_get_size(pe
->ex_entry
));
2461 static int cmp_ebr_offsets(const void *a
, const void *b
)
2463 const struct pte
*ae
= (const struct pte
*) a
,
2464 *be
= (const struct pte
*) b
;
2466 if (ae
->offset
== 0 && be
->offset
== 0)
2468 if (ae
->offset
== 0)
2470 if (be
->offset
== 0)
2473 return cmp_numbers(ae
->offset
, be
->offset
);
2477 * Fix the chain of logicals.
2479 * The function does not modify data partitions within EBR tables
2480 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
2481 * (pte->ex_entry) between EBR tables.
2484 static void fix_chain_of_logicals(struct fdisk_context
*cxt
)
2486 struct fdisk_dos_label
*l
= self_label(cxt
);
2490 DBG(LABEL
, print_chain_of_logicals(cxt
));
2492 /* Sort chain by EBR offsets */
2493 qsort(&l
->ptes
[4], cxt
->label
->nparts_max
- 4, sizeof(struct pte
),
2497 /* Sort data partitions by start */
2498 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
2499 struct pte
*cur
= self_pte(cxt
, i
),
2500 *nxt
= self_pte(cxt
, i
+ 1);
2505 if (get_abs_partition_start(cur
) >
2506 get_abs_partition_start(nxt
)) {
2508 struct dos_partition tmp
= *cur
->pt_entry
;
2509 fdisk_sector_t cur_start
= get_abs_partition_start(cur
),
2510 nxt_start
= get_abs_partition_start(nxt
);
2512 /* swap data partitions */
2513 *cur
->pt_entry
= *nxt
->pt_entry
;
2514 *nxt
->pt_entry
= tmp
;
2516 /* Recount starts according to EBR offsets, the absolute
2517 * address still has to be the same! */
2518 dos_partition_set_start(cur
->pt_entry
, nxt_start
- cur
->offset
);
2519 dos_partition_sync_chs(cur
->pt_entry
, cur
->offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
2520 dos_partition_set_start(nxt
->pt_entry
, cur_start
- nxt
->offset
);
2521 dos_partition_sync_chs(nxt
->pt_entry
, nxt
->offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
2523 partition_set_changed(cxt
, i
, 1);
2524 partition_set_changed(cxt
, i
+ 1, 1);
2529 /* Update EBR links */
2530 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
2531 struct pte
*cur
= self_pte(cxt
, i
),
2532 *nxt
= self_pte(cxt
, i
+ 1);
2537 fdisk_sector_t noff
= nxt
->offset
- l
->ext_offset
,
2538 ooff
= dos_partition_get_start(cur
->ex_entry
);
2543 DBG(LABEL
, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2544 (uintmax_t) cur
->offset
,
2545 (uintmax_t) ooff
, (uintmax_t) noff
));
2547 set_partition(cxt
, i
, 1, nxt
->offset
,
2548 get_abs_partition_end(nxt
),
2549 MBR_DOS_EXTENDED_PARTITION
, 0);
2552 /* always terminate the chain ! */
2553 last
= self_pte(cxt
, cxt
->label
->nparts_max
- 1);
2555 clear_partition(last
->ex_entry
);
2556 partition_set_changed(cxt
, cxt
->label
->nparts_max
- 1, 1);
2559 DBG(LABEL
, print_chain_of_logicals(cxt
));
2562 static int dos_reorder(struct fdisk_context
*cxt
)
2564 struct pte
*pei
, *pek
;
2567 if (!wrong_p_order(cxt
, NULL
))
2570 while ((i
= wrong_p_order(cxt
, &k
)) != 0 && i
< 4) {
2571 /* partition i should have come earlier, move it */
2572 /* We have to move data in the MBR */
2573 struct dos_partition
*pi
, *pk
, *pe
, pbuf
;
2574 pei
= self_pte(cxt
, i
);
2575 pek
= self_pte(cxt
, k
);
2581 pei
->ex_entry
= pek
->ex_entry
;
2587 memmove(&pbuf
, pi
, sizeof(struct dos_partition
));
2588 memmove(pi
, pk
, sizeof(struct dos_partition
));
2589 memmove(pk
, &pbuf
, sizeof(struct dos_partition
));
2591 partition_set_changed(cxt
, i
, 1);
2592 partition_set_changed(cxt
, k
, 1);
2596 fix_chain_of_logicals(cxt
);
2602 * fdisk_dos_fix_chs:
2603 * @cxt: fdisk context
2605 * Fix beginning and ending C/H/S values for every partition
2606 * according to LBA relative offset, relative beginning and
2607 * size and fdisk idea of disk geometry (sectors per track
2608 * and number of heads).
2610 * Returns: number of fixed (changed) partitions.
2612 int fdisk_dos_fix_chs(struct fdisk_context
*cxt
)
2614 unsigned int obc
, obh
, obs
; /* old beginning c, h, s */
2615 unsigned int oec
, oeh
, oes
; /* old ending c, h, s */
2616 unsigned int nbc
, nbh
, nbs
; /* new beginning c, h, s */
2617 unsigned int nec
, neh
, nes
; /* new ending c, h, s */
2618 fdisk_sector_t l
, sects
; /* lba beginning and size */
2619 struct dos_partition
*p
;
2624 assert(fdisk_is_label(cxt
, DOS
));
2626 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
2627 p
= self_partition(cxt
, i
);
2628 if (!p
|| !is_used_partition(p
))
2631 pe
= self_pte(cxt
, i
);
2633 /* old beginning c, h, s */
2634 obc
= cylinder(p
->bs
, p
->bc
);
2636 obs
= sector(p
->bs
);
2638 /* old ending c, h, s */
2639 oec
= cylinder(p
->es
, p
->ec
);
2641 oes
= sector(p
->es
);
2643 /* new beginning c, h, s */
2644 l
= get_abs_partition_start(pe
);
2645 long2chs(cxt
, l
, &nbc
, &nbh
, &nbs
);
2646 if (l
> UINT32_MAX
|| nbc
>= 1024) {
2648 nbh
= cxt
->geom
.heads
-1;
2649 nbs
= cxt
->geom
.sectors
;
2652 /* new ending c, h, s */
2653 sects
= dos_partition_get_size(p
);
2654 long2chs(cxt
, l
+ sects
- 1, &nec
, &neh
, &nes
);
2655 if (lba_overflowed(l
, sects
) || nec
>= 1024) {
2657 neh
= cxt
->geom
.heads
-1;
2658 nes
= cxt
->geom
.sectors
;
2661 if (obc
!= nbc
|| obh
!= nbh
|| obs
!= nbs
||
2662 oec
!= nec
|| oeh
!= neh
|| oes
!= nes
) {
2663 DBG(LABEL
, ul_debug("DOS: changing %zu partition CHS "
2664 "from (%d, %d, %d)-(%d, %d, %d) "
2665 "to (%d, %d, %d)-(%d, %d, %d)",
2666 i
, obc
, obh
, obs
, oec
, oeh
, oes
,
2667 nbc
, nbh
, nbs
, nec
, neh
, nes
));
2670 p
->bs
= nbs
| ((nbc
>> 2) & 0xc0);
2673 p
->es
= nes
| ((nec
>> 2) & 0xc0);
2674 partition_set_changed(cxt
, i
, 1);
2682 /* TODO: use fdisk_set_partition() API */
2683 int fdisk_dos_move_begin(struct fdisk_context
*cxt
, size_t i
)
2686 struct dos_partition
*p
;
2687 unsigned int new, free_start
, curr_start
, last
;
2693 assert(fdisk_is_label(cxt
, DOS
));
2695 pe
= self_pte(cxt
, i
);
2701 if (!is_used_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
2702 fdisk_warnx(cxt
, _("Partition %zu: no data area."), i
+ 1);
2706 /* The safe start is at the second sector, but some use-cases require
2707 * to have MBR within the first partition , so default to the first
2708 * sector of the disk or at the second sector of the extended partition
2710 free_start
= pe
->offset
? pe
->offset
+ 1 : 0;
2712 curr_start
= get_abs_partition_start(pe
);
2714 /* look for a free space before the current start of the partition */
2715 for (x
= 0; x
< cxt
->label
->nparts_max
; x
++) {
2717 struct pte
*prev_pe
= self_pte(cxt
, x
);
2718 struct dos_partition
*prev_p
;
2722 prev_p
= prev_pe
->pt_entry
;
2725 end
= get_abs_partition_start(prev_pe
)
2726 + dos_partition_get_size(prev_p
);
2728 if (is_used_partition(prev_p
) &&
2729 end
> free_start
&& end
<= curr_start
)
2733 last
= get_abs_partition_end(pe
);
2735 rc
= fdisk_ask_number(cxt
, free_start
, curr_start
, last
,
2736 _("New beginning of data"), &res
);
2740 new = res
- pe
->offset
;
2742 if (new != dos_partition_get_size(p
)) {
2743 unsigned int sects
= dos_partition_get_size(p
)
2744 + dos_partition_get_start(p
) - new;
2746 dos_partition_set_size(p
, sects
);
2747 dos_partition_set_start(p
, new);
2748 dos_partition_sync_chs(p
, pe
->offset
, cxt
->geom
.sectors
, cxt
->geom
.heads
);
2750 partition_set_changed(cxt
, i
, 1);
2753 fdisk_info(cxt
, _("The new beginning of the partition overlaps the disk "
2754 "label area. Be very careful when using the partition. "
2755 "You can lose all your partitions on the disk."));
2761 static int dos_partition_is_used(
2762 struct fdisk_context
*cxt
,
2765 struct dos_partition
*p
;
2769 assert(fdisk_is_label(cxt
, DOS
));
2771 if (i
>= cxt
->label
->nparts_max
)
2774 p
= self_partition(cxt
, i
);
2776 return p
&& !is_cleared_partition(p
);
2779 static int dos_toggle_partition_flag(
2780 struct fdisk_context
*cxt
,
2784 struct dos_partition
*p
;
2788 assert(fdisk_is_label(cxt
, DOS
));
2790 if (i
>= cxt
->label
->nparts_max
)
2793 p
= self_partition(cxt
, i
);
2797 case DOS_FLAG_ACTIVE
:
2798 if (IS_EXTENDED(p
->sys_ind
) && !p
->boot_ind
)
2799 fdisk_warnx(cxt
, _("Partition %zu: is an extended "
2800 "partition."), i
+ 1);
2802 p
->boot_ind
= (p
->boot_ind
? 0 : ACTIVE_FLAG
);
2803 partition_set_changed(cxt
, i
, 1);
2804 fdisk_info(cxt
, p
->boot_ind
?
2805 _("The bootable flag on partition %zu is enabled now.") :
2806 _("The bootable flag on partition %zu is disabled now."),
2816 static const struct fdisk_field dos_fields
[] =
2819 { FDISK_FIELD_DEVICE
, N_("Device"), 10, 0 },
2820 { FDISK_FIELD_BOOT
, N_("Boot"), 1, 0 },
2821 { FDISK_FIELD_START
, N_("Start"), 5, FDISK_FIELDFL_NUMBER
},
2822 { FDISK_FIELD_END
, N_("End"), 5, FDISK_FIELDFL_NUMBER
},
2823 { FDISK_FIELD_SECTORS
, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER
},
2824 { FDISK_FIELD_CYLINDERS
,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER
},
2825 { FDISK_FIELD_SIZE
, N_("Size"), 5, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_EYECANDY
},
2826 { FDISK_FIELD_TYPEID
, N_("Id"), 2, FDISK_FIELDFL_NUMBER
},
2827 { FDISK_FIELD_TYPE
, N_("Type"), 0.1, 0 },
2830 { FDISK_FIELD_SADDR
, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2831 { FDISK_FIELD_EADDR
, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2832 { FDISK_FIELD_ATTR
, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
}
2836 static const struct fdisk_label_operations dos_operations
=
2838 .probe
= dos_probe_label
,
2839 .write
= dos_write_disklabel
,
2840 .verify
= dos_verify_disklabel
,
2841 .create
= dos_create_disklabel
,
2842 .locate
= dos_locate_disklabel
,
2843 .get_item
= dos_get_disklabel_item
,
2844 .set_id
= dos_set_disklabel_id
,
2846 .get_part
= dos_get_partition
,
2847 .set_part
= dos_set_partition
,
2848 .add_part
= dos_add_partition
,
2849 .del_part
= dos_delete_partition
,
2850 .reorder
= dos_reorder
,
2852 .part_toggle_flag
= dos_toggle_partition_flag
,
2853 .part_is_used
= dos_partition_is_used
,
2855 .reset_alignment
= dos_reset_alignment
,
2857 .deinit
= dos_deinit
,
2861 * allocates DOS in-memory stuff
2863 struct fdisk_label
*fdisk_new_dos_label(struct fdisk_context
*cxt
__attribute__ ((__unused__
)))
2865 struct fdisk_label
*lb
;
2866 struct fdisk_dos_label
*dos
;
2868 dos
= calloc(1, sizeof(*dos
));
2872 /* initialize generic part of the driver */
2873 lb
= (struct fdisk_label
*) dos
;
2875 lb
->id
= FDISK_DISKLABEL_DOS
;
2876 lb
->op
= &dos_operations
;
2878 lb
->parttypes
= dos_parttypes
;
2879 lb
->nparttypes
= ARRAY_SIZE(dos_parttypes
) - 1;
2880 lb
->parttype_cuts
= dos_parttype_cuts
;
2881 lb
->nparttype_cuts
= ARRAY_SIZE(dos_parttype_cuts
);
2883 lb
->fields
= dos_fields
;
2884 lb
->nfields
= ARRAY_SIZE(dos_fields
);
2886 lb
->geom_min
.sectors
= 1;
2887 lb
->geom_min
.heads
= 1;
2888 lb
->geom_min
.cylinders
= 1;
2890 lb
->geom_max
.sectors
= 63;
2891 lb
->geom_max
.heads
= 255;
2892 lb
->geom_max
.cylinders
= 1048576;
2894 /* return calloc() result to keep static anaylizers happy */
2895 return (struct fdisk_label
*) dos
;
2899 * fdisk_dos_enable_compatible:
2900 * @lb: DOS label (see fdisk_get_label())
2903 * Enables deprecated DOS compatible mode, in this mode library checks for
2904 * cylinders boundary, cases about CHS addressing and another obscure things.
2906 * Returns: 0 on success, <0 on error.
2908 int fdisk_dos_enable_compatible(struct fdisk_label
*lb
, int enable
)
2910 struct fdisk_dos_label
*dos
= (struct fdisk_dos_label
*) lb
;
2915 dos
->compatible
= enable
;
2917 lb
->flags
|= FDISK_LABEL_FL_REQUIRE_GEOMETRY
;
2922 * fdisk_dos_is_compatible:
2925 * Returns: 0 if DOS compatibility disabled, 1 if enabled
2927 int fdisk_dos_is_compatible(struct fdisk_label
*lb
)
2929 return ((struct fdisk_dos_label
*) lb
)->compatible
;