]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libfdisk/src/dos.c
fdisk: fix printf stuff
[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 "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 #define IS_EXTENDED(i) \
23 ((i) == MBR_DOS_EXTENDED_PARTITION \
24 || (i) == MBR_W95_EXTENDED_PARTITION \
25 || (i) == MBR_LINUX_EXTENDED_PARTITION)
26
27 /*
28 * per partition table entry data
29 *
30 * The four primary partitions have the same sectorbuffer
31 * and have NULL ex_entry.
32 *
33 * Each logical partition table entry has two pointers, one for the
34 * partition and one link to the next one.
35 */
36 struct pte {
37 struct dos_partition *pt_entry; /* on-disk MBR entry */
38 struct dos_partition *ex_entry; /* on-disk EBR entry */
39 sector_t offset; /* disk sector number */
40 unsigned char *sectorbuffer; /* disk sector contents */
41
42 unsigned int changed : 1,
43 private_sectorbuffer : 1;
44 };
45
46 /*
47 * in-memory fdisk GPT stuff
48 */
49 struct fdisk_dos_label {
50 struct fdisk_label head; /* generic part */
51
52 struct pte ptes[MAXIMUM_PARTS]; /* partition */
53 sector_t ext_offset;
54 size_t ext_index;
55 unsigned int compatible : 1, /* is DOS compatible? */
56 non_pt_changed : 1; /* MBR, but no PT changed */
57 };
58
59 /*
60 * Partition types
61 */
62 static struct fdisk_parttype dos_parttypes[] = {
63 #include "pt-mbr-partnames.h"
64 };
65
66 #define set_hsc(h,s,c,sector) { \
67 s = sector % cxt->geom.sectors + 1; \
68 sector /= cxt->geom.sectors; \
69 h = sector % cxt->geom.heads; \
70 sector /= cxt->geom.heads; \
71 c = sector & 0xff; \
72 s |= (sector >> 2) & 0xc0; \
73 }
74
75
76 #define sector(s) ((s) & 0x3f)
77 #define cylinder(s, c) ((c) | (((s) & 0xc0) << 2))
78
79 #define alignment_required(_x) ((_x)->grain != (_x)->sector_size)
80
81 #define is_dos_compatible(_x) \
82 (fdisk_is_disklabel(_x, DOS) && \
83 fdisk_dos_is_compatible(fdisk_context_get_label(_x, NULL)))
84
85 #define cround(c, n) fdisk_cround(c, n)
86
87
88 static inline struct fdisk_dos_label *self_label(struct fdisk_context *cxt)
89 {
90 assert(cxt);
91 assert(cxt->label);
92 assert(fdisk_is_disklabel(cxt, DOS));
93
94 return (struct fdisk_dos_label *) cxt->label;
95 }
96
97 static inline struct pte *self_pte(struct fdisk_context *cxt, size_t i)
98 {
99 struct fdisk_dos_label *l = self_label(cxt);
100
101 if (i >= ARRAY_SIZE(l->ptes))
102 return NULL;
103
104 return &l->ptes[i];
105 }
106
107 static inline struct dos_partition *self_partition(
108 struct fdisk_context *cxt,
109 size_t i)
110 {
111 struct pte *pe = self_pte(cxt, i);
112 return pe ? pe->pt_entry : NULL;
113 }
114
115 struct dos_partition *fdisk_dos_get_partition(
116 struct fdisk_context *cxt,
117 size_t i)
118 {
119 assert(cxt);
120 assert(cxt->label);
121 assert(fdisk_is_disklabel(cxt, DOS));
122
123 return self_partition(cxt, i);
124 }
125
126 static void partition_set_changed(
127 struct fdisk_context *cxt,
128 size_t i,
129 int changed)
130 {
131 struct pte *pe = self_pte(cxt, i);
132
133 if (!pe)
134 return;
135
136 DBG(LABEL, dbgprint("DOS: setting %zu partition changed", i));
137
138 pe->changed = changed ? 1 : 0;
139 if (changed)
140 fdisk_label_set_changed(cxt->label, 1);
141 }
142
143 static sector_t get_abs_partition_start(struct pte *pe)
144 {
145 assert(pe);
146 assert(pe->pt_entry);
147
148 return pe->offset + dos_partition_get_start(pe->pt_entry);
149 }
150
151 static int is_cleared_partition(struct dos_partition *p)
152 {
153 return !(!p || p->boot_ind || p->bh || p->bs || p->bc ||
154 p->sys_ind || p->eh || p->es || p->ec ||
155 dos_partition_get_start(p) || dos_partition_get_size(p));
156 }
157
158 static int get_partition_unused_primary(struct fdisk_context *cxt)
159 {
160 size_t orgmax = cxt->label->nparts_max;
161 size_t n;
162 int rc;
163
164 cxt->label->nparts_max = 4;
165 rc = fdisk_ask_partnum(cxt, &n, TRUE);
166 cxt->label->nparts_max = orgmax;
167
168 switch (rc) {
169 case 1:
170 fdisk_info(cxt, _("All primary partitions have been defined already."));
171 return -1;
172 case 0:
173 return n;
174 default:
175 return rc;
176 }
177 }
178
179 static int seek_sector(struct fdisk_context *cxt, sector_t secno)
180 {
181 off_t offset = (off_t) secno * cxt->sector_size;
182
183 return lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0;
184 }
185
186 static int read_sector(struct fdisk_context *cxt, sector_t secno,
187 unsigned char *buf)
188 {
189 int rc = seek_sector(cxt, secno);
190
191 if (rc < 0)
192 return rc;
193
194 return read(cxt->dev_fd, buf, cxt->sector_size) !=
195 (ssize_t) cxt->sector_size ? -errno : 0;
196 }
197
198 /* Allocate a buffer and read a partition table sector */
199 static int read_pte(struct fdisk_context *cxt, int pno, sector_t offset)
200 {
201 unsigned char *buf;
202 struct pte *pe = self_pte(cxt, pno);
203
204 buf = calloc(1, cxt->sector_size);
205 if (!buf)
206 return -ENOMEM;
207
208 DBG(LABEL, dbgprint("DOS: reading pte %d sector buffer %p", pno, buf));
209
210 pe->offset = offset;
211 pe->sectorbuffer = buf;
212 pe->private_sectorbuffer = 1;
213
214 if (read_sector(cxt, offset, pe->sectorbuffer) != 0)
215 fdisk_warn(cxt, _("Failed to read extended partition table "
216 "(offset=%jd)"), (uintmax_t) offset);
217 pe->changed = 0;
218 pe->pt_entry = pe->ex_entry = NULL;
219 return 0;
220 }
221
222
223 static void clear_partition(struct dos_partition *p)
224 {
225 if (!p)
226 return;
227 p->boot_ind = 0;
228 p->bh = 0;
229 p->bs = 0;
230 p->bc = 0;
231 p->sys_ind = 0;
232 p->eh = 0;
233 p->es = 0;
234 p->ec = 0;
235 dos_partition_set_start(p,0);
236 dos_partition_set_size(p,0);
237 }
238
239 static void dos_init(struct fdisk_context *cxt)
240 {
241 struct fdisk_dos_label *l = self_label(cxt);
242 size_t i;
243
244 assert(cxt);
245 assert(cxt->label);
246 assert(fdisk_is_disklabel(cxt, DOS));
247
248 DBG(LABEL, dbgprint("DOS: initialize, first sector buffer %p", cxt->firstsector));
249
250 cxt->label->nparts_max = 4; /* default, unlimited number of logical */
251
252 l->ext_index = 0;
253 l->ext_offset = 0;
254 l->non_pt_changed = 0;
255
256 memset(l->ptes, 0, sizeof(l->ptes));
257
258 for (i = 0; i < 4; i++) {
259 struct pte *pe = self_pte(cxt, i);
260
261 pe->pt_entry = mbr_get_partition(cxt->firstsector, i);
262 pe->ex_entry = NULL;
263 pe->offset = 0;
264 pe->sectorbuffer = cxt->firstsector;
265 pe->private_sectorbuffer = 0;
266 pe->changed = 0;
267 }
268
269 if (fdisk_context_listonly(cxt))
270 return;
271 /*
272 * Various warnings...
273 */
274 if (fdisk_missing_geometry(cxt))
275 fdisk_warnx(cxt, _("You can set geometry from the extra functions menu."));
276
277 if (is_dos_compatible(cxt)) {
278 fdisk_warnx(cxt, _("DOS-compatible mode is deprecated."));
279
280 if (cxt->sector_size != cxt->phy_sector_size)
281 fdisk_info(cxt, _(
282 "The device presents a logical sector size that is smaller than "
283 "the physical sector size. Aligning to a physical sector (or optimal "
284 "I/O) size boundary is recommended, or performance may be impacted."));
285 }
286
287 if (fdisk_context_use_cylinders(cxt))
288 fdisk_warnx(cxt, _("Cylinders as display units are deprecated."));
289
290 if (cxt->total_sectors > UINT_MAX) {
291 uint64_t bytes = cxt->total_sectors * cxt->sector_size;
292 char *szstr = size_to_human_string(SIZE_SUFFIX_SPACE
293 | SIZE_SUFFIX_3LETTER, bytes);
294 fdisk_warnx(cxt,
295 _("The size of this disk is %s (%llu bytes). DOS "
296 "partition table format can not be used on drives for "
297 "volumes larger than (%llu bytes) for %ld-byte "
298 "sectors. Use GUID partition table format (GPT)."),
299 szstr, (unsigned long long) bytes,
300 (unsigned long long) UINT_MAX * cxt->sector_size,
301 cxt->sector_size);
302 free(szstr);
303 }
304 }
305
306 /* callback called by libfdisk */
307 static void dos_deinit(struct fdisk_label *lb)
308 {
309 size_t i;
310 struct fdisk_dos_label *l = (struct fdisk_dos_label *) lb;
311
312 for (i = 0; i < ARRAY_SIZE(l->ptes); i++) {
313 struct pte *pe = &l->ptes[i];
314
315 if (pe->private_sectorbuffer && pe->sectorbuffer) {
316 DBG(LABEL, dbgprint("DOS: freeing pte %zu sector buffer %p",
317 i, pe->sectorbuffer));
318 free(pe->sectorbuffer);
319 }
320 pe->sectorbuffer = NULL;
321 pe->private_sectorbuffer = 0;
322 }
323
324 memset(l->ptes, 0, sizeof(l->ptes));
325 }
326
327 static int dos_delete_partition(struct fdisk_context *cxt, size_t partnum)
328 {
329 struct fdisk_dos_label *l;
330 struct pte *pe;
331 struct dos_partition *p;
332 struct dos_partition *q;
333
334 assert(cxt);
335 assert(cxt->label);
336 assert(fdisk_is_disklabel(cxt, DOS));
337
338 pe = self_pte(cxt, partnum);
339 if (!pe)
340 return -EINVAL;
341
342 DBG(LABEL, dbgprint("DOS: delete partiton %zu", partnum));
343
344 l = self_label(cxt);
345 p = pe->pt_entry;
346 q = pe->ex_entry;
347
348 /* Note that for the fifth partition (partnum == 4) we don't actually
349 decrement partitions. */
350 if (partnum < 4) {
351 DBG(LABEL, dbgprint("--> delete primary"));
352 if (IS_EXTENDED(p->sys_ind) && partnum == l->ext_index) {
353 cxt->label->nparts_max = 4;
354 l->ptes[l->ext_index].ex_entry = NULL;
355 l->ext_offset = 0;
356 }
357 partition_set_changed(cxt, partnum, 1);
358 clear_partition(p);
359 } else if (!q->sys_ind && partnum > 4) {
360 DBG(LABEL, dbgprint("--> delete logical [last in the chain]"));
361 --cxt->label->nparts_max;
362 --partnum;
363 clear_partition(l->ptes[partnum].ex_entry);
364 partition_set_changed(cxt, partnum, 1);
365 } else {
366 DBG(LABEL, dbgprint("--> delete logical [non-last, move down]"));
367 if (partnum > 4) {
368 /* delete this link in the chain */
369 p = l->ptes[partnum - 1].ex_entry;
370 *p = *q;
371 dos_partition_set_start(p, dos_partition_get_start(q));
372 dos_partition_set_size(p, dos_partition_get_size(q));
373 partition_set_changed(cxt, partnum - 1, 1);
374 } else if (cxt->label->nparts_max > 5) { /* 5 will be moved to 4 */
375 /* the first logical in a longer chain */
376 pe = &l->ptes[5];
377
378 if (pe->pt_entry) /* prevent SEGFAULT */
379 dos_partition_set_start(pe->pt_entry,
380 get_abs_partition_start(pe) -
381 l->ext_offset);
382 pe->offset = l->ext_offset;
383 partition_set_changed(cxt, 5, 1);
384 }
385
386 if (cxt->label->nparts_max > 5) {
387 cxt->label->nparts_max--;
388 if (l->ptes[partnum].private_sectorbuffer) {
389 DBG(LABEL, dbgprint("--> freeing pte %zu sector buffer %p",
390 partnum, l->ptes[partnum].sectorbuffer));
391 free(l->ptes[partnum].sectorbuffer);
392 }
393 while (partnum < cxt->label->nparts_max) {
394 DBG(LABEL, dbgprint("--> moving pte %zu <-- %zd", partnum, partnum + 1));
395 l->ptes[partnum] = l->ptes[partnum + 1];
396 partnum++;
397 }
398 memset(&l->ptes[partnum], 0, sizeof(struct pte));
399 } else
400 /* the only logical: clear only */
401 clear_partition(l->ptes[partnum].pt_entry);
402 }
403
404 fdisk_label_set_changed(cxt->label, 1);
405 return 0;
406 }
407
408 static void read_extended(struct fdisk_context *cxt, int ext)
409 {
410 size_t i;
411 struct pte *pex;
412 struct dos_partition *p, *q;
413 struct fdisk_dos_label *l = self_label(cxt);
414
415 l->ext_index = ext;
416 pex = self_pte(cxt, ext);
417 pex->ex_entry = pex->pt_entry;
418
419 p = pex->pt_entry;
420 if (!dos_partition_get_start(p)) {
421 fdisk_warnx(cxt, _("Bad offset in primary extended partition."));
422 return;
423 }
424
425 DBG(LABEL, dbgprint("DOS: REading extended %d", ext));
426
427 while (IS_EXTENDED (p->sys_ind)) {
428 struct pte *pe = self_pte(cxt, cxt->label->nparts_max);
429
430 if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
431 /* This is not a Linux restriction, but
432 this program uses arrays of size MAXIMUM_PARTS.
433 Do not try to `improve' this test. */
434 struct pte *pre = self_pte(cxt,
435 cxt->label->nparts_max - 1);
436 fdisk_warnx(cxt,
437 _("Omitting partitions after #%zd. They will be deleted "
438 "if you save this partition table."),
439 cxt->label->nparts_max);
440
441 clear_partition(pre->ex_entry);
442 partition_set_changed(cxt,
443 cxt->label->nparts_max - 1, 1);
444 return;
445 }
446
447 read_pte(cxt, cxt->label->nparts_max,
448 l->ext_offset + dos_partition_get_start(p));
449
450 if (!l->ext_offset)
451 l->ext_offset = dos_partition_get_start(p);
452
453 q = p = mbr_get_partition(pe->sectorbuffer, 0);
454
455 for (i = 0; i < 4; i++, p++) if (dos_partition_get_size(p)) {
456 if (IS_EXTENDED (p->sys_ind)) {
457 if (pe->ex_entry)
458 fdisk_warnx(cxt, _(
459 "Extra link pointer in partition "
460 "table %zd."),
461 cxt->label->nparts_max + 1);
462 else
463 pe->ex_entry = p;
464 } else if (p->sys_ind) {
465 if (pe->pt_entry)
466 fdisk_warnx(cxt, _(
467 "Ignoring extra data in partition "
468 "table %zd."),
469 cxt->label->nparts_max + 1);
470 else
471 pe->pt_entry = p;
472 }
473 }
474
475 /* very strange code here... */
476 if (!pe->pt_entry) {
477 if (q != pe->ex_entry)
478 pe->pt_entry = q;
479 else
480 pe->pt_entry = q + 1;
481 }
482 if (!pe->ex_entry) {
483 if (q != pe->pt_entry)
484 pe->ex_entry = q;
485 else
486 pe->ex_entry = q + 1;
487 }
488
489 p = pe->ex_entry;
490 cxt->label->nparts_cur = ++cxt->label->nparts_max;
491 }
492
493 /* remove empty links */
494 remove:
495 q = self_partition(cxt, 4);
496 for (i = 4; i < cxt->label->nparts_max; i++) {
497 p = self_partition(cxt, i);
498
499 if (!dos_partition_get_size(p) &&
500 (cxt->label->nparts_max > 5 || q->sys_ind)) {
501 fdisk_info(cxt, _("omitting empty partition (%zd)"), i+1);
502 dos_delete_partition(cxt, i);
503 goto remove; /* numbering changed */
504 }
505 }
506 }
507
508 static int dos_get_disklabel_id(struct fdisk_context *cxt, char **id)
509 {
510 unsigned int num;
511
512 assert(cxt);
513 assert(id);
514 assert(cxt->label);
515 assert(fdisk_is_disklabel(cxt, DOS));
516
517 num = mbr_get_id(cxt->firstsector);
518 if (asprintf(id, "0x%08x", num) > 0)
519 return 0;
520
521 return -ENOMEM;
522 }
523
524 static int dos_create_disklabel(struct fdisk_context *cxt)
525 {
526 unsigned int id;
527
528 assert(cxt);
529 assert(cxt->label);
530 assert(fdisk_is_disklabel(cxt, DOS));
531
532 DBG(LABEL, dbgprint("DOS: creating new disklabel"));
533
534 /* random disk signature */
535 random_get_bytes(&id, sizeof(id));
536
537 dos_init(cxt);
538 fdisk_zeroize_firstsector(cxt);
539 fdisk_label_set_changed(cxt->label, 1);
540
541 /* Generate an MBR ID for this disk */
542 mbr_set_id(cxt->firstsector, id);
543
544 /* Put MBR signature */
545 mbr_set_magic(cxt->firstsector);
546
547 fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
548 ("Created a new DOS disklabel with disk "
549 "identifier 0x%08x."), id);
550 return 0;
551 }
552
553 static int dos_set_disklabel_id(struct fdisk_context *cxt)
554 {
555 char *end = NULL, *str = NULL;
556 unsigned int id, old;
557 struct fdisk_dos_label *l;
558 int rc;
559
560 assert(cxt);
561 assert(cxt->label);
562 assert(fdisk_is_disklabel(cxt, DOS));
563
564 DBG(LABEL, dbgprint("DOS: setting Id"));
565
566 l = self_label(cxt);
567 old = mbr_get_id(cxt->firstsector);
568 rc = fdisk_ask_string(cxt,
569 _("Enter the new disk identifier"), &str);
570 if (rc)
571 return rc;
572
573 errno = 0;
574 id = strtoul(str, &end, 0);
575 if (errno || str == end || (end && *end)) {
576 fdisk_warnx(cxt, _("Incorrect value."));
577 return -EINVAL;
578 }
579
580
581 mbr_set_id(cxt->firstsector, id);
582 l->non_pt_changed = 1;
583 fdisk_label_set_changed(cxt->label, 1);
584
585 fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
586 _("Disk identifier changed from 0x%08x to 0x%08x."),
587 old, id);
588 return 0;
589 }
590
591 static void get_partition_table_geometry(struct fdisk_context *cxt,
592 unsigned int *ph, unsigned int *ps)
593 {
594 unsigned char *bufp = cxt->firstsector;
595 struct dos_partition *p;
596 int i, h, s, hh, ss;
597 int first = 1;
598 int bad = 0;
599
600 hh = ss = 0;
601 for (i = 0; i < 4; i++) {
602 p = mbr_get_partition(bufp, i);
603 if (p->sys_ind != 0) {
604 h = p->eh + 1;
605 s = (p->es & 077);
606 if (first) {
607 hh = h;
608 ss = s;
609 first = 0;
610 } else if (hh != h || ss != s)
611 bad = 1;
612 }
613 }
614
615 if (!first && !bad) {
616 *ph = hh;
617 *ps = ss;
618 }
619
620 DBG(LABEL, dbgprint("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps));
621 }
622
623 static int dos_reset_alignment(struct fdisk_context *cxt)
624 {
625 assert(cxt);
626 assert(cxt->label);
627 assert(fdisk_is_disklabel(cxt, DOS));
628
629 /* overwrite necessary stuff by DOS deprecated stuff */
630 if (is_dos_compatible(cxt)) {
631 DBG(LABEL, dbgprint("DOS: reseting alignemnt for DOS-comaptiblem PT"));
632 if (cxt->geom.sectors)
633 cxt->first_lba = cxt->geom.sectors; /* usually 63 */
634
635 cxt->grain = cxt->sector_size; /* usually 512 */
636 }
637
638 return 0;
639 }
640
641 /* TODO: move to include/pt-dos.h and share with libblkid */
642 #define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
643 #define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
644
645 static int dos_probe_label(struct fdisk_context *cxt)
646 {
647 size_t i;
648 unsigned int h = 0, s = 0;
649
650 assert(cxt);
651 assert(cxt->label);
652 assert(fdisk_is_disklabel(cxt, DOS));
653
654 /* ignore disks with AIX magic number */
655 if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0)
656 return 0;
657
658 if (!mbr_is_valid_magic(cxt->firstsector))
659 return 0;
660
661 dos_init(cxt);
662
663 get_partition_table_geometry(cxt, &h, &s);
664 if (h && s) {
665 cxt->geom.heads = h;
666 cxt->geom.sectors = s;
667 }
668
669 for (i = 0; i < 4; i++) {
670 struct pte *pe = self_pte(cxt, i);
671
672 if (!is_cleared_partition(pe->pt_entry))
673 cxt->label->nparts_cur++;
674
675 if (IS_EXTENDED (pe->pt_entry->sys_ind)) {
676 if (cxt->label->nparts_max != 4)
677 fdisk_warnx(cxt, _(
678 "Ignoring extra extended partition %zd"),
679 i + 1);
680 else
681 read_extended(cxt, i);
682 }
683 }
684
685 for (i = 3; i < cxt->label->nparts_max; i++) {
686 struct pte *pe = self_pte(cxt, i);
687
688 if (!mbr_is_valid_magic(pe->sectorbuffer)) {
689 fdisk_info(cxt, _(
690 "Invalid flag 0x%02x%02x of partition table %zd will "
691 "be corrected by w(rite)"),
692 pe->sectorbuffer[510],
693 pe->sectorbuffer[511],
694 i + 1);
695 partition_set_changed(cxt, 1, 1);
696 }
697 }
698
699 return 1;
700 }
701
702 /*
703 * Avoid warning about DOS partitions when no DOS partition was changed.
704 * Here a heuristic "is probably dos partition".
705 * We might also do the opposite and warn in all cases except
706 * for "is probably nondos partition".
707 */
708 static int is_dos_partition(int t)
709 {
710 return (t == 1 || t == 4 || t == 6 ||
711 t == 0x0b || t == 0x0c || t == 0x0e ||
712 t == 0x11 || t == 0x12 || t == 0x14 || t == 0x16 ||
713 t == 0x1b || t == 0x1c || t == 0x1e || t == 0x24 ||
714 t == 0xc1 || t == 0xc4 || t == 0xc6);
715 }
716
717 static void set_partition(struct fdisk_context *cxt,
718 int i, int doext, sector_t start,
719 sector_t stop, int sysid)
720 {
721 struct pte *pe = self_pte(cxt, i);
722 struct dos_partition *p;
723 sector_t offset;
724
725 DBG(LABEL, dbgprint("DOS: setting partition %d%s, start=%zu, stop=%zu, sysid=%02x",
726 i, doext ? " [extended]" : "",
727 (size_t) start, (size_t) stop, sysid));
728
729 if (doext) {
730 struct fdisk_dos_label *l = self_label(cxt);
731 p = pe->ex_entry;
732 offset = l->ext_offset;
733 } else {
734 p = pe->pt_entry;
735 offset = pe->offset;
736 }
737 p->boot_ind = 0;
738 p->sys_ind = sysid;
739 dos_partition_set_start(p, start - offset);
740 dos_partition_set_size(p, stop - start + 1);
741
742 if (!doext) {
743 struct fdisk_parttype *t = fdisk_get_parttype_from_code(cxt, sysid);
744 fdisk_info_new_partition(cxt, i + 1, start, stop, t);
745 }
746 if (is_dos_compatible(cxt) && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
747 start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
748 set_hsc(p->bh, p->bs, p->bc, start);
749 if (is_dos_compatible(cxt) && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
750 stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
751 set_hsc(p->eh, p->es, p->ec, stop);
752 partition_set_changed(cxt, i, 1);
753 }
754
755 static sector_t get_unused_start(struct fdisk_context *cxt,
756 int part_n, sector_t start,
757 sector_t first[], sector_t last[])
758 {
759 size_t i;
760
761 for (i = 0; i < cxt->label->nparts_max; i++) {
762 sector_t lastplusoff;
763 struct pte *pe = self_pte(cxt, i);
764
765 if (start == pe->offset)
766 start += cxt->first_lba;
767 lastplusoff = last[i] + ((part_n < 4) ? 0 : cxt->first_lba);
768 if (start >= first[i] && start <= lastplusoff)
769 start = lastplusoff + 1;
770 }
771
772 return start;
773 }
774
775 static void fill_bounds(struct fdisk_context *cxt,
776 sector_t *first, sector_t *last)
777 {
778 size_t i;
779 struct pte *pe = self_pte(cxt, 0);
780 struct dos_partition *p;
781
782 for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
783 p = pe->pt_entry;
784 if (!p->sys_ind || IS_EXTENDED (p->sys_ind)) {
785 first[i] = 0xffffffff;
786 last[i] = 0;
787 } else {
788 first[i] = get_abs_partition_start(pe);
789 last[i] = first[i] + dos_partition_get_size(p) - 1;
790 }
791 }
792 }
793
794 static int add_partition(struct fdisk_context *cxt, int n, struct fdisk_parttype *t)
795 {
796 int sys, read = 0, rc;
797 size_t i;
798 struct fdisk_dos_label *l = self_label(cxt);
799 struct dos_partition *p = self_partition(cxt, n);
800 struct dos_partition *q = self_partition(cxt, l->ext_index);
801
802 sector_t start, stop = 0, limit, temp,
803 first[cxt->label->nparts_max],
804 last[cxt->label->nparts_max];
805
806 DBG(LABEL, dbgprint("DOS: adding partition %d", n));
807
808 sys = t ? t->type : MBR_LINUX_DATA_PARTITION;
809
810 if (p && p->sys_ind) {
811 fdisk_warnx(cxt, _("Partition %zd is already defined. "
812 "Delete it before re-adding it."),
813 (ssize_t) n + 1);
814 return -EINVAL;
815 }
816 fill_bounds(cxt, first, last);
817 if (n < 4) {
818 start = cxt->first_lba;
819 if (fdisk_context_use_cylinders(cxt) || !cxt->total_sectors)
820 limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
821 else
822 limit = cxt->total_sectors - 1;
823
824 if (limit > UINT_MAX)
825 limit = UINT_MAX;
826
827 if (l->ext_offset) {
828 first[l->ext_index] = l->ext_offset;
829 last[l->ext_index] = dos_partition_get_start(q) +
830 dos_partition_get_size(q) - 1;
831 }
832 } else {
833 start = l->ext_offset + cxt->first_lba;
834 limit = dos_partition_get_start(q)
835 + dos_partition_get_size(q) - 1;
836 }
837 if (fdisk_context_use_cylinders(cxt))
838 for (i = 0; i < cxt->label->nparts_max; i++) {
839 first[i] = (cround(cxt, first[i]) - 1)
840 * fdisk_context_get_units_per_sector(cxt);
841 }
842
843 /*
844 * Ask for first sector
845 */
846 do {
847 sector_t dflt, aligned;
848
849 temp = start;
850 dflt = start = get_unused_start(cxt, n, start, first, last);
851
852 /* the default sector should be aligned and unused */
853 do {
854 aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
855 dflt = get_unused_start(cxt, n, aligned, first, last);
856 } while (dflt != aligned && dflt > aligned && dflt < limit);
857
858 if (dflt >= limit)
859 dflt = start;
860 if (start > limit)
861 break;
862 if (start >= temp + fdisk_context_get_units_per_sector(cxt)
863 && read) {
864 fdisk_info(cxt, _("Sector %llu is already allocated."),
865 temp);
866 temp = start;
867 read = 0;
868 }
869
870 if (!read && start == temp) {
871 sector_t j = start;
872 struct fdisk_ask *ask = fdisk_new_ask();
873
874 if (fdisk_context_use_cylinders(cxt))
875 fdisk_ask_set_query(ask, _("First cylinder"));
876 else
877 fdisk_ask_set_query(ask, _("First sector"));
878
879 fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
880 fdisk_ask_number_set_low(ask, cround(cxt, j));
881 fdisk_ask_number_set_default(ask, cround(cxt, dflt));
882 fdisk_ask_number_set_high(ask, cround(cxt, limit));
883
884 rc = fdisk_do_ask(cxt, ask);
885 if (!rc)
886 start = fdisk_ask_number_get_result(ask);
887 fdisk_free_ask(ask);
888 if (rc)
889 return rc;
890
891 if (fdisk_context_use_cylinders(cxt)) {
892 start = (start - 1)
893 * fdisk_context_get_units_per_sector(cxt);
894 if (start < j)
895 start = j;
896 }
897 read = 1;
898 }
899 } while (start != temp || !read);
900
901 if (n > 4) { /* NOT for fifth partition */
902 struct pte *pe = self_pte(cxt, n);
903
904 pe->offset = start - cxt->first_lba;
905 if (pe->offset == l->ext_offset) { /* must be corrected */
906 pe->offset++;
907 if (cxt->first_lba == 1)
908 start++;
909 }
910 }
911
912 for (i = 0; i < cxt->label->nparts_max; i++) {
913 struct pte *pe = self_pte(cxt, i);
914
915 if (start < pe->offset && limit >= pe->offset)
916 limit = pe->offset - 1;
917 if (start < first[i] && limit >= first[i])
918 limit = first[i] - 1;
919 }
920 if (start > limit) {
921 fdisk_info(cxt, _("No free sectors available."));
922 if (n > 4)
923 cxt->label->nparts_max--;
924 return -ENOSPC;
925 }
926 if (cround(cxt, start) == cround(cxt, limit)) {
927 stop = limit;
928 } else {
929 /*
930 * Ask for last sector
931 */
932 struct fdisk_ask *ask = fdisk_new_ask();
933
934 fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
935
936 if (fdisk_context_use_cylinders(cxt)) {
937 fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
938 fdisk_ask_number_set_unit(ask,
939 cxt->sector_size *
940 fdisk_context_get_units_per_sector(cxt));
941 } else {
942 fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
943 fdisk_ask_number_set_unit(ask,cxt->sector_size);
944 }
945
946 fdisk_ask_number_set_low(ask, cround(cxt, start));
947 fdisk_ask_number_set_default(ask, cround(cxt, limit));
948 fdisk_ask_number_set_high(ask, cround(cxt, limit));
949 fdisk_ask_number_set_base(ask, cround(cxt, start)); /* base for relative input */
950
951 rc = fdisk_do_ask(cxt, ask);
952 if (rc) {
953 fdisk_free_ask(ask);
954 return rc;
955 }
956
957 stop = fdisk_ask_number_get_result(ask);
958
959 if (fdisk_context_use_cylinders(cxt)) {
960 stop = stop * fdisk_context_get_units_per_sector(cxt) - 1;
961 if (stop >limit)
962 stop = limit;
963 }
964 if (fdisk_ask_number_is_relative(ask)
965 && alignment_required(cxt)) {
966 /* the last sector has not been exactly requested (but
967 * defined by +size{K,M,G} convention), so be smart and
968 * align the end of the partition. The next partition
969 * will start at phy.block boundary.
970 */
971 stop = fdisk_align_lba_in_range(cxt, stop, start, limit) - 1;
972 if (stop > limit)
973 stop = limit;
974 }
975 fdisk_free_ask(ask);
976 }
977
978 set_partition(cxt, n, 0, start, stop, sys);
979 if (n > 4) {
980 struct pte *pe = self_pte(cxt, n);
981 set_partition(cxt, n - 1, 1, pe->offset, stop,
982 MBR_DOS_EXTENDED_PARTITION);
983 }
984
985 if (IS_EXTENDED(sys)) {
986 struct pte *pe4 = self_pte(cxt, 4);
987 struct pte *pen = self_pte(cxt, n);
988
989 l->ext_index = n;
990 pen->ex_entry = p;
991 pe4->offset = l->ext_offset = start;
992 pe4->sectorbuffer = calloc(1, cxt->sector_size);
993 if (!pe4->sectorbuffer)
994 return -ENOMEM;
995 DBG(LABEL, dbgprint("DOS: add partition, sector buffer %p", pe4->sectorbuffer));
996 pe4->private_sectorbuffer = 1;
997 pe4->pt_entry = mbr_get_partition(pe4->sectorbuffer, 0);
998 pe4->ex_entry = pe4->pt_entry + 1;
999
1000 partition_set_changed(cxt, 4, 1);
1001 cxt->label->nparts_max = 5;
1002 }
1003
1004 fdisk_label_set_changed(cxt->label, 1);
1005 return 0;
1006 }
1007
1008 static int add_logical(struct fdisk_context *cxt)
1009 {
1010 struct dos_partition *p4 = self_partition(cxt, 4);
1011
1012 assert(cxt);
1013 assert(cxt->label);
1014
1015 if (cxt->label->nparts_max > 5 || p4->sys_ind) {
1016 struct pte *pe = self_pte(cxt, cxt->label->nparts_max);
1017
1018 pe->sectorbuffer = calloc(1, cxt->sector_size);
1019 if (!pe->sectorbuffer)
1020 return -ENOMEM;
1021 DBG(LABEL, dbgprint("DOS: add logical, sector buffer %p", pe->sectorbuffer));
1022 pe->private_sectorbuffer = 1;
1023 pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
1024 pe->ex_entry = pe->pt_entry + 1;
1025 pe->offset = 0;
1026
1027 partition_set_changed(cxt, cxt->label->nparts_max, 1);
1028 cxt->label->nparts_max++;
1029 }
1030 fdisk_info(cxt, _("Adding logical partition %zd"),
1031 cxt->label->nparts_max);
1032 return add_partition(cxt, cxt->label->nparts_max - 1, NULL);
1033 }
1034
1035 static void check(struct fdisk_context *cxt, size_t n,
1036 unsigned int h, unsigned int s, unsigned int c,
1037 unsigned int start)
1038 {
1039 unsigned int total, real_s, real_c;
1040
1041 real_s = sector(s) - 1;
1042 real_c = cylinder(s, c);
1043 total = (real_c * cxt->geom.sectors + real_s) * cxt->geom.heads + h;
1044
1045 if (!total)
1046 fdisk_warnx(cxt, _("Partition %zd: contains sector 0"), n);
1047 if (h >= cxt->geom.heads)
1048 fdisk_warnx(cxt, _("Partition %zd: head %d greater than "
1049 "maximum %d"), n, h + 1, cxt->geom.heads);
1050 if (real_s >= cxt->geom.sectors)
1051 fdisk_warnx(cxt, _("Partition %zd: sector %d greater than "
1052 "maximum %llu"), n, s, cxt->geom.sectors);
1053 if (real_c >= cxt->geom.cylinders)
1054 fdisk_warnx(cxt, _("Partition %zd: cylinder %d greater than "
1055 "maximum %llu"),
1056 n, real_c + 1,
1057 cxt->geom.cylinders);
1058
1059 if (cxt->geom.cylinders <= 1024 && start != total)
1060 fdisk_warnx(cxt, _("Partition %zd: previous sectors %d "
1061 "disagrees with total %d"), n, start, total);
1062 }
1063
1064 /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1065 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1066 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1067 * Lubkin Oct. 1991). */
1068
1069 static void
1070 long2chs(struct fdisk_context *cxt, unsigned long ls,
1071 unsigned int *c, unsigned int *h, unsigned int *s) {
1072 int spc = cxt->geom.heads * cxt->geom.sectors;
1073
1074 *c = ls / spc;
1075 ls = ls % spc;
1076 *h = ls / cxt->geom.sectors;
1077 *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
1078 }
1079
1080 static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
1081 size_t partition)
1082 {
1083 unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
1084 unsigned int pec, peh, pes; /* physical ending c, h, s */
1085 unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
1086 unsigned int lec, leh, les; /* logical ending c, h, s */
1087
1088 if (!is_dos_compatible(cxt))
1089 return;
1090
1091 if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
1092 return; /* do not check extended partitions */
1093
1094 /* physical beginning c, h, s */
1095 pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
1096 pbh = p->bh;
1097 pbs = p->bs & 0x3f;
1098
1099 /* physical ending c, h, s */
1100 pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
1101 peh = p->eh;
1102 pes = p->es & 0x3f;
1103
1104 /* compute logical beginning (c, h, s) */
1105 long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
1106
1107 /* compute logical ending (c, h, s) */
1108 long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
1109
1110 /* Same physical / logical beginning? */
1111 if (cxt->geom.cylinders <= 1024
1112 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
1113 fdisk_warnx(cxt, _("Partition %zd: different physical/logical "
1114 "beginnings (non-Linux?): "
1115 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1116 partition + 1,
1117 pbc, pbh, pbs,
1118 lbc, lbh, lbs);
1119 }
1120
1121 /* Same physical / logical ending? */
1122 if (cxt->geom.cylinders <= 1024
1123 && (pec != lec || peh != leh || pes != les)) {
1124 fdisk_warnx(cxt, _("Partition %zd: different physical/logical "
1125 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1126 partition + 1,
1127 pec, peh, pes,
1128 lec, leh, les);
1129 }
1130
1131 /* Ending on cylinder boundary? */
1132 if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
1133 fdisk_warnx(cxt, _("Partition %zd: does not end on "
1134 "cylinder boundary."),
1135 partition + 1);
1136 }
1137 }
1138
1139 static int dos_verify_disklabel(struct fdisk_context *cxt)
1140 {
1141 size_t i, j;
1142 sector_t total = 1, n_sectors = cxt->total_sectors;
1143 unsigned long long first[cxt->label->nparts_max],
1144 last[cxt->label->nparts_max];
1145 struct dos_partition *p;
1146 struct fdisk_dos_label *l = self_label(cxt);
1147
1148 assert(fdisk_is_disklabel(cxt, DOS));
1149
1150 fill_bounds(cxt, first, last);
1151 for (i = 0; i < cxt->label->nparts_max; i++) {
1152 struct pte *pe = self_pte(cxt, i);
1153
1154 p = self_partition(cxt, i);
1155 if (p->sys_ind && !IS_EXTENDED(p->sys_ind)) {
1156 check_consistency(cxt, p, i);
1157 fdisk_warn_alignment(cxt, get_abs_partition_start(pe), i);
1158 if (get_abs_partition_start(pe) < first[i])
1159 fdisk_warnx(cxt, _(
1160 "Partition %zd: bad start-of-data."),
1161 i + 1);
1162
1163 check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
1164 total += last[i] + 1 - first[i];
1165
1166 for (j = 0; j < i; j++) {
1167 if ((first[i] >= first[j] && first[i] <= last[j])
1168 || ((last[i] <= last[j] && last[i] >= first[j]))) {
1169
1170 fdisk_warnx(cxt, _("Partition %zd: "
1171 "overlaps partition %zd."),
1172 j + 1, i + 1);
1173
1174 total += first[i] >= first[j] ?
1175 first[i] : first[j];
1176 total -= last[i] <= last[j] ?
1177 last[i] : last[j];
1178 }
1179 }
1180 }
1181 }
1182
1183 if (l->ext_offset) {
1184 sector_t e_last;
1185 p = self_partition(cxt, l->ext_index);
1186 e_last = dos_partition_get_start(p)
1187 + dos_partition_get_size(p) - 1;
1188
1189 for (i = 4; i < cxt->label->nparts_max; i++) {
1190 total++;
1191 p = self_partition(cxt, i);
1192
1193 if (!p->sys_ind) {
1194 if (i != 4 || i + 1 < cxt->label->nparts_max)
1195 fdisk_warnx(cxt,
1196 _("Partition %zd: empty."),
1197 i + 1);
1198 } else if (first[i] < l->ext_offset
1199 || last[i] > e_last) {
1200
1201 fdisk_warnx(cxt, _("Logical partition %zd: "
1202 "not entirely in partition %zd."),
1203 i + 1, l->ext_index + 1);
1204 }
1205 }
1206 }
1207
1208 if (total > n_sectors)
1209 fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
1210 "than the maximum %llu."), total, n_sectors);
1211 else if (total < n_sectors)
1212 fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
1213 "sectors."), n_sectors - total, cxt->sector_size);
1214
1215 return 0;
1216 }
1217
1218 /*
1219 * Ask the user for new partition type information (logical, extended).
1220 * This function calls the actual partition adding logic - add_partition.
1221 *
1222 * API callback.
1223 */
1224 static int dos_add_partition(
1225 struct fdisk_context *cxt,
1226 size_t partnum __attribute__ ((__unused__)),
1227 struct fdisk_parttype *t)
1228 {
1229 size_t i, free_primary = 0;
1230 int rc = 0;
1231 struct fdisk_dos_label *l = self_label(cxt);
1232
1233 assert(cxt);
1234 assert(cxt->label);
1235 assert(fdisk_is_disklabel(cxt, DOS));
1236
1237 for (i = 0; i < 4; i++) {
1238 struct dos_partition *p = self_partition(cxt, i);
1239 free_primary += !p->sys_ind;
1240 }
1241
1242 if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
1243 fdisk_info(cxt, _("The maximum number of partitions has "
1244 "been created."));
1245 return -EINVAL;
1246 }
1247 rc = 1;
1248
1249 if (!free_primary) {
1250 if (l->ext_offset) {
1251 fdisk_info(cxt, _("All primary partitions are in use."));
1252 rc = add_logical(cxt);
1253 } else
1254 fdisk_info(cxt, _("If you want to create more than "
1255 "four partitions, you must replace a "
1256 "primary partition with an extended "
1257 "partition first."));
1258
1259 } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
1260 int j;
1261
1262 fdisk_info(cxt, _("All logical partitions are in use. "
1263 "Adding a primary partition."));
1264 j = get_partition_unused_primary(cxt);
1265 if (j >= 0)
1266 rc = add_partition(cxt, j, t);
1267 } else {
1268 char *buf;
1269 char c, prompt[BUFSIZ];
1270 int dflt;
1271
1272 dflt = (free_primary == 1 && !l->ext_offset) ? 'e' : 'p';
1273
1274 snprintf(prompt, sizeof(prompt),
1275 _("Partition type:\n"
1276 " p primary (%zd primary, %d extended, %zd free)\n"
1277 "%s\n"
1278 "Select (default %c)"),
1279 4 - (l->ext_offset ? 1 : 0) - free_primary,
1280 l->ext_offset ? 1 : 0, free_primary,
1281 l->ext_offset ? _(" l logical (numbered from 5)") : _(" e extended"),
1282 dflt);
1283
1284 rc = fdisk_ask_string(cxt, prompt, &buf);
1285 if (rc)
1286 return rc;
1287 if (!buf[0]) {
1288 c = dflt;
1289 fdisk_info(cxt, _("Using default response %c."), c);
1290 } else
1291 c = tolower(buf[0]);
1292 free(buf);
1293
1294 if (c == 'p') {
1295 int j = get_partition_unused_primary(cxt);
1296 if (j >= 0)
1297 rc = add_partition(cxt, j, t);
1298 goto done;
1299 } else if (c == 'l' && l->ext_offset) {
1300 rc = add_logical(cxt);
1301 goto done;
1302 } else if (c == 'e' && !l->ext_offset) {
1303 int j = get_partition_unused_primary(cxt);
1304 if (j >= 0) {
1305 t = fdisk_get_parttype_from_code(cxt,
1306 MBR_DOS_EXTENDED_PARTITION);
1307 rc = add_partition(cxt, j, t);
1308 }
1309 goto done;
1310 } else
1311 fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
1312 }
1313 done:
1314 if (rc == 0)
1315 cxt->label->nparts_cur++;
1316 return rc;
1317 }
1318
1319 static int write_sector(struct fdisk_context *cxt, sector_t secno,
1320 unsigned char *buf)
1321 {
1322 int rc;
1323
1324 rc = seek_sector(cxt, secno);
1325 if (rc != 0) {
1326 fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
1327 (uintmax_t) secno);
1328 return rc;
1329 }
1330 if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
1331 return -errno;
1332 return 0;
1333 }
1334
1335 static int dos_write_disklabel(struct fdisk_context *cxt)
1336 {
1337 struct fdisk_dos_label *l = self_label(cxt);
1338 size_t i;
1339 int rc = 0, mbr_changed = 0;
1340
1341 assert(cxt);
1342 assert(cxt->label);
1343 assert(fdisk_is_disklabel(cxt, DOS));
1344
1345 mbr_changed = l->non_pt_changed;
1346
1347 /* MBR (primary partitions) */
1348 if (!mbr_changed) {
1349 for (i = 0; i < 4; i++) {
1350 struct pte *pe = self_pte(cxt, i);
1351 if (pe->changed)
1352 mbr_changed = 1;
1353 }
1354 }
1355 if (mbr_changed) {
1356 mbr_set_magic(cxt->firstsector);
1357 rc = write_sector(cxt, 0, cxt->firstsector);
1358 if (rc)
1359 goto done;
1360 }
1361
1362 /* EBR (logical partitions) */
1363 for (i = 4; i < cxt->label->nparts_max; i++) {
1364 struct pte *pe = self_pte(cxt, i);
1365
1366 if (pe->changed) {
1367 mbr_set_magic(pe->sectorbuffer);
1368 rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
1369 if (rc)
1370 goto done;
1371 }
1372 }
1373
1374 done:
1375 return rc;
1376 }
1377
1378 static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
1379 const char **name, off_t *offset, size_t *size)
1380 {
1381 assert(cxt);
1382
1383 *name = NULL;
1384 *offset = 0;
1385 *size = 0;
1386
1387 switch (n) {
1388 case 0:
1389 *name = "MBR";
1390 *offset = 0;
1391 *size = 512;
1392 break;
1393 default:
1394 /* extended partitions */
1395 if (n - 1 + 4 < cxt->label->nparts_max) {
1396 struct pte *pe = self_pte(cxt, n - 1 + 4);
1397
1398 assert(pe->private_sectorbuffer);
1399
1400 *name = "EBR";
1401 *offset = pe->offset * cxt->sector_size;
1402 *size = 512;
1403 } else
1404 return 1;
1405 break;
1406 }
1407
1408 return 0;
1409 }
1410
1411 static struct fdisk_parttype *dos_get_parttype(
1412 struct fdisk_context *cxt,
1413 size_t partnum)
1414 {
1415 struct fdisk_parttype *t;
1416 struct dos_partition *p;
1417
1418 assert(cxt);
1419 assert(cxt->label);
1420 assert(fdisk_is_disklabel(cxt, DOS));
1421
1422 if (partnum >= cxt->label->nparts_max)
1423 return NULL;
1424
1425 p = self_partition(cxt, partnum);
1426 t = fdisk_get_parttype_from_code(cxt, p->sys_ind);
1427 if (!t)
1428 t = fdisk_new_unknown_parttype(p->sys_ind, NULL);
1429 return t;
1430 }
1431
1432 static int dos_set_parttype(
1433 struct fdisk_context *cxt,
1434 size_t partnum,
1435 struct fdisk_parttype *t)
1436 {
1437 struct dos_partition *p;
1438
1439 assert(cxt);
1440 assert(cxt->label);
1441 assert(fdisk_is_disklabel(cxt, DOS));
1442
1443 if (partnum >= cxt->label->nparts_max || !t || t->type > UINT8_MAX)
1444 return -EINVAL;
1445
1446 p = self_partition(cxt, partnum);
1447 if (t->type == p->sys_ind)
1448 return 0;
1449
1450 if (IS_EXTENDED(p->sys_ind) || IS_EXTENDED(t->type)) {
1451 fdisk_warnx(cxt, _("You cannot change a partition into an "
1452 "extended one or vice versa. Delete it first."));
1453 return -EINVAL;
1454 }
1455
1456 if (is_dos_partition(t->type) || is_dos_partition(p->sys_ind))
1457 fdisk_info(cxt, _("If you have created or modified any DOS 6.x "
1458 "partitions, please see the fdisk documentation for additional "
1459 "information."));
1460
1461 p->sys_ind = t->type;
1462
1463 partition_set_changed(cxt, partnum, 1);
1464 return 0;
1465 }
1466
1467 /*
1468 * Check whether partition entries are ordered by their starting positions.
1469 * Return 0 if OK. Return i if partition i should have been earlier.
1470 * Two separate checks: primary and logical partitions.
1471 */
1472 static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
1473 {
1474 size_t last_p_start_pos = 0, p_start_pos;
1475 size_t i, last_i = 0;
1476
1477 for (i = 0 ; i < cxt->label->nparts_max; i++) {
1478
1479 struct pte *pe = self_pte(cxt, i);
1480 struct dos_partition *p = pe->pt_entry;
1481
1482 if (i == 4) {
1483 last_i = 4;
1484 last_p_start_pos = 0;
1485 }
1486 if (p->sys_ind) {
1487 p_start_pos = get_abs_partition_start(pe);
1488
1489 if (last_p_start_pos > p_start_pos) {
1490 if (prev)
1491 *prev = last_i;
1492 return i;
1493 }
1494
1495 last_p_start_pos = p_start_pos;
1496 last_i = i;
1497 }
1498 }
1499 return 0;
1500 }
1501
1502 static int is_garbage_table(struct fdisk_context *cxt)
1503 {
1504 size_t i;
1505
1506 for (i = 0; i < 4; i++) {
1507 struct dos_partition *p = self_partition(cxt, i);
1508
1509 if (p->boot_ind != 0 && p->boot_ind != 0x80)
1510 return 1;
1511 }
1512 return 0;
1513 }
1514
1515 /*
1516 * List all PT fields.
1517 *
1518 * This is useful for PT debugging (or for 70's Hippies
1519 * who are on permanent LSD trip).
1520 */
1521 static int dos_fulllist_disklabel(struct fdisk_context *cxt, int ext)
1522 {
1523 int rc;
1524 size_t i;
1525 struct tt *tb = NULL;
1526
1527 assert(cxt);
1528 assert(cxt->label);
1529 assert(fdisk_is_disklabel(cxt, DOS));
1530
1531 tb = tt_new_table(TT_FL_FREEDATA);
1532 if (!tb)
1533 return -ENOMEM;
1534
1535 tt_define_column(tb, _("Nr"), 2, TT_FL_RIGHT);
1536 tt_define_column(tb, _("AF"), 2, TT_FL_RIGHT);
1537
1538 tt_define_column(tb, _("Hd"), 4, TT_FL_RIGHT);
1539 tt_define_column(tb, _("Sec"), 4, TT_FL_RIGHT);
1540 tt_define_column(tb, _("Cyl"), 5, TT_FL_RIGHT);
1541
1542 tt_define_column(tb, _("Hd"), 4, TT_FL_RIGHT);
1543 tt_define_column(tb, _("Sec"), 4, TT_FL_RIGHT);
1544 tt_define_column(tb, _("Cyl"), 5, TT_FL_RIGHT);
1545
1546 tt_define_column(tb, _("Start"), 9, TT_FL_RIGHT);
1547 tt_define_column(tb, _("Size"), 9, TT_FL_RIGHT);
1548 tt_define_column(tb, _("Id"), 2, TT_FL_RIGHT);
1549
1550 for (i = 0 ; i < cxt->label->nparts_max; i++) {
1551 struct pte *pe = self_pte(cxt, i);
1552 struct dos_partition *p;
1553 struct tt_line *ln;
1554 char *str;
1555
1556 p = ext ? pe->ex_entry : pe->pt_entry;
1557 if (!p)
1558 continue;
1559 ln = tt_add_line(tb, NULL);
1560 if (!ln)
1561 continue;
1562
1563 if (asprintf(&str, "%zd", i + 1) > 0)
1564 tt_line_set_data(ln, 0, str); /* Nr */
1565 if (asprintf(&str, "%02x", p->boot_ind) > 0)
1566 tt_line_set_data(ln, 1, str); /* AF */
1567
1568 if (asprintf(&str, "%d", p->bh) > 0)
1569 tt_line_set_data(ln, 2, str); /* Hd */
1570 if (asprintf(&str, "%d", sector(p->bs)) > 0)
1571 tt_line_set_data(ln, 3, str); /* Sec */
1572 if (asprintf(&str, "%d", cylinder(p->bs, p->bc)) > 0)
1573 tt_line_set_data(ln, 4, str); /* Cyl */
1574
1575 if (asprintf(&str, "%d", p->eh) > 0)
1576 tt_line_set_data(ln, 5, str); /* Hd */
1577 if (asprintf(&str, "%d", sector(p->es)) > 0)
1578 tt_line_set_data(ln, 6, str); /* Sec */
1579 if (asprintf(&str, "%d", cylinder(p->es, p->ec)) > 0)
1580 tt_line_set_data(ln, 7, str); /* Cyl */
1581
1582 if (asprintf(&str, "%lu",
1583 (unsigned long) dos_partition_get_start(p)) > 0)
1584 tt_line_set_data(ln, 8, str); /* Start */
1585 if (asprintf(&str, "%lu",
1586 (unsigned long) dos_partition_get_size(p)) > 0)
1587 tt_line_set_data(ln, 9, str); /* End */
1588
1589 if (asprintf(&str, "%02x", p->sys_ind) > 0)
1590 tt_line_set_data(ln, 10, str); /* Id */
1591
1592 if (p->sys_ind) {
1593 check_consistency(cxt, p, i);
1594 fdisk_warn_alignment(cxt,
1595 get_abs_partition_start(pe), i);
1596 }
1597 }
1598
1599 rc = fdisk_print_table(cxt, tb);
1600 tt_free_table(tb);
1601
1602 return rc;
1603 }
1604
1605 int fdisk_dos_list_extended(struct fdisk_context *cxt)
1606 {
1607 return dos_fulllist_disklabel(cxt, 1);
1608 }
1609
1610 static int dos_list_disklabel(struct fdisk_context *cxt)
1611 {
1612 int rc = 0, trunc = TT_FL_TRUNC;
1613 size_t i;
1614 struct tt *tb = NULL;
1615
1616 assert(cxt);
1617 assert(cxt->label);
1618 assert(fdisk_is_disklabel(cxt, DOS));
1619
1620 if (is_garbage_table(cxt)) {
1621 fdisk_warnx(cxt, _(
1622 "This doesn't look like a partition table. "
1623 "Probably you selected the wrong device."));
1624 }
1625
1626 if (fdisk_context_display_details(cxt))
1627 return dos_fulllist_disklabel(cxt, 0);
1628
1629 tb = tt_new_table(TT_FL_FREEDATA);
1630 if (!tb)
1631 return -ENOMEM;
1632
1633 /* don't trunc anything in expert mode */
1634 if (fdisk_context_display_details(cxt))
1635 trunc = 0;
1636
1637 tt_define_column(tb, _("Device"), 0.1, 0);
1638 tt_define_column(tb, _("Boot"), 1, 0);
1639 tt_define_column(tb, _("Start"), 9, TT_FL_RIGHT);
1640 tt_define_column(tb, _("End"), 9, TT_FL_RIGHT);
1641 /* TRANSLATORS: keep one blank space behind 'Blocks' */
1642 tt_define_column(tb, _("Blocks "), 5, TT_FL_RIGHT);
1643 tt_define_column(tb, _("Id"), 2, TT_FL_RIGHT);
1644 tt_define_column(tb, _("System"), 0.1, trunc);
1645
1646 for (i = 0; i < cxt->label->nparts_max; i++) {
1647 struct pte *pe = self_pte(cxt, i);
1648 struct dos_partition *p = pe->pt_entry;
1649 unsigned int psects, pblocks, podd = 0;
1650 struct fdisk_parttype *type;
1651 struct tt_line *ln;
1652 char *str;
1653
1654 if (!p || is_cleared_partition(p))
1655 continue;
1656 ln = tt_add_line(tb, NULL);
1657 if (!ln)
1658 continue;
1659
1660 pblocks = psects = dos_partition_get_size(p);
1661 type = fdisk_get_parttype_from_code(cxt, p->sys_ind);
1662
1663 if (cxt->sector_size < 1024) {
1664 pblocks /= (1024 / cxt->sector_size);
1665 podd = psects % (1024 / cxt->sector_size);
1666 }
1667 if (cxt->sector_size > 1024)
1668 pblocks *= (cxt->sector_size / 1024);
1669
1670 str = fdisk_partname(cxt->dev_path, i + 1);
1671 if (str)
1672 tt_line_set_data(ln, 0, str); /* device */
1673
1674 str = strdup(p->boot_ind ?
1675 p->boot_ind == ACTIVE_FLAG ? "*" : "?" : " ");
1676 if (str)
1677 tt_line_set_data(ln, 1, str); /* boot flag */
1678
1679 if (asprintf(&str, "%lu", (unsigned long) cround(cxt,
1680 get_abs_partition_start(pe))) > 0)
1681 tt_line_set_data(ln, 2, str); /* start */
1682
1683 if (asprintf(&str, "%lu", (unsigned long) cround(cxt,
1684 get_abs_partition_start(pe)
1685 + psects - (psects ? 1 : 0))) > 0)
1686 tt_line_set_data(ln, 3, str); /* end */
1687
1688 if (asprintf(&str, "%lu%c", (unsigned long) pblocks,
1689 podd ? '+' : ' ') > 0)
1690 tt_line_set_data(ln, 4, str); /* blocks<flag> */
1691
1692 if (asprintf(&str, "%x", p->sys_ind) > 0)
1693 tt_line_set_data(ln, 5, str); /* id */
1694
1695 str = strdup(type ? type->name : _("Unknown"));
1696 if (str)
1697 tt_line_set_data(ln, 6, str);
1698
1699 check_consistency(cxt, p, i);
1700 fdisk_warn_alignment(cxt, get_abs_partition_start(pe), i);
1701 fdisk_free_parttype(type);
1702 }
1703
1704 rc = fdisk_print_table(cxt, tb);
1705 tt_free_table(tb);
1706
1707 /* Is partition table in disk order? It need not be, but... */
1708 /* partition table entries are not checked for correct order if this
1709 is a sgi, sun labeled disk... */
1710 if (wrong_p_order(cxt, NULL))
1711 fdisk_info(cxt, _("Partition table entries are not in "
1712 "disk order."));
1713
1714 return rc;
1715 }
1716
1717
1718 /*
1719 * Fix the chain of logicals.
1720 * ext_offset is unchanged, the set of sectors used is unchanged
1721 * The chain is sorted so that sectors increase, and so that
1722 * starting sectors increase.
1723 *
1724 * After this it may still be that cfdisk doesn't like the table.
1725 * (This is because cfdisk considers expanded parts, from link to
1726 * end of partition, and these may still overlap.)
1727 * Now
1728 * sfdisk /dev/hda > ohda; sfdisk /dev/hda < ohda
1729 * may help.
1730 */
1731 static void fix_chain_of_logicals(struct fdisk_context *cxt)
1732 {
1733 struct fdisk_dos_label *l = self_label(cxt);
1734 size_t j, oj, ojj, sj, sjj;
1735 struct dos_partition *pj,*pjj,tmp;
1736
1737 /* Stage 1: sort sectors but leave sector of part 4 */
1738 /* (Its sector is the global ext_offset.) */
1739 stage1:
1740 for (j = 5; j < cxt->label->nparts_max - 1; j++) {
1741 oj = l->ptes[j].offset;
1742 ojj = l->ptes[j + 1].offset;
1743 if (oj > ojj) {
1744 l->ptes[j].offset = ojj;
1745 l->ptes[j + 1].offset = oj;
1746 pj = l->ptes[j].pt_entry;
1747 dos_partition_set_start(pj, dos_partition_get_start(pj)+oj-ojj);
1748 pjj = l->ptes[j + 1].pt_entry;
1749 dos_partition_set_start(pjj, dos_partition_get_start(pjj)+ojj-oj);
1750 dos_partition_set_start(l->ptes[j - 1].ex_entry,
1751 ojj - l->ext_offset);
1752 dos_partition_set_start(l->ptes[j].ex_entry,
1753 oj - l->ext_offset);
1754 goto stage1;
1755 }
1756 }
1757
1758 /* Stage 2: sort starting sectors */
1759 stage2:
1760 for (j = 4; j < cxt->label->nparts_max - 1; j++) {
1761 pj = l->ptes[j].pt_entry;
1762 pjj = l->ptes[j + 1].pt_entry;
1763 sj = dos_partition_get_start(pj);
1764 sjj = dos_partition_get_start(pjj);
1765 oj = l->ptes[j].offset;
1766 ojj = l->ptes[j+1].offset;
1767 if (oj+sj > ojj+sjj) {
1768 tmp = *pj;
1769 *pj = *pjj;
1770 *pjj = tmp;
1771 dos_partition_set_start(pj, ojj+sjj-oj);
1772 dos_partition_set_start(pjj, oj+sj-ojj);
1773 goto stage2;
1774 }
1775 }
1776
1777 /* Probably something was changed */
1778 for (j = 4; j < cxt->label->nparts_max; j++)
1779 l->ptes[j].changed = 1;
1780 }
1781
1782 int fdisk_dos_fix_order(struct fdisk_context *cxt)
1783 {
1784 struct pte *pei, *pek;
1785 size_t i,k;
1786
1787 if (!wrong_p_order(cxt, NULL)) {
1788 fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
1789 return 0;
1790 }
1791
1792 while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
1793 /* partition i should have come earlier, move it */
1794 /* We have to move data in the MBR */
1795 struct dos_partition *pi, *pk, *pe, pbuf;
1796 pei = self_pte(cxt, i);
1797 pek = self_pte(cxt, k);
1798
1799 pe = pei->ex_entry;
1800 pei->ex_entry = pek->ex_entry;
1801 pek->ex_entry = pe;
1802
1803 pi = pei->pt_entry;
1804 pk = pek->pt_entry;
1805
1806 memmove(&pbuf, pi, sizeof(struct dos_partition));
1807 memmove(pi, pk, sizeof(struct dos_partition));
1808 memmove(pk, &pbuf, sizeof(struct dos_partition));
1809
1810 partition_set_changed(cxt, i, 1);
1811 partition_set_changed(cxt, k, 1);
1812 }
1813
1814 if (i)
1815 fix_chain_of_logicals(cxt);
1816
1817 fdisk_info(cxt, _("Done."));
1818 return 0;
1819 }
1820
1821 int fdisk_dos_move_begin(struct fdisk_context *cxt, int i)
1822 {
1823 struct pte *pe;
1824 struct dos_partition *p;
1825 unsigned int new, free_start, curr_start, last;
1826 uintmax_t res = 0;
1827 size_t x;
1828 int rc;
1829
1830 assert(cxt);
1831 assert(fdisk_is_disklabel(cxt, DOS));
1832
1833 pe = self_pte(cxt, i);
1834 p = pe->pt_entry;
1835
1836 if (!p->sys_ind || !dos_partition_get_size(p) || IS_EXTENDED (p->sys_ind)) {
1837 fdisk_warnx(cxt, _("Partition %d: no data area."), i + 1);
1838 return 0;
1839 }
1840
1841 /* the default start is at the second sector of the disk or at the
1842 * second sector of the extended partition
1843 */
1844 free_start = pe->offset ? pe->offset + 1 : 1;
1845
1846 curr_start = get_abs_partition_start(pe);
1847
1848 /* look for a free space before the current start of the partition */
1849 for (x = 0; x < cxt->label->nparts_max; x++) {
1850 unsigned int end;
1851 struct pte *prev_pe = self_pte(cxt, x);
1852 struct dos_partition *prev_p = prev_pe->pt_entry;
1853
1854 if (!prev_p)
1855 continue;
1856 end = get_abs_partition_start(prev_pe)
1857 + dos_partition_get_size(prev_p);
1858
1859 if (!is_cleared_partition(prev_p) &&
1860 end > free_start && end <= curr_start)
1861 free_start = end;
1862 }
1863
1864 last = get_abs_partition_start(pe) + dos_partition_get_size(p) - 1;
1865
1866 rc = fdisk_ask_number(cxt, free_start, curr_start, last,
1867 _("New beginning of data"), &res);
1868 if (rc)
1869 return rc;
1870
1871 new = res - pe->offset;
1872
1873 if (new != dos_partition_get_size(p)) {
1874 unsigned int sects = dos_partition_get_size(p)
1875 + dos_partition_get_start(p) - new;
1876
1877 dos_partition_set_size(p, sects);
1878 dos_partition_set_start(p, new);
1879
1880 partition_set_changed(cxt, i, 1);
1881 }
1882
1883 return rc;
1884 }
1885
1886 static int dos_get_partition_status(
1887 struct fdisk_context *cxt,
1888 size_t i,
1889 int *status)
1890 {
1891 struct dos_partition *p;
1892
1893 assert(cxt);
1894 assert(cxt->label);
1895 assert(fdisk_is_disklabel(cxt, DOS));
1896
1897 if (!status || i >= cxt->label->nparts_max)
1898 return -EINVAL;
1899
1900 p = self_partition(cxt, i);
1901
1902 if (p && !is_cleared_partition(p))
1903 *status = FDISK_PARTSTAT_USED;
1904 else
1905 *status = FDISK_PARTSTAT_NONE;
1906
1907 return 0;
1908 }
1909
1910 static int dos_toggle_partition_flag(
1911 struct fdisk_context *cxt,
1912 size_t i,
1913 unsigned long flag)
1914 {
1915 struct dos_partition *p;
1916
1917 assert(cxt);
1918 assert(cxt->label);
1919 assert(fdisk_is_disklabel(cxt, DOS));
1920
1921 if (i >= cxt->label->nparts_max)
1922 return -EINVAL;
1923
1924 p = self_partition(cxt, i);
1925
1926 switch (flag) {
1927 case DOS_FLAG_ACTIVE:
1928 if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
1929 fdisk_warnx(cxt, _("Partition %d: is an extended "
1930 "partition."), (int) i + 1);
1931
1932 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
1933 partition_set_changed(cxt, i, 1);
1934 break;
1935 default:
1936 return 1;
1937 }
1938
1939 return 0;
1940 }
1941
1942 static const struct fdisk_label_operations dos_operations =
1943 {
1944 .probe = dos_probe_label,
1945 .write = dos_write_disklabel,
1946 .verify = dos_verify_disklabel,
1947 .create = dos_create_disklabel,
1948 .locate = dos_locate_disklabel,
1949 .list = dos_list_disklabel,
1950 .get_id = dos_get_disklabel_id,
1951 .set_id = dos_set_disklabel_id,
1952
1953 .part_add = dos_add_partition,
1954 .part_delete = dos_delete_partition,
1955 .part_get_type = dos_get_parttype,
1956 .part_set_type = dos_set_parttype,
1957
1958 .part_toggle_flag = dos_toggle_partition_flag,
1959 .part_get_status = dos_get_partition_status,
1960
1961 .reset_alignment = dos_reset_alignment,
1962
1963 .deinit = dos_deinit,
1964 };
1965
1966 /*
1967 * allocates DOS in-memory stuff
1968 */
1969 struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt)
1970 {
1971 struct fdisk_label *lb;
1972 struct fdisk_dos_label *dos;
1973
1974 assert(cxt);
1975
1976 dos = calloc(1, sizeof(*dos));
1977 if (!dos)
1978 return NULL;
1979
1980 /* initialize generic part of the driver */
1981 lb = (struct fdisk_label *) dos;
1982 lb->name = "dos";
1983 lb->id = FDISK_DISKLABEL_DOS;
1984 lb->op = &dos_operations;
1985 lb->parttypes = dos_parttypes;
1986 lb->nparttypes = ARRAY_SIZE(dos_parttypes);
1987
1988 lb->flags |= FDISK_LABEL_FL_ADDPART_NOPARTNO;
1989
1990 return lb;
1991 }
1992
1993 /*
1994 * Public label specific functions
1995 */
1996
1997 int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
1998 {
1999 struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
2000
2001 if (!lb)
2002 return -EINVAL;
2003
2004 dos->compatible = enable;
2005 if (enable)
2006 lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
2007 return 0;
2008 }
2009
2010 int fdisk_dos_is_compatible(struct fdisk_label *lb)
2011 {
2012 return ((struct fdisk_dos_label *) lb)->compatible;
2013 }