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 #define set_hsc(h,s,c,sector) { \
74 s = sector % cxt->geom.sectors + 1; \
75 sector /= cxt->geom.sectors; \
76 h = sector % cxt->geom.heads; \
77 sector /= cxt->geom.heads; \
79 s |= (sector >> 2) & 0xc0; \
83 #define sector(s) ((s) & 0x3f)
84 #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
86 #define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
88 #define is_dos_compatible(_x) \
89 (fdisk_is_label(_x, DOS) && \
90 fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
92 #define cround(c, n) fdisk_cround(c, n)
95 static inline struct fdisk_dos_label
*self_label(struct fdisk_context
*cxt
)
99 assert(fdisk_is_label(cxt
, DOS
));
101 return (struct fdisk_dos_label
*) cxt
->label
;
104 static inline struct pte
*self_pte(struct fdisk_context
*cxt
, size_t i
)
106 struct fdisk_dos_label
*l
= self_label(cxt
);
108 if (i
>= ARRAY_SIZE(l
->ptes
))
114 static inline struct dos_partition
*self_partition(
115 struct fdisk_context
*cxt
,
118 struct pte
*pe
= self_pte(cxt
, i
);
119 return pe
? pe
->pt_entry
: NULL
;
122 struct dos_partition
*fdisk_dos_get_partition(
123 struct fdisk_context
*cxt
,
128 assert(fdisk_is_label(cxt
, DOS
));
130 return self_partition(cxt
, i
);
133 static struct fdisk_parttype
*dos_partition_parttype(
134 struct fdisk_context
*cxt
,
135 struct dos_partition
*p
)
137 struct fdisk_parttype
*t
138 = fdisk_label_get_parttype_from_code(cxt
->label
, p
->sys_ind
);
139 return t
? : fdisk_new_unknown_parttype(p
->sys_ind
, NULL
);
143 * Linux kernel cares about partition size only. Things like
144 * partition type or so are completely irrelevant -- kzak Nov-2013
146 static int is_used_partition(struct dos_partition
*p
)
148 return p
&& dos_partition_get_size(p
) != 0;
151 static void partition_set_changed(
152 struct fdisk_context
*cxt
,
156 struct pte
*pe
= self_pte(cxt
, i
);
161 DBG(LABEL
, ul_debug("DOS: setting %zu partition to %s", i
,
162 changed
? "changed" : "unchanged"));
164 pe
->changed
= changed
? 1 : 0;
166 fdisk_label_set_changed(cxt
->label
, 1);
169 static fdisk_sector_t
get_abs_partition_start(struct pte
*pe
)
172 assert(pe
->pt_entry
);
174 return pe
->offset
+ dos_partition_get_start(pe
->pt_entry
);
177 static fdisk_sector_t
get_abs_partition_end(struct pte
*pe
)
182 assert(pe
->pt_entry
);
184 size
= dos_partition_get_size(pe
->pt_entry
);
185 return get_abs_partition_start(pe
) + size
- (size
? 1 : 0);
188 static int is_cleared_partition(struct dos_partition
*p
)
190 return !(!p
|| p
->boot_ind
|| p
->bh
|| p
->bs
|| p
->bc
||
191 p
->sys_ind
|| p
->eh
|| p
->es
|| p
->ec
||
192 dos_partition_get_start(p
) || dos_partition_get_size(p
));
195 static int get_partition_unused_primary(struct fdisk_context
*cxt
,
196 struct fdisk_partition
*pa
,
206 org
= cxt
->label
->nparts_max
;
208 cxt
->label
->nparts_max
= 4;
209 rc
= fdisk_partition_next_partno(pa
, cxt
, &n
);
210 cxt
->label
->nparts_max
= org
;
213 fdisk_info(cxt
, _("All primary partitions have been defined already."));
215 } else if (rc
== -ERANGE
) {
216 fdisk_warnx(cxt
, _("Primary partition not available."));
223 static int seek_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
)
225 off_t offset
= (off_t
) secno
* cxt
->sector_size
;
227 return lseek(cxt
->dev_fd
, offset
, SEEK_SET
) == (off_t
) -1 ? -errno
: 0;
230 static int read_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
,
233 int rc
= seek_sector(cxt
, secno
);
239 r
= read(cxt
->dev_fd
, buf
, cxt
->sector_size
);
240 if (r
== (ssize_t
) cxt
->sector_size
)
247 /* Allocate a buffer and read a partition table sector */
248 static int read_pte(struct fdisk_context
*cxt
, size_t pno
, fdisk_sector_t offset
)
252 struct pte
*pe
= self_pte(cxt
, pno
);
257 buf
= calloc(1, cxt
->sector_size
);
261 DBG(LABEL
, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
262 pno
, (uintmax_t) offset
, buf
));
265 pe
->sectorbuffer
= buf
;
266 pe
->private_sectorbuffer
= 1;
268 rc
= read_sector(cxt
, offset
, pe
->sectorbuffer
);
270 fdisk_warn(cxt
, _("Failed to read extended partition table "
271 "(offset=%ju)"), (uintmax_t) offset
);
276 pe
->pt_entry
= pe
->ex_entry
= NULL
;
281 static void clear_partition(struct dos_partition
*p
)
293 dos_partition_set_start(p
,0);
294 dos_partition_set_size(p
,0);
297 static void dos_init(struct fdisk_context
*cxt
)
299 struct fdisk_dos_label
*l
= self_label(cxt
);
304 assert(fdisk_is_label(cxt
, DOS
));
306 DBG(LABEL
, ul_debug("DOS: initialize, first sector buffer %p", cxt
->firstsector
));
308 cxt
->label
->nparts_max
= 4; /* default, unlimited number of logical */
312 l
->non_pt_changed
= 0;
314 memset(l
->ptes
, 0, sizeof(l
->ptes
));
316 for (i
= 0; i
< 4; i
++) {
317 struct pte
*pe
= self_pte(cxt
, i
);
320 pe
->pt_entry
= mbr_get_partition(cxt
->firstsector
, i
);
323 pe
->sectorbuffer
= cxt
->firstsector
;
324 pe
->private_sectorbuffer
= 0;
327 DBG(LABEL
, ul_debug("DOS: initialize: #%zu start=%u size=%u sysid=%02x",
329 dos_partition_get_start(pe
->pt_entry
),
330 dos_partition_get_size(pe
->pt_entry
),
331 pe
->pt_entry
->sys_ind
));
334 if (fdisk_is_listonly(cxt
))
337 * Various warnings...
339 if (fdisk_missing_geometry(cxt
))
340 fdisk_warnx(cxt
, _("You can set geometry from the extra functions menu."));
342 if (is_dos_compatible(cxt
)) {
343 fdisk_warnx(cxt
, _("DOS-compatible mode is deprecated."));
345 if (cxt
->sector_size
!= cxt
->phy_sector_size
)
347 "The device presents a logical sector size that is smaller than "
348 "the physical sector size. Aligning to a physical sector (or optimal "
349 "I/O) size boundary is recommended, or performance may be impacted."));
352 if (fdisk_use_cylinders(cxt
))
353 fdisk_warnx(cxt
, _("Cylinders as display units are deprecated."));
355 if (cxt
->total_sectors
> UINT_MAX
) {
356 uint64_t bytes
= cxt
->total_sectors
* cxt
->sector_size
;
357 char *szstr
= size_to_human_string(SIZE_SUFFIX_SPACE
358 | SIZE_SUFFIX_3LETTER
, bytes
);
360 _("The size of this disk is %s (%ju bytes). DOS "
361 "partition table format cannot be used on drives for "
362 "volumes larger than %lu bytes for %lu-byte "
363 "sectors. Use GUID partition table format (GPT)."),
365 UINT_MAX
* cxt
->sector_size
,
371 /* callback called by libfdisk */
372 static void dos_deinit(struct fdisk_label
*lb
)
375 struct fdisk_dos_label
*l
= (struct fdisk_dos_label
*) lb
;
377 for (i
= 0; i
< ARRAY_SIZE(l
->ptes
); i
++) {
378 struct pte
*pe
= &l
->ptes
[i
];
380 if (pe
->private_sectorbuffer
&& pe
->sectorbuffer
) {
381 DBG(LABEL
, ul_debug("DOS: freeing pte %zu sector buffer %p",
382 i
, pe
->sectorbuffer
));
383 free(pe
->sectorbuffer
);
385 pe
->sectorbuffer
= NULL
;
386 pe
->private_sectorbuffer
= 0;
389 memset(l
->ptes
, 0, sizeof(l
->ptes
));
392 static void reset_pte(struct pte
*pe
)
396 if (pe
->private_sectorbuffer
) {
397 DBG(LABEL
, ul_debug(" --> freeing pte sector buffer %p",
399 free(pe
->sectorbuffer
);
401 memset(pe
, 0, sizeof(struct pte
));
404 static int delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
406 struct fdisk_dos_label
*l
;
408 struct dos_partition
*p
;
409 struct dos_partition
*q
;
413 assert(fdisk_is_label(cxt
, DOS
));
415 pe
= self_pte(cxt
, partnum
);
419 DBG(LABEL
, ul_debug("DOS: delete partition %zu (max=%zu)", partnum
,
420 cxt
->label
->nparts_max
));
426 /* Note that for the fifth partition (partnum == 4) we don't actually
427 decrement partitions. */
429 DBG(LABEL
, ul_debug("--> delete primary"));
430 if (IS_EXTENDED(p
->sys_ind
) && partnum
== l
->ext_index
) {
432 DBG(LABEL
, ul_debug(" --> delete extended"));
433 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
434 DBG(LABEL
, ul_debug(" --> delete logical #%zu", i
));
435 reset_pte(&l
->ptes
[i
]);
438 cxt
->label
->nparts_max
= 4;
439 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
443 partition_set_changed(cxt
, partnum
, 1);
445 } else if (!q
->sys_ind
&& partnum
> 4) {
446 DBG(LABEL
, ul_debug("--> delete logical [last in the chain]"));
447 reset_pte(&l
->ptes
[partnum
]);
448 --cxt
->label
->nparts_max
;
450 /* clear link to deleted partition */
451 clear_partition(l
->ptes
[partnum
].ex_entry
);
452 partition_set_changed(cxt
, partnum
, 1);
454 DBG(LABEL
, ul_debug("--> delete logical [move down]"));
456 DBG(LABEL
, ul_debug(" --> delete %zu logical link", partnum
));
457 p
= l
->ptes
[partnum
- 1].ex_entry
;
459 dos_partition_set_start(p
, dos_partition_get_start(q
));
460 dos_partition_set_size(p
, dos_partition_get_size(q
));
461 partition_set_changed(cxt
, partnum
- 1, 1);
463 } else if (cxt
->label
->nparts_max
> 5) {
464 DBG(LABEL
, ul_debug(" --> delete first logical link"));
465 pe
= &l
->ptes
[5]; /* second logical */
467 if (pe
->pt_entry
) /* prevent SEGFAULT */
468 dos_partition_set_start(pe
->pt_entry
,
469 get_abs_partition_start(pe
) -
471 pe
->offset
= l
->ext_offset
;
472 partition_set_changed(cxt
, 5, 1);
475 if (cxt
->label
->nparts_max
> 5) {
476 DBG(LABEL
, ul_debug(" --> move ptes"));
477 cxt
->label
->nparts_max
--;
478 reset_pte(&l
->ptes
[partnum
]);
479 while (partnum
< cxt
->label
->nparts_max
) {
480 DBG(LABEL
, ul_debug(" --> moving pte %zu <-- %zu", partnum
, partnum
+ 1));
481 l
->ptes
[partnum
] = l
->ptes
[partnum
+ 1];
484 memset(&l
->ptes
[partnum
], 0, sizeof(struct pte
));
486 DBG(LABEL
, ul_debug(" --> the only logical: clear only"));
487 clear_partition(l
->ptes
[partnum
].pt_entry
);
488 cxt
->label
->nparts_max
--;
491 DBG(LABEL
, ul_debug(" --> clear last logical"));
492 reset_pte(&l
->ptes
[partnum
]);
493 partition_set_changed(cxt
, l
->ext_index
, 1);
498 fdisk_label_set_changed(cxt
->label
, 1);
502 static int dos_delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
508 assert(fdisk_is_label(cxt
, DOS
));
510 pe
= self_pte(cxt
, partnum
);
511 if (!pe
|| !is_used_partition(pe
->pt_entry
))
514 return delete_partition(cxt
, partnum
);
517 static void read_extended(struct fdisk_context
*cxt
, size_t ext
)
520 struct pte
*pex
, *pe
;
521 struct dos_partition
*p
, *q
;
522 struct fdisk_dos_label
*l
= self_label(cxt
);
525 pex
= self_pte(cxt
, ext
);
527 DBG(LABEL
, ul_debug("DOS: uninitialized pointer to %zu pex", ext
));
530 pex
->ex_entry
= pex
->pt_entry
;
533 if (!dos_partition_get_start(p
)) {
534 fdisk_warnx(cxt
, _("Bad offset in primary extended partition."));
538 DBG(LABEL
, ul_debug("DOS: Reading extended %zu", ext
));
540 while (IS_EXTENDED (p
->sys_ind
)) {
541 if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
542 /* This is not a Linux restriction, but
543 this program uses arrays of size MAXIMUM_PARTS.
544 Do not try to `improve' this test. */
545 struct pte
*pre
= self_pte(cxt
,
546 cxt
->label
->nparts_max
- 1);
548 _("Omitting partitions after #%zu. They will be deleted "
549 "if you save this partition table."),
550 cxt
->label
->nparts_max
);
553 clear_partition(pre
->ex_entry
);
554 partition_set_changed(cxt
,
555 cxt
->label
->nparts_max
- 1, 1);
560 pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
564 if (read_pte(cxt
, cxt
->label
->nparts_max
, l
->ext_offset
+
565 dos_partition_get_start(p
)))
569 l
->ext_offset
= dos_partition_get_start(p
);
571 assert(pe
->sectorbuffer
);
572 q
= p
= mbr_get_partition(pe
->sectorbuffer
, 0);
574 for (i
= 0; i
< 4; i
++, p
++) {
575 if (!dos_partition_get_size(p
))
578 if (IS_EXTENDED (p
->sys_ind
)) {
581 "Extra link pointer in partition "
583 cxt
->label
->nparts_max
+ 1);
586 } else if (p
->sys_ind
) {
589 "Ignoring extra data in partition "
591 cxt
->label
->nparts_max
+ 1);
597 /* very strange code here... */
599 if (q
!= pe
->ex_entry
)
602 pe
->pt_entry
= q
+ 1;
605 if (q
!= pe
->pt_entry
)
608 pe
->ex_entry
= q
+ 1;
612 cxt
->label
->nparts_cur
= ++cxt
->label
->nparts_max
;
614 DBG(LABEL
, ul_debug("DOS: EBR[offset=%ju]: link: type=%x, start=%u, size=%u; "
615 " data: type=%x, start=%u, size=%u",
616 (uintmax_t) pe
->offset
,
617 pe
->ex_entry
->sys_ind
,
618 dos_partition_get_start(pe
->ex_entry
),
619 dos_partition_get_size(pe
->ex_entry
),
620 pe
->pt_entry
->sys_ind
,
621 dos_partition_get_start(pe
->pt_entry
),
622 dos_partition_get_size(pe
->pt_entry
)));
626 /* remove last empty EBR */
627 pe
= self_pte(cxt
, cxt
->label
->nparts_max
- 1);
629 is_cleared_partition(pe
->ex_entry
) &&
630 is_cleared_partition(pe
->pt_entry
)) {
631 DBG(LABEL
, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe
->offset
));
633 cxt
->label
->nparts_max
--;
634 cxt
->label
->nparts_cur
--;
637 /* remove empty links */
639 q
= self_partition(cxt
, 4);
640 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
641 p
= self_partition(cxt
, i
);
643 if (p
&& !dos_partition_get_size(p
) &&
644 (cxt
->label
->nparts_max
> 5 || (q
&& q
->sys_ind
))) {
645 fdisk_info(cxt
, _("omitting empty partition (%zu)"), i
+1);
646 delete_partition(cxt
, i
);
647 goto remove
; /* numbering changed */
651 DBG(LABEL
, ul_debug("DOS: nparts_max: %zu", cxt
->label
->nparts_max
));
654 static int dos_create_disklabel(struct fdisk_context
*cxt
)
658 struct fdisk_dos_label
*l
;
662 assert(fdisk_is_label(cxt
, DOS
));
664 DBG(LABEL
, ul_debug("DOS: creating new disklabel"));
668 const char *s
= fdisk_script_get_header(cxt
->script
, "label-id");
672 id
= strtoul(s
, &end
, 16);
673 if (!errno
&& end
&& s
< end
) {
675 DBG(LABEL
, ul_debug("DOS: re-use ID from script (0x%08x)", id
));
677 DBG(LABEL
, ul_debug("DOS: failed to parse label=id '%s'", s
));
681 /* random disk signature */
683 DBG(LABEL
, ul_debug("DOS: generate new ID"));
684 random_get_bytes(&id
, sizeof(id
));
687 if (fdisk_has_protected_bootbits(cxt
))
688 rc
= fdisk_init_firstsector_buffer(cxt
, 0, MBR_PT_BOOTBITS_SIZE
);
690 rc
= fdisk_init_firstsector_buffer(cxt
, 0, 0);
697 /* Generate an MBR ID for this disk */
698 mbr_set_id(cxt
->firstsector
, id
);
699 l
->non_pt_changed
= 1;
700 fdisk_label_set_changed(cxt
->label
, 1);
702 /* Put MBR signature */
703 mbr_set_magic(cxt
->firstsector
);
705 fdisk_info(cxt
, _("Created a new DOS disklabel with disk "
706 "identifier 0x%08x."), id
);
710 static int dos_set_disklabel_id(struct fdisk_context
*cxt
)
712 char *end
= NULL
, *str
= NULL
;
713 unsigned int id
, old
;
714 struct fdisk_dos_label
*l
;
719 assert(fdisk_is_label(cxt
, DOS
));
721 DBG(LABEL
, ul_debug("DOS: setting Id"));
724 old
= mbr_get_id(cxt
->firstsector
);
725 rc
= fdisk_ask_string(cxt
,
726 _("Enter the new disk identifier"), &str
);
731 id
= strtoul(str
, &end
, 0);
732 if (errno
|| str
== end
|| (end
&& *end
)) {
733 fdisk_warnx(cxt
, _("Incorrect value."));
738 mbr_set_id(cxt
->firstsector
, id
);
739 l
->non_pt_changed
= 1;
740 fdisk_label_set_changed(cxt
->label
, 1);
742 fdisk_info(cxt
, _("Disk identifier changed from 0x%08x to 0x%08x."),
747 static void get_partition_table_geometry(struct fdisk_context
*cxt
,
748 unsigned int *ph
, unsigned int *ps
)
750 unsigned char *bufp
= cxt
->firstsector
;
751 struct dos_partition
*p
;
757 for (i
= 0; i
< 4; i
++) {
758 p
= mbr_get_partition(bufp
, i
);
759 if (p
->sys_ind
!= 0) {
766 } else if (hh
!= h
|| ss
!= s
)
771 if (!first
&& !bad
) {
776 DBG(LABEL
, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph
, *ps
));
779 static int dos_reset_alignment(struct fdisk_context
*cxt
)
783 assert(fdisk_is_label(cxt
, DOS
));
785 /* overwrite necessary stuff by DOS deprecated stuff */
786 if (is_dos_compatible(cxt
)) {
787 DBG(LABEL
, ul_debug("DOS: resetting alignment for DOS-compatible PT"));
788 if (cxt
->geom
.sectors
)
789 cxt
->first_lba
= cxt
->geom
.sectors
; /* usually 63 */
791 cxt
->grain
= cxt
->sector_size
; /* usually 512 */
797 /* TODO: move to include/pt-dos.h and share with libblkid */
798 #define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
799 #define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
801 static int dos_probe_label(struct fdisk_context
*cxt
)
804 unsigned int h
= 0, s
= 0;
808 assert(fdisk_is_label(cxt
, DOS
));
810 /* ignore disks with AIX magic number */
811 if (memcmp(cxt
->firstsector
, AIX_MAGIC_STRING
, AIX_MAGIC_STRLEN
) == 0)
814 if (!mbr_is_valid_magic(cxt
->firstsector
))
819 get_partition_table_geometry(cxt
, &h
, &s
);
822 cxt
->geom
.sectors
= s
;
824 if (fdisk_has_user_device_geometry(cxt
))
825 fdisk_apply_user_device_properties(cxt
);
828 for (i
= 0; i
< 4; i
++) {
829 struct pte
*pe
= self_pte(cxt
, i
);
832 if (is_used_partition(pe
->pt_entry
))
833 cxt
->label
->nparts_cur
++;
835 if (IS_EXTENDED (pe
->pt_entry
->sys_ind
)) {
836 if (cxt
->label
->nparts_max
!= 4)
838 "Ignoring extra extended partition %zu"),
841 read_extended(cxt
, i
);
845 for (i
= 3; i
< cxt
->label
->nparts_max
; i
++) {
846 struct pte
*pe
= self_pte(cxt
, i
);
847 struct fdisk_dos_label
*l
= self_label(cxt
);
850 if (!mbr_is_valid_magic(pe
->sectorbuffer
)) {
852 "Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
853 "be corrected by w(rite)."),
854 pe
->sectorbuffer
[510],
855 pe
->sectorbuffer
[511],
857 partition_set_changed(cxt
, i
, 1);
859 /* mark also extended as changed to update the first EBR
860 * in situation that there is no logical partitions at all */
861 partition_set_changed(cxt
, l
->ext_index
, 1);
868 static void set_partition(struct fdisk_context
*cxt
,
869 int i
, int doext
, fdisk_sector_t start
,
870 fdisk_sector_t stop
, int sysid
, int boot
)
872 struct pte
*pe
= self_pte(cxt
, i
);
873 struct dos_partition
*p
;
874 fdisk_sector_t offset
;
876 assert(!FDISK_IS_UNDEF(start
));
877 assert(!FDISK_IS_UNDEF(stop
));
881 struct fdisk_dos_label
*l
= self_label(cxt
);
883 offset
= l
->ext_offset
;
889 DBG(LABEL
, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, size=%zu, sysid=%02x",
890 i
, doext
? " [extended]" : "",
892 (size_t) (start
- offset
),
893 (size_t) (stop
- start
+ 1),
896 p
->boot_ind
= boot
? ACTIVE_FLAG
: 0;
898 dos_partition_set_start(p
, start
- offset
);
899 dos_partition_set_size(p
, stop
- start
+ 1);
901 if (is_dos_compatible(cxt
) && (start
/(cxt
->geom
.sectors
*cxt
->geom
.heads
) > 1023))
902 start
= cxt
->geom
.heads
*cxt
->geom
.sectors
*1024 - 1;
903 set_hsc(p
->bh
, p
->bs
, p
->bc
, start
);
904 if (is_dos_compatible(cxt
) && (stop
/(cxt
->geom
.sectors
*cxt
->geom
.heads
) > 1023))
905 stop
= cxt
->geom
.heads
*cxt
->geom
.sectors
*1024 - 1;
906 set_hsc(p
->eh
, p
->es
, p
->ec
, stop
);
907 partition_set_changed(cxt
, i
, 1);
911 static int get_start_from_user( struct fdisk_context
*cxt
,
912 fdisk_sector_t
*start
,
915 fdisk_sector_t limit
,
916 struct fdisk_partition
*pa
)
920 /* try to use template from 'pa' */
921 if (pa
&& pa
->start_follow_default
)
924 else if (pa
&& fdisk_partition_has_start(pa
)) {
925 DBG(LABEL
, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
926 (uintmax_t) pa
->start
, (uintmax_t) low
, (uintmax_t) limit
));
928 if (*start
< low
|| *start
> limit
) {
929 fdisk_warnx(cxt
, _("Start sector %ju out of range."),
934 /* ask user by dialog */
935 struct fdisk_ask
*ask
= fdisk_new_ask();
940 fdisk_ask_set_query(ask
,
941 fdisk_use_cylinders(cxt
) ?
942 _("First cylinder") : _("First sector"));
943 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_NUMBER
);
944 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, low
));
945 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, dflt
));
946 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
948 rc
= fdisk_do_ask(cxt
, ask
);
949 *start
= fdisk_ask_number_get_result(ask
);
950 fdisk_unref_ask(ask
);
953 if (fdisk_use_cylinders(cxt
)) {
954 *start
= (*start
- 1)
955 * fdisk_get_units_per_sector(cxt
);
961 DBG(LABEL
, ul_debug("DOS: start is %ju", (uintmax_t) *start
));
965 static int find_last_free_sector_in_range(
966 struct fdisk_context
*cxt
,
968 fdisk_sector_t begin
,
970 fdisk_sector_t
*result
)
973 fdisk_sector_t last
= end
;
976 size_t i
= logical
? 4 : 0;
979 for ( ; i
< cxt
->label
->nparts_max
; i
++) {
980 struct pte
*pe
= self_pte(cxt
, i
);
981 fdisk_sector_t p_start
= get_abs_partition_start(pe
);
982 fdisk_sector_t p_end
= get_abs_partition_end(pe
);
984 if (is_cleared_partition(pe
->pt_entry
))
987 /* count EBR and begin of the logical partition as used area */
989 p_start
-= cxt
->first_lba
;
991 if (last
>= p_start
&& last
<= p_end
) {
996 DBG(LABEL
, ul_debug("DOS: last free out of range <%ju,%ju>: %ju",
997 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) last
));
1003 } while (last_moved
== 1);
1005 DBG(LABEL
, ul_debug("DOS: last unused sector in range <%ju,%ju>: %ju",
1006 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) last
));
1012 static int find_first_free_sector_in_range(
1013 struct fdisk_context
*cxt
,
1015 fdisk_sector_t begin
,
1017 fdisk_sector_t
*result
)
1019 int first_moved
= 0;
1020 fdisk_sector_t first
= begin
;
1023 size_t i
= logical
? 4 : 0;
1026 for (; i
< cxt
->label
->nparts_max
; i
++) {
1027 struct pte
*pe
= self_pte(cxt
, i
);
1028 fdisk_sector_t p_start
= get_abs_partition_start(pe
);
1029 fdisk_sector_t p_end
= get_abs_partition_end(pe
);
1031 if (is_cleared_partition(pe
->pt_entry
))
1033 /* count EBR and begin of the logical partition as used area */
1035 p_start
-= cxt
->first_lba
;
1036 if (first
< p_start
)
1038 if (first
<= p_end
) {
1039 first
= p_end
+ 1 + (logical
? cxt
->first_lba
: 0);
1043 DBG(LABEL
, ul_debug("DOS: first free out of range <%ju,%ju>: %ju",
1044 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) first
));
1049 } while (first_moved
== 1);
1051 DBG(LABEL
, ul_debug("DOS: first unused sector in range <%ju,%ju>: %ju",
1052 (uintmax_t) begin
, (uintmax_t) end
, (uintmax_t) first
));
1057 static int get_disk_ranges(struct fdisk_context
*cxt
, int logical
,
1058 fdisk_sector_t
*first
, fdisk_sector_t
*last
)
1061 /* logical partitions */
1062 struct fdisk_dos_label
*l
= self_label(cxt
);
1063 struct pte
*ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1068 *first
= l
->ext_offset
+ cxt
->first_lba
;
1069 *last
= get_abs_partition_end(ext_pe
);
1072 /* primary partitions */
1073 if (fdisk_use_cylinders(cxt
) || !cxt
->total_sectors
)
1074 *last
= cxt
->geom
.heads
* cxt
->geom
.sectors
* cxt
->geom
.cylinders
- 1;
1076 *last
= cxt
->total_sectors
- 1;
1078 if (*last
> UINT_MAX
)
1080 *first
= cxt
->first_lba
;
1086 static int find_last_free_sector(struct fdisk_context
*cxt
, int logical
, fdisk_sector_t
*result
)
1088 fdisk_sector_t first
, last
;
1091 rc
= get_disk_ranges(cxt
, logical
, &first
, &last
);
1095 return find_last_free_sector_in_range(cxt
, logical
, first
, last
, result
);
1098 static int find_first_free_sector(struct fdisk_context
*cxt
,
1100 fdisk_sector_t start
,
1101 fdisk_sector_t
*result
)
1103 fdisk_sector_t first
, last
;
1106 rc
= get_disk_ranges(cxt
, logical
, &first
, &last
);
1110 return find_first_free_sector_in_range(cxt
, logical
, start
, last
, result
);
1113 static int add_partition(struct fdisk_context
*cxt
, size_t n
,
1114 struct fdisk_partition
*pa
)
1116 int sys
, read
= 0, rc
, isrel
= 0, is_logical
;
1117 struct fdisk_dos_label
*l
= self_label(cxt
);
1118 struct dos_partition
*p
= self_partition(cxt
, n
);
1119 struct pte
*ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1120 struct fdisk_ask
*ask
= NULL
;
1122 fdisk_sector_t start
, stop
= 0, limit
, temp
;
1124 DBG(LABEL
, ul_debug("DOS: adding partition %zu", n
));
1126 sys
= pa
&& pa
->type
? pa
->type
->code
: MBR_LINUX_DATA_PARTITION
;
1127 is_logical
= n
>= 4;
1129 if (p
&& is_used_partition(p
)) {
1130 fdisk_warnx(cxt
, _("Partition %zu is already defined. "
1131 "Delete it before re-adding it."),
1136 rc
= find_last_free_sector(cxt
, is_logical
, &limit
);
1138 fdisk_warnx(cxt
, _("No free sectors available."));
1143 if (cxt
->parent
&& fdisk_is_label(cxt
->parent
, GPT
))
1144 start
= 1; /* Bad boy modifies hybrid MBR */
1146 if (cxt
->script
&& pa
&& fdisk_partition_has_start(pa
)
1147 && pa
->start
< cxt
->first_lba
1149 fdisk_set_first_lba(cxt
, 1);
1151 start
= cxt
->first_lba
;
1156 if (cxt
->script
&& pa
&& fdisk_partition_has_start(pa
)
1157 && pa
->start
>= l
->ext_offset
1158 && pa
->start
< l
->ext_offset
+ cxt
->first_lba
)
1159 fdisk_set_first_lba(cxt
, 1);
1161 start
= l
->ext_offset
+ cxt
->first_lba
;
1165 * Ask for first sector
1168 fdisk_sector_t dflt
, aligned
;
1172 rc
= find_first_free_sector(cxt
, is_logical
, start
, &dflt
);
1174 fdisk_warnx(cxt
, _("No free sectors available."));
1179 if (n
>= 4 && pa
&& fdisk_partition_has_start(pa
) && cxt
->script
1180 && cxt
->first_lba
> 1
1181 && temp
== start
- cxt
->first_lba
) {
1182 fdisk_set_first_lba(cxt
, 1);
1186 /* the default sector should be aligned and unused */
1188 aligned
= fdisk_align_lba_in_range(cxt
, dflt
, dflt
, limit
);
1189 find_first_free_sector(cxt
, is_logical
, aligned
, &dflt
);
1190 } while (dflt
!= aligned
&& dflt
> aligned
&& dflt
< limit
);
1196 if (start
>= temp
+ fdisk_get_units_per_sector(cxt
)
1198 fdisk_info(cxt
, _("Sector %llu is already allocated."),
1202 if (pa
&& (fdisk_partition_has_start(pa
) ||
1203 pa
->start_follow_default
))
1207 if (!read
&& start
== temp
) {
1208 rc
= get_start_from_user(cxt
, &start
, temp
, dflt
, limit
, pa
);
1213 } while (start
!= temp
|| !read
);
1216 /* The first EBR is stored at begin of the extended partition */
1217 struct pte
*pe
= self_pte(cxt
, n
);
1220 pe
->offset
= l
->ext_offset
;
1222 /* The second (and another) EBR */
1223 struct pte
*pe
= self_pte(cxt
, n
);
1226 pe
->offset
= start
- cxt
->first_lba
;
1227 if (pe
->offset
== l
->ext_offset
) { /* must be corrected */
1229 if (cxt
->first_lba
== 1)
1234 rc
= find_last_free_sector_in_range(cxt
, is_logical
, start
, limit
, &stop
);
1236 fdisk_warnx(cxt
, _("No free sectors available."));
1242 * Ask for last sector
1244 if (fdisk_cround(cxt
, start
) == fdisk_cround(cxt
, limit
))
1246 else if (pa
&& pa
->end_follow_default
)
1248 else if (pa
&& fdisk_partition_has_size(pa
)) {
1249 stop
= start
+ pa
->size
;
1250 isrel
= pa
->size_explicit
? 0 : 1;
1251 if ((!isrel
|| !alignment_required(cxt
)) && stop
> start
)
1254 /* ask user by dialog */
1257 ask
= fdisk_new_ask();
1259 fdisk_reset_ask(ask
);
1262 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_OFFSET
);
1264 if (fdisk_use_cylinders(cxt
)) {
1265 fdisk_ask_set_query(ask
, _("Last cylinder, +/-cylinders or +/-size{K,M,G,T,P}"));
1266 fdisk_ask_number_set_unit(ask
,
1268 fdisk_get_units_per_sector(cxt
));
1270 fdisk_ask_set_query(ask
, _("Last sector, +/-sectors or +/-size{K,M,G,T,P}"));
1271 fdisk_ask_number_set_unit(ask
,cxt
->sector_size
);
1274 fdisk_ask_number_set_low(ask
, fdisk_cround(cxt
, start
));
1275 fdisk_ask_number_set_default(ask
, fdisk_cround(cxt
, limit
));
1276 fdisk_ask_number_set_high(ask
, fdisk_cround(cxt
, limit
));
1277 fdisk_ask_number_set_base(ask
, fdisk_cround(cxt
, start
)); /* base for relative input */
1278 fdisk_ask_number_set_wrap_negative(ask
, 1); /* wrap negative around high */
1280 rc
= fdisk_do_ask(cxt
, ask
);
1284 stop
= fdisk_ask_number_get_result(ask
);
1285 isrel
= fdisk_ask_number_is_relative(ask
);
1286 if (fdisk_use_cylinders(cxt
)) {
1287 stop
= stop
* fdisk_get_units_per_sector(cxt
) - 1;
1292 if (stop
>= start
&& stop
<= limit
)
1294 fdisk_warnx(cxt
, _("Value out of range."));
1298 DBG(LABEL
, ul_debug("DOS: raw stop: %ju [limit %ju]", (uintmax_t) stop
, (uintmax_t) limit
));
1303 if (isrel
&& stop
- start
< (cxt
->grain
/ fdisk_get_sector_size(cxt
))) {
1304 /* Don't try to be smart on very small partitions and don't align so small sizes */
1308 DBG(LABEL
, ul_debug("DOS: don't align end of tiny partition [start=%ju, stop=%ju, grain=%lu]",
1309 (uintmax_t)start
, (uintmax_t)stop
, cxt
->grain
));
1312 if (stop
< limit
&& isrel
&& alignment_required(cxt
)) {
1313 /* the last sector has not been exactly requested (but
1314 * defined by +size{K,M,G} convention), so be smart and
1315 * align the end of the partition. The next partition
1316 * will start at phy.block boundary.
1318 stop
= fdisk_align_lba_in_range(cxt
, stop
, start
, limit
);
1323 DBG(LABEL
, ul_debug("DOS: aligned stop: %ju", (uintmax_t) stop
));
1326 set_partition(cxt
, n
, 0, start
, stop
, sys
, fdisk_partition_is_bootable(pa
));
1328 struct pte
*pe
= self_pte(cxt
, n
);
1331 set_partition(cxt
, n
- 1, 1, pe
->offset
, stop
,
1332 MBR_DOS_EXTENDED_PARTITION
, 0);
1337 struct fdisk_parttype
*t
=
1338 fdisk_label_get_parttype_from_code(cxt
->label
, sys
);
1339 fdisk_info_new_partition(cxt
, n
+ 1, start
, stop
, t
);
1340 fdisk_unref_parttype(t
);
1344 if (IS_EXTENDED(sys
)) {
1345 struct pte
*pen
= self_pte(cxt
, n
);
1349 l
->ext_offset
= start
;
1353 fdisk_label_set_changed(cxt
->label
, 1);
1356 fdisk_unref_ask(ask
);
1360 static int add_logical(struct fdisk_context
*cxt
,
1361 struct fdisk_partition
*pa
,
1370 assert(self_label(cxt
)->ext_offset
);
1372 DBG(LABEL
, ul_debug("DOS: nparts max: %zu", cxt
->label
->nparts_max
));
1373 pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
1376 if (!pe
->sectorbuffer
) {
1377 pe
->sectorbuffer
= calloc(1, cxt
->sector_size
);
1378 if (!pe
->sectorbuffer
)
1380 DBG(LABEL
, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1381 cxt
->label
->nparts_max
, pe
->sectorbuffer
));
1382 pe
->private_sectorbuffer
= 1;
1384 pe
->pt_entry
= mbr_get_partition(pe
->sectorbuffer
, 0);
1385 pe
->ex_entry
= pe
->pt_entry
+ 1;
1387 partition_set_changed(cxt
, cxt
->label
->nparts_max
, 1);
1389 cxt
->label
->nparts_max
++;
1391 /* this message makes sense only when we use extended/primary/logical
1392 * dialog. The dialog is disable for scripts, see dos_add_partition() */
1394 fdisk_info(cxt
, _("Adding logical partition %zu"),
1395 cxt
->label
->nparts_max
);
1396 *partno
= cxt
->label
->nparts_max
- 1;
1397 rc
= add_partition(cxt
, *partno
, pa
);
1400 /* reset on error */
1401 cxt
->label
->nparts_max
--;
1402 pe
->pt_entry
= NULL
;
1403 pe
->ex_entry
= NULL
;
1411 static void check(struct fdisk_context
*cxt
, size_t n
,
1412 unsigned int h
, unsigned int s
, unsigned int c
,
1415 unsigned int total
, real_s
, real_c
;
1417 if (!is_dos_compatible(cxt
))
1420 real_s
= sector(s
) - 1;
1421 real_c
= cylinder(s
, c
);
1422 total
= (real_c
* cxt
->geom
.heads
+ h
) * cxt
->geom
.sectors
+ real_s
;
1425 fdisk_warnx(cxt
, _("Partition %zu: contains sector 0"), n
);
1426 if (h
>= cxt
->geom
.heads
)
1427 fdisk_warnx(cxt
, _("Partition %zu: head %d greater than "
1428 "maximum %d"), n
, h
+ 1, cxt
->geom
.heads
);
1429 if (real_s
>= cxt
->geom
.sectors
)
1430 fdisk_warnx(cxt
, _("Partition %zu: sector %d greater than "
1431 "maximum %llu"), n
, s
, cxt
->geom
.sectors
);
1432 if (real_c
>= cxt
->geom
.cylinders
)
1433 fdisk_warnx(cxt
, _("Partition %zu: cylinder %d greater than "
1436 cxt
->geom
.cylinders
);
1438 if (cxt
->geom
.cylinders
<= 1024 && start
!= total
)
1439 fdisk_warnx(cxt
, _("Partition %zu: previous sectors %u "
1440 "disagrees with total %u"), n
, start
, total
);
1443 /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1444 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1445 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1446 * Lubkin Oct. 1991). */
1449 long2chs(struct fdisk_context
*cxt
, unsigned long ls
,
1450 unsigned int *c
, unsigned int *h
, unsigned int *s
) {
1451 int spc
= cxt
->geom
.heads
* cxt
->geom
.sectors
;
1455 *h
= ls
/ cxt
->geom
.sectors
;
1456 *s
= ls
% cxt
->geom
.sectors
+ 1; /* sectors count from 1 */
1459 static void check_consistency(struct fdisk_context
*cxt
, struct dos_partition
*p
,
1462 unsigned int pbc
, pbh
, pbs
; /* physical beginning c, h, s */
1463 unsigned int pec
, peh
, pes
; /* physical ending c, h, s */
1464 unsigned int lbc
, lbh
, lbs
; /* logical beginning c, h, s */
1465 unsigned int lec
, leh
, les
; /* logical ending c, h, s */
1467 if (!is_dos_compatible(cxt
))
1470 if (!cxt
->geom
.heads
|| !cxt
->geom
.sectors
|| (partition
>= 4))
1471 return; /* do not check extended partitions */
1473 /* physical beginning c, h, s */
1474 pbc
= (p
->bc
& 0xff) | ((p
->bs
<< 2) & 0x300);
1478 /* physical ending c, h, s */
1479 pec
= (p
->ec
& 0xff) | ((p
->es
<< 2) & 0x300);
1483 /* compute logical beginning (c, h, s) */
1484 long2chs(cxt
, dos_partition_get_start(p
), &lbc
, &lbh
, &lbs
);
1486 /* compute logical ending (c, h, s) */
1487 long2chs(cxt
, dos_partition_get_start(p
) + dos_partition_get_size(p
) - 1, &lec
, &leh
, &les
);
1489 /* Same physical / logical beginning? */
1490 if (cxt
->geom
.cylinders
<= 1024
1491 && (pbc
!= lbc
|| pbh
!= lbh
|| pbs
!= lbs
)) {
1492 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1493 "beginnings (non-Linux?): "
1494 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1500 /* Same physical / logical ending? */
1501 if (cxt
->geom
.cylinders
<= 1024
1502 && (pec
!= lec
|| peh
!= leh
|| pes
!= les
)) {
1503 fdisk_warnx(cxt
, _("Partition %zu: different physical/logical "
1504 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1510 /* Ending on cylinder boundary? */
1511 if (peh
!= (cxt
->geom
.heads
- 1) || pes
!= cxt
->geom
.sectors
) {
1512 fdisk_warnx(cxt
, _("Partition %zu: does not end on "
1513 "cylinder boundary."),
1518 static void fill_bounds(struct fdisk_context
*cxt
,
1519 fdisk_sector_t
*first
, fdisk_sector_t
*last
)
1522 struct pte
*pe
= self_pte(cxt
, 0);
1523 struct dos_partition
*p
;
1526 for (i
= 0; i
< cxt
->label
->nparts_max
; pe
++,i
++) {
1528 if (is_cleared_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
1529 first
[i
] = SIZE_MAX
;
1532 first
[i
] = get_abs_partition_start(pe
);
1533 last
[i
] = get_abs_partition_end(pe
);
1538 static int dos_verify_disklabel(struct fdisk_context
*cxt
)
1541 fdisk_sector_t total
= 1, n_sectors
= cxt
->total_sectors
;
1542 fdisk_sector_t first
[cxt
->label
->nparts_max
],
1543 last
[cxt
->label
->nparts_max
];
1544 struct dos_partition
*p
;
1545 struct fdisk_dos_label
*l
= self_label(cxt
);
1547 assert(fdisk_is_label(cxt
, DOS
));
1549 fill_bounds(cxt
, first
, last
);
1550 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
1551 struct pte
*pe
= self_pte(cxt
, i
);
1553 p
= self_partition(cxt
, i
);
1554 if (p
&& is_used_partition(p
) && !IS_EXTENDED(p
->sys_ind
)) {
1555 check_consistency(cxt
, p
, i
);
1557 if (get_abs_partition_start(pe
) < first
[i
])
1559 "Partition %zu: bad start-of-data."),
1562 check(cxt
, i
+ 1, p
->eh
, p
->es
, p
->ec
, last
[i
]);
1563 total
+= last
[i
] + 1 - first
[i
];
1566 total
+= get_abs_partition_start(pe
) - 1;
1568 for (j
= 0; j
< i
; j
++) {
1569 if ((first
[i
] >= first
[j
] && first
[i
] <= last
[j
])
1570 || ((last
[i
] <= last
[j
] && last
[i
] >= first
[j
]))) {
1572 fdisk_warnx(cxt
, _("Partition %zu: "
1573 "overlaps partition %zu."),
1576 total
+= first
[i
] >= first
[j
] ?
1577 first
[i
] : first
[j
];
1578 total
-= last
[i
] <= last
[j
] ?
1585 if (l
->ext_offset
) {
1586 fdisk_sector_t e_last
;
1587 struct pte
*ext_pe
= self_pte(cxt
, l
->ext_index
);
1590 e_last
= get_abs_partition_end(ext_pe
);
1592 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1594 p
= self_partition(cxt
, i
);
1598 if (i
!= 4 || i
+ 1 < cxt
->label
->nparts_max
)
1600 _("Partition %zu: empty."),
1602 } else if (first
[i
] < l
->ext_offset
1603 || last
[i
] > e_last
) {
1605 fdisk_warnx(cxt
, _("Logical partition %zu: "
1606 "not entirely in partition %zu."),
1607 i
+ 1, l
->ext_index
+ 1);
1612 if (total
> n_sectors
)
1613 fdisk_warnx(cxt
, _("Total allocated sectors %llu greater "
1614 "than the maximum %llu."), total
, n_sectors
);
1615 else if (total
< n_sectors
)
1616 fdisk_warnx(cxt
, _("Remaining %lld unallocated %ld-byte "
1617 "sectors."), n_sectors
- total
, cxt
->sector_size
);
1623 * Ask the user for new partition type information (logical, extended).
1624 * This function calls the actual partition adding logic - add_partition.
1628 static int dos_add_partition(struct fdisk_context
*cxt
,
1629 struct fdisk_partition
*pa
,
1632 size_t i
, free_primary
= 0, free_sectors
= 0;
1633 fdisk_sector_t last
= 0, grain
;
1635 struct fdisk_dos_label
*l
;
1637 size_t res
= 0; /* partno */
1641 assert(fdisk_is_label(cxt
, DOS
));
1643 DBG(LABEL
, ul_debug("DOS: new partition wanted"));
1645 l
= self_label(cxt
);
1646 ext_pe
= l
->ext_offset
? self_pte(cxt
, l
->ext_index
) : NULL
;
1649 * partition template (@pa) based partitioning
1652 /* A) template specifies start within extended partition; add logical */
1653 if (pa
&& fdisk_partition_has_start(pa
) && ext_pe
1654 && pa
->start
>= l
->ext_offset
1655 && pa
->start
<= get_abs_partition_end(ext_pe
)) {
1656 DBG(LABEL
, ul_debug("DOS: pa template %p: add logical (by offset)", pa
));
1658 if (fdisk_partition_has_partno(pa
) && fdisk_partition_get_partno(pa
) < 4) {
1659 DBG(LABEL
, ul_debug("DOS: pa template specifies partno<4 for logical partition"));
1662 rc
= add_logical(cxt
, pa
, &res
);
1665 /* B) template specifies start out of extended partition; add primary */
1666 } else if (pa
&& fdisk_partition_has_start(pa
) && ext_pe
) {
1667 DBG(LABEL
, ul_debug("DOS: pa template %p: add primary (by offset)", pa
));
1669 if (fdisk_partition_has_partno(pa
) && fdisk_partition_get_partno(pa
) >= 4) {
1670 DBG(LABEL
, ul_debug("DOS: pa template specifies partno>=4 for primary partition"));
1673 if (ext_pe
&& pa
->type
&& IS_EXTENDED(pa
->type
->code
)) {
1674 fdisk_warnx(cxt
, _("Extended partition already exists."));
1677 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1679 rc
= add_partition(cxt
, res
, pa
);
1682 /* C) template specifies start (or default), partno < 4; add primary */
1683 } else if (pa
&& (fdisk_partition_start_is_default(pa
) || fdisk_partition_has_start(pa
))
1684 && fdisk_partition_has_partno(pa
)
1685 && pa
->partno
< 4) {
1686 DBG(LABEL
, ul_debug("DOS: pa template %p: add primary (by partno)", pa
));
1688 if (ext_pe
&& pa
->type
&& IS_EXTENDED(pa
->type
->code
)) {
1689 fdisk_warnx(cxt
, _("Extended partition already exists."));
1692 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1694 rc
= add_partition(cxt
, res
, pa
);
1697 /* D) template specifies start (or default), partno >= 4; add logical */
1698 } else if (pa
&& (fdisk_partition_start_is_default(pa
) || fdisk_partition_has_start(pa
))
1699 && fdisk_partition_has_partno(pa
)
1700 && pa
->partno
>= 4) {
1701 DBG(LABEL
, ul_debug("DOS: pa template %p: add logical (by partno)", pa
));
1704 fdisk_warnx(cxt
, _("Extended partition does not exists. Failed to add logical partition."));
1706 } else if (fdisk_partition_has_start(pa
)
1707 && pa
->start
< l
->ext_offset
1708 && pa
->start
> get_abs_partition_end(ext_pe
)) {
1709 DBG(LABEL
, ul_debug("DOS: pa template specifies partno>=4, but start out of extended"));
1713 rc
= add_logical(cxt
, pa
, &res
);
1717 DBG(LABEL
, ul_debug("DOS: dialog driven partitioning"));
1718 /* Note @pa may be still used for things like partition type, etc */
1720 /* check if there is space for primary partition */
1721 grain
= cxt
->grain
> cxt
->sector_size
? cxt
->grain
/ cxt
->sector_size
: 1;
1722 last
= cxt
->first_lba
;
1724 for (i
= 0; i
< 4; i
++) {
1725 struct dos_partition
*p
= self_partition(cxt
, i
);
1728 if (is_used_partition(p
)) {
1729 fdisk_sector_t start
= dos_partition_get_start(p
);
1730 if (last
+ grain
<= start
)
1732 last
= start
+ dos_partition_get_size(p
);
1736 if (last
+ grain
< cxt
->total_sectors
- 1)
1739 if (!free_primary
&& cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1740 fdisk_info(cxt
, _("The maximum number of partitions has "
1745 if (!free_primary
|| !free_sectors
) {
1746 DBG(LABEL
, ul_debug("DOS: primary impossible, add logical"));
1747 if (l
->ext_offset
) {
1748 if (!pa
|| fdisk_partition_has_start(pa
)) {
1749 /* See above case A); here we have start, but
1750 * out of extended partition */
1753 msg
= _("All primary partitions are in use.");
1755 msg
= _("All space for primary partitions is in use.");
1757 if (pa
&& fdisk_partition_has_start(pa
)) {
1758 fdisk_warnx(cxt
, msg
);
1761 fdisk_info(cxt
, msg
);
1763 rc
= add_logical(cxt
, pa
, &res
);
1766 fdisk_info(cxt
, _("All space for primary partitions is in use."));
1768 /* TRANSLATORS: Try to keep this within 80 characters. */
1769 fdisk_info(cxt
, _("To create more partitions, first replace "
1770 "a primary with an extended partition."));
1773 } else if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1774 fdisk_info(cxt
, _("All logical partitions are in use. "
1775 "Adding a primary partition."));
1776 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1778 rc
= add_partition(cxt
, res
, pa
);
1781 struct fdisk_ask
*ask
;
1784 /* the default layout for scripts is to create primary partitions */
1785 if (cxt
->script
|| !fdisk_has_dialogs(cxt
)) {
1786 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1788 rc
= add_partition(cxt
, res
, pa
);
1792 ask
= fdisk_new_ask();
1795 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_MENU
);
1796 fdisk_ask_set_query(ask
, _("Partition type"));
1797 fdisk_ask_menu_set_default(ask
, free_primary
== 1
1798 && !l
->ext_offset
? 'e' : 'p');
1799 snprintf(hint
, sizeof(hint
),
1800 _("%zu primary, %d extended, %zu free"),
1801 4 - (l
->ext_offset
? 1 : 0) - free_primary
,
1802 l
->ext_offset
? 1 : 0,
1805 fdisk_ask_menu_add_item(ask
, 'p', _("primary"), hint
);
1807 fdisk_ask_menu_add_item(ask
, 'e', _("extended"), _("container for logical partitions"));
1809 fdisk_ask_menu_add_item(ask
, 'l', _("logical"), _("numbered from 5"));
1811 rc
= fdisk_do_ask(cxt
, ask
);
1813 fdisk_ask_menu_get_result(ask
, &c
);
1814 fdisk_unref_ask(ask
);
1819 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1821 rc
= add_partition(cxt
, res
, pa
);
1823 } else if (c
== 'l' && l
->ext_offset
) {
1824 rc
= add_logical(cxt
, pa
, &res
);
1826 } else if (c
== 'e' && !l
->ext_offset
) {
1827 rc
= get_partition_unused_primary(cxt
, pa
, &res
);
1829 struct fdisk_partition
*xpa
= NULL
;
1830 struct fdisk_parttype
*t
;
1832 t
= fdisk_label_get_parttype_from_code(cxt
->label
,
1833 MBR_DOS_EXTENDED_PARTITION
);
1835 pa
= xpa
= fdisk_new_partition();
1839 fdisk_partition_set_type(pa
, t
);
1840 rc
= add_partition(cxt
, res
, pa
);
1842 fdisk_unref_partition(xpa
);
1848 fdisk_warnx(cxt
, _("Invalid partition type `%c'."), c
);
1852 cxt
->label
->nparts_cur
++;
1859 static int write_sector(struct fdisk_context
*cxt
, fdisk_sector_t secno
,
1864 rc
= seek_sector(cxt
, secno
);
1866 fdisk_warn(cxt
, _("Cannot write sector %jd: seek failed"),
1871 DBG(LABEL
, ul_debug("DOS: writing to sector %ju", (uintmax_t) secno
));
1873 if (write(cxt
->dev_fd
, buf
, cxt
->sector_size
) != (ssize_t
) cxt
->sector_size
)
1878 static int dos_write_disklabel(struct fdisk_context
*cxt
)
1880 struct fdisk_dos_label
*l
= self_label(cxt
);
1882 int rc
= 0, mbr_changed
= 0;
1886 assert(fdisk_is_label(cxt
, DOS
));
1888 DBG(LABEL
, ul_debug("DOS: write PT requested [label-changed: %d, non-pt-changed: %d]",
1889 cxt
->label
->changed
, l
->non_pt_changed
));
1891 mbr_changed
= l
->non_pt_changed
;
1893 /* MBR (primary partitions) */
1895 for (i
= 0; i
< 4; i
++) {
1896 struct pte
*pe
= self_pte(cxt
, i
);
1904 DBG(LABEL
, ul_debug("DOS: MBR changed, writing"));
1905 mbr_set_magic(cxt
->firstsector
);
1906 rc
= write_sector(cxt
, 0, cxt
->firstsector
);
1911 if (cxt
->label
->nparts_max
<= 4 && l
->ext_offset
) {
1912 /* we have empty extended partition, check if the partition has
1913 * been modified and then cleanup possible remaining EBR */
1914 struct pte
*pe
= self_pte(cxt
, l
->ext_index
);
1915 unsigned char empty
[512] = { 0 };
1916 fdisk_sector_t off
= pe
? get_abs_partition_start(pe
) : 0;
1918 if (off
&& pe
->changed
) {
1919 mbr_set_magic(empty
);
1920 write_sector(cxt
, off
, empty
);
1924 /* EBR (logical partitions) */
1925 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1926 struct pte
*pe
= self_pte(cxt
, i
);
1929 if (!pe
->changed
|| !pe
->offset
|| !pe
->sectorbuffer
)
1932 mbr_set_magic(pe
->sectorbuffer
);
1933 rc
= write_sector(cxt
, pe
->offset
, pe
->sectorbuffer
);
1942 static int dos_locate_disklabel(struct fdisk_context
*cxt
, int n
,
1943 const char **name
, uint64_t *offset
, size_t *size
)
1958 /* extended partitions */
1959 if ((size_t)n
- 1 + 4 < cxt
->label
->nparts_max
) {
1960 struct pte
*pe
= self_pte(cxt
, n
- 1 + 4);
1963 assert(pe
->private_sectorbuffer
);
1966 *offset
= (uint64_t) pe
->offset
* cxt
->sector_size
;
1977 * Check whether partition entries are ordered by their starting positions.
1978 * Return 0 if OK. Return i if partition i should have been earlier.
1979 * Two separate checks: primary and logical partitions.
1981 static int wrong_p_order(struct fdisk_context
*cxt
, size_t *prev
)
1983 size_t last_p_start_pos
= 0, p_start_pos
;
1984 size_t i
, last_i
= 0;
1986 for (i
= 0 ; i
< cxt
->label
->nparts_max
; i
++) {
1988 struct pte
*pe
= self_pte(cxt
, i
);
1989 struct dos_partition
*p
;
1996 last_p_start_pos
= 0;
1998 if (is_used_partition(p
)) {
1999 p_start_pos
= get_abs_partition_start(pe
);
2001 if (last_p_start_pos
> p_start_pos
) {
2007 last_p_start_pos
= p_start_pos
;
2014 static int dos_get_disklabel_item(struct fdisk_context
*cxt
, struct fdisk_labelitem
*item
)
2020 assert(fdisk_is_label(cxt
, DOS
));
2023 case FDISK_LABELITEM_ID
:
2025 unsigned int num
= mbr_get_id(cxt
->firstsector
);
2026 item
->name
= _("Disk identifier");
2028 if (asprintf(&item
->data
.str
, "0x%08x", num
) < 0)
2033 if (item
->id
< __FDISK_NLABELITEMS
)
2034 rc
= 1; /* unsupported generic item */
2036 rc
= 2; /* out of range */
2044 static int dos_get_partition(struct fdisk_context
*cxt
, size_t n
,
2045 struct fdisk_partition
*pa
)
2047 struct dos_partition
*p
;
2049 struct fdisk_dos_label
*lb
;
2054 assert(fdisk_is_label(cxt
, DOS
));
2056 lb
= self_label(cxt
);
2058 pe
= self_pte(cxt
, n
);
2062 pa
->used
= !is_cleared_partition(p
);
2066 pa
->type
= dos_partition_parttype(cxt
, p
);
2067 pa
->boot
= p
->boot_ind
== ACTIVE_FLAG
? 1 : 0;
2068 pa
->start
= get_abs_partition_start(pe
);
2069 pa
->size
= dos_partition_get_size(p
);
2070 pa
->container
= lb
->ext_offset
&& n
== lb
->ext_index
;
2073 pa
->parent_partno
= lb
->ext_index
;
2075 if (p
->boot_ind
&& asprintf(&pa
->attrs
, "%02x", p
->boot_ind
) < 0)
2079 if (asprintf(&pa
->start_chs
, "%d/%d/%d",
2080 cylinder(p
->bs
, p
->bc
),
2086 if (asprintf(&pa
->end_chs
, "%d/%d/%d",
2087 cylinder(p
->es
, p
->ec
),
2095 static int has_logical(struct fdisk_context
*cxt
)
2098 struct fdisk_dos_label
*l
= self_label(cxt
);
2100 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2101 if (l
->ptes
[i
].pt_entry
)
2107 static int dos_set_partition(struct fdisk_context
*cxt
, size_t n
,
2108 struct fdisk_partition
*pa
)
2110 struct fdisk_dos_label
*l
;
2111 struct dos_partition
*p
;
2114 fdisk_sector_t start
, size
;
2119 assert(fdisk_is_label(cxt
, DOS
));
2121 if (n
>= cxt
->label
->nparts_max
)
2124 l
= self_label(cxt
);
2125 p
= self_partition(cxt
, n
);
2127 pe
= self_pte(cxt
, n
);
2131 orgtype
= p
->sys_ind
;
2134 if (IS_EXTENDED(pa
->type
->code
) && l
->ext_offset
&& l
->ext_index
!= n
) {
2135 fdisk_warnx(cxt
, _("Extended partition already exists."));
2139 if (!pa
->type
->code
)
2140 fdisk_warnx(cxt
, _("Type 0 means free space to many systems. "
2141 "Having partitions of type 0 is probably unwise."));
2143 if (IS_EXTENDED(p
->sys_ind
) && !IS_EXTENDED(pa
->type
->code
) && has_logical(cxt
)) {
2145 "Cannot change type of the extended partition which is "
2146 "already used by logical partitions. Delete logical "
2147 "partitions first."));
2152 FDISK_INIT_UNDEF(start
);
2153 FDISK_INIT_UNDEF(size
);
2155 if (fdisk_partition_has_start(pa
))
2157 if (fdisk_partition_has_size(pa
))
2160 if (!FDISK_IS_UNDEF(start
) || !FDISK_IS_UNDEF(size
)) {
2161 DBG(LABEL
, ul_debug("DOS: resize partition"));
2163 if (FDISK_IS_UNDEF(start
))
2164 start
= get_abs_partition_start(pe
);
2165 if (FDISK_IS_UNDEF(size
))
2166 size
= dos_partition_get_size(p
);
2168 set_partition(cxt
, n
, 0, start
, start
+ size
- 1,
2169 pa
->type
? pa
->type
->code
: p
->sys_ind
,
2170 FDISK_IS_UNDEF(pa
->boot
) ?
2171 p
->boot_ind
== ACTIVE_FLAG
:
2172 fdisk_partition_is_bootable(pa
));
2174 DBG(LABEL
, ul_debug("DOS: keep size, modify properties"));
2176 p
->sys_ind
= pa
->type
->code
;
2177 if (!FDISK_IS_UNDEF(pa
->boot
))
2178 p
->boot_ind
= fdisk_partition_is_bootable(pa
) ? ACTIVE_FLAG
: 0;
2182 if (IS_EXTENDED(pa
->type
->code
) && !IS_EXTENDED(orgtype
)) {
2183 /* new extended partition - create a reference */
2185 l
->ext_offset
= dos_partition_get_start(p
);
2187 } else if (IS_EXTENDED(orgtype
)) {
2188 /* remove extended partition */
2189 cxt
->label
->nparts_max
= 4;
2190 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
2196 partition_set_changed(cxt
, n
, 1);
2200 static void print_chain_of_logicals(struct fdisk_context
*cxt
)
2203 struct fdisk_dos_label
*l
= self_label(cxt
);
2205 fputc('\n', stdout
);
2207 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
2208 struct pte
*pe
= self_pte(cxt
, i
);
2211 fprintf(stderr
, "#%02zu EBR [%10ju], "
2212 "data[start=%10ju (%10ju), size=%10ju], "
2213 "link[start=%10ju (%10ju), size=%10ju]\n",
2214 i
, (uintmax_t) pe
->offset
,
2216 (uintmax_t) dos_partition_get_start(pe
->pt_entry
),
2217 (uintmax_t) get_abs_partition_start(pe
),
2218 (uintmax_t) dos_partition_get_size(pe
->pt_entry
),
2220 (uintmax_t) dos_partition_get_start(pe
->ex_entry
),
2221 (uintmax_t) l
->ext_offset
+ dos_partition_get_start(pe
->ex_entry
),
2222 (uintmax_t) dos_partition_get_size(pe
->ex_entry
));
2226 static int cmp_ebr_offsets(const void *a
, const void *b
)
2228 const struct pte
*ae
= (const struct pte
*) a
,
2229 *be
= (const struct pte
*) b
;
2231 if (ae
->offset
== 0 && be
->offset
== 0)
2233 if (ae
->offset
== 0)
2235 if (be
->offset
== 0)
2238 return cmp_numbers(ae
->offset
, be
->offset
);
2242 * Fix the chain of logicals.
2244 * The function does not modify data partitions within EBR tables
2245 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
2246 * (pte->ex_entry) between EBR tables.
2249 static void fix_chain_of_logicals(struct fdisk_context
*cxt
)
2251 struct fdisk_dos_label
*l
= self_label(cxt
);
2255 DBG(LABEL
, print_chain_of_logicals(cxt
));
2257 /* Sort chain by EBR offsets */
2258 qsort(&l
->ptes
[4], cxt
->label
->nparts_max
- 4, sizeof(struct pte
),
2262 /* Sort data partitions by start */
2263 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
2264 struct pte
*cur
= self_pte(cxt
, i
),
2265 *nxt
= self_pte(cxt
, i
+ 1);
2270 if (get_abs_partition_start(cur
) >
2271 get_abs_partition_start(nxt
)) {
2273 struct dos_partition tmp
= *cur
->pt_entry
;
2274 fdisk_sector_t cur_start
= get_abs_partition_start(cur
),
2275 nxt_start
= get_abs_partition_start(nxt
);
2277 /* swap data partitions */
2278 *cur
->pt_entry
= *nxt
->pt_entry
;
2279 *nxt
->pt_entry
= tmp
;
2281 /* Recount starts according to EBR offsets, the absolute
2282 * address still has to be the same! */
2283 dos_partition_set_start(cur
->pt_entry
, nxt_start
- cur
->offset
);
2284 dos_partition_set_start(nxt
->pt_entry
, cur_start
- nxt
->offset
);
2286 partition_set_changed(cxt
, i
, 1);
2287 partition_set_changed(cxt
, i
+ 1, 1);
2292 /* Update EBR links */
2293 for (i
= 4; i
< cxt
->label
->nparts_max
- 1; i
++) {
2294 struct pte
*cur
= self_pte(cxt
, i
),
2295 *nxt
= self_pte(cxt
, i
+ 1);
2300 fdisk_sector_t noff
= nxt
->offset
- l
->ext_offset
,
2301 ooff
= dos_partition_get_start(cur
->ex_entry
);
2306 DBG(LABEL
, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2307 (uintmax_t) cur
->offset
,
2308 (uintmax_t) ooff
, (uintmax_t) noff
));
2310 set_partition(cxt
, i
, 1, nxt
->offset
,
2311 get_abs_partition_end(nxt
),
2312 MBR_DOS_EXTENDED_PARTITION
, 0);
2315 /* always terminate the chain ! */
2316 last
= self_pte(cxt
, cxt
->label
->nparts_max
- 1);
2318 clear_partition(last
->ex_entry
);
2319 partition_set_changed(cxt
, cxt
->label
->nparts_max
- 1, 1);
2322 DBG(LABEL
, print_chain_of_logicals(cxt
));
2325 static int dos_reorder(struct fdisk_context
*cxt
)
2327 struct pte
*pei
, *pek
;
2330 if (!wrong_p_order(cxt
, NULL
)) {
2331 fdisk_info(cxt
, _("Nothing to do. Ordering is correct already."));
2335 while ((i
= wrong_p_order(cxt
, &k
)) != 0 && i
< 4) {
2336 /* partition i should have come earlier, move it */
2337 /* We have to move data in the MBR */
2338 struct dos_partition
*pi
, *pk
, *pe
, pbuf
;
2339 pei
= self_pte(cxt
, i
);
2340 pek
= self_pte(cxt
, k
);
2346 pei
->ex_entry
= pek
->ex_entry
;
2352 memmove(&pbuf
, pi
, sizeof(struct dos_partition
));
2353 memmove(pi
, pk
, sizeof(struct dos_partition
));
2354 memmove(pk
, &pbuf
, sizeof(struct dos_partition
));
2356 partition_set_changed(cxt
, i
, 1);
2357 partition_set_changed(cxt
, k
, 1);
2361 fix_chain_of_logicals(cxt
);
2366 /* TODO: use fdisk_set_partition() API */
2367 int fdisk_dos_move_begin(struct fdisk_context
*cxt
, size_t i
)
2370 struct dos_partition
*p
;
2371 unsigned int new, free_start
, curr_start
, last
;
2377 assert(fdisk_is_label(cxt
, DOS
));
2379 pe
= self_pte(cxt
, i
);
2385 if (!is_used_partition(p
) || IS_EXTENDED (p
->sys_ind
)) {
2386 fdisk_warnx(cxt
, _("Partition %zu: no data area."), i
+ 1);
2390 /* the default start is at the second sector of the disk or at the
2391 * second sector of the extended partition
2393 free_start
= pe
->offset
? pe
->offset
+ 1 : 1;
2395 curr_start
= get_abs_partition_start(pe
);
2397 /* look for a free space before the current start of the partition */
2398 for (x
= 0; x
< cxt
->label
->nparts_max
; x
++) {
2400 struct pte
*prev_pe
= self_pte(cxt
, x
);
2401 struct dos_partition
*prev_p
;
2405 prev_p
= prev_pe
->pt_entry
;
2408 end
= get_abs_partition_start(prev_pe
)
2409 + dos_partition_get_size(prev_p
);
2411 if (is_used_partition(prev_p
) &&
2412 end
> free_start
&& end
<= curr_start
)
2416 last
= get_abs_partition_end(pe
);
2418 rc
= fdisk_ask_number(cxt
, free_start
, curr_start
, last
,
2419 _("New beginning of data"), &res
);
2423 new = res
- pe
->offset
;
2425 if (new != dos_partition_get_size(p
)) {
2426 unsigned int sects
= dos_partition_get_size(p
)
2427 + dos_partition_get_start(p
) - new;
2429 dos_partition_set_size(p
, sects
);
2430 dos_partition_set_start(p
, new);
2432 partition_set_changed(cxt
, i
, 1);
2438 static int dos_partition_is_used(
2439 struct fdisk_context
*cxt
,
2442 struct dos_partition
*p
;
2446 assert(fdisk_is_label(cxt
, DOS
));
2448 if (i
>= cxt
->label
->nparts_max
)
2451 p
= self_partition(cxt
, i
);
2453 return p
&& !is_cleared_partition(p
);
2456 static int dos_toggle_partition_flag(
2457 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
);
2473 case DOS_FLAG_ACTIVE
:
2474 if (IS_EXTENDED(p
->sys_ind
) && !p
->boot_ind
)
2475 fdisk_warnx(cxt
, _("Partition %zu: is an extended "
2476 "partition."), i
+ 1);
2478 p
->boot_ind
= (p
->boot_ind
? 0 : ACTIVE_FLAG
);
2479 partition_set_changed(cxt
, i
, 1);
2480 fdisk_info(cxt
, p
->boot_ind
?
2481 _("The bootable flag on partition %zu is enabled now.") :
2482 _("The bootable flag on partition %zu is disabled now."),
2492 static const struct fdisk_field dos_fields
[] =
2495 { FDISK_FIELD_DEVICE
, N_("Device"), 10, 0 },
2496 { FDISK_FIELD_BOOT
, N_("Boot"), 1, 0 },
2497 { FDISK_FIELD_START
, N_("Start"), 5, FDISK_FIELDFL_NUMBER
},
2498 { FDISK_FIELD_END
, N_("End"), 5, FDISK_FIELDFL_NUMBER
},
2499 { FDISK_FIELD_SECTORS
, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER
},
2500 { FDISK_FIELD_CYLINDERS
,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER
},
2501 { FDISK_FIELD_SIZE
, N_("Size"), 5, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_EYECANDY
},
2502 { FDISK_FIELD_TYPEID
, N_("Id"), 2, FDISK_FIELDFL_NUMBER
},
2503 { FDISK_FIELD_TYPE
, N_("Type"), 0.1, 0 },
2506 { FDISK_FIELD_SADDR
, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2507 { FDISK_FIELD_EADDR
, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
},
2508 { FDISK_FIELD_ATTR
, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER
| FDISK_FIELDFL_DETAIL
}
2512 static const struct fdisk_label_operations dos_operations
=
2514 .probe
= dos_probe_label
,
2515 .write
= dos_write_disklabel
,
2516 .verify
= dos_verify_disklabel
,
2517 .create
= dos_create_disklabel
,
2518 .locate
= dos_locate_disklabel
,
2519 .get_item
= dos_get_disklabel_item
,
2520 .set_id
= dos_set_disklabel_id
,
2522 .get_part
= dos_get_partition
,
2523 .set_part
= dos_set_partition
,
2524 .add_part
= dos_add_partition
,
2525 .del_part
= dos_delete_partition
,
2526 .reorder
= dos_reorder
,
2528 .part_toggle_flag
= dos_toggle_partition_flag
,
2529 .part_is_used
= dos_partition_is_used
,
2531 .reset_alignment
= dos_reset_alignment
,
2533 .deinit
= dos_deinit
,
2537 * allocates DOS in-memory stuff
2539 struct fdisk_label
*fdisk_new_dos_label(struct fdisk_context
*cxt
__attribute__ ((__unused__
)))
2541 struct fdisk_label
*lb
;
2542 struct fdisk_dos_label
*dos
;
2544 dos
= calloc(1, sizeof(*dos
));
2548 /* initialize generic part of the driver */
2549 lb
= (struct fdisk_label
*) dos
;
2551 lb
->id
= FDISK_DISKLABEL_DOS
;
2552 lb
->op
= &dos_operations
;
2553 lb
->parttypes
= dos_parttypes
;
2554 lb
->nparttypes
= ARRAY_SIZE(dos_parttypes
) - 1;
2555 lb
->fields
= dos_fields
;
2556 lb
->nfields
= ARRAY_SIZE(dos_fields
);
2558 lb
->geom_min
.sectors
= 1;
2559 lb
->geom_min
.heads
= 1;
2560 lb
->geom_min
.cylinders
= 1;
2562 lb
->geom_max
.sectors
= 63;
2563 lb
->geom_max
.heads
= 255;
2564 lb
->geom_max
.cylinders
= 1048576;
2570 * fdisk_dos_enable_compatible:
2571 * @lb: DOS label (see fdisk_get_label())
2574 * Enables deprecated DOS compatible mode, in this mode library checks for
2575 * cylinders boundary, cases about CHS addressing and another obscure things.
2577 * Returns: 0 on success, <0 on error.
2579 int fdisk_dos_enable_compatible(struct fdisk_label
*lb
, int enable
)
2581 struct fdisk_dos_label
*dos
= (struct fdisk_dos_label
*) lb
;
2586 dos
->compatible
= enable
;
2588 lb
->flags
|= FDISK_LABEL_FL_REQUIRE_GEOMETRY
;
2593 * fdisk_dos_is_compatible:
2596 * Returns: 0 if DOS compatibility disabled, 1 if enabled
2598 int fdisk_dos_is_compatible(struct fdisk_label
*lb
)
2600 return ((struct fdisk_dos_label
*) lb
)->compatible
;