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