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