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.
10 #include "randutils.h"
18 #define MAXIMUM_PARTS 60
19 #define ACTIVE_FLAG 0x80
24 * @short_description: disk label specific functions
29 #define IS_EXTENDED(i) \
30 ((i) == MBR_DOS_EXTENDED_PARTITION \
31 || (i) == MBR_W95_EXTENDED_PARTITION \
32 || (i) == MBR_LINUX_EXTENDED_PARTITION)
35 * per partition table entry data
37 * The four primary partitions have the same sectorbuffer
38 * and have NULL ex_entry.
40 * Each logical partition table entry has two pointers, one for the
41 * partition and one link to the next one.
44 struct dos_partition
*pt_entry
; /* on-disk MBR entry */
45 struct dos_partition
*ex_entry
; /* on-disk EBR entry */
46 fdisk_sector_t offset
; /* disk sector number */
47 unsigned char *sectorbuffer
; /* disk sector contents */
49 unsigned int changed
: 1,
50 private_sectorbuffer
: 1;
54 * in-memory fdisk GPT stuff
56 struct fdisk_dos_label
{
57 struct fdisk_label head
; /* generic part */
59 struct pte ptes
[MAXIMUM_PARTS
]; /* partition */
60 fdisk_sector_t ext_offset
; /* start of the ext.partition */
61 size_t ext_index
; /* ext.partition index (if ext_offset is set) */
62 unsigned int compatible
: 1, /* is DOS compatible? */
63 non_pt_changed
: 1; /* MBR, but no PT changed */
69 static struct fdisk_parttype dos_parttypes
[] = {
70 #include "pt-mbr-partnames.h"
73 static const struct fdisk_shortcut dos_parttype_cuts
[] =
75 { .shortcut
= "L", .alias
= "linux", .data
= "83" },
76 { .shortcut
= "S", .alias
= "swap", .data
= "82" },
77 { .shortcut
= "E", .alias
= "extended", .data
= "05", .deprecated
= 1 }, /* collision with 0x0e type */
78 { .shortcut
= "Ex",.alias
= "extended", .data
= "05" }, /* MBR extended */
79 { .shortcut
= "U", .alias
= "uefi", .data
= "EF" }, /* UEFI system */
80 { .shortcut
= "R", .alias
= "raid", .data
= "FD" }, /* Linux RAID */
81 { .shortcut
= "V", .alias
= "lvm", .data
= "8E" }, /* LVM */
82 { .shortcut
= "X", .alias
= "linuxex", .data
= "85" } /* Linux extended */
85 #define set_hsc(h,s,c,sector) { \
86 s = sector % cxt->geom.sectors + 1; \
87 sector /= cxt->geom.sectors; \
88 h = sector % cxt->geom.heads; \
89 sector /= cxt->geom.heads; \
91 s |= (sector >> 2) & 0xc0; \
95 #define sector(s) ((s) & 0x3f)
96 #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
98 #define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
100 #define is_dos_compatible(_x) \
101 (fdisk_is_label(_x, DOS) && \
102 fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
104 #define cround(c, n) fdisk_cround(c, n)
107 static inline struct fdisk_dos_label
*self_label(struct fdisk_context
*cxt
)
111 assert(fdisk_is_label(cxt
, DOS
));
113 return (struct fdisk_dos_label
*) cxt
->label
;
116 static inline struct pte
*self_pte(struct fdisk_context
*cxt
, size_t i
)
118 struct fdisk_dos_label
*l
= self_label(cxt
);
120 if (i
>= ARRAY_SIZE(l
->ptes
))
126 static inline struct dos_partition
*self_partition(
127 struct fdisk_context
*cxt
,
130 struct pte
*pe
= self_pte(cxt
, i
);
131 return pe
? pe
->pt_entry
: NULL
;
134 struct dos_partition
*fdisk_dos_get_partition(
135 struct fdisk_context
*cxt
,
140 assert(fdisk_is_label(cxt
, DOS
));
142 return self_partition(cxt
, i
);
145 static struct fdisk_parttype
*dos_partition_parttype(
146 struct fdisk_context
*cxt
,
147 struct dos_partition
*p
)
149 struct fdisk_parttype
*t
150 = fdisk_label_get_parttype_from_code(cxt
->label
, p
->sys_ind
);
151 return t
? : fdisk_new_unknown_parttype(p
->sys_ind
, NULL
);
155 * Linux kernel cares about partition size only. Things like
156 * partition type or so are completely irrelevant -- kzak Nov-2013
158 static int is_used_partition(struct dos_partition
*p
)
160 return p
&& dos_partition_get_size(p
) != 0;
163 static void partition_set_changed(
164 struct fdisk_context
*cxt
,
168 struct pte
*pe
= self_pte(cxt
, i
);
173 DBG(LABEL
, ul_debug("DOS: setting %zu partition to %s", i
,
174 changed
? "changed" : "unchanged"));
176 pe
->changed
= changed
? 1 : 0;
178 fdisk_label_set_changed(cxt
->label
, 1);
181 static fdisk_sector_t
get_abs_partition_start(struct pte
*pe
)
184 assert(pe
->pt_entry
);
186 return pe
->offset
+ dos_partition_get_start(pe
->pt_entry
);
189 static fdisk_sector_t
get_abs_partition_end(struct pte
*pe
)
194 assert(pe
->pt_entry
);
196 size
= dos_partition_get_size(pe
->pt_entry
);
197 return get_abs_partition_start(pe
) + size
- (size
? 1 : 0);
200 static int is_cleared_partition(struct dos_partition
*p
)
202 return !(!p
|| p
->boot_ind
|| p
->bh
|| p
->bs
|| p
->bc
||
203 p
->sys_ind
|| p
->eh
|| p
->es
|| p
->ec
||
204 dos_partition_get_start(p
) || dos_partition_get_size(p
));
207 static int get_partition_unused_primary(struct fdisk_context
*cxt
,
208 struct fdisk_partition
*pa
,
218 org
= cxt
->label
->nparts_max
;
220 cxt
->label
->nparts_max
= 4;
221 rc
= fdisk_partition_next_partno(pa
, cxt
, &n
);
222 cxt
->label
->nparts_max
= org
;
225 fdisk_info(cxt
, _("All primary partitions have been defined already."));
227 } else if (rc
== -ERANGE
) {
228 fdisk_warnx(cxt
, _("Primary partition not available."));
235 static int seek_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
)
237 off_t offset
= (off_t
) secno
* cxt
->sector_size
;
239 return lseek(cxt
->dev_fd
, offset
, SEEK_SET
) == (off_t
) -1 ? -errno
: 0;
242 static int read_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
,
245 int rc
= seek_sector(cxt
, secno
);
251 r
= read(cxt
->dev_fd
, buf
, cxt
->sector_size
);
252 if (r
== (ssize_t
) cxt
->sector_size
)
259 /* Allocate a buffer and read a partition table sector */
260 static int read_pte(struct fdisk_context
*cxt
, size_t pno
, fdisk_sector_t offset
)
264 struct pte
*pe
= self_pte(cxt
, pno
);
269 buf
= calloc(1, cxt
->sector_size
);
273 DBG(LABEL
, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
274 pno
, (uintmax_t) offset
, buf
));
277 pe
->sectorbuffer
= buf
;
278 pe
->private_sectorbuffer
= 1;
280 rc
= read_sector(cxt
, offset
, pe
->sectorbuffer
);
282 fdisk_warn(cxt
, _("Failed to read extended partition table "
283 "(offset=%ju)"), (uintmax_t) offset
);
288 pe
->pt_entry
= pe
->ex_entry
= NULL
;
293 static void clear_partition(struct dos_partition
*p
)
305 dos_partition_set_start(p
,0);
306 dos_partition_set_size(p
,0);
309 static void dos_init(struct fdisk_context
*cxt
)
311 struct fdisk_dos_label
*l
= self_label(cxt
);
316 assert(fdisk_is_label(cxt
, DOS
));
318 DBG(LABEL
, ul_debug("DOS: initialize, first sector buffer %p", cxt
->firstsector
));
320 cxt
->label
->nparts_max
= 4; /* default, unlimited number of logical */
324 l
->non_pt_changed
= 0;
326 memset(l
->ptes
, 0, sizeof(l
->ptes
));
328 for (i
= 0; i
< 4; i
++) {
329 struct pte
*pe
= self_pte(cxt
, i
);
332 pe
->pt_entry
= mbr_get_partition(cxt
->firstsector
, i
);
335 pe
->sectorbuffer
= cxt
->firstsector
;
336 pe
->private_sectorbuffer
= 0;
339 DBG(LABEL
, ul_debug("DOS: initialize: #%zu start=%u size=%u sysid=%02x",
341 dos_partition_get_start(pe
->pt_entry
),
342 dos_partition_get_size(pe
->pt_entry
),
343 pe
->pt_entry
->sys_ind
));
346 if (fdisk_is_listonly(cxt
))
349 * Various warnings...
351 if (fdisk_missing_geometry(cxt
))
352 fdisk_warnx(cxt
, _("You can set geometry from the extra functions menu."));
354 if (is_dos_compatible(cxt
)) {
355 fdisk_warnx(cxt
, _("DOS-compatible mode is deprecated."));
357 if (cxt
->sector_size
!= cxt
->phy_sector_size
)
359 "The device presents a logical sector size that is smaller than "
360 "the physical sector size. Aligning to a physical sector (or optimal "
361 "I/O) size boundary is recommended, or performance may be impacted."));
364 if (fdisk_use_cylinders(cxt
))
365 fdisk_warnx(cxt
, _("Cylinders as display units are deprecated."));
367 if (cxt
->total_sectors
> UINT_MAX
) {
368 uint64_t bytes
= cxt
->total_sectors
* cxt
->sector_size
;
369 char *szstr
= size_to_human_string(SIZE_SUFFIX_SPACE
370 | SIZE_SUFFIX_3LETTER
, bytes
);
372 _("The size of this disk is %s (%ju bytes). DOS "
373 "partition table format cannot be used on drives for "
374 "volumes larger than %lu bytes for %lu-byte "
375 "sectors. Use GUID partition table format (GPT)."),
377 UINT_MAX
* cxt
->sector_size
,
383 /* callback called by libfdisk */
384 static void dos_deinit(struct fdisk_label
*lb
)
387 struct fdisk_dos_label
*l
= (struct fdisk_dos_label
*) lb
;
389 for (i
= 0; i
< ARRAY_SIZE(l
->ptes
); i
++) {
390 struct pte
*pe
= &l
->ptes
[i
];
392 if (pe
->private_sectorbuffer
&& pe
->sectorbuffer
) {
393 DBG(LABEL
, ul_debug("DOS: freeing pte %zu sector buffer %p",
394 i
, pe
->sectorbuffer
));
395 free(pe
->sectorbuffer
);
397 pe
->sectorbuffer
= NULL
;
398 pe
->private_sectorbuffer
= 0;
401 memset(l
->ptes
, 0, sizeof(l
->ptes
));
404 static void reset_pte(struct pte
*pe
)
408 if (pe
->private_sectorbuffer
) {
409 DBG(LABEL
, ul_debug(" --> freeing pte sector buffer %p",
411 free(pe
->sectorbuffer
);
413 memset(pe
, 0, sizeof(struct pte
));
416 static int delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
418 struct fdisk_dos_label
*l
;
420 struct dos_partition
*p
;
421 struct dos_partition
*q
;
425 assert(fdisk_is_label(cxt
, DOS
));
427 pe
= self_pte(cxt
, partnum
);
431 DBG(LABEL
, ul_debug("DOS: delete partition %zu (max=%zu)", partnum
,
432 cxt
->label
->nparts_max
));
438 /* Note that for the fifth partition (partnum == 4) we don't actually
439 decrement partitions. */
441 DBG(LABEL
, ul_debug("--> delete primary"));
442 if (IS_EXTENDED(p
->sys_ind
) && partnum
== l
->ext_index
) {
444 DBG(LABEL
, ul_debug(" --> delete extended"));
445 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
446 DBG(LABEL
, ul_debug(" --> delete logical #%zu", i
));
447 reset_pte(&l
->ptes
[i
]);
450 cxt
->label
->nparts_max
= 4;
451 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
455 partition_set_changed(cxt
, partnum
, 1);
457 } else if (!q
->sys_ind
&& partnum
> 4) {
458 DBG(LABEL
, ul_debug("--> delete logical [last in the chain]"));
459 reset_pte(&l
->ptes
[partnum
]);
460 --cxt
->label
->nparts_max
;
462 /* clear link to deleted partition */
463 clear_partition(l
->ptes
[partnum
].ex_entry
);
464 partition_set_changed(cxt
, partnum
, 1);
466 DBG(LABEL
, ul_debug("--> delete logical [move down]"));
468 DBG(LABEL
, ul_debug(" --> delete %zu logical link", partnum
));
469 p
= l
->ptes
[partnum
- 1].ex_entry
;
471 dos_partition_set_start(p
, dos_partition_get_start(q
));
472 dos_partition_set_size(p
, dos_partition_get_size(q
));
473 partition_set_changed(cxt
, partnum
- 1, 1);
475 } else if (cxt
->label
->nparts_max
> 5) {
476 DBG(LABEL
, ul_debug(" --> delete first logical link"));
477 pe
= &l
->ptes
[5]; /* second logical */
479 if (pe
->pt_entry
) /* prevent SEGFAULT */
480 dos_partition_set_start(pe
->pt_entry
,
481 get_abs_partition_start(pe
) -
483 pe
->offset
= l
->ext_offset
;
484 partition_set_changed(cxt
, 5, 1);
487 if (cxt
->label
->nparts_max
> 5) {
488 DBG(LABEL
, ul_debug(" --> move ptes"));
489 cxt
->label
->nparts_max
--;
490 reset_pte(&l
->ptes
[partnum
]);
491 while (partnum
< cxt
->label
->nparts_max
) {
492 DBG(LABEL
, ul_debug(" --> moving pte %zu <-- %zu", partnum
, partnum
+ 1));
493 l
->ptes
[partnum
] = l
->ptes
[partnum
+ 1];
496 memset(&l
->ptes
[partnum
], 0, sizeof(struct pte
));
498 DBG(LABEL
, ul_debug(" --> the only logical: clear only"));
499 clear_partition(l
->ptes
[partnum
].pt_entry
);
500 cxt
->label
->nparts_max
--;
503 DBG(LABEL
, ul_debug(" --> clear last logical"));
504 reset_pte(&l
->ptes
[partnum
]);
505 partition_set_changed(cxt
, l
->ext_index
, 1);
510 fdisk_label_set_changed(cxt
->label
, 1);
514 static int dos_delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
520 assert(fdisk_is_label(cxt
, DOS
));
522 pe
= self_pte(cxt
, partnum
);
523 if (!pe
|| !is_used_partition(pe
->pt_entry
))
526 return delete_partition(cxt
, partnum
);
529 static void read_extended(struct fdisk_context
*cxt
, size_t ext
)
532 struct pte
*pex
, *pe
;
533 struct dos_partition
*p
, *q
;
534 struct fdisk_dos_label
*l
= self_label(cxt
);
537 pex
= self_pte(cxt
, ext
);
539 DBG(LABEL
, ul_debug("DOS: uninitialized pointer to %zu pex", ext
));
542 pex
->ex_entry
= pex
->pt_entry
;
545 if (!dos_partition_get_start(p
)) {
546 fdisk_warnx(cxt
, _("Bad offset in primary extended partition."));
550 DBG(LABEL
, ul_debug("DOS: Reading extended %zu", ext
));
552 while (IS_EXTENDED (p
->sys_ind
)) {
553 if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
554 /* This is not a Linux restriction, but
555 this program uses arrays of size MAXIMUM_PARTS.
556 Do not try to `improve' this test. */
557 struct pte
*pre
= self_pte(cxt
,
558 cxt
->label
->nparts_max
- 1);
560 _("Omitting partitions after #%zu. They will be deleted "
561 "if you save this partition table."),
562 cxt
->label
->nparts_max
);
565 clear_partition(pre
->ex_entry
);
566 partition_set_changed(cxt
,
567 cxt
->label
->nparts_max
- 1, 1);
572 pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
576 if (read_pte(cxt
, cxt
->label
->nparts_max
, l
->ext_offset
+
577 dos_partition_get_start(p
)))
581 l
->ext_offset
= dos_partition_get_start(p
);
583 assert(pe
->sectorbuffer
);
584 q
= p
= mbr_get_partition(pe
->sectorbuffer
, 0);
586 for (i
= 0; i
< 4; i
++, p
++) {
587 if (!dos_partition_get_size(p
))
590 if (IS_EXTENDED (p
->sys_ind
)) {
593 "Extra link pointer in partition "
595 cxt
->label
->nparts_max
+ 1);
598 } else if (p
->sys_ind
) {
601 "Ignoring extra data in partition "
603 cxt
->label
->nparts_max
+ 1);
609 /* very strange code here... */
611 if (q
!= pe
->ex_entry
)
614 pe
->pt_entry
= q
+ 1;
617 if (q
!= pe
->pt_entry
)
620 pe
->ex_entry
= q
+ 1;
624 cxt
->label
->nparts_cur
= ++cxt
->label
->nparts_max
;
626 DBG(LABEL
, ul_debug("DOS: EBR[offset=%ju]: link: type=%x, start=%u, size=%u; "
627 " data: type=%x, start=%u, size=%u",
628 (uintmax_t) pe
->offset
,
629 pe
->ex_entry
->sys_ind
,
630 dos_partition_get_start(pe
->ex_entry
),
631 dos_partition_get_size(pe
->ex_entry
),
632 pe
->pt_entry
->sys_ind
,
633 dos_partition_get_start(pe
->pt_entry
),
634 dos_partition_get_size(pe
->pt_entry
)));
638 /* remove last empty EBR */
639 pe
= self_pte(cxt
, cxt
->label
->nparts_max
- 1);
641 is_cleared_partition(pe
->ex_entry
) &&
642 is_cleared_partition(pe
->pt_entry
)) {
643 DBG(LABEL
, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe
->offset
));
645 cxt
->label
->nparts_max
--;
646 cxt
->label
->nparts_cur
--;
649 /* remove empty links */
651 q
= self_partition(cxt
, 4);
652 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
653 p
= self_partition(cxt
, i
);
655 if (p
&& !dos_partition_get_size(p
) &&
656 (cxt
->label
->nparts_max
> 5 || (q
&& q
->sys_ind
))) {
657 fdisk_info(cxt
, _("omitting empty partition (%zu)"), i
+1);
658 delete_partition(cxt
, i
);
659 goto remove
; /* numbering changed */
663 DBG(LABEL
, ul_debug("DOS: nparts_max: %zu", cxt
->label
->nparts_max
));
666 static int dos_create_disklabel(struct fdisk_context
*cxt
)
670 struct fdisk_dos_label
*l
;
674 assert(fdisk_is_label(cxt
, DOS
));
676 DBG(LABEL
, ul_debug("DOS: creating new disklabel"));
680 const char *s
= fdisk_script_get_header(cxt
->script
, "label-id");
684 id
= strtoul(s
, &end
, 16);
685 if (!errno
&& end
&& s
< end
) {
687 DBG(LABEL
, ul_debug("DOS: re-use ID from script (0x%08x)", id
));
689 DBG(LABEL
, ul_debug("DOS: failed to parse label=id '%s'", s
));
693 /* random disk signature */
695 DBG(LABEL
, ul_debug("DOS: generate new ID"));
696 random_get_bytes(&id
, sizeof(id
));
699 if (fdisk_has_protected_bootbits(cxt
))
700 rc
= fdisk_init_firstsector_buffer(cxt
, 0, MBR_PT_BOOTBITS_SIZE
);
702 rc
= fdisk_init_firstsector_buffer(cxt
, 0, 0);
709 /* Generate an MBR ID for this disk */
710 mbr_set_id(cxt
->firstsector
, id
);
711 l
->non_pt_changed
= 1;
712 fdisk_label_set_changed(cxt
->label
, 1);
714 /* Put MBR signature */
715 mbr_set_magic(cxt
->firstsector
);
717 fdisk_info(cxt
, _("Created a new DOS disklabel with disk "
718 "identifier 0x%08x."), id
);
722 static int dos_set_disklabel_id(struct fdisk_context
*cxt
, const char *str
)
725 unsigned int id
, old
;
726 struct fdisk_dos_label
*l
;
731 assert(fdisk_is_label(cxt
, DOS
));
733 DBG(LABEL
, ul_debug("DOS: setting Id"));
736 old
= mbr_get_id(cxt
->firstsector
);
739 rc
= fdisk_ask_string(cxt
,
740 _("Enter the new disk identifier"), &buf
);
747 id
= strtoul(str
, &end
, 0);
748 if (errno
|| str
== end
|| (end
&& *end
)) {
749 fdisk_warnx(cxt
, _("Incorrect value."));
758 mbr_set_id(cxt
->firstsector
, id
);
759 l
->non_pt_changed
= 1;
760 fdisk_label_set_changed(cxt
->label
, 1);
762 fdisk_info(cxt
, _("Disk identifier changed from 0x%08x to 0x%08x."),
767 static void get_partition_table_geometry(struct fdisk_context
*cxt
,
768 unsigned int *ph
, unsigned int *ps
)
770 unsigned char *bufp
= cxt
->firstsector
;
771 struct dos_partition
*p
;
777 for (i
= 0; i
< 4; i
++) {
778 p
= mbr_get_partition(bufp
, i
);
779 if (p
->sys_ind
!= 0) {
786 } else if (hh
!= h
|| ss
!= s
)
791 if (!first
&& !bad
) {
796 DBG(LABEL
, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph
, *ps
));
799 static int dos_reset_alignment(struct fdisk_context
*cxt
)
803 assert(fdisk_is_label(cxt
, DOS
));
805 /* overwrite necessary stuff by DOS deprecated stuff */
806 if (is_dos_compatible(cxt
)) {
807 DBG(LABEL
, ul_debug("DOS: resetting alignment for DOS-compatible PT"));
808 if (cxt
->geom
.sectors
)
809 cxt
->first_lba
= cxt
->geom
.sectors
; /* usually 63 */
811 cxt
->grain
= cxt
->sector_size
; /* usually 512 */
817 /* TODO: move to include/pt-dos.h and share with libblkid */
818 #define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
819 #define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
821 static int dos_probe_label(struct fdisk_context
*cxt
)
824 unsigned int h
= 0, s
= 0;
828 assert(fdisk_is_label(cxt
, DOS
));
830 /* ignore disks with AIX magic number */
831 if (memcmp(cxt
->firstsector
, AIX_MAGIC_STRING
, AIX_MAGIC_STRLEN
) == 0)
834 if (!mbr_is_valid_magic(cxt
->firstsector
))
837 /* ignore disks with FAT */
838 if (cxt
->collision
&&
839 (strcmp(cxt
->collision
, "vfat") == 0 ||
840 strcmp(cxt
->collision
, "ntfs") == 0))
845 get_partition_table_geometry(cxt
, &h
, &s
);
848 cxt
->geom
.sectors
= s
;
850 if (fdisk_has_user_device_geometry(cxt
))
851 fdisk_apply_user_device_properties(cxt
);
854 for (i
= 0; i
< 4; i
++) {
855 struct pte
*pe
= self_pte(cxt
, i
);
858 if (is_used_partition(pe
->pt_entry
))
859 cxt
->label
->nparts_cur
++;
861 if (IS_EXTENDED (pe
->pt_entry
->sys_ind
)) {
862 if (cxt
->label
->nparts_max
!= 4)
864 "Ignoring extra extended partition %zu"),
867 read_extended(cxt
, i
);
871 for (i
= 3; i
< cxt
->label
->nparts_max
; i
++) {
872 struct pte
*pe
= self_pte(cxt
, i
);
873 struct fdisk_dos_label
*l
= self_label(cxt
);
876 if (!mbr_is_valid_magic(pe
->sectorbuffer
)) {
878 "Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
879 "be corrected by w(rite)."),
880 pe
->sectorbuffer
[510],
881 pe
->sectorbuffer
[511],
883 partition_set_changed(cxt
, i
, 1);
885 /* mark also extended as changed to update the first EBR
886 * in situation that there is no logical partitions at all */
887 partition_set_changed(cxt
, l
->ext_index
, 1);
894 static void set_partition(struct fdisk_context
*cxt
,
895 int i
, int doext
, fdisk_sector_t start
,
896 fdisk_sector_t stop
, int sysid
, int boot
)
898 struct pte
*pe
= self_pte(cxt
, i
);
899 struct dos_partition
*p
;
900 fdisk_sector_t offset
;
902 assert(!FDISK_IS_UNDEF(start
));
903 assert(!FDISK_IS_UNDEF(stop
));
907 struct fdisk_dos_label
*l
= self_label(cxt
);
909 offset
= l
->ext_offset
;
915 DBG(LABEL
, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, size=%zu, sysid=%02x",
916 i
, doext
? " [extended]" : "",
918 (size_t) (start
- offset
),
919 (size_t) (stop
- start
+ 1),
922 p
->boot_ind
= boot
? ACTIVE_FLAG
: 0;
924 dos_partition_set_start(p
, start
- offset
);
925 dos_partition_set_size(p
, stop
- start
+ 1);
927 if (is_dos_compatible(cxt
) && (start
/(cxt
->geom
.sectors
*cxt
->geom
.heads
) > 1023))
928 start
= cxt
->geom
.heads
*cxt
->geom
.sectors
*1024 - 1;
929 set_hsc(p
->bh
, p
->bs
, p
->bc
, start
);
930 if (is_dos_compatible(cxt
) && (stop
/(cxt
->geom
.sectors
*cxt
->geom
.heads
) > 1023))
931 stop
= cxt
->geom
.heads
*cxt
->geom
.sectors
*1024 - 1;
932 set_hsc(p
->eh
, p
->es
, p
->ec
, stop
);
933 partition_set_changed(cxt
, i
, 1);
937 static int get_start_from_user( struct fdisk_context
*cxt
,
938 fdisk_sector_t
*start
,
941 fdisk_sector_t limit
,
942 struct fdisk_partition
*pa
)
946 /* try to use template from 'pa' */
947 if (pa
&& pa
->start_follow_default
)
950 else if (pa
&& fdisk_partition_has_start(pa
)) {
951 DBG(LABEL
, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
952 (uintmax_t) pa
->start
, (uintmax_t) low
, (uintmax_t) limit
));
954 if (*start
< low
|| *start
> limit
) {
955 fdisk_warnx(cxt
, _("Start sector %ju out of range."),
960 /* ask user by dialog */
961 struct fdisk_ask
*ask
= fdisk_new_ask();
966 fdisk_ask_set_query(ask
,
967 fdisk_use_cylinders(cxt
) ?
968 _("First cylinder") : _("First sector"));
969 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_NUMBER
);
970 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, low
));
971 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, dflt
));
972 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
974 rc
= fdisk_do_ask(cxt
, ask
);
975 *start
= fdisk_ask_number_get_result(ask
);
976 fdisk_unref_ask(ask
);
979 if (fdisk_use_cylinders(cxt
)) {
980 *start
= (*start
- 1)
981 * fdisk_get_units_per_sector(cxt
);
987 DBG(LABEL
, ul_debug("DOS: start is %ju", (uintmax_t) *start
));
991 static int find_last_free_sector_in_range(
992 struct fdisk_context
*cxt
,
994 fdisk_sector_t begin
,
996 fdisk_sector_t
*result
)
999 fdisk_sector_t last
= end
;
1002 size_t i
= logical
? 4 : 0;
1005 for ( ; i
< cxt
->label
->nparts_max
; i
++) {
1006 struct pte
*pe
= self_pte(cxt
, i
);
1009 fdisk_sector_t p_start
= get_abs_partition_start(pe
);
1010 fdisk_sector_t p_end
= get_abs_partition_end(pe
);
1012 if (is_cleared_partition(pe
->pt_entry
))
1015 /* count EBR and begin of the logical partition as used area */
1017 p_start
-= cxt
->first_lba
;
1019 if (last
>= p_start
&& last
<= p_end
) {
1024 DBG(LABEL
, ul_debug("DOS: last free out of range <%ju,%ju>: %ju",
1025 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) last
));
1031 } while (last_moved
== 1);
1033 DBG(LABEL
, ul_debug("DOS: last unused sector in range <%ju,%ju>: %ju",
1034 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) last
));
1040 static int find_first_free_sector_in_range(
1041 struct fdisk_context
*cxt
,
1043 fdisk_sector_t begin
,
1045 fdisk_sector_t
*result
)
1047 int first_moved
= 0;
1048 fdisk_sector_t first
= begin
;
1051 size_t i
= logical
? 4 : 0;
1054 for (; i
< cxt
->label
->nparts_max
; i
++) {
1055 struct pte
*pe
= self_pte(cxt
, i
);
1058 fdisk_sector_t p_start
= get_abs_partition_start(pe
);
1059 fdisk_sector_t p_end
= get_abs_partition_end(pe
);
1061 if (is_cleared_partition(pe
->pt_entry
))
1063 /* count EBR and begin of the logical partition as used area */
1065 p_start
-= cxt
->first_lba
;
1066 if (first
< p_start
)
1068 if (first
<= p_end
) {
1069 first
= p_end
+ 1 + (logical
? cxt
->first_lba
: 0);
1073 DBG(LABEL
, ul_debug("DOS: first free out of range <%ju,%ju>: %ju",
1074 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) first
));
1079 } while (first_moved
== 1);
1081 DBG(LABEL
, ul_debug("DOS: first unused sector in range <%ju,%ju>: %ju",
1082 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) first
));
1087 static int get_disk_ranges(struct fdisk_context
*cxt
, int logical
,
1088 fdisk_sector_t
*first
, fdisk_sector_t
*last
)
1091 /* logical partitions */
1092 struct fdisk_dos_label
*l
= self_label(cxt
);
1093 struct pte
*ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1098 *first
= l
->ext_offset
+ cxt
->first_lba
;
1099 *last
= get_abs_partition_end(ext_pe
);
1102 /* primary partitions */
1103 if (fdisk_use_cylinders(cxt
) || !cxt
->total_sectors
)
1104 *last
= cxt
->geom
.heads
* cxt
->geom
.sectors
* cxt
->geom
.cylinders
- 1;
1106 *last
= cxt
->total_sectors
- 1;
1108 if (*last
> UINT_MAX
)
1110 *first
= cxt
->first_lba
;
1116 static int find_first_free_sector(struct fdisk_context
*cxt
,
1118 fdisk_sector_t start
,
1119 fdisk_sector_t
*result
)
1121 fdisk_sector_t first
, last
;
1124 rc
= get_disk_ranges(cxt
, logical
, &first
, &last
);
1128 return find_first_free_sector_in_range(cxt
, logical
, start
, last
, result
);
1131 static int add_partition(struct fdisk_context
*cxt
, size_t n
,
1132 struct fdisk_partition
*pa
)
1134 int sys
, read
= 0, rc
, isrel
= 0, is_logical
;
1135 struct fdisk_dos_label
*l
= self_label(cxt
);
1136 struct dos_partition
*p
= self_partition(cxt
, n
);
1137 struct fdisk_ask
*ask
= NULL
;
1139 fdisk_sector_t start
, stop
, limit
, temp
;
1141 DBG(LABEL
, ul_debug("DOS: adding partition %zu", n
));
1143 sys
= pa
&& pa
->type
? pa
->type
->code
: MBR_LINUX_DATA_PARTITION
;
1144 is_logical
= n
>= 4;
1146 if (p
&& is_used_partition(p
)) {
1147 fdisk_warnx(cxt
, _("Partition %zu is already defined. "
1148 "Delete it before re-adding it."),
1153 rc
= get_disk_ranges(cxt
, is_logical
, &start
, &stop
);
1157 if (!is_logical
&& cxt
->parent
&& fdisk_is_label(cxt
->parent
, GPT
))
1158 start
= 1; /* Bad boy modifies hybrid MBR */
1160 rc
= find_last_free_sector_in_range(cxt
, is_logical
, start
, stop
, &limit
);
1162 fdisk_warnx(cxt
, _("No free sectors available."));
1166 if ((is_logical
|| !cxt
->parent
|| !fdisk_is_label(cxt
->parent
, GPT
))
1167 && cxt
->script
&& pa
&& fdisk_partition_has_start(pa
)
1168 && pa
->start
>= (is_logical
? l
->ext_offset
: 1)
1169 && pa
->start
< start
) {
1170 fdisk_set_first_lba(cxt
, 1);
1172 rc
= get_disk_ranges(cxt
, is_logical
, &start
, &stop
);
1173 if (rc
) /* won't happen, but checking to be proper */
1178 * Ask for first sector
1181 fdisk_sector_t dflt
, aligned
;
1185 rc
= find_first_free_sector(cxt
, is_logical
, start
, &dflt
);
1187 fdisk_warnx(cxt
, _("No free sectors available."));
1192 if (n
>= 4 && pa
&& fdisk_partition_has_start(pa
) && cxt
->script
1193 && cxt
->first_lba
> 1
1194 && temp
== start
- cxt
->first_lba
) {
1195 fdisk_set_first_lba(cxt
, 1);
1199 /* the default sector should be aligned and unused */
1201 aligned
= fdisk_align_lba_in_range(cxt
, dflt
, dflt
, limit
);
1202 find_first_free_sector(cxt
, is_logical
, aligned
, &dflt
);
1203 } while (dflt
!= aligned
&& dflt
> aligned
&& dflt
< limit
);
1209 if (start
>= temp
+ fdisk_get_units_per_sector(cxt
)
1211 fdisk_info(cxt
, _("Sector %llu is already allocated."),
1215 if (pa
&& (fdisk_partition_has_start(pa
) ||
1216 pa
->start_follow_default
))
1220 if (!read
&& start
== temp
) {
1221 rc
= get_start_from_user(cxt
, &start
, temp
, dflt
, limit
, pa
);
1226 } while (start
!= temp
|| !read
);
1229 /* The first EBR is stored at begin of the extended partition */
1230 struct pte
*pe
= self_pte(cxt
, n
);
1233 pe
->offset
= l
->ext_offset
;
1235 /* The second (and another) EBR */
1236 struct pte
*pe
= self_pte(cxt
, n
);
1239 assert(start
>= cxt
->first_lba
);
1241 pe
->offset
= start
- cxt
->first_lba
;
1242 DBG(LABEL
, ul_debug("DOS: setting EBR offset to %ju [start=%ju]", pe
->offset
, start
));
1244 if (pe
->offset
== l
->ext_offset
) { /* must be corrected */
1246 if (cxt
->first_lba
== 1)
1251 rc
= find_last_free_sector_in_range(cxt
, is_logical
, start
, limit
, &stop
);
1253 fdisk_warnx(cxt
, _("No free sectors available."));
1259 * Ask for last sector
1261 if (fdisk_cround(cxt
, start
) == fdisk_cround(cxt
, limit
))
1263 else if (pa
&& pa
->end_follow_default
)
1265 else if (pa
&& fdisk_partition_has_size(pa
)) {
1266 stop
= start
+ pa
->size
;
1267 isrel
= pa
->size_explicit
? 0 : 1;
1268 if ((!isrel
|| !alignment_required(cxt
)) && stop
> start
)
1271 /* ask user by dialog */
1274 ask
= fdisk_new_ask();
1276 fdisk_reset_ask(ask
);
1279 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_OFFSET
);
1281 if (fdisk_use_cylinders(cxt
)) {
1282 fdisk_ask_set_query(ask
, _("Last cylinder, +/-cylinders or +/-size{K,M,G,T,P}"));
1283 fdisk_ask_number_set_unit(ask
,
1285 fdisk_get_units_per_sector(cxt
));
1287 fdisk_ask_set_query(ask
, _("Last sector, +/-sectors or +/-size{K,M,G,T,P}"));
1288 fdisk_ask_number_set_unit(ask
,cxt
->sector_size
);
1291 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, start
));
1292 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, limit
));
1293 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
1294 fdisk_ask_number_set_base(ask
, fdisk_cround(cxt
, start
)); /* base for relative input */
1295 fdisk_ask_number_set_wrap_negative(ask
, 1); /* wrap negative around high */
1297 rc
= fdisk_do_ask(cxt
, ask
);
1301 stop
= fdisk_ask_number_get_result(ask
);
1302 isrel
= fdisk_ask_number_is_relative(ask
);
1303 if (fdisk_use_cylinders(cxt
)) {
1304 stop
= stop
* fdisk_get_units_per_sector(cxt
) - 1;
1309 if (stop
>= start
&& stop
<= limit
)
1311 fdisk_warnx(cxt
, _("Value out of range."));
1315 DBG(LABEL
, ul_debug("DOS: raw stop: %ju [limit %ju]", (uintmax_t) stop
, (uintmax_t) limit
));
1320 if (isrel
&& stop
- start
< (cxt
->grain
/ fdisk_get_sector_size(cxt
))) {
1321 /* Don't try to be smart on very small partitions and don't align so small sizes */
1323 DBG(LABEL
, ul_debug("DOS: don't align end of tiny partition [start=%ju, stop=%ju, grain=%lu]",
1324 (uintmax_t)start
, (uintmax_t)stop
, cxt
->grain
));
1327 if (stop
< limit
&& isrel
&& alignment_required(cxt
)) {
1328 /* the last sector has not been exactly requested (but
1329 * defined by +size{K,M,G} convention), so be smart and
1330 * align the end of the partition. The next partition
1331 * will start at phy.block boundary.
1333 stop
= fdisk_align_lba_in_range(cxt
, stop
, start
, limit
);
1335 stop
-= 1; /* end one sector before aligned offset */
1338 DBG(LABEL
, ul_debug("DOS: aligned stop: %ju", (uintmax_t) stop
));
1341 set_partition(cxt
, n
, 0, start
, stop
, sys
, fdisk_partition_is_bootable(pa
));
1343 struct pte
*pe
= self_pte(cxt
, n
);
1345 set_partition(cxt
, n
- 1, 1, pe
->offset
, stop
,
1346 MBR_DOS_EXTENDED_PARTITION
, 0);
1351 struct fdisk_parttype
*t
=
1352 fdisk_label_get_parttype_from_code(cxt
->label
, sys
);
1353 fdisk_info_new_partition(cxt
, n
+ 1, start
, stop
, t
);
1354 fdisk_unref_parttype(t
);
1358 if (IS_EXTENDED(sys
)) {
1359 struct pte
*pen
= self_pte(cxt
, n
);
1363 l
->ext_offset
= start
;
1367 fdisk_label_set_changed(cxt
->label
, 1);
1370 fdisk_unref_ask(ask
);
1374 static int add_logical(struct fdisk_context
*cxt
,
1375 struct fdisk_partition
*pa
,
1384 assert(self_label(cxt
)->ext_offset
);
1386 DBG(LABEL
, ul_debug("DOS: nparts max: %zu", cxt
->label
->nparts_max
));
1387 pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
1390 if (!pe
->sectorbuffer
) {
1391 pe
->sectorbuffer
= calloc(1, cxt
->sector_size
);
1392 if (!pe
->sectorbuffer
)
1394 DBG(LABEL
, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1395 cxt
->label
->nparts_max
, pe
->sectorbuffer
));
1396 pe
->private_sectorbuffer
= 1;
1398 pe
->pt_entry
= mbr_get_partition(pe
->sectorbuffer
, 0);
1399 pe
->ex_entry
= pe
->pt_entry
+ 1;
1401 partition_set_changed(cxt
, cxt
->label
->nparts_max
, 1);
1403 cxt
->label
->nparts_max
++;
1405 /* this message makes sense only when we use extended/primary/logical
1406 * dialog. The dialog is disable for scripts, see dos_add_partition() */
1408 fdisk_info(cxt
, _("Adding logical partition %zu"),
1409 cxt
->label
->nparts_max
);
1410 *partno
= cxt
->label
->nparts_max
- 1;
1411 rc
= add_partition(cxt
, *partno
, pa
);
1414 /* reset on error */
1415 cxt
->label
->nparts_max
--;
1416 pe
->pt_entry
= NULL
;
1417 pe
->ex_entry
= NULL
;
1425 static void check(struct fdisk_context
*cxt
, size_t n
,
1426 unsigned int h
, unsigned int s
, unsigned int c
,
1429 unsigned int total
, real_s
, real_c
;
1431 if (!is_dos_compatible(cxt
))
1434 real_s
= sector(s
) - 1;
1435 real_c
= cylinder(s
, c
);
1436 total
= (real_c
* cxt
->geom
.heads
+ h
) * cxt
->geom
.sectors
+ real_s
;
1439 fdisk_warnx(cxt
, _("Partition %zu: contains sector 0"), n
);
1440 if (h
>= cxt
->geom
.heads
)
1441 fdisk_warnx(cxt
, _("Partition %zu: head %d greater than "
1442 "maximum %d"), n
, h
+ 1, cxt
->geom
.heads
);
1443 if (real_s
>= cxt
->geom
.sectors
)
1444 fdisk_warnx(cxt
, _("Partition %zu: sector %d greater than "
1445 "maximum %llu"), n
, s
, cxt
->geom
.sectors
);
1446 if (real_c
>= cxt
->geom
.cylinders
)
1447 fdisk_warnx(cxt
, _("Partition %zu: cylinder %d greater than "
1450 cxt
->geom
.cylinders
);
1452 if (cxt
->geom
.cylinders
<= 1024 && start
!= total
)
1453 fdisk_warnx(cxt
, _("Partition %zu: previous sectors %u "
1454 "disagrees with total %u"), n
, start
, total
);
1457 /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1458 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1459 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1460 * Lubkin Oct. 1991). */
1463 long2chs(struct fdisk_context
*cxt
, unsigned long ls
,
1464 unsigned int *c
, unsigned int *h
, unsigned int *s
) {
1465 int spc
= cxt
->geom
.heads
* cxt
->geom
.sectors
;
1469 *h
= ls
/ cxt
->geom
.sectors
;
1470 *s
= ls
% cxt
->geom
.sectors
+ 1; /* sectors count from 1 */
1473 static void check_consistency(struct fdisk_context
*cxt
, struct dos_partition
*p
,
1476 unsigned int pbc
, pbh
, pbs
; /* physical beginning c, h, s */
1477 unsigned int pec
, peh
, pes
; /* physical ending c, h, s */
1478 unsigned int lbc
, lbh
, lbs
; /* logical beginning c, h, s */
1479 unsigned int lec
, leh
, les
; /* logical ending c, h, s */
1481 if (!is_dos_compatible(cxt
))
1484 if (!cxt
->geom
.heads
|| !cxt
->geom
.sectors
|| (partition
>= 4))
1485 return; /* do not check extended partitions */
1487 /* physical beginning c, h, s */
1488 pbc
= (p
->bc
& 0xff) | ((p
->bs
<< 2) & 0x300);
1492 /* physical ending c, h, s */
1493 pec
= (p
->ec
& 0xff) | ((p
->es
<< 2) & 0x300);
1497 /* compute logical beginning (c, h, s) */
1498 long2chs(cxt
, dos_partition_get_start(p
), &lbc
, &lbh
, &lbs
);
1500 /* compute logical ending (c, h, s) */
1501 long2chs(cxt
, dos_partition_get_start(p
) + dos_partition_get_size(p
) - 1, &lec
, &leh
, &les
);
1503 /* Same physical / logical beginning? */
1504 if (cxt
->geom
.cylinders
<= 1024
1505 && (pbc
!= lbc
|| pbh
!= lbh
|| pbs
!= lbs
)) {
1506 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1507 "beginnings (non-Linux?): "
1508 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1514 /* Same physical / logical ending? */
1515 if (cxt
->geom
.cylinders
<= 1024
1516 && (pec
!= lec
|| peh
!= leh
|| pes
!= les
)) {
1517 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1518 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1524 /* Ending on cylinder boundary? */
1525 if (peh
!= (cxt
->geom
.heads
- 1) || pes
!= cxt
->geom
.sectors
) {
1526 fdisk_warnx(cxt
, _("Partition %zu: does not end on "
1527 "cylinder boundary."),
1532 static void fill_bounds(struct fdisk_context
*cxt
,
1533 fdisk_sector_t
*first
, fdisk_sector_t
*last
)
1536 struct pte
*pe
= self_pte(cxt
, 0);
1537 struct dos_partition
*p
;
1540 for (i
= 0; i
< cxt
->label
->nparts_max
; pe
++,i
++) {
1542 if (is_cleared_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
1543 first
[i
] = SIZE_MAX
;
1546 first
[i
] = get_abs_partition_start(pe
);
1547 last
[i
] = get_abs_partition_end(pe
);
1552 static int dos_verify_disklabel(struct fdisk_context
*cxt
)
1555 fdisk_sector_t total
= 1, n_sectors
= cxt
->total_sectors
;
1556 fdisk_sector_t first
[cxt
->label
->nparts_max
],
1557 last
[cxt
->label
->nparts_max
];
1558 struct dos_partition
*p
;
1559 struct fdisk_dos_label
*l
= self_label(cxt
);
1561 assert(fdisk_is_label(cxt
, DOS
));
1563 fill_bounds(cxt
, first
, last
);
1564 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
1565 struct pte
*pe
= self_pte(cxt
, i
);
1567 p
= self_partition(cxt
, i
);
1568 if (p
&& is_used_partition(p
) && !IS_EXTENDED(p
->sys_ind
)) {
1569 check_consistency(cxt
, p
, i
);
1571 if (get_abs_partition_start(pe
) < first
[i
])
1573 "Partition %zu: bad start-of-data."),
1576 check(cxt
, i
+ 1, p
->eh
, p
->es
, p
->ec
, last
[i
]);
1577 total
+= last
[i
] + 1 - first
[i
];
1580 total
+= get_abs_partition_start(pe
) - 1;
1582 for (j
= 0; j
< i
; j
++) {
1583 if ((first
[i
] >= first
[j
] && first
[i
] <= last
[j
])
1584 || ((last
[i
] <= last
[j
] && last
[i
] >= first
[j
]))) {
1586 fdisk_warnx(cxt
, _("Partition %zu: "
1587 "overlaps partition %zu."),
1590 total
+= first
[i
] >= first
[j
] ?
1591 first
[i
] : first
[j
];
1592 total
-= last
[i
] <= last
[j
] ?
1599 if (l
->ext_offset
) {
1600 fdisk_sector_t e_last
;
1601 struct pte
*ext_pe
= self_pte(cxt
, l
->ext_index
);
1604 e_last
= get_abs_partition_end(ext_pe
);
1606 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1608 p
= self_partition(cxt
, i
);
1612 if (i
!= 4 || i
+ 1 < cxt
->label
->nparts_max
)
1614 _("Partition %zu: empty."),
1616 } else if (first
[i
] < l
->ext_offset
1617 || last
[i
] > e_last
) {
1619 fdisk_warnx(cxt
, _("Logical partition %zu: "
1620 "not entirely in partition %zu."),
1621 i
+ 1, l
->ext_index
+ 1);
1626 if (total
> n_sectors
)
1627 fdisk_warnx(cxt
, _("Total allocated sectors %llu greater "
1628 "than the maximum %llu."), total
, n_sectors
);
1629 else if (total
< n_sectors
)
1630 fdisk_warnx(cxt
, _("Remaining %lld unallocated %ld-byte "
1631 "sectors."), n_sectors
- total
, cxt
->sector_size
);
1637 * Ask the user for new partition type information (logical, extended).
1638 * This function calls the actual partition adding logic - add_partition.
1642 static int dos_add_partition(struct fdisk_context
*cxt
,
1643 struct fdisk_partition
*pa
,
1647 uint8_t free_primary
= 0, free_sectors
= 0;
1648 fdisk_sector_t last
= 0, grain
;
1650 struct fdisk_dos_label
*l
;
1652 size_t res
= 0; /* partno */
1656 assert(fdisk_is_label(cxt
, DOS
));
1658 DBG(LABEL
, ul_debug("DOS: new partition wanted"));
1660 l
= self_label(cxt
);
1662 if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1663 fdisk_warnx(cxt
, _("The maximum number of partitions has "
1668 ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1671 * partition template (@pa) based partitioning
1674 /* A) template specifies start within extended partition; add logical */
1675 if (pa
&& fdisk_partition_has_start(pa
) && ext_pe
1676 && pa
->start
>= l
->ext_offset
1677 && pa
->start
<= get_abs_partition_end(ext_pe
)) {
1678 DBG(LABEL
, ul_debug("DOS: pa template %p: add logical (by offset)", pa
));
1680 if (fdisk_partition_has_partno(pa
) && fdisk_partition_get_partno(pa
) < 4) {
1681 DBG(LABEL
, ul_debug("DOS: pa template specifies partno<4 for logical partition"));
1684 rc
= add_logical(cxt
, pa
, &res
);
1687 /* B) template specifies start out of extended partition; add primary */
1688 } else if (pa
&& fdisk_partition_has_start(pa
) && ext_pe
) {
1689 DBG(LABEL
, ul_debug("DOS: pa template %p: add primary (by offset)", pa
));
1691 if (fdisk_partition_has_partno(pa
) && fdisk_partition_get_partno(pa
) >= 4) {
1692 DBG(LABEL
, ul_debug("DOS: pa template specifies partno>=4 for primary partition"));
1695 if (ext_pe
&& pa
->type
&& IS_EXTENDED(pa
->type
->code
)) {
1696 fdisk_warnx(cxt
, _("Extended partition already exists."));
1699 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1701 rc
= add_partition(cxt
, res
, pa
);
1704 /* C) template specifies start (or default), partno < 4; add primary */
1705 } else if (pa
&& (fdisk_partition_start_is_default(pa
) || fdisk_partition_has_start(pa
))
1706 && fdisk_partition_has_partno(pa
)
1707 && pa
->partno
< 4) {
1708 DBG(LABEL
, ul_debug("DOS: pa template %p: add primary (by partno)", pa
));
1710 if (ext_pe
&& pa
->type
&& IS_EXTENDED(pa
->type
->code
)) {
1711 fdisk_warnx(cxt
, _("Extended partition already exists."));
1714 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1716 rc
= add_partition(cxt
, res
, pa
);
1719 /* D) template specifies start (or default), partno >= 4; add logical */
1720 } else if (pa
&& (fdisk_partition_start_is_default(pa
) || fdisk_partition_has_start(pa
))
1721 && fdisk_partition_has_partno(pa
)
1722 && pa
->partno
>= 4) {
1723 DBG(LABEL
, ul_debug("DOS: pa template %p: add logical (by partno)", pa
));
1726 fdisk_warnx(cxt
, _("Extended partition does not exists. Failed to add logical partition."));
1730 if (fdisk_partition_has_start(pa
)
1731 && pa
->start
< l
->ext_offset
1732 && pa
->start
> get_abs_partition_end(ext_pe
)) {
1733 DBG(LABEL
, ul_debug("DOS: pa template specifies partno>=4, but start out of extended"));
1737 rc
= add_logical(cxt
, pa
, &res
);
1741 DBG(LABEL
, ul_debug("DOS: dialog driven partitioning"));
1742 /* Note @pa may be still used for things like partition type, etc */
1744 /* check if there is space for primary partition */
1745 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
1746 last
= cxt
->first_lba
;
1748 if (cxt
->parent
&& fdisk_is_label(cxt
->parent
, GPT
)) {
1749 /* modifying a hybrid MBR, which throws out the rules */
1754 for (i
= 0; i
< 4; i
++) {
1755 struct dos_partition
*p
= self_partition(cxt
, i
);
1758 if (is_used_partition(p
)) {
1759 fdisk_sector_t start
= dos_partition_get_start(p
);
1760 if (last
+ grain
<= start
)
1762 last
= start
+ dos_partition_get_size(p
);
1766 if (last
+ grain
< cxt
->total_sectors
- 1)
1770 if (!free_primary
|| !free_sectors
) {
1771 DBG(LABEL
, ul_debug("DOS: primary impossible, add logical"));
1772 if (l
->ext_offset
) {
1773 if (!pa
|| fdisk_partition_has_start(pa
)) {
1774 /* See above case A); here we have start, but
1775 * out of extended partition */
1778 msg
= _("All primary partitions are in use.");
1780 msg
= _("All space for primary partitions is in use.");
1782 if (pa
&& fdisk_partition_has_start(pa
)) {
1783 fdisk_warnx(cxt
, msg
);
1786 fdisk_info(cxt
, msg
);
1788 rc
= add_logical(cxt
, pa
, &res
);
1791 fdisk_info(cxt
, _("All space for primary partitions is in use."));
1793 /* TRANSLATORS: Try to keep this within 80 characters. */
1794 fdisk_info(cxt
, _("To create more partitions, first replace "
1795 "a primary with an extended partition."));
1800 struct fdisk_ask
*ask
;
1803 /* the default layout for scripts is to create primary partitions */
1804 if (cxt
->script
|| !fdisk_has_dialogs(cxt
)) {
1805 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1807 rc
= add_partition(cxt
, res
, pa
);
1811 ask
= fdisk_new_ask();
1814 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_MENU
);
1815 fdisk_ask_set_query(ask
, _("Partition type"));
1816 fdisk_ask_menu_set_default(ask
, free_primary
== 1
1817 && !l
->ext_offset
? 'e' : 'p');
1818 snprintf(hint
, sizeof(hint
),
1819 _("%u primary, %d extended, %u free"),
1820 4 - (l
->ext_offset
? 1 : 0) - free_primary
,
1821 l
->ext_offset
? 1 : 0,
1824 fdisk_ask_menu_add_item(ask
, 'p', _("primary"), hint
);
1826 fdisk_ask_menu_add_item(ask
, 'e', _("extended"), _("container for logical partitions"));
1828 fdisk_ask_menu_add_item(ask
, 'l', _("logical"), _("numbered from 5"));
1830 rc
= fdisk_do_ask(cxt
, ask
);
1832 fdisk_ask_menu_get_result(ask
, &c
);
1833 fdisk_unref_ask(ask
);
1838 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1840 rc
= add_partition(cxt
, res
, pa
);
1842 } else if (c
== 'l' && l
->ext_offset
) {
1843 rc
= add_logical(cxt
, pa
, &res
);
1845 } else if (c
== 'e' && !l
->ext_offset
) {
1846 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1848 struct fdisk_partition
*xpa
= NULL
;
1849 struct fdisk_parttype
*t
;
1851 t
= fdisk_label_get_parttype_from_code(cxt
->label
,
1852 MBR_DOS_EXTENDED_PARTITION
);
1854 pa
= xpa
= fdisk_new_partition();
1858 fdisk_partition_set_type(pa
, t
);
1859 rc
= add_partition(cxt
, res
, pa
);
1861 fdisk_unref_partition(xpa
);
1867 fdisk_warnx(cxt
, _("Invalid partition type `%c'."), c
);
1871 cxt
->label
->nparts_cur
++;
1878 static int write_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
,
1883 rc
= seek_sector(cxt
, secno
);
1885 fdisk_warn(cxt
, _("Cannot write sector %jd: seek failed"),
1890 DBG(LABEL
, ul_debug("DOS: writing to sector %ju", (uintmax_t) secno
));
1892 if (write(cxt
->dev_fd
, buf
, cxt
->sector_size
) != (ssize_t
) cxt
->sector_size
)
1897 static int dos_write_disklabel(struct fdisk_context
*cxt
)
1899 struct fdisk_dos_label
*l
= self_label(cxt
);
1901 int rc
= 0, mbr_changed
= 0;
1905 assert(fdisk_is_label(cxt
, DOS
));
1907 DBG(LABEL
, ul_debug("DOS: write PT requested [label-changed: %d, non-pt-changed: %d]",
1908 cxt
->label
->changed
, l
->non_pt_changed
));
1910 mbr_changed
= l
->non_pt_changed
;
1912 /* MBR (primary partitions) */
1914 for (i
= 0; i
< 4; i
++) {
1915 struct pte
*pe
= self_pte(cxt
, i
);
1923 DBG(LABEL
, ul_debug("DOS: MBR changed, writing"));
1924 mbr_set_magic(cxt
->firstsector
);
1925 rc
= write_sector(cxt
, 0, cxt
->firstsector
);
1930 if (cxt
->label
->nparts_max
<= 4 && l
->ext_offset
) {
1931 /* we have empty extended partition, check if the partition has
1932 * been modified and then cleanup possible remaining EBR */
1933 struct pte
*pe
= self_pte(cxt
, l
->ext_index
);
1934 unsigned char empty
[512] = { 0 };
1935 fdisk_sector_t off
= pe
? get_abs_partition_start(pe
) : 0;
1937 if (off
&& pe
->changed
) {
1938 mbr_set_magic(empty
);
1939 write_sector(cxt
, off
, empty
);
1943 /* EBR (logical partitions) */
1944 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1945 struct pte
*pe
= self_pte(cxt
, i
);
1948 if (!pe
->changed
|| !pe
->offset
|| !pe
->sectorbuffer
)
1951 mbr_set_magic(pe
->sectorbuffer
);
1952 rc
= write_sector(cxt
, pe
->offset
, pe
->sectorbuffer
);
1961 static int dos_locate_disklabel(struct fdisk_context
*cxt
, int n
,
1962 const char **name
, uint64_t *offset
, size_t *size
)
1977 /* extended partitions */
1978 if ((size_t)n
- 1 + 4 < cxt
->label
->nparts_max
) {
1979 struct pte
*pe
= self_pte(cxt
, n
- 1 + 4);
1982 assert(pe
->private_sectorbuffer
);
1985 *offset
= (uint64_t) pe
->offset
* cxt
->sector_size
;
1996 * Check whether partition entries are ordered by their starting positions.
1997 * Return 0 if OK. Return i if partition i should have been earlier.
1998 * Two separate checks: primary and logical partitions.
2000 static int wrong_p_order(struct fdisk_context
*cxt
, size_t *prev
)
2002 size_t last_p_start_pos
= 0, p_start_pos
;
2003 size_t i
, last_i
= 0;
2005 for (i
= 0 ; i
< cxt
->label
->nparts_max
; i
++) {
2007 struct pte
*pe
= self_pte(cxt
, i
);
2008 struct dos_partition
*p
;
2015 last_p_start_pos
= 0;
2017 if (is_used_partition(p
)) {
2018 p_start_pos
= get_abs_partition_start(pe
);
2020 if (last_p_start_pos
> p_start_pos
) {
2026 last_p_start_pos
= p_start_pos
;
2033 static int dos_get_disklabel_item(struct fdisk_context
*cxt
, struct fdisk_labelitem
*item
)
2039 assert(fdisk_is_label(cxt
, DOS
));
2042 case FDISK_LABELITEM_ID
:
2044 unsigned int num
= mbr_get_id(cxt
->firstsector
);
2045 item
->name
= _("Disk identifier");
2047 if (asprintf(&item
->data
.str
, "0x%08x", num
) < 0)
2052 if (item
->id
< __FDISK_NLABELITEMS
)
2053 rc
= 1; /* unsupported generic item */
2055 rc
= 2; /* out of range */
2063 static int dos_get_partition(struct fdisk_context
*cxt
, size_t n
,
2064 struct fdisk_partition
*pa
)
2066 struct dos_partition
*p
;
2068 struct fdisk_dos_label
*lb
;
2073 assert(fdisk_is_label(cxt
, DOS
));
2075 lb
= self_label(cxt
);
2077 pe
= self_pte(cxt
, n
);
2081 pa
->used
= !is_cleared_partition(p
);
2085 pa
->type
= dos_partition_parttype(cxt
, p
);
2086 pa
->boot
= p
->boot_ind
== ACTIVE_FLAG
? 1 : 0;
2087 pa
->start
= get_abs_partition_start(pe
);
2088 pa
->size
= dos_partition_get_size(p
);
2089 pa
->container
= lb
->ext_offset
&& n
== lb
->ext_index
;
2092 pa
->parent_partno
= lb
->ext_index
;
2094 if (p
->boot_ind
&& asprintf(&pa
->attrs
, "%02x", p
->boot_ind
) < 0)
2098 if (asprintf(&pa
->start_chs
, "%d/%d/%d",
2099 cylinder(p
->bs
, p
->bc
),
2105 if (asprintf(&pa
->end_chs
, "%d/%d/%d",
2106 cylinder(p
->es
, p
->ec
),
2114 static int has_logical(struct fdisk_context
*cxt
)
2117 struct fdisk_dos_label
*l
= self_label(cxt
);
2119 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2120 if (l
->ptes
[i
].pt_entry
)
2126 static int dos_set_partition(struct fdisk_context
*cxt
, size_t n
,
2127 struct fdisk_partition
*pa
)
2129 struct fdisk_dos_label
*l
;
2130 struct dos_partition
*p
;
2133 fdisk_sector_t start
, size
;
2138 assert(fdisk_is_label(cxt
, DOS
));
2140 if (n
>= cxt
->label
->nparts_max
)
2143 l
= self_label(cxt
);
2144 p
= self_partition(cxt
, n
);
2146 pe
= self_pte(cxt
, n
);
2150 orgtype
= p
->sys_ind
;
2153 if (IS_EXTENDED(pa
->type
->code
) && l
->ext_offset
&& l
->ext_index
!= n
) {
2154 fdisk_warnx(cxt
, _("Extended partition already exists."));
2158 if (!pa
->type
->code
)
2159 fdisk_warnx(cxt
, _("Type 0 means free space to many systems. "
2160 "Having partitions of type 0 is probably unwise."));
2162 if (IS_EXTENDED(p
->sys_ind
) && !IS_EXTENDED(pa
->type
->code
) && has_logical(cxt
)) {
2164 "Cannot change type of the extended partition which is "
2165 "already used by logical partitions. Delete logical "
2166 "partitions first."));
2171 FDISK_INIT_UNDEF(start
);
2172 FDISK_INIT_UNDEF(size
);
2174 if (fdisk_partition_has_start(pa
))
2176 if (fdisk_partition_has_size(pa
))
2179 if (!FDISK_IS_UNDEF(start
) || !FDISK_IS_UNDEF(size
)) {
2180 DBG(LABEL
, ul_debug("DOS: resize partition"));
2182 if (FDISK_IS_UNDEF(start
))
2183 start
= get_abs_partition_start(pe
);
2184 if (FDISK_IS_UNDEF(size
))
2185 size
= dos_partition_get_size(p
);
2187 set_partition(cxt
, n
, 0, start
, start
+ size
- 1,
2188 pa
->type
? pa
->type
->code
: p
->sys_ind
,
2189 FDISK_IS_UNDEF(pa
->boot
) ?
2190 p
->boot_ind
== ACTIVE_FLAG
:
2191 fdisk_partition_is_bootable(pa
));
2193 DBG(LABEL
, ul_debug("DOS: keep size, modify properties"));
2195 p
->sys_ind
= pa
->type
->code
;
2196 if (!FDISK_IS_UNDEF(pa
->boot
))
2197 p
->boot_ind
= fdisk_partition_is_bootable(pa
) ? ACTIVE_FLAG
: 0;
2201 if (IS_EXTENDED(pa
->type
->code
) && !IS_EXTENDED(orgtype
)) {
2202 /* new extended partition - create a reference */
2204 l
->ext_offset
= dos_partition_get_start(p
);
2206 } else if (IS_EXTENDED(orgtype
)) {
2207 /* remove extended partition */
2208 cxt
->label
->nparts_max
= 4;
2209 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
2215 partition_set_changed(cxt
, n
, 1);
2219 static void print_chain_of_logicals(struct fdisk_context
*cxt
)
2222 struct fdisk_dos_label
*l
= self_label(cxt
);
2224 fputc('\n', stdout
);
2226 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2227 struct pte
*pe
= self_pte(cxt
, i
);
2230 fprintf(stderr
, "#%02zu EBR [%10ju], "
2231 "data[start=%10ju (%10ju), size=%10ju], "
2232 "link[start=%10ju (%10ju), size=%10ju]\n",
2233 i
, (uintmax_t) pe
->offset
,
2235 (uintmax_t) dos_partition_get_start(pe
->pt_entry
),
2236 (uintmax_t) get_abs_partition_start(pe
),
2237 (uintmax_t) dos_partition_get_size(pe
->pt_entry
),
2239 (uintmax_t) dos_partition_get_start(pe
->ex_entry
),
2240 (uintmax_t) l
->ext_offset
+ dos_partition_get_start(pe
->ex_entry
),
2241 (uintmax_t) dos_partition_get_size(pe
->ex_entry
));
2245 static int cmp_ebr_offsets(const void *a
, const void *b
)
2247 const struct pte
*ae
= (const struct pte
*) a
,
2248 *be
= (const struct pte
*) b
;
2250 if (ae
->offset
== 0 && be
->offset
== 0)
2252 if (ae
->offset
== 0)
2254 if (be
->offset
== 0)
2257 return cmp_numbers(ae
->offset
, be
->offset
);
2261 * Fix the chain of logicals.
2263 * The function does not modify data partitions within EBR tables
2264 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
2265 * (pte->ex_entry) between EBR tables.
2268 static void fix_chain_of_logicals(struct fdisk_context
*cxt
)
2270 struct fdisk_dos_label
*l
= self_label(cxt
);
2274 DBG(LABEL
, print_chain_of_logicals(cxt
));
2276 /* Sort chain by EBR offsets */
2277 qsort(&l
->ptes
[4], cxt
->label
->nparts_max
- 4, sizeof(struct pte
),
2281 /* Sort data partitions by start */
2282 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
2283 struct pte
*cur
= self_pte(cxt
, i
),
2284 *nxt
= self_pte(cxt
, i
+ 1);
2289 if (get_abs_partition_start(cur
) >
2290 get_abs_partition_start(nxt
)) {
2292 struct dos_partition tmp
= *cur
->pt_entry
;
2293 fdisk_sector_t cur_start
= get_abs_partition_start(cur
),
2294 nxt_start
= get_abs_partition_start(nxt
);
2296 /* swap data partitions */
2297 *cur
->pt_entry
= *nxt
->pt_entry
;
2298 *nxt
->pt_entry
= tmp
;
2300 /* Recount starts according to EBR offsets, the absolute
2301 * address still has to be the same! */
2302 dos_partition_set_start(cur
->pt_entry
, nxt_start
- cur
->offset
);
2303 dos_partition_set_start(nxt
->pt_entry
, cur_start
- nxt
->offset
);
2305 partition_set_changed(cxt
, i
, 1);
2306 partition_set_changed(cxt
, i
+ 1, 1);
2311 /* Update EBR links */
2312 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
2313 struct pte
*cur
= self_pte(cxt
, i
),
2314 *nxt
= self_pte(cxt
, i
+ 1);
2319 fdisk_sector_t noff
= nxt
->offset
- l
->ext_offset
,
2320 ooff
= dos_partition_get_start(cur
->ex_entry
);
2325 DBG(LABEL
, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2326 (uintmax_t) cur
->offset
,
2327 (uintmax_t) ooff
, (uintmax_t) noff
));
2329 set_partition(cxt
, i
, 1, nxt
->offset
,
2330 get_abs_partition_end(nxt
),
2331 MBR_DOS_EXTENDED_PARTITION
, 0);
2334 /* always terminate the chain ! */
2335 last
= self_pte(cxt
, cxt
->label
->nparts_max
- 1);
2337 clear_partition(last
->ex_entry
);
2338 partition_set_changed(cxt
, cxt
->label
->nparts_max
- 1, 1);
2341 DBG(LABEL
, print_chain_of_logicals(cxt
));
2344 static int dos_reorder(struct fdisk_context
*cxt
)
2346 struct pte
*pei
, *pek
;
2349 if (!wrong_p_order(cxt
, NULL
)) {
2350 fdisk_info(cxt
, _("Nothing to do. Ordering is correct already."));
2354 while ((i
= wrong_p_order(cxt
, &k
)) != 0 && i
< 4) {
2355 /* partition i should have come earlier, move it */
2356 /* We have to move data in the MBR */
2357 struct dos_partition
*pi
, *pk
, *pe
, pbuf
;
2358 pei
= self_pte(cxt
, i
);
2359 pek
= self_pte(cxt
, k
);
2365 pei
->ex_entry
= pek
->ex_entry
;
2371 memmove(&pbuf
, pi
, sizeof(struct dos_partition
));
2372 memmove(pi
, pk
, sizeof(struct dos_partition
));
2373 memmove(pk
, &pbuf
, sizeof(struct dos_partition
));
2375 partition_set_changed(cxt
, i
, 1);
2376 partition_set_changed(cxt
, k
, 1);
2380 fix_chain_of_logicals(cxt
);
2385 /* TODO: use fdisk_set_partition() API */
2386 int fdisk_dos_move_begin(struct fdisk_context
*cxt
, size_t i
)
2389 struct dos_partition
*p
;
2390 unsigned int new, free_start
, curr_start
, last
;
2396 assert(fdisk_is_label(cxt
, DOS
));
2398 pe
= self_pte(cxt
, i
);
2404 if (!is_used_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
2405 fdisk_warnx(cxt
, _("Partition %zu: no data area."), i
+ 1);
2409 /* the default start is at the second sector of the disk or at the
2410 * second sector of the extended partition
2412 free_start
= pe
->offset
? pe
->offset
+ 1 : 1;
2414 curr_start
= get_abs_partition_start(pe
);
2416 /* look for a free space before the current start of the partition */
2417 for (x
= 0; x
< cxt
->label
->nparts_max
; x
++) {
2419 struct pte
*prev_pe
= self_pte(cxt
, x
);
2420 struct dos_partition
*prev_p
;
2424 prev_p
= prev_pe
->pt_entry
;
2427 end
= get_abs_partition_start(prev_pe
)
2428 + dos_partition_get_size(prev_p
);
2430 if (is_used_partition(prev_p
) &&
2431 end
> free_start
&& end
<= curr_start
)
2435 last
= get_abs_partition_end(pe
);
2437 rc
= fdisk_ask_number(cxt
, free_start
, curr_start
, last
,
2438 _("New beginning of data"), &res
);
2442 new = res
- pe
->offset
;
2444 if (new != dos_partition_get_size(p
)) {
2445 unsigned int sects
= dos_partition_get_size(p
)
2446 + dos_partition_get_start(p
) - new;
2448 dos_partition_set_size(p
, sects
);
2449 dos_partition_set_start(p
, new);
2451 partition_set_changed(cxt
, i
, 1);
2457 static int dos_partition_is_used(
2458 struct fdisk_context
*cxt
,
2461 struct dos_partition
*p
;
2465 assert(fdisk_is_label(cxt
, DOS
));
2467 if (i
>= cxt
->label
->nparts_max
)
2470 p
= self_partition(cxt
, i
);
2472 return p
&& !is_cleared_partition(p
);
2475 static int dos_toggle_partition_flag(
2476 struct fdisk_context
*cxt
,
2480 struct dos_partition
*p
;
2484 assert(fdisk_is_label(cxt
, DOS
));
2486 if (i
>= cxt
->label
->nparts_max
)
2489 p
= self_partition(cxt
, i
);
2492 case DOS_FLAG_ACTIVE
:
2493 if (IS_EXTENDED(p
->sys_ind
) && !p
->boot_ind
)
2494 fdisk_warnx(cxt
, _("Partition %zu: is an extended "
2495 "partition."), i
+ 1);
2497 p
->boot_ind
= (p
->boot_ind
? 0 : ACTIVE_FLAG
);
2498 partition_set_changed(cxt
, i
, 1);
2499 fdisk_info(cxt
, p
->boot_ind
?
2500 _("The bootable flag on partition %zu is enabled now.") :
2501 _("The bootable flag on partition %zu is disabled now."),
2511 static const struct fdisk_field dos_fields
[] =
2514 { FDISK_FIELD_DEVICE
, N_("Device"), 10, 0 },
2515 { FDISK_FIELD_BOOT
, N_("Boot"), 1, 0 },
2516 { FDISK_FIELD_START
, N_("Start"), 5, FDISK_FIELDFL_NUMBER
},
2517 { FDISK_FIELD_END
, N_("End"), 5, FDISK_FIELDFL_NUMBER
},
2518 { FDISK_FIELD_SECTORS
, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER
},
2519 { FDISK_FIELD_CYLINDERS
,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER
},
2520 { FDISK_FIELD_SIZE
, N_("Size"), 5, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_EYECANDY
},
2521 { FDISK_FIELD_TYPEID
, N_("Id"), 2, FDISK_FIELDFL_NUMBER
},
2522 { FDISK_FIELD_TYPE
, N_("Type"), 0.1, 0 },
2525 { FDISK_FIELD_SADDR
, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2526 { FDISK_FIELD_EADDR
, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2527 { FDISK_FIELD_ATTR
, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
}
2531 static const struct fdisk_label_operations dos_operations
=
2533 .probe
= dos_probe_label
,
2534 .write
= dos_write_disklabel
,
2535 .verify
= dos_verify_disklabel
,
2536 .create
= dos_create_disklabel
,
2537 .locate
= dos_locate_disklabel
,
2538 .get_item
= dos_get_disklabel_item
,
2539 .set_id
= dos_set_disklabel_id
,
2541 .get_part
= dos_get_partition
,
2542 .set_part
= dos_set_partition
,
2543 .add_part
= dos_add_partition
,
2544 .del_part
= dos_delete_partition
,
2545 .reorder
= dos_reorder
,
2547 .part_toggle_flag
= dos_toggle_partition_flag
,
2548 .part_is_used
= dos_partition_is_used
,
2550 .reset_alignment
= dos_reset_alignment
,
2552 .deinit
= dos_deinit
,
2556 * allocates DOS in-memory stuff
2558 struct fdisk_label
*fdisk_new_dos_label(struct fdisk_context
*cxt
__attribute__ ((__unused__
)))
2560 struct fdisk_label
*lb
;
2561 struct fdisk_dos_label
*dos
;
2563 dos
= calloc(1, sizeof(*dos
));
2567 /* initialize generic part of the driver */
2568 lb
= (struct fdisk_label
*) dos
;
2570 lb
->id
= FDISK_DISKLABEL_DOS
;
2571 lb
->op
= &dos_operations
;
2573 lb
->parttypes
= dos_parttypes
;
2574 lb
->nparttypes
= ARRAY_SIZE(dos_parttypes
) - 1;
2575 lb
->parttype_cuts
= dos_parttype_cuts
;
2576 lb
->nparttype_cuts
= ARRAY_SIZE(dos_parttype_cuts
);
2578 lb
->fields
= dos_fields
;
2579 lb
->nfields
= ARRAY_SIZE(dos_fields
);
2581 lb
->geom_min
.sectors
= 1;
2582 lb
->geom_min
.heads
= 1;
2583 lb
->geom_min
.cylinders
= 1;
2585 lb
->geom_max
.sectors
= 63;
2586 lb
->geom_max
.heads
= 255;
2587 lb
->geom_max
.cylinders
= 1048576;
2593 * fdisk_dos_enable_compatible:
2594 * @lb: DOS label (see fdisk_get_label())
2597 * Enables deprecated DOS compatible mode, in this mode library checks for
2598 * cylinders boundary, cases about CHS addressing and another obscure things.
2600 * Returns: 0 on success, <0 on error.
2602 int fdisk_dos_enable_compatible(struct fdisk_label
*lb
, int enable
)
2604 struct fdisk_dos_label
*dos
= (struct fdisk_dos_label
*) lb
;
2609 dos
->compatible
= enable
;
2611 lb
->flags
|= FDISK_LABEL_FL_REQUIRE_GEOMETRY
;
2616 * fdisk_dos_is_compatible:
2619 * Returns: 0 if DOS compatibility disabled, 1 if enabled
2621 int fdisk_dos_is_compatible(struct fdisk_label
*lb
)
2623 return ((struct fdisk_dos_label
*) lb
)->compatible
;