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