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