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