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