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