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