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