]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libfdisk/src/dos.c
libfdisk: assert if self_pte() returns NULL
[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
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;
834b3d07 672 id = strtoul(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
cc0c4e56 910
77d6a70a 911static int get_start_from_user( struct fdisk_context *cxt,
0073a4cf
KZ
912 fdisk_sector_t *start,
913 fdisk_sector_t low,
914 fdisk_sector_t dflt,
915 fdisk_sector_t limit,
77d6a70a 916 struct fdisk_partition *pa)
9dea2923 917{
77d6a70a
KZ
918 assert(start);
919
9e930041 920 /* try to use template from 'pa' */
77d6a70a
KZ
921 if (pa && pa->start_follow_default)
922 *start = dflt;
923
ecf40cda 924 else if (pa && fdisk_partition_has_start(pa)) {
88141067 925 DBG(LABEL, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
20f878fe 926 (uintmax_t) pa->start, (uintmax_t) low, (uintmax_t) limit));
77d6a70a 927 *start = pa->start;
20f878fe 928 if (*start < low || *start > limit) {
a3e37fa7
KZ
929 fdisk_warnx(cxt, _("Start sector %ju out of range."),
930 (uintmax_t) *start);
77d6a70a 931 return -ERANGE;
20f878fe 932 }
77d6a70a
KZ
933 } else {
934 /* ask user by dialog */
935 struct fdisk_ask *ask = fdisk_new_ask();
936 int rc;
937
938 if (!ask)
939 return -ENOMEM;
940 fdisk_ask_set_query(ask,
6a632136 941 fdisk_use_cylinders(cxt) ?
77d6a70a
KZ
942 _("First cylinder") : _("First sector"));
943 fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
944 fdisk_ask_number_set_low(ask, fdisk_cround(cxt, low));
945 fdisk_ask_number_set_default(ask, fdisk_cround(cxt, dflt));
946 fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
947
948 rc = fdisk_do_ask(cxt, ask);
949 *start = fdisk_ask_number_get_result(ask);
a3d83488 950 fdisk_unref_ask(ask);
77d6a70a
KZ
951 if (rc)
952 return rc;
6a632136 953 if (fdisk_use_cylinders(cxt)) {
77d6a70a 954 *start = (*start - 1)
6a632136 955 * fdisk_get_units_per_sector(cxt);
77d6a70a
KZ
956 if (*start < low)
957 *start = low;
958 }
959 }
960
c3bc7483 961 DBG(LABEL, ul_debug("DOS: start is %ju", (uintmax_t) *start));
77d6a70a
KZ
962 return 0;
963}
964
adea904a
KZ
965static int find_last_free_sector_in_range(
966 struct fdisk_context *cxt,
967 int logical,
968 fdisk_sector_t begin,
969 fdisk_sector_t end,
970 fdisk_sector_t *result)
333c3761 971{
adea904a
KZ
972 int last_moved;
973 fdisk_sector_t last = end;
974
975 do {
976 size_t i = logical ? 4 : 0;
977
978 last_moved = 0;
979 for ( ; i < cxt->label->nparts_max; i++) {
980 struct pte *pe = self_pte(cxt, i);
620851e0
SK
981
982 assert(pe);
adea904a
KZ
983 fdisk_sector_t p_start = get_abs_partition_start(pe);
984 fdisk_sector_t p_end = get_abs_partition_end(pe);
985
986 if (is_cleared_partition(pe->pt_entry))
987 continue;
988
989 /* count EBR and begin of the logical partition as used area */
990 if (pe->offset)
991 p_start -= cxt->first_lba;
992
993 if (last >= p_start && last <= p_end) {
994 last = p_start - 1;
995 last_moved = 1;
996
997 if (last < begin) {
998 DBG(LABEL, ul_debug("DOS: last free out of range <%ju,%ju>: %ju",
999 (uintmax_t) begin, (uintmax_t) end, (uintmax_t) last));
1000
1001 return -ENOSPC;
1002 }
1003 }
1004 }
1005 } while (last_moved == 1);
1006
1007 DBG(LABEL, ul_debug("DOS: last unused sector in range <%ju,%ju>: %ju",
1008 (uintmax_t) begin, (uintmax_t) end, (uintmax_t) last));
1009
1010 *result = last;
1011 return 0;
1012}
1013
1014static int find_first_free_sector_in_range(
1015 struct fdisk_context *cxt,
1016 int logical,
1017 fdisk_sector_t begin,
1018 fdisk_sector_t end,
1019 fdisk_sector_t *result)
1020{
1021 int first_moved = 0;
1022 fdisk_sector_t first = begin;
1023
1024 do {
1025 size_t i = logical ? 4 : 0;
1026
1027 first_moved = 0;
1028 for (; i < cxt->label->nparts_max; i++) {
1029 struct pte *pe = self_pte(cxt, i);
620851e0
SK
1030
1031 assert(pe);
adea904a
KZ
1032 fdisk_sector_t p_start = get_abs_partition_start(pe);
1033 fdisk_sector_t p_end = get_abs_partition_end(pe);
1034
1035 if (is_cleared_partition(pe->pt_entry))
1036 continue;
1037 /* count EBR and begin of the logical partition as used area */
1038 if (pe->offset)
1039 p_start -= cxt->first_lba;
1040 if (first < p_start)
1041 continue;
1042 if (first <= p_end) {
1043 first = p_end + 1 + (logical ? cxt->first_lba : 0);
1044 first_moved = 1;
1045
1046 if (first > end) {
1047 DBG(LABEL, ul_debug("DOS: first free out of range <%ju,%ju>: %ju",
1048 (uintmax_t) begin, (uintmax_t) end, (uintmax_t) first));
1049 return -ENOSPC;
1050 }
1051 }
1052 }
1053 } while (first_moved == 1);
1054
1055 DBG(LABEL, ul_debug("DOS: first unused sector in range <%ju,%ju>: %ju",
1056 (uintmax_t) begin, (uintmax_t) end, (uintmax_t) first));
1057 *result = first;
1058 return 0;
1059}
333c3761 1060
adea904a
KZ
1061static int get_disk_ranges(struct fdisk_context *cxt, int logical,
1062 fdisk_sector_t *first, fdisk_sector_t *last)
1063{
1064 if (logical) {
333c3761
KZ
1065 /* logical partitions */
1066 struct fdisk_dos_label *l = self_label(cxt);
1067 struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1068
1069 if (!ext_pe)
adea904a
KZ
1070 return -EINVAL;
1071
1072 *first = l->ext_offset + cxt->first_lba;
1073 *last = get_abs_partition_end(ext_pe);
1074
333c3761
KZ
1075 } else {
1076 /* primary partitions */
1077 if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
adea904a 1078 *last = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
333c3761 1079 else
adea904a 1080 *last = cxt->total_sectors - 1;
333c3761 1081
adea904a
KZ
1082 if (*last > UINT_MAX)
1083 *last = UINT_MAX;
1084 *first = cxt->first_lba;
333c3761
KZ
1085 }
1086
adea904a 1087 return 0;
333c3761
KZ
1088}
1089
adea904a 1090static int find_last_free_sector(struct fdisk_context *cxt, int logical, fdisk_sector_t *result)
333c3761 1091{
adea904a
KZ
1092 fdisk_sector_t first, last;
1093 int rc;
333c3761 1094
adea904a
KZ
1095 rc = get_disk_ranges(cxt, logical, &first, &last);
1096 if (rc)
1097 return rc;
333c3761 1098
adea904a
KZ
1099 return find_last_free_sector_in_range(cxt, logical, first, last, result);
1100}
1101
1102static int find_first_free_sector(struct fdisk_context *cxt,
1103 int logical,
1104 fdisk_sector_t start,
1105 fdisk_sector_t *result)
1106{
1107 fdisk_sector_t first, last;
1108 int rc;
1109
1110 rc = get_disk_ranges(cxt, logical, &first, &last);
1111 if (rc)
1112 return rc;
333c3761 1113
adea904a 1114 return find_first_free_sector_in_range(cxt, logical, start, last, result);
333c3761
KZ
1115}
1116
77d6a70a
KZ
1117static int add_partition(struct fdisk_context *cxt, size_t n,
1118 struct fdisk_partition *pa)
1119{
adea904a 1120 int sys, read = 0, rc, isrel = 0, is_logical;
fcea064b 1121 struct fdisk_dos_label *l = self_label(cxt);
af0df606 1122 struct dos_partition *p = self_partition(cxt, n);
1b92d076 1123 struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
7c43fd23
KZ
1124 struct fdisk_ask *ask = NULL;
1125
adea904a 1126 fdisk_sector_t start, stop = 0, limit, temp;
9dea2923 1127
88141067 1128 DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
9c825fa0 1129
a745611d 1130 sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
adea904a 1131 is_logical = n >= 4;
ed470672 1132
b2b52925 1133 if (p && is_used_partition(p)) {
829f4206 1134 fdisk_warnx(cxt, _("Partition %zu is already defined. "
e39966c6 1135 "Delete it before re-adding it."),
829f4206 1136 n + 1);
8254c3a5 1137 return -EINVAL;
9dea2923 1138 }
333c3761 1139
adea904a
KZ
1140 rc = find_last_free_sector(cxt, is_logical, &limit);
1141 if (rc == -ENOSPC)
1142 fdisk_warnx(cxt, _("No free sectors available."));
1143 if (rc)
1144 return rc;
1145
1146 if (!is_logical) {
aa36c2cf 1147 if (cxt->parent && fdisk_is_label(cxt->parent, GPT))
433d05ff 1148 start = 1; /* Bad boy modifies hybrid MBR */
bcdeacd7 1149 else {
ecf40cda
KZ
1150 if (cxt->script && pa && fdisk_partition_has_start(pa)
1151 && pa->start < cxt->first_lba
bcdeacd7
KZ
1152 && pa->start >= 1)
1153 fdisk_set_first_lba(cxt, 1);
1154
433d05ff 1155 start = cxt->first_lba;
bcdeacd7 1156 }
9dea2923 1157 } else {
1b92d076 1158 assert(ext_pe);
bcdeacd7 1159
ecf40cda
KZ
1160 if (cxt->script && pa && fdisk_partition_has_start(pa)
1161 && pa->start >= l->ext_offset
bcdeacd7
KZ
1162 && pa->start < l->ext_offset + cxt->first_lba)
1163 fdisk_set_first_lba(cxt, 1);
1164
1165 start = l->ext_offset + cxt->first_lba;
9dea2923 1166 }
333c3761 1167
0e07540d
KZ
1168 /*
1169 * Ask for first sector
1170 */
9dea2923 1171 do {
0073a4cf 1172 fdisk_sector_t dflt, aligned;
9dea2923
DB
1173
1174 temp = start;
adea904a
KZ
1175
1176 rc = find_first_free_sector(cxt, is_logical, start, &dflt);
1177 if (rc == -ENOSPC)
1178 fdisk_warnx(cxt, _("No free sectors available."));
1179 if (rc)
1180 return rc;
1181 start = dflt;
9dea2923 1182
ecf40cda 1183 if (n >= 4 && pa && fdisk_partition_has_start(pa) && cxt->script
bcdeacd7
KZ
1184 && cxt->first_lba > 1
1185 && temp == start - cxt->first_lba) {
1186 fdisk_set_first_lba(cxt, 1);
1187 start = pa->start;
1188 }
1189
9dea2923
DB
1190 /* the default sector should be aligned and unused */
1191 do {
9475cc78 1192 aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
adea904a 1193 find_first_free_sector(cxt, is_logical, aligned, &dflt);
9dea2923
DB
1194 } while (dflt != aligned && dflt > aligned && dflt < limit);
1195
1196 if (dflt >= limit)
1197 dflt = start;
1198 if (start > limit)
1199 break;
6a632136 1200 if (start >= temp + fdisk_get_units_per_sector(cxt)
fcea064b 1201 && read) {
ac1a559a 1202 fdisk_info(cxt, _("Sector %llu is already allocated."),
fcea064b 1203 temp);
9dea2923
DB
1204 temp = start;
1205 read = 0;
ecf40cda
KZ
1206 if (pa && (fdisk_partition_has_start(pa) ||
1207 pa->start_follow_default))
3186f4a9 1208 break;
9dea2923 1209 }
0e07540d 1210
9dea2923 1211 if (!read && start == temp) {
77d6a70a 1212 rc = get_start_from_user(cxt, &start, temp, dflt, limit, pa);
0e07540d
KZ
1213 if (rc)
1214 return rc;
9dea2923
DB
1215 read = 1;
1216 }
1217 } while (start != temp || !read);
0e07540d 1218
613596b3
KZ
1219 if (n == 4) {
1220 /* The first EBR is stored at begin of the extended partition */
1221 struct pte *pe = self_pte(cxt, n);
613596b3 1222
b2b52925
KZ
1223 assert(pe);
1224 pe->offset = l->ext_offset;
613596b3
KZ
1225 } else if (n > 4) {
1226 /* The second (and another) EBR */
af0df606 1227 struct pte *pe = self_pte(cxt, n);
9dea2923 1228
b2b52925 1229 assert(pe);
cf3808e4 1230 pe->offset = start - cxt->first_lba;
fcea064b 1231 if (pe->offset == l->ext_offset) { /* must be corrected */
9dea2923 1232 pe->offset++;
cf3808e4 1233 if (cxt->first_lba == 1)
9dea2923
DB
1234 start++;
1235 }
1236 }
1237
adea904a
KZ
1238 rc = find_last_free_sector_in_range(cxt, is_logical, start, limit, &stop);
1239 if (rc == -ENOSPC)
eed0e7b9 1240 fdisk_warnx(cxt, _("No free sectors available."));
adea904a
KZ
1241 if (rc)
1242 return rc;
1243 limit = stop;
77d6a70a
KZ
1244
1245 /*
1246 * Ask for last sector
1247 */
1248 if (fdisk_cround(cxt, start) == fdisk_cround(cxt, limit))
1249 stop = limit;
1250 else if (pa && pa->end_follow_default)
9dea2923 1251 stop = limit;
ecf40cda 1252 else if (pa && fdisk_partition_has_size(pa)) {
7c43fd23 1253 stop = start + pa->size;
bcdeacd7 1254 isrel = pa->size_explicit ? 0 : 1;
980cc4a1 1255 if ((!isrel || !alignment_required(cxt)) && stop > start)
7c43fd23 1256 stop -= 1;
9dea2923 1257 } else {
77d6a70a 1258 /* ask user by dialog */
7c43fd23
KZ
1259 for (;;) {
1260 if (!ask)
1261 ask = fdisk_new_ask();
1262 else
1263 fdisk_reset_ask(ask);
1264 if (!ask)
1265 return -ENOMEM;
7c43fd23
KZ
1266 fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
1267
1268 if (fdisk_use_cylinders(cxt)) {
757cefbb 1269 fdisk_ask_set_query(ask, _("Last cylinder, +/-cylinders or +/-size{K,M,G,T,P}"));
7c43fd23
KZ
1270 fdisk_ask_number_set_unit(ask,
1271 cxt->sector_size *
1272 fdisk_get_units_per_sector(cxt));
1273 } else {
757cefbb 1274 fdisk_ask_set_query(ask, _("Last sector, +/-sectors or +/-size{K,M,G,T,P}"));
7c43fd23
KZ
1275 fdisk_ask_number_set_unit(ask,cxt->sector_size);
1276 }
9dea2923 1277
7c43fd23
KZ
1278 fdisk_ask_number_set_low(ask, fdisk_cround(cxt, start));
1279 fdisk_ask_number_set_default(ask, fdisk_cround(cxt, limit));
1280 fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
1281 fdisk_ask_number_set_base(ask, fdisk_cround(cxt, start)); /* base for relative input */
757cefbb 1282 fdisk_ask_number_set_wrap_negative(ask, 1); /* wrap negative around high */
9dea2923 1283
7c43fd23
KZ
1284 rc = fdisk_do_ask(cxt, ask);
1285 if (rc)
1286 goto done;
0e07540d 1287
7c43fd23
KZ
1288 stop = fdisk_ask_number_get_result(ask);
1289 isrel = fdisk_ask_number_is_relative(ask);
535748a6
KZ
1290 if (fdisk_use_cylinders(cxt)) {
1291 stop = stop * fdisk_get_units_per_sector(cxt) - 1;
1292 if (stop >limit)
1293 stop = limit;
1294 }
7c43fd23 1295
8c73e509
KZ
1296 if (stop >= start && stop <= limit)
1297 break;
8c73e509 1298 fdisk_warnx(cxt, _("Value out of range."));
7c43fd23 1299 }
77d6a70a
KZ
1300 }
1301
980cc4a1 1302 DBG(LABEL, ul_debug("DOS: raw stop: %ju [limit %ju]", (uintmax_t) stop, (uintmax_t) limit));
bcdeacd7 1303
77d6a70a
KZ
1304 if (stop > limit)
1305 stop = limit;
1306
d527d2dd 1307 if (isrel && stop - start < (cxt->grain / fdisk_get_sector_size(cxt))) {
7c43fd23 1308 /* Don't try to be smart on very small partitions and don't align so small sizes */
68fe4b28 1309 isrel = 0;
09a4ca5e
KZ
1310 if (stop > start)
1311 stop -= 1;
980cc4a1 1312 DBG(LABEL, ul_debug("DOS: don't align end of tiny partition [start=%ju, stop=%ju, grain=%lu]",
fdbd7bb9 1313 (uintmax_t)start, (uintmax_t)stop, cxt->grain));
d527d2dd 1314 }
68fe4b28 1315
74ce680a
SK
1316 if (stop < limit && isrel && alignment_required(cxt)) {
1317 /* the last sector has not been exactly requested (but
1318 * defined by +size{K,M,G} convention), so be smart and
1319 * align the end of the partition. The next partition
1320 * will start at phy.block boundary.
1321 */
1322 stop = fdisk_align_lba_in_range(cxt, stop, start, limit);
1323 if (stop > start)
1324 stop -= 1;
1325 if (stop > limit)
1326 stop = limit;
1327 DBG(LABEL, ul_debug("DOS: aligned stop: %ju", (uintmax_t) stop));
9dea2923
DB
1328 }
1329
4d156d92 1330 set_partition(cxt, n, 0, start, stop, sys, fdisk_partition_is_bootable(pa));
af0df606
KZ
1331 if (n > 4) {
1332 struct pte *pe = self_pte(cxt, n);
b2b52925
KZ
1333
1334 assert(pe);
bb58112e 1335 set_partition(cxt, n - 1, 1, pe->offset, stop,
7bc31ada 1336 MBR_DOS_EXTENDED_PARTITION, 0);
af0df606 1337 }
9dea2923 1338
0ecf3ab5
KZ
1339 /* report */
1340 {
1341 struct fdisk_parttype *t =
1342 fdisk_label_get_parttype_from_code(cxt->label, sys);
1343 fdisk_info_new_partition(cxt, n + 1, start, stop, t);
dfc6db2a 1344 fdisk_unref_parttype(t);
0ecf3ab5
KZ
1345 }
1346
1347
bb58112e 1348 if (IS_EXTENDED(sys)) {
af0df606 1349 struct pte *pen = self_pte(cxt, n);
9dea2923 1350
b2b52925 1351 assert(pen);
fcea064b 1352 l->ext_index = n;
613596b3 1353 l->ext_offset = start;
6864f168 1354 pen->ex_entry = p;
9dea2923 1355 }
8254c3a5 1356
bddd84e7 1357 fdisk_label_set_changed(cxt->label, 1);
7c43fd23 1358 rc = 0;
7c43fd23
KZ
1359done:
1360 fdisk_unref_ask(ask);
1361 return rc;
9dea2923
DB
1362}
1363
c3bc7483
KZ
1364static int add_logical(struct fdisk_context *cxt,
1365 struct fdisk_partition *pa,
1366 size_t *partno)
9dea2923 1367{
613596b3 1368 struct pte *pe;
28d79d9b 1369 int rc;
af0df606 1370
e3661531 1371 assert(cxt);
c3bc7483 1372 assert(partno);
e3661531 1373 assert(cxt->label);
613596b3 1374 assert(self_label(cxt)->ext_offset);
e3661531 1375
613596b3
KZ
1376 DBG(LABEL, ul_debug("DOS: nparts max: %zu", cxt->label->nparts_max));
1377 pe = self_pte(cxt, cxt->label->nparts_max);
b2b52925 1378 assert(pe);
af0df606 1379
613596b3 1380 if (!pe->sectorbuffer) {
af0df606
KZ
1381 pe->sectorbuffer = calloc(1, cxt->sector_size);
1382 if (!pe->sectorbuffer)
1383 return -ENOMEM;
613596b3
KZ
1384 DBG(LABEL, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1385 cxt->label->nparts_max, pe->sectorbuffer));
ddec0e40 1386 pe->private_sectorbuffer = 1;
9dea2923 1387 }
613596b3
KZ
1388 pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
1389 pe->ex_entry = pe->pt_entry + 1;
1390 pe->offset = 0;
1391 partition_set_changed(cxt, cxt->label->nparts_max, 1);
1392
1393 cxt->label->nparts_max++;
1394
a536cf71
KZ
1395 /* this message makes sense only when we use extended/primary/logical
1396 * dialog. The dialog is disable for scripts, see dos_add_partition() */
1397 if (!cxt->script)
1398 fdisk_info(cxt, _("Adding logical partition %zu"),
1399 cxt->label->nparts_max);
c3bc7483 1400 *partno = cxt->label->nparts_max - 1;
28d79d9b
KZ
1401 rc = add_partition(cxt, *partno, pa);
1402
1403 if (rc) {
1404 /* reset on error */
1405 cxt->label->nparts_max--;
1406 pe->pt_entry = NULL;
1407 pe->ex_entry = NULL;
1408 pe->offset = 0;
1409 pe->changed = 0;
1410 }
1411
1412 return rc;
9dea2923
DB
1413}
1414
e3661531 1415static void check(struct fdisk_context *cxt, size_t n,
5dfca634
KZ
1416 unsigned int h, unsigned int s, unsigned int c,
1417 unsigned int start)
1418{
1419 unsigned int total, real_s, real_c;
1420
63fb717d
KZ
1421 if (!is_dos_compatible(cxt))
1422 return;
1423
5dfca634
KZ
1424 real_s = sector(s) - 1;
1425 real_c = cylinder(s, c);
9bb8caff 1426 total = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
fcea064b 1427
5dfca634 1428 if (!total)
829f4206 1429 fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
5dfca634 1430 if (h >= cxt->geom.heads)
829f4206 1431 fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
fcea064b 1432 "maximum %d"), n, h + 1, cxt->geom.heads);
5dfca634 1433 if (real_s >= cxt->geom.sectors)
829f4206 1434 fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
fcea064b 1435 "maximum %llu"), n, s, cxt->geom.sectors);
5dfca634 1436 if (real_c >= cxt->geom.cylinders)
829f4206 1437 fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
fcea064b
KZ
1438 "maximum %llu"),
1439 n, real_c + 1,
1440 cxt->geom.cylinders);
1441
5dfca634 1442 if (cxt->geom.cylinders <= 1024 && start != total)
829f4206
KZ
1443 fdisk_warnx(cxt, _("Partition %zu: previous sectors %u "
1444 "disagrees with total %u"), n, start, total);
5dfca634
KZ
1445}
1446
1447/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1448 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1449 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1450 * Lubkin Oct. 1991). */
1451
1452static void
1453long2chs(struct fdisk_context *cxt, unsigned long ls,
1454 unsigned int *c, unsigned int *h, unsigned int *s) {
1455 int spc = cxt->geom.heads * cxt->geom.sectors;
1456
1457 *c = ls / spc;
1458 ls = ls % spc;
1459 *h = ls / cxt->geom.sectors;
1460 *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
1461}
1462
f540fe15 1463static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
e3661531 1464 size_t partition)
5dfca634
KZ
1465{
1466 unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
1467 unsigned int pec, peh, pes; /* physical ending c, h, s */
1468 unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
1469 unsigned int lec, leh, les; /* logical ending c, h, s */
1470
1471 if (!is_dos_compatible(cxt))
1472 return;
1473
1474 if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
1475 return; /* do not check extended partitions */
1476
60c1b036 1477 /* physical beginning c, h, s */
da967236
KZ
1478 pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
1479 pbh = p->bh;
1480 pbs = p->bs & 0x3f;
5dfca634 1481
60c1b036 1482 /* physical ending c, h, s */
da967236
KZ
1483 pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
1484 peh = p->eh;
1485 pes = p->es & 0x3f;
5dfca634 1486
60c1b036 1487 /* compute logical beginning (c, h, s) */
b44244cb 1488 long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
5dfca634 1489
60c1b036 1490 /* compute logical ending (c, h, s) */
b44244cb 1491 long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
5dfca634 1492
60c1b036 1493 /* Same physical / logical beginning? */
fcea064b
KZ
1494 if (cxt->geom.cylinders <= 1024
1495 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
829f4206 1496 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
fcea064b
KZ
1497 "beginnings (non-Linux?): "
1498 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1499 partition + 1,
1500 pbc, pbh, pbs,
1501 lbc, lbh, lbs);
5dfca634
KZ
1502 }
1503
60c1b036 1504 /* Same physical / logical ending? */
fcea064b
KZ
1505 if (cxt->geom.cylinders <= 1024
1506 && (pec != lec || peh != leh || pes != les)) {
829f4206 1507 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
fcea064b
KZ
1508 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1509 partition + 1,
1510 pec, peh, pes,
1511 lec, leh, les);
5dfca634
KZ
1512 }
1513
60c1b036 1514 /* Ending on cylinder boundary? */
5dfca634 1515 if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
829f4206 1516 fdisk_warnx(cxt, _("Partition %zu: does not end on "
fcea064b 1517 "cylinder boundary."),
5dfca634
KZ
1518 partition + 1);
1519 }
1520}
1521
adea904a
KZ
1522static void fill_bounds(struct fdisk_context *cxt,
1523 fdisk_sector_t *first, fdisk_sector_t *last)
1524{
1525 size_t i;
1526 struct pte *pe = self_pte(cxt, 0);
1527 struct dos_partition *p;
1528
1529 assert(pe);
1530 for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
1531 p = pe->pt_entry;
1532 if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
1533 first[i] = SIZE_MAX;
1534 last[i] = 0;
1535 } else {
1536 first[i] = get_abs_partition_start(pe);
1537 last[i] = get_abs_partition_end(pe);
1538 }
1539 }
1540}
1541
9ffeb235 1542static int dos_verify_disklabel(struct fdisk_context *cxt)
2ca61a61 1543{
e3661531 1544 size_t i, j;
0073a4cf
KZ
1545 fdisk_sector_t total = 1, n_sectors = cxt->total_sectors;
1546 fdisk_sector_t first[cxt->label->nparts_max],
1547 last[cxt->label->nparts_max];
f540fe15 1548 struct dos_partition *p;
fcea064b 1549 struct fdisk_dos_label *l = self_label(cxt);
2ca61a61 1550
aa36c2cf 1551 assert(fdisk_is_label(cxt, DOS));
9ffeb235 1552
e3661531
KZ
1553 fill_bounds(cxt, first, last);
1554 for (i = 0; i < cxt->label->nparts_max; i++) {
af0df606 1555 struct pte *pe = self_pte(cxt, i);
2ca61a61 1556
af0df606 1557 p = self_partition(cxt, i);
b2b52925 1558 if (p && is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
2ca61a61 1559 check_consistency(cxt, p, i);
b2b52925 1560 assert(pe);
61eaad50 1561 if (get_abs_partition_start(pe) < first[i])
fcea064b 1562 fdisk_warnx(cxt, _(
829f4206 1563 "Partition %zu: bad start-of-data."),
fcea064b
KZ
1564 i + 1);
1565
1566 check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
2ca61a61 1567 total += last[i] + 1 - first[i];
fcea064b 1568
11712b66
KZ
1569 if (i == 0)
1570 total += get_abs_partition_start(pe) - 1;
1571
fcea064b 1572 for (j = 0; j < i; j++) {
2ca61a61
DB
1573 if ((first[i] >= first[j] && first[i] <= last[j])
1574 || ((last[i] <= last[j] && last[i] >= first[j]))) {
fcea064b 1575
829f4206
KZ
1576 fdisk_warnx(cxt, _("Partition %zu: "
1577 "overlaps partition %zu."),
fcea064b
KZ
1578 j + 1, i + 1);
1579
2ca61a61
DB
1580 total += first[i] >= first[j] ?
1581 first[i] : first[j];
1582 total -= last[i] <= last[j] ?
1583 last[i] : last[j];
1584 }
fcea064b 1585 }
2ca61a61
DB
1586 }
1587 }
1588
fcea064b 1589 if (l->ext_offset) {
0073a4cf 1590 fdisk_sector_t e_last;
1b92d076
KZ
1591 struct pte *ext_pe = self_pte(cxt, l->ext_index);
1592
b2b52925 1593 assert(ext_pe);
1b92d076 1594 e_last = get_abs_partition_end(ext_pe);
2ca61a61 1595
e3661531 1596 for (i = 4; i < cxt->label->nparts_max; i++) {
2ca61a61 1597 total++;
af0df606 1598 p = self_partition(cxt, i);
b2b52925 1599 assert(p);
af0df606 1600
2ca61a61 1601 if (!p->sys_ind) {
e3661531 1602 if (i != 4 || i + 1 < cxt->label->nparts_max)
fcea064b 1603 fdisk_warnx(cxt,
829f4206 1604 _("Partition %zu: empty."),
fcea064b
KZ
1605 i + 1);
1606 } else if (first[i] < l->ext_offset
1607 || last[i] > e_last) {
1608
829f4206
KZ
1609 fdisk_warnx(cxt, _("Logical partition %zu: "
1610 "not entirely in partition %zu."),
fcea064b 1611 i + 1, l->ext_index + 1);
2ca61a61 1612 }
2ca61a61
DB
1613 }
1614 }
1615
1616 if (total > n_sectors)
fcea064b
KZ
1617 fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
1618 "than the maximum %llu."), total, n_sectors);
2ca61a61 1619 else if (total < n_sectors)
fcea064b 1620 fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
ac1a559a 1621 "sectors."), n_sectors - total, cxt->sector_size);
2ca61a61
DB
1622
1623 return 0;
1624}
1625
9dea2923
DB
1626/*
1627 * Ask the user for new partition type information (logical, extended).
0f639e54
DB
1628 * This function calls the actual partition adding logic - add_partition.
1629 *
1630 * API callback.
9dea2923 1631 */
77d6a70a 1632static int dos_add_partition(struct fdisk_context *cxt,
c3bc7483
KZ
1633 struct fdisk_partition *pa,
1634 size_t *partno)
9dea2923 1635{
ea987bb5 1636 size_t i, free_primary = 0, free_sectors = 0;
0073a4cf 1637 fdisk_sector_t last = 0, grain;
e3661531 1638 int rc = 0;
e77313a6 1639 struct fdisk_dos_label *l;
1b92d076 1640 struct pte *ext_pe;
3fdbf0ee 1641 size_t res = 0; /* partno */
9dea2923 1642
9ffeb235
KZ
1643 assert(cxt);
1644 assert(cxt->label);
aa36c2cf 1645 assert(fdisk_is_label(cxt, DOS));
9ffeb235 1646
c3bc7483
KZ
1647 DBG(LABEL, ul_debug("DOS: new partition wanted"));
1648
e77313a6 1649 l = self_label(cxt);
1b92d076 1650 ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
a3e37fa7 1651
c3bc7483
KZ
1652 /*
1653 * partition template (@pa) based partitioning
1654 */
1655
2cb47542 1656 /* A) template specifies start within extended partition; add logical */
ecf40cda 1657 if (pa && fdisk_partition_has_start(pa) && ext_pe
a3e37fa7 1658 && pa->start >= l->ext_offset
1b92d076 1659 && pa->start <= get_abs_partition_end(ext_pe)) {
2cb47542
KZ
1660 DBG(LABEL, ul_debug("DOS: pa template %p: add logical (by offset)", pa));
1661
1662 if (fdisk_partition_has_partno(pa) && fdisk_partition_get_partno(pa) < 4) {
1663 DBG(LABEL, ul_debug("DOS: pa template specifies partno<4 for logical partition"));
1664 return -EINVAL;
1665 }
c3bc7483 1666 rc = add_logical(cxt, pa, &res);
a3e37fa7
KZ
1667 goto done;
1668
2cb47542
KZ
1669 /* B) template specifies start out of extended partition; add primary */
1670 } else if (pa && fdisk_partition_has_start(pa) && ext_pe) {
1671 DBG(LABEL, ul_debug("DOS: pa template %p: add primary (by offset)", pa));
1672
1673 if (fdisk_partition_has_partno(pa) && fdisk_partition_get_partno(pa) >= 4) {
1674 DBG(LABEL, ul_debug("DOS: pa template specifies partno>=4 for primary partition"));
ea987bb5
KZ
1675 return -EINVAL;
1676 }
992f7cba 1677 if (ext_pe && pa->type && IS_EXTENDED(pa->type->code)) {
2cb47542
KZ
1678 fdisk_warnx(cxt, _("Extended partition already exists."));
1679 return -EINVAL;
c3bc7483 1680 }
44374b10 1681 rc = get_partition_unused_primary(cxt, pa, &res);
8d03f4af 1682 if (rc == 0)
c3bc7483 1683 rc = add_partition(cxt, res, pa);
8d03f4af
KZ
1684 goto done;
1685
2cb47542
KZ
1686 /* C) template specifies start (or default), partno < 4; add primary */
1687 } else if (pa && (fdisk_partition_start_is_default(pa) || fdisk_partition_has_start(pa))
8d03f4af 1688 && fdisk_partition_has_partno(pa)
ee9ba4fa 1689 && pa->partno < 4) {
2cb47542
KZ
1690 DBG(LABEL, ul_debug("DOS: pa template %p: add primary (by partno)", pa));
1691
1692 if (ext_pe && pa->type && IS_EXTENDED(pa->type->code)) {
1693 fdisk_warnx(cxt, _("Extended partition already exists."));
1694 return -EINVAL;
1695 }
8d03f4af
KZ
1696 rc = get_partition_unused_primary(cxt, pa, &res);
1697 if (rc == 0)
1698 rc = add_partition(cxt, res, pa);
1699 goto done;
1700
167d7e95
KZ
1701 /* D) template specifies start (or default), partno >= 4; add logical */
1702 } else if (pa && (fdisk_partition_start_is_default(pa) || fdisk_partition_has_start(pa))
8d03f4af
KZ
1703 && fdisk_partition_has_partno(pa)
1704 && pa->partno >= 4) {
2cb47542
KZ
1705 DBG(LABEL, ul_debug("DOS: pa template %p: add logical (by partno)", pa));
1706
1707 if (!ext_pe) {
167d7e95
KZ
1708 fdisk_warnx(cxt, _("Extended partition does not exists. Failed to add logical partition."));
1709 return -EINVAL;
1710 } else if (fdisk_partition_has_start(pa)
1711 && pa->start < l->ext_offset
1712 && pa->start > get_abs_partition_end(ext_pe)) {
1713 DBG(LABEL, ul_debug("DOS: pa template specifies partno>=4, but start out of extended"));
2cb47542
KZ
1714 return -EINVAL;
1715 }
167d7e95 1716
8d03f4af
KZ
1717 rc = add_logical(cxt, pa, &res);
1718 goto done;
a3e37fa7 1719 }
20f878fe 1720
2cb47542
KZ
1721 DBG(LABEL, ul_debug("DOS: dialog driven partitioning"));
1722 /* Note @pa may be still used for things like partition type, etc */
c3bc7483 1723
ea987bb5
KZ
1724 /* check if there is space for primary partition */
1725 grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
1726 last = cxt->first_lba;
1727
af0df606
KZ
1728 for (i = 0; i < 4; i++) {
1729 struct dos_partition *p = self_partition(cxt, i);
ea987bb5 1730
b2b52925 1731 assert(p);
ea987bb5 1732 if (is_used_partition(p)) {
0073a4cf 1733 fdisk_sector_t start = dos_partition_get_start(p);
ea987bb5
KZ
1734 if (last + grain <= start)
1735 free_sectors = 1;
1736 last = start + dos_partition_get_size(p);
1737 } else
1738 free_primary++;
af0df606 1739 }
ea987bb5
KZ
1740 if (last + grain < cxt->total_sectors - 1)
1741 free_sectors = 1;
9dea2923 1742
e3661531 1743 if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
fcea064b
KZ
1744 fdisk_info(cxt, _("The maximum number of partitions has "
1745 "been created."));
8254c3a5 1746 return -EINVAL;
9dea2923 1747 }
fcea064b 1748
ea987bb5 1749 if (!free_primary || !free_sectors) {
c3bc7483 1750 DBG(LABEL, ul_debug("DOS: primary impossible, add logical"));
fcea064b 1751 if (l->ext_offset) {
ecf40cda 1752 if (!pa || fdisk_partition_has_start(pa)) {
992f7cba
KZ
1753 /* See above case A); here we have start, but
1754 * out of extended partition */
eed0e7b9 1755 const char *msg;
c3bc7483 1756 if (!free_primary)
eed0e7b9 1757 msg = _("All primary partitions are in use.");
c603d076 1758 else
eed0e7b9
KZ
1759 msg = _("All space for primary partitions is in use.");
1760
1761 if (pa && fdisk_partition_has_start(pa)) {
1762 fdisk_warnx(cxt, msg);
1763 return -EINVAL;
1764 } else
1765 fdisk_info(cxt, msg);
c3bc7483
KZ
1766 }
1767 rc = add_logical(cxt, pa, &res);
ea987bb5 1768 } else {
240ddd3a
IW
1769 if (free_primary)
1770 fdisk_info(cxt, _("All space for primary partitions is in use."));
1771 else
33b981f7 1772 /* TRANSLATORS: Try to keep this within 80 characters. */
240ddd3a 1773 fdisk_info(cxt, _("To create more partitions, first replace "
33b981f7 1774 "a primary with an extended partition."));
ea987bb5
KZ
1775 return -EINVAL;
1776 }
e3661531 1777 } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
fcea064b
KZ
1778 fdisk_info(cxt, _("All logical partitions are in use. "
1779 "Adding a primary partition."));
44374b10
BE
1780 rc = get_partition_unused_primary(cxt, pa, &res);
1781 if (rc == 0)
c3bc7483 1782 rc = add_partition(cxt, res, pa);
9dea2923 1783 } else {
20f878fe
KZ
1784 char hint[BUFSIZ];
1785 struct fdisk_ask *ask;
4813a521 1786 int c = 0;
20f878fe 1787
ea987bb5 1788 /* the default layout for scripts is to create primary partitions */
992f7cba 1789 if (cxt->script || !fdisk_has_dialogs(cxt)) {
44374b10
BE
1790 rc = get_partition_unused_primary(cxt, pa, &res);
1791 if (rc == 0)
c3bc7483 1792 rc = add_partition(cxt, res, pa);
ea987bb5
KZ
1793 goto done;
1794 }
1795
20f878fe
KZ
1796 ask = fdisk_new_ask();
1797 if (!ask)
1798 return -ENOMEM;
1799 fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
1800 fdisk_ask_set_query(ask, _("Partition type"));
1801 fdisk_ask_menu_set_default(ask, free_primary == 1
1802 && !l->ext_offset ? 'e' : 'p');
1803 snprintf(hint, sizeof(hint),
1804 _("%zu primary, %d extended, %zu free"),
1805 4 - (l->ext_offset ? 1 : 0) - free_primary,
1806 l->ext_offset ? 1 : 0,
1807 free_primary);
1808
1809 fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
1810 if (!l->ext_offset)
1811 fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
1812 else
1813 fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
1814
1815 rc = fdisk_do_ask(cxt, ask);
5cebb2ab
VD
1816 if (!rc)
1817 fdisk_ask_menu_get_result(ask, &c);
1818 fdisk_unref_ask(ask);
bd0d850b
KZ
1819 if (rc)
1820 return rc;
bd0d850b 1821
9dea2923 1822 if (c == 'p') {
44374b10
BE
1823 rc = get_partition_unused_primary(cxt, pa, &res);
1824 if (rc == 0)
c3bc7483 1825 rc = add_partition(cxt, res, pa);
8254c3a5 1826 goto done;
fcea064b 1827 } else if (c == 'l' && l->ext_offset) {
c3bc7483 1828 rc = add_logical(cxt, pa, &res);
8254c3a5 1829 goto done;
fcea064b 1830 } else if (c == 'e' && !l->ext_offset) {
44374b10
BE
1831 rc = get_partition_unused_primary(cxt, pa, &res);
1832 if (rc == 0) {
ecf40cda 1833 struct fdisk_partition *xpa = NULL;
77d6a70a
KZ
1834 struct fdisk_parttype *t;
1835
a745611d 1836 t = fdisk_label_get_parttype_from_code(cxt->label,
bb58112e 1837 MBR_DOS_EXTENDED_PARTITION);
ecf40cda
KZ
1838 if (!pa) {
1839 pa = xpa = fdisk_new_partition();
1840 if (!xpa)
1841 return -ENOMEM;
1842 }
77d6a70a 1843 fdisk_partition_set_type(pa, t);
c3bc7483 1844 rc = add_partition(cxt, res, pa);
ecf40cda
KZ
1845 if (xpa) {
1846 fdisk_unref_partition(xpa);
1847 pa = NULL;
1848 }
ed470672 1849 }
8254c3a5 1850 goto done;
9dea2923 1851 } else
ac1a559a 1852 fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
9dea2923 1853 }
8254c3a5 1854done:
c3bc7483 1855 if (rc == 0) {
e3661531 1856 cxt->label->nparts_cur++;
44374b10 1857 if (partno)
c3bc7483
KZ
1858 *partno = res;
1859 }
8254c3a5 1860 return rc;
9dea2923 1861}
0dc13a38 1862
0073a4cf 1863static int write_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
fae7b1bc 1864 unsigned char *buf)
0dc13a38 1865{
21770662
KZ
1866 int rc;
1867
1868 rc = seek_sector(cxt, secno);
1869 if (rc != 0) {
98aeae96 1870 fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
21770662
KZ
1871 (uintmax_t) secno);
1872 return rc;
1873 }
bcddbe96 1874
9e930041 1875 DBG(LABEL, ul_debug("DOS: writing to sector %ju", (uintmax_t) secno));
bcddbe96 1876
fae7b1bc
DB
1877 if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
1878 return -errno;
1879 return 0;
1880}
1881
9ffeb235 1882static int dos_write_disklabel(struct fdisk_context *cxt)
fae7b1bc 1883{
8b3d9241 1884 struct fdisk_dos_label *l = self_label(cxt);
e3661531 1885 size_t i;
8b3d9241 1886 int rc = 0, mbr_changed = 0;
0dc13a38 1887
9ffeb235
KZ
1888 assert(cxt);
1889 assert(cxt->label);
aa36c2cf 1890 assert(fdisk_is_label(cxt, DOS));
9ffeb235 1891
3d919d95
KZ
1892 DBG(LABEL, ul_debug("DOS: write PT requested [label-changed: %d, non-pt-changed: %d]",
1893 cxt->label->changed, l->non_pt_changed));
1894
8b3d9241
KZ
1895 mbr_changed = l->non_pt_changed;
1896
0dc13a38 1897 /* MBR (primary partitions) */
8b3d9241 1898 if (!mbr_changed) {
af0df606
KZ
1899 for (i = 0; i < 4; i++) {
1900 struct pte *pe = self_pte(cxt, i);
b2b52925
KZ
1901
1902 assert(pe);
af0df606 1903 if (pe->changed)
8b3d9241 1904 mbr_changed = 1;
af0df606 1905 }
0dc13a38 1906 }
8b3d9241 1907 if (mbr_changed) {
9e930041 1908 DBG(LABEL, ul_debug("DOS: MBR changed, writing"));
67987b47 1909 mbr_set_magic(cxt->firstsector);
fae7b1bc
DB
1910 rc = write_sector(cxt, 0, cxt->firstsector);
1911 if (rc)
1912 goto done;
0dc13a38 1913 }
8b3d9241 1914
bcddbe96
KZ
1915 if (cxt->label->nparts_max <= 4 && l->ext_offset) {
1916 /* we have empty extended partition, check if the partition has
1917 * been modified and then cleanup possible remaining EBR */
1918 struct pte *pe = self_pte(cxt, l->ext_index);
1919 unsigned char empty[512] = { 0 };
0073a4cf 1920 fdisk_sector_t off = pe ? get_abs_partition_start(pe) : 0;
bcddbe96
KZ
1921
1922 if (off && pe->changed) {
1923 mbr_set_magic(empty);
1924 write_sector(cxt, off, empty);
1925 }
1926 }
1927
0dc13a38 1928 /* EBR (logical partitions) */
e3661531 1929 for (i = 4; i < cxt->label->nparts_max; i++) {
af0df606 1930 struct pte *pe = self_pte(cxt, i);
0dc13a38 1931
b2b52925 1932 assert(pe);
28d79d9b 1933 if (!pe->changed || !pe->offset || !pe->sectorbuffer)
613596b3
KZ
1934 continue;
1935
613596b3
KZ
1936 mbr_set_magic(pe->sectorbuffer);
1937 rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
1938 if (rc)
1939 goto done;
0dc13a38 1940 }
fae7b1bc
DB
1941
1942done:
1943 return rc;
0dc13a38 1944}
b8855c86 1945
f32a9be4 1946static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
9bbcf43f 1947 const char **name, uint64_t *offset, size_t *size)
f32a9be4
KZ
1948{
1949 assert(cxt);
1950
1951 *name = NULL;
1952 *offset = 0;
1953 *size = 0;
1954
1955 switch (n) {
1956 case 0:
1957 *name = "MBR";
1958 *offset = 0;
1959 *size = 512;
1960 break;
1961 default:
1962 /* extended partitions */
b9710f1f 1963 if ((size_t)n - 1 + 4 < cxt->label->nparts_max) {
f32a9be4
KZ
1964 struct pte *pe = self_pte(cxt, n - 1 + 4);
1965
b2b52925 1966 assert(pe);
f32a9be4
KZ
1967 assert(pe->private_sectorbuffer);
1968
1969 *name = "EBR";
9bbcf43f 1970 *offset = (uint64_t) pe->offset * cxt->sector_size;
f32a9be4
KZ
1971 *size = 512;
1972 } else
1973 return 1;
1974 break;
1975 }
1976
1977 return 0;
1978}
1979
dfc96cbf
KZ
1980/*
1981 * Check whether partition entries are ordered by their starting positions.
1982 * Return 0 if OK. Return i if partition i should have been earlier.
1983 * Two separate checks: primary and logical partitions.
1984 */
e3661531 1985static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
dfc96cbf 1986{
e3661531
KZ
1987 size_t last_p_start_pos = 0, p_start_pos;
1988 size_t i, last_i = 0;
dfc96cbf 1989
e3661531 1990 for (i = 0 ; i < cxt->label->nparts_max; i++) {
af0df606
KZ
1991
1992 struct pte *pe = self_pte(cxt, i);
b2b52925
KZ
1993 struct dos_partition *p;
1994
1995 assert(pe);
1996 p = pe->pt_entry;
af0df606 1997
dfc96cbf
KZ
1998 if (i == 4) {
1999 last_i = 4;
2000 last_p_start_pos = 0;
2001 }
d0a870c5 2002 if (is_used_partition(p)) {
61eaad50 2003 p_start_pos = get_abs_partition_start(pe);
dfc96cbf
KZ
2004
2005 if (last_p_start_pos > p_start_pos) {
2006 if (prev)
2007 *prev = last_i;
2008 return i;
2009 }
2010
2011 last_p_start_pos = p_start_pos;
2012 last_i = i;
2013 }
2014 }
2015 return 0;
2016}
6d864a49 2017
5989556a 2018static int dos_get_disklabel_item(struct fdisk_context *cxt, struct fdisk_labelitem *item)
1cf67f1c 2019{
5989556a
KZ
2020 int rc = 0;
2021
6d864a49 2022 assert(cxt);
9ffeb235 2023 assert(cxt->label);
aa36c2cf 2024 assert(fdisk_is_label(cxt, DOS));
6d864a49 2025
5989556a
KZ
2026 switch (item->id) {
2027 case FDISK_LABELITEM_ID:
2028 {
2029 unsigned int num = mbr_get_id(cxt->firstsector);
2030 item->name = _("Disk identifier");
2031 item->type = 's';
2032 if (asprintf(&item->data.str, "0x%08x", num) < 0)
2033 rc = -ENOMEM;
2034 break;
2035 }
2036 default:
2037 if (item->id < __FDISK_NLABELITEMS)
9e930041 2038 rc = 1; /* unsupported generic item */
5989556a
KZ
2039 else
2040 rc = 2; /* out of range */
2041 break;
2042 }
2043
2044 return rc;
2045
262002ee 2046}
1cf67f1c 2047
262002ee
KZ
2048static int dos_get_partition(struct fdisk_context *cxt, size_t n,
2049 struct fdisk_partition *pa)
2050{
2051 struct dos_partition *p;
2052 struct pte *pe;
bd5e8291 2053 struct fdisk_dos_label *lb;
1cf67f1c 2054
262002ee
KZ
2055 assert(cxt);
2056 assert(pa);
2057 assert(cxt->label);
aa36c2cf 2058 assert(fdisk_is_label(cxt, DOS));
1cf67f1c 2059
bd5e8291 2060 lb = self_label(cxt);
b2b52925 2061
262002ee 2062 pe = self_pte(cxt, n);
b2b52925
KZ
2063 assert(pe);
2064
262002ee
KZ
2065 p = pe->pt_entry;
2066 pa->used = !is_cleared_partition(p);
2067 if (!pa->used)
2068 return 0;
1cf67f1c 2069
7f01ec7a 2070 pa->type = dos_partition_parttype(cxt, p);
cb4d6804 2071 pa->boot = p->boot_ind == ACTIVE_FLAG ? 1 : 0;
77d6a70a 2072 pa->start = get_abs_partition_start(pe);
1b92d076 2073 pa->size = dos_partition_get_size(p);
e77313a6 2074 pa->container = lb->ext_offset && n == lb->ext_index;
1cf67f1c 2075
bd5e8291
KZ
2076 if (n >= 4)
2077 pa->parent_partno = lb->ext_index;
03643931 2078
cb4d6804 2079 if (p->boot_ind && asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
262002ee 2080 return -ENOMEM;
6d864a49 2081
262002ee 2082 /* start C/H/S */
ecf40cda 2083 if (asprintf(&pa->start_chs, "%d/%d/%d",
262002ee 2084 cylinder(p->bs, p->bc),
a2ad58ac
AH
2085 p->bh,
2086 sector(p->bs)) < 0)
262002ee 2087 return -ENOMEM;
1cf67f1c 2088
262002ee 2089 /* end C/H/S */
ecf40cda 2090 if (asprintf(&pa->end_chs, "%d/%d/%d",
262002ee 2091 cylinder(p->es, p->ec),
a2ad58ac
AH
2092 p->eh,
2093 sector(p->es)) < 0)
262002ee 2094 return -ENOMEM;
6d864a49 2095
262002ee 2096 return 0;
6d864a49
KZ
2097}
2098
df15fb3d
KZ
2099static int has_logical(struct fdisk_context *cxt)
2100{
2101 size_t i;
2102 struct fdisk_dos_label *l = self_label(cxt);
2103
2104 for (i = 4; i < cxt->label->nparts_max; i++) {
2105 if (l->ptes[i].pt_entry)
2106 return 1;
2107 }
2108 return 0;
2109}
2110
0ecf3ab5
KZ
2111static int dos_set_partition(struct fdisk_context *cxt, size_t n,
2112 struct fdisk_partition *pa)
2113{
df15fb3d 2114 struct fdisk_dos_label *l;
0ecf3ab5
KZ
2115 struct dos_partition *p;
2116 struct pte *pe;
df15fb3d 2117 int orgtype;
0073a4cf 2118 fdisk_sector_t start, size;
0ecf3ab5
KZ
2119
2120 assert(cxt);
2121 assert(pa);
2122 assert(cxt->label);
2123 assert(fdisk_is_label(cxt, DOS));
2124
2125 if (n >= cxt->label->nparts_max)
2126 return -EINVAL;
2127
df15fb3d 2128 l = self_label(cxt);
333c3761 2129 p = self_partition(cxt, n);
b2b52925 2130
333c3761 2131 pe = self_pte(cxt, n);
b2b52925
KZ
2132 if (!pe)
2133 return -EINVAL;
2134
df15fb3d
KZ
2135 orgtype = p->sys_ind;
2136
2137 if (pa->type) {
e6b4209d 2138 if (IS_EXTENDED(pa->type->code) && l->ext_offset && l->ext_index != n) {
df15fb3d
KZ
2139 fdisk_warnx(cxt, _("Extended partition already exists."));
2140 return -EINVAL;
2141 }
2142
2143 if (!pa->type->code)
2144 fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
2145 "Having partitions of type 0 is probably unwise."));
2146
2147 if (IS_EXTENDED(p->sys_ind) && !IS_EXTENDED(pa->type->code) && has_logical(cxt)) {
2148 fdisk_warnx(cxt, _(
2149 "Cannot change type of the extended partition which is "
75a8e726 2150 "already used by logical partitions. Delete logical "
df15fb3d
KZ
2151 "partitions first."));
2152 return -EINVAL;
2153 }
2154 }
150d98ee 2155
333c3761
KZ
2156 FDISK_INIT_UNDEF(start);
2157 FDISK_INIT_UNDEF(size);
0ecf3ab5 2158
333c3761
KZ
2159 if (fdisk_partition_has_start(pa))
2160 start = pa->start;
2161 if (fdisk_partition_has_size(pa))
2162 size = pa->size;
150d98ee 2163
333c3761
KZ
2164 if (!FDISK_IS_UNDEF(start) || !FDISK_IS_UNDEF(size)) {
2165 DBG(LABEL, ul_debug("DOS: resize partition"));
150d98ee 2166
333c3761
KZ
2167 if (FDISK_IS_UNDEF(start))
2168 start = get_abs_partition_start(pe);
2169 if (FDISK_IS_UNDEF(size))
2170 size = dos_partition_get_size(p);
0ecf3ab5 2171
150d98ee 2172 set_partition(cxt, n, 0, start, start + size - 1,
0ecf3ab5 2173 pa->type ? pa->type->code : p->sys_ind,
4d156d92
KZ
2174 FDISK_IS_UNDEF(pa->boot) ?
2175 p->boot_ind == ACTIVE_FLAG :
2176 fdisk_partition_is_bootable(pa));
150d98ee 2177 } else {
ecf40cda 2178 DBG(LABEL, ul_debug("DOS: keep size, modify properties"));
150d98ee
KZ
2179 if (pa->type)
2180 p->sys_ind = pa->type->code;
ecf40cda 2181 if (!FDISK_IS_UNDEF(pa->boot))
4d156d92 2182 p->boot_ind = fdisk_partition_is_bootable(pa) ? ACTIVE_FLAG : 0;
150d98ee
KZ
2183 }
2184
df15fb3d
KZ
2185 if (pa->type) {
2186 if (IS_EXTENDED(pa->type->code) && !IS_EXTENDED(orgtype)) {
2187 /* new extended partition - create a reference */
2188 l->ext_index = n;
2189 l->ext_offset = dos_partition_get_start(p);
2190 pe->ex_entry = p;
2191 } else if (IS_EXTENDED(orgtype)) {
2192 /* remove extended partition */
2193 cxt->label->nparts_max = 4;
2194 l->ptes[l->ext_index].ex_entry = NULL;
2195 l->ext_offset = 0;
2196 l->ext_index = 0;
2197 }
2198 }
2199
0ecf3ab5
KZ
2200 partition_set_changed(cxt, n, 1);
2201 return 0;
2202}
2203
a60087aa
KZ
2204static void print_chain_of_logicals(struct fdisk_context *cxt)
2205{
2206 size_t i;
6c64adee 2207 struct fdisk_dos_label *l = self_label(cxt);
a60087aa
KZ
2208
2209 fputc('\n', stdout);
2210
2211 for (i = 4; i < cxt->label->nparts_max; i++) {
2212 struct pte *pe = self_pte(cxt, i);
2213
b2b52925 2214 assert(pe);
2248aaf9 2215 fprintf(stderr, "#%02zu EBR [%10ju], "
6c64adee
KZ
2216 "data[start=%10ju (%10ju), size=%10ju], "
2217 "link[start=%10ju (%10ju), size=%10ju]\n",
a60087aa 2218 i, (uintmax_t) pe->offset,
6c64adee 2219 /* data */
a60087aa 2220 (uintmax_t) dos_partition_get_start(pe->pt_entry),
6c64adee 2221 (uintmax_t) get_abs_partition_start(pe),
a60087aa 2222 (uintmax_t) dos_partition_get_size(pe->pt_entry),
6c64adee 2223 /* link */
a60087aa 2224 (uintmax_t) dos_partition_get_start(pe->ex_entry),
6c64adee 2225 (uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
a60087aa
KZ
2226 (uintmax_t) dos_partition_get_size(pe->ex_entry));
2227 }
2228}
2229
c1156d85
KZ
2230static int cmp_ebr_offsets(const void *a, const void *b)
2231{
ec4b88b8
KZ
2232 const struct pte *ae = (const struct pte *) a,
2233 *be = (const struct pte *) b;
c1156d85 2234
99cacc6a 2235 if (ae->offset == 0 && be->offset == 0)
c1156d85
KZ
2236 return 0;
2237 if (ae->offset == 0)
2238 return 1;
2239 if (be->offset == 0)
2240 return -1;
2241
19ff8ff7 2242 return cmp_numbers(ae->offset, be->offset);
c1156d85
KZ
2243}
2244
dfc96cbf
KZ
2245/*
2246 * Fix the chain of logicals.
dfc96cbf 2247 *
a60087aa
KZ
2248 * The function does not modify data partitions within EBR tables
2249 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
2250 * (pte->ex_entry) between EBR tables.
2251 *
dfc96cbf 2252 */
e3661531 2253static void fix_chain_of_logicals(struct fdisk_context *cxt)
dfc96cbf 2254{
af0df606 2255 struct fdisk_dos_label *l = self_label(cxt);
d0bdf184 2256 struct pte *last;
a60087aa
KZ
2257 size_t i;
2258
a60087aa
KZ
2259 DBG(LABEL, print_chain_of_logicals(cxt));
2260
c1156d85
KZ
2261 /* Sort chain by EBR offsets */
2262 qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
2263 cmp_ebr_offsets);
2264
a60087aa 2265again:
c1156d85 2266 /* Sort data partitions by start */
a60087aa
KZ
2267 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2268 struct pte *cur = self_pte(cxt, i),
2269 *nxt = self_pte(cxt, i + 1);
a60087aa 2270
b2b52925
KZ
2271 assert(cur);
2272 assert(nxt);
2273
c1156d85
KZ
2274 if (get_abs_partition_start(cur) >
2275 get_abs_partition_start(nxt)) {
a60087aa 2276
c1156d85 2277 struct dos_partition tmp = *cur->pt_entry;
0073a4cf 2278 fdisk_sector_t cur_start = get_abs_partition_start(cur),
c1156d85 2279 nxt_start = get_abs_partition_start(nxt);
a60087aa 2280
c1156d85
KZ
2281 /* swap data partitions */
2282 *cur->pt_entry = *nxt->pt_entry;
2283 *nxt->pt_entry = tmp;
a60087aa 2284
c1156d85 2285 /* Recount starts according to EBR offsets, the absolute
9e930041 2286 * address still has to be the same! */
c1156d85
KZ
2287 dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
2288 dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
a60087aa 2289
c1156d85
KZ
2290 partition_set_changed(cxt, i, 1);
2291 partition_set_changed(cxt, i + 1, 1);
2292 goto again;
2293 }
dfc96cbf
KZ
2294 }
2295
c1156d85 2296 /* Update EBR links */
a60087aa
KZ
2297 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2298 struct pte *cur = self_pte(cxt, i),
2299 *nxt = self_pte(cxt, i + 1);
2300
b2b52925
KZ
2301 assert(cur);
2302 assert(nxt);
2303
0073a4cf 2304 fdisk_sector_t noff = nxt->offset - l->ext_offset,
b2b52925 2305 ooff = dos_partition_get_start(cur->ex_entry);
a60087aa 2306
a60087aa
KZ
2307 if (noff == ooff)
2308 continue;
2309
2310 DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2311 (uintmax_t) cur->offset,
2312 (uintmax_t) ooff, (uintmax_t) noff));
2313
2314 set_partition(cxt, i, 1, nxt->offset,
7bc31ada
KZ
2315 get_abs_partition_end(nxt),
2316 MBR_DOS_EXTENDED_PARTITION, 0);
d0bdf184 2317 }
6c64adee 2318
d0bdf184
KZ
2319 /* always terminate the chain ! */
2320 last = self_pte(cxt, cxt->label->nparts_max - 1);
2321 if (last) {
2322 clear_partition(last->ex_entry);
2323 partition_set_changed(cxt, cxt->label->nparts_max - 1, 1);
a60087aa 2324 }
d0bdf184 2325
a60087aa 2326 DBG(LABEL, print_chain_of_logicals(cxt));
dfc96cbf
KZ
2327}
2328
dd7ba604 2329static int dos_reorder(struct fdisk_context *cxt)
dfc96cbf
KZ
2330{
2331 struct pte *pei, *pek;
e3661531 2332 size_t i,k;
dfc96cbf 2333
e3661531 2334 if (!wrong_p_order(cxt, NULL)) {
fcea064b 2335 fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
dd49c7d6 2336 return 1;
dfc96cbf
KZ
2337 }
2338
e3661531 2339 while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
dfc96cbf
KZ
2340 /* partition i should have come earlier, move it */
2341 /* We have to move data in the MBR */
f540fe15 2342 struct dos_partition *pi, *pk, *pe, pbuf;
af0df606
KZ
2343 pei = self_pte(cxt, i);
2344 pek = self_pte(cxt, k);
dfc96cbf 2345
b2b52925
KZ
2346 assert(pei);
2347 assert(pek);
2348
6864f168
KZ
2349 pe = pei->ex_entry;
2350 pei->ex_entry = pek->ex_entry;
2351 pek->ex_entry = pe;
dfc96cbf 2352
6864f168
KZ
2353 pi = pei->pt_entry;
2354 pk = pek->pt_entry;
dfc96cbf 2355
f540fe15
KZ
2356 memmove(&pbuf, pi, sizeof(struct dos_partition));
2357 memmove(pi, pk, sizeof(struct dos_partition));
2358 memmove(pk, &pbuf, sizeof(struct dos_partition));
dfc96cbf 2359
af0df606
KZ
2360 partition_set_changed(cxt, i, 1);
2361 partition_set_changed(cxt, k, 1);
dfc96cbf
KZ
2362 }
2363
2364 if (i)
e3661531 2365 fix_chain_of_logicals(cxt);
dfc96cbf 2366
f8ad3899 2367 return 0;
dfc96cbf 2368}
6d864a49 2369
ecf40cda 2370/* TODO: use fdisk_set_partition() API */
829f4206 2371int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
71ee484e 2372{
f8ad3899
KZ
2373 struct pte *pe;
2374 struct dos_partition *p;
71ee484e 2375 unsigned int new, free_start, curr_start, last;
bd0d850b 2376 uintmax_t res = 0;
e3661531 2377 size_t x;
f8ad3899 2378 int rc;
71ee484e
KZ
2379
2380 assert(cxt);
aa36c2cf 2381 assert(fdisk_is_label(cxt, DOS));
71ee484e 2382
f8ad3899 2383 pe = self_pte(cxt, i);
b2b52925
KZ
2384 if (!pe)
2385 return -EINVAL;
2386
f8ad3899
KZ
2387 p = pe->pt_entry;
2388
d0a870c5 2389 if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
829f4206 2390 fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
f8ad3899 2391 return 0;
71ee484e
KZ
2392 }
2393
2394 /* the default start is at the second sector of the disk or at the
2395 * second sector of the extended partition
2396 */
2397 free_start = pe->offset ? pe->offset + 1 : 1;
2398
61eaad50 2399 curr_start = get_abs_partition_start(pe);
71ee484e
KZ
2400
2401 /* look for a free space before the current start of the partition */
e3661531 2402 for (x = 0; x < cxt->label->nparts_max; x++) {
71ee484e 2403 unsigned int end;
af0df606 2404 struct pte *prev_pe = self_pte(cxt, x);
b2b52925
KZ
2405 struct dos_partition *prev_p;
2406
2407 assert(prev_pe);
71ee484e 2408
b2b52925 2409 prev_p = prev_pe->pt_entry;
71ee484e
KZ
2410 if (!prev_p)
2411 continue;
af0df606
KZ
2412 end = get_abs_partition_start(prev_pe)
2413 + dos_partition_get_size(prev_p);
71ee484e 2414
d0a870c5 2415 if (is_used_partition(prev_p) &&
71ee484e
KZ
2416 end > free_start && end <= curr_start)
2417 free_start = end;
2418 }
2419
1b92d076 2420 last = get_abs_partition_end(pe);
71ee484e 2421
f8ad3899
KZ
2422 rc = fdisk_ask_number(cxt, free_start, curr_start, last,
2423 _("New beginning of data"), &res);
2424 if (rc)
2425 return rc;
2426
bd0d850b 2427 new = res - pe->offset;
71ee484e 2428
b44244cb 2429 if (new != dos_partition_get_size(p)) {
af0df606
KZ
2430 unsigned int sects = dos_partition_get_size(p)
2431 + dos_partition_get_start(p) - new;
2432
b44244cb
KZ
2433 dos_partition_set_size(p, sects);
2434 dos_partition_set_start(p, new);
af0df606
KZ
2435
2436 partition_set_changed(cxt, i, 1);
71ee484e 2437 }
f8ad3899
KZ
2438
2439 return rc;
71ee484e 2440}
6d864a49 2441
8c0a7f91 2442static int dos_partition_is_used(
47b8e7c0 2443 struct fdisk_context *cxt,
8c0a7f91 2444 size_t i)
47b8e7c0 2445{
f540fe15 2446 struct dos_partition *p;
47b8e7c0
KZ
2447
2448 assert(cxt);
9ffeb235 2449 assert(cxt->label);
aa36c2cf 2450 assert(fdisk_is_label(cxt, DOS));
47b8e7c0 2451
8c0a7f91
KZ
2452 if (i >= cxt->label->nparts_max)
2453 return 0;
47b8e7c0 2454
af0df606 2455 p = self_partition(cxt, i);
47b8e7c0 2456
8c0a7f91 2457 return p && !is_cleared_partition(p);
47b8e7c0
KZ
2458}
2459
fb1caca7
KZ
2460static int dos_toggle_partition_flag(
2461 struct fdisk_context *cxt,
2462 size_t i,
2463 unsigned long flag)
2464{
f540fe15 2465 struct dos_partition *p;
fb1caca7
KZ
2466
2467 assert(cxt);
2468 assert(cxt->label);
aa36c2cf 2469 assert(fdisk_is_label(cxt, DOS));
fb1caca7
KZ
2470
2471 if (i >= cxt->label->nparts_max)
2472 return -EINVAL;
2473
af0df606 2474 p = self_partition(cxt, i);
fb1caca7
KZ
2475
2476 switch (flag) {
2477 case DOS_FLAG_ACTIVE:
2478 if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
829f4206
KZ
2479 fdisk_warnx(cxt, _("Partition %zu: is an extended "
2480 "partition."), i + 1);
fb1caca7
KZ
2481
2482 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
af0df606 2483 partition_set_changed(cxt, i, 1);
0477369a 2484 fdisk_info(cxt, p->boot_ind ?
f736ae51
KZ
2485 _("The bootable flag on partition %zu is enabled now.") :
2486 _("The bootable flag on partition %zu is disabled now."),
2487 i + 1);
fb1caca7
KZ
2488 break;
2489 default:
2490 return 1;
2491 }
2492
2493 return 0;
2494}
2495
bd85d11f 2496static const struct fdisk_field dos_fields[] =
262002ee
KZ
2497{
2498 /* basic */
bd85d11f
KZ
2499 { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
2500 { FDISK_FIELD_BOOT, N_("Boot"), 1, 0 },
2501 { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
2502 { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
2503 { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
2504 { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
2505 { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
2506 { FDISK_FIELD_TYPEID, N_("Id"), 2, FDISK_FIELDFL_NUMBER },
2507 { FDISK_FIELD_TYPE, N_("Type"), 0.1, 0 },
262002ee
KZ
2508
2509 /* expert mode */
bd85d11f
KZ
2510 { FDISK_FIELD_SADDR, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2511 { FDISK_FIELD_EADDR, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2512 { FDISK_FIELD_ATTR, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL }
262002ee
KZ
2513
2514};
2515
0c5d095e 2516static const struct fdisk_label_operations dos_operations =
b8855c86 2517{
0c5d095e
KZ
2518 .probe = dos_probe_label,
2519 .write = dos_write_disklabel,
2520 .verify = dos_verify_disklabel,
2521 .create = dos_create_disklabel,
f32a9be4 2522 .locate = dos_locate_disklabel,
5989556a 2523 .get_item = dos_get_disklabel_item,
35b1f0a4 2524 .set_id = dos_set_disklabel_id,
1cf67f1c 2525
262002ee 2526 .get_part = dos_get_partition,
0ecf3ab5 2527 .set_part = dos_set_partition,
77d6a70a 2528 .add_part = dos_add_partition,
e11c6684 2529 .del_part = dos_delete_partition,
5989556a 2530 .reorder = dos_reorder,
262002ee 2531
fb1caca7 2532 .part_toggle_flag = dos_toggle_partition_flag,
8c0a7f91 2533 .part_is_used = dos_partition_is_used,
47b8e7c0 2534
cf3808e4 2535 .reset_alignment = dos_reset_alignment,
ddec0e40
KZ
2536
2537 .deinit = dos_deinit,
b8855c86 2538};
0c5d095e
KZ
2539
2540/*
2541 * allocates DOS in-memory stuff
2542 */
01aec449 2543struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt __attribute__ ((__unused__)))
0c5d095e
KZ
2544{
2545 struct fdisk_label *lb;
2546 struct fdisk_dos_label *dos;
2547
0c5d095e
KZ
2548 dos = calloc(1, sizeof(*dos));
2549 if (!dos)
2550 return NULL;
2551
2552 /* initialize generic part of the driver */
2553 lb = (struct fdisk_label *) dos;
2554 lb->name = "dos";
53b422ab 2555 lb->id = FDISK_DISKLABEL_DOS;
0c5d095e
KZ
2556 lb->op = &dos_operations;
2557 lb->parttypes = dos_parttypes;
a745611d 2558 lb->nparttypes = ARRAY_SIZE(dos_parttypes) - 1;
bd85d11f
KZ
2559 lb->fields = dos_fields;
2560 lb->nfields = ARRAY_SIZE(dos_fields);
0c5d095e 2561
2dd2880f
KZ
2562 lb->geom_min.sectors = 1;
2563 lb->geom_min.heads = 1;
2564 lb->geom_min.cylinders = 1;
2565
2566 lb->geom_max.sectors = 63;
280b9754 2567 lb->geom_max.heads = 255;
2dd2880f
KZ
2568 lb->geom_max.cylinders = 1048576;
2569
0c5d095e
KZ
2570 return lb;
2571}
852ce62b 2572
b7d101a2
KZ
2573/**
2574 * fdisk_dos_enable_compatible:
2575 * @lb: DOS label (see fdisk_get_label())
2576 * @enable: 0 or 1
2577 *
2578 * Enables deprecated DOS compatible mode, in this mode library checks for
2579 * cylinders boundary, cases about CHS addressing and another obscure things.
2580 *
2581 * Returns: 0 on success, <0 on error.
852ce62b 2582 */
852ce62b
KZ
2583int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
2584{
2585 struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
2586
2587 if (!lb)
2588 return -EINVAL;
2589
2590 dos->compatible = enable;
0b52b94c
KZ
2591 if (enable)
2592 lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
852ce62b
KZ
2593 return 0;
2594}
2595
b7d101a2
KZ
2596/**
2597 * fdisk_dos_is_compatible:
2598 * @lb: DOS label
2599 *
2600 * Returns: 0 if DOS compatibility disabled, 1 if enabled
2601 */
852ce62b
KZ
2602int fdisk_dos_is_compatible(struct fdisk_label *lb)
2603{
2604 return ((struct fdisk_dos_label *) lb)->compatible;
2605}