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