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