blob: 2a067076e394788565fd6b1e83625224f8918baa [file] [log] [blame]
bigbiff7b4c7a62015-01-01 19:44:14 -05001/*
2 *
3 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
4 * 2012 Davidlohr Bueso <dave@gnu.org>
5 *
6 * This is re-written version for libfdisk, the original was fdiskdoslabel.c
7 * from util-linux fdisk.
8 */
9#include "c.h"
10#include "nls.h"
11#include "randutils.h"
12#include "pt-mbr.h"
13#include "strutils.h"
14
15#include "fdiskP.h"
16
17#include <ctype.h>
18
19#define MAXIMUM_PARTS 60
20#define ACTIVE_FLAG 0x80
21
22/**
23 * SECTION: dos
24 * @title: DOS (MBR)
25 * @short_description: disk label specific functions
26 *
27 */
28
29
30#define IS_EXTENDED(i) \
31 ((i) == MBR_DOS_EXTENDED_PARTITION \
32 || (i) == MBR_W95_EXTENDED_PARTITION \
33 || (i) == MBR_LINUX_EXTENDED_PARTITION)
34
35/*
36 * per partition table entry data
37 *
38 * The four primary partitions have the same sectorbuffer
39 * and have NULL ex_entry.
40 *
41 * Each logical partition table entry has two pointers, one for the
42 * partition and one link to the next one.
43 */
44struct pte {
45 struct dos_partition *pt_entry; /* on-disk MBR entry */
46 struct dos_partition *ex_entry; /* on-disk EBR entry */
47 fdisk_sector_t offset; /* disk sector number */
48 unsigned char *sectorbuffer; /* disk sector contents */
49
50 unsigned int changed : 1,
51 private_sectorbuffer : 1;
52};
53
54/*
55 * in-memory fdisk GPT stuff
56 */
57struct fdisk_dos_label {
58 struct fdisk_label head; /* generic part */
59
60 struct pte ptes[MAXIMUM_PARTS]; /* partition */
61 fdisk_sector_t ext_offset; /* start of the ext.partition */
62 size_t ext_index; /* ext.partition index (if ext_offset is set) */
63 unsigned int compatible : 1, /* is DOS compatible? */
64 non_pt_changed : 1; /* MBR, but no PT changed */
65};
66
67/*
68 * Partition types
69 */
70static struct fdisk_parttype dos_parttypes[] = {
71 #include "pt-mbr-partnames.h"
72};
73
74#define set_hsc(h,s,c,sector) { \
75 s = sector % cxt->geom.sectors + 1; \
76 sector /= cxt->geom.sectors; \
77 h = sector % cxt->geom.heads; \
78 sector /= cxt->geom.heads; \
79 c = sector & 0xff; \
80 s |= (sector >> 2) & 0xc0; \
81 }
82
83
84#define sector(s) ((s) & 0x3f)
85#define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
86
87#define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
88
89#define is_dos_compatible(_x) \
90 (fdisk_is_label(_x, DOS) && \
91 fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
92
93#define cround(c, n) fdisk_cround(c, n)
94
95
96static inline struct fdisk_dos_label *self_label(struct fdisk_context *cxt)
97{
98 assert(cxt);
99 assert(cxt->label);
100 assert(fdisk_is_label(cxt, DOS));
101
102 return (struct fdisk_dos_label *) cxt->label;
103}
104
105static inline struct pte *self_pte(struct fdisk_context *cxt, size_t i)
106{
107 struct fdisk_dos_label *l = self_label(cxt);
108
109 if (i >= ARRAY_SIZE(l->ptes))
110 return NULL;
111
112 return &l->ptes[i];
113}
114
115static inline struct dos_partition *self_partition(
116 struct fdisk_context *cxt,
117 size_t i)
118{
119 struct pte *pe = self_pte(cxt, i);
120 return pe ? pe->pt_entry : NULL;
121}
122
123struct dos_partition *fdisk_dos_get_partition(
124 struct fdisk_context *cxt,
125 size_t i)
126{
127 assert(cxt);
128 assert(cxt->label);
129 assert(fdisk_is_label(cxt, DOS));
130
131 return self_partition(cxt, i);
132}
133
134static struct fdisk_parttype *dos_partition_parttype(
135 struct fdisk_context *cxt,
136 struct dos_partition *p)
137{
138 struct fdisk_parttype *t
139 = fdisk_label_get_parttype_from_code(cxt->label, p->sys_ind);
140 return t ? : fdisk_new_unknown_parttype(p->sys_ind, NULL);
141}
142
143/*
144 * Linux kernel cares about partition size only. Things like
145 * partition type or so are completely irrelevant -- kzak Nov-2013
146 */
147static int is_used_partition(struct dos_partition *p)
148{
149 return p && dos_partition_get_size(p) != 0;
150}
151
152static void partition_set_changed(
153 struct fdisk_context *cxt,
154 size_t i,
155 int changed)
156{
157 struct pte *pe = self_pte(cxt, i);
158
159 if (!pe)
160 return;
161
162 DBG(LABEL, ul_debug("DOS: setting %zu partition to %s", i,
163 changed ? "changed" : "unchanged"));
164
165 pe->changed = changed ? 1 : 0;
166 if (changed)
167 fdisk_label_set_changed(cxt->label, 1);
168}
169
170static fdisk_sector_t get_abs_partition_start(struct pte *pe)
171{
172 assert(pe);
173 assert(pe->pt_entry);
174
175 return pe->offset + dos_partition_get_start(pe->pt_entry);
176}
177
178static fdisk_sector_t get_abs_partition_end(struct pte *pe)
179{
180 fdisk_sector_t size;
181
182 assert(pe);
183 assert(pe->pt_entry);
184
185 size = dos_partition_get_size(pe->pt_entry);
186 return get_abs_partition_start(pe) + size - (size ? 1 : 0);
187}
188
189static int is_cleared_partition(struct dos_partition *p)
190{
191 return !(!p || p->boot_ind || p->bh || p->bs || p->bc ||
192 p->sys_ind || p->eh || p->es || p->ec ||
193 dos_partition_get_start(p) || dos_partition_get_size(p));
194}
195
196static int get_partition_unused_primary(struct fdisk_context *cxt,
197 struct fdisk_partition *pa,
198 size_t *partno)
199{
200 size_t org, n;
201 int rc;
202
203 assert(cxt);
204 assert(cxt->label);
205 assert(partno);
206
207 org = cxt->label->nparts_max;
208
209 cxt->label->nparts_max = 4;
210 rc = fdisk_partition_next_partno(pa, cxt, &n);
211 cxt->label->nparts_max = org;
212
213 if (rc == 1) {
214 fdisk_info(cxt, _("All primary partitions have been defined already."));
215 rc = -1;
216 } else if (rc == 0)
217 *partno = n;
218 return rc;
219}
220
221static int seek_sector(struct fdisk_context *cxt, fdisk_sector_t secno)
222{
223 off_t offset = (off_t) secno * cxt->sector_size;
224
225 return lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0;
226}
227
228static int read_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
229 unsigned char *buf)
230{
231 int rc = seek_sector(cxt, secno);
232 ssize_t r;
233
234 if (rc < 0)
235 return rc;
236
237 r = read(cxt->dev_fd, buf, cxt->sector_size);
238 if (r == (ssize_t) cxt->sector_size)
239 return 0;
240 if (r < 0)
241 return -errno;
242 return -1;
243}
244
245/* Allocate a buffer and read a partition table sector */
246static int read_pte(struct fdisk_context *cxt, size_t pno, fdisk_sector_t offset)
247{
248 int rc;
249 unsigned char *buf;
250 struct pte *pe = self_pte(cxt, pno);
251
252 buf = calloc(1, cxt->sector_size);
253 if (!buf)
254 return -ENOMEM;
255
256 DBG(LABEL, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
257 pno, (uintmax_t) offset, buf));
258
259 pe->offset = offset;
260 pe->sectorbuffer = buf;
261 pe->private_sectorbuffer = 1;
262
263 rc = read_sector(cxt, offset, pe->sectorbuffer);
264 if (rc) {
265 fdisk_warn(cxt, _("Failed to read extended partition table "
266 "(offset=%ju)"), (uintmax_t) offset);
267 return rc;
268 }
269
270 pe->changed = 0;
271 pe->pt_entry = pe->ex_entry = NULL;
272 return 0;
273}
274
275
276static void clear_partition(struct dos_partition *p)
277{
278 if (!p)
279 return;
280 p->boot_ind = 0;
281 p->bh = 0;
282 p->bs = 0;
283 p->bc = 0;
284 p->sys_ind = 0;
285 p->eh = 0;
286 p->es = 0;
287 p->ec = 0;
288 dos_partition_set_start(p,0);
289 dos_partition_set_size(p,0);
290}
291
292static void dos_init(struct fdisk_context *cxt)
293{
294 struct fdisk_dos_label *l = self_label(cxt);
295 size_t i;
296
297 assert(cxt);
298 assert(cxt->label);
299 assert(fdisk_is_label(cxt, DOS));
300
301 DBG(LABEL, ul_debug("DOS: initialize, first sector buffer %p", cxt->firstsector));
302
303 cxt->label->nparts_max = 4; /* default, unlimited number of logical */
304
305 l->ext_index = 0;
306 l->ext_offset = 0;
307 l->non_pt_changed = 0;
308
309 memset(l->ptes, 0, sizeof(l->ptes));
310
311 for (i = 0; i < 4; i++) {
312 struct pte *pe = self_pte(cxt, i);
313
314 pe->pt_entry = mbr_get_partition(cxt->firstsector, i);
315 pe->ex_entry = NULL;
316 pe->offset = 0;
317 pe->sectorbuffer = cxt->firstsector;
318 pe->private_sectorbuffer = 0;
319 pe->changed = 0;
320 }
321
322 if (fdisk_is_listonly(cxt))
323 return;
324 /*
325 * Various warnings...
326 */
327 if (fdisk_missing_geometry(cxt))
328 fdisk_warnx(cxt, _("You can set geometry from the extra functions menu."));
329
330 if (is_dos_compatible(cxt)) {
331 fdisk_warnx(cxt, _("DOS-compatible mode is deprecated."));
332
333 if (cxt->sector_size != cxt->phy_sector_size)
334 fdisk_info(cxt, _(
335 "The device presents a logical sector size that is smaller than "
336 "the physical sector size. Aligning to a physical sector (or optimal "
337 "I/O) size boundary is recommended, or performance may be impacted."));
338 }
339
340 if (fdisk_use_cylinders(cxt))
341 fdisk_warnx(cxt, _("Cylinders as display units are deprecated."));
342
343 if (cxt->total_sectors > UINT_MAX) {
344 uint64_t bytes = cxt->total_sectors * cxt->sector_size;
345 char *szstr = size_to_human_string(SIZE_SUFFIX_SPACE
346 | SIZE_SUFFIX_3LETTER, bytes);
347 fdisk_warnx(cxt,
348 _("The size of this disk is %s (%ju bytes). DOS "
349 "partition table format can not be used on drives for "
350 "volumes larger than %lu bytes for %lu-byte "
351 "sectors. Use GUID partition table format (GPT)."),
352 szstr, bytes,
353 UINT_MAX * cxt->sector_size,
354 cxt->sector_size);
355 free(szstr);
356 }
357}
358
359/* callback called by libfdisk */
360static void dos_deinit(struct fdisk_label *lb)
361{
362 size_t i;
363 struct fdisk_dos_label *l = (struct fdisk_dos_label *) lb;
364
365 for (i = 0; i < ARRAY_SIZE(l->ptes); i++) {
366 struct pte *pe = &l->ptes[i];
367
368 if (pe->private_sectorbuffer && pe->sectorbuffer) {
369 DBG(LABEL, ul_debug("DOS: freeing pte %zu sector buffer %p",
370 i, pe->sectorbuffer));
371 free(pe->sectorbuffer);
372 }
373 pe->sectorbuffer = NULL;
374 pe->private_sectorbuffer = 0;
375 }
376
377 memset(l->ptes, 0, sizeof(l->ptes));
378}
379
380static void reset_pte(struct pte *pe)
381{
382 assert(pe);
383
384 if (pe->private_sectorbuffer) {
385 DBG(LABEL, ul_debug(" --> freeing pte sector buffer %p",
386 pe->sectorbuffer));
387 free(pe->sectorbuffer);
388 }
389 memset(pe, 0, sizeof(struct pte));
390}
391
392static int dos_delete_partition(struct fdisk_context *cxt, size_t partnum)
393{
394 struct fdisk_dos_label *l;
395 struct pte *pe;
396 struct dos_partition *p;
397 struct dos_partition *q;
398
399 assert(cxt);
400 assert(cxt->label);
401 assert(fdisk_is_label(cxt, DOS));
402
403 pe = self_pte(cxt, partnum);
404 if (!pe)
405 return -EINVAL;
406
407 DBG(LABEL, ul_debug("DOS: delete partiton %zu (max=%zu)", partnum,
408 cxt->label->nparts_max));
409
410 l = self_label(cxt);
411 p = pe->pt_entry;
412 q = pe->ex_entry;
413
414 /* Note that for the fifth partition (partnum == 4) we don't actually
415 decrement partitions. */
416 if (partnum < 4) {
417 DBG(LABEL, ul_debug("--> delete primary"));
418 if (IS_EXTENDED(p->sys_ind) && partnum == l->ext_index) {
419 cxt->label->nparts_max = 4;
420 l->ptes[l->ext_index].ex_entry = NULL;
421 l->ext_offset = 0;
422 l->ext_index = 0;
423 }
424 partition_set_changed(cxt, partnum, 1);
425 clear_partition(p);
426 } else if (!q->sys_ind && partnum > 4) {
427 DBG(LABEL, ul_debug("--> delete logical [last in the chain]"));
428 reset_pte(&l->ptes[partnum]);
429 --cxt->label->nparts_max;
430 --partnum;
431 /* clear link to deleted partition */
432 clear_partition(l->ptes[partnum].ex_entry);
433 partition_set_changed(cxt, partnum, 1);
434 } else {
435 DBG(LABEL, ul_debug("--> delete logical [move down]"));
436 if (partnum > 4) {
437 DBG(LABEL, ul_debug(" --> delete %zu logical link", partnum));
438 p = l->ptes[partnum - 1].ex_entry;
439 *p = *q;
440 dos_partition_set_start(p, dos_partition_get_start(q));
441 dos_partition_set_size(p, dos_partition_get_size(q));
442 partition_set_changed(cxt, partnum - 1, 1);
443
444 } else if (cxt->label->nparts_max > 5) {
445 DBG(LABEL, ul_debug(" --> delete first logical link"));
446 pe = &l->ptes[5]; /* second logical */
447
448 if (pe->pt_entry) /* prevent SEGFAULT */
449 dos_partition_set_start(pe->pt_entry,
450 get_abs_partition_start(pe) -
451 l->ext_offset);
452 pe->offset = l->ext_offset;
453 partition_set_changed(cxt, 5, 1);
454 }
455
456 if (cxt->label->nparts_max > 5) {
457 DBG(LABEL, ul_debug(" --> move ptes"));
458 cxt->label->nparts_max--;
459 reset_pte(&l->ptes[partnum]);
460 while (partnum < cxt->label->nparts_max) {
461 DBG(LABEL, ul_debug(" --> moving pte %zu <-- %zu", partnum, partnum + 1));
462 l->ptes[partnum] = l->ptes[partnum + 1];
463 partnum++;
464 }
465 memset(&l->ptes[partnum], 0, sizeof(struct pte));
466 } else {
467 DBG(LABEL, ul_debug(" --> the only logical: clear only"));
468 clear_partition(l->ptes[partnum].pt_entry);
469 cxt->label->nparts_max--;
470
471 if (partnum == 4) {
472 DBG(LABEL, ul_debug(" --> clear last logical"));
473 reset_pte(&l->ptes[partnum]);
474 partition_set_changed(cxt, l->ext_index, 1);
475 }
476 }
477 }
478
479 fdisk_label_set_changed(cxt->label, 1);
480 return 0;
481}
482
483static void read_extended(struct fdisk_context *cxt, size_t ext)
484{
485 size_t i;
486 struct pte *pex, *pe;
487 struct dos_partition *p, *q;
488 struct fdisk_dos_label *l = self_label(cxt);
489
490 l->ext_index = ext;
491 pex = self_pte(cxt, ext);
492 pex->ex_entry = pex->pt_entry;
493
494 p = pex->pt_entry;
495 if (!dos_partition_get_start(p)) {
496 fdisk_warnx(cxt, _("Bad offset in primary extended partition."));
497 return;
498 }
499
500 DBG(LABEL, ul_debug("DOS: Reading extended %zu", ext));
501
502 while (IS_EXTENDED (p->sys_ind)) {
503 pe = self_pte(cxt, cxt->label->nparts_max);
504
505 if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
506 /* This is not a Linux restriction, but
507 this program uses arrays of size MAXIMUM_PARTS.
508 Do not try to `improve' this test. */
509 struct pte *pre = self_pte(cxt,
510 cxt->label->nparts_max - 1);
511 fdisk_warnx(cxt,
512 _("Omitting partitions after #%zu. They will be deleted "
513 "if you save this partition table."),
514 cxt->label->nparts_max);
515
516 clear_partition(pre->ex_entry);
517 partition_set_changed(cxt,
518 cxt->label->nparts_max - 1, 1);
519 return;
520 }
521
522 if (read_pte(cxt, cxt->label->nparts_max, l->ext_offset +
523 dos_partition_get_start(p)))
524 return;
525
526 if (!l->ext_offset)
527 l->ext_offset = dos_partition_get_start(p);
528
529 assert(pe->sectorbuffer);
530 q = p = mbr_get_partition(pe->sectorbuffer, 0);
531
532 for (i = 0; i < 4; i++, p++) {
533 if (!dos_partition_get_size(p))
534 continue;
535
536 if (IS_EXTENDED (p->sys_ind)) {
537 if (pe->ex_entry)
538 fdisk_warnx(cxt, _(
539 "Extra link pointer in partition "
540 "table %zu."),
541 cxt->label->nparts_max + 1);
542 else
543 pe->ex_entry = p;
544 } else if (p->sys_ind) {
545 if (pe->pt_entry)
546 fdisk_warnx(cxt, _(
547 "Ignoring extra data in partition "
548 "table %zu."),
549 cxt->label->nparts_max + 1);
550 else
551 pe->pt_entry = p;
552 }
553 }
554
555 /* very strange code here... */
556 if (!pe->pt_entry) {
557 if (q != pe->ex_entry)
558 pe->pt_entry = q;
559 else
560 pe->pt_entry = q + 1;
561 }
562 if (!pe->ex_entry) {
563 if (q != pe->pt_entry)
564 pe->ex_entry = q;
565 else
566 pe->ex_entry = q + 1;
567 }
568
569 p = pe->ex_entry;
570 cxt->label->nparts_cur = ++cxt->label->nparts_max;
571
572 DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: link: type=%x, start=%u, size=%u; "
573 " data: type=%x, start=%u, size=%u",
574 (uintmax_t) pe->offset,
575 pe->ex_entry->sys_ind,
576 dos_partition_get_start(pe->ex_entry),
577 dos_partition_get_size(pe->ex_entry),
578 pe->pt_entry->sys_ind,
579 dos_partition_get_start(pe->pt_entry),
580 dos_partition_get_size(pe->pt_entry)));
581
582 }
583
584 /* remove last empty EBR */
585 pe = self_pte(cxt, cxt->label->nparts_max - 1);
586 if (is_cleared_partition(pe->ex_entry) &&
587 is_cleared_partition(pe->pt_entry)) {
588 DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe->offset));
589 reset_pte(pe);
590 cxt->label->nparts_max--;
591 cxt->label->nparts_cur--;
592 }
593
594 /* remove empty links */
595 remove:
596 q = self_partition(cxt, 4);
597 for (i = 4; i < cxt->label->nparts_max; i++) {
598 p = self_partition(cxt, i);
599
600 if (!dos_partition_get_size(p) &&
601 (cxt->label->nparts_max > 5 || q->sys_ind)) {
602 fdisk_info(cxt, _("omitting empty partition (%zu)"), i+1);
603 dos_delete_partition(cxt, i);
604 goto remove; /* numbering changed */
605 }
606 }
607
608 DBG(LABEL, ul_debug("DOS: nparts_max: %zu", cxt->label->nparts_max));
609}
610
611static int dos_get_disklabel_id(struct fdisk_context *cxt, char **id)
612{
613 unsigned int num;
614
615 assert(cxt);
616 assert(id);
617 assert(cxt->label);
618 assert(fdisk_is_label(cxt, DOS));
619
620 num = mbr_get_id(cxt->firstsector);
621 if (asprintf(id, "0x%08x", num) > 0)
622 return 0;
623
624 return -ENOMEM;
625}
626
627static int dos_create_disklabel(struct fdisk_context *cxt)
628{
629 unsigned int id = 0;
630 int rc, has_id = 0;
631
632 assert(cxt);
633 assert(cxt->label);
634 assert(fdisk_is_label(cxt, DOS));
635
636 DBG(LABEL, ul_debug("DOS: creating new disklabel"));
637
638 if (cxt->script) {
639 char *end = NULL;
640 const char *s = fdisk_script_get_header(cxt->script, "label-id");
641
642 if (s) {
643 errno = 0;
644 id = strtol(s, &end, 16);
645 if (!errno && end && s < end)
646 has_id = 1;
647 }
648 }
649
650 /* random disk signature */
651 if (!has_id)
652 random_get_bytes(&id, sizeof(id));
653
654 dos_init(cxt);
655 rc = fdisk_init_firstsector_buffer(cxt);
656 if (rc)
657 return rc;
658 fdisk_label_set_changed(cxt->label, 1);
659
660 /* Generate an MBR ID for this disk */
661 mbr_set_id(cxt->firstsector, id);
662
663 /* Put MBR signature */
664 mbr_set_magic(cxt->firstsector);
665
666 fdisk_info(cxt, _("Created a new DOS disklabel with disk "
667 "identifier 0x%08x."), id);
668 return 0;
669}
670
671static int dos_set_disklabel_id(struct fdisk_context *cxt)
672{
673 char *end = NULL, *str = NULL;
674 unsigned int id, old;
675 struct fdisk_dos_label *l;
676 int rc;
677
678 assert(cxt);
679 assert(cxt->label);
680 assert(fdisk_is_label(cxt, DOS));
681
682 DBG(LABEL, ul_debug("DOS: setting Id"));
683
684 l = self_label(cxt);
685 old = mbr_get_id(cxt->firstsector);
686 rc = fdisk_ask_string(cxt,
687 _("Enter the new disk identifier"), &str);
688 if (rc)
689 return rc;
690
691 errno = 0;
692 id = strtoul(str, &end, 0);
693 if (errno || str == end || (end && *end)) {
694 fdisk_warnx(cxt, _("Incorrect value."));
695 return -EINVAL;
696 }
697
698
699 mbr_set_id(cxt->firstsector, id);
700 l->non_pt_changed = 1;
701 fdisk_label_set_changed(cxt->label, 1);
702
703 fdisk_info(cxt, _("Disk identifier changed from 0x%08x to 0x%08x."),
704 old, id);
705 return 0;
706}
707
708static void get_partition_table_geometry(struct fdisk_context *cxt,
709 unsigned int *ph, unsigned int *ps)
710{
711 unsigned char *bufp = cxt->firstsector;
712 struct dos_partition *p;
713 int i, h, s, hh, ss;
714 int first = 1;
715 int bad = 0;
716
717 hh = ss = 0;
718 for (i = 0; i < 4; i++) {
719 p = mbr_get_partition(bufp, i);
720 if (p->sys_ind != 0) {
721 h = p->eh + 1;
722 s = (p->es & 077);
723 if (first) {
724 hh = h;
725 ss = s;
726 first = 0;
727 } else if (hh != h || ss != s)
728 bad = 1;
729 }
730 }
731
732 if (!first && !bad) {
733 *ph = hh;
734 *ps = ss;
735 }
736
737 DBG(LABEL, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps));
738}
739
740static int dos_reset_alignment(struct fdisk_context *cxt)
741{
742 assert(cxt);
743 assert(cxt->label);
744 assert(fdisk_is_label(cxt, DOS));
745
746 /* overwrite necessary stuff by DOS deprecated stuff */
747 if (is_dos_compatible(cxt)) {
748 DBG(LABEL, ul_debug("DOS: reseting alignemnt for DOS-comaptiblem PT"));
749 if (cxt->geom.sectors)
750 cxt->first_lba = cxt->geom.sectors; /* usually 63 */
751
752 cxt->grain = cxt->sector_size; /* usually 512 */
753 }
754
755 return 0;
756}
757
758/* TODO: move to include/pt-dos.h and share with libblkid */
759#define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
760#define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
761
762static int dos_probe_label(struct fdisk_context *cxt)
763{
764 size_t i;
765 unsigned int h = 0, s = 0;
766
767 assert(cxt);
768 assert(cxt->label);
769 assert(fdisk_is_label(cxt, DOS));
770
771 /* ignore disks with AIX magic number */
772 if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0)
773 return 0;
774
775 if (!mbr_is_valid_magic(cxt->firstsector))
776 return 0;
777
778 dos_init(cxt);
779
780 get_partition_table_geometry(cxt, &h, &s);
781 if (h && s) {
782 cxt->geom.heads = h;
783 cxt->geom.sectors = s;
784 }
785
786 for (i = 0; i < 4; i++) {
787 struct pte *pe = self_pte(cxt, i);
788
789 if (is_used_partition(pe->pt_entry))
790 cxt->label->nparts_cur++;
791
792 if (IS_EXTENDED (pe->pt_entry->sys_ind)) {
793 if (cxt->label->nparts_max != 4)
794 fdisk_warnx(cxt, _(
795 "Ignoring extra extended partition %zu"),
796 i + 1);
797 else
798 read_extended(cxt, i);
799 }
800 }
801
802 for (i = 3; i < cxt->label->nparts_max; i++) {
803 struct pte *pe = self_pte(cxt, i);
804 struct fdisk_dos_label *l = self_label(cxt);
805
806 if (!mbr_is_valid_magic(pe->sectorbuffer)) {
807 fdisk_info(cxt, _(
808 "Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
809 "be corrected by w(rite)."),
810 pe->sectorbuffer[510],
811 pe->sectorbuffer[511],
812 i + 1);
813 partition_set_changed(cxt, i, 1);
814
815 /* mark also extended as changed to update the first EBR
816 * in situation that there is no logical partitions at all */
817 partition_set_changed(cxt, l->ext_index, 1);
818 }
819 }
820
821 return 1;
822}
823
824static void set_partition(struct fdisk_context *cxt,
825 int i, int doext, fdisk_sector_t start,
826 fdisk_sector_t stop, int sysid, int boot)
827{
828 struct pte *pe = self_pte(cxt, i);
829 struct dos_partition *p;
830 fdisk_sector_t offset;
831
832 assert(!FDISK_IS_UNDEF(start));
833 assert(!FDISK_IS_UNDEF(stop));
834
835 if (doext) {
836 struct fdisk_dos_label *l = self_label(cxt);
837 p = pe->ex_entry;
838 offset = l->ext_offset;
839 } else {
840 p = pe->pt_entry;
841 offset = pe->offset;
842 }
843
844 DBG(LABEL, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, stop=%zu, sysid=%02x",
845 i, doext ? " [extended]" : "",
846 (size_t) offset,
847 (size_t) (start - offset),
848 (size_t) (stop - start + 1),
849 sysid));
850
851 p->boot_ind = boot ? ACTIVE_FLAG : 0;
852 p->sys_ind = sysid;
853 dos_partition_set_start(p, start - offset);
854 dos_partition_set_size(p, stop - start + 1);
855
856 if (is_dos_compatible(cxt) && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
857 start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
858 set_hsc(p->bh, p->bs, p->bc, start);
859 if (is_dos_compatible(cxt) && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
860 stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
861 set_hsc(p->eh, p->es, p->ec, stop);
862 partition_set_changed(cxt, i, 1);
863}
864
865static fdisk_sector_t get_unused_start(struct fdisk_context *cxt,
866 int part_n, fdisk_sector_t start,
867 fdisk_sector_t first[], fdisk_sector_t last[])
868{
869 size_t i;
870
871 for (i = 0; i < cxt->label->nparts_max; i++) {
872 fdisk_sector_t lastplusoff;
873 struct pte *pe = self_pte(cxt, i);
874
875 if (start == pe->offset)
876 start += cxt->first_lba;
877 lastplusoff = last[i] + ((part_n < 4) ? 0 : cxt->first_lba);
878 if (start >= first[i] && start <= lastplusoff)
879 start = lastplusoff + 1;
880 }
881
882 return start;
883}
884
885static void fill_bounds(struct fdisk_context *cxt,
886 fdisk_sector_t *first, fdisk_sector_t *last)
887{
888 size_t i;
889 struct pte *pe = self_pte(cxt, 0);
890 struct dos_partition *p;
891
892 for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
893 p = pe->pt_entry;
894 if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
895 first[i] = 0xffffffff;
896 last[i] = 0;
897 } else {
898 first[i] = get_abs_partition_start(pe);
899 last[i] = get_abs_partition_end(pe);
900 }
901 }
902}
903
904static int get_start_from_user( struct fdisk_context *cxt,
905 fdisk_sector_t *start,
906 fdisk_sector_t low,
907 fdisk_sector_t dflt,
908 fdisk_sector_t limit,
909 struct fdisk_partition *pa)
910{
911 assert(start);
912
913 /* try to use tepmlate from 'pa' */
914 if (pa && pa->start_follow_default)
915 *start = dflt;
916
917 else if (pa && fdisk_partition_has_start(pa)) {
918 DBG(LABEL, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
919 (uintmax_t) pa->start, (uintmax_t) low, (uintmax_t) limit));
920 *start = pa->start;
921 if (*start < low || *start > limit) {
922 fdisk_warnx(cxt, _("Start sector %ju out of range."),
923 (uintmax_t) *start);
924 return -ERANGE;
925 }
926 } else {
927 /* ask user by dialog */
928 struct fdisk_ask *ask = fdisk_new_ask();
929 int rc;
930
931 if (!ask)
932 return -ENOMEM;
933 fdisk_ask_set_query(ask,
934 fdisk_use_cylinders(cxt) ?
935 _("First cylinder") : _("First sector"));
936 fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
937 fdisk_ask_number_set_low(ask, fdisk_cround(cxt, low));
938 fdisk_ask_number_set_default(ask, fdisk_cround(cxt, dflt));
939 fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
940
941 rc = fdisk_do_ask(cxt, ask);
942 *start = fdisk_ask_number_get_result(ask);
943 fdisk_unref_ask(ask);
944 if (rc)
945 return rc;
946 if (fdisk_use_cylinders(cxt)) {
947 *start = (*start - 1)
948 * fdisk_get_units_per_sector(cxt);
949 if (*start < low)
950 *start = low;
951 }
952 }
953
954 DBG(LABEL, ul_debug("DOS: start is %ju", (uintmax_t) *start));
955 return 0;
956}
957
958static fdisk_sector_t get_possible_last(struct fdisk_context *cxt, size_t n)
959{
960 fdisk_sector_t limit;
961
962 if (n >= 4) {
963 /* logical partitions */
964 struct fdisk_dos_label *l = self_label(cxt);
965 struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
966
967 if (!ext_pe)
968 return 0;
969 limit = get_abs_partition_end(ext_pe);
970 } else {
971 /* primary partitions */
972 if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
973 limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
974 else
975 limit = cxt->total_sectors - 1;
976
977 if (limit > UINT_MAX)
978 limit = UINT_MAX;
979 }
980
981 DBG(LABEL, ul_debug("DOS: last possible sector for #%zu is %ju",
982 n, (uintmax_t) limit));
983 return limit;
984}
985
986/* returns last free sector for area addressed by @start, the first[] and
987 * last[] are fill_bounds() results */
988static fdisk_sector_t get_unused_last(struct fdisk_context *cxt, size_t n,
989 fdisk_sector_t start,
990 fdisk_sector_t first[], fdisk_sector_t last[])
991{
992 size_t i;
993 fdisk_sector_t limit = get_possible_last(cxt, n);
994
995 for (i = 0; i < cxt->label->nparts_max; i++) {
996 struct pte *pe = self_pte(cxt, i);
997
998 if (start < pe->offset && limit >= pe->offset)
999 limit = pe->offset - 1;
1000 if (start < first[i] && limit >= first[i])
1001 limit = first[i] - 1;
1002 }
1003
1004 DBG(LABEL, ul_debug("DOS: unused sector for #%zu is %ju",
1005 n, (uintmax_t) limit));
1006 return limit;
1007}
1008
1009static int add_partition(struct fdisk_context *cxt, size_t n,
1010 struct fdisk_partition *pa)
1011{
1012 int sys, read = 0, rc, isrel = 0;
1013 size_t i;
1014 struct fdisk_dos_label *l = self_label(cxt);
1015 struct dos_partition *p = self_partition(cxt, n);
1016 struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1017
1018 fdisk_sector_t start, stop = 0, limit, temp,
1019 first[cxt->label->nparts_max],
1020 last[cxt->label->nparts_max];
1021
1022 DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
1023
1024 sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
1025
1026 if (is_used_partition(p)) {
1027 fdisk_warnx(cxt, _("Partition %zu is already defined. "
1028 "Delete it before re-adding it."),
1029 n + 1);
1030 return -EINVAL;
1031 }
1032 fill_bounds(cxt, first, last);
1033 limit = get_possible_last(cxt, n);
1034
1035 if (n < 4) {
1036 if (cxt->parent && fdisk_is_label(cxt->parent, GPT))
1037 start = 1; /* Bad boy modifies hybrid MBR */
1038 else {
1039 if (cxt->script && pa && fdisk_partition_has_start(pa)
1040 && pa->start < cxt->first_lba
1041 && pa->start >= 1)
1042 fdisk_set_first_lba(cxt, 1);
1043
1044 start = cxt->first_lba;
1045 }
1046
1047 if (l->ext_offset) {
1048 assert(ext_pe);
1049 first[l->ext_index] = l->ext_offset;
1050 last[l->ext_index] = get_abs_partition_end(ext_pe);
1051 }
1052 } else {
1053 assert(ext_pe);
1054
1055 if (cxt->script && pa && fdisk_partition_has_start(pa)
1056 && pa->start >= l->ext_offset
1057 && pa->start < l->ext_offset + cxt->first_lba)
1058 fdisk_set_first_lba(cxt, 1);
1059
1060 start = l->ext_offset + cxt->first_lba;
1061 }
1062
1063 if (fdisk_use_cylinders(cxt))
1064 for (i = 0; i < cxt->label->nparts_max; i++) {
1065 first[i] = (fdisk_cround(cxt, first[i]) - 1)
1066 * fdisk_get_units_per_sector(cxt);
1067 }
1068
1069 /*
1070 * Ask for first sector
1071 */
1072 do {
1073 fdisk_sector_t dflt, aligned;
1074
1075 temp = start;
1076 dflt = start = get_unused_start(cxt, n, start, first, last);
1077
1078 if (n >= 4 && pa && fdisk_partition_has_start(pa) && cxt->script
1079 && cxt->first_lba > 1
1080 && temp == start - cxt->first_lba) {
1081 fdisk_set_first_lba(cxt, 1);
1082 start = pa->start;
1083 }
1084
1085 /* the default sector should be aligned and unused */
1086 do {
1087 aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
1088 dflt = get_unused_start(cxt, n, aligned, first, last);
1089 } while (dflt != aligned && dflt > aligned && dflt < limit);
1090
1091 if (dflt >= limit)
1092 dflt = start;
1093 if (start > limit)
1094 break;
1095 if (start >= temp + fdisk_get_units_per_sector(cxt)
1096 && read) {
1097 fdisk_info(cxt, _("Sector %llu is already allocated."),
1098 temp);
1099 temp = start;
1100 read = 0;
1101 if (pa && (fdisk_partition_has_start(pa) ||
1102 pa->start_follow_default))
1103 break;
1104 }
1105
1106 if (!read && start == temp) {
1107 rc = get_start_from_user(cxt, &start, temp, dflt, limit, pa);
1108 if (rc)
1109 return rc;
1110 read = 1;
1111 }
1112 } while (start != temp || !read);
1113
1114 if (n == 4) {
1115 /* The first EBR is stored at begin of the extended partition */
1116 struct pte *pe = self_pte(cxt, n);
1117 pe->offset = l->ext_offset;
1118
1119 } else if (n > 4) {
1120 /* The second (and another) EBR */
1121 struct pte *pe = self_pte(cxt, n);
1122
1123 pe->offset = start - cxt->first_lba;
1124 if (pe->offset == l->ext_offset) { /* must be corrected */
1125 pe->offset++;
1126 if (cxt->first_lba == 1)
1127 start++;
1128 }
1129 }
1130
1131 limit = get_unused_last(cxt, n, start, first, last);
1132
1133 if (start > limit) {
1134 fdisk_info(cxt, _("No free sectors available."));
1135 if (n > 4)
1136 cxt->label->nparts_max--;
1137 return -ENOSPC;
1138 }
1139
1140 /*
1141 * Ask for last sector
1142 */
1143 if (fdisk_cround(cxt, start) == fdisk_cround(cxt, limit))
1144 stop = limit;
1145 else if (pa && pa->end_follow_default)
1146 stop = limit;
1147 else if (pa && fdisk_partition_has_size(pa)) {
1148 stop = start + pa->size - 1;
1149 isrel = pa->size_explicit ? 0 : 1;
1150 } else {
1151 /* ask user by dialog */
1152 struct fdisk_ask *ask = fdisk_new_ask();
1153
1154 if (!ask)
1155 return -ENOMEM;
1156 fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
1157
1158 if (fdisk_use_cylinders(cxt)) {
1159 fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
1160 fdisk_ask_number_set_unit(ask,
1161 cxt->sector_size *
1162 fdisk_get_units_per_sector(cxt));
1163 } else {
1164 fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
1165 fdisk_ask_number_set_unit(ask,cxt->sector_size);
1166 }
1167
1168 fdisk_ask_number_set_low(ask, fdisk_cround(cxt, start));
1169 fdisk_ask_number_set_default(ask, fdisk_cround(cxt, limit));
1170 fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
1171 fdisk_ask_number_set_base(ask, fdisk_cround(cxt, start)); /* base for relative input */
1172
1173 rc = fdisk_do_ask(cxt, ask);
1174 stop = fdisk_ask_number_get_result(ask);
1175 isrel = fdisk_ask_number_is_relative(ask);
1176 fdisk_unref_ask(ask);
1177 if (rc)
1178 return rc;
1179 if (fdisk_use_cylinders(cxt)) {
1180 stop = stop * fdisk_get_units_per_sector(cxt) - 1;
1181 if (stop >limit)
1182 stop = limit;
1183 }
1184 }
1185
1186 DBG(LABEL, ul_debug("DOS: raw stop: %ju", (uintmax_t) stop));
1187
1188 if (stop > limit)
1189 stop = limit;
1190
1191 if (stop < limit) {
1192 if (isrel && alignment_required(cxt)) {
1193 /* the last sector has not been exactly requested (but
1194 * defined by +size{K,M,G} convention), so be smart and
1195 * align the end of the partition. The next partition
1196 * will start at phy.block boundary.
1197 */
1198 stop = fdisk_align_lba_in_range(cxt, stop, start, limit) - 1;
1199 if (stop > limit)
1200 stop = limit;
1201 }
1202 }
1203
1204 set_partition(cxt, n, 0, start, stop, sys, pa && pa->boot == 1 ? 1 : 0);
1205 if (n > 4) {
1206 struct pte *pe = self_pte(cxt, n);
1207 set_partition(cxt, n - 1, 1, pe->offset, stop,
1208 MBR_DOS_EXTENDED_PARTITION, 0);
1209 }
1210
1211 /* report */
1212 {
1213 struct fdisk_parttype *t =
1214 fdisk_label_get_parttype_from_code(cxt->label, sys);
1215 fdisk_info_new_partition(cxt, n + 1, start, stop, t);
1216 fdisk_unref_parttype(t);
1217 }
1218
1219
1220 if (IS_EXTENDED(sys)) {
1221 struct pte *pen = self_pte(cxt, n);
1222
1223 l->ext_index = n;
1224 l->ext_offset = start;
1225 pen->ex_entry = p;
1226 }
1227
1228 fdisk_label_set_changed(cxt->label, 1);
1229 return 0;
1230}
1231
1232static int add_logical(struct fdisk_context *cxt,
1233 struct fdisk_partition *pa,
1234 size_t *partno)
1235{
1236 struct pte *pe;
1237 int rc;
1238
1239 assert(cxt);
1240 assert(partno);
1241 assert(cxt->label);
1242 assert(self_label(cxt)->ext_offset);
1243
1244 DBG(LABEL, ul_debug("DOS: nparts max: %zu", cxt->label->nparts_max));
1245 pe = self_pte(cxt, cxt->label->nparts_max);
1246
1247 if (!pe->sectorbuffer) {
1248 pe->sectorbuffer = calloc(1, cxt->sector_size);
1249 if (!pe->sectorbuffer)
1250 return -ENOMEM;
1251 DBG(LABEL, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1252 cxt->label->nparts_max, pe->sectorbuffer));
1253 pe->private_sectorbuffer = 1;
1254 }
1255 pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
1256 pe->ex_entry = pe->pt_entry + 1;
1257 pe->offset = 0;
1258 partition_set_changed(cxt, cxt->label->nparts_max, 1);
1259
1260 cxt->label->nparts_max++;
1261
1262 /* this message makes sense only when we use extended/primary/logical
1263 * dialog. The dialog is disable for scripts, see dos_add_partition() */
1264 if (!cxt->script)
1265 fdisk_info(cxt, _("Adding logical partition %zu"),
1266 cxt->label->nparts_max);
1267 *partno = cxt->label->nparts_max - 1;
1268 rc = add_partition(cxt, *partno, pa);
1269
1270 if (rc) {
1271 /* reset on error */
1272 cxt->label->nparts_max--;
1273 pe->pt_entry = NULL;
1274 pe->ex_entry = NULL;
1275 pe->offset = 0;
1276 pe->changed = 0;
1277 }
1278
1279 return rc;
1280}
1281
1282static void check(struct fdisk_context *cxt, size_t n,
1283 unsigned int h, unsigned int s, unsigned int c,
1284 unsigned int start)
1285{
1286 unsigned int total, real_s, real_c;
1287
1288 if (!is_dos_compatible(cxt))
1289 return;
1290
1291 real_s = sector(s) - 1;
1292 real_c = cylinder(s, c);
1293 total = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
1294
1295 if (!total)
1296 fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
1297 if (h >= cxt->geom.heads)
1298 fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
1299 "maximum %d"), n, h + 1, cxt->geom.heads);
1300 if (real_s >= cxt->geom.sectors)
1301 fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
1302 "maximum %llu"), n, s, cxt->geom.sectors);
1303 if (real_c >= cxt->geom.cylinders)
1304 fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
1305 "maximum %llu"),
1306 n, real_c + 1,
1307 cxt->geom.cylinders);
1308
1309 if (cxt->geom.cylinders <= 1024 && start != total)
1310 fdisk_warnx(cxt, _("Partition %zu: previous sectors %u "
1311 "disagrees with total %u"), n, start, total);
1312}
1313
1314/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1315 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1316 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1317 * Lubkin Oct. 1991). */
1318
1319static void
1320long2chs(struct fdisk_context *cxt, unsigned long ls,
1321 unsigned int *c, unsigned int *h, unsigned int *s) {
1322 int spc = cxt->geom.heads * cxt->geom.sectors;
1323
1324 *c = ls / spc;
1325 ls = ls % spc;
1326 *h = ls / cxt->geom.sectors;
1327 *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
1328}
1329
1330static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
1331 size_t partition)
1332{
1333 unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
1334 unsigned int pec, peh, pes; /* physical ending c, h, s */
1335 unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
1336 unsigned int lec, leh, les; /* logical ending c, h, s */
1337
1338 if (!is_dos_compatible(cxt))
1339 return;
1340
1341 if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
1342 return; /* do not check extended partitions */
1343
1344 /* physical beginning c, h, s */
1345 pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
1346 pbh = p->bh;
1347 pbs = p->bs & 0x3f;
1348
1349 /* physical ending c, h, s */
1350 pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
1351 peh = p->eh;
1352 pes = p->es & 0x3f;
1353
1354 /* compute logical beginning (c, h, s) */
1355 long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
1356
1357 /* compute logical ending (c, h, s) */
1358 long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
1359
1360 /* Same physical / logical beginning? */
1361 if (cxt->geom.cylinders <= 1024
1362 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
1363 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
1364 "beginnings (non-Linux?): "
1365 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1366 partition + 1,
1367 pbc, pbh, pbs,
1368 lbc, lbh, lbs);
1369 }
1370
1371 /* Same physical / logical ending? */
1372 if (cxt->geom.cylinders <= 1024
1373 && (pec != lec || peh != leh || pes != les)) {
1374 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
1375 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1376 partition + 1,
1377 pec, peh, pes,
1378 lec, leh, les);
1379 }
1380
1381 /* Ending on cylinder boundary? */
1382 if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
1383 fdisk_warnx(cxt, _("Partition %zu: does not end on "
1384 "cylinder boundary."),
1385 partition + 1);
1386 }
1387}
1388
1389static int dos_verify_disklabel(struct fdisk_context *cxt)
1390{
1391 size_t i, j;
1392 fdisk_sector_t total = 1, n_sectors = cxt->total_sectors;
1393 fdisk_sector_t first[cxt->label->nparts_max],
1394 last[cxt->label->nparts_max];
1395 struct dos_partition *p;
1396 struct fdisk_dos_label *l = self_label(cxt);
1397
1398 assert(fdisk_is_label(cxt, DOS));
1399
1400 fill_bounds(cxt, first, last);
1401 for (i = 0; i < cxt->label->nparts_max; i++) {
1402 struct pte *pe = self_pte(cxt, i);
1403
1404 p = self_partition(cxt, i);
1405 if (is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
1406 check_consistency(cxt, p, i);
1407 if (get_abs_partition_start(pe) < first[i])
1408 fdisk_warnx(cxt, _(
1409 "Partition %zu: bad start-of-data."),
1410 i + 1);
1411
1412 check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
1413 total += last[i] + 1 - first[i];
1414
1415 if (i == 0)
1416 total += get_abs_partition_start(pe) - 1;
1417
1418 for (j = 0; j < i; j++) {
1419 if ((first[i] >= first[j] && first[i] <= last[j])
1420 || ((last[i] <= last[j] && last[i] >= first[j]))) {
1421
1422 fdisk_warnx(cxt, _("Partition %zu: "
1423 "overlaps partition %zu."),
1424 j + 1, i + 1);
1425
1426 total += first[i] >= first[j] ?
1427 first[i] : first[j];
1428 total -= last[i] <= last[j] ?
1429 last[i] : last[j];
1430 }
1431 }
1432 }
1433 }
1434
1435 if (l->ext_offset) {
1436 fdisk_sector_t e_last;
1437 struct pte *ext_pe = self_pte(cxt, l->ext_index);
1438
1439 e_last = get_abs_partition_end(ext_pe);
1440
1441 for (i = 4; i < cxt->label->nparts_max; i++) {
1442 total++;
1443 p = self_partition(cxt, i);
1444
1445 if (!p->sys_ind) {
1446 if (i != 4 || i + 1 < cxt->label->nparts_max)
1447 fdisk_warnx(cxt,
1448 _("Partition %zu: empty."),
1449 i + 1);
1450 } else if (first[i] < l->ext_offset
1451 || last[i] > e_last) {
1452
1453 fdisk_warnx(cxt, _("Logical partition %zu: "
1454 "not entirely in partition %zu."),
1455 i + 1, l->ext_index + 1);
1456 }
1457 }
1458 }
1459
1460 if (total > n_sectors)
1461 fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
1462 "than the maximum %llu."), total, n_sectors);
1463 else if (total < n_sectors)
1464 fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
1465 "sectors."), n_sectors - total, cxt->sector_size);
1466
1467 return 0;
1468}
1469
1470/*
1471 * Ask the user for new partition type information (logical, extended).
1472 * This function calls the actual partition adding logic - add_partition.
1473 *
1474 * API callback.
1475 */
1476static int dos_add_partition(struct fdisk_context *cxt,
1477 struct fdisk_partition *pa,
1478 size_t *partno)
1479{
1480 size_t i, free_primary = 0, free_sectors = 0;
1481 fdisk_sector_t last = 0, grain;
1482 int rc = 0;
1483 struct fdisk_dos_label *l;
1484 struct pte *ext_pe;
1485 size_t res; /* partno */
1486
1487 assert(cxt);
1488 assert(cxt->label);
1489 assert(fdisk_is_label(cxt, DOS));
1490
1491 DBG(LABEL, ul_debug("DOS: new partition wanted"));
1492
1493 l = self_label(cxt);
1494 ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1495
1496 /*
1497 * partition template (@pa) based partitioning
1498 */
1499
1500 /* pa specifies start within extended partition, add logical */
1501 if (pa && fdisk_partition_has_start(pa) && ext_pe
1502 && pa->start >= l->ext_offset
1503 && pa->start <= get_abs_partition_end(ext_pe)) {
1504 DBG(LABEL, ul_debug("DOS: pa template %p: add logical", pa));
1505 rc = add_logical(cxt, pa, &res);
1506 goto done;
1507
1508 /* pa specifies that extended partition is wanted */
1509 } else if (pa && pa->type && pa->type->code == MBR_DOS_EXTENDED_PARTITION) {
1510 DBG(LABEL, ul_debug("DOS: pa template %p: add extened", pa));
1511 if (l->ext_offset) {
1512 fdisk_warnx(cxt, _("Extended partition already exists."));
1513 return -EINVAL;
1514 }
1515 rc = get_partition_unused_primary(cxt, pa, &res);
1516 if (rc == 0) {
1517 rc = add_partition(cxt, res, pa);
1518 goto done;
1519 }
1520
1521 /* pa specifies start, but outside extended partition */
1522 } else if (pa && fdisk_partition_has_start(pa) && l->ext_offset) {
1523 DBG(LABEL, ul_debug("DOS: pa template %p: add primary", pa));
1524 rc = get_partition_unused_primary(cxt, pa, &res);
1525 if (rc == 0) {
1526 rc = add_partition(cxt, res, pa);
1527 goto done;
1528 }
1529 }
1530
1531 /*
1532 * dialog driven partitioning (it does not mean that @pa template is
1533 * completely ignored!)
1534 */
1535
1536 /* check if there is space for primary partition */
1537 grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
1538 last = cxt->first_lba;
1539
1540 for (i = 0; i < 4; i++) {
1541 struct dos_partition *p = self_partition(cxt, i);
1542
1543 if (is_used_partition(p)) {
1544 fdisk_sector_t start = dos_partition_get_start(p);
1545 if (last + grain <= start)
1546 free_sectors = 1;
1547 last = start + dos_partition_get_size(p);
1548 } else
1549 free_primary++;
1550 }
1551 if (last + grain < cxt->total_sectors - 1)
1552 free_sectors = 1;
1553
1554 if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
1555 fdisk_info(cxt, _("The maximum number of partitions has "
1556 "been created."));
1557 return -EINVAL;
1558 }
1559 rc = 1;
1560
1561 if (!free_primary || !free_sectors) {
1562 DBG(LABEL, ul_debug("DOS: primary impossible, add logical"));
1563 if (l->ext_offset) {
1564 if (!pa || fdisk_partition_has_start(pa)) {
1565 if (!free_primary)
1566 fdisk_info(cxt, _("All primary partitions are in use."));
1567 else if (!free_sectors)
1568 fdisk_info(cxt, _("All space for primary partitions is in use."));
1569 }
1570 rc = add_logical(cxt, pa, &res);
1571 } else {
1572 fdisk_info(cxt,
1573 _( "Impossible to create another primary partition. "
1574 "If you want to create more partitions, you must "
1575 "replace a primary partition with an extended "
1576 "partition first."));
1577 return -EINVAL;
1578 }
1579 } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
1580 fdisk_info(cxt, _("All logical partitions are in use. "
1581 "Adding a primary partition."));
1582 rc = get_partition_unused_primary(cxt, pa, &res);
1583 if (rc == 0)
1584 rc = add_partition(cxt, res, pa);
1585 } else {
1586 char hint[BUFSIZ];
1587 struct fdisk_ask *ask;
1588 int c;
1589
1590 /* the default layout for scripts is to create primary partitions */
1591 if (cxt->script) {
1592 rc = get_partition_unused_primary(cxt, pa, &res);
1593 if (rc == 0)
1594 rc = add_partition(cxt, res, pa);
1595 goto done;
1596 }
1597
1598 ask = fdisk_new_ask();
1599 if (!ask)
1600 return -ENOMEM;
1601 fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
1602 fdisk_ask_set_query(ask, _("Partition type"));
1603 fdisk_ask_menu_set_default(ask, free_primary == 1
1604 && !l->ext_offset ? 'e' : 'p');
1605 snprintf(hint, sizeof(hint),
1606 _("%zu primary, %d extended, %zu free"),
1607 4 - (l->ext_offset ? 1 : 0) - free_primary,
1608 l->ext_offset ? 1 : 0,
1609 free_primary);
1610
1611 fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
1612 if (!l->ext_offset)
1613 fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
1614 else
1615 fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
1616
1617 rc = fdisk_do_ask(cxt, ask);
1618 if (rc)
1619 return rc;
1620 fdisk_ask_menu_get_result(ask, &c);
1621 fdisk_unref_ask(ask);
1622
1623 if (c == 'p') {
1624 rc = get_partition_unused_primary(cxt, pa, &res);
1625 if (rc == 0)
1626 rc = add_partition(cxt, res, pa);
1627 goto done;
1628 } else if (c == 'l' && l->ext_offset) {
1629 rc = add_logical(cxt, pa, &res);
1630 goto done;
1631 } else if (c == 'e' && !l->ext_offset) {
1632 rc = get_partition_unused_primary(cxt, pa, &res);
1633 if (rc == 0) {
1634 struct fdisk_partition *xpa = NULL;
1635 struct fdisk_parttype *t;
1636
1637 t = fdisk_label_get_parttype_from_code(cxt->label,
1638 MBR_DOS_EXTENDED_PARTITION);
1639 if (!pa) {
1640 pa = xpa = fdisk_new_partition();
1641 if (!xpa)
1642 return -ENOMEM;
1643 }
1644 fdisk_partition_set_type(pa, t);
1645 rc = add_partition(cxt, res, pa);
1646 if (xpa) {
1647 fdisk_unref_partition(xpa);
1648 pa = NULL;
1649 }
1650 }
1651 goto done;
1652 } else
1653 fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
1654 }
1655done:
1656 if (rc == 0) {
1657 cxt->label->nparts_cur++;
1658 if (partno)
1659 *partno = res;
1660 }
1661 return rc;
1662}
1663
1664static int write_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
1665 unsigned char *buf)
1666{
1667 int rc;
1668
1669 rc = seek_sector(cxt, secno);
1670 if (rc != 0) {
1671 fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
1672 (uintmax_t) secno);
1673 return rc;
1674 }
1675
1676 DBG(LABEL, ul_debug("DOS: writting to sector %ju", (uintmax_t) secno));
1677
1678 if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
1679 return -errno;
1680 return 0;
1681}
1682
1683static int dos_write_disklabel(struct fdisk_context *cxt)
1684{
1685 struct fdisk_dos_label *l = self_label(cxt);
1686 size_t i;
1687 int rc = 0, mbr_changed = 0;
1688
1689 assert(cxt);
1690 assert(cxt->label);
1691 assert(fdisk_is_label(cxt, DOS));
1692
1693 mbr_changed = l->non_pt_changed;
1694
1695 /* MBR (primary partitions) */
1696 if (!mbr_changed) {
1697 for (i = 0; i < 4; i++) {
1698 struct pte *pe = self_pte(cxt, i);
1699 if (pe->changed)
1700 mbr_changed = 1;
1701 }
1702 }
1703 if (mbr_changed) {
1704 mbr_set_magic(cxt->firstsector);
1705 rc = write_sector(cxt, 0, cxt->firstsector);
1706 if (rc)
1707 goto done;
1708 }
1709
1710 if (cxt->label->nparts_max <= 4 && l->ext_offset) {
1711 /* we have empty extended partition, check if the partition has
1712 * been modified and then cleanup possible remaining EBR */
1713 struct pte *pe = self_pte(cxt, l->ext_index);
1714 unsigned char empty[512] = { 0 };
1715 fdisk_sector_t off = pe ? get_abs_partition_start(pe) : 0;
1716
1717 if (off && pe->changed) {
1718 mbr_set_magic(empty);
1719 write_sector(cxt, off, empty);
1720 }
1721 }
1722
1723 /* EBR (logical partitions) */
1724 for (i = 4; i < cxt->label->nparts_max; i++) {
1725 struct pte *pe = self_pte(cxt, i);
1726
1727 if (!pe->changed || !pe->offset || !pe->sectorbuffer)
1728 continue;
1729
1730 mbr_set_magic(pe->sectorbuffer);
1731 rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
1732 if (rc)
1733 goto done;
1734 }
1735
1736done:
1737 return rc;
1738}
1739
1740static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
1741 const char **name, off_t *offset, size_t *size)
1742{
1743 assert(cxt);
1744
1745 *name = NULL;
1746 *offset = 0;
1747 *size = 0;
1748
1749 switch (n) {
1750 case 0:
1751 *name = "MBR";
1752 *offset = 0;
1753 *size = 512;
1754 break;
1755 default:
1756 /* extended partitions */
1757 if (n - 1 + 4 < cxt->label->nparts_max) {
1758 struct pte *pe = self_pte(cxt, n - 1 + 4);
1759
1760 assert(pe->private_sectorbuffer);
1761
1762 *name = "EBR";
1763 *offset = pe->offset * cxt->sector_size;
1764 *size = 512;
1765 } else
1766 return 1;
1767 break;
1768 }
1769
1770 return 0;
1771}
1772
1773/*
1774 * Check whether partition entries are ordered by their starting positions.
1775 * Return 0 if OK. Return i if partition i should have been earlier.
1776 * Two separate checks: primary and logical partitions.
1777 */
1778static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
1779{
1780 size_t last_p_start_pos = 0, p_start_pos;
1781 size_t i, last_i = 0;
1782
1783 for (i = 0 ; i < cxt->label->nparts_max; i++) {
1784
1785 struct pte *pe = self_pte(cxt, i);
1786 struct dos_partition *p = pe->pt_entry;
1787
1788 if (i == 4) {
1789 last_i = 4;
1790 last_p_start_pos = 0;
1791 }
1792 if (is_used_partition(p)) {
1793 p_start_pos = get_abs_partition_start(pe);
1794
1795 if (last_p_start_pos > p_start_pos) {
1796 if (prev)
1797 *prev = last_i;
1798 return i;
1799 }
1800
1801 last_p_start_pos = p_start_pos;
1802 last_i = i;
1803 }
1804 }
1805 return 0;
1806}
1807
1808static int dos_list_disklabel(struct fdisk_context *cxt)
1809{
1810 assert(cxt);
1811 assert(cxt->label);
1812 assert(fdisk_is_label(cxt, DOS));
1813
1814 return 0;
1815}
1816
1817static int dos_get_partition(struct fdisk_context *cxt, size_t n,
1818 struct fdisk_partition *pa)
1819{
1820 struct dos_partition *p;
1821 struct pte *pe;
1822 struct fdisk_dos_label *lb;
1823
1824 assert(cxt);
1825 assert(pa);
1826 assert(cxt->label);
1827 assert(fdisk_is_label(cxt, DOS));
1828
1829 lb = self_label(cxt);
1830 pe = self_pte(cxt, n);
1831 p = pe->pt_entry;
1832 pa->used = !is_cleared_partition(p);
1833 if (!pa->used)
1834 return 0;
1835
1836 pa->type = dos_partition_parttype(cxt, p);
1837 pa->boot = p->boot_ind == ACTIVE_FLAG ? 1 : 0;
1838 pa->start = get_abs_partition_start(pe);
1839 pa->size = dos_partition_get_size(p);
1840 pa->container = lb->ext_offset && n == lb->ext_index;
1841
1842 if (n >= 4)
1843 pa->parent_partno = lb->ext_index;
1844
1845 if (p->boot_ind && asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
1846 return -ENOMEM;
1847
1848 /* start C/H/S */
1849 if (asprintf(&pa->start_chs, "%d/%d/%d",
1850 cylinder(p->bs, p->bc),
1851 sector(p->bs),
1852 p->bh) < 0)
1853 return -ENOMEM;
1854
1855 /* end C/H/S */
1856 if (asprintf(&pa->end_chs, "%d/%d/%d",
1857 cylinder(p->es, p->ec),
1858 sector(p->es),
1859 p->eh) < 0)
1860 return -ENOMEM;
1861
1862 return 0;
1863}
1864
1865static int dos_set_partition(struct fdisk_context *cxt, size_t n,
1866 struct fdisk_partition *pa)
1867{
1868 struct fdisk_dos_label *l;
1869 struct dos_partition *p;
1870 struct pte *pe;
1871 fdisk_sector_t start, size;
1872
1873 assert(cxt);
1874 assert(pa);
1875 assert(cxt->label);
1876 assert(fdisk_is_label(cxt, DOS));
1877
1878 if (n >= cxt->label->nparts_max)
1879 return -EINVAL;
1880
1881 if (pa->type && IS_EXTENDED(pa->type->code)) {
1882 fdisk_warnx(cxt, _("You cannot change a partition into an "
1883 "extended one or vice versa. Delete it first."));
1884 return -EINVAL;
1885 }
1886
1887 if (pa->type && !pa->type->code)
1888 fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
1889 "Having partitions of type 0 is probably unwise."));
1890 l = self_label(cxt);
1891 p = self_partition(cxt, n);
1892 pe = self_pte(cxt, n);
1893
1894 FDISK_INIT_UNDEF(start);
1895 FDISK_INIT_UNDEF(size);
1896
1897 if (fdisk_partition_has_start(pa))
1898 start = pa->start;
1899 if (fdisk_partition_has_size(pa))
1900 size = pa->size;
1901
1902 if (pa->end_follow_default) {
1903 fdisk_sector_t first[cxt->label->nparts_max],
1904 last[cxt->label->nparts_max],
1905 xlast;
1906 struct pte *ext = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1907
1908 fill_bounds(cxt, first, last);
1909
1910 if (ext && l->ext_offset) {
1911 first[l->ext_index] = l->ext_offset;
1912 last[l->ext_index] = get_abs_partition_end(ext);
1913 }
1914 if (FDISK_IS_UNDEF(start))
1915 start = get_abs_partition_start(pe);
1916
1917 DBG(LABEL, ul_debug("DOS: #%zu now %ju +%ju sectors",
1918 n, (uintmax_t) start, (uintmax_t) dos_partition_get_size(p)));
1919
1920 xlast = get_unused_last(cxt, n, start, first, last);
1921 size = xlast ? xlast - start + 1: dos_partition_get_size(p);
1922
1923 DBG(LABEL, ul_debug("DOS: #%zu wanted %ju +%ju sectors",
1924 n, (uintmax_t) start, (uintmax_t) size));
1925 }
1926
1927 if (!FDISK_IS_UNDEF(start) || !FDISK_IS_UNDEF(size)) {
1928 DBG(LABEL, ul_debug("DOS: resize partition"));
1929
1930 if (FDISK_IS_UNDEF(start))
1931 start = get_abs_partition_start(pe);
1932 if (FDISK_IS_UNDEF(size))
1933 size = dos_partition_get_size(p);
1934
1935 set_partition(cxt, n, 0, start, start + size - 1,
1936 pa->type ? pa->type->code : p->sys_ind,
1937 pa->boot == 1);
1938 } else {
1939 DBG(LABEL, ul_debug("DOS: keep size, modify properties"));
1940 if (pa->type)
1941 p->sys_ind = pa->type->code;
1942 if (!FDISK_IS_UNDEF(pa->boot))
1943 p->boot_ind = pa->boot == 1 ? ACTIVE_FLAG : 0;
1944 }
1945
1946 partition_set_changed(cxt, n, 1);
1947 return 0;
1948}
1949
1950static void print_chain_of_logicals(struct fdisk_context *cxt)
1951{
1952 size_t i;
1953 struct fdisk_dos_label *l = self_label(cxt);
1954
1955 fputc('\n', stdout);
1956
1957 for (i = 4; i < cxt->label->nparts_max; i++) {
1958 struct pte *pe = self_pte(cxt, i);
1959
1960 fprintf(stderr, "#%02zu EBR [%10ju], "
1961 "data[start=%10ju (%10ju), size=%10ju], "
1962 "link[start=%10ju (%10ju), size=%10ju]\n",
1963 i, (uintmax_t) pe->offset,
1964 /* data */
1965 (uintmax_t) dos_partition_get_start(pe->pt_entry),
1966 (uintmax_t) get_abs_partition_start(pe),
1967 (uintmax_t) dos_partition_get_size(pe->pt_entry),
1968 /* link */
1969 (uintmax_t) dos_partition_get_start(pe->ex_entry),
1970 (uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
1971 (uintmax_t) dos_partition_get_size(pe->ex_entry));
1972 }
1973}
1974
1975static int cmp_ebr_offsets(const void *a, const void *b)
1976{
1977 struct pte *ae = (struct pte *) a,
1978 *be = (struct pte *) b;
1979
1980 if (ae->offset == 0 && be->offset == 0)
1981 return 0;
1982 if (ae->offset == 0)
1983 return 1;
1984 if (be->offset == 0)
1985 return -1;
1986
1987 return cmp_numbers(ae->offset, be->offset);
1988}
1989
1990/*
1991 * Fix the chain of logicals.
1992 *
1993 * The function does not modify data partitions within EBR tables
1994 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
1995 * (pte->ex_entry) between EBR tables.
1996 *
1997 */
1998static void fix_chain_of_logicals(struct fdisk_context *cxt)
1999{
2000 struct fdisk_dos_label *l = self_label(cxt);
2001 struct pte *last;
2002 size_t i;
2003
2004 DBG(LABEL, print_chain_of_logicals(cxt));
2005
2006 /* Sort chain by EBR offsets */
2007 qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
2008 cmp_ebr_offsets);
2009
2010again:
2011 /* Sort data partitions by start */
2012 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2013 struct pte *cur = self_pte(cxt, i),
2014 *nxt = self_pte(cxt, i + 1);
2015
2016 if (get_abs_partition_start(cur) >
2017 get_abs_partition_start(nxt)) {
2018
2019 struct dos_partition tmp = *cur->pt_entry;
2020 fdisk_sector_t cur_start = get_abs_partition_start(cur),
2021 nxt_start = get_abs_partition_start(nxt);
2022
2023 /* swap data partitions */
2024 *cur->pt_entry = *nxt->pt_entry;
2025 *nxt->pt_entry = tmp;
2026
2027 /* Recount starts according to EBR offsets, the absolute
2028 * address tas to be still the same! */
2029 dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
2030 dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
2031
2032 partition_set_changed(cxt, i, 1);
2033 partition_set_changed(cxt, i + 1, 1);
2034 goto again;
2035 }
2036 }
2037
2038 /* Update EBR links */
2039 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2040 struct pte *cur = self_pte(cxt, i),
2041 *nxt = self_pte(cxt, i + 1);
2042
2043 fdisk_sector_t noff = nxt->offset - l->ext_offset,
2044 ooff = dos_partition_get_start(cur->ex_entry);
2045
2046 if (noff == ooff)
2047 continue;
2048
2049 DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2050 (uintmax_t) cur->offset,
2051 (uintmax_t) ooff, (uintmax_t) noff));
2052
2053 set_partition(cxt, i, 1, nxt->offset,
2054 get_abs_partition_end(nxt),
2055 MBR_DOS_EXTENDED_PARTITION, 0);
2056 }
2057
2058 /* always terminate the chain ! */
2059 last = self_pte(cxt, cxt->label->nparts_max - 1);
2060 if (last) {
2061 clear_partition(last->ex_entry);
2062 partition_set_changed(cxt, cxt->label->nparts_max - 1, 1);
2063 }
2064
2065 DBG(LABEL, print_chain_of_logicals(cxt));
2066}
2067
2068static int dos_reorder(struct fdisk_context *cxt)
2069{
2070 struct pte *pei, *pek;
2071 size_t i,k;
2072
2073 if (!wrong_p_order(cxt, NULL)) {
2074 fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
2075 return 0;
2076 }
2077
2078 while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
2079 /* partition i should have come earlier, move it */
2080 /* We have to move data in the MBR */
2081 struct dos_partition *pi, *pk, *pe, pbuf;
2082 pei = self_pte(cxt, i);
2083 pek = self_pte(cxt, k);
2084
2085 pe = pei->ex_entry;
2086 pei->ex_entry = pek->ex_entry;
2087 pek->ex_entry = pe;
2088
2089 pi = pei->pt_entry;
2090 pk = pek->pt_entry;
2091
2092 memmove(&pbuf, pi, sizeof(struct dos_partition));
2093 memmove(pi, pk, sizeof(struct dos_partition));
2094 memmove(pk, &pbuf, sizeof(struct dos_partition));
2095
2096 partition_set_changed(cxt, i, 1);
2097 partition_set_changed(cxt, k, 1);
2098 }
2099
2100 if (i)
2101 fix_chain_of_logicals(cxt);
2102
2103 fdisk_info(cxt, _("Done."));
2104 return 0;
2105}
2106
2107/* TODO: use fdisk_set_partition() API */
2108int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
2109{
2110 struct pte *pe;
2111 struct dos_partition *p;
2112 unsigned int new, free_start, curr_start, last;
2113 uintmax_t res = 0;
2114 size_t x;
2115 int rc;
2116
2117 assert(cxt);
2118 assert(fdisk_is_label(cxt, DOS));
2119
2120 pe = self_pte(cxt, i);
2121 p = pe->pt_entry;
2122
2123 if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
2124 fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
2125 return 0;
2126 }
2127
2128 /* the default start is at the second sector of the disk or at the
2129 * second sector of the extended partition
2130 */
2131 free_start = pe->offset ? pe->offset + 1 : 1;
2132
2133 curr_start = get_abs_partition_start(pe);
2134
2135 /* look for a free space before the current start of the partition */
2136 for (x = 0; x < cxt->label->nparts_max; x++) {
2137 unsigned int end;
2138 struct pte *prev_pe = self_pte(cxt, x);
2139 struct dos_partition *prev_p = prev_pe->pt_entry;
2140
2141 if (!prev_p)
2142 continue;
2143 end = get_abs_partition_start(prev_pe)
2144 + dos_partition_get_size(prev_p);
2145
2146 if (is_used_partition(prev_p) &&
2147 end > free_start && end <= curr_start)
2148 free_start = end;
2149 }
2150
2151 last = get_abs_partition_end(pe);
2152
2153 rc = fdisk_ask_number(cxt, free_start, curr_start, last,
2154 _("New beginning of data"), &res);
2155 if (rc)
2156 return rc;
2157
2158 new = res - pe->offset;
2159
2160 if (new != dos_partition_get_size(p)) {
2161 unsigned int sects = dos_partition_get_size(p)
2162 + dos_partition_get_start(p) - new;
2163
2164 dos_partition_set_size(p, sects);
2165 dos_partition_set_start(p, new);
2166
2167 partition_set_changed(cxt, i, 1);
2168 }
2169
2170 return rc;
2171}
2172
2173static int dos_partition_is_used(
2174 struct fdisk_context *cxt,
2175 size_t i)
2176{
2177 struct dos_partition *p;
2178
2179 assert(cxt);
2180 assert(cxt->label);
2181 assert(fdisk_is_label(cxt, DOS));
2182
2183 if (i >= cxt->label->nparts_max)
2184 return 0;
2185
2186 p = self_partition(cxt, i);
2187
2188 return p && !is_cleared_partition(p);
2189}
2190
2191static int dos_toggle_partition_flag(
2192 struct fdisk_context *cxt,
2193 size_t i,
2194 unsigned long flag)
2195{
2196 struct dos_partition *p;
2197
2198 assert(cxt);
2199 assert(cxt->label);
2200 assert(fdisk_is_label(cxt, DOS));
2201
2202 if (i >= cxt->label->nparts_max)
2203 return -EINVAL;
2204
2205 p = self_partition(cxt, i);
2206
2207 switch (flag) {
2208 case DOS_FLAG_ACTIVE:
2209 if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
2210 fdisk_warnx(cxt, _("Partition %zu: is an extended "
2211 "partition."), i + 1);
2212
2213 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
2214 partition_set_changed(cxt, i, 1);
2215 fdisk_info(cxt, p->boot_ind ?
2216 _("The bootable flag on partition %zu is enabled now.") :
2217 _("The bootable flag on partition %zu is disabled now."),
2218 i + 1);
2219 break;
2220 default:
2221 return 1;
2222 }
2223
2224 return 0;
2225}
2226
2227static const struct fdisk_field dos_fields[] =
2228{
2229 /* basic */
2230 { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
2231 { FDISK_FIELD_BOOT, N_("Boot"), 1, 0 },
2232 { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
2233 { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
2234 { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
2235 { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
2236 { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
2237 { FDISK_FIELD_TYPEID, N_("Id"), 2, FDISK_FIELDFL_NUMBER },
2238 { FDISK_FIELD_TYPE, N_("Type"), 0.1, 0 },
2239
2240 /* expert mode */
2241 { FDISK_FIELD_SADDR, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2242 { FDISK_FIELD_EADDR, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2243 { FDISK_FIELD_ATTR, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL }
2244
2245};
2246
2247static const struct fdisk_label_operations dos_operations =
2248{
2249 .probe = dos_probe_label,
2250 .write = dos_write_disklabel,
2251 .verify = dos_verify_disklabel,
2252 .create = dos_create_disklabel,
2253 .locate = dos_locate_disklabel,
2254 .list = dos_list_disklabel,
2255 .reorder = dos_reorder,
2256 .get_id = dos_get_disklabel_id,
2257 .set_id = dos_set_disklabel_id,
2258
2259 .get_part = dos_get_partition,
2260 .set_part = dos_set_partition,
2261 .add_part = dos_add_partition,
2262 .del_part = dos_delete_partition,
2263
2264 .part_toggle_flag = dos_toggle_partition_flag,
2265 .part_is_used = dos_partition_is_used,
2266
2267 .reset_alignment = dos_reset_alignment,
2268
2269 .deinit = dos_deinit,
2270};
2271
2272/*
2273 * allocates DOS in-memory stuff
2274 */
2275struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt)
2276{
2277 struct fdisk_label *lb;
2278 struct fdisk_dos_label *dos;
2279
2280 assert(cxt);
2281
2282 dos = calloc(1, sizeof(*dos));
2283 if (!dos)
2284 return NULL;
2285
2286 /* initialize generic part of the driver */
2287 lb = (struct fdisk_label *) dos;
2288 lb->name = "dos";
2289 lb->id = FDISK_DISKLABEL_DOS;
2290 lb->op = &dos_operations;
2291 lb->parttypes = dos_parttypes;
2292 lb->nparttypes = ARRAY_SIZE(dos_parttypes) - 1;
2293 lb->fields = dos_fields;
2294 lb->nfields = ARRAY_SIZE(dos_fields);
2295
2296 return lb;
2297}
2298
2299/**
2300 * fdisk_dos_enable_compatible:
2301 * @lb: DOS label (see fdisk_get_label())
2302 * @enable: 0 or 1
2303 *
2304 * Enables deprecated DOS compatible mode, in this mode library checks for
2305 * cylinders boundary, cases about CHS addressing and another obscure things.
2306 *
2307 * Returns: 0 on success, <0 on error.
2308 */
2309int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
2310{
2311 struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
2312
2313 if (!lb)
2314 return -EINVAL;
2315
2316 dos->compatible = enable;
2317 if (enable)
2318 lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
2319 return 0;
2320}
2321
2322/**
2323 * fdisk_dos_is_compatible:
2324 * @lb: DOS label
2325 *
2326 * Returns: 0 if DOS compatibility disabled, 1 if enabled
2327 */
2328int fdisk_dos_is_compatible(struct fdisk_label *lb)
2329{
2330 return ((struct fdisk_dos_label *) lb)->compatible;
2331}