]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libfdisk/src/dos.c
40b9aa758ca159bd6a4a1da44e08e84032439cdc
[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 = strtoul(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
911 static int get_start_from_user( struct fdisk_context *cxt,
912 fdisk_sector_t *start,
913 fdisk_sector_t low,
914 fdisk_sector_t dflt,
915 fdisk_sector_t limit,
916 struct fdisk_partition *pa)
917 {
918 assert(start);
919
920 /* try to use template from 'pa' */
921 if (pa && pa->start_follow_default)
922 *start = dflt;
923
924 else if (pa && fdisk_partition_has_start(pa)) {
925 DBG(LABEL, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
926 (uintmax_t) pa->start, (uintmax_t) low, (uintmax_t) limit));
927 *start = pa->start;
928 if (*start < low || *start > limit) {
929 fdisk_warnx(cxt, _("Start sector %ju out of range."),
930 (uintmax_t) *start);
931 return -ERANGE;
932 }
933 } else {
934 /* ask user by dialog */
935 struct fdisk_ask *ask = fdisk_new_ask();
936 int rc;
937
938 if (!ask)
939 return -ENOMEM;
940 fdisk_ask_set_query(ask,
941 fdisk_use_cylinders(cxt) ?
942 _("First cylinder") : _("First sector"));
943 fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
944 fdisk_ask_number_set_low(ask, fdisk_cround(cxt, low));
945 fdisk_ask_number_set_default(ask, fdisk_cround(cxt, dflt));
946 fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
947
948 rc = fdisk_do_ask(cxt, ask);
949 *start = fdisk_ask_number_get_result(ask);
950 fdisk_unref_ask(ask);
951 if (rc)
952 return rc;
953 if (fdisk_use_cylinders(cxt)) {
954 *start = (*start - 1)
955 * fdisk_get_units_per_sector(cxt);
956 if (*start < low)
957 *start = low;
958 }
959 }
960
961 DBG(LABEL, ul_debug("DOS: start is %ju", (uintmax_t) *start));
962 return 0;
963 }
964
965 static int find_last_free_sector_in_range(
966 struct fdisk_context *cxt,
967 int logical,
968 fdisk_sector_t begin,
969 fdisk_sector_t end,
970 fdisk_sector_t *result)
971 {
972 int last_moved;
973 fdisk_sector_t last = end;
974
975 do {
976 size_t i = logical ? 4 : 0;
977
978 last_moved = 0;
979 for ( ; i < cxt->label->nparts_max; i++) {
980 struct pte *pe = self_pte(cxt, i);
981 fdisk_sector_t p_start = get_abs_partition_start(pe);
982 fdisk_sector_t p_end = get_abs_partition_end(pe);
983
984 if (is_cleared_partition(pe->pt_entry))
985 continue;
986
987 /* count EBR and begin of the logical partition as used area */
988 if (pe->offset)
989 p_start -= cxt->first_lba;
990
991 if (last >= p_start && last <= p_end) {
992 last = p_start - 1;
993 last_moved = 1;
994
995 if (last < begin) {
996 DBG(LABEL, ul_debug("DOS: last free out of range <%ju,%ju>: %ju",
997 (uintmax_t) begin, (uintmax_t) end, (uintmax_t) last));
998
999 return -ENOSPC;
1000 }
1001 }
1002 }
1003 } while (last_moved == 1);
1004
1005 DBG(LABEL, ul_debug("DOS: last unused sector in range <%ju,%ju>: %ju",
1006 (uintmax_t) begin, (uintmax_t) end, (uintmax_t) last));
1007
1008 *result = last;
1009 return 0;
1010 }
1011
1012 static int find_first_free_sector_in_range(
1013 struct fdisk_context *cxt,
1014 int logical,
1015 fdisk_sector_t begin,
1016 fdisk_sector_t end,
1017 fdisk_sector_t *result)
1018 {
1019 int first_moved = 0;
1020 fdisk_sector_t first = begin;
1021
1022 do {
1023 size_t i = logical ? 4 : 0;
1024
1025 first_moved = 0;
1026 for (; i < cxt->label->nparts_max; i++) {
1027 struct pte *pe = self_pte(cxt, i);
1028 fdisk_sector_t p_start = get_abs_partition_start(pe);
1029 fdisk_sector_t p_end = get_abs_partition_end(pe);
1030
1031 if (is_cleared_partition(pe->pt_entry))
1032 continue;
1033 /* count EBR and begin of the logical partition as used area */
1034 if (pe->offset)
1035 p_start -= cxt->first_lba;
1036 if (first < p_start)
1037 continue;
1038 if (first <= p_end) {
1039 first = p_end + 1 + (logical ? cxt->first_lba : 0);
1040 first_moved = 1;
1041
1042 if (first > end) {
1043 DBG(LABEL, ul_debug("DOS: first free out of range <%ju,%ju>: %ju",
1044 (uintmax_t) begin, (uintmax_t) end, (uintmax_t) first));
1045 return -ENOSPC;
1046 }
1047 }
1048 }
1049 } while (first_moved == 1);
1050
1051 DBG(LABEL, ul_debug("DOS: first unused sector in range <%ju,%ju>: %ju",
1052 (uintmax_t) begin, (uintmax_t) end, (uintmax_t) first));
1053 *result = first;
1054 return 0;
1055 }
1056
1057 static int get_disk_ranges(struct fdisk_context *cxt, int logical,
1058 fdisk_sector_t *first, fdisk_sector_t *last)
1059 {
1060 if (logical) {
1061 /* logical partitions */
1062 struct fdisk_dos_label *l = self_label(cxt);
1063 struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1064
1065 if (!ext_pe)
1066 return -EINVAL;
1067
1068 *first = l->ext_offset + cxt->first_lba;
1069 *last = get_abs_partition_end(ext_pe);
1070
1071 } else {
1072 /* primary partitions */
1073 if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
1074 *last = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
1075 else
1076 *last = cxt->total_sectors - 1;
1077
1078 if (*last > UINT_MAX)
1079 *last = UINT_MAX;
1080 *first = cxt->first_lba;
1081 }
1082
1083 return 0;
1084 }
1085
1086 static int find_last_free_sector(struct fdisk_context *cxt, int logical, fdisk_sector_t *result)
1087 {
1088 fdisk_sector_t first, last;
1089 int rc;
1090
1091 rc = get_disk_ranges(cxt, logical, &first, &last);
1092 if (rc)
1093 return rc;
1094
1095 return find_last_free_sector_in_range(cxt, logical, first, last, result);
1096 }
1097
1098 static int find_first_free_sector(struct fdisk_context *cxt,
1099 int logical,
1100 fdisk_sector_t start,
1101 fdisk_sector_t *result)
1102 {
1103 fdisk_sector_t first, last;
1104 int rc;
1105
1106 rc = get_disk_ranges(cxt, logical, &first, &last);
1107 if (rc)
1108 return rc;
1109
1110 return find_first_free_sector_in_range(cxt, logical, start, last, result);
1111 }
1112
1113 static int add_partition(struct fdisk_context *cxt, size_t n,
1114 struct fdisk_partition *pa)
1115 {
1116 int sys, read = 0, rc, isrel = 0, is_logical;
1117 struct fdisk_dos_label *l = self_label(cxt);
1118 struct dos_partition *p = self_partition(cxt, n);
1119 struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1120 struct fdisk_ask *ask = NULL;
1121
1122 fdisk_sector_t start, stop = 0, limit, temp;
1123
1124 DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
1125
1126 sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
1127 is_logical = n >= 4;
1128
1129 if (p && is_used_partition(p)) {
1130 fdisk_warnx(cxt, _("Partition %zu is already defined. "
1131 "Delete it before re-adding it."),
1132 n + 1);
1133 return -EINVAL;
1134 }
1135
1136 rc = find_last_free_sector(cxt, is_logical, &limit);
1137 if (rc == -ENOSPC)
1138 fdisk_warnx(cxt, _("No free sectors available."));
1139 if (rc)
1140 return rc;
1141
1142 if (!is_logical) {
1143 if (cxt->parent && fdisk_is_label(cxt->parent, GPT))
1144 start = 1; /* Bad boy modifies hybrid MBR */
1145 else {
1146 if (cxt->script && pa && fdisk_partition_has_start(pa)
1147 && pa->start < cxt->first_lba
1148 && pa->start >= 1)
1149 fdisk_set_first_lba(cxt, 1);
1150
1151 start = cxt->first_lba;
1152 }
1153 } else {
1154 assert(ext_pe);
1155
1156 if (cxt->script && pa && fdisk_partition_has_start(pa)
1157 && pa->start >= l->ext_offset
1158 && pa->start < l->ext_offset + cxt->first_lba)
1159 fdisk_set_first_lba(cxt, 1);
1160
1161 start = l->ext_offset + cxt->first_lba;
1162 }
1163
1164 /*
1165 * Ask for first sector
1166 */
1167 do {
1168 fdisk_sector_t dflt, aligned;
1169
1170 temp = start;
1171
1172 rc = find_first_free_sector(cxt, is_logical, start, &dflt);
1173 if (rc == -ENOSPC)
1174 fdisk_warnx(cxt, _("No free sectors available."));
1175 if (rc)
1176 return rc;
1177 start = dflt;
1178
1179 if (n >= 4 && pa && fdisk_partition_has_start(pa) && cxt->script
1180 && cxt->first_lba > 1
1181 && temp == start - cxt->first_lba) {
1182 fdisk_set_first_lba(cxt, 1);
1183 start = pa->start;
1184 }
1185
1186 /* the default sector should be aligned and unused */
1187 do {
1188 aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
1189 find_first_free_sector(cxt, is_logical, aligned, &dflt);
1190 } while (dflt != aligned && dflt > aligned && dflt < limit);
1191
1192 if (dflt >= limit)
1193 dflt = start;
1194 if (start > limit)
1195 break;
1196 if (start >= temp + fdisk_get_units_per_sector(cxt)
1197 && read) {
1198 fdisk_info(cxt, _("Sector %llu is already allocated."),
1199 temp);
1200 temp = start;
1201 read = 0;
1202 if (pa && (fdisk_partition_has_start(pa) ||
1203 pa->start_follow_default))
1204 break;
1205 }
1206
1207 if (!read && start == temp) {
1208 rc = get_start_from_user(cxt, &start, temp, dflt, limit, pa);
1209 if (rc)
1210 return rc;
1211 read = 1;
1212 }
1213 } while (start != temp || !read);
1214
1215 if (n == 4) {
1216 /* The first EBR is stored at begin of the extended partition */
1217 struct pte *pe = self_pte(cxt, n);
1218
1219 assert(pe);
1220 pe->offset = l->ext_offset;
1221 } else if (n > 4) {
1222 /* The second (and another) EBR */
1223 struct pte *pe = self_pte(cxt, n);
1224
1225 assert(pe);
1226 pe->offset = start - cxt->first_lba;
1227 if (pe->offset == l->ext_offset) { /* must be corrected */
1228 pe->offset++;
1229 if (cxt->first_lba == 1)
1230 start++;
1231 }
1232 }
1233
1234 rc = find_last_free_sector_in_range(cxt, is_logical, start, limit, &stop);
1235 if (rc == -ENOSPC)
1236 fdisk_warnx(cxt, _("No free sectors available."));
1237 if (rc)
1238 return rc;
1239 limit = stop;
1240
1241 /*
1242 * Ask for last sector
1243 */
1244 if (fdisk_cround(cxt, start) == fdisk_cround(cxt, limit))
1245 stop = limit;
1246 else if (pa && pa->end_follow_default)
1247 stop = limit;
1248 else if (pa && fdisk_partition_has_size(pa)) {
1249 stop = start + pa->size;
1250 isrel = pa->size_explicit ? 0 : 1;
1251 if ((!isrel || !alignment_required(cxt)) && stop > start)
1252 stop -= 1;
1253 } else {
1254 /* ask user by dialog */
1255 for (;;) {
1256 if (!ask)
1257 ask = fdisk_new_ask();
1258 else
1259 fdisk_reset_ask(ask);
1260 if (!ask)
1261 return -ENOMEM;
1262 fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
1263
1264 if (fdisk_use_cylinders(cxt)) {
1265 fdisk_ask_set_query(ask, _("Last cylinder, +/-cylinders or +/-size{K,M,G,T,P}"));
1266 fdisk_ask_number_set_unit(ask,
1267 cxt->sector_size *
1268 fdisk_get_units_per_sector(cxt));
1269 } else {
1270 fdisk_ask_set_query(ask, _("Last sector, +/-sectors or +/-size{K,M,G,T,P}"));
1271 fdisk_ask_number_set_unit(ask,cxt->sector_size);
1272 }
1273
1274 fdisk_ask_number_set_low(ask, fdisk_cround(cxt, start));
1275 fdisk_ask_number_set_default(ask, fdisk_cround(cxt, limit));
1276 fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
1277 fdisk_ask_number_set_base(ask, fdisk_cround(cxt, start)); /* base for relative input */
1278 fdisk_ask_number_set_wrap_negative(ask, 1); /* wrap negative around high */
1279
1280 rc = fdisk_do_ask(cxt, ask);
1281 if (rc)
1282 goto done;
1283
1284 stop = fdisk_ask_number_get_result(ask);
1285 isrel = fdisk_ask_number_is_relative(ask);
1286 if (fdisk_use_cylinders(cxt)) {
1287 stop = stop * fdisk_get_units_per_sector(cxt) - 1;
1288 if (stop >limit)
1289 stop = limit;
1290 }
1291
1292 if (stop >= start && stop <= limit)
1293 break;
1294 fdisk_warnx(cxt, _("Value out of range."));
1295 }
1296 }
1297
1298 DBG(LABEL, ul_debug("DOS: raw stop: %ju [limit %ju]", (uintmax_t) stop, (uintmax_t) limit));
1299
1300 if (stop > limit)
1301 stop = limit;
1302
1303 if (isrel && stop - start < (cxt->grain / fdisk_get_sector_size(cxt))) {
1304 /* Don't try to be smart on very small partitions and don't align so small sizes */
1305 isrel = 0;
1306 if (stop > start)
1307 stop -= 1;
1308 DBG(LABEL, ul_debug("DOS: don't align end of tiny partition [start=%ju, stop=%ju, grain=%lu]",
1309 (uintmax_t)start, (uintmax_t)stop, cxt->grain));
1310 }
1311
1312 if (stop < limit && isrel && alignment_required(cxt)) {
1313 /* the last sector has not been exactly requested (but
1314 * defined by +size{K,M,G} convention), so be smart and
1315 * align the end of the partition. The next partition
1316 * will start at phy.block boundary.
1317 */
1318 stop = fdisk_align_lba_in_range(cxt, stop, start, limit);
1319 if (stop > start)
1320 stop -= 1;
1321 if (stop > limit)
1322 stop = limit;
1323 DBG(LABEL, ul_debug("DOS: aligned stop: %ju", (uintmax_t) stop));
1324 }
1325
1326 set_partition(cxt, n, 0, start, stop, sys, fdisk_partition_is_bootable(pa));
1327 if (n > 4) {
1328 struct pte *pe = self_pte(cxt, n);
1329
1330 assert(pe);
1331 set_partition(cxt, n - 1, 1, pe->offset, stop,
1332 MBR_DOS_EXTENDED_PARTITION, 0);
1333 }
1334
1335 /* report */
1336 {
1337 struct fdisk_parttype *t =
1338 fdisk_label_get_parttype_from_code(cxt->label, sys);
1339 fdisk_info_new_partition(cxt, n + 1, start, stop, t);
1340 fdisk_unref_parttype(t);
1341 }
1342
1343
1344 if (IS_EXTENDED(sys)) {
1345 struct pte *pen = self_pte(cxt, n);
1346
1347 assert(pen);
1348 l->ext_index = n;
1349 l->ext_offset = start;
1350 pen->ex_entry = p;
1351 }
1352
1353 fdisk_label_set_changed(cxt->label, 1);
1354 rc = 0;
1355 done:
1356 fdisk_unref_ask(ask);
1357 return rc;
1358 }
1359
1360 static int add_logical(struct fdisk_context *cxt,
1361 struct fdisk_partition *pa,
1362 size_t *partno)
1363 {
1364 struct pte *pe;
1365 int rc;
1366
1367 assert(cxt);
1368 assert(partno);
1369 assert(cxt->label);
1370 assert(self_label(cxt)->ext_offset);
1371
1372 DBG(LABEL, ul_debug("DOS: nparts max: %zu", cxt->label->nparts_max));
1373 pe = self_pte(cxt, cxt->label->nparts_max);
1374 assert(pe);
1375
1376 if (!pe->sectorbuffer) {
1377 pe->sectorbuffer = calloc(1, cxt->sector_size);
1378 if (!pe->sectorbuffer)
1379 return -ENOMEM;
1380 DBG(LABEL, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1381 cxt->label->nparts_max, pe->sectorbuffer));
1382 pe->private_sectorbuffer = 1;
1383 }
1384 pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
1385 pe->ex_entry = pe->pt_entry + 1;
1386 pe->offset = 0;
1387 partition_set_changed(cxt, cxt->label->nparts_max, 1);
1388
1389 cxt->label->nparts_max++;
1390
1391 /* this message makes sense only when we use extended/primary/logical
1392 * dialog. The dialog is disable for scripts, see dos_add_partition() */
1393 if (!cxt->script)
1394 fdisk_info(cxt, _("Adding logical partition %zu"),
1395 cxt->label->nparts_max);
1396 *partno = cxt->label->nparts_max - 1;
1397 rc = add_partition(cxt, *partno, pa);
1398
1399 if (rc) {
1400 /* reset on error */
1401 cxt->label->nparts_max--;
1402 pe->pt_entry = NULL;
1403 pe->ex_entry = NULL;
1404 pe->offset = 0;
1405 pe->changed = 0;
1406 }
1407
1408 return rc;
1409 }
1410
1411 static void check(struct fdisk_context *cxt, size_t n,
1412 unsigned int h, unsigned int s, unsigned int c,
1413 unsigned int start)
1414 {
1415 unsigned int total, real_s, real_c;
1416
1417 if (!is_dos_compatible(cxt))
1418 return;
1419
1420 real_s = sector(s) - 1;
1421 real_c = cylinder(s, c);
1422 total = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
1423
1424 if (!total)
1425 fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
1426 if (h >= cxt->geom.heads)
1427 fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
1428 "maximum %d"), n, h + 1, cxt->geom.heads);
1429 if (real_s >= cxt->geom.sectors)
1430 fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
1431 "maximum %llu"), n, s, cxt->geom.sectors);
1432 if (real_c >= cxt->geom.cylinders)
1433 fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
1434 "maximum %llu"),
1435 n, real_c + 1,
1436 cxt->geom.cylinders);
1437
1438 if (cxt->geom.cylinders <= 1024 && start != total)
1439 fdisk_warnx(cxt, _("Partition %zu: previous sectors %u "
1440 "disagrees with total %u"), n, start, total);
1441 }
1442
1443 /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1444 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1445 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1446 * Lubkin Oct. 1991). */
1447
1448 static void
1449 long2chs(struct fdisk_context *cxt, unsigned long ls,
1450 unsigned int *c, unsigned int *h, unsigned int *s) {
1451 int spc = cxt->geom.heads * cxt->geom.sectors;
1452
1453 *c = ls / spc;
1454 ls = ls % spc;
1455 *h = ls / cxt->geom.sectors;
1456 *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
1457 }
1458
1459 static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
1460 size_t partition)
1461 {
1462 unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
1463 unsigned int pec, peh, pes; /* physical ending c, h, s */
1464 unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
1465 unsigned int lec, leh, les; /* logical ending c, h, s */
1466
1467 if (!is_dos_compatible(cxt))
1468 return;
1469
1470 if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
1471 return; /* do not check extended partitions */
1472
1473 /* physical beginning c, h, s */
1474 pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
1475 pbh = p->bh;
1476 pbs = p->bs & 0x3f;
1477
1478 /* physical ending c, h, s */
1479 pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
1480 peh = p->eh;
1481 pes = p->es & 0x3f;
1482
1483 /* compute logical beginning (c, h, s) */
1484 long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
1485
1486 /* compute logical ending (c, h, s) */
1487 long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
1488
1489 /* Same physical / logical beginning? */
1490 if (cxt->geom.cylinders <= 1024
1491 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
1492 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
1493 "beginnings (non-Linux?): "
1494 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1495 partition + 1,
1496 pbc, pbh, pbs,
1497 lbc, lbh, lbs);
1498 }
1499
1500 /* Same physical / logical ending? */
1501 if (cxt->geom.cylinders <= 1024
1502 && (pec != lec || peh != leh || pes != les)) {
1503 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
1504 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1505 partition + 1,
1506 pec, peh, pes,
1507 lec, leh, les);
1508 }
1509
1510 /* Ending on cylinder boundary? */
1511 if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
1512 fdisk_warnx(cxt, _("Partition %zu: does not end on "
1513 "cylinder boundary."),
1514 partition + 1);
1515 }
1516 }
1517
1518 static void fill_bounds(struct fdisk_context *cxt,
1519 fdisk_sector_t *first, fdisk_sector_t *last)
1520 {
1521 size_t i;
1522 struct pte *pe = self_pte(cxt, 0);
1523 struct dos_partition *p;
1524
1525 assert(pe);
1526 for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
1527 p = pe->pt_entry;
1528 if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
1529 first[i] = SIZE_MAX;
1530 last[i] = 0;
1531 } else {
1532 first[i] = get_abs_partition_start(pe);
1533 last[i] = get_abs_partition_end(pe);
1534 }
1535 }
1536 }
1537
1538 static int dos_verify_disklabel(struct fdisk_context *cxt)
1539 {
1540 size_t i, j;
1541 fdisk_sector_t total = 1, n_sectors = cxt->total_sectors;
1542 fdisk_sector_t first[cxt->label->nparts_max],
1543 last[cxt->label->nparts_max];
1544 struct dos_partition *p;
1545 struct fdisk_dos_label *l = self_label(cxt);
1546
1547 assert(fdisk_is_label(cxt, DOS));
1548
1549 fill_bounds(cxt, first, last);
1550 for (i = 0; i < cxt->label->nparts_max; i++) {
1551 struct pte *pe = self_pte(cxt, i);
1552
1553 p = self_partition(cxt, i);
1554 if (p && is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
1555 check_consistency(cxt, p, i);
1556 assert(pe);
1557 if (get_abs_partition_start(pe) < first[i])
1558 fdisk_warnx(cxt, _(
1559 "Partition %zu: bad start-of-data."),
1560 i + 1);
1561
1562 check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
1563 total += last[i] + 1 - first[i];
1564
1565 if (i == 0)
1566 total += get_abs_partition_start(pe) - 1;
1567
1568 for (j = 0; j < i; j++) {
1569 if ((first[i] >= first[j] && first[i] <= last[j])
1570 || ((last[i] <= last[j] && last[i] >= first[j]))) {
1571
1572 fdisk_warnx(cxt, _("Partition %zu: "
1573 "overlaps partition %zu."),
1574 j + 1, i + 1);
1575
1576 total += first[i] >= first[j] ?
1577 first[i] : first[j];
1578 total -= last[i] <= last[j] ?
1579 last[i] : last[j];
1580 }
1581 }
1582 }
1583 }
1584
1585 if (l->ext_offset) {
1586 fdisk_sector_t e_last;
1587 struct pte *ext_pe = self_pte(cxt, l->ext_index);
1588
1589 assert(ext_pe);
1590 e_last = get_abs_partition_end(ext_pe);
1591
1592 for (i = 4; i < cxt->label->nparts_max; i++) {
1593 total++;
1594 p = self_partition(cxt, i);
1595 assert(p);
1596
1597 if (!p->sys_ind) {
1598 if (i != 4 || i + 1 < cxt->label->nparts_max)
1599 fdisk_warnx(cxt,
1600 _("Partition %zu: empty."),
1601 i + 1);
1602 } else if (first[i] < l->ext_offset
1603 || last[i] > e_last) {
1604
1605 fdisk_warnx(cxt, _("Logical partition %zu: "
1606 "not entirely in partition %zu."),
1607 i + 1, l->ext_index + 1);
1608 }
1609 }
1610 }
1611
1612 if (total > n_sectors)
1613 fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
1614 "than the maximum %llu."), total, n_sectors);
1615 else if (total < n_sectors)
1616 fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
1617 "sectors."), n_sectors - total, cxt->sector_size);
1618
1619 return 0;
1620 }
1621
1622 /*
1623 * Ask the user for new partition type information (logical, extended).
1624 * This function calls the actual partition adding logic - add_partition.
1625 *
1626 * API callback.
1627 */
1628 static int dos_add_partition(struct fdisk_context *cxt,
1629 struct fdisk_partition *pa,
1630 size_t *partno)
1631 {
1632 size_t i, free_primary = 0, free_sectors = 0;
1633 fdisk_sector_t last = 0, grain;
1634 int rc = 0;
1635 struct fdisk_dos_label *l;
1636 struct pte *ext_pe;
1637 size_t res = 0; /* partno */
1638
1639 assert(cxt);
1640 assert(cxt->label);
1641 assert(fdisk_is_label(cxt, DOS));
1642
1643 DBG(LABEL, ul_debug("DOS: new partition wanted"));
1644
1645 l = self_label(cxt);
1646 ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1647
1648 /*
1649 * partition template (@pa) based partitioning
1650 */
1651
1652 /* A) template specifies start within extended partition; add logical */
1653 if (pa && fdisk_partition_has_start(pa) && ext_pe
1654 && pa->start >= l->ext_offset
1655 && pa->start <= get_abs_partition_end(ext_pe)) {
1656 DBG(LABEL, ul_debug("DOS: pa template %p: add logical (by offset)", pa));
1657
1658 if (fdisk_partition_has_partno(pa) && fdisk_partition_get_partno(pa) < 4) {
1659 DBG(LABEL, ul_debug("DOS: pa template specifies partno<4 for logical partition"));
1660 return -EINVAL;
1661 }
1662 rc = add_logical(cxt, pa, &res);
1663 goto done;
1664
1665 /* B) template specifies start out of extended partition; add primary */
1666 } else if (pa && fdisk_partition_has_start(pa) && ext_pe) {
1667 DBG(LABEL, ul_debug("DOS: pa template %p: add primary (by offset)", pa));
1668
1669 if (fdisk_partition_has_partno(pa) && fdisk_partition_get_partno(pa) >= 4) {
1670 DBG(LABEL, ul_debug("DOS: pa template specifies partno>=4 for primary partition"));
1671 return -EINVAL;
1672 }
1673 if (ext_pe && pa->type && IS_EXTENDED(pa->type->code)) {
1674 fdisk_warnx(cxt, _("Extended partition already exists."));
1675 return -EINVAL;
1676 }
1677 rc = get_partition_unused_primary(cxt, pa, &res);
1678 if (rc == 0)
1679 rc = add_partition(cxt, res, pa);
1680 goto done;
1681
1682 /* C) template specifies start (or default), partno < 4; add primary */
1683 } else if (pa && (fdisk_partition_start_is_default(pa) || fdisk_partition_has_start(pa))
1684 && fdisk_partition_has_partno(pa)
1685 && pa->partno < 4) {
1686 DBG(LABEL, ul_debug("DOS: pa template %p: add primary (by partno)", pa));
1687
1688 if (ext_pe && pa->type && IS_EXTENDED(pa->type->code)) {
1689 fdisk_warnx(cxt, _("Extended partition already exists."));
1690 return -EINVAL;
1691 }
1692 rc = get_partition_unused_primary(cxt, pa, &res);
1693 if (rc == 0)
1694 rc = add_partition(cxt, res, pa);
1695 goto done;
1696
1697 /* D) template specifies start (or default), partno >= 4; add logical */
1698 } else if (pa && (fdisk_partition_start_is_default(pa) || fdisk_partition_has_start(pa))
1699 && fdisk_partition_has_partno(pa)
1700 && pa->partno >= 4) {
1701 DBG(LABEL, ul_debug("DOS: pa template %p: add logical (by partno)", pa));
1702
1703 if (!ext_pe) {
1704 fdisk_warnx(cxt, _("Extended partition does not exists. Failed to add logical partition."));
1705 return -EINVAL;
1706 } else if (fdisk_partition_has_start(pa)
1707 && pa->start < l->ext_offset
1708 && pa->start > get_abs_partition_end(ext_pe)) {
1709 DBG(LABEL, ul_debug("DOS: pa template specifies partno>=4, but start out of extended"));
1710 return -EINVAL;
1711 }
1712
1713 rc = add_logical(cxt, pa, &res);
1714 goto done;
1715 }
1716
1717 DBG(LABEL, ul_debug("DOS: dialog driven partitioning"));
1718 /* Note @pa may be still used for things like partition type, etc */
1719
1720 /* check if there is space for primary partition */
1721 grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
1722 last = cxt->first_lba;
1723
1724 for (i = 0; i < 4; i++) {
1725 struct dos_partition *p = self_partition(cxt, i);
1726
1727 assert(p);
1728 if (is_used_partition(p)) {
1729 fdisk_sector_t start = dos_partition_get_start(p);
1730 if (last + grain <= start)
1731 free_sectors = 1;
1732 last = start + dos_partition_get_size(p);
1733 } else
1734 free_primary++;
1735 }
1736 if (last + grain < cxt->total_sectors - 1)
1737 free_sectors = 1;
1738
1739 if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
1740 fdisk_info(cxt, _("The maximum number of partitions has "
1741 "been created."));
1742 return -EINVAL;
1743 }
1744
1745 if (!free_primary || !free_sectors) {
1746 DBG(LABEL, ul_debug("DOS: primary impossible, add logical"));
1747 if (l->ext_offset) {
1748 if (!pa || fdisk_partition_has_start(pa)) {
1749 /* See above case A); here we have start, but
1750 * out of extended partition */
1751 const char *msg;
1752 if (!free_primary)
1753 msg = _("All primary partitions are in use.");
1754 else
1755 msg = _("All space for primary partitions is in use.");
1756
1757 if (pa && fdisk_partition_has_start(pa)) {
1758 fdisk_warnx(cxt, msg);
1759 return -EINVAL;
1760 } else
1761 fdisk_info(cxt, msg);
1762 }
1763 rc = add_logical(cxt, pa, &res);
1764 } else {
1765 if (free_primary)
1766 fdisk_info(cxt, _("All space for primary partitions is in use."));
1767 else
1768 /* TRANSLATORS: Try to keep this within 80 characters. */
1769 fdisk_info(cxt, _("To create more partitions, first replace "
1770 "a primary with an extended partition."));
1771 return -EINVAL;
1772 }
1773 } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
1774 fdisk_info(cxt, _("All logical partitions are in use. "
1775 "Adding a primary partition."));
1776 rc = get_partition_unused_primary(cxt, pa, &res);
1777 if (rc == 0)
1778 rc = add_partition(cxt, res, pa);
1779 } else {
1780 char hint[BUFSIZ];
1781 struct fdisk_ask *ask;
1782 int c = 0;
1783
1784 /* the default layout for scripts is to create primary partitions */
1785 if (cxt->script || !fdisk_has_dialogs(cxt)) {
1786 rc = get_partition_unused_primary(cxt, pa, &res);
1787 if (rc == 0)
1788 rc = add_partition(cxt, res, pa);
1789 goto done;
1790 }
1791
1792 ask = fdisk_new_ask();
1793 if (!ask)
1794 return -ENOMEM;
1795 fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
1796 fdisk_ask_set_query(ask, _("Partition type"));
1797 fdisk_ask_menu_set_default(ask, free_primary == 1
1798 && !l->ext_offset ? 'e' : 'p');
1799 snprintf(hint, sizeof(hint),
1800 _("%zu primary, %d extended, %zu free"),
1801 4 - (l->ext_offset ? 1 : 0) - free_primary,
1802 l->ext_offset ? 1 : 0,
1803 free_primary);
1804
1805 fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
1806 if (!l->ext_offset)
1807 fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
1808 else
1809 fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
1810
1811 rc = fdisk_do_ask(cxt, ask);
1812 if (!rc)
1813 fdisk_ask_menu_get_result(ask, &c);
1814 fdisk_unref_ask(ask);
1815 if (rc)
1816 return rc;
1817
1818 if (c == 'p') {
1819 rc = get_partition_unused_primary(cxt, pa, &res);
1820 if (rc == 0)
1821 rc = add_partition(cxt, res, pa);
1822 goto done;
1823 } else if (c == 'l' && l->ext_offset) {
1824 rc = add_logical(cxt, pa, &res);
1825 goto done;
1826 } else if (c == 'e' && !l->ext_offset) {
1827 rc = get_partition_unused_primary(cxt, pa, &res);
1828 if (rc == 0) {
1829 struct fdisk_partition *xpa = NULL;
1830 struct fdisk_parttype *t;
1831
1832 t = fdisk_label_get_parttype_from_code(cxt->label,
1833 MBR_DOS_EXTENDED_PARTITION);
1834 if (!pa) {
1835 pa = xpa = fdisk_new_partition();
1836 if (!xpa)
1837 return -ENOMEM;
1838 }
1839 fdisk_partition_set_type(pa, t);
1840 rc = add_partition(cxt, res, pa);
1841 if (xpa) {
1842 fdisk_unref_partition(xpa);
1843 pa = NULL;
1844 }
1845 }
1846 goto done;
1847 } else
1848 fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
1849 }
1850 done:
1851 if (rc == 0) {
1852 cxt->label->nparts_cur++;
1853 if (partno)
1854 *partno = res;
1855 }
1856 return rc;
1857 }
1858
1859 static int write_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
1860 unsigned char *buf)
1861 {
1862 int rc;
1863
1864 rc = seek_sector(cxt, secno);
1865 if (rc != 0) {
1866 fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
1867 (uintmax_t) secno);
1868 return rc;
1869 }
1870
1871 DBG(LABEL, ul_debug("DOS: writing to sector %ju", (uintmax_t) secno));
1872
1873 if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
1874 return -errno;
1875 return 0;
1876 }
1877
1878 static int dos_write_disklabel(struct fdisk_context *cxt)
1879 {
1880 struct fdisk_dos_label *l = self_label(cxt);
1881 size_t i;
1882 int rc = 0, mbr_changed = 0;
1883
1884 assert(cxt);
1885 assert(cxt->label);
1886 assert(fdisk_is_label(cxt, DOS));
1887
1888 DBG(LABEL, ul_debug("DOS: write PT requested [label-changed: %d, non-pt-changed: %d]",
1889 cxt->label->changed, l->non_pt_changed));
1890
1891 mbr_changed = l->non_pt_changed;
1892
1893 /* MBR (primary partitions) */
1894 if (!mbr_changed) {
1895 for (i = 0; i < 4; i++) {
1896 struct pte *pe = self_pte(cxt, i);
1897
1898 assert(pe);
1899 if (pe->changed)
1900 mbr_changed = 1;
1901 }
1902 }
1903 if (mbr_changed) {
1904 DBG(LABEL, ul_debug("DOS: MBR changed, writing"));
1905 mbr_set_magic(cxt->firstsector);
1906 rc = write_sector(cxt, 0, cxt->firstsector);
1907 if (rc)
1908 goto done;
1909 }
1910
1911 if (cxt->label->nparts_max <= 4 && l->ext_offset) {
1912 /* we have empty extended partition, check if the partition has
1913 * been modified and then cleanup possible remaining EBR */
1914 struct pte *pe = self_pte(cxt, l->ext_index);
1915 unsigned char empty[512] = { 0 };
1916 fdisk_sector_t off = pe ? get_abs_partition_start(pe) : 0;
1917
1918 if (off && pe->changed) {
1919 mbr_set_magic(empty);
1920 write_sector(cxt, off, empty);
1921 }
1922 }
1923
1924 /* EBR (logical partitions) */
1925 for (i = 4; i < cxt->label->nparts_max; i++) {
1926 struct pte *pe = self_pte(cxt, i);
1927
1928 assert(pe);
1929 if (!pe->changed || !pe->offset || !pe->sectorbuffer)
1930 continue;
1931
1932 mbr_set_magic(pe->sectorbuffer);
1933 rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
1934 if (rc)
1935 goto done;
1936 }
1937
1938 done:
1939 return rc;
1940 }
1941
1942 static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
1943 const char **name, uint64_t *offset, size_t *size)
1944 {
1945 assert(cxt);
1946
1947 *name = NULL;
1948 *offset = 0;
1949 *size = 0;
1950
1951 switch (n) {
1952 case 0:
1953 *name = "MBR";
1954 *offset = 0;
1955 *size = 512;
1956 break;
1957 default:
1958 /* extended partitions */
1959 if ((size_t)n - 1 + 4 < cxt->label->nparts_max) {
1960 struct pte *pe = self_pte(cxt, n - 1 + 4);
1961
1962 assert(pe);
1963 assert(pe->private_sectorbuffer);
1964
1965 *name = "EBR";
1966 *offset = (uint64_t) pe->offset * cxt->sector_size;
1967 *size = 512;
1968 } else
1969 return 1;
1970 break;
1971 }
1972
1973 return 0;
1974 }
1975
1976 /*
1977 * Check whether partition entries are ordered by their starting positions.
1978 * Return 0 if OK. Return i if partition i should have been earlier.
1979 * Two separate checks: primary and logical partitions.
1980 */
1981 static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
1982 {
1983 size_t last_p_start_pos = 0, p_start_pos;
1984 size_t i, last_i = 0;
1985
1986 for (i = 0 ; i < cxt->label->nparts_max; i++) {
1987
1988 struct pte *pe = self_pte(cxt, i);
1989 struct dos_partition *p;
1990
1991 assert(pe);
1992 p = pe->pt_entry;
1993
1994 if (i == 4) {
1995 last_i = 4;
1996 last_p_start_pos = 0;
1997 }
1998 if (is_used_partition(p)) {
1999 p_start_pos = get_abs_partition_start(pe);
2000
2001 if (last_p_start_pos > p_start_pos) {
2002 if (prev)
2003 *prev = last_i;
2004 return i;
2005 }
2006
2007 last_p_start_pos = p_start_pos;
2008 last_i = i;
2009 }
2010 }
2011 return 0;
2012 }
2013
2014 static int dos_get_disklabel_item(struct fdisk_context *cxt, struct fdisk_labelitem *item)
2015 {
2016 int rc = 0;
2017
2018 assert(cxt);
2019 assert(cxt->label);
2020 assert(fdisk_is_label(cxt, DOS));
2021
2022 switch (item->id) {
2023 case FDISK_LABELITEM_ID:
2024 {
2025 unsigned int num = mbr_get_id(cxt->firstsector);
2026 item->name = _("Disk identifier");
2027 item->type = 's';
2028 if (asprintf(&item->data.str, "0x%08x", num) < 0)
2029 rc = -ENOMEM;
2030 break;
2031 }
2032 default:
2033 if (item->id < __FDISK_NLABELITEMS)
2034 rc = 1; /* unsupported generic item */
2035 else
2036 rc = 2; /* out of range */
2037 break;
2038 }
2039
2040 return rc;
2041
2042 }
2043
2044 static int dos_get_partition(struct fdisk_context *cxt, size_t n,
2045 struct fdisk_partition *pa)
2046 {
2047 struct dos_partition *p;
2048 struct pte *pe;
2049 struct fdisk_dos_label *lb;
2050
2051 assert(cxt);
2052 assert(pa);
2053 assert(cxt->label);
2054 assert(fdisk_is_label(cxt, DOS));
2055
2056 lb = self_label(cxt);
2057
2058 pe = self_pte(cxt, n);
2059 assert(pe);
2060
2061 p = pe->pt_entry;
2062 pa->used = !is_cleared_partition(p);
2063 if (!pa->used)
2064 return 0;
2065
2066 pa->type = dos_partition_parttype(cxt, p);
2067 pa->boot = p->boot_ind == ACTIVE_FLAG ? 1 : 0;
2068 pa->start = get_abs_partition_start(pe);
2069 pa->size = dos_partition_get_size(p);
2070 pa->container = lb->ext_offset && n == lb->ext_index;
2071
2072 if (n >= 4)
2073 pa->parent_partno = lb->ext_index;
2074
2075 if (p->boot_ind && asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
2076 return -ENOMEM;
2077
2078 /* start C/H/S */
2079 if (asprintf(&pa->start_chs, "%d/%d/%d",
2080 cylinder(p->bs, p->bc),
2081 p->bh,
2082 sector(p->bs)) < 0)
2083 return -ENOMEM;
2084
2085 /* end C/H/S */
2086 if (asprintf(&pa->end_chs, "%d/%d/%d",
2087 cylinder(p->es, p->ec),
2088 p->eh,
2089 sector(p->es)) < 0)
2090 return -ENOMEM;
2091
2092 return 0;
2093 }
2094
2095 static int has_logical(struct fdisk_context *cxt)
2096 {
2097 size_t i;
2098 struct fdisk_dos_label *l = self_label(cxt);
2099
2100 for (i = 4; i < cxt->label->nparts_max; i++) {
2101 if (l->ptes[i].pt_entry)
2102 return 1;
2103 }
2104 return 0;
2105 }
2106
2107 static int dos_set_partition(struct fdisk_context *cxt, size_t n,
2108 struct fdisk_partition *pa)
2109 {
2110 struct fdisk_dos_label *l;
2111 struct dos_partition *p;
2112 struct pte *pe;
2113 int orgtype;
2114 fdisk_sector_t start, size;
2115
2116 assert(cxt);
2117 assert(pa);
2118 assert(cxt->label);
2119 assert(fdisk_is_label(cxt, DOS));
2120
2121 if (n >= cxt->label->nparts_max)
2122 return -EINVAL;
2123
2124 l = self_label(cxt);
2125 p = self_partition(cxt, n);
2126
2127 pe = self_pte(cxt, n);
2128 if (!pe)
2129 return -EINVAL;
2130
2131 orgtype = p->sys_ind;
2132
2133 if (pa->type) {
2134 if (IS_EXTENDED(pa->type->code) && l->ext_offset && l->ext_index != n) {
2135 fdisk_warnx(cxt, _("Extended partition already exists."));
2136 return -EINVAL;
2137 }
2138
2139 if (!pa->type->code)
2140 fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
2141 "Having partitions of type 0 is probably unwise."));
2142
2143 if (IS_EXTENDED(p->sys_ind) && !IS_EXTENDED(pa->type->code) && has_logical(cxt)) {
2144 fdisk_warnx(cxt, _(
2145 "Cannot change type of the extended partition which is "
2146 "already used by logical partitions. Delete logical "
2147 "partitions first."));
2148 return -EINVAL;
2149 }
2150 }
2151
2152 FDISK_INIT_UNDEF(start);
2153 FDISK_INIT_UNDEF(size);
2154
2155 if (fdisk_partition_has_start(pa))
2156 start = pa->start;
2157 if (fdisk_partition_has_size(pa))
2158 size = pa->size;
2159
2160 if (!FDISK_IS_UNDEF(start) || !FDISK_IS_UNDEF(size)) {
2161 DBG(LABEL, ul_debug("DOS: resize partition"));
2162
2163 if (FDISK_IS_UNDEF(start))
2164 start = get_abs_partition_start(pe);
2165 if (FDISK_IS_UNDEF(size))
2166 size = dos_partition_get_size(p);
2167
2168 set_partition(cxt, n, 0, start, start + size - 1,
2169 pa->type ? pa->type->code : p->sys_ind,
2170 FDISK_IS_UNDEF(pa->boot) ?
2171 p->boot_ind == ACTIVE_FLAG :
2172 fdisk_partition_is_bootable(pa));
2173 } else {
2174 DBG(LABEL, ul_debug("DOS: keep size, modify properties"));
2175 if (pa->type)
2176 p->sys_ind = pa->type->code;
2177 if (!FDISK_IS_UNDEF(pa->boot))
2178 p->boot_ind = fdisk_partition_is_bootable(pa) ? ACTIVE_FLAG : 0;
2179 }
2180
2181 if (pa->type) {
2182 if (IS_EXTENDED(pa->type->code) && !IS_EXTENDED(orgtype)) {
2183 /* new extended partition - create a reference */
2184 l->ext_index = n;
2185 l->ext_offset = dos_partition_get_start(p);
2186 pe->ex_entry = p;
2187 } else if (IS_EXTENDED(orgtype)) {
2188 /* remove extended partition */
2189 cxt->label->nparts_max = 4;
2190 l->ptes[l->ext_index].ex_entry = NULL;
2191 l->ext_offset = 0;
2192 l->ext_index = 0;
2193 }
2194 }
2195
2196 partition_set_changed(cxt, n, 1);
2197 return 0;
2198 }
2199
2200 static void print_chain_of_logicals(struct fdisk_context *cxt)
2201 {
2202 size_t i;
2203 struct fdisk_dos_label *l = self_label(cxt);
2204
2205 fputc('\n', stdout);
2206
2207 for (i = 4; i < cxt->label->nparts_max; i++) {
2208 struct pte *pe = self_pte(cxt, i);
2209
2210 assert(pe);
2211 fprintf(stderr, "#%02zu EBR [%10ju], "
2212 "data[start=%10ju (%10ju), size=%10ju], "
2213 "link[start=%10ju (%10ju), size=%10ju]\n",
2214 i, (uintmax_t) pe->offset,
2215 /* data */
2216 (uintmax_t) dos_partition_get_start(pe->pt_entry),
2217 (uintmax_t) get_abs_partition_start(pe),
2218 (uintmax_t) dos_partition_get_size(pe->pt_entry),
2219 /* link */
2220 (uintmax_t) dos_partition_get_start(pe->ex_entry),
2221 (uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
2222 (uintmax_t) dos_partition_get_size(pe->ex_entry));
2223 }
2224 }
2225
2226 static int cmp_ebr_offsets(const void *a, const void *b)
2227 {
2228 const struct pte *ae = (const struct pte *) a,
2229 *be = (const struct pte *) b;
2230
2231 if (ae->offset == 0 && be->offset == 0)
2232 return 0;
2233 if (ae->offset == 0)
2234 return 1;
2235 if (be->offset == 0)
2236 return -1;
2237
2238 return cmp_numbers(ae->offset, be->offset);
2239 }
2240
2241 /*
2242 * Fix the chain of logicals.
2243 *
2244 * The function does not modify data partitions within EBR tables
2245 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
2246 * (pte->ex_entry) between EBR tables.
2247 *
2248 */
2249 static void fix_chain_of_logicals(struct fdisk_context *cxt)
2250 {
2251 struct fdisk_dos_label *l = self_label(cxt);
2252 struct pte *last;
2253 size_t i;
2254
2255 DBG(LABEL, print_chain_of_logicals(cxt));
2256
2257 /* Sort chain by EBR offsets */
2258 qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
2259 cmp_ebr_offsets);
2260
2261 again:
2262 /* Sort data partitions by start */
2263 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2264 struct pte *cur = self_pte(cxt, i),
2265 *nxt = self_pte(cxt, i + 1);
2266
2267 assert(cur);
2268 assert(nxt);
2269
2270 if (get_abs_partition_start(cur) >
2271 get_abs_partition_start(nxt)) {
2272
2273 struct dos_partition tmp = *cur->pt_entry;
2274 fdisk_sector_t cur_start = get_abs_partition_start(cur),
2275 nxt_start = get_abs_partition_start(nxt);
2276
2277 /* swap data partitions */
2278 *cur->pt_entry = *nxt->pt_entry;
2279 *nxt->pt_entry = tmp;
2280
2281 /* Recount starts according to EBR offsets, the absolute
2282 * address still has to be the same! */
2283 dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
2284 dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
2285
2286 partition_set_changed(cxt, i, 1);
2287 partition_set_changed(cxt, i + 1, 1);
2288 goto again;
2289 }
2290 }
2291
2292 /* Update EBR links */
2293 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2294 struct pte *cur = self_pte(cxt, i),
2295 *nxt = self_pte(cxt, i + 1);
2296
2297 assert(cur);
2298 assert(nxt);
2299
2300 fdisk_sector_t noff = nxt->offset - l->ext_offset,
2301 ooff = dos_partition_get_start(cur->ex_entry);
2302
2303 if (noff == ooff)
2304 continue;
2305
2306 DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2307 (uintmax_t) cur->offset,
2308 (uintmax_t) ooff, (uintmax_t) noff));
2309
2310 set_partition(cxt, i, 1, nxt->offset,
2311 get_abs_partition_end(nxt),
2312 MBR_DOS_EXTENDED_PARTITION, 0);
2313 }
2314
2315 /* always terminate the chain ! */
2316 last = self_pte(cxt, cxt->label->nparts_max - 1);
2317 if (last) {
2318 clear_partition(last->ex_entry);
2319 partition_set_changed(cxt, cxt->label->nparts_max - 1, 1);
2320 }
2321
2322 DBG(LABEL, print_chain_of_logicals(cxt));
2323 }
2324
2325 static int dos_reorder(struct fdisk_context *cxt)
2326 {
2327 struct pte *pei, *pek;
2328 size_t i,k;
2329
2330 if (!wrong_p_order(cxt, NULL)) {
2331 fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
2332 return 1;
2333 }
2334
2335 while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
2336 /* partition i should have come earlier, move it */
2337 /* We have to move data in the MBR */
2338 struct dos_partition *pi, *pk, *pe, pbuf;
2339 pei = self_pte(cxt, i);
2340 pek = self_pte(cxt, k);
2341
2342 assert(pei);
2343 assert(pek);
2344
2345 pe = pei->ex_entry;
2346 pei->ex_entry = pek->ex_entry;
2347 pek->ex_entry = pe;
2348
2349 pi = pei->pt_entry;
2350 pk = pek->pt_entry;
2351
2352 memmove(&pbuf, pi, sizeof(struct dos_partition));
2353 memmove(pi, pk, sizeof(struct dos_partition));
2354 memmove(pk, &pbuf, sizeof(struct dos_partition));
2355
2356 partition_set_changed(cxt, i, 1);
2357 partition_set_changed(cxt, k, 1);
2358 }
2359
2360 if (i)
2361 fix_chain_of_logicals(cxt);
2362
2363 return 0;
2364 }
2365
2366 /* TODO: use fdisk_set_partition() API */
2367 int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
2368 {
2369 struct pte *pe;
2370 struct dos_partition *p;
2371 unsigned int new, free_start, curr_start, last;
2372 uintmax_t res = 0;
2373 size_t x;
2374 int rc;
2375
2376 assert(cxt);
2377 assert(fdisk_is_label(cxt, DOS));
2378
2379 pe = self_pte(cxt, i);
2380 if (!pe)
2381 return -EINVAL;
2382
2383 p = pe->pt_entry;
2384
2385 if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
2386 fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
2387 return 0;
2388 }
2389
2390 /* the default start is at the second sector of the disk or at the
2391 * second sector of the extended partition
2392 */
2393 free_start = pe->offset ? pe->offset + 1 : 1;
2394
2395 curr_start = get_abs_partition_start(pe);
2396
2397 /* look for a free space before the current start of the partition */
2398 for (x = 0; x < cxt->label->nparts_max; x++) {
2399 unsigned int end;
2400 struct pte *prev_pe = self_pte(cxt, x);
2401 struct dos_partition *prev_p;
2402
2403 assert(prev_pe);
2404
2405 prev_p = prev_pe->pt_entry;
2406 if (!prev_p)
2407 continue;
2408 end = get_abs_partition_start(prev_pe)
2409 + dos_partition_get_size(prev_p);
2410
2411 if (is_used_partition(prev_p) &&
2412 end > free_start && end <= curr_start)
2413 free_start = end;
2414 }
2415
2416 last = get_abs_partition_end(pe);
2417
2418 rc = fdisk_ask_number(cxt, free_start, curr_start, last,
2419 _("New beginning of data"), &res);
2420 if (rc)
2421 return rc;
2422
2423 new = res - pe->offset;
2424
2425 if (new != dos_partition_get_size(p)) {
2426 unsigned int sects = dos_partition_get_size(p)
2427 + dos_partition_get_start(p) - new;
2428
2429 dos_partition_set_size(p, sects);
2430 dos_partition_set_start(p, new);
2431
2432 partition_set_changed(cxt, i, 1);
2433 }
2434
2435 return rc;
2436 }
2437
2438 static int dos_partition_is_used(
2439 struct fdisk_context *cxt,
2440 size_t i)
2441 {
2442 struct dos_partition *p;
2443
2444 assert(cxt);
2445 assert(cxt->label);
2446 assert(fdisk_is_label(cxt, DOS));
2447
2448 if (i >= cxt->label->nparts_max)
2449 return 0;
2450
2451 p = self_partition(cxt, i);
2452
2453 return p && !is_cleared_partition(p);
2454 }
2455
2456 static int dos_toggle_partition_flag(
2457 struct fdisk_context *cxt,
2458 size_t i,
2459 unsigned long flag)
2460 {
2461 struct dos_partition *p;
2462
2463 assert(cxt);
2464 assert(cxt->label);
2465 assert(fdisk_is_label(cxt, DOS));
2466
2467 if (i >= cxt->label->nparts_max)
2468 return -EINVAL;
2469
2470 p = self_partition(cxt, i);
2471
2472 switch (flag) {
2473 case DOS_FLAG_ACTIVE:
2474 if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
2475 fdisk_warnx(cxt, _("Partition %zu: is an extended "
2476 "partition."), i + 1);
2477
2478 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
2479 partition_set_changed(cxt, i, 1);
2480 fdisk_info(cxt, p->boot_ind ?
2481 _("The bootable flag on partition %zu is enabled now.") :
2482 _("The bootable flag on partition %zu is disabled now."),
2483 i + 1);
2484 break;
2485 default:
2486 return 1;
2487 }
2488
2489 return 0;
2490 }
2491
2492 static const struct fdisk_field dos_fields[] =
2493 {
2494 /* basic */
2495 { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
2496 { FDISK_FIELD_BOOT, N_("Boot"), 1, 0 },
2497 { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
2498 { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
2499 { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
2500 { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
2501 { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
2502 { FDISK_FIELD_TYPEID, N_("Id"), 2, FDISK_FIELDFL_NUMBER },
2503 { FDISK_FIELD_TYPE, N_("Type"), 0.1, 0 },
2504
2505 /* expert mode */
2506 { FDISK_FIELD_SADDR, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2507 { FDISK_FIELD_EADDR, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2508 { FDISK_FIELD_ATTR, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL }
2509
2510 };
2511
2512 static const struct fdisk_label_operations dos_operations =
2513 {
2514 .probe = dos_probe_label,
2515 .write = dos_write_disklabel,
2516 .verify = dos_verify_disklabel,
2517 .create = dos_create_disklabel,
2518 .locate = dos_locate_disklabel,
2519 .get_item = dos_get_disklabel_item,
2520 .set_id = dos_set_disklabel_id,
2521
2522 .get_part = dos_get_partition,
2523 .set_part = dos_set_partition,
2524 .add_part = dos_add_partition,
2525 .del_part = dos_delete_partition,
2526 .reorder = dos_reorder,
2527
2528 .part_toggle_flag = dos_toggle_partition_flag,
2529 .part_is_used = dos_partition_is_used,
2530
2531 .reset_alignment = dos_reset_alignment,
2532
2533 .deinit = dos_deinit,
2534 };
2535
2536 /*
2537 * allocates DOS in-memory stuff
2538 */
2539 struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt __attribute__ ((__unused__)))
2540 {
2541 struct fdisk_label *lb;
2542 struct fdisk_dos_label *dos;
2543
2544 dos = calloc(1, sizeof(*dos));
2545 if (!dos)
2546 return NULL;
2547
2548 /* initialize generic part of the driver */
2549 lb = (struct fdisk_label *) dos;
2550 lb->name = "dos";
2551 lb->id = FDISK_DISKLABEL_DOS;
2552 lb->op = &dos_operations;
2553 lb->parttypes = dos_parttypes;
2554 lb->nparttypes = ARRAY_SIZE(dos_parttypes) - 1;
2555 lb->fields = dos_fields;
2556 lb->nfields = ARRAY_SIZE(dos_fields);
2557
2558 lb->geom_min.sectors = 1;
2559 lb->geom_min.heads = 1;
2560 lb->geom_min.cylinders = 1;
2561
2562 lb->geom_max.sectors = 63;
2563 lb->geom_max.heads = 255;
2564 lb->geom_max.cylinders = 1048576;
2565
2566 return lb;
2567 }
2568
2569 /**
2570 * fdisk_dos_enable_compatible:
2571 * @lb: DOS label (see fdisk_get_label())
2572 * @enable: 0 or 1
2573 *
2574 * Enables deprecated DOS compatible mode, in this mode library checks for
2575 * cylinders boundary, cases about CHS addressing and another obscure things.
2576 *
2577 * Returns: 0 on success, <0 on error.
2578 */
2579 int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
2580 {
2581 struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
2582
2583 if (!lb)
2584 return -EINVAL;
2585
2586 dos->compatible = enable;
2587 if (enable)
2588 lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
2589 return 0;
2590 }
2591
2592 /**
2593 * fdisk_dos_is_compatible:
2594 * @lb: DOS label
2595 *
2596 * Returns: 0 if DOS compatibility disabled, 1 if enabled
2597 */
2598 int fdisk_dos_is_compatible(struct fdisk_label *lb)
2599 {
2600 return ((struct fdisk_dos_label *) lb)->compatible;
2601 }