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