3 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
4 * 2012 Davidlohr Bueso <dave@gnu.org>
6 * This is re-written version for libfdisk, the original was fdiskdoslabel.c
7 * from util-linux fdisk.
11 #include "randutils.h"
19 #define MAXIMUM_PARTS 60
20 #define ACTIVE_FLAG 0x80
22 #define IS_EXTENDED(i) \
23 ((i) == MBR_DOS_EXTENDED_PARTITION \
24 || (i) == MBR_W95_EXTENDED_PARTITION \
25 || (i) == MBR_LINUX_EXTENDED_PARTITION)
28 * per partition table entry data
30 * The four primary partitions have the same sectorbuffer
31 * and have NULL ex_entry.
33 * Each logical partition table entry has two pointers, one for the
34 * partition and one link to the next one.
37 struct dos_partition
*pt_entry
; /* on-disk MBR entry */
38 struct dos_partition
*ex_entry
; /* on-disk EBR entry */
39 sector_t offset
; /* disk sector number */
40 unsigned char *sectorbuffer
; /* disk sector contents */
42 unsigned int changed
: 1,
43 private_sectorbuffer
: 1;
47 * in-memory fdisk GPT stuff
49 struct fdisk_dos_label
{
50 struct fdisk_label head
; /* generic part */
52 struct pte ptes
[MAXIMUM_PARTS
]; /* partition */
53 sector_t ext_offset
; /* start of the ext.partition */
54 size_t ext_index
; /* ext.partition index (if ext_offset is set) */
55 unsigned int compatible
: 1, /* is DOS compatible? */
56 non_pt_changed
: 1; /* MBR, but no PT changed */
62 static struct fdisk_parttype dos_parttypes
[] = {
63 #include "pt-mbr-partnames.h"
66 #define set_hsc(h,s,c,sector) { \
67 s = sector % cxt->geom.sectors + 1; \
68 sector /= cxt->geom.sectors; \
69 h = sector % cxt->geom.heads; \
70 sector /= cxt->geom.heads; \
72 s |= (sector >> 2) & 0xc0; \
76 #define sector(s) ((s) & 0x3f)
77 #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
79 #define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
81 #define is_dos_compatible(_x) \
82 (fdisk_is_disklabel(_x, DOS) && \
83 fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
85 #define cround(c, n) fdisk_cround(c, n)
88 static inline struct fdisk_dos_label
*self_label(struct fdisk_context
*cxt
)
92 assert(fdisk_is_disklabel(cxt
, DOS
));
94 return (struct fdisk_dos_label
*) cxt
->label
;
97 static inline struct pte
*self_pte(struct fdisk_context
*cxt
, size_t i
)
99 struct fdisk_dos_label
*l
= self_label(cxt
);
101 if (i
>= ARRAY_SIZE(l
->ptes
))
107 static inline struct dos_partition
*self_partition(
108 struct fdisk_context
*cxt
,
111 struct pte
*pe
= self_pte(cxt
, i
);
112 return pe
? pe
->pt_entry
: NULL
;
115 struct dos_partition
*fdisk_dos_get_partition(
116 struct fdisk_context
*cxt
,
121 assert(fdisk_is_disklabel(cxt
, DOS
));
123 return self_partition(cxt
, i
);
126 static struct fdisk_parttype
*dos_partition_parttype(
127 struct fdisk_context
*cxt
,
128 struct dos_partition
*p
)
130 struct fdisk_parttype
*t
131 = fdisk_get_parttype_from_code(cxt
, p
->sys_ind
);
132 return t
? : fdisk_new_unknown_parttype(p
->sys_ind
, NULL
);
136 * Linux kernel cares about partition size only. Things like
137 * partition type or so are completely irrelevant -- kzak Nov-2013
139 static int is_used_partition(struct dos_partition
*p
)
141 return p
&& dos_partition_get_size(p
) != 0;
144 static void partition_set_changed(
145 struct fdisk_context
*cxt
,
149 struct pte
*pe
= self_pte(cxt
, i
);
154 DBG(LABEL
, ul_debug("DOS: setting %zu partition to %s", i
,
155 changed
? "changed" : "unchnaged"));
157 pe
->changed
= changed
? 1 : 0;
159 fdisk_label_set_changed(cxt
->label
, 1);
162 static sector_t
get_abs_partition_start(struct pte
*pe
)
165 assert(pe
->pt_entry
);
167 return pe
->offset
+ dos_partition_get_start(pe
->pt_entry
);
170 static sector_t
get_abs_partition_end(struct pte
*pe
)
175 assert(pe
->pt_entry
);
177 size
= dos_partition_get_size(pe
->pt_entry
);
178 return get_abs_partition_start(pe
) + size
- (size
? 1 : 0);
181 static int is_cleared_partition(struct dos_partition
*p
)
183 return !(!p
|| p
->boot_ind
|| p
->bh
|| p
->bs
|| p
->bc
||
184 p
->sys_ind
|| p
->eh
|| p
->es
|| p
->ec
||
185 dos_partition_get_start(p
) || dos_partition_get_size(p
));
188 static int get_partition_unused_primary(struct fdisk_context
*cxt
,
189 struct fdisk_partition
*pa
)
191 size_t org
= cxt
->label
->nparts_max
, n
;
194 cxt
->label
->nparts_max
= 4;
195 rc
= fdisk_partition_next_partno(pa
, cxt
, &n
);
196 cxt
->label
->nparts_max
= org
;
200 fdisk_info(cxt
, _("All primary partitions have been defined already."));
209 static int seek_sector(struct fdisk_context
*cxt
, sector_t secno
)
211 off_t offset
= (off_t
) secno
* cxt
->sector_size
;
213 return lseek(cxt
->dev_fd
, offset
, SEEK_SET
) == (off_t
) -1 ? -errno
: 0;
216 static int read_sector(struct fdisk_context
*cxt
, sector_t secno
,
219 int rc
= seek_sector(cxt
, secno
);
225 r
= read(cxt
->dev_fd
, buf
, cxt
->sector_size
);
226 if (r
== (ssize_t
) cxt
->sector_size
)
233 /* Allocate a buffer and read a partition table sector */
234 static int read_pte(struct fdisk_context
*cxt
, size_t pno
, sector_t offset
)
238 struct pte
*pe
= self_pte(cxt
, pno
);
240 buf
= calloc(1, cxt
->sector_size
);
244 DBG(LABEL
, ul_debug("DOS: reading pte %zu sector buffer %p", pno
, buf
));
247 pe
->sectorbuffer
= buf
;
248 pe
->private_sectorbuffer
= 1;
250 rc
= read_sector(cxt
, offset
, pe
->sectorbuffer
);
252 fdisk_warn(cxt
, _("Failed to read extended partition table "
253 "(offset=%ju)"), (uintmax_t) offset
);
258 pe
->pt_entry
= pe
->ex_entry
= NULL
;
263 static void clear_partition(struct dos_partition
*p
)
275 dos_partition_set_start(p
,0);
276 dos_partition_set_size(p
,0);
279 static void dos_init(struct fdisk_context
*cxt
)
281 struct fdisk_dos_label
*l
= self_label(cxt
);
286 assert(fdisk_is_disklabel(cxt
, DOS
));
288 DBG(LABEL
, ul_debug("DOS: initialize, first sector buffer %p", cxt
->firstsector
));
290 cxt
->label
->nparts_max
= 4; /* default, unlimited number of logical */
294 l
->non_pt_changed
= 0;
296 memset(l
->ptes
, 0, sizeof(l
->ptes
));
298 for (i
= 0; i
< 4; i
++) {
299 struct pte
*pe
= self_pte(cxt
, i
);
301 pe
->pt_entry
= mbr_get_partition(cxt
->firstsector
, i
);
304 pe
->sectorbuffer
= cxt
->firstsector
;
305 pe
->private_sectorbuffer
= 0;
309 if (fdisk_is_listonly(cxt
))
312 * Various warnings...
314 if (fdisk_missing_geometry(cxt
))
315 fdisk_warnx(cxt
, _("You can set geometry from the extra functions menu."));
317 if (is_dos_compatible(cxt
)) {
318 fdisk_warnx(cxt
, _("DOS-compatible mode is deprecated."));
320 if (cxt
->sector_size
!= cxt
->phy_sector_size
)
322 "The device presents a logical sector size that is smaller than "
323 "the physical sector size. Aligning to a physical sector (or optimal "
324 "I/O) size boundary is recommended, or performance may be impacted."));
327 if (fdisk_use_cylinders(cxt
))
328 fdisk_warnx(cxt
, _("Cylinders as display units are deprecated."));
330 if (cxt
->total_sectors
> UINT_MAX
) {
331 uint64_t bytes
= cxt
->total_sectors
* cxt
->sector_size
;
332 char *szstr
= size_to_human_string(SIZE_SUFFIX_SPACE
333 | SIZE_SUFFIX_3LETTER
, bytes
);
335 _("The size of this disk is %s (%ju bytes). DOS "
336 "partition table format can not be used on drives for "
337 "volumes larger than %lu bytes for %lu-byte "
338 "sectors. Use GUID partition table format (GPT)."),
340 UINT_MAX
* cxt
->sector_size
,
346 /* callback called by libfdisk */
347 static void dos_deinit(struct fdisk_label
*lb
)
350 struct fdisk_dos_label
*l
= (struct fdisk_dos_label
*) lb
;
352 for (i
= 0; i
< ARRAY_SIZE(l
->ptes
); i
++) {
353 struct pte
*pe
= &l
->ptes
[i
];
355 if (pe
->private_sectorbuffer
&& pe
->sectorbuffer
) {
356 DBG(LABEL
, ul_debug("DOS: freeing pte %zu sector buffer %p",
357 i
, pe
->sectorbuffer
));
358 free(pe
->sectorbuffer
);
360 pe
->sectorbuffer
= NULL
;
361 pe
->private_sectorbuffer
= 0;
364 memset(l
->ptes
, 0, sizeof(l
->ptes
));
367 static int dos_delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
369 struct fdisk_dos_label
*l
;
371 struct dos_partition
*p
;
372 struct dos_partition
*q
;
376 assert(fdisk_is_disklabel(cxt
, DOS
));
378 pe
= self_pte(cxt
, partnum
);
382 DBG(LABEL
, ul_debug("DOS: delete partiton %zu (max=%zu)", partnum
,
383 cxt
->label
->nparts_max
));
389 /* Note that for the fifth partition (partnum == 4) we don't actually
390 decrement partitions. */
392 DBG(LABEL
, ul_debug("--> delete primary"));
393 if (IS_EXTENDED(p
->sys_ind
) && partnum
== l
->ext_index
) {
394 cxt
->label
->nparts_max
= 4;
395 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
399 partition_set_changed(cxt
, partnum
, 1);
401 } else if (!q
->sys_ind
&& partnum
> 4) {
402 DBG(LABEL
, ul_debug("--> delete logical [last in the chain]"));
403 --cxt
->label
->nparts_max
;
405 clear_partition(l
->ptes
[partnum
].ex_entry
);
406 partition_set_changed(cxt
, partnum
, 1);
408 DBG(LABEL
, ul_debug("--> delete logical [move down]"));
410 DBG(LABEL
, ul_debug(" --> delete %zu logical link", partnum
));
411 p
= l
->ptes
[partnum
- 1].ex_entry
;
413 dos_partition_set_start(p
, dos_partition_get_start(q
));
414 dos_partition_set_size(p
, dos_partition_get_size(q
));
415 partition_set_changed(cxt
, partnum
- 1, 1);
417 } else if (cxt
->label
->nparts_max
> 5) {
418 DBG(LABEL
, ul_debug(" --> delete first logical link"));
419 pe
= &l
->ptes
[5]; /* second logical */
421 if (pe
->pt_entry
) /* prevent SEGFAULT */
422 dos_partition_set_start(pe
->pt_entry
,
423 get_abs_partition_start(pe
) -
425 pe
->offset
= l
->ext_offset
;
426 partition_set_changed(cxt
, 5, 1);
429 if (cxt
->label
->nparts_max
> 5) {
430 DBG(LABEL
, ul_debug(" --> move ptes"));
431 cxt
->label
->nparts_max
--;
432 if (l
->ptes
[partnum
].private_sectorbuffer
) {
433 DBG(LABEL
, ul_debug(" --> freeing pte %zu sector buffer %p",
434 partnum
, l
->ptes
[partnum
].sectorbuffer
));
435 free(l
->ptes
[partnum
].sectorbuffer
);
437 while (partnum
< cxt
->label
->nparts_max
) {
438 DBG(LABEL
, ul_debug(" --> moving pte %zu <-- %zu", partnum
, partnum
+ 1));
439 l
->ptes
[partnum
] = l
->ptes
[partnum
+ 1];
442 memset(&l
->ptes
[partnum
], 0, sizeof(struct pte
));
444 DBG(LABEL
, ul_debug(" --> the only logical: clear only"));
445 clear_partition(l
->ptes
[partnum
].pt_entry
);
446 cxt
->label
->nparts_max
--;
449 DBG(LABEL
, ul_debug(" --> clear last logical"));
450 if (l
->ptes
[partnum
].private_sectorbuffer
) {
451 DBG(LABEL
, ul_debug(" --> freeing pte %zu sector buffer %p",
452 partnum
, l
->ptes
[partnum
].sectorbuffer
));
453 free(l
->ptes
[partnum
].sectorbuffer
);
455 memset(&l
->ptes
[partnum
], 0, sizeof(struct pte
));
456 partition_set_changed(cxt
, l
->ext_index
, 1);
461 fdisk_label_set_changed(cxt
->label
, 1);
465 static void read_extended(struct fdisk_context
*cxt
, size_t ext
)
469 struct dos_partition
*p
, *q
;
470 struct fdisk_dos_label
*l
= self_label(cxt
);
473 pex
= self_pte(cxt
, ext
);
474 pex
->ex_entry
= pex
->pt_entry
;
477 if (!dos_partition_get_start(p
)) {
478 fdisk_warnx(cxt
, _("Bad offset in primary extended partition."));
482 DBG(LABEL
, ul_debug("DOS: Reading extended %zu", ext
));
484 while (IS_EXTENDED (p
->sys_ind
)) {
485 struct pte
*pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
487 if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
488 /* This is not a Linux restriction, but
489 this program uses arrays of size MAXIMUM_PARTS.
490 Do not try to `improve' this test. */
491 struct pte
*pre
= self_pte(cxt
,
492 cxt
->label
->nparts_max
- 1);
494 _("Omitting partitions after #%zu. They will be deleted "
495 "if you save this partition table."),
496 cxt
->label
->nparts_max
);
498 clear_partition(pre
->ex_entry
);
499 partition_set_changed(cxt
,
500 cxt
->label
->nparts_max
- 1, 1);
504 if (read_pte(cxt
, cxt
->label
->nparts_max
, l
->ext_offset
+
505 dos_partition_get_start(p
)))
509 l
->ext_offset
= dos_partition_get_start(p
);
511 q
= p
= mbr_get_partition(pe
->sectorbuffer
, 0);
513 for (i
= 0; i
< 4; i
++, p
++) {
514 if (!dos_partition_get_size(p
))
517 if (IS_EXTENDED (p
->sys_ind
)) {
520 "Extra link pointer in partition "
522 cxt
->label
->nparts_max
+ 1);
525 } else if (p
->sys_ind
) {
528 "Ignoring extra data in partition "
530 cxt
->label
->nparts_max
+ 1);
536 /* very strange code here... */
538 if (q
!= pe
->ex_entry
)
541 pe
->pt_entry
= q
+ 1;
544 if (q
!= pe
->pt_entry
)
547 pe
->ex_entry
= q
+ 1;
551 cxt
->label
->nparts_cur
= ++cxt
->label
->nparts_max
;
554 /* remove empty links */
556 q
= self_partition(cxt
, 4);
557 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
558 p
= self_partition(cxt
, i
);
560 if (!dos_partition_get_size(p
) &&
561 (cxt
->label
->nparts_max
> 5 || q
->sys_ind
)) {
562 fdisk_info(cxt
, _("omitting empty partition (%zu)"), i
+1);
563 dos_delete_partition(cxt
, i
);
564 goto remove
; /* numbering changed */
569 static int dos_get_disklabel_id(struct fdisk_context
*cxt
, char **id
)
576 assert(fdisk_is_disklabel(cxt
, DOS
));
578 num
= mbr_get_id(cxt
->firstsector
);
579 if (asprintf(id
, "0x%08x", num
) > 0)
585 static int dos_create_disklabel(struct fdisk_context
*cxt
)
592 assert(fdisk_is_disklabel(cxt
, DOS
));
594 DBG(LABEL
, ul_debug("DOS: creating new disklabel"));
596 /* random disk signature */
597 random_get_bytes(&id
, sizeof(id
));
600 rc
= fdisk_init_firstsector_buffer(cxt
);
603 fdisk_label_set_changed(cxt
->label
, 1);
605 /* Generate an MBR ID for this disk */
606 mbr_set_id(cxt
->firstsector
, id
);
608 /* Put MBR signature */
609 mbr_set_magic(cxt
->firstsector
);
611 fdisk_sinfo(cxt
, FDISK_INFO_SUCCESS
,
612 ("Created a new DOS disklabel with disk "
613 "identifier 0x%08x."), id
);
617 static int dos_set_disklabel_id(struct fdisk_context
*cxt
)
619 char *end
= NULL
, *str
= NULL
;
620 unsigned int id
, old
;
621 struct fdisk_dos_label
*l
;
626 assert(fdisk_is_disklabel(cxt
, DOS
));
628 DBG(LABEL
, ul_debug("DOS: setting Id"));
631 old
= mbr_get_id(cxt
->firstsector
);
632 rc
= fdisk_ask_string(cxt
,
633 _("Enter the new disk identifier"), &str
);
638 id
= strtoul(str
, &end
, 0);
639 if (errno
|| str
== end
|| (end
&& *end
)) {
640 fdisk_warnx(cxt
, _("Incorrect value."));
645 mbr_set_id(cxt
->firstsector
, id
);
646 l
->non_pt_changed
= 1;
647 fdisk_label_set_changed(cxt
->label
, 1);
649 fdisk_sinfo(cxt
, FDISK_INFO_SUCCESS
,
650 _("Disk identifier changed from 0x%08x to 0x%08x."),
655 static void get_partition_table_geometry(struct fdisk_context
*cxt
,
656 unsigned int *ph
, unsigned int *ps
)
658 unsigned char *bufp
= cxt
->firstsector
;
659 struct dos_partition
*p
;
665 for (i
= 0; i
< 4; i
++) {
666 p
= mbr_get_partition(bufp
, i
);
667 if (p
->sys_ind
!= 0) {
674 } else if (hh
!= h
|| ss
!= s
)
679 if (!first
&& !bad
) {
684 DBG(LABEL
, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph
, *ps
));
687 static int dos_reset_alignment(struct fdisk_context
*cxt
)
691 assert(fdisk_is_disklabel(cxt
, DOS
));
693 /* overwrite necessary stuff by DOS deprecated stuff */
694 if (is_dos_compatible(cxt
)) {
695 DBG(LABEL
, ul_debug("DOS: reseting alignemnt for DOS-comaptiblem PT"));
696 if (cxt
->geom
.sectors
)
697 cxt
->first_lba
= cxt
->geom
.sectors
; /* usually 63 */
699 cxt
->grain
= cxt
->sector_size
; /* usually 512 */
705 /* TODO: move to include/pt-dos.h and share with libblkid */
706 #define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
707 #define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
709 static int dos_probe_label(struct fdisk_context
*cxt
)
712 unsigned int h
= 0, s
= 0;
716 assert(fdisk_is_disklabel(cxt
, DOS
));
718 /* ignore disks with AIX magic number */
719 if (memcmp(cxt
->firstsector
, AIX_MAGIC_STRING
, AIX_MAGIC_STRLEN
) == 0)
722 if (!mbr_is_valid_magic(cxt
->firstsector
))
727 get_partition_table_geometry(cxt
, &h
, &s
);
730 cxt
->geom
.sectors
= s
;
733 for (i
= 0; i
< 4; i
++) {
734 struct pte
*pe
= self_pte(cxt
, i
);
736 if (is_used_partition(pe
->pt_entry
))
737 cxt
->label
->nparts_cur
++;
739 if (IS_EXTENDED (pe
->pt_entry
->sys_ind
)) {
740 if (cxt
->label
->nparts_max
!= 4)
742 "Ignoring extra extended partition %zu"),
745 read_extended(cxt
, i
);
749 for (i
= 3; i
< cxt
->label
->nparts_max
; i
++) {
750 struct pte
*pe
= self_pte(cxt
, i
);
751 struct fdisk_dos_label
*l
= self_label(cxt
);
753 if (!mbr_is_valid_magic(pe
->sectorbuffer
)) {
755 "Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
756 "be corrected by w(rite)."),
757 pe
->sectorbuffer
[510],
758 pe
->sectorbuffer
[511],
760 partition_set_changed(cxt
, i
, 1);
762 /* mark also extended as changed to update the first EBR
763 * in situation that there is no logical partitions at all */
764 partition_set_changed(cxt
, l
->ext_index
, 1);
772 * Avoid warning about DOS partitions when no DOS partition was changed.
773 * Here a heuristic "is probably dos partition".
774 * We might also do the opposite and warn in all cases except
775 * for "is probably nondos partition".
777 static int is_dos_partition(int t
)
779 return (t
== 1 || t
== 4 || t
== 6 ||
780 t
== 0x0b || t
== 0x0c || t
== 0x0e ||
781 t
== 0x11 || t
== 0x12 || t
== 0x14 || t
== 0x16 ||
782 t
== 0x1b || t
== 0x1c || t
== 0x1e || t
== 0x24 ||
783 t
== 0xc1 || t
== 0xc4 || t
== 0xc6);
786 static void set_partition(struct fdisk_context
*cxt
,
787 int i
, int doext
, sector_t start
,
788 sector_t stop
, int sysid
)
790 struct pte
*pe
= self_pte(cxt
, i
);
791 struct dos_partition
*p
;
796 struct fdisk_dos_label
*l
= self_label(cxt
);
798 offset
= l
->ext_offset
;
804 DBG(LABEL
, ul_debug("DOS: setting partition %d%s, start=%zu, stop=%zu, sysid=%02x",
805 i
, doext
? " [extended]" : "",
806 (size_t) (start
- offset
), (size_t) stop
, sysid
));
810 dos_partition_set_start(p
, start
- offset
);
811 dos_partition_set_size(p
, stop
- start
+ 1);
814 struct fdisk_parttype
*t
= fdisk_get_parttype_from_code(cxt
, sysid
);
815 fdisk_info_new_partition(cxt
, i
+ 1, start
, stop
, t
);
817 if (is_dos_compatible(cxt
) && (start
/(cxt
->geom
.sectors
*cxt
->geom
.heads
) > 1023))
818 start
= cxt
->geom
.heads
*cxt
->geom
.sectors
*1024 - 1;
819 set_hsc(p
->bh
, p
->bs
, p
->bc
, start
);
820 if (is_dos_compatible(cxt
) && (stop
/(cxt
->geom
.sectors
*cxt
->geom
.heads
) > 1023))
821 stop
= cxt
->geom
.heads
*cxt
->geom
.sectors
*1024 - 1;
822 set_hsc(p
->eh
, p
->es
, p
->ec
, stop
);
823 partition_set_changed(cxt
, i
, 1);
826 static sector_t
get_unused_start(struct fdisk_context
*cxt
,
827 int part_n
, sector_t start
,
828 sector_t first
[], sector_t last
[])
832 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
833 sector_t lastplusoff
;
834 struct pte
*pe
= self_pte(cxt
, i
);
836 if (start
== pe
->offset
)
837 start
+= cxt
->first_lba
;
838 lastplusoff
= last
[i
] + ((part_n
< 4) ? 0 : cxt
->first_lba
);
839 if (start
>= first
[i
] && start
<= lastplusoff
)
840 start
= lastplusoff
+ 1;
846 static void fill_bounds(struct fdisk_context
*cxt
,
847 sector_t
*first
, sector_t
*last
)
850 struct pte
*pe
= self_pte(cxt
, 0);
851 struct dos_partition
*p
;
853 for (i
= 0; i
< cxt
->label
->nparts_max
; pe
++,i
++) {
855 if (is_cleared_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
856 first
[i
] = 0xffffffff;
859 first
[i
] = get_abs_partition_start(pe
);
860 last
[i
] = get_abs_partition_end(pe
);
865 static int get_start_from_user( struct fdisk_context
*cxt
,
870 struct fdisk_partition
*pa
)
874 /* try to use tepmlate from 'pa' */
875 if (pa
&& pa
->start_follow_default
)
878 else if (pa
&& pa
->start
) {
879 DBG(LABEL
, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
880 (uintmax_t) pa
->start
, (uintmax_t) low
, (uintmax_t) limit
));
882 if (*start
< low
|| *start
> limit
) {
883 fdisk_warnx(cxt
, _("Start sector %ju out of range."),
888 /* ask user by dialog */
889 struct fdisk_ask
*ask
= fdisk_new_ask();
894 fdisk_ask_set_query(ask
,
895 fdisk_use_cylinders(cxt
) ?
896 _("First cylinder") : _("First sector"));
897 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_NUMBER
);
898 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, low
));
899 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, dflt
));
900 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
902 rc
= fdisk_do_ask(cxt
, ask
);
903 *start
= fdisk_ask_number_get_result(ask
);
907 if (fdisk_use_cylinders(cxt
)) {
908 *start
= (*start
- 1)
909 * fdisk_get_units_per_sector(cxt
);
918 static int add_partition(struct fdisk_context
*cxt
, size_t n
,
919 struct fdisk_partition
*pa
)
921 int sys
, read
= 0, rc
, isrel
= 0;
923 struct fdisk_dos_label
*l
= self_label(cxt
);
924 struct dos_partition
*p
= self_partition(cxt
, n
);
925 struct pte
*ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
927 sector_t start
, stop
= 0, limit
, temp
,
928 first
[cxt
->label
->nparts_max
],
929 last
[cxt
->label
->nparts_max
];
931 DBG(LABEL
, ul_debug("DOS: adding partition %zu", n
));
933 sys
= pa
&& pa
->type
? pa
->type
->type
: MBR_LINUX_DATA_PARTITION
;
935 if (is_used_partition(p
)) {
936 fdisk_warnx(cxt
, _("Partition %zu is already defined. "
937 "Delete it before re-adding it."),
941 fill_bounds(cxt
, first
, last
);
943 if (cxt
->parent
&& fdisk_is_disklabel(cxt
->parent
, GPT
))
944 start
= 1; /* Bad boy modifies hybrid MBR */
946 start
= cxt
->first_lba
;
948 if (fdisk_use_cylinders(cxt
) || !cxt
->total_sectors
)
949 limit
= cxt
->geom
.heads
* cxt
->geom
.sectors
* cxt
->geom
.cylinders
- 1;
951 limit
= cxt
->total_sectors
- 1;
953 if (limit
> UINT_MAX
)
958 first
[l
->ext_index
] = l
->ext_offset
;
959 last
[l
->ext_index
] = get_abs_partition_end(ext_pe
);
963 start
= l
->ext_offset
+ cxt
->first_lba
;
964 limit
= get_abs_partition_end(ext_pe
);
966 if (fdisk_use_cylinders(cxt
))
967 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
968 first
[i
] = (fdisk_cround(cxt
, first
[i
]) - 1)
969 * fdisk_get_units_per_sector(cxt
);
973 * Ask for first sector
976 sector_t dflt
, aligned
;
979 dflt
= start
= get_unused_start(cxt
, n
, start
, first
, last
);
981 /* the default sector should be aligned and unused */
983 aligned
= fdisk_align_lba_in_range(cxt
, dflt
, dflt
, limit
);
984 dflt
= get_unused_start(cxt
, n
, aligned
, first
, last
);
985 } while (dflt
!= aligned
&& dflt
> aligned
&& dflt
< limit
);
991 if (start
>= temp
+ fdisk_get_units_per_sector(cxt
)
993 fdisk_info(cxt
, _("Sector %llu is already allocated."),
999 if (!read
&& start
== temp
) {
1000 rc
= get_start_from_user(cxt
, &start
, temp
, dflt
, limit
, pa
);
1005 } while (start
!= temp
|| !read
);
1007 if (n
> 4) { /* NOT for fifth partition */
1008 struct pte
*pe
= self_pte(cxt
, n
);
1010 pe
->offset
= start
- cxt
->first_lba
;
1011 if (pe
->offset
== l
->ext_offset
) { /* must be corrected */
1013 if (cxt
->first_lba
== 1)
1018 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
1019 struct pte
*pe
= self_pte(cxt
, i
);
1021 if (start
< pe
->offset
&& limit
>= pe
->offset
)
1022 limit
= pe
->offset
- 1;
1023 if (start
< first
[i
] && limit
>= first
[i
])
1024 limit
= first
[i
] - 1;
1026 if (start
> limit
) {
1027 fdisk_info(cxt
, _("No free sectors available."));
1029 cxt
->label
->nparts_max
--;
1034 * Ask for last sector
1036 if (fdisk_cround(cxt
, start
) == fdisk_cround(cxt
, limit
))
1038 else if (pa
&& pa
->end_follow_default
)
1040 else if (pa
&& pa
->size
) {
1041 stop
= start
+ pa
->size
;
1044 /* ask user by dialog */
1045 struct fdisk_ask
*ask
= fdisk_new_ask();
1049 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_OFFSET
);
1051 if (fdisk_use_cylinders(cxt
)) {
1052 fdisk_ask_set_query(ask
, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
1053 fdisk_ask_number_set_unit(ask
,
1055 fdisk_get_units_per_sector(cxt
));
1057 fdisk_ask_set_query(ask
, _("Last sector, +sectors or +size{K,M,G,T,P}"));
1058 fdisk_ask_number_set_unit(ask
,cxt
->sector_size
);
1061 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, start
));
1062 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, limit
));
1063 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
1064 fdisk_ask_number_set_base(ask
, fdisk_cround(cxt
, start
)); /* base for relative input */
1066 rc
= fdisk_do_ask(cxt
, ask
);
1067 stop
= fdisk_ask_number_get_result(ask
);
1068 isrel
= fdisk_ask_number_is_relative(ask
);
1069 fdisk_free_ask(ask
);
1072 if (fdisk_use_cylinders(cxt
)) {
1073 stop
= stop
* fdisk_get_units_per_sector(cxt
) - 1;
1083 if (isrel
&& alignment_required(cxt
)) {
1084 /* the last sector has not been exactly requested (but
1085 * defined by +size{K,M,G} convention), so be smart and
1086 * align the end of the partition. The next partition
1087 * will start at phy.block boundary.
1089 stop
= fdisk_align_lba_in_range(cxt
, stop
, start
, limit
) - 1;
1095 set_partition(cxt
, n
, 0, start
, stop
, sys
);
1097 struct pte
*pe
= self_pte(cxt
, n
);
1098 set_partition(cxt
, n
- 1, 1, pe
->offset
, stop
,
1099 MBR_DOS_EXTENDED_PARTITION
);
1102 if (IS_EXTENDED(sys
)) {
1103 struct pte
*pe4
= self_pte(cxt
, 4);
1104 struct pte
*pen
= self_pte(cxt
, n
);
1108 pe4
->offset
= l
->ext_offset
= start
;
1109 pe4
->sectorbuffer
= calloc(1, cxt
->sector_size
);
1110 if (!pe4
->sectorbuffer
)
1112 DBG(LABEL
, ul_debug("DOS: add partition, sector buffer %p", pe4
->sectorbuffer
));
1113 pe4
->private_sectorbuffer
= 1;
1114 pe4
->pt_entry
= mbr_get_partition(pe4
->sectorbuffer
, 0);
1115 pe4
->ex_entry
= pe4
->pt_entry
+ 1;
1117 partition_set_changed(cxt
, 4, 1);
1118 cxt
->label
->nparts_max
= 5;
1121 fdisk_label_set_changed(cxt
->label
, 1);
1125 static int add_logical(struct fdisk_context
*cxt
, struct fdisk_partition
*pa
)
1127 struct dos_partition
*p4
= self_partition(cxt
, 4);
1132 if (cxt
->label
->nparts_max
> 5 || !is_cleared_partition(p4
)) {
1133 struct pte
*pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
1135 pe
->sectorbuffer
= calloc(1, cxt
->sector_size
);
1136 if (!pe
->sectorbuffer
)
1138 DBG(LABEL
, ul_debug("DOS: add logical, sector buffer %p", pe
->sectorbuffer
));
1139 pe
->private_sectorbuffer
= 1;
1140 pe
->pt_entry
= mbr_get_partition(pe
->sectorbuffer
, 0);
1141 pe
->ex_entry
= pe
->pt_entry
+ 1;
1144 partition_set_changed(cxt
, cxt
->label
->nparts_max
, 1);
1145 cxt
->label
->nparts_max
++;
1147 fdisk_info(cxt
, _("Adding logical partition %zu"),
1148 cxt
->label
->nparts_max
);
1149 return add_partition(cxt
, cxt
->label
->nparts_max
- 1, pa
);
1152 static void check(struct fdisk_context
*cxt
, size_t n
,
1153 unsigned int h
, unsigned int s
, unsigned int c
,
1156 unsigned int total
, real_s
, real_c
;
1158 if (!is_dos_compatible(cxt
))
1161 real_s
= sector(s
) - 1;
1162 real_c
= cylinder(s
, c
);
1163 total
= (real_c
* cxt
->geom
.heads
+ h
) * cxt
->geom
.sectors
+ real_s
;
1166 fdisk_warnx(cxt
, _("Partition %zu: contains sector 0"), n
);
1167 if (h
>= cxt
->geom
.heads
)
1168 fdisk_warnx(cxt
, _("Partition %zu: head %d greater than "
1169 "maximum %d"), n
, h
+ 1, cxt
->geom
.heads
);
1170 if (real_s
>= cxt
->geom
.sectors
)
1171 fdisk_warnx(cxt
, _("Partition %zu: sector %d greater than "
1172 "maximum %llu"), n
, s
, cxt
->geom
.sectors
);
1173 if (real_c
>= cxt
->geom
.cylinders
)
1174 fdisk_warnx(cxt
, _("Partition %zu: cylinder %d greater than "
1177 cxt
->geom
.cylinders
);
1179 if (cxt
->geom
.cylinders
<= 1024 && start
!= total
)
1180 fdisk_warnx(cxt
, _("Partition %zu: previous sectors %u "
1181 "disagrees with total %u"), n
, start
, total
);
1184 /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1185 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1186 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1187 * Lubkin Oct. 1991). */
1190 long2chs(struct fdisk_context
*cxt
, unsigned long ls
,
1191 unsigned int *c
, unsigned int *h
, unsigned int *s
) {
1192 int spc
= cxt
->geom
.heads
* cxt
->geom
.sectors
;
1196 *h
= ls
/ cxt
->geom
.sectors
;
1197 *s
= ls
% cxt
->geom
.sectors
+ 1; /* sectors count from 1 */
1200 static void check_consistency(struct fdisk_context
*cxt
, struct dos_partition
*p
,
1203 unsigned int pbc
, pbh
, pbs
; /* physical beginning c, h, s */
1204 unsigned int pec
, peh
, pes
; /* physical ending c, h, s */
1205 unsigned int lbc
, lbh
, lbs
; /* logical beginning c, h, s */
1206 unsigned int lec
, leh
, les
; /* logical ending c, h, s */
1208 if (!is_dos_compatible(cxt
))
1211 if (!cxt
->geom
.heads
|| !cxt
->geom
.sectors
|| (partition
>= 4))
1212 return; /* do not check extended partitions */
1214 /* physical beginning c, h, s */
1215 pbc
= (p
->bc
& 0xff) | ((p
->bs
<< 2) & 0x300);
1219 /* physical ending c, h, s */
1220 pec
= (p
->ec
& 0xff) | ((p
->es
<< 2) & 0x300);
1224 /* compute logical beginning (c, h, s) */
1225 long2chs(cxt
, dos_partition_get_start(p
), &lbc
, &lbh
, &lbs
);
1227 /* compute logical ending (c, h, s) */
1228 long2chs(cxt
, dos_partition_get_start(p
) + dos_partition_get_size(p
) - 1, &lec
, &leh
, &les
);
1230 /* Same physical / logical beginning? */
1231 if (cxt
->geom
.cylinders
<= 1024
1232 && (pbc
!= lbc
|| pbh
!= lbh
|| pbs
!= lbs
)) {
1233 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1234 "beginnings (non-Linux?): "
1235 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1241 /* Same physical / logical ending? */
1242 if (cxt
->geom
.cylinders
<= 1024
1243 && (pec
!= lec
|| peh
!= leh
|| pes
!= les
)) {
1244 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1245 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1251 /* Ending on cylinder boundary? */
1252 if (peh
!= (cxt
->geom
.heads
- 1) || pes
!= cxt
->geom
.sectors
) {
1253 fdisk_warnx(cxt
, _("Partition %zu: does not end on "
1254 "cylinder boundary."),
1259 static int dos_verify_disklabel(struct fdisk_context
*cxt
)
1262 sector_t total
= 1, n_sectors
= cxt
->total_sectors
;
1263 unsigned long long first
[cxt
->label
->nparts_max
],
1264 last
[cxt
->label
->nparts_max
];
1265 struct dos_partition
*p
;
1266 struct fdisk_dos_label
*l
= self_label(cxt
);
1268 assert(fdisk_is_disklabel(cxt
, DOS
));
1270 fill_bounds(cxt
, first
, last
);
1271 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
1272 struct pte
*pe
= self_pte(cxt
, i
);
1274 p
= self_partition(cxt
, i
);
1275 if (is_used_partition(p
) && !IS_EXTENDED(p
->sys_ind
)) {
1276 check_consistency(cxt
, p
, i
);
1277 fdisk_warn_alignment(cxt
, get_abs_partition_start(pe
), i
);
1278 if (get_abs_partition_start(pe
) < first
[i
])
1280 "Partition %zu: bad start-of-data."),
1283 check(cxt
, i
+ 1, p
->eh
, p
->es
, p
->ec
, last
[i
]);
1284 total
+= last
[i
] + 1 - first
[i
];
1287 total
+= get_abs_partition_start(pe
) - 1;
1289 for (j
= 0; j
< i
; j
++) {
1290 if ((first
[i
] >= first
[j
] && first
[i
] <= last
[j
])
1291 || ((last
[i
] <= last
[j
] && last
[i
] >= first
[j
]))) {
1293 fdisk_warnx(cxt
, _("Partition %zu: "
1294 "overlaps partition %zu."),
1297 total
+= first
[i
] >= first
[j
] ?
1298 first
[i
] : first
[j
];
1299 total
-= last
[i
] <= last
[j
] ?
1306 if (l
->ext_offset
) {
1308 struct pte
*ext_pe
= self_pte(cxt
, l
->ext_index
);
1310 e_last
= get_abs_partition_end(ext_pe
);
1312 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1314 p
= self_partition(cxt
, i
);
1317 if (i
!= 4 || i
+ 1 < cxt
->label
->nparts_max
)
1319 _("Partition %zu: empty."),
1321 } else if (first
[i
] < l
->ext_offset
1322 || last
[i
] > e_last
) {
1324 fdisk_warnx(cxt
, _("Logical partition %zu: "
1325 "not entirely in partition %zu."),
1326 i
+ 1, l
->ext_index
+ 1);
1331 if (total
> n_sectors
)
1332 fdisk_warnx(cxt
, _("Total allocated sectors %llu greater "
1333 "than the maximum %llu."), total
, n_sectors
);
1334 else if (total
< n_sectors
)
1335 fdisk_warnx(cxt
, _("Remaining %lld unallocated %ld-byte "
1336 "sectors."), n_sectors
- total
, cxt
->sector_size
);
1342 * Ask the user for new partition type information (logical, extended).
1343 * This function calls the actual partition adding logic - add_partition.
1347 static int dos_add_partition(struct fdisk_context
*cxt
,
1348 struct fdisk_partition
*pa
)
1350 size_t i
, free_primary
= 0;
1352 struct fdisk_dos_label
*l
;
1357 assert(fdisk_is_disklabel(cxt
, DOS
));
1359 l
= self_label(cxt
);
1360 ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1362 /* pa specifies start within extended partition, add logical */
1363 if (pa
&& pa
->start
&& ext_pe
1364 && pa
->start
>= l
->ext_offset
1365 && pa
->start
<= get_abs_partition_end(ext_pe
)) {
1366 rc
= add_logical(cxt
, pa
);
1369 /* pa specifies start, but outside extended partition */
1370 } else if (pa
&& pa
->start
&& l
->ext_offset
) {
1373 j
= get_partition_unused_primary(cxt
, pa
);
1375 rc
= add_partition(cxt
, j
, pa
);
1381 for (i
= 0; i
< 4; i
++) {
1382 struct dos_partition
*p
= self_partition(cxt
, i
);
1383 free_primary
+= !is_used_partition(p
);
1386 if (!free_primary
&& cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1387 fdisk_info(cxt
, _("The maximum number of partitions has "
1393 if (!free_primary
) {
1394 if (l
->ext_offset
) {
1395 fdisk_info(cxt
, _("All primary partitions are in use."));
1396 rc
= add_logical(cxt
, pa
);
1398 fdisk_info(cxt
, _("If you want to create more than "
1399 "four partitions, you must replace a "
1400 "primary partition with an extended "
1401 "partition first."));
1403 } else if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1406 fdisk_info(cxt
, _("All logical partitions are in use. "
1407 "Adding a primary partition."));
1408 j
= get_partition_unused_primary(cxt
, pa
);
1410 rc
= add_partition(cxt
, j
, pa
);
1413 struct fdisk_ask
*ask
;
1416 ask
= fdisk_new_ask();
1419 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_MENU
);
1420 fdisk_ask_set_query(ask
, _("Partition type"));
1421 fdisk_ask_menu_set_default(ask
, free_primary
== 1
1422 && !l
->ext_offset
? 'e' : 'p');
1423 snprintf(hint
, sizeof(hint
),
1424 _("%zu primary, %d extended, %zu free"),
1425 4 - (l
->ext_offset
? 1 : 0) - free_primary
,
1426 l
->ext_offset
? 1 : 0,
1429 fdisk_ask_menu_add_item(ask
, 'p', _("primary"), hint
);
1431 fdisk_ask_menu_add_item(ask
, 'e', _("extended"), _("container for logical partitions"));
1433 fdisk_ask_menu_add_item(ask
, 'l', _("logical"), _("numbered from 5"));
1435 rc
= fdisk_do_ask(cxt
, ask
);
1438 fdisk_ask_menu_get_result(ask
, &c
);
1439 fdisk_free_ask(ask
);
1442 int j
= get_partition_unused_primary(cxt
, pa
);
1444 rc
= add_partition(cxt
, j
, pa
);
1446 } else if (c
== 'l' && l
->ext_offset
) {
1447 rc
= add_logical(cxt
, pa
);
1449 } else if (c
== 'e' && !l
->ext_offset
) {
1450 int j
= get_partition_unused_primary(cxt
, pa
);
1452 struct fdisk_partition xpa
= { .type
= NULL
};
1453 struct fdisk_parttype
*t
;
1455 t
= fdisk_get_parttype_from_code(cxt
,
1456 MBR_DOS_EXTENDED_PARTITION
);
1459 fdisk_partition_set_type(pa
, t
);
1460 rc
= add_partition(cxt
, j
, pa
);
1464 fdisk_warnx(cxt
, _("Invalid partition type `%c'."), c
);
1468 cxt
->label
->nparts_cur
++;
1472 static int write_sector(struct fdisk_context
*cxt
, sector_t secno
,
1477 rc
= seek_sector(cxt
, secno
);
1479 fdisk_warn(cxt
, _("Cannot write sector %jd: seek failed"),
1484 DBG(LABEL
, ul_debug("DOS: writting to sector %ju", (uintmax_t) secno
));
1486 if (write(cxt
->dev_fd
, buf
, cxt
->sector_size
) != (ssize_t
) cxt
->sector_size
)
1491 static int dos_write_disklabel(struct fdisk_context
*cxt
)
1493 struct fdisk_dos_label
*l
= self_label(cxt
);
1495 int rc
= 0, mbr_changed
= 0;
1499 assert(fdisk_is_disklabel(cxt
, DOS
));
1501 mbr_changed
= l
->non_pt_changed
;
1503 /* MBR (primary partitions) */
1505 for (i
= 0; i
< 4; i
++) {
1506 struct pte
*pe
= self_pte(cxt
, i
);
1512 mbr_set_magic(cxt
->firstsector
);
1513 rc
= write_sector(cxt
, 0, cxt
->firstsector
);
1518 if (cxt
->label
->nparts_max
<= 4 && l
->ext_offset
) {
1519 /* we have empty extended partition, check if the partition has
1520 * been modified and then cleanup possible remaining EBR */
1521 struct pte
*pe
= self_pte(cxt
, l
->ext_index
);
1522 unsigned char empty
[512] = { 0 };
1523 sector_t off
= pe
? get_abs_partition_start(pe
) : 0;
1525 if (off
&& pe
->changed
) {
1526 mbr_set_magic(empty
);
1527 write_sector(cxt
, off
, empty
);
1531 /* EBR (logical partitions) */
1532 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1533 struct pte
*pe
= self_pte(cxt
, i
);
1536 mbr_set_magic(pe
->sectorbuffer
);
1537 rc
= write_sector(cxt
, pe
->offset
, pe
->sectorbuffer
);
1547 static int dos_locate_disklabel(struct fdisk_context
*cxt
, int n
,
1548 const char **name
, off_t
*offset
, size_t *size
)
1563 /* extended partitions */
1564 if (n
- 1 + 4 < cxt
->label
->nparts_max
) {
1565 struct pte
*pe
= self_pte(cxt
, n
- 1 + 4);
1567 assert(pe
->private_sectorbuffer
);
1570 *offset
= pe
->offset
* cxt
->sector_size
;
1580 static int dos_set_parttype(
1581 struct fdisk_context
*cxt
,
1583 struct fdisk_parttype
*t
)
1585 struct dos_partition
*p
;
1589 assert(fdisk_is_disklabel(cxt
, DOS
));
1591 if (partnum
>= cxt
->label
->nparts_max
|| !t
|| t
->type
> UINT8_MAX
)
1594 p
= self_partition(cxt
, partnum
);
1595 if (t
->type
== p
->sys_ind
)
1598 if (IS_EXTENDED(p
->sys_ind
) || IS_EXTENDED(t
->type
)) {
1599 fdisk_warnx(cxt
, _("You cannot change a partition into an "
1600 "extended one or vice versa. Delete it first."));
1604 if (is_dos_partition(t
->type
) || is_dos_partition(p
->sys_ind
))
1605 fdisk_info(cxt
, _("If you have created or modified any DOS 6.x "
1606 "partitions, please see the fdisk documentation for additional "
1610 fdisk_warnx(cxt
, _("Type 0 means free space to many systems. "
1611 "Having partitions of type 0 is probably unwise."));
1612 p
->sys_ind
= t
->type
;
1614 partition_set_changed(cxt
, partnum
, 1);
1619 * Check whether partition entries are ordered by their starting positions.
1620 * Return 0 if OK. Return i if partition i should have been earlier.
1621 * Two separate checks: primary and logical partitions.
1623 static int wrong_p_order(struct fdisk_context
*cxt
, size_t *prev
)
1625 size_t last_p_start_pos
= 0, p_start_pos
;
1626 size_t i
, last_i
= 0;
1628 for (i
= 0 ; i
< cxt
->label
->nparts_max
; i
++) {
1630 struct pte
*pe
= self_pte(cxt
, i
);
1631 struct dos_partition
*p
= pe
->pt_entry
;
1635 last_p_start_pos
= 0;
1637 if (is_used_partition(p
)) {
1638 p_start_pos
= get_abs_partition_start(pe
);
1640 if (last_p_start_pos
> p_start_pos
) {
1646 last_p_start_pos
= p_start_pos
;
1653 static int dos_list_disklabel(struct fdisk_context
*cxt
)
1657 assert(fdisk_is_disklabel(cxt
, DOS
));
1662 static int dos_get_partition(struct fdisk_context
*cxt
, size_t n
,
1663 struct fdisk_partition
*pa
)
1665 struct dos_partition
*p
;
1667 struct fdisk_dos_label
*lb
;
1672 assert(fdisk_is_disklabel(cxt
, DOS
));
1674 lb
= self_label(cxt
);
1675 pe
= self_pte(cxt
, n
);
1677 pa
->used
= !is_cleared_partition(p
);
1681 pa
->type
= dos_partition_parttype(cxt
, p
);
1682 pa
->boot
= p
->boot_ind
? p
->boot_ind
== ACTIVE_FLAG
? '*' : '?' : ' ';
1683 pa
->start
= get_abs_partition_start(pe
);
1684 pa
->end
= get_abs_partition_end(pe
);
1685 pa
->size
= dos_partition_get_size(p
);
1686 pa
->container
= lb
->ext_offset
&& n
== lb
->ext_index
;
1689 pa
->parent_partno
= lb
->ext_index
;
1691 if (asprintf(&pa
->attrs
, "%02x", p
->boot_ind
) < 0)
1695 if (asprintf(&pa
->start_addr
, "%d/%d/%d",
1696 cylinder(p
->bs
, p
->bc
),
1702 if (asprintf(&pa
->end_addr
, "%d/%d/%d",
1703 cylinder(p
->es
, p
->ec
),
1711 static void print_chain_of_logicals(struct fdisk_context
*cxt
)
1714 struct fdisk_dos_label
*l
= self_label(cxt
);
1716 fputc('\n', stdout
);
1718 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1719 struct pte
*pe
= self_pte(cxt
, i
);
1721 printf("#%02zu EBR [%10ju], "
1722 "data[start=%10ju (%10ju), size=%10ju], "
1723 "link[start=%10ju (%10ju), size=%10ju]\n",
1724 i
, (uintmax_t) pe
->offset
,
1726 (uintmax_t) dos_partition_get_start(pe
->pt_entry
),
1727 (uintmax_t) get_abs_partition_start(pe
),
1728 (uintmax_t) dos_partition_get_size(pe
->pt_entry
),
1730 (uintmax_t) dos_partition_get_start(pe
->ex_entry
),
1731 (uintmax_t) l
->ext_offset
+ dos_partition_get_start(pe
->ex_entry
),
1732 (uintmax_t) dos_partition_get_size(pe
->ex_entry
));
1736 static int cmp_ebr_offsets(const void *a
, const void *b
)
1738 struct pte
*ae
= (struct pte
*) a
,
1739 *be
= (struct pte
*) b
;
1741 if (ae
->offset
== 0 && be
->offset
== 0)
1743 if (ae
->offset
== 0)
1745 if (be
->offset
== 0)
1748 return ae
->offset
- be
->offset
;
1752 * Fix the chain of logicals.
1754 * The function does not modify data partitions within EBR tables
1755 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
1756 * (pte->ex_entry) between EBR tables.
1759 static void fix_chain_of_logicals(struct fdisk_context
*cxt
)
1761 struct fdisk_dos_label
*l
= self_label(cxt
);
1764 DBG(LABEL
, print_chain_of_logicals(cxt
));
1766 /* Sort chain by EBR offsets */
1767 qsort(&l
->ptes
[4], cxt
->label
->nparts_max
- 4, sizeof(struct pte
),
1771 /* Sort data partitions by start */
1772 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
1773 struct pte
*cur
= self_pte(cxt
, i
),
1774 *nxt
= self_pte(cxt
, i
+ 1);
1776 if (get_abs_partition_start(cur
) >
1777 get_abs_partition_start(nxt
)) {
1779 struct dos_partition tmp
= *cur
->pt_entry
;
1780 sector_t cur_start
= get_abs_partition_start(cur
),
1781 nxt_start
= get_abs_partition_start(nxt
);
1783 /* swap data partitions */
1784 *cur
->pt_entry
= *nxt
->pt_entry
;
1785 *nxt
->pt_entry
= tmp
;
1787 /* Recount starts according to EBR offsets, the absolute
1788 * address tas to be still the same! */
1789 dos_partition_set_start(cur
->pt_entry
, nxt_start
- cur
->offset
);
1790 dos_partition_set_start(nxt
->pt_entry
, cur_start
- nxt
->offset
);
1792 partition_set_changed(cxt
, i
, 1);
1793 partition_set_changed(cxt
, i
+ 1, 1);
1798 /* Update EBR links */
1799 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
1800 struct pte
*cur
= self_pte(cxt
, i
),
1801 *nxt
= self_pte(cxt
, i
+ 1);
1803 sector_t noff
= nxt
->offset
- l
->ext_offset
,
1804 ooff
= dos_partition_get_start(cur
->ex_entry
);
1809 DBG(LABEL
, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
1810 (uintmax_t) cur
->offset
,
1811 (uintmax_t) ooff
, (uintmax_t) noff
));
1813 set_partition(cxt
, i
, 1, nxt
->offset
,
1814 get_abs_partition_end(nxt
), MBR_DOS_EXTENDED_PARTITION
);
1816 if (i
+ 1 == cxt
->label
->nparts_max
- 1) {
1817 clear_partition(nxt
->ex_entry
);
1818 partition_set_changed(cxt
, i
+ 1, 1);
1822 DBG(LABEL
, print_chain_of_logicals(cxt
));
1825 static int dos_reorder(struct fdisk_context
*cxt
)
1827 struct pte
*pei
, *pek
;
1830 if (!wrong_p_order(cxt
, NULL
)) {
1831 fdisk_info(cxt
, _("Nothing to do. Ordering is correct already."));
1835 while ((i
= wrong_p_order(cxt
, &k
)) != 0 && i
< 4) {
1836 /* partition i should have come earlier, move it */
1837 /* We have to move data in the MBR */
1838 struct dos_partition
*pi
, *pk
, *pe
, pbuf
;
1839 pei
= self_pte(cxt
, i
);
1840 pek
= self_pte(cxt
, k
);
1843 pei
->ex_entry
= pek
->ex_entry
;
1849 memmove(&pbuf
, pi
, sizeof(struct dos_partition
));
1850 memmove(pi
, pk
, sizeof(struct dos_partition
));
1851 memmove(pk
, &pbuf
, sizeof(struct dos_partition
));
1853 partition_set_changed(cxt
, i
, 1);
1854 partition_set_changed(cxt
, k
, 1);
1858 fix_chain_of_logicals(cxt
);
1860 fdisk_info(cxt
, _("Done."));
1864 int fdisk_dos_move_begin(struct fdisk_context
*cxt
, size_t i
)
1867 struct dos_partition
*p
;
1868 unsigned int new, free_start
, curr_start
, last
;
1874 assert(fdisk_is_disklabel(cxt
, DOS
));
1876 pe
= self_pte(cxt
, i
);
1879 if (!is_used_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
1880 fdisk_warnx(cxt
, _("Partition %zu: no data area."), i
+ 1);
1884 /* the default start is at the second sector of the disk or at the
1885 * second sector of the extended partition
1887 free_start
= pe
->offset
? pe
->offset
+ 1 : 1;
1889 curr_start
= get_abs_partition_start(pe
);
1891 /* look for a free space before the current start of the partition */
1892 for (x
= 0; x
< cxt
->label
->nparts_max
; x
++) {
1894 struct pte
*prev_pe
= self_pte(cxt
, x
);
1895 struct dos_partition
*prev_p
= prev_pe
->pt_entry
;
1899 end
= get_abs_partition_start(prev_pe
)
1900 + dos_partition_get_size(prev_p
);
1902 if (is_used_partition(prev_p
) &&
1903 end
> free_start
&& end
<= curr_start
)
1907 last
= get_abs_partition_end(pe
);
1909 rc
= fdisk_ask_number(cxt
, free_start
, curr_start
, last
,
1910 _("New beginning of data"), &res
);
1914 new = res
- pe
->offset
;
1916 if (new != dos_partition_get_size(p
)) {
1917 unsigned int sects
= dos_partition_get_size(p
)
1918 + dos_partition_get_start(p
) - new;
1920 dos_partition_set_size(p
, sects
);
1921 dos_partition_set_start(p
, new);
1923 partition_set_changed(cxt
, i
, 1);
1929 static int dos_partition_is_used(
1930 struct fdisk_context
*cxt
,
1933 struct dos_partition
*p
;
1937 assert(fdisk_is_disklabel(cxt
, DOS
));
1939 if (i
>= cxt
->label
->nparts_max
)
1942 p
= self_partition(cxt
, i
);
1944 return p
&& !is_cleared_partition(p
);
1947 static int dos_toggle_partition_flag(
1948 struct fdisk_context
*cxt
,
1952 struct dos_partition
*p
;
1956 assert(fdisk_is_disklabel(cxt
, DOS
));
1958 if (i
>= cxt
->label
->nparts_max
)
1961 p
= self_partition(cxt
, i
);
1964 case DOS_FLAG_ACTIVE
:
1965 if (IS_EXTENDED(p
->sys_ind
) && !p
->boot_ind
)
1966 fdisk_warnx(cxt
, _("Partition %zu: is an extended "
1967 "partition."), i
+ 1);
1969 p
->boot_ind
= (p
->boot_ind
? 0 : ACTIVE_FLAG
);
1970 partition_set_changed(cxt
, i
, 1);
1971 fdisk_sinfo(cxt
, FDISK_INFO_SUCCESS
,
1973 _("The bootable flag on partition %zu is enabled now.") :
1974 _("The bootable flag on partition %zu is disabled now."),
1984 static const struct fdisk_field dos_fields
[] =
1987 { FDISK_FIELD_DEVICE
, N_("Device"), 10, 0 },
1988 { FDISK_FIELD_BOOT
, N_("Boot"), 1, 0 },
1989 { FDISK_FIELD_START
, N_("Start"), 5, FDISK_FIELDFL_NUMBER
},
1990 { FDISK_FIELD_END
, N_("End"), 5, FDISK_FIELDFL_NUMBER
},
1991 { FDISK_FIELD_SECTORS
, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER
},
1992 { FDISK_FIELD_CYLINDERS
,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER
},
1993 { FDISK_FIELD_SIZE
, N_("Size"), 5, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_EYECANDY
},
1994 { FDISK_FIELD_TYPEID
, N_("Id"), 2, FDISK_FIELDFL_NUMBER
},
1995 { FDISK_FIELD_TYPE
, N_("Type"), 0.1, 0 },
1998 { FDISK_FIELD_SADDR
, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
1999 { FDISK_FIELD_EADDR
, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2000 { FDISK_FIELD_ATTR
, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
}
2004 static const struct fdisk_label_operations dos_operations
=
2006 .probe
= dos_probe_label
,
2007 .write
= dos_write_disklabel
,
2008 .verify
= dos_verify_disklabel
,
2009 .create
= dos_create_disklabel
,
2010 .locate
= dos_locate_disklabel
,
2011 .list
= dos_list_disklabel
,
2012 .reorder
= dos_reorder
,
2013 .get_id
= dos_get_disklabel_id
,
2014 .set_id
= dos_set_disklabel_id
,
2016 .get_part
= dos_get_partition
,
2017 .add_part
= dos_add_partition
,
2019 .part_delete
= dos_delete_partition
,
2020 .part_set_type
= dos_set_parttype
,
2022 .part_toggle_flag
= dos_toggle_partition_flag
,
2023 .part_is_used
= dos_partition_is_used
,
2025 .reset_alignment
= dos_reset_alignment
,
2027 .deinit
= dos_deinit
,
2031 * allocates DOS in-memory stuff
2033 struct fdisk_label
*fdisk_new_dos_label(struct fdisk_context
*cxt
)
2035 struct fdisk_label
*lb
;
2036 struct fdisk_dos_label
*dos
;
2040 dos
= calloc(1, sizeof(*dos
));
2044 /* initialize generic part of the driver */
2045 lb
= (struct fdisk_label
*) dos
;
2047 lb
->id
= FDISK_DISKLABEL_DOS
;
2048 lb
->op
= &dos_operations
;
2049 lb
->parttypes
= dos_parttypes
;
2050 lb
->nparttypes
= ARRAY_SIZE(dos_parttypes
);
2051 lb
->fields
= dos_fields
;
2052 lb
->nfields
= ARRAY_SIZE(dos_fields
);
2058 * Public label specific functions
2061 int fdisk_dos_enable_compatible(struct fdisk_label
*lb
, int enable
)
2063 struct fdisk_dos_label
*dos
= (struct fdisk_dos_label
*) lb
;
2068 dos
->compatible
= enable
;
2070 lb
->flags
|= FDISK_LABEL_FL_REQUIRE_GEOMETRY
;
2074 int fdisk_dos_is_compatible(struct fdisk_label
*lb
)
2076 return ((struct fdisk_dos_label
*) lb
)->compatible
;