3 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
4 * 2012 Davidlohr Bueso <dave@gnu.org>
6 * This is re-written version for libfdisk, the original was fdiskdoslabel.c
7 * from util-linux fdisk.
11 #include "randutils.h"
19 #define MAXIMUM_PARTS 60
20 #define ACTIVE_FLAG 0x80
22 #define IS_EXTENDED(i) \
23 ((i) == MBR_DOS_EXTENDED_PARTITION \
24 || (i) == MBR_W95_EXTENDED_PARTITION \
25 || (i) == MBR_LINUX_EXTENDED_PARTITION)
28 * per partition table entry data
30 * The four primary partitions have the same sectorbuffer
31 * and have NULL ex_entry.
33 * Each logical partition table entry has two pointers, one for the
34 * partition and one link to the next one.
37 struct dos_partition
*pt_entry
; /* on-disk MBR entry */
38 struct dos_partition
*ex_entry
; /* on-disk EBR entry */
39 sector_t offset
; /* disk sector number */
40 unsigned char *sectorbuffer
; /* disk sector contents */
42 unsigned int changed
: 1,
43 private_sectorbuffer
: 1;
47 * in-memory fdisk GPT stuff
49 struct fdisk_dos_label
{
50 struct fdisk_label head
; /* generic part */
52 struct pte ptes
[MAXIMUM_PARTS
]; /* partition */
55 unsigned int compatible
: 1, /* is DOS compatible? */
56 non_pt_changed
: 1; /* MBR, but no PT changed */
62 static struct fdisk_parttype dos_parttypes
[] = {
63 #include "pt-mbr-partnames.h"
66 #define set_hsc(h,s,c,sector) { \
67 s = sector % cxt->geom.sectors + 1; \
68 sector /= cxt->geom.sectors; \
69 h = sector % cxt->geom.heads; \
70 sector /= cxt->geom.heads; \
72 s |= (sector >> 2) & 0xc0; \
76 #define sector(s) ((s) & 0x3f)
77 #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
79 #define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
81 #define is_dos_compatible(_x) \
82 (fdisk_is_disklabel(_x, DOS) && \
83 fdisk_dos_is_compatible(fdisk_context_get_label(_x, NULL)))
85 #define cround(c, n) fdisk_cround(c, n)
88 static inline struct fdisk_dos_label
*self_label(struct fdisk_context
*cxt
)
92 assert(fdisk_is_disklabel(cxt
, DOS
));
94 return (struct fdisk_dos_label
*) cxt
->label
;
97 static inline struct pte
*self_pte(struct fdisk_context
*cxt
, size_t i
)
99 struct fdisk_dos_label
*l
= self_label(cxt
);
101 if (i
>= ARRAY_SIZE(l
->ptes
))
107 static inline struct dos_partition
*self_partition(
108 struct fdisk_context
*cxt
,
111 struct pte
*pe
= self_pte(cxt
, i
);
112 return pe
? pe
->pt_entry
: NULL
;
115 struct dos_partition
*fdisk_dos_get_partition(
116 struct fdisk_context
*cxt
,
121 assert(fdisk_is_disklabel(cxt
, DOS
));
123 return self_partition(cxt
, i
);
126 static void partition_set_changed(
127 struct fdisk_context
*cxt
,
131 struct pte
*pe
= self_pte(cxt
, i
);
136 DBG(LABEL
, dbgprint("DOS: setting %zu partition changed", i
));
138 pe
->changed
= changed
? 1 : 0;
140 fdisk_label_set_changed(cxt
->label
, 1);
143 static sector_t
get_abs_partition_start(struct pte
*pe
)
146 assert(pe
->pt_entry
);
148 return pe
->offset
+ dos_partition_get_start(pe
->pt_entry
);
151 static int is_cleared_partition(struct dos_partition
*p
)
153 return !(!p
|| p
->boot_ind
|| p
->bh
|| p
->bs
|| p
->bc
||
154 p
->sys_ind
|| p
->eh
|| p
->es
|| p
->ec
||
155 dos_partition_get_start(p
) || dos_partition_get_size(p
));
158 static int get_partition_unused_primary(struct fdisk_context
*cxt
)
160 size_t orgmax
= cxt
->label
->nparts_max
;
164 cxt
->label
->nparts_max
= 4;
165 rc
= fdisk_ask_partnum(cxt
, &n
, TRUE
);
166 cxt
->label
->nparts_max
= orgmax
;
170 fdisk_info(cxt
, _("All primary partitions have been defined already."));
179 static int seek_sector(struct fdisk_context
*cxt
, sector_t secno
)
181 off_t offset
= (off_t
) secno
* cxt
->sector_size
;
183 return lseek(cxt
->dev_fd
, offset
, SEEK_SET
) == (off_t
) -1 ? -errno
: 0;
186 static int read_sector(struct fdisk_context
*cxt
, sector_t secno
,
189 int rc
= seek_sector(cxt
, secno
);
194 return read(cxt
->dev_fd
, buf
, cxt
->sector_size
) !=
195 (ssize_t
) cxt
->sector_size
? -errno
: 0;
198 /* Allocate a buffer and read a partition table sector */
199 static int read_pte(struct fdisk_context
*cxt
, int pno
, sector_t offset
)
202 struct pte
*pe
= self_pte(cxt
, pno
);
204 buf
= calloc(1, cxt
->sector_size
);
208 DBG(LABEL
, dbgprint("DOS: reading pte %d sector buffer %p", pno
, buf
));
211 pe
->sectorbuffer
= buf
;
212 pe
->private_sectorbuffer
= 1;
214 if (read_sector(cxt
, offset
, pe
->sectorbuffer
) != 0)
215 fdisk_warn(cxt
, _("Failed to read extended partition table "
216 "(offset=%jd)"), (uintmax_t) offset
);
218 pe
->pt_entry
= pe
->ex_entry
= NULL
;
223 static void clear_partition(struct dos_partition
*p
)
235 dos_partition_set_start(p
,0);
236 dos_partition_set_size(p
,0);
239 static void dos_init(struct fdisk_context
*cxt
)
241 struct fdisk_dos_label
*l
= self_label(cxt
);
246 assert(fdisk_is_disklabel(cxt
, DOS
));
248 DBG(LABEL
, dbgprint("DOS: initialize, first sector buffer %p", cxt
->firstsector
));
250 cxt
->label
->nparts_max
= 4; /* default, unlimited number of logical */
254 l
->non_pt_changed
= 0;
256 memset(l
->ptes
, 0, sizeof(l
->ptes
));
258 for (i
= 0; i
< 4; i
++) {
259 struct pte
*pe
= self_pte(cxt
, i
);
261 pe
->pt_entry
= mbr_get_partition(cxt
->firstsector
, i
);
264 pe
->sectorbuffer
= cxt
->firstsector
;
265 pe
->private_sectorbuffer
= 0;
269 if (fdisk_context_listonly(cxt
))
272 * Various warnings...
274 if (fdisk_missing_geometry(cxt
))
275 fdisk_warnx(cxt
, _("You can set geometry from the extra functions menu."));
277 if (is_dos_compatible(cxt
)) {
278 fdisk_warnx(cxt
, _("DOS-compatible mode is deprecated."));
280 if (cxt
->sector_size
!= cxt
->phy_sector_size
)
282 "The device presents a logical sector size that is smaller than "
283 "the physical sector size. Aligning to a physical sector (or optimal "
284 "I/O) size boundary is recommended, or performance may be impacted."));
287 if (fdisk_context_use_cylinders(cxt
))
288 fdisk_warnx(cxt
, _("Cylinders as display units are deprecated."));
290 if (cxt
->total_sectors
> UINT_MAX
) {
291 uint64_t bytes
= cxt
->total_sectors
* cxt
->sector_size
;
292 char *szstr
= size_to_human_string(SIZE_SUFFIX_SPACE
293 | SIZE_SUFFIX_3LETTER
, bytes
);
295 _("The size of this disk is %s (%llu bytes). DOS "
296 "partition table format can not be used on drives for "
297 "volumes larger than (%llu bytes) for %ld-byte "
298 "sectors. Use GUID partition table format (GPT)."),
299 szstr
, (unsigned long long) bytes
,
300 (unsigned long long) UINT_MAX
* cxt
->sector_size
,
306 /* callback called by libfdisk */
307 static void dos_deinit(struct fdisk_label
*lb
)
310 struct fdisk_dos_label
*l
= (struct fdisk_dos_label
*) lb
;
312 for (i
= 0; i
< ARRAY_SIZE(l
->ptes
); i
++) {
313 struct pte
*pe
= &l
->ptes
[i
];
315 if (pe
->private_sectorbuffer
&& pe
->sectorbuffer
) {
316 DBG(LABEL
, dbgprint("DOS: freeing pte %zu sector buffer %p",
317 i
, pe
->sectorbuffer
));
318 free(pe
->sectorbuffer
);
320 pe
->sectorbuffer
= NULL
;
321 pe
->private_sectorbuffer
= 0;
324 memset(l
->ptes
, 0, sizeof(l
->ptes
));
327 static int dos_delete_partition(struct fdisk_context
*cxt
, size_t partnum
)
329 struct fdisk_dos_label
*l
;
331 struct dos_partition
*p
;
332 struct dos_partition
*q
;
336 assert(fdisk_is_disklabel(cxt
, DOS
));
338 pe
= self_pte(cxt
, partnum
);
342 DBG(LABEL
, dbgprint("DOS: delete partiton %zu", partnum
));
348 /* Note that for the fifth partition (partnum == 4) we don't actually
349 decrement partitions. */
351 DBG(LABEL
, dbgprint("--> delete primary"));
352 if (IS_EXTENDED(p
->sys_ind
) && partnum
== l
->ext_index
) {
353 cxt
->label
->nparts_max
= 4;
354 l
->ptes
[l
->ext_index
].ex_entry
= NULL
;
357 partition_set_changed(cxt
, partnum
, 1);
359 } else if (!q
->sys_ind
&& partnum
> 4) {
360 DBG(LABEL
, dbgprint("--> delete logical [last in the chain]"));
361 --cxt
->label
->nparts_max
;
363 clear_partition(l
->ptes
[partnum
].ex_entry
);
364 partition_set_changed(cxt
, partnum
, 1);
366 DBG(LABEL
, dbgprint("--> delete logical [non-last, move down]"));
368 /* delete this link in the chain */
369 p
= l
->ptes
[partnum
- 1].ex_entry
;
371 dos_partition_set_start(p
, dos_partition_get_start(q
));
372 dos_partition_set_size(p
, dos_partition_get_size(q
));
373 partition_set_changed(cxt
, partnum
- 1, 1);
374 } else if (cxt
->label
->nparts_max
> 5) { /* 5 will be moved to 4 */
375 /* the first logical in a longer chain */
378 if (pe
->pt_entry
) /* prevent SEGFAULT */
379 dos_partition_set_start(pe
->pt_entry
,
380 get_abs_partition_start(pe
) -
382 pe
->offset
= l
->ext_offset
;
383 partition_set_changed(cxt
, 5, 1);
386 if (cxt
->label
->nparts_max
> 5) {
387 cxt
->label
->nparts_max
--;
388 if (l
->ptes
[partnum
].private_sectorbuffer
) {
389 DBG(LABEL
, dbgprint("--> freeing pte %zu sector buffer %p",
390 partnum
, l
->ptes
[partnum
].sectorbuffer
));
391 free(l
->ptes
[partnum
].sectorbuffer
);
393 while (partnum
< cxt
->label
->nparts_max
) {
394 DBG(LABEL
, dbgprint("--> moving pte %zu <-- %zd", partnum
, partnum
+ 1));
395 l
->ptes
[partnum
] = l
->ptes
[partnum
+ 1];
398 memset(&l
->ptes
[partnum
], 0, sizeof(struct pte
));
400 /* the only logical: clear only */
401 clear_partition(l
->ptes
[partnum
].pt_entry
);
404 fdisk_label_set_changed(cxt
->label
, 1);
408 static void read_extended(struct fdisk_context
*cxt
, int ext
)
412 struct dos_partition
*p
, *q
;
413 struct fdisk_dos_label
*l
= self_label(cxt
);
416 pex
= self_pte(cxt
, ext
);
417 pex
->ex_entry
= pex
->pt_entry
;
420 if (!dos_partition_get_start(p
)) {
421 fdisk_warnx(cxt
, _("Bad offset in primary extended partition."));
425 DBG(LABEL
, dbgprint("DOS: REading extended %d", ext
));
427 while (IS_EXTENDED (p
->sys_ind
)) {
428 struct pte
*pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
430 if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
431 /* This is not a Linux restriction, but
432 this program uses arrays of size MAXIMUM_PARTS.
433 Do not try to `improve' this test. */
434 struct pte
*pre
= self_pte(cxt
,
435 cxt
->label
->nparts_max
- 1);
437 _("Omitting partitions after #%zd. They will be deleted "
438 "if you save this partition table."),
439 cxt
->label
->nparts_max
);
441 clear_partition(pre
->ex_entry
);
442 partition_set_changed(cxt
,
443 cxt
->label
->nparts_max
- 1, 1);
447 read_pte(cxt
, cxt
->label
->nparts_max
,
448 l
->ext_offset
+ dos_partition_get_start(p
));
451 l
->ext_offset
= dos_partition_get_start(p
);
453 q
= p
= mbr_get_partition(pe
->sectorbuffer
, 0);
455 for (i
= 0; i
< 4; i
++, p
++) if (dos_partition_get_size(p
)) {
456 if (IS_EXTENDED (p
->sys_ind
)) {
459 "Extra link pointer in partition "
461 cxt
->label
->nparts_max
+ 1);
464 } else if (p
->sys_ind
) {
467 "Ignoring extra data in partition "
469 cxt
->label
->nparts_max
+ 1);
475 /* very strange code here... */
477 if (q
!= pe
->ex_entry
)
480 pe
->pt_entry
= q
+ 1;
483 if (q
!= pe
->pt_entry
)
486 pe
->ex_entry
= q
+ 1;
490 cxt
->label
->nparts_cur
= ++cxt
->label
->nparts_max
;
493 /* remove empty links */
495 q
= self_partition(cxt
, 4);
496 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
497 p
= self_partition(cxt
, i
);
499 if (!dos_partition_get_size(p
) &&
500 (cxt
->label
->nparts_max
> 5 || q
->sys_ind
)) {
501 fdisk_info(cxt
, _("omitting empty partition (%zd)"), i
+1);
502 dos_delete_partition(cxt
, i
);
503 goto remove
; /* numbering changed */
508 static int dos_get_disklabel_id(struct fdisk_context
*cxt
, char **id
)
515 assert(fdisk_is_disklabel(cxt
, DOS
));
517 num
= mbr_get_id(cxt
->firstsector
);
518 if (asprintf(id
, "0x%08x", num
) > 0)
524 static int dos_create_disklabel(struct fdisk_context
*cxt
)
530 assert(fdisk_is_disklabel(cxt
, DOS
));
532 DBG(LABEL
, dbgprint("DOS: creating new disklabel"));
534 /* random disk signature */
535 random_get_bytes(&id
, sizeof(id
));
538 fdisk_zeroize_firstsector(cxt
);
539 fdisk_label_set_changed(cxt
->label
, 1);
541 /* Generate an MBR ID for this disk */
542 mbr_set_id(cxt
->firstsector
, id
);
544 /* Put MBR signature */
545 mbr_set_magic(cxt
->firstsector
);
547 fdisk_sinfo(cxt
, FDISK_INFO_SUCCESS
,
548 ("Created a new DOS disklabel with disk "
549 "identifier 0x%08x."), id
);
553 static int dos_set_disklabel_id(struct fdisk_context
*cxt
)
555 char *end
= NULL
, *str
= NULL
;
556 unsigned int id
, old
;
557 struct fdisk_dos_label
*l
;
562 assert(fdisk_is_disklabel(cxt
, DOS
));
564 DBG(LABEL
, dbgprint("DOS: setting Id"));
567 old
= mbr_get_id(cxt
->firstsector
);
568 rc
= fdisk_ask_string(cxt
,
569 _("Enter the new disk identifier"), &str
);
574 id
= strtoul(str
, &end
, 0);
575 if (errno
|| str
== end
|| (end
&& *end
)) {
576 fdisk_warnx(cxt
, _("Incorrect value."));
581 mbr_set_id(cxt
->firstsector
, id
);
582 l
->non_pt_changed
= 1;
583 fdisk_label_set_changed(cxt
->label
, 1);
585 fdisk_sinfo(cxt
, FDISK_INFO_SUCCESS
,
586 _("Disk identifier changed from 0x%08x to 0x%08x."),
591 static void get_partition_table_geometry(struct fdisk_context
*cxt
,
592 unsigned int *ph
, unsigned int *ps
)
594 unsigned char *bufp
= cxt
->firstsector
;
595 struct dos_partition
*p
;
601 for (i
= 0; i
< 4; i
++) {
602 p
= mbr_get_partition(bufp
, i
);
603 if (p
->sys_ind
!= 0) {
610 } else if (hh
!= h
|| ss
!= s
)
615 if (!first
&& !bad
) {
620 DBG(LABEL
, dbgprint("DOS PT geometry: heads=%u, sectors=%u", *ph
, *ps
));
623 static int dos_reset_alignment(struct fdisk_context
*cxt
)
627 assert(fdisk_is_disklabel(cxt
, DOS
));
629 /* overwrite necessary stuff by DOS deprecated stuff */
630 if (is_dos_compatible(cxt
)) {
631 DBG(LABEL
, dbgprint("DOS: reseting alignemnt for DOS-comaptiblem PT"));
632 if (cxt
->geom
.sectors
)
633 cxt
->first_lba
= cxt
->geom
.sectors
; /* usually 63 */
635 cxt
->grain
= cxt
->sector_size
; /* usually 512 */
641 /* TODO: move to include/pt-dos.h and share with libblkid */
642 #define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
643 #define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
645 static int dos_probe_label(struct fdisk_context
*cxt
)
648 unsigned int h
= 0, s
= 0;
652 assert(fdisk_is_disklabel(cxt
, DOS
));
654 /* ignore disks with AIX magic number */
655 if (memcmp(cxt
->firstsector
, AIX_MAGIC_STRING
, AIX_MAGIC_STRLEN
) == 0)
658 if (!mbr_is_valid_magic(cxt
->firstsector
))
663 get_partition_table_geometry(cxt
, &h
, &s
);
666 cxt
->geom
.sectors
= s
;
669 for (i
= 0; i
< 4; i
++) {
670 struct pte
*pe
= self_pte(cxt
, i
);
672 if (!is_cleared_partition(pe
->pt_entry
))
673 cxt
->label
->nparts_cur
++;
675 if (IS_EXTENDED (pe
->pt_entry
->sys_ind
)) {
676 if (cxt
->label
->nparts_max
!= 4)
678 "Ignoring extra extended partition %zd"),
681 read_extended(cxt
, i
);
685 for (i
= 3; i
< cxt
->label
->nparts_max
; i
++) {
686 struct pte
*pe
= self_pte(cxt
, i
);
688 if (!mbr_is_valid_magic(pe
->sectorbuffer
)) {
690 "Invalid flag 0x%02x%02x of partition table %zd will "
691 "be corrected by w(rite)"),
692 pe
->sectorbuffer
[510],
693 pe
->sectorbuffer
[511],
695 partition_set_changed(cxt
, 1, 1);
703 * Avoid warning about DOS partitions when no DOS partition was changed.
704 * Here a heuristic "is probably dos partition".
705 * We might also do the opposite and warn in all cases except
706 * for "is probably nondos partition".
708 static int is_dos_partition(int t
)
710 return (t
== 1 || t
== 4 || t
== 6 ||
711 t
== 0x0b || t
== 0x0c || t
== 0x0e ||
712 t
== 0x11 || t
== 0x12 || t
== 0x14 || t
== 0x16 ||
713 t
== 0x1b || t
== 0x1c || t
== 0x1e || t
== 0x24 ||
714 t
== 0xc1 || t
== 0xc4 || t
== 0xc6);
717 static void set_partition(struct fdisk_context
*cxt
,
718 int i
, int doext
, sector_t start
,
719 sector_t stop
, int sysid
)
721 struct pte
*pe
= self_pte(cxt
, i
);
722 struct dos_partition
*p
;
725 DBG(LABEL
, dbgprint("DOS: setting partition %d%s, start=%zu, stop=%zu, sysid=%02x",
726 i
, doext
? " [extended]" : "",
727 (size_t) start
, (size_t) stop
, sysid
));
730 struct fdisk_dos_label
*l
= self_label(cxt
);
732 offset
= l
->ext_offset
;
739 dos_partition_set_start(p
, start
- offset
);
740 dos_partition_set_size(p
, stop
- start
+ 1);
743 struct fdisk_parttype
*t
= fdisk_get_parttype_from_code(cxt
, sysid
);
744 fdisk_info_new_partition(cxt
, i
+ 1, start
, stop
, t
);
746 if (is_dos_compatible(cxt
) && (start
/(cxt
->geom
.sectors
*cxt
->geom
.heads
) > 1023))
747 start
= cxt
->geom
.heads
*cxt
->geom
.sectors
*1024 - 1;
748 set_hsc(p
->bh
, p
->bs
, p
->bc
, start
);
749 if (is_dos_compatible(cxt
) && (stop
/(cxt
->geom
.sectors
*cxt
->geom
.heads
) > 1023))
750 stop
= cxt
->geom
.heads
*cxt
->geom
.sectors
*1024 - 1;
751 set_hsc(p
->eh
, p
->es
, p
->ec
, stop
);
752 partition_set_changed(cxt
, i
, 1);
755 static sector_t
get_unused_start(struct fdisk_context
*cxt
,
756 int part_n
, sector_t start
,
757 sector_t first
[], sector_t last
[])
761 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
762 sector_t lastplusoff
;
763 struct pte
*pe
= self_pte(cxt
, i
);
765 if (start
== pe
->offset
)
766 start
+= cxt
->first_lba
;
767 lastplusoff
= last
[i
] + ((part_n
< 4) ? 0 : cxt
->first_lba
);
768 if (start
>= first
[i
] && start
<= lastplusoff
)
769 start
= lastplusoff
+ 1;
775 static void fill_bounds(struct fdisk_context
*cxt
,
776 sector_t
*first
, sector_t
*last
)
779 struct pte
*pe
= self_pte(cxt
, 0);
780 struct dos_partition
*p
;
782 for (i
= 0; i
< cxt
->label
->nparts_max
; pe
++,i
++) {
784 if (!p
->sys_ind
|| IS_EXTENDED (p
->sys_ind
)) {
785 first
[i
] = 0xffffffff;
788 first
[i
] = get_abs_partition_start(pe
);
789 last
[i
] = first
[i
] + dos_partition_get_size(p
) - 1;
794 static int add_partition(struct fdisk_context
*cxt
, int n
, struct fdisk_parttype
*t
)
796 int sys
, read
= 0, rc
;
798 struct fdisk_dos_label
*l
= self_label(cxt
);
799 struct dos_partition
*p
= self_partition(cxt
, n
);
800 struct dos_partition
*q
= self_partition(cxt
, l
->ext_index
);
802 sector_t start
, stop
= 0, limit
, temp
,
803 first
[cxt
->label
->nparts_max
],
804 last
[cxt
->label
->nparts_max
];
806 DBG(LABEL
, dbgprint("DOS: adding partition %d", n
));
808 sys
= t
? t
->type
: MBR_LINUX_DATA_PARTITION
;
810 if (p
&& p
->sys_ind
) {
811 fdisk_warnx(cxt
, _("Partition %zd is already defined. "
812 "Delete it before re-adding it."),
816 fill_bounds(cxt
, first
, last
);
818 start
= cxt
->first_lba
;
819 if (fdisk_context_use_cylinders(cxt
) || !cxt
->total_sectors
)
820 limit
= cxt
->geom
.heads
* cxt
->geom
.sectors
* cxt
->geom
.cylinders
- 1;
822 limit
= cxt
->total_sectors
- 1;
824 if (limit
> UINT_MAX
)
828 first
[l
->ext_index
] = l
->ext_offset
;
829 last
[l
->ext_index
] = dos_partition_get_start(q
) +
830 dos_partition_get_size(q
) - 1;
833 start
= l
->ext_offset
+ cxt
->first_lba
;
834 limit
= dos_partition_get_start(q
)
835 + dos_partition_get_size(q
) - 1;
837 if (fdisk_context_use_cylinders(cxt
))
838 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
839 first
[i
] = (cround(cxt
, first
[i
]) - 1)
840 * fdisk_context_get_units_per_sector(cxt
);
844 * Ask for first sector
847 sector_t dflt
, aligned
;
850 dflt
= start
= get_unused_start(cxt
, n
, start
, first
, last
);
852 /* the default sector should be aligned and unused */
854 aligned
= fdisk_align_lba_in_range(cxt
, dflt
, dflt
, limit
);
855 dflt
= get_unused_start(cxt
, n
, aligned
, first
, last
);
856 } while (dflt
!= aligned
&& dflt
> aligned
&& dflt
< limit
);
862 if (start
>= temp
+ fdisk_context_get_units_per_sector(cxt
)
864 fdisk_info(cxt
, _("Sector %llu is already allocated."),
870 if (!read
&& start
== temp
) {
872 struct fdisk_ask
*ask
= fdisk_new_ask();
874 if (fdisk_context_use_cylinders(cxt
))
875 fdisk_ask_set_query(ask
, _("First cylinder"));
877 fdisk_ask_set_query(ask
, _("First sector"));
879 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_NUMBER
);
880 fdisk_ask_number_set_low(ask
, cround(cxt
, j
));
881 fdisk_ask_number_set_default(ask
, cround(cxt
, dflt
));
882 fdisk_ask_number_set_high(ask
, cround(cxt
, limit
));
884 rc
= fdisk_do_ask(cxt
, ask
);
886 start
= fdisk_ask_number_get_result(ask
);
891 if (fdisk_context_use_cylinders(cxt
)) {
893 * fdisk_context_get_units_per_sector(cxt
);
899 } while (start
!= temp
|| !read
);
901 if (n
> 4) { /* NOT for fifth partition */
902 struct pte
*pe
= self_pte(cxt
, n
);
904 pe
->offset
= start
- cxt
->first_lba
;
905 if (pe
->offset
== l
->ext_offset
) { /* must be corrected */
907 if (cxt
->first_lba
== 1)
912 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
913 struct pte
*pe
= self_pte(cxt
, i
);
915 if (start
< pe
->offset
&& limit
>= pe
->offset
)
916 limit
= pe
->offset
- 1;
917 if (start
< first
[i
] && limit
>= first
[i
])
918 limit
= first
[i
] - 1;
921 fdisk_info(cxt
, _("No free sectors available."));
923 cxt
->label
->nparts_max
--;
926 if (cround(cxt
, start
) == cround(cxt
, limit
)) {
930 * Ask for last sector
932 struct fdisk_ask
*ask
= fdisk_new_ask();
934 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_OFFSET
);
936 if (fdisk_context_use_cylinders(cxt
)) {
937 fdisk_ask_set_query(ask
, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
938 fdisk_ask_number_set_unit(ask
,
940 fdisk_context_get_units_per_sector(cxt
));
942 fdisk_ask_set_query(ask
, _("Last sector, +sectors or +size{K,M,G,T,P}"));
943 fdisk_ask_number_set_unit(ask
,cxt
->sector_size
);
946 fdisk_ask_number_set_low(ask
, cround(cxt
, start
));
947 fdisk_ask_number_set_default(ask
, cround(cxt
, limit
));
948 fdisk_ask_number_set_high(ask
, cround(cxt
, limit
));
949 fdisk_ask_number_set_base(ask
, cround(cxt
, start
)); /* base for relative input */
951 rc
= fdisk_do_ask(cxt
, ask
);
957 stop
= fdisk_ask_number_get_result(ask
);
959 if (fdisk_context_use_cylinders(cxt
)) {
960 stop
= stop
* fdisk_context_get_units_per_sector(cxt
) - 1;
964 if (fdisk_ask_number_is_relative(ask
)
965 && alignment_required(cxt
)) {
966 /* the last sector has not been exactly requested (but
967 * defined by +size{K,M,G} convention), so be smart and
968 * align the end of the partition. The next partition
969 * will start at phy.block boundary.
971 stop
= fdisk_align_lba_in_range(cxt
, stop
, start
, limit
) - 1;
978 set_partition(cxt
, n
, 0, start
, stop
, sys
);
980 struct pte
*pe
= self_pte(cxt
, n
);
981 set_partition(cxt
, n
- 1, 1, pe
->offset
, stop
,
982 MBR_DOS_EXTENDED_PARTITION
);
985 if (IS_EXTENDED(sys
)) {
986 struct pte
*pe4
= self_pte(cxt
, 4);
987 struct pte
*pen
= self_pte(cxt
, n
);
991 pe4
->offset
= l
->ext_offset
= start
;
992 pe4
->sectorbuffer
= calloc(1, cxt
->sector_size
);
993 if (!pe4
->sectorbuffer
)
995 DBG(LABEL
, dbgprint("DOS: add partition, sector buffer %p", pe4
->sectorbuffer
));
996 pe4
->private_sectorbuffer
= 1;
997 pe4
->pt_entry
= mbr_get_partition(pe4
->sectorbuffer
, 0);
998 pe4
->ex_entry
= pe4
->pt_entry
+ 1;
1000 partition_set_changed(cxt
, 4, 1);
1001 cxt
->label
->nparts_max
= 5;
1004 fdisk_label_set_changed(cxt
->label
, 1);
1008 static int add_logical(struct fdisk_context
*cxt
)
1010 struct dos_partition
*p4
= self_partition(cxt
, 4);
1015 if (cxt
->label
->nparts_max
> 5 || p4
->sys_ind
) {
1016 struct pte
*pe
= self_pte(cxt
, cxt
->label
->nparts_max
);
1018 pe
->sectorbuffer
= calloc(1, cxt
->sector_size
);
1019 if (!pe
->sectorbuffer
)
1021 DBG(LABEL
, dbgprint("DOS: add logical, sector buffer %p", pe
->sectorbuffer
));
1022 pe
->private_sectorbuffer
= 1;
1023 pe
->pt_entry
= mbr_get_partition(pe
->sectorbuffer
, 0);
1024 pe
->ex_entry
= pe
->pt_entry
+ 1;
1027 partition_set_changed(cxt
, cxt
->label
->nparts_max
, 1);
1028 cxt
->label
->nparts_max
++;
1030 fdisk_info(cxt
, _("Adding logical partition %zd"),
1031 cxt
->label
->nparts_max
);
1032 return add_partition(cxt
, cxt
->label
->nparts_max
- 1, NULL
);
1035 static void check(struct fdisk_context
*cxt
, size_t n
,
1036 unsigned int h
, unsigned int s
, unsigned int c
,
1039 unsigned int total
, real_s
, real_c
;
1041 real_s
= sector(s
) - 1;
1042 real_c
= cylinder(s
, c
);
1043 total
= (real_c
* cxt
->geom
.sectors
+ real_s
) * cxt
->geom
.heads
+ h
;
1046 fdisk_warnx(cxt
, _("Partition %zd: contains sector 0"), n
);
1047 if (h
>= cxt
->geom
.heads
)
1048 fdisk_warnx(cxt
, _("Partition %zd: head %d greater than "
1049 "maximum %d"), n
, h
+ 1, cxt
->geom
.heads
);
1050 if (real_s
>= cxt
->geom
.sectors
)
1051 fdisk_warnx(cxt
, _("Partition %zd: sector %d greater than "
1052 "maximum %llu"), n
, s
, cxt
->geom
.sectors
);
1053 if (real_c
>= cxt
->geom
.cylinders
)
1054 fdisk_warnx(cxt
, _("Partition %zd: cylinder %d greater than "
1057 cxt
->geom
.cylinders
);
1059 if (cxt
->geom
.cylinders
<= 1024 && start
!= total
)
1060 fdisk_warnx(cxt
, _("Partition %zd: previous sectors %d "
1061 "disagrees with total %d"), n
, start
, total
);
1064 /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1065 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1066 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1067 * Lubkin Oct. 1991). */
1070 long2chs(struct fdisk_context
*cxt
, unsigned long ls
,
1071 unsigned int *c
, unsigned int *h
, unsigned int *s
) {
1072 int spc
= cxt
->geom
.heads
* cxt
->geom
.sectors
;
1076 *h
= ls
/ cxt
->geom
.sectors
;
1077 *s
= ls
% cxt
->geom
.sectors
+ 1; /* sectors count from 1 */
1080 static void check_consistency(struct fdisk_context
*cxt
, struct dos_partition
*p
,
1083 unsigned int pbc
, pbh
, pbs
; /* physical beginning c, h, s */
1084 unsigned int pec
, peh
, pes
; /* physical ending c, h, s */
1085 unsigned int lbc
, lbh
, lbs
; /* logical beginning c, h, s */
1086 unsigned int lec
, leh
, les
; /* logical ending c, h, s */
1088 if (!is_dos_compatible(cxt
))
1091 if (!cxt
->geom
.heads
|| !cxt
->geom
.sectors
|| (partition
>= 4))
1092 return; /* do not check extended partitions */
1094 /* physical beginning c, h, s */
1095 pbc
= (p
->bc
& 0xff) | ((p
->bs
<< 2) & 0x300);
1099 /* physical ending c, h, s */
1100 pec
= (p
->ec
& 0xff) | ((p
->es
<< 2) & 0x300);
1104 /* compute logical beginning (c, h, s) */
1105 long2chs(cxt
, dos_partition_get_start(p
), &lbc
, &lbh
, &lbs
);
1107 /* compute logical ending (c, h, s) */
1108 long2chs(cxt
, dos_partition_get_start(p
) + dos_partition_get_size(p
) - 1, &lec
, &leh
, &les
);
1110 /* Same physical / logical beginning? */
1111 if (cxt
->geom
.cylinders
<= 1024
1112 && (pbc
!= lbc
|| pbh
!= lbh
|| pbs
!= lbs
)) {
1113 fdisk_warnx(cxt
, _("Partition %zd: different physical/logical "
1114 "beginnings (non-Linux?): "
1115 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1121 /* Same physical / logical ending? */
1122 if (cxt
->geom
.cylinders
<= 1024
1123 && (pec
!= lec
|| peh
!= leh
|| pes
!= les
)) {
1124 fdisk_warnx(cxt
, _("Partition %zd: different physical/logical "
1125 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1131 /* Ending on cylinder boundary? */
1132 if (peh
!= (cxt
->geom
.heads
- 1) || pes
!= cxt
->geom
.sectors
) {
1133 fdisk_warnx(cxt
, _("Partition %zd: does not end on "
1134 "cylinder boundary."),
1139 static int dos_verify_disklabel(struct fdisk_context
*cxt
)
1142 sector_t total
= 1, n_sectors
= cxt
->total_sectors
;
1143 unsigned long long first
[cxt
->label
->nparts_max
],
1144 last
[cxt
->label
->nparts_max
];
1145 struct dos_partition
*p
;
1146 struct fdisk_dos_label
*l
= self_label(cxt
);
1148 assert(fdisk_is_disklabel(cxt
, DOS
));
1150 fill_bounds(cxt
, first
, last
);
1151 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
1152 struct pte
*pe
= self_pte(cxt
, i
);
1154 p
= self_partition(cxt
, i
);
1155 if (p
->sys_ind
&& !IS_EXTENDED(p
->sys_ind
)) {
1156 check_consistency(cxt
, p
, i
);
1157 fdisk_warn_alignment(cxt
, get_abs_partition_start(pe
), i
);
1158 if (get_abs_partition_start(pe
) < first
[i
])
1160 "Partition %zd: bad start-of-data."),
1163 check(cxt
, i
+ 1, p
->eh
, p
->es
, p
->ec
, last
[i
]);
1164 total
+= last
[i
] + 1 - first
[i
];
1166 for (j
= 0; j
< i
; j
++) {
1167 if ((first
[i
] >= first
[j
] && first
[i
] <= last
[j
])
1168 || ((last
[i
] <= last
[j
] && last
[i
] >= first
[j
]))) {
1170 fdisk_warnx(cxt
, _("Partition %zd: "
1171 "overlaps partition %zd."),
1174 total
+= first
[i
] >= first
[j
] ?
1175 first
[i
] : first
[j
];
1176 total
-= last
[i
] <= last
[j
] ?
1183 if (l
->ext_offset
) {
1185 p
= self_partition(cxt
, l
->ext_index
);
1186 e_last
= dos_partition_get_start(p
)
1187 + dos_partition_get_size(p
) - 1;
1189 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1191 p
= self_partition(cxt
, i
);
1194 if (i
!= 4 || i
+ 1 < cxt
->label
->nparts_max
)
1196 _("Partition %zd: empty."),
1198 } else if (first
[i
] < l
->ext_offset
1199 || last
[i
] > e_last
) {
1201 fdisk_warnx(cxt
, _("Logical partition %zd: "
1202 "not entirely in partition %zd."),
1203 i
+ 1, l
->ext_index
+ 1);
1208 if (total
> n_sectors
)
1209 fdisk_warnx(cxt
, _("Total allocated sectors %llu greater "
1210 "than the maximum %llu."), total
, n_sectors
);
1211 else if (total
< n_sectors
)
1212 fdisk_warnx(cxt
, _("Remaining %lld unallocated %ld-byte "
1213 "sectors."), n_sectors
- total
, cxt
->sector_size
);
1219 * Ask the user for new partition type information (logical, extended).
1220 * This function calls the actual partition adding logic - add_partition.
1224 static int dos_add_partition(
1225 struct fdisk_context
*cxt
,
1226 size_t partnum
__attribute__ ((__unused__
)),
1227 struct fdisk_parttype
*t
)
1229 size_t i
, free_primary
= 0;
1231 struct fdisk_dos_label
*l
= self_label(cxt
);
1235 assert(fdisk_is_disklabel(cxt
, DOS
));
1237 for (i
= 0; i
< 4; i
++) {
1238 struct dos_partition
*p
= self_partition(cxt
, i
);
1239 free_primary
+= !p
->sys_ind
;
1242 if (!free_primary
&& cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1243 fdisk_info(cxt
, _("The maximum number of partitions has "
1249 if (!free_primary
) {
1250 if (l
->ext_offset
) {
1251 fdisk_info(cxt
, _("All primary partitions are in use."));
1252 rc
= add_logical(cxt
);
1254 fdisk_info(cxt
, _("If you want to create more than "
1255 "four partitions, you must replace a "
1256 "primary partition with an extended "
1257 "partition first."));
1259 } else if (cxt
->label
->nparts_max
>= MAXIMUM_PARTS
) {
1262 fdisk_info(cxt
, _("All logical partitions are in use. "
1263 "Adding a primary partition."));
1264 j
= get_partition_unused_primary(cxt
);
1266 rc
= add_partition(cxt
, j
, t
);
1269 char c
, prompt
[BUFSIZ
];
1272 dflt
= (free_primary
== 1 && !l
->ext_offset
) ? 'e' : 'p';
1274 snprintf(prompt
, sizeof(prompt
),
1275 _("Partition type:\n"
1276 " p primary (%zd primary, %d extended, %zd free)\n"
1278 "Select (default %c)"),
1279 4 - (l
->ext_offset
? 1 : 0) - free_primary
,
1280 l
->ext_offset
? 1 : 0, free_primary
,
1281 l
->ext_offset
? _(" l logical (numbered from 5)") : _(" e extended"),
1284 rc
= fdisk_ask_string(cxt
, prompt
, &buf
);
1289 fdisk_info(cxt
, _("Using default response %c."), c
);
1291 c
= tolower(buf
[0]);
1295 int j
= get_partition_unused_primary(cxt
);
1297 rc
= add_partition(cxt
, j
, t
);
1299 } else if (c
== 'l' && l
->ext_offset
) {
1300 rc
= add_logical(cxt
);
1302 } else if (c
== 'e' && !l
->ext_offset
) {
1303 int j
= get_partition_unused_primary(cxt
);
1305 t
= fdisk_get_parttype_from_code(cxt
,
1306 MBR_DOS_EXTENDED_PARTITION
);
1307 rc
= add_partition(cxt
, j
, t
);
1311 fdisk_warnx(cxt
, _("Invalid partition type `%c'."), c
);
1315 cxt
->label
->nparts_cur
++;
1319 static int write_sector(struct fdisk_context
*cxt
, sector_t secno
,
1324 rc
= seek_sector(cxt
, secno
);
1326 fdisk_warn(cxt
, _("Cannot write sector %jd: seek failed"),
1330 if (write(cxt
->dev_fd
, buf
, cxt
->sector_size
) != (ssize_t
) cxt
->sector_size
)
1335 static int dos_write_disklabel(struct fdisk_context
*cxt
)
1337 struct fdisk_dos_label
*l
= self_label(cxt
);
1339 int rc
= 0, mbr_changed
= 0;
1343 assert(fdisk_is_disklabel(cxt
, DOS
));
1345 mbr_changed
= l
->non_pt_changed
;
1347 /* MBR (primary partitions) */
1349 for (i
= 0; i
< 4; i
++) {
1350 struct pte
*pe
= self_pte(cxt
, i
);
1356 mbr_set_magic(cxt
->firstsector
);
1357 rc
= write_sector(cxt
, 0, cxt
->firstsector
);
1362 /* EBR (logical partitions) */
1363 for (i
= 4; i
< cxt
->label
->nparts_max
; i
++) {
1364 struct pte
*pe
= self_pte(cxt
, i
);
1367 mbr_set_magic(pe
->sectorbuffer
);
1368 rc
= write_sector(cxt
, pe
->offset
, pe
->sectorbuffer
);
1378 static int dos_locate_disklabel(struct fdisk_context
*cxt
, int n
,
1379 const char **name
, off_t
*offset
, size_t *size
)
1394 /* extended partitions */
1395 if (n
- 1 + 4 < cxt
->label
->nparts_max
) {
1396 struct pte
*pe
= self_pte(cxt
, n
- 1 + 4);
1398 assert(pe
->private_sectorbuffer
);
1401 *offset
= pe
->offset
* cxt
->sector_size
;
1411 static struct fdisk_parttype
*dos_get_parttype(
1412 struct fdisk_context
*cxt
,
1415 struct fdisk_parttype
*t
;
1416 struct dos_partition
*p
;
1420 assert(fdisk_is_disklabel(cxt
, DOS
));
1422 if (partnum
>= cxt
->label
->nparts_max
)
1425 p
= self_partition(cxt
, partnum
);
1426 t
= fdisk_get_parttype_from_code(cxt
, p
->sys_ind
);
1428 t
= fdisk_new_unknown_parttype(p
->sys_ind
, NULL
);
1432 static int dos_set_parttype(
1433 struct fdisk_context
*cxt
,
1435 struct fdisk_parttype
*t
)
1437 struct dos_partition
*p
;
1441 assert(fdisk_is_disklabel(cxt
, DOS
));
1443 if (partnum
>= cxt
->label
->nparts_max
|| !t
|| t
->type
> UINT8_MAX
)
1446 p
= self_partition(cxt
, partnum
);
1447 if (t
->type
== p
->sys_ind
)
1450 if (IS_EXTENDED(p
->sys_ind
) || IS_EXTENDED(t
->type
)) {
1451 fdisk_warnx(cxt
, _("You cannot change a partition into an "
1452 "extended one or vice versa. Delete it first."));
1456 if (is_dos_partition(t
->type
) || is_dos_partition(p
->sys_ind
))
1457 fdisk_info(cxt
, _("If you have created or modified any DOS 6.x "
1458 "partitions, please see the fdisk documentation for additional "
1461 p
->sys_ind
= t
->type
;
1463 partition_set_changed(cxt
, partnum
, 1);
1468 * Check whether partition entries are ordered by their starting positions.
1469 * Return 0 if OK. Return i if partition i should have been earlier.
1470 * Two separate checks: primary and logical partitions.
1472 static int wrong_p_order(struct fdisk_context
*cxt
, size_t *prev
)
1474 size_t last_p_start_pos
= 0, p_start_pos
;
1475 size_t i
, last_i
= 0;
1477 for (i
= 0 ; i
< cxt
->label
->nparts_max
; i
++) {
1479 struct pte
*pe
= self_pte(cxt
, i
);
1480 struct dos_partition
*p
= pe
->pt_entry
;
1484 last_p_start_pos
= 0;
1487 p_start_pos
= get_abs_partition_start(pe
);
1489 if (last_p_start_pos
> p_start_pos
) {
1495 last_p_start_pos
= p_start_pos
;
1502 static int is_garbage_table(struct fdisk_context
*cxt
)
1506 for (i
= 0; i
< 4; i
++) {
1507 struct dos_partition
*p
= self_partition(cxt
, i
);
1509 if (p
->boot_ind
!= 0 && p
->boot_ind
!= 0x80)
1516 * List all PT fields.
1518 * This is useful for PT debugging (or for 70's Hippies
1519 * who are on permanent LSD trip).
1521 static int dos_fulllist_disklabel(struct fdisk_context
*cxt
, int ext
)
1525 struct tt
*tb
= NULL
;
1529 assert(fdisk_is_disklabel(cxt
, DOS
));
1531 tb
= tt_new_table(TT_FL_FREEDATA
);
1535 tt_define_column(tb
, _("Nr"), 2, TT_FL_RIGHT
);
1536 tt_define_column(tb
, _("AF"), 2, TT_FL_RIGHT
);
1538 tt_define_column(tb
, _("Hd"), 4, TT_FL_RIGHT
);
1539 tt_define_column(tb
, _("Sec"), 4, TT_FL_RIGHT
);
1540 tt_define_column(tb
, _("Cyl"), 5, TT_FL_RIGHT
);
1542 tt_define_column(tb
, _("Hd"), 4, TT_FL_RIGHT
);
1543 tt_define_column(tb
, _("Sec"), 4, TT_FL_RIGHT
);
1544 tt_define_column(tb
, _("Cyl"), 5, TT_FL_RIGHT
);
1546 tt_define_column(tb
, _("Start"), 9, TT_FL_RIGHT
);
1547 tt_define_column(tb
, _("Size"), 9, TT_FL_RIGHT
);
1548 tt_define_column(tb
, _("Id"), 2, TT_FL_RIGHT
);
1550 for (i
= 0 ; i
< cxt
->label
->nparts_max
; i
++) {
1551 struct pte
*pe
= self_pte(cxt
, i
);
1552 struct dos_partition
*p
;
1556 p
= ext
? pe
->ex_entry
: pe
->pt_entry
;
1559 ln
= tt_add_line(tb
, NULL
);
1563 if (asprintf(&str
, "%zd", i
+ 1) > 0)
1564 tt_line_set_data(ln
, 0, str
); /* Nr */
1565 if (asprintf(&str
, "%02x", p
->boot_ind
) > 0)
1566 tt_line_set_data(ln
, 1, str
); /* AF */
1568 if (asprintf(&str
, "%d", p
->bh
) > 0)
1569 tt_line_set_data(ln
, 2, str
); /* Hd */
1570 if (asprintf(&str
, "%d", sector(p
->bs
)) > 0)
1571 tt_line_set_data(ln
, 3, str
); /* Sec */
1572 if (asprintf(&str
, "%d", cylinder(p
->bs
, p
->bc
)) > 0)
1573 tt_line_set_data(ln
, 4, str
); /* Cyl */
1575 if (asprintf(&str
, "%d", p
->eh
) > 0)
1576 tt_line_set_data(ln
, 5, str
); /* Hd */
1577 if (asprintf(&str
, "%d", sector(p
->es
)) > 0)
1578 tt_line_set_data(ln
, 6, str
); /* Sec */
1579 if (asprintf(&str
, "%d", cylinder(p
->es
, p
->ec
)) > 0)
1580 tt_line_set_data(ln
, 7, str
); /* Cyl */
1582 if (asprintf(&str
, "%lu",
1583 (unsigned long) dos_partition_get_start(p
)) > 0)
1584 tt_line_set_data(ln
, 8, str
); /* Start */
1585 if (asprintf(&str
, "%lu",
1586 (unsigned long) dos_partition_get_size(p
)) > 0)
1587 tt_line_set_data(ln
, 9, str
); /* End */
1589 if (asprintf(&str
, "%02x", p
->sys_ind
) > 0)
1590 tt_line_set_data(ln
, 10, str
); /* Id */
1593 check_consistency(cxt
, p
, i
);
1594 fdisk_warn_alignment(cxt
,
1595 get_abs_partition_start(pe
), i
);
1599 rc
= fdisk_print_table(cxt
, tb
);
1605 int fdisk_dos_list_extended(struct fdisk_context
*cxt
)
1607 return dos_fulllist_disklabel(cxt
, 1);
1610 static int dos_list_disklabel(struct fdisk_context
*cxt
)
1612 int rc
= 0, trunc
= TT_FL_TRUNC
;
1614 struct tt
*tb
= NULL
;
1618 assert(fdisk_is_disklabel(cxt
, DOS
));
1620 if (is_garbage_table(cxt
)) {
1622 "This doesn't look like a partition table. "
1623 "Probably you selected the wrong device."));
1626 if (fdisk_context_display_details(cxt
))
1627 return dos_fulllist_disklabel(cxt
, 0);
1629 tb
= tt_new_table(TT_FL_FREEDATA
);
1633 /* don't trunc anything in expert mode */
1634 if (fdisk_context_display_details(cxt
))
1637 tt_define_column(tb
, _("Device"), 0.1, 0);
1638 tt_define_column(tb
, _("Boot"), 1, 0);
1639 tt_define_column(tb
, _("Start"), 9, TT_FL_RIGHT
);
1640 tt_define_column(tb
, _("End"), 9, TT_FL_RIGHT
);
1641 /* TRANSLATORS: keep one blank space behind 'Blocks' */
1642 tt_define_column(tb
, _("Blocks "), 5, TT_FL_RIGHT
);
1643 tt_define_column(tb
, _("Id"), 2, TT_FL_RIGHT
);
1644 tt_define_column(tb
, _("System"), 0.1, trunc
);
1646 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
1647 struct pte
*pe
= self_pte(cxt
, i
);
1648 struct dos_partition
*p
= pe
->pt_entry
;
1649 unsigned int psects
, pblocks
, podd
= 0;
1650 struct fdisk_parttype
*type
;
1654 if (!p
|| is_cleared_partition(p
))
1656 ln
= tt_add_line(tb
, NULL
);
1660 pblocks
= psects
= dos_partition_get_size(p
);
1661 type
= fdisk_get_parttype_from_code(cxt
, p
->sys_ind
);
1663 if (cxt
->sector_size
< 1024) {
1664 pblocks
/= (1024 / cxt
->sector_size
);
1665 podd
= psects
% (1024 / cxt
->sector_size
);
1667 if (cxt
->sector_size
> 1024)
1668 pblocks
*= (cxt
->sector_size
/ 1024);
1670 str
= fdisk_partname(cxt
->dev_path
, i
+ 1);
1672 tt_line_set_data(ln
, 0, str
); /* device */
1674 str
= strdup(p
->boot_ind
?
1675 p
->boot_ind
== ACTIVE_FLAG
? "*" : "?" : " ");
1677 tt_line_set_data(ln
, 1, str
); /* boot flag */
1679 if (asprintf(&str
, "%lu", (unsigned long) cround(cxt
,
1680 get_abs_partition_start(pe
))) > 0)
1681 tt_line_set_data(ln
, 2, str
); /* start */
1683 if (asprintf(&str
, "%lu", (unsigned long) cround(cxt
,
1684 get_abs_partition_start(pe
)
1685 + psects
- (psects
? 1 : 0))) > 0)
1686 tt_line_set_data(ln
, 3, str
); /* end */
1688 if (asprintf(&str
, "%lu%c", (unsigned long) pblocks
,
1689 podd
? '+' : ' ') > 0)
1690 tt_line_set_data(ln
, 4, str
); /* blocks<flag> */
1692 if (asprintf(&str
, "%x", p
->sys_ind
) > 0)
1693 tt_line_set_data(ln
, 5, str
); /* id */
1695 str
= strdup(type
? type
->name
: _("Unknown"));
1697 tt_line_set_data(ln
, 6, str
);
1699 check_consistency(cxt
, p
, i
);
1700 fdisk_warn_alignment(cxt
, get_abs_partition_start(pe
), i
);
1701 fdisk_free_parttype(type
);
1704 rc
= fdisk_print_table(cxt
, tb
);
1707 /* Is partition table in disk order? It need not be, but... */
1708 /* partition table entries are not checked for correct order if this
1709 is a sgi, sun labeled disk... */
1710 if (wrong_p_order(cxt
, NULL
))
1711 fdisk_info(cxt
, _("Partition table entries are not in "
1719 * Fix the chain of logicals.
1720 * ext_offset is unchanged, the set of sectors used is unchanged
1721 * The chain is sorted so that sectors increase, and so that
1722 * starting sectors increase.
1724 * After this it may still be that cfdisk doesn't like the table.
1725 * (This is because cfdisk considers expanded parts, from link to
1726 * end of partition, and these may still overlap.)
1728 * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
1731 static void fix_chain_of_logicals(struct fdisk_context
*cxt
)
1733 struct fdisk_dos_label
*l
= self_label(cxt
);
1734 size_t j
, oj
, ojj
, sj
, sjj
;
1735 struct dos_partition
*pj
,*pjj
,tmp
;
1737 /* Stage 1: sort sectors but leave sector of part 4 */
1738 /* (Its sector is the global ext_offset.) */
1740 for (j
= 5; j
< cxt
->label
->nparts_max
- 1; j
++) {
1741 oj
= l
->ptes
[j
].offset
;
1742 ojj
= l
->ptes
[j
+ 1].offset
;
1744 l
->ptes
[j
].offset
= ojj
;
1745 l
->ptes
[j
+ 1].offset
= oj
;
1746 pj
= l
->ptes
[j
].pt_entry
;
1747 dos_partition_set_start(pj
, dos_partition_get_start(pj
)+oj
-ojj
);
1748 pjj
= l
->ptes
[j
+ 1].pt_entry
;
1749 dos_partition_set_start(pjj
, dos_partition_get_start(pjj
)+ojj
-oj
);
1750 dos_partition_set_start(l
->ptes
[j
- 1].ex_entry
,
1751 ojj
- l
->ext_offset
);
1752 dos_partition_set_start(l
->ptes
[j
].ex_entry
,
1753 oj
- l
->ext_offset
);
1758 /* Stage 2: sort starting sectors */
1760 for (j
= 4; j
< cxt
->label
->nparts_max
- 1; j
++) {
1761 pj
= l
->ptes
[j
].pt_entry
;
1762 pjj
= l
->ptes
[j
+ 1].pt_entry
;
1763 sj
= dos_partition_get_start(pj
);
1764 sjj
= dos_partition_get_start(pjj
);
1765 oj
= l
->ptes
[j
].offset
;
1766 ojj
= l
->ptes
[j
+1].offset
;
1767 if (oj
+sj
> ojj
+sjj
) {
1771 dos_partition_set_start(pj
, ojj
+sjj
-oj
);
1772 dos_partition_set_start(pjj
, oj
+sj
-ojj
);
1777 /* Probably something was changed */
1778 for (j
= 4; j
< cxt
->label
->nparts_max
; j
++)
1779 l
->ptes
[j
].changed
= 1;
1782 int fdisk_dos_fix_order(struct fdisk_context
*cxt
)
1784 struct pte
*pei
, *pek
;
1787 if (!wrong_p_order(cxt
, NULL
)) {
1788 fdisk_info(cxt
, _("Nothing to do. Ordering is correct already."));
1792 while ((i
= wrong_p_order(cxt
, &k
)) != 0 && i
< 4) {
1793 /* partition i should have come earlier, move it */
1794 /* We have to move data in the MBR */
1795 struct dos_partition
*pi
, *pk
, *pe
, pbuf
;
1796 pei
= self_pte(cxt
, i
);
1797 pek
= self_pte(cxt
, k
);
1800 pei
->ex_entry
= pek
->ex_entry
;
1806 memmove(&pbuf
, pi
, sizeof(struct dos_partition
));
1807 memmove(pi
, pk
, sizeof(struct dos_partition
));
1808 memmove(pk
, &pbuf
, sizeof(struct dos_partition
));
1810 partition_set_changed(cxt
, i
, 1);
1811 partition_set_changed(cxt
, k
, 1);
1815 fix_chain_of_logicals(cxt
);
1817 fdisk_info(cxt
, _("Done."));
1821 int fdisk_dos_move_begin(struct fdisk_context
*cxt
, int i
)
1824 struct dos_partition
*p
;
1825 unsigned int new, free_start
, curr_start
, last
;
1831 assert(fdisk_is_disklabel(cxt
, DOS
));
1833 pe
= self_pte(cxt
, i
);
1836 if (!p
->sys_ind
|| !dos_partition_get_size(p
) || IS_EXTENDED (p
->sys_ind
)) {
1837 fdisk_warnx(cxt
, _("Partition %d: no data area."), i
+ 1);
1841 /* the default start is at the second sector of the disk or at the
1842 * second sector of the extended partition
1844 free_start
= pe
->offset
? pe
->offset
+ 1 : 1;
1846 curr_start
= get_abs_partition_start(pe
);
1848 /* look for a free space before the current start of the partition */
1849 for (x
= 0; x
< cxt
->label
->nparts_max
; x
++) {
1851 struct pte
*prev_pe
= self_pte(cxt
, x
);
1852 struct dos_partition
*prev_p
= prev_pe
->pt_entry
;
1856 end
= get_abs_partition_start(prev_pe
)
1857 + dos_partition_get_size(prev_p
);
1859 if (!is_cleared_partition(prev_p
) &&
1860 end
> free_start
&& end
<= curr_start
)
1864 last
= get_abs_partition_start(pe
) + dos_partition_get_size(p
) - 1;
1866 rc
= fdisk_ask_number(cxt
, free_start
, curr_start
, last
,
1867 _("New beginning of data"), &res
);
1871 new = res
- pe
->offset
;
1873 if (new != dos_partition_get_size(p
)) {
1874 unsigned int sects
= dos_partition_get_size(p
)
1875 + dos_partition_get_start(p
) - new;
1877 dos_partition_set_size(p
, sects
);
1878 dos_partition_set_start(p
, new);
1880 partition_set_changed(cxt
, i
, 1);
1886 static int dos_get_partition_status(
1887 struct fdisk_context
*cxt
,
1891 struct dos_partition
*p
;
1895 assert(fdisk_is_disklabel(cxt
, DOS
));
1897 if (!status
|| i
>= cxt
->label
->nparts_max
)
1900 p
= self_partition(cxt
, i
);
1902 if (p
&& !is_cleared_partition(p
))
1903 *status
= FDISK_PARTSTAT_USED
;
1905 *status
= FDISK_PARTSTAT_NONE
;
1910 static int dos_toggle_partition_flag(
1911 struct fdisk_context
*cxt
,
1915 struct dos_partition
*p
;
1919 assert(fdisk_is_disklabel(cxt
, DOS
));
1921 if (i
>= cxt
->label
->nparts_max
)
1924 p
= self_partition(cxt
, i
);
1927 case DOS_FLAG_ACTIVE
:
1928 if (IS_EXTENDED(p
->sys_ind
) && !p
->boot_ind
)
1929 fdisk_warnx(cxt
, _("Partition %d: is an extended "
1930 "partition."), (int) i
+ 1);
1932 p
->boot_ind
= (p
->boot_ind
? 0 : ACTIVE_FLAG
);
1933 partition_set_changed(cxt
, i
, 1);
1942 static const struct fdisk_label_operations dos_operations
=
1944 .probe
= dos_probe_label
,
1945 .write
= dos_write_disklabel
,
1946 .verify
= dos_verify_disklabel
,
1947 .create
= dos_create_disklabel
,
1948 .locate
= dos_locate_disklabel
,
1949 .list
= dos_list_disklabel
,
1950 .get_id
= dos_get_disklabel_id
,
1951 .set_id
= dos_set_disklabel_id
,
1953 .part_add
= dos_add_partition
,
1954 .part_delete
= dos_delete_partition
,
1955 .part_get_type
= dos_get_parttype
,
1956 .part_set_type
= dos_set_parttype
,
1958 .part_toggle_flag
= dos_toggle_partition_flag
,
1959 .part_get_status
= dos_get_partition_status
,
1961 .reset_alignment
= dos_reset_alignment
,
1963 .deinit
= dos_deinit
,
1967 * allocates DOS in-memory stuff
1969 struct fdisk_label
*fdisk_new_dos_label(struct fdisk_context
*cxt
)
1971 struct fdisk_label
*lb
;
1972 struct fdisk_dos_label
*dos
;
1976 dos
= calloc(1, sizeof(*dos
));
1980 /* initialize generic part of the driver */
1981 lb
= (struct fdisk_label
*) dos
;
1983 lb
->id
= FDISK_DISKLABEL_DOS
;
1984 lb
->op
= &dos_operations
;
1985 lb
->parttypes
= dos_parttypes
;
1986 lb
->nparttypes
= ARRAY_SIZE(dos_parttypes
);
1988 lb
->flags
|= FDISK_LABEL_FL_ADDPART_NOPARTNO
;
1994 * Public label specific functions
1997 int fdisk_dos_enable_compatible(struct fdisk_label
*lb
, int enable
)
1999 struct fdisk_dos_label
*dos
= (struct fdisk_dos_label
*) lb
;
2004 dos
->compatible
= enable
;
2006 lb
->flags
|= FDISK_LABEL_FL_REQUIRE_GEOMETRY
;
2010 int fdisk_dos_is_compatible(struct fdisk_label
*lb
)
2012 return ((struct fdisk_dos_label
*) lb
)->compatible
;