]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libfdisk/src/dos.c
misc: fix warnings "unused parameter" [-Wunused-parameter]
[thirdparty/util-linux.git] / libfdisk / src / dos.c
CommitLineData
e2ee9178 1/*
c10937dc 2 *
50992267
KZ
3 * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
4 * 2012 Davidlohr Bueso <dave@gnu.org>
c10937dc
KZ
5 *
6 * This is re-written version for libfdisk, the original was fdiskdoslabel.c
7 * from util-linux fdisk.
e2ee9178 8 */
ddec0e40 9#include "c.h"
e2ee9178 10#include "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
9ffeb235 618static int dos_create_disklabel(struct fdisk_context *cxt)
e2ee9178 619{
95085ec5
KZ
620 unsigned int id = 0;
621 int rc, has_id = 0;
3d919d95 622 struct fdisk_dos_label *l;
e2ee9178 623
9ffeb235
KZ
624 assert(cxt);
625 assert(cxt->label);
aa36c2cf 626 assert(fdisk_is_label(cxt, DOS));
9ffeb235 627
88141067 628 DBG(LABEL, ul_debug("DOS: creating new disklabel"));
9c825fa0 629
95085ec5
KZ
630 if (cxt->script) {
631 char *end = NULL;
632 const char *s = fdisk_script_get_header(cxt->script, "label-id");
633
634 if (s) {
635 errno = 0;
636 id = strtol(s, &end, 16);
637 if (!errno && end && s < end)
638 has_id = 1;
639 }
640 }
641
e2ee9178 642 /* random disk signature */
95085ec5
KZ
643 if (!has_id)
644 random_get_bytes(&id, sizeof(id));
e2ee9178 645
3457d90e
KZ
646 if (fdisk_has_protected_bootbits(cxt))
647 rc = fdisk_init_firstsector_buffer(cxt, 0, MBR_PT_BOOTBITS_SIZE);
648 else
649 rc = fdisk_init_firstsector_buffer(cxt, 0, 0);
7c2cfb18
KZ
650 if (rc)
651 return rc;
3d919d95
KZ
652 dos_init(cxt);
653
654 l = self_label(cxt);
e2ee9178
DB
655
656 /* Generate an MBR ID for this disk */
67987b47 657 mbr_set_id(cxt->firstsector, id);
3d919d95
KZ
658 l->non_pt_changed = 1;
659 fdisk_label_set_changed(cxt->label, 1);
e2ee9178
DB
660
661 /* Put MBR signature */
67987b47 662 mbr_set_magic(cxt->firstsector);
ac1a559a 663
0477369a 664 fdisk_info(cxt, _("Created a new DOS disklabel with disk "
ac1a559a 665 "identifier 0x%08x."), id);
a71601af 666 return 0;
e2ee9178
DB
667}
668
35b1f0a4 669static int dos_set_disklabel_id(struct fdisk_context *cxt)
e2ee9178 670{
0a49e5d5
KZ
671 char *end = NULL, *str = NULL;
672 unsigned int id, old;
35b1f0a4 673 struct fdisk_dos_label *l;
0a49e5d5 674 int rc;
e2ee9178 675
35b1f0a4
KZ
676 assert(cxt);
677 assert(cxt->label);
aa36c2cf 678 assert(fdisk_is_label(cxt, DOS));
35b1f0a4 679
88141067 680 DBG(LABEL, ul_debug("DOS: setting Id"));
9c825fa0 681
35b1f0a4 682 l = self_label(cxt);
0a49e5d5
KZ
683 old = mbr_get_id(cxt->firstsector);
684 rc = fdisk_ask_string(cxt,
a0862e26 685 _("Enter the new disk identifier"), &str);
0a49e5d5
KZ
686 if (rc)
687 return rc;
e2ee9178 688
0a49e5d5
KZ
689 errno = 0;
690 id = strtoul(str, &end, 0);
691 if (errno || str == end || (end && *end)) {
692 fdisk_warnx(cxt, _("Incorrect value."));
693 return -EINVAL;
694 }
e2ee9178 695
e2ee9178 696
0a49e5d5 697 mbr_set_id(cxt->firstsector, id);
8b3d9241 698 l->non_pt_changed = 1;
bddd84e7 699 fdisk_label_set_changed(cxt->label, 1);
ac1a559a 700
0477369a 701 fdisk_info(cxt, _("Disk identifier changed from 0x%08x to 0x%08x."),
ac1a559a 702 old, id);
0a49e5d5 703 return 0;
e2ee9178
DB
704}
705
9a5e29e9
KZ
706static void get_partition_table_geometry(struct fdisk_context *cxt,
707 unsigned int *ph, unsigned int *ps)
708{
67987b47 709 unsigned char *bufp = cxt->firstsector;
f540fe15 710 struct dos_partition *p;
9a5e29e9
KZ
711 int i, h, s, hh, ss;
712 int first = 1;
713 int bad = 0;
714
715 hh = ss = 0;
60c1b036 716 for (i = 0; i < 4; i++) {
e8bf93de 717 p = mbr_get_partition(bufp, i);
9a5e29e9 718 if (p->sys_ind != 0) {
da967236
KZ
719 h = p->eh + 1;
720 s = (p->es & 077);
9a5e29e9
KZ
721 if (first) {
722 hh = h;
723 ss = s;
724 first = 0;
725 } else if (hh != h || ss != s)
726 bad = 1;
727 }
728 }
729
730 if (!first && !bad) {
731 *ph = hh;
732 *ps = ss;
733 }
cf3808e4 734
88141067 735 DBG(LABEL, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps));
9a5e29e9
KZ
736}
737
9ffeb235 738static int dos_reset_alignment(struct fdisk_context *cxt)
cf3808e4 739{
9ffeb235
KZ
740 assert(cxt);
741 assert(cxt->label);
aa36c2cf 742 assert(fdisk_is_label(cxt, DOS));
9ffeb235 743
cf3808e4 744 /* overwrite necessary stuff by DOS deprecated stuff */
852ce62b 745 if (is_dos_compatible(cxt)) {
88141067 746 DBG(LABEL, ul_debug("DOS: reseting alignemnt for DOS-comaptiblem PT"));
cf3808e4
KZ
747 if (cxt->geom.sectors)
748 cxt->first_lba = cxt->geom.sectors; /* usually 63 */
749
750 cxt->grain = cxt->sector_size; /* usually 512 */
751 }
cf3808e4
KZ
752
753 return 0;
754}
9a5e29e9 755
9ed268d0
KZ
756/* TODO: move to include/pt-dos.h and share with libblkid */
757#define AIX_MAGIC_STRING "\xC9\xC2\xD4\xC1"
758#define AIX_MAGIC_STRLEN (sizeof(AIX_MAGIC_STRING) - 1)
759
9ffeb235 760static int dos_probe_label(struct fdisk_context *cxt)
e2ee9178 761{
e3661531 762 size_t i;
9a5e29e9 763 unsigned int h = 0, s = 0;
e2ee9178 764
e3661531
KZ
765 assert(cxt);
766 assert(cxt->label);
aa36c2cf 767 assert(fdisk_is_label(cxt, DOS));
e3661531 768
9ed268d0
KZ
769 /* ignore disks with AIX magic number */
770 if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0)
771 return 0;
772
67987b47 773 if (!mbr_is_valid_magic(cxt->firstsector))
e2ee9178
DB
774 return 0;
775
e53ced85 776 dos_init(cxt);
e2ee9178 777
9a5e29e9
KZ
778 get_partition_table_geometry(cxt, &h, &s);
779 if (h && s) {
780 cxt->geom.heads = h;
781 cxt->geom.sectors = s;
782 }
783
e2ee9178 784 for (i = 0; i < 4; i++) {
af0df606 785 struct pte *pe = self_pte(cxt, i);
e2ee9178 786
d0a870c5 787 if (is_used_partition(pe->pt_entry))
e3661531
KZ
788 cxt->label->nparts_cur++;
789
6864f168 790 if (IS_EXTENDED (pe->pt_entry->sys_ind)) {
e3661531 791 if (cxt->label->nparts_max != 4)
fcea064b 792 fdisk_warnx(cxt, _(
829f4206 793 "Ignoring extra extended partition %zu"),
fcea064b 794 i + 1);
e2ee9178 795 else
7737f698 796 read_extended(cxt, i);
e2ee9178
DB
797 }
798 }
799
e3661531 800 for (i = 3; i < cxt->label->nparts_max; i++) {
af0df606 801 struct pte *pe = self_pte(cxt, i);
bcddbe96 802 struct fdisk_dos_label *l = self_label(cxt);
e2ee9178 803
67188340 804 if (!mbr_is_valid_magic(pe->sectorbuffer)) {
fcea064b 805 fdisk_info(cxt, _(
bcddbe96
KZ
806 "Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
807 "be corrected by w(rite)."),
6fe71269
KZ
808 pe->sectorbuffer[510],
809 pe->sectorbuffer[511],
810 i + 1);
bcddbe96
KZ
811 partition_set_changed(cxt, i, 1);
812
813 /* mark also extended as changed to update the first EBR
814 * in situation that there is no logical partitions at all */
815 partition_set_changed(cxt, l->ext_index, 1);
e2ee9178
DB
816 }
817 }
818
819 return 1;
820}
821
e53ced85 822static void set_partition(struct fdisk_context *cxt,
0073a4cf
KZ
823 int i, int doext, fdisk_sector_t start,
824 fdisk_sector_t stop, int sysid, int boot)
9dea2923 825{
af0df606 826 struct pte *pe = self_pte(cxt, i);
f540fe15 827 struct dos_partition *p;
0073a4cf 828 fdisk_sector_t offset;
9dea2923 829
ecf40cda
KZ
830 assert(!FDISK_IS_UNDEF(start));
831 assert(!FDISK_IS_UNDEF(stop));
80063996 832
9dea2923 833 if (doext) {
fcea064b 834 struct fdisk_dos_label *l = self_label(cxt);
af0df606 835 p = pe->ex_entry;
fcea064b 836 offset = l->ext_offset;
9dea2923 837 } else {
af0df606
KZ
838 p = pe->pt_entry;
839 offset = pe->offset;
9dea2923 840 }
a60087aa 841
1bb387bd 842 DBG(LABEL, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, size=%zu, sysid=%02x",
a60087aa 843 i, doext ? " [extended]" : "",
613596b3
KZ
844 (size_t) offset,
845 (size_t) (start - offset),
846 (size_t) (stop - start + 1),
847 sysid));
a60087aa 848
50f7514e 849 p->boot_ind = boot ? ACTIVE_FLAG : 0;
9dea2923 850 p->sys_ind = sysid;
b44244cb
KZ
851 dos_partition_set_start(p, start - offset);
852 dos_partition_set_size(p, stop - start + 1);
9dea2923 853
852ce62b 854 if (is_dos_compatible(cxt) && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
24cd580b 855 start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
da967236 856 set_hsc(p->bh, p->bs, p->bc, start);
852ce62b 857 if (is_dos_compatible(cxt) && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
24cd580b 858 stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
da967236 859 set_hsc(p->eh, p->es, p->ec, stop);
af0df606 860 partition_set_changed(cxt, i, 1);
9dea2923
DB
861}
862
0073a4cf
KZ
863static fdisk_sector_t get_unused_start(struct fdisk_context *cxt,
864 int part_n, fdisk_sector_t start,
865 fdisk_sector_t first[], fdisk_sector_t last[])
9dea2923 866{
e3661531 867 size_t i;
9dea2923 868
e3661531 869 for (i = 0; i < cxt->label->nparts_max; i++) {
0073a4cf 870 fdisk_sector_t lastplusoff;
af0df606 871 struct pte *pe = self_pte(cxt, i);
9dea2923 872
af0df606 873 if (start == pe->offset)
cf3808e4
KZ
874 start += cxt->first_lba;
875 lastplusoff = last[i] + ((part_n < 4) ? 0 : cxt->first_lba);
9dea2923
DB
876 if (start >= first[i] && start <= lastplusoff)
877 start = lastplusoff + 1;
878 }
879
880 return start;
881}
882
e3661531 883static void fill_bounds(struct fdisk_context *cxt,
0073a4cf 884 fdisk_sector_t *first, fdisk_sector_t *last)
cc0c4e56 885{
e3661531 886 size_t i;
af0df606 887 struct pte *pe = self_pte(cxt, 0);
f540fe15 888 struct dos_partition *p;
cc0c4e56 889
e3661531 890 for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
6864f168 891 p = pe->pt_entry;
d0a870c5 892 if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
cc0c4e56
KZ
893 first[i] = 0xffffffff;
894 last[i] = 0;
895 } else {
61eaad50 896 first[i] = get_abs_partition_start(pe);
1b92d076 897 last[i] = get_abs_partition_end(pe);
cc0c4e56
KZ
898 }
899 }
900}
901
77d6a70a 902static int get_start_from_user( struct fdisk_context *cxt,
0073a4cf
KZ
903 fdisk_sector_t *start,
904 fdisk_sector_t low,
905 fdisk_sector_t dflt,
906 fdisk_sector_t limit,
77d6a70a 907 struct fdisk_partition *pa)
9dea2923 908{
77d6a70a
KZ
909 assert(start);
910
911 /* try to use tepmlate from 'pa' */
912 if (pa && pa->start_follow_default)
913 *start = dflt;
914
ecf40cda 915 else if (pa && fdisk_partition_has_start(pa)) {
88141067 916 DBG(LABEL, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
20f878fe 917 (uintmax_t) pa->start, (uintmax_t) low, (uintmax_t) limit));
77d6a70a 918 *start = pa->start;
20f878fe 919 if (*start < low || *start > limit) {
a3e37fa7
KZ
920 fdisk_warnx(cxt, _("Start sector %ju out of range."),
921 (uintmax_t) *start);
77d6a70a 922 return -ERANGE;
20f878fe 923 }
77d6a70a
KZ
924 } else {
925 /* ask user by dialog */
926 struct fdisk_ask *ask = fdisk_new_ask();
927 int rc;
928
929 if (!ask)
930 return -ENOMEM;
931 fdisk_ask_set_query(ask,
6a632136 932 fdisk_use_cylinders(cxt) ?
77d6a70a
KZ
933 _("First cylinder") : _("First sector"));
934 fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
935 fdisk_ask_number_set_low(ask, fdisk_cround(cxt, low));
936 fdisk_ask_number_set_default(ask, fdisk_cround(cxt, dflt));
937 fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
938
939 rc = fdisk_do_ask(cxt, ask);
940 *start = fdisk_ask_number_get_result(ask);
a3d83488 941 fdisk_unref_ask(ask);
77d6a70a
KZ
942 if (rc)
943 return rc;
6a632136 944 if (fdisk_use_cylinders(cxt)) {
77d6a70a 945 *start = (*start - 1)
6a632136 946 * fdisk_get_units_per_sector(cxt);
77d6a70a
KZ
947 if (*start < low)
948 *start = low;
949 }
950 }
951
c3bc7483 952 DBG(LABEL, ul_debug("DOS: start is %ju", (uintmax_t) *start));
77d6a70a
KZ
953 return 0;
954}
955
0073a4cf 956static fdisk_sector_t get_possible_last(struct fdisk_context *cxt, size_t n)
333c3761 957{
0073a4cf 958 fdisk_sector_t limit;
333c3761
KZ
959
960 if (n >= 4) {
961 /* logical partitions */
962 struct fdisk_dos_label *l = self_label(cxt);
963 struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
964
965 if (!ext_pe)
966 return 0;
967 limit = get_abs_partition_end(ext_pe);
968 } else {
969 /* primary partitions */
970 if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
971 limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
972 else
973 limit = cxt->total_sectors - 1;
974
975 if (limit > UINT_MAX)
976 limit = UINT_MAX;
977 }
978
979 DBG(LABEL, ul_debug("DOS: last possible sector for #%zu is %ju",
980 n, (uintmax_t) limit));
981 return limit;
982}
983
984/* returns last free sector for area addressed by @start, the first[] and
985 * last[] are fill_bounds() results */
0073a4cf
KZ
986static fdisk_sector_t get_unused_last(struct fdisk_context *cxt, size_t n,
987 fdisk_sector_t start,
71177ab8 988 fdisk_sector_t first[])
333c3761
KZ
989{
990 size_t i;
0073a4cf 991 fdisk_sector_t limit = get_possible_last(cxt, n);
333c3761
KZ
992
993 for (i = 0; i < cxt->label->nparts_max; i++) {
994 struct pte *pe = self_pte(cxt, i);
995
996 if (start < pe->offset && limit >= pe->offset)
997 limit = pe->offset - 1;
998 if (start < first[i] && limit >= first[i])
999 limit = first[i] - 1;
1000 }
1001
1002 DBG(LABEL, ul_debug("DOS: unused sector for #%zu is %ju",
1003 n, (uintmax_t) limit));
1004 return limit;
1005}
1006
77d6a70a
KZ
1007static int add_partition(struct fdisk_context *cxt, size_t n,
1008 struct fdisk_partition *pa)
1009{
1010 int sys, read = 0, rc, isrel = 0;
e3661531 1011 size_t i;
fcea064b 1012 struct fdisk_dos_label *l = self_label(cxt);
af0df606 1013 struct dos_partition *p = self_partition(cxt, n);
1b92d076 1014 struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
7c43fd23
KZ
1015 struct fdisk_ask *ask = NULL;
1016
0073a4cf 1017 fdisk_sector_t start, stop = 0, limit, temp,
e3661531
KZ
1018 first[cxt->label->nparts_max],
1019 last[cxt->label->nparts_max];
9dea2923 1020
88141067 1021 DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
9c825fa0 1022
a745611d 1023 sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
ed470672 1024
d0a870c5 1025 if (is_used_partition(p)) {
829f4206 1026 fdisk_warnx(cxt, _("Partition %zu is already defined. "
e39966c6 1027 "Delete it before re-adding it."),
829f4206 1028 n + 1);
8254c3a5 1029 return -EINVAL;
9dea2923 1030 }
e3661531 1031 fill_bounds(cxt, first, last);
333c3761
KZ
1032 limit = get_possible_last(cxt, n);
1033
9dea2923 1034 if (n < 4) {
aa36c2cf 1035 if (cxt->parent && fdisk_is_label(cxt->parent, GPT))
433d05ff 1036 start = 1; /* Bad boy modifies hybrid MBR */
bcdeacd7 1037 else {
ecf40cda
KZ
1038 if (cxt->script && pa && fdisk_partition_has_start(pa)
1039 && pa->start < cxt->first_lba
bcdeacd7
KZ
1040 && pa->start >= 1)
1041 fdisk_set_first_lba(cxt, 1);
1042
433d05ff 1043 start = cxt->first_lba;
bcdeacd7 1044 }
433d05ff 1045
fcea064b 1046 if (l->ext_offset) {
1b92d076 1047 assert(ext_pe);
fcea064b 1048 first[l->ext_index] = l->ext_offset;
1b92d076 1049 last[l->ext_index] = get_abs_partition_end(ext_pe);
9dea2923
DB
1050 }
1051 } else {
1b92d076 1052 assert(ext_pe);
bcdeacd7 1053
ecf40cda
KZ
1054 if (cxt->script && pa && fdisk_partition_has_start(pa)
1055 && pa->start >= l->ext_offset
bcdeacd7
KZ
1056 && pa->start < l->ext_offset + cxt->first_lba)
1057 fdisk_set_first_lba(cxt, 1);
1058
1059 start = l->ext_offset + cxt->first_lba;
9dea2923 1060 }
333c3761 1061
6a632136 1062 if (fdisk_use_cylinders(cxt))
80063996 1063 for (i = 0; i < cxt->label->nparts_max; i++) {
77d6a70a 1064 first[i] = (fdisk_cround(cxt, first[i]) - 1)
6a632136 1065 * fdisk_get_units_per_sector(cxt);
80063996 1066 }
9dea2923 1067
0e07540d
KZ
1068 /*
1069 * Ask for first sector
1070 */
9dea2923 1071 do {
0073a4cf 1072 fdisk_sector_t dflt, aligned;
9dea2923
DB
1073
1074 temp = start;
cf3808e4 1075 dflt = start = get_unused_start(cxt, n, start, first, last);
9dea2923 1076
ecf40cda 1077 if (n >= 4 && pa && fdisk_partition_has_start(pa) && cxt->script
bcdeacd7
KZ
1078 && cxt->first_lba > 1
1079 && temp == start - cxt->first_lba) {
1080 fdisk_set_first_lba(cxt, 1);
1081 start = pa->start;
1082 }
1083
9dea2923
DB
1084 /* the default sector should be aligned and unused */
1085 do {
9475cc78 1086 aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
cf3808e4 1087 dflt = get_unused_start(cxt, n, aligned, first, last);
9dea2923
DB
1088 } while (dflt != aligned && dflt > aligned && dflt < limit);
1089
1090 if (dflt >= limit)
1091 dflt = start;
1092 if (start > limit)
1093 break;
6a632136 1094 if (start >= temp + fdisk_get_units_per_sector(cxt)
fcea064b 1095 && read) {
ac1a559a 1096 fdisk_info(cxt, _("Sector %llu is already allocated."),
fcea064b 1097 temp);
9dea2923
DB
1098 temp = start;
1099 read = 0;
ecf40cda
KZ
1100 if (pa && (fdisk_partition_has_start(pa) ||
1101 pa->start_follow_default))
3186f4a9 1102 break;
9dea2923 1103 }
0e07540d 1104
9dea2923 1105 if (!read && start == temp) {
77d6a70a 1106 rc = get_start_from_user(cxt, &start, temp, dflt, limit, pa);
0e07540d
KZ
1107 if (rc)
1108 return rc;
9dea2923
DB
1109 read = 1;
1110 }
1111 } while (start != temp || !read);
0e07540d 1112
613596b3
KZ
1113 if (n == 4) {
1114 /* The first EBR is stored at begin of the extended partition */
1115 struct pte *pe = self_pte(cxt, n);
1116 pe->offset = l->ext_offset;
1117
1118 } else if (n > 4) {
1119 /* The second (and another) EBR */
af0df606 1120 struct pte *pe = self_pte(cxt, n);
9dea2923 1121
cf3808e4 1122 pe->offset = start - cxt->first_lba;
fcea064b 1123 if (pe->offset == l->ext_offset) { /* must be corrected */
9dea2923 1124 pe->offset++;
cf3808e4 1125 if (cxt->first_lba == 1)
9dea2923
DB
1126 start++;
1127 }
1128 }
1129
71177ab8 1130 limit = get_unused_last(cxt, n, start, first);
9dea2923 1131
9dea2923 1132 if (start > limit) {
eed0e7b9 1133 fdisk_warnx(cxt, _("No free sectors available."));
8254c3a5 1134 return -ENOSPC;
9dea2923 1135 }
77d6a70a
KZ
1136
1137 /*
1138 * Ask for last sector
1139 */
1140 if (fdisk_cround(cxt, start) == fdisk_cround(cxt, limit))
1141 stop = limit;
1142 else if (pa && pa->end_follow_default)
9dea2923 1143 stop = limit;
ecf40cda 1144 else if (pa && fdisk_partition_has_size(pa)) {
7c43fd23 1145 stop = start + pa->size;
bcdeacd7 1146 isrel = pa->size_explicit ? 0 : 1;
7c43fd23
KZ
1147 if (!isrel && stop > start)
1148 stop -= 1;
9dea2923 1149 } else {
77d6a70a 1150 /* ask user by dialog */
7c43fd23
KZ
1151 for (;;) {
1152 if (!ask)
1153 ask = fdisk_new_ask();
1154 else
1155 fdisk_reset_ask(ask);
1156 if (!ask)
1157 return -ENOMEM;
7c43fd23
KZ
1158 fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
1159
1160 if (fdisk_use_cylinders(cxt)) {
1161 fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
1162 fdisk_ask_number_set_unit(ask,
1163 cxt->sector_size *
1164 fdisk_get_units_per_sector(cxt));
1165 } else {
1166 fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
1167 fdisk_ask_number_set_unit(ask,cxt->sector_size);
1168 }
9dea2923 1169
7c43fd23
KZ
1170 fdisk_ask_number_set_low(ask, fdisk_cround(cxt, start));
1171 fdisk_ask_number_set_default(ask, fdisk_cround(cxt, limit));
1172 fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
1173 fdisk_ask_number_set_base(ask, fdisk_cround(cxt, start)); /* base for relative input */
9dea2923 1174
7c43fd23
KZ
1175 rc = fdisk_do_ask(cxt, ask);
1176 if (rc)
1177 goto done;
0e07540d 1178
7c43fd23
KZ
1179 stop = fdisk_ask_number_get_result(ask);
1180 isrel = fdisk_ask_number_is_relative(ask);
535748a6
KZ
1181 if (fdisk_use_cylinders(cxt)) {
1182 stop = stop * fdisk_get_units_per_sector(cxt) - 1;
1183 if (stop >limit)
1184 stop = limit;
1185 }
7c43fd23 1186
8c73e509
KZ
1187 if (stop >= start && stop <= limit)
1188 break;
8c73e509 1189 fdisk_warnx(cxt, _("Value out of range."));
7c43fd23 1190 }
77d6a70a
KZ
1191 }
1192
bcdeacd7
KZ
1193 DBG(LABEL, ul_debug("DOS: raw stop: %ju", (uintmax_t) stop));
1194
77d6a70a
KZ
1195 if (stop > limit)
1196 stop = limit;
1197
d527d2dd 1198 if (isrel && stop - start < (cxt->grain / fdisk_get_sector_size(cxt))) {
7c43fd23 1199 /* Don't try to be smart on very small partitions and don't align so small sizes */
68fe4b28 1200 isrel = 0;
09a4ca5e
KZ
1201 if (stop > start)
1202 stop -= 1;
99923244 1203 DBG(LABEL, ul_debug("DOS: don't align end os tiny partition [start=%ju, stop=%ju, grain=%lu]",
d527d2dd
KZ
1204 start, stop, cxt->grain));
1205 }
68fe4b28 1206
77d6a70a
KZ
1207 if (stop < limit) {
1208 if (isrel && alignment_required(cxt)) {
9dea2923 1209 /* the last sector has not been exactly requested (but
0e07540d
KZ
1210 * defined by +size{K,M,G} convention), so be smart and
1211 * align the end of the partition. The next partition
1212 * will start at phy.block boundary.
9dea2923 1213 */
1bb387bd
KZ
1214 stop = fdisk_align_lba_in_range(cxt, stop, start, limit);
1215 if (stop > start)
1216 stop -= 1;
9dea2923
DB
1217 if (stop > limit)
1218 stop = limit;
1bb387bd 1219 DBG(LABEL, ul_debug("DOS: aligned stop: %ju", (uintmax_t) stop));
9dea2923
DB
1220 }
1221 }
1222
4d156d92 1223 set_partition(cxt, n, 0, start, stop, sys, fdisk_partition_is_bootable(pa));
af0df606
KZ
1224 if (n > 4) {
1225 struct pte *pe = self_pte(cxt, n);
bb58112e 1226 set_partition(cxt, n - 1, 1, pe->offset, stop,
7bc31ada 1227 MBR_DOS_EXTENDED_PARTITION, 0);
af0df606 1228 }
9dea2923 1229
0ecf3ab5
KZ
1230 /* report */
1231 {
1232 struct fdisk_parttype *t =
1233 fdisk_label_get_parttype_from_code(cxt->label, sys);
1234 fdisk_info_new_partition(cxt, n + 1, start, stop, t);
dfc6db2a 1235 fdisk_unref_parttype(t);
0ecf3ab5
KZ
1236 }
1237
1238
bb58112e 1239 if (IS_EXTENDED(sys)) {
af0df606 1240 struct pte *pen = self_pte(cxt, n);
9dea2923 1241
fcea064b 1242 l->ext_index = n;
613596b3 1243 l->ext_offset = start;
6864f168 1244 pen->ex_entry = p;
9dea2923 1245 }
8254c3a5 1246
bddd84e7 1247 fdisk_label_set_changed(cxt->label, 1);
7c43fd23 1248 rc = 0;
7c43fd23
KZ
1249done:
1250 fdisk_unref_ask(ask);
1251 return rc;
9dea2923
DB
1252}
1253
c3bc7483
KZ
1254static int add_logical(struct fdisk_context *cxt,
1255 struct fdisk_partition *pa,
1256 size_t *partno)
9dea2923 1257{
613596b3 1258 struct pte *pe;
28d79d9b 1259 int rc;
af0df606 1260
e3661531 1261 assert(cxt);
c3bc7483 1262 assert(partno);
e3661531 1263 assert(cxt->label);
613596b3 1264 assert(self_label(cxt)->ext_offset);
e3661531 1265
613596b3
KZ
1266 DBG(LABEL, ul_debug("DOS: nparts max: %zu", cxt->label->nparts_max));
1267 pe = self_pte(cxt, cxt->label->nparts_max);
af0df606 1268
613596b3 1269 if (!pe->sectorbuffer) {
af0df606
KZ
1270 pe->sectorbuffer = calloc(1, cxt->sector_size);
1271 if (!pe->sectorbuffer)
1272 return -ENOMEM;
613596b3
KZ
1273 DBG(LABEL, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1274 cxt->label->nparts_max, pe->sectorbuffer));
ddec0e40 1275 pe->private_sectorbuffer = 1;
9dea2923 1276 }
613596b3
KZ
1277 pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
1278 pe->ex_entry = pe->pt_entry + 1;
1279 pe->offset = 0;
1280 partition_set_changed(cxt, cxt->label->nparts_max, 1);
1281
1282 cxt->label->nparts_max++;
1283
a536cf71
KZ
1284 /* this message makes sense only when we use extended/primary/logical
1285 * dialog. The dialog is disable for scripts, see dos_add_partition() */
1286 if (!cxt->script)
1287 fdisk_info(cxt, _("Adding logical partition %zu"),
1288 cxt->label->nparts_max);
c3bc7483 1289 *partno = cxt->label->nparts_max - 1;
28d79d9b
KZ
1290 rc = add_partition(cxt, *partno, pa);
1291
1292 if (rc) {
1293 /* reset on error */
1294 cxt->label->nparts_max--;
1295 pe->pt_entry = NULL;
1296 pe->ex_entry = NULL;
1297 pe->offset = 0;
1298 pe->changed = 0;
1299 }
1300
1301 return rc;
9dea2923
DB
1302}
1303
e3661531 1304static void check(struct fdisk_context *cxt, size_t n,
5dfca634
KZ
1305 unsigned int h, unsigned int s, unsigned int c,
1306 unsigned int start)
1307{
1308 unsigned int total, real_s, real_c;
1309
63fb717d
KZ
1310 if (!is_dos_compatible(cxt))
1311 return;
1312
5dfca634
KZ
1313 real_s = sector(s) - 1;
1314 real_c = cylinder(s, c);
9bb8caff 1315 total = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
fcea064b 1316
5dfca634 1317 if (!total)
829f4206 1318 fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
5dfca634 1319 if (h >= cxt->geom.heads)
829f4206 1320 fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
fcea064b 1321 "maximum %d"), n, h + 1, cxt->geom.heads);
5dfca634 1322 if (real_s >= cxt->geom.sectors)
829f4206 1323 fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
fcea064b 1324 "maximum %llu"), n, s, cxt->geom.sectors);
5dfca634 1325 if (real_c >= cxt->geom.cylinders)
829f4206 1326 fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
fcea064b
KZ
1327 "maximum %llu"),
1328 n, real_c + 1,
1329 cxt->geom.cylinders);
1330
5dfca634 1331 if (cxt->geom.cylinders <= 1024 && start != total)
829f4206
KZ
1332 fdisk_warnx(cxt, _("Partition %zu: previous sectors %u "
1333 "disagrees with total %u"), n, start, total);
5dfca634
KZ
1334}
1335
1336/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1337 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1338 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1339 * Lubkin Oct. 1991). */
1340
1341static void
1342long2chs(struct fdisk_context *cxt, unsigned long ls,
1343 unsigned int *c, unsigned int *h, unsigned int *s) {
1344 int spc = cxt->geom.heads * cxt->geom.sectors;
1345
1346 *c = ls / spc;
1347 ls = ls % spc;
1348 *h = ls / cxt->geom.sectors;
1349 *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
1350}
1351
f540fe15 1352static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
e3661531 1353 size_t partition)
5dfca634
KZ
1354{
1355 unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
1356 unsigned int pec, peh, pes; /* physical ending c, h, s */
1357 unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
1358 unsigned int lec, leh, les; /* logical ending c, h, s */
1359
1360 if (!is_dos_compatible(cxt))
1361 return;
1362
1363 if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
1364 return; /* do not check extended partitions */
1365
60c1b036 1366 /* physical beginning c, h, s */
da967236
KZ
1367 pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
1368 pbh = p->bh;
1369 pbs = p->bs & 0x3f;
5dfca634 1370
60c1b036 1371 /* physical ending c, h, s */
da967236
KZ
1372 pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
1373 peh = p->eh;
1374 pes = p->es & 0x3f;
5dfca634 1375
60c1b036 1376 /* compute logical beginning (c, h, s) */
b44244cb 1377 long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
5dfca634 1378
60c1b036 1379 /* compute logical ending (c, h, s) */
b44244cb 1380 long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
5dfca634 1381
60c1b036 1382 /* Same physical / logical beginning? */
fcea064b
KZ
1383 if (cxt->geom.cylinders <= 1024
1384 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
829f4206 1385 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
fcea064b
KZ
1386 "beginnings (non-Linux?): "
1387 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1388 partition + 1,
1389 pbc, pbh, pbs,
1390 lbc, lbh, lbs);
5dfca634
KZ
1391 }
1392
60c1b036 1393 /* Same physical / logical ending? */
fcea064b
KZ
1394 if (cxt->geom.cylinders <= 1024
1395 && (pec != lec || peh != leh || pes != les)) {
829f4206 1396 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
fcea064b
KZ
1397 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1398 partition + 1,
1399 pec, peh, pes,
1400 lec, leh, les);
5dfca634
KZ
1401 }
1402
60c1b036 1403 /* Ending on cylinder boundary? */
5dfca634 1404 if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
829f4206 1405 fdisk_warnx(cxt, _("Partition %zu: does not end on "
fcea064b 1406 "cylinder boundary."),
5dfca634
KZ
1407 partition + 1);
1408 }
1409}
1410
9ffeb235 1411static int dos_verify_disklabel(struct fdisk_context *cxt)
2ca61a61 1412{
e3661531 1413 size_t i, j;
0073a4cf
KZ
1414 fdisk_sector_t total = 1, n_sectors = cxt->total_sectors;
1415 fdisk_sector_t first[cxt->label->nparts_max],
1416 last[cxt->label->nparts_max];
f540fe15 1417 struct dos_partition *p;
fcea064b 1418 struct fdisk_dos_label *l = self_label(cxt);
2ca61a61 1419
aa36c2cf 1420 assert(fdisk_is_label(cxt, DOS));
9ffeb235 1421
e3661531
KZ
1422 fill_bounds(cxt, first, last);
1423 for (i = 0; i < cxt->label->nparts_max; i++) {
af0df606 1424 struct pte *pe = self_pte(cxt, i);
2ca61a61 1425
af0df606 1426 p = self_partition(cxt, i);
d0a870c5 1427 if (is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
2ca61a61 1428 check_consistency(cxt, p, i);
61eaad50 1429 if (get_abs_partition_start(pe) < first[i])
fcea064b 1430 fdisk_warnx(cxt, _(
829f4206 1431 "Partition %zu: bad start-of-data."),
fcea064b
KZ
1432 i + 1);
1433
1434 check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
2ca61a61 1435 total += last[i] + 1 - first[i];
fcea064b 1436
11712b66
KZ
1437 if (i == 0)
1438 total += get_abs_partition_start(pe) - 1;
1439
fcea064b 1440 for (j = 0; j < i; j++) {
2ca61a61
DB
1441 if ((first[i] >= first[j] && first[i] <= last[j])
1442 || ((last[i] <= last[j] && last[i] >= first[j]))) {
fcea064b 1443
829f4206
KZ
1444 fdisk_warnx(cxt, _("Partition %zu: "
1445 "overlaps partition %zu."),
fcea064b
KZ
1446 j + 1, i + 1);
1447
2ca61a61
DB
1448 total += first[i] >= first[j] ?
1449 first[i] : first[j];
1450 total -= last[i] <= last[j] ?
1451 last[i] : last[j];
1452 }
fcea064b 1453 }
2ca61a61
DB
1454 }
1455 }
1456
fcea064b 1457 if (l->ext_offset) {
0073a4cf 1458 fdisk_sector_t e_last;
1b92d076
KZ
1459 struct pte *ext_pe = self_pte(cxt, l->ext_index);
1460
1461 e_last = get_abs_partition_end(ext_pe);
2ca61a61 1462
e3661531 1463 for (i = 4; i < cxt->label->nparts_max; i++) {
2ca61a61 1464 total++;
af0df606
KZ
1465 p = self_partition(cxt, i);
1466
2ca61a61 1467 if (!p->sys_ind) {
e3661531 1468 if (i != 4 || i + 1 < cxt->label->nparts_max)
fcea064b 1469 fdisk_warnx(cxt,
829f4206 1470 _("Partition %zu: empty."),
fcea064b
KZ
1471 i + 1);
1472 } else if (first[i] < l->ext_offset
1473 || last[i] > e_last) {
1474
829f4206
KZ
1475 fdisk_warnx(cxt, _("Logical partition %zu: "
1476 "not entirely in partition %zu."),
fcea064b 1477 i + 1, l->ext_index + 1);
2ca61a61 1478 }
2ca61a61
DB
1479 }
1480 }
1481
1482 if (total > n_sectors)
fcea064b
KZ
1483 fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
1484 "than the maximum %llu."), total, n_sectors);
2ca61a61 1485 else if (total < n_sectors)
fcea064b 1486 fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
ac1a559a 1487 "sectors."), n_sectors - total, cxt->sector_size);
2ca61a61
DB
1488
1489 return 0;
1490}
1491
9dea2923
DB
1492/*
1493 * Ask the user for new partition type information (logical, extended).
0f639e54
DB
1494 * This function calls the actual partition adding logic - add_partition.
1495 *
1496 * API callback.
9dea2923 1497 */
77d6a70a 1498static int dos_add_partition(struct fdisk_context *cxt,
c3bc7483
KZ
1499 struct fdisk_partition *pa,
1500 size_t *partno)
9dea2923 1501{
ea987bb5 1502 size_t i, free_primary = 0, free_sectors = 0;
0073a4cf 1503 fdisk_sector_t last = 0, grain;
e3661531 1504 int rc = 0;
e77313a6 1505 struct fdisk_dos_label *l;
1b92d076 1506 struct pte *ext_pe;
c3bc7483 1507 size_t res; /* partno */
9dea2923 1508
9ffeb235
KZ
1509 assert(cxt);
1510 assert(cxt->label);
aa36c2cf 1511 assert(fdisk_is_label(cxt, DOS));
9ffeb235 1512
c3bc7483
KZ
1513 DBG(LABEL, ul_debug("DOS: new partition wanted"));
1514
e77313a6 1515 l = self_label(cxt);
1b92d076 1516 ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
a3e37fa7 1517
c3bc7483
KZ
1518 /*
1519 * partition template (@pa) based partitioning
1520 */
1521
a3e37fa7 1522 /* pa specifies start within extended partition, add logical */
ecf40cda 1523 if (pa && fdisk_partition_has_start(pa) && ext_pe
a3e37fa7 1524 && pa->start >= l->ext_offset
1b92d076 1525 && pa->start <= get_abs_partition_end(ext_pe)) {
c3bc7483
KZ
1526 DBG(LABEL, ul_debug("DOS: pa template %p: add logical", pa));
1527 rc = add_logical(cxt, pa, &res);
a3e37fa7
KZ
1528 goto done;
1529
ea987bb5 1530 /* pa specifies that extended partition is wanted */
df15fb3d 1531 } else if (pa && pa->type && IS_EXTENDED(pa->type->code)) {
c3bc7483 1532 DBG(LABEL, ul_debug("DOS: pa template %p: add extened", pa));
ea987bb5
KZ
1533 if (l->ext_offset) {
1534 fdisk_warnx(cxt, _("Extended partition already exists."));
1535 return -EINVAL;
1536 }
44374b10
BE
1537 rc = get_partition_unused_primary(cxt, pa, &res);
1538 if (rc == 0) {
c3bc7483
KZ
1539 rc = add_partition(cxt, res, pa);
1540 goto done;
1541 }
1542
1543 /* pa specifies start, but outside extended partition */
ecf40cda 1544 } else if (pa && fdisk_partition_has_start(pa) && l->ext_offset) {
c3bc7483 1545 DBG(LABEL, ul_debug("DOS: pa template %p: add primary", pa));
44374b10
BE
1546 rc = get_partition_unused_primary(cxt, pa, &res);
1547 if (rc == 0) {
c3bc7483 1548 rc = add_partition(cxt, res, pa);
ea987bb5
KZ
1549 goto done;
1550 }
a3e37fa7 1551 }
20f878fe 1552
c3bc7483
KZ
1553 /*
1554 * dialog driven partitioning (it does not mean that @pa template is
1555 * completely ignored!)
1556 */
1557
ea987bb5
KZ
1558 /* check if there is space for primary partition */
1559 grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
1560 last = cxt->first_lba;
1561
af0df606
KZ
1562 for (i = 0; i < 4; i++) {
1563 struct dos_partition *p = self_partition(cxt, i);
ea987bb5
KZ
1564
1565 if (is_used_partition(p)) {
0073a4cf 1566 fdisk_sector_t start = dos_partition_get_start(p);
ea987bb5
KZ
1567 if (last + grain <= start)
1568 free_sectors = 1;
1569 last = start + dos_partition_get_size(p);
1570 } else
1571 free_primary++;
af0df606 1572 }
ea987bb5
KZ
1573 if (last + grain < cxt->total_sectors - 1)
1574 free_sectors = 1;
9dea2923 1575
e3661531 1576 if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
fcea064b
KZ
1577 fdisk_info(cxt, _("The maximum number of partitions has "
1578 "been created."));
8254c3a5 1579 return -EINVAL;
9dea2923 1580 }
1bf3a034 1581 rc = 1;
fcea064b 1582
ea987bb5 1583 if (!free_primary || !free_sectors) {
c3bc7483 1584 DBG(LABEL, ul_debug("DOS: primary impossible, add logical"));
fcea064b 1585 if (l->ext_offset) {
ecf40cda 1586 if (!pa || fdisk_partition_has_start(pa)) {
eed0e7b9 1587 const char *msg;
c3bc7483 1588 if (!free_primary)
eed0e7b9 1589 msg = _("All primary partitions are in use.");
c603d076 1590 else
eed0e7b9
KZ
1591 msg = _("All space for primary partitions is in use.");
1592
1593 if (pa && fdisk_partition_has_start(pa)) {
1594 fdisk_warnx(cxt, msg);
1595 return -EINVAL;
1596 } else
1597 fdisk_info(cxt, msg);
c3bc7483
KZ
1598 }
1599 rc = add_logical(cxt, pa, &res);
ea987bb5 1600 } else {
240ddd3a
IW
1601 if (free_primary)
1602 fdisk_info(cxt, _("All space for primary partitions is in use."));
1603 else
33b981f7 1604 /* TRANSLATORS: Try to keep this within 80 characters. */
240ddd3a 1605 fdisk_info(cxt, _("To create more partitions, first replace "
33b981f7 1606 "a primary with an extended partition."));
ea987bb5
KZ
1607 return -EINVAL;
1608 }
e3661531 1609 } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
fcea064b
KZ
1610 fdisk_info(cxt, _("All logical partitions are in use. "
1611 "Adding a primary partition."));
44374b10
BE
1612 rc = get_partition_unused_primary(cxt, pa, &res);
1613 if (rc == 0)
c3bc7483 1614 rc = add_partition(cxt, res, pa);
9dea2923 1615 } else {
20f878fe
KZ
1616 char hint[BUFSIZ];
1617 struct fdisk_ask *ask;
1618 int c;
1619
ea987bb5
KZ
1620 /* the default layout for scripts is to create primary partitions */
1621 if (cxt->script) {
44374b10
BE
1622 rc = get_partition_unused_primary(cxt, pa, &res);
1623 if (rc == 0)
c3bc7483 1624 rc = add_partition(cxt, res, pa);
ea987bb5
KZ
1625 goto done;
1626 }
1627
20f878fe
KZ
1628 ask = fdisk_new_ask();
1629 if (!ask)
1630 return -ENOMEM;
1631 fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
1632 fdisk_ask_set_query(ask, _("Partition type"));
1633 fdisk_ask_menu_set_default(ask, free_primary == 1
1634 && !l->ext_offset ? 'e' : 'p');
1635 snprintf(hint, sizeof(hint),
1636 _("%zu primary, %d extended, %zu free"),
1637 4 - (l->ext_offset ? 1 : 0) - free_primary,
1638 l->ext_offset ? 1 : 0,
1639 free_primary);
1640
1641 fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
1642 if (!l->ext_offset)
1643 fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
1644 else
1645 fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
1646
1647 rc = fdisk_do_ask(cxt, ask);
bd0d850b
KZ
1648 if (rc)
1649 return rc;
20f878fe 1650 fdisk_ask_menu_get_result(ask, &c);
a3d83488 1651 fdisk_unref_ask(ask);
bd0d850b 1652
9dea2923 1653 if (c == 'p') {
44374b10
BE
1654 rc = get_partition_unused_primary(cxt, pa, &res);
1655 if (rc == 0)
c3bc7483 1656 rc = add_partition(cxt, res, pa);
8254c3a5 1657 goto done;
fcea064b 1658 } else if (c == 'l' && l->ext_offset) {
c3bc7483 1659 rc = add_logical(cxt, pa, &res);
8254c3a5 1660 goto done;
fcea064b 1661 } else if (c == 'e' && !l->ext_offset) {
44374b10
BE
1662 rc = get_partition_unused_primary(cxt, pa, &res);
1663 if (rc == 0) {
ecf40cda 1664 struct fdisk_partition *xpa = NULL;
77d6a70a
KZ
1665 struct fdisk_parttype *t;
1666
a745611d 1667 t = fdisk_label_get_parttype_from_code(cxt->label,
bb58112e 1668 MBR_DOS_EXTENDED_PARTITION);
ecf40cda
KZ
1669 if (!pa) {
1670 pa = xpa = fdisk_new_partition();
1671 if (!xpa)
1672 return -ENOMEM;
1673 }
77d6a70a 1674 fdisk_partition_set_type(pa, t);
c3bc7483 1675 rc = add_partition(cxt, res, pa);
ecf40cda
KZ
1676 if (xpa) {
1677 fdisk_unref_partition(xpa);
1678 pa = NULL;
1679 }
ed470672 1680 }
8254c3a5 1681 goto done;
9dea2923 1682 } else
ac1a559a 1683 fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
9dea2923 1684 }
8254c3a5 1685done:
c3bc7483 1686 if (rc == 0) {
e3661531 1687 cxt->label->nparts_cur++;
44374b10 1688 if (partno)
c3bc7483
KZ
1689 *partno = res;
1690 }
8254c3a5 1691 return rc;
9dea2923 1692}
0dc13a38 1693
0073a4cf 1694static int write_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
fae7b1bc 1695 unsigned char *buf)
0dc13a38 1696{
21770662
KZ
1697 int rc;
1698
1699 rc = seek_sector(cxt, secno);
1700 if (rc != 0) {
98aeae96 1701 fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
21770662
KZ
1702 (uintmax_t) secno);
1703 return rc;
1704 }
bcddbe96 1705
88141067 1706 DBG(LABEL, ul_debug("DOS: writting to sector %ju", (uintmax_t) secno));
bcddbe96 1707
fae7b1bc
DB
1708 if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
1709 return -errno;
1710 return 0;
1711}
1712
9ffeb235 1713static int dos_write_disklabel(struct fdisk_context *cxt)
fae7b1bc 1714{
8b3d9241 1715 struct fdisk_dos_label *l = self_label(cxt);
e3661531 1716 size_t i;
8b3d9241 1717 int rc = 0, mbr_changed = 0;
0dc13a38 1718
9ffeb235
KZ
1719 assert(cxt);
1720 assert(cxt->label);
aa36c2cf 1721 assert(fdisk_is_label(cxt, DOS));
9ffeb235 1722
3d919d95
KZ
1723 DBG(LABEL, ul_debug("DOS: write PT requested [label-changed: %d, non-pt-changed: %d]",
1724 cxt->label->changed, l->non_pt_changed));
1725
8b3d9241
KZ
1726 mbr_changed = l->non_pt_changed;
1727
0dc13a38 1728 /* MBR (primary partitions) */
8b3d9241 1729 if (!mbr_changed) {
af0df606
KZ
1730 for (i = 0; i < 4; i++) {
1731 struct pte *pe = self_pte(cxt, i);
1732 if (pe->changed)
8b3d9241 1733 mbr_changed = 1;
af0df606 1734 }
0dc13a38 1735 }
8b3d9241 1736 if (mbr_changed) {
3d919d95 1737 DBG(LABEL, ul_debug("DOS: MBR changed, writting"));
67987b47 1738 mbr_set_magic(cxt->firstsector);
fae7b1bc
DB
1739 rc = write_sector(cxt, 0, cxt->firstsector);
1740 if (rc)
1741 goto done;
0dc13a38 1742 }
8b3d9241 1743
bcddbe96
KZ
1744 if (cxt->label->nparts_max <= 4 && l->ext_offset) {
1745 /* we have empty extended partition, check if the partition has
1746 * been modified and then cleanup possible remaining EBR */
1747 struct pte *pe = self_pte(cxt, l->ext_index);
1748 unsigned char empty[512] = { 0 };
0073a4cf 1749 fdisk_sector_t off = pe ? get_abs_partition_start(pe) : 0;
bcddbe96
KZ
1750
1751 if (off && pe->changed) {
1752 mbr_set_magic(empty);
1753 write_sector(cxt, off, empty);
1754 }
1755 }
1756
0dc13a38 1757 /* EBR (logical partitions) */
e3661531 1758 for (i = 4; i < cxt->label->nparts_max; i++) {
af0df606 1759 struct pte *pe = self_pte(cxt, i);
0dc13a38 1760
28d79d9b 1761 if (!pe->changed || !pe->offset || !pe->sectorbuffer)
613596b3
KZ
1762 continue;
1763
613596b3
KZ
1764 mbr_set_magic(pe->sectorbuffer);
1765 rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
1766 if (rc)
1767 goto done;
0dc13a38 1768 }
fae7b1bc
DB
1769
1770done:
1771 return rc;
0dc13a38 1772}
b8855c86 1773
f32a9be4 1774static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
9bbcf43f 1775 const char **name, uint64_t *offset, size_t *size)
f32a9be4
KZ
1776{
1777 assert(cxt);
1778
1779 *name = NULL;
1780 *offset = 0;
1781 *size = 0;
1782
1783 switch (n) {
1784 case 0:
1785 *name = "MBR";
1786 *offset = 0;
1787 *size = 512;
1788 break;
1789 default:
1790 /* extended partitions */
1791 if (n - 1 + 4 < cxt->label->nparts_max) {
1792 struct pte *pe = self_pte(cxt, n - 1 + 4);
1793
1794 assert(pe->private_sectorbuffer);
1795
1796 *name = "EBR";
9bbcf43f 1797 *offset = (uint64_t) pe->offset * cxt->sector_size;
f32a9be4
KZ
1798 *size = 512;
1799 } else
1800 return 1;
1801 break;
1802 }
1803
1804 return 0;
1805}
1806
dfc96cbf
KZ
1807/*
1808 * Check whether partition entries are ordered by their starting positions.
1809 * Return 0 if OK. Return i if partition i should have been earlier.
1810 * Two separate checks: primary and logical partitions.
1811 */
e3661531 1812static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
dfc96cbf 1813{
e3661531
KZ
1814 size_t last_p_start_pos = 0, p_start_pos;
1815 size_t i, last_i = 0;
dfc96cbf 1816
e3661531 1817 for (i = 0 ; i < cxt->label->nparts_max; i++) {
af0df606
KZ
1818
1819 struct pte *pe = self_pte(cxt, i);
1820 struct dos_partition *p = pe->pt_entry;
1821
dfc96cbf
KZ
1822 if (i == 4) {
1823 last_i = 4;
1824 last_p_start_pos = 0;
1825 }
d0a870c5 1826 if (is_used_partition(p)) {
61eaad50 1827 p_start_pos = get_abs_partition_start(pe);
dfc96cbf
KZ
1828
1829 if (last_p_start_pos > p_start_pos) {
1830 if (prev)
1831 *prev = last_i;
1832 return i;
1833 }
1834
1835 last_p_start_pos = p_start_pos;
1836 last_i = i;
1837 }
1838 }
1839 return 0;
1840}
6d864a49 1841
5989556a 1842static int dos_get_disklabel_item(struct fdisk_context *cxt, struct fdisk_labelitem *item)
1cf67f1c 1843{
5989556a
KZ
1844 int rc = 0;
1845
6d864a49 1846 assert(cxt);
9ffeb235 1847 assert(cxt->label);
aa36c2cf 1848 assert(fdisk_is_label(cxt, DOS));
6d864a49 1849
5989556a
KZ
1850 switch (item->id) {
1851 case FDISK_LABELITEM_ID:
1852 {
1853 unsigned int num = mbr_get_id(cxt->firstsector);
1854 item->name = _("Disk identifier");
1855 item->type = 's';
1856 if (asprintf(&item->data.str, "0x%08x", num) < 0)
1857 rc = -ENOMEM;
1858 break;
1859 }
1860 default:
1861 if (item->id < __FDISK_NLABELITEMS)
1862 rc = 1; /* unssupported generic item */
1863 else
1864 rc = 2; /* out of range */
1865 break;
1866 }
1867
1868 return rc;
1869
262002ee 1870}
1cf67f1c 1871
262002ee
KZ
1872static int dos_get_partition(struct fdisk_context *cxt, size_t n,
1873 struct fdisk_partition *pa)
1874{
1875 struct dos_partition *p;
1876 struct pte *pe;
bd5e8291 1877 struct fdisk_dos_label *lb;
1cf67f1c 1878
262002ee
KZ
1879 assert(cxt);
1880 assert(pa);
1881 assert(cxt->label);
aa36c2cf 1882 assert(fdisk_is_label(cxt, DOS));
1cf67f1c 1883
bd5e8291 1884 lb = self_label(cxt);
262002ee
KZ
1885 pe = self_pte(cxt, n);
1886 p = pe->pt_entry;
1887 pa->used = !is_cleared_partition(p);
1888 if (!pa->used)
1889 return 0;
1cf67f1c 1890
7f01ec7a 1891 pa->type = dos_partition_parttype(cxt, p);
cb4d6804 1892 pa->boot = p->boot_ind == ACTIVE_FLAG ? 1 : 0;
77d6a70a 1893 pa->start = get_abs_partition_start(pe);
1b92d076 1894 pa->size = dos_partition_get_size(p);
e77313a6 1895 pa->container = lb->ext_offset && n == lb->ext_index;
1cf67f1c 1896
bd5e8291
KZ
1897 if (n >= 4)
1898 pa->parent_partno = lb->ext_index;
03643931 1899
cb4d6804 1900 if (p->boot_ind && asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
262002ee 1901 return -ENOMEM;
6d864a49 1902
262002ee 1903 /* start C/H/S */
ecf40cda 1904 if (asprintf(&pa->start_chs, "%d/%d/%d",
262002ee
KZ
1905 cylinder(p->bs, p->bc),
1906 sector(p->bs),
1907 p->bh) < 0)
1908 return -ENOMEM;
1cf67f1c 1909
262002ee 1910 /* end C/H/S */
ecf40cda 1911 if (asprintf(&pa->end_chs, "%d/%d/%d",
262002ee
KZ
1912 cylinder(p->es, p->ec),
1913 sector(p->es),
1914 p->eh) < 0)
1915 return -ENOMEM;
6d864a49 1916
262002ee 1917 return 0;
6d864a49
KZ
1918}
1919
df15fb3d
KZ
1920static int has_logical(struct fdisk_context *cxt)
1921{
1922 size_t i;
1923 struct fdisk_dos_label *l = self_label(cxt);
1924
1925 for (i = 4; i < cxt->label->nparts_max; i++) {
1926 if (l->ptes[i].pt_entry)
1927 return 1;
1928 }
1929 return 0;
1930}
1931
0ecf3ab5
KZ
1932static int dos_set_partition(struct fdisk_context *cxt, size_t n,
1933 struct fdisk_partition *pa)
1934{
df15fb3d 1935 struct fdisk_dos_label *l;
0ecf3ab5
KZ
1936 struct dos_partition *p;
1937 struct pte *pe;
df15fb3d 1938 int orgtype;
0073a4cf 1939 fdisk_sector_t start, size;
0ecf3ab5
KZ
1940
1941 assert(cxt);
1942 assert(pa);
1943 assert(cxt->label);
1944 assert(fdisk_is_label(cxt, DOS));
1945
1946 if (n >= cxt->label->nparts_max)
1947 return -EINVAL;
1948
df15fb3d 1949 l = self_label(cxt);
333c3761
KZ
1950 p = self_partition(cxt, n);
1951 pe = self_pte(cxt, n);
df15fb3d
KZ
1952 orgtype = p->sys_ind;
1953
1954 if (pa->type) {
1955 if (IS_EXTENDED(pa->type->code) && l->ext_offset) {
1956 fdisk_warnx(cxt, _("Extended partition already exists."));
1957 return -EINVAL;
1958 }
1959
1960 if (!pa->type->code)
1961 fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
1962 "Having partitions of type 0 is probably unwise."));
1963
1964 if (IS_EXTENDED(p->sys_ind) && !IS_EXTENDED(pa->type->code) && has_logical(cxt)) {
1965 fdisk_warnx(cxt, _(
1966 "Cannot change type of the extended partition which is "
75a8e726 1967 "already used by logical partitions. Delete logical "
df15fb3d
KZ
1968 "partitions first."));
1969 return -EINVAL;
1970 }
1971 }
150d98ee 1972
333c3761
KZ
1973 FDISK_INIT_UNDEF(start);
1974 FDISK_INIT_UNDEF(size);
0ecf3ab5 1975
333c3761
KZ
1976 if (fdisk_partition_has_start(pa))
1977 start = pa->start;
1978 if (fdisk_partition_has_size(pa))
1979 size = pa->size;
150d98ee 1980
333c3761
KZ
1981 if (!FDISK_IS_UNDEF(start) || !FDISK_IS_UNDEF(size)) {
1982 DBG(LABEL, ul_debug("DOS: resize partition"));
150d98ee 1983
333c3761
KZ
1984 if (FDISK_IS_UNDEF(start))
1985 start = get_abs_partition_start(pe);
1986 if (FDISK_IS_UNDEF(size))
1987 size = dos_partition_get_size(p);
0ecf3ab5 1988
150d98ee 1989 set_partition(cxt, n, 0, start, start + size - 1,
0ecf3ab5 1990 pa->type ? pa->type->code : p->sys_ind,
4d156d92
KZ
1991 FDISK_IS_UNDEF(pa->boot) ?
1992 p->boot_ind == ACTIVE_FLAG :
1993 fdisk_partition_is_bootable(pa));
150d98ee 1994 } else {
ecf40cda 1995 DBG(LABEL, ul_debug("DOS: keep size, modify properties"));
150d98ee
KZ
1996 if (pa->type)
1997 p->sys_ind = pa->type->code;
ecf40cda 1998 if (!FDISK_IS_UNDEF(pa->boot))
4d156d92 1999 p->boot_ind = fdisk_partition_is_bootable(pa) ? ACTIVE_FLAG : 0;
150d98ee
KZ
2000 }
2001
df15fb3d
KZ
2002 if (pa->type) {
2003 if (IS_EXTENDED(pa->type->code) && !IS_EXTENDED(orgtype)) {
2004 /* new extended partition - create a reference */
2005 l->ext_index = n;
2006 l->ext_offset = dos_partition_get_start(p);
2007 pe->ex_entry = p;
2008 } else if (IS_EXTENDED(orgtype)) {
2009 /* remove extended partition */
2010 cxt->label->nparts_max = 4;
2011 l->ptes[l->ext_index].ex_entry = NULL;
2012 l->ext_offset = 0;
2013 l->ext_index = 0;
2014 }
2015 }
2016
0ecf3ab5
KZ
2017 partition_set_changed(cxt, n, 1);
2018 return 0;
2019}
2020
a60087aa
KZ
2021static void print_chain_of_logicals(struct fdisk_context *cxt)
2022{
2023 size_t i;
6c64adee 2024 struct fdisk_dos_label *l = self_label(cxt);
a60087aa
KZ
2025
2026 fputc('\n', stdout);
2027
2028 for (i = 4; i < cxt->label->nparts_max; i++) {
2029 struct pte *pe = self_pte(cxt, i);
2030
2248aaf9 2031 fprintf(stderr, "#%02zu EBR [%10ju], "
6c64adee
KZ
2032 "data[start=%10ju (%10ju), size=%10ju], "
2033 "link[start=%10ju (%10ju), size=%10ju]\n",
a60087aa 2034 i, (uintmax_t) pe->offset,
6c64adee 2035 /* data */
a60087aa 2036 (uintmax_t) dos_partition_get_start(pe->pt_entry),
6c64adee 2037 (uintmax_t) get_abs_partition_start(pe),
a60087aa 2038 (uintmax_t) dos_partition_get_size(pe->pt_entry),
6c64adee 2039 /* link */
a60087aa 2040 (uintmax_t) dos_partition_get_start(pe->ex_entry),
6c64adee 2041 (uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
a60087aa
KZ
2042 (uintmax_t) dos_partition_get_size(pe->ex_entry));
2043 }
2044}
2045
c1156d85
KZ
2046static int cmp_ebr_offsets(const void *a, const void *b)
2047{
2048 struct pte *ae = (struct pte *) a,
2049 *be = (struct pte *) b;
2050
99cacc6a 2051 if (ae->offset == 0 && be->offset == 0)
c1156d85
KZ
2052 return 0;
2053 if (ae->offset == 0)
2054 return 1;
2055 if (be->offset == 0)
2056 return -1;
2057
19ff8ff7 2058 return cmp_numbers(ae->offset, be->offset);
c1156d85
KZ
2059}
2060
dfc96cbf
KZ
2061/*
2062 * Fix the chain of logicals.
dfc96cbf 2063 *
a60087aa
KZ
2064 * The function does not modify data partitions within EBR tables
2065 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
2066 * (pte->ex_entry) between EBR tables.
2067 *
dfc96cbf 2068 */
e3661531 2069static void fix_chain_of_logicals(struct fdisk_context *cxt)
dfc96cbf 2070{
af0df606 2071 struct fdisk_dos_label *l = self_label(cxt);
d0bdf184 2072 struct pte *last;
a60087aa
KZ
2073 size_t i;
2074
a60087aa
KZ
2075 DBG(LABEL, print_chain_of_logicals(cxt));
2076
c1156d85
KZ
2077 /* Sort chain by EBR offsets */
2078 qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
2079 cmp_ebr_offsets);
2080
a60087aa 2081again:
c1156d85 2082 /* Sort data partitions by start */
a60087aa
KZ
2083 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2084 struct pte *cur = self_pte(cxt, i),
2085 *nxt = self_pte(cxt, i + 1);
a60087aa 2086
c1156d85
KZ
2087 if (get_abs_partition_start(cur) >
2088 get_abs_partition_start(nxt)) {
a60087aa 2089
c1156d85 2090 struct dos_partition tmp = *cur->pt_entry;
0073a4cf 2091 fdisk_sector_t cur_start = get_abs_partition_start(cur),
c1156d85 2092 nxt_start = get_abs_partition_start(nxt);
a60087aa 2093
c1156d85
KZ
2094 /* swap data partitions */
2095 *cur->pt_entry = *nxt->pt_entry;
2096 *nxt->pt_entry = tmp;
a60087aa 2097
c1156d85
KZ
2098 /* Recount starts according to EBR offsets, the absolute
2099 * address tas to be still the same! */
2100 dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
2101 dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
a60087aa 2102
c1156d85
KZ
2103 partition_set_changed(cxt, i, 1);
2104 partition_set_changed(cxt, i + 1, 1);
2105 goto again;
2106 }
dfc96cbf
KZ
2107 }
2108
c1156d85 2109 /* Update EBR links */
a60087aa
KZ
2110 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2111 struct pte *cur = self_pte(cxt, i),
2112 *nxt = self_pte(cxt, i + 1);
2113
0073a4cf 2114 fdisk_sector_t noff = nxt->offset - l->ext_offset,
a60087aa
KZ
2115 ooff = dos_partition_get_start(cur->ex_entry);
2116
a60087aa
KZ
2117 if (noff == ooff)
2118 continue;
2119
2120 DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2121 (uintmax_t) cur->offset,
2122 (uintmax_t) ooff, (uintmax_t) noff));
2123
2124 set_partition(cxt, i, 1, nxt->offset,
7bc31ada
KZ
2125 get_abs_partition_end(nxt),
2126 MBR_DOS_EXTENDED_PARTITION, 0);
d0bdf184 2127 }
6c64adee 2128
d0bdf184
KZ
2129 /* always terminate the chain ! */
2130 last = self_pte(cxt, cxt->label->nparts_max - 1);
2131 if (last) {
2132 clear_partition(last->ex_entry);
2133 partition_set_changed(cxt, cxt->label->nparts_max - 1, 1);
a60087aa 2134 }
d0bdf184 2135
a60087aa 2136 DBG(LABEL, print_chain_of_logicals(cxt));
dfc96cbf
KZ
2137}
2138
dd7ba604 2139static int dos_reorder(struct fdisk_context *cxt)
dfc96cbf
KZ
2140{
2141 struct pte *pei, *pek;
e3661531 2142 size_t i,k;
dfc96cbf 2143
e3661531 2144 if (!wrong_p_order(cxt, NULL)) {
fcea064b 2145 fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
dd49c7d6 2146 return 1;
dfc96cbf
KZ
2147 }
2148
e3661531 2149 while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
dfc96cbf
KZ
2150 /* partition i should have come earlier, move it */
2151 /* We have to move data in the MBR */
f540fe15 2152 struct dos_partition *pi, *pk, *pe, pbuf;
af0df606
KZ
2153 pei = self_pte(cxt, i);
2154 pek = self_pte(cxt, k);
dfc96cbf 2155
6864f168
KZ
2156 pe = pei->ex_entry;
2157 pei->ex_entry = pek->ex_entry;
2158 pek->ex_entry = pe;
dfc96cbf 2159
6864f168
KZ
2160 pi = pei->pt_entry;
2161 pk = pek->pt_entry;
dfc96cbf 2162
f540fe15
KZ
2163 memmove(&pbuf, pi, sizeof(struct dos_partition));
2164 memmove(pi, pk, sizeof(struct dos_partition));
2165 memmove(pk, &pbuf, sizeof(struct dos_partition));
dfc96cbf 2166
af0df606
KZ
2167 partition_set_changed(cxt, i, 1);
2168 partition_set_changed(cxt, k, 1);
dfc96cbf
KZ
2169 }
2170
2171 if (i)
e3661531 2172 fix_chain_of_logicals(cxt);
dfc96cbf 2173
f8ad3899 2174 return 0;
dfc96cbf 2175}
6d864a49 2176
ecf40cda 2177/* TODO: use fdisk_set_partition() API */
829f4206 2178int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
71ee484e 2179{
f8ad3899
KZ
2180 struct pte *pe;
2181 struct dos_partition *p;
71ee484e 2182 unsigned int new, free_start, curr_start, last;
bd0d850b 2183 uintmax_t res = 0;
e3661531 2184 size_t x;
f8ad3899 2185 int rc;
71ee484e
KZ
2186
2187 assert(cxt);
aa36c2cf 2188 assert(fdisk_is_label(cxt, DOS));
71ee484e 2189
f8ad3899
KZ
2190 pe = self_pte(cxt, i);
2191 p = pe->pt_entry;
2192
d0a870c5 2193 if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
829f4206 2194 fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
f8ad3899 2195 return 0;
71ee484e
KZ
2196 }
2197
2198 /* the default start is at the second sector of the disk or at the
2199 * second sector of the extended partition
2200 */
2201 free_start = pe->offset ? pe->offset + 1 : 1;
2202
61eaad50 2203 curr_start = get_abs_partition_start(pe);
71ee484e
KZ
2204
2205 /* look for a free space before the current start of the partition */
e3661531 2206 for (x = 0; x < cxt->label->nparts_max; x++) {
71ee484e 2207 unsigned int end;
af0df606 2208 struct pte *prev_pe = self_pte(cxt, x);
f540fe15 2209 struct dos_partition *prev_p = prev_pe->pt_entry;
71ee484e
KZ
2210
2211 if (!prev_p)
2212 continue;
af0df606
KZ
2213 end = get_abs_partition_start(prev_pe)
2214 + dos_partition_get_size(prev_p);
71ee484e 2215
d0a870c5 2216 if (is_used_partition(prev_p) &&
71ee484e
KZ
2217 end > free_start && end <= curr_start)
2218 free_start = end;
2219 }
2220
1b92d076 2221 last = get_abs_partition_end(pe);
71ee484e 2222
f8ad3899
KZ
2223 rc = fdisk_ask_number(cxt, free_start, curr_start, last,
2224 _("New beginning of data"), &res);
2225 if (rc)
2226 return rc;
2227
bd0d850b 2228 new = res - pe->offset;
71ee484e 2229
b44244cb 2230 if (new != dos_partition_get_size(p)) {
af0df606
KZ
2231 unsigned int sects = dos_partition_get_size(p)
2232 + dos_partition_get_start(p) - new;
2233
b44244cb
KZ
2234 dos_partition_set_size(p, sects);
2235 dos_partition_set_start(p, new);
af0df606
KZ
2236
2237 partition_set_changed(cxt, i, 1);
71ee484e 2238 }
f8ad3899
KZ
2239
2240 return rc;
71ee484e 2241}
6d864a49 2242
8c0a7f91 2243static int dos_partition_is_used(
47b8e7c0 2244 struct fdisk_context *cxt,
8c0a7f91 2245 size_t i)
47b8e7c0 2246{
f540fe15 2247 struct dos_partition *p;
47b8e7c0
KZ
2248
2249 assert(cxt);
9ffeb235 2250 assert(cxt->label);
aa36c2cf 2251 assert(fdisk_is_label(cxt, DOS));
47b8e7c0 2252
8c0a7f91
KZ
2253 if (i >= cxt->label->nparts_max)
2254 return 0;
47b8e7c0 2255
af0df606 2256 p = self_partition(cxt, i);
47b8e7c0 2257
8c0a7f91 2258 return p && !is_cleared_partition(p);
47b8e7c0
KZ
2259}
2260
fb1caca7
KZ
2261static int dos_toggle_partition_flag(
2262 struct fdisk_context *cxt,
2263 size_t i,
2264 unsigned long flag)
2265{
f540fe15 2266 struct dos_partition *p;
fb1caca7
KZ
2267
2268 assert(cxt);
2269 assert(cxt->label);
aa36c2cf 2270 assert(fdisk_is_label(cxt, DOS));
fb1caca7
KZ
2271
2272 if (i >= cxt->label->nparts_max)
2273 return -EINVAL;
2274
af0df606 2275 p = self_partition(cxt, i);
fb1caca7
KZ
2276
2277 switch (flag) {
2278 case DOS_FLAG_ACTIVE:
2279 if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
829f4206
KZ
2280 fdisk_warnx(cxt, _("Partition %zu: is an extended "
2281 "partition."), i + 1);
fb1caca7
KZ
2282
2283 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
af0df606 2284 partition_set_changed(cxt, i, 1);
0477369a 2285 fdisk_info(cxt, p->boot_ind ?
f736ae51
KZ
2286 _("The bootable flag on partition %zu is enabled now.") :
2287 _("The bootable flag on partition %zu is disabled now."),
2288 i + 1);
fb1caca7
KZ
2289 break;
2290 default:
2291 return 1;
2292 }
2293
2294 return 0;
2295}
2296
bd85d11f 2297static const struct fdisk_field dos_fields[] =
262002ee
KZ
2298{
2299 /* basic */
bd85d11f
KZ
2300 { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
2301 { FDISK_FIELD_BOOT, N_("Boot"), 1, 0 },
2302 { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
2303 { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
2304 { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
2305 { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
2306 { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
2307 { FDISK_FIELD_TYPEID, N_("Id"), 2, FDISK_FIELDFL_NUMBER },
2308 { FDISK_FIELD_TYPE, N_("Type"), 0.1, 0 },
262002ee
KZ
2309
2310 /* expert mode */
bd85d11f
KZ
2311 { FDISK_FIELD_SADDR, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2312 { FDISK_FIELD_EADDR, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2313 { FDISK_FIELD_ATTR, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL }
262002ee
KZ
2314
2315};
2316
0c5d095e 2317static const struct fdisk_label_operations dos_operations =
b8855c86 2318{
0c5d095e
KZ
2319 .probe = dos_probe_label,
2320 .write = dos_write_disklabel,
2321 .verify = dos_verify_disklabel,
2322 .create = dos_create_disklabel,
f32a9be4 2323 .locate = dos_locate_disklabel,
5989556a 2324 .get_item = dos_get_disklabel_item,
35b1f0a4 2325 .set_id = dos_set_disklabel_id,
1cf67f1c 2326
262002ee 2327 .get_part = dos_get_partition,
0ecf3ab5 2328 .set_part = dos_set_partition,
77d6a70a 2329 .add_part = dos_add_partition,
e11c6684 2330 .del_part = dos_delete_partition,
5989556a 2331 .reorder = dos_reorder,
262002ee 2332
fb1caca7 2333 .part_toggle_flag = dos_toggle_partition_flag,
8c0a7f91 2334 .part_is_used = dos_partition_is_used,
47b8e7c0 2335
cf3808e4 2336 .reset_alignment = dos_reset_alignment,
ddec0e40
KZ
2337
2338 .deinit = dos_deinit,
b8855c86 2339};
0c5d095e
KZ
2340
2341/*
2342 * allocates DOS in-memory stuff
2343 */
2344struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt)
2345{
2346 struct fdisk_label *lb;
2347 struct fdisk_dos_label *dos;
2348
2349 assert(cxt);
2350
2351 dos = calloc(1, sizeof(*dos));
2352 if (!dos)
2353 return NULL;
2354
2355 /* initialize generic part of the driver */
2356 lb = (struct fdisk_label *) dos;
2357 lb->name = "dos";
53b422ab 2358 lb->id = FDISK_DISKLABEL_DOS;
0c5d095e
KZ
2359 lb->op = &dos_operations;
2360 lb->parttypes = dos_parttypes;
a745611d 2361 lb->nparttypes = ARRAY_SIZE(dos_parttypes) - 1;
bd85d11f
KZ
2362 lb->fields = dos_fields;
2363 lb->nfields = ARRAY_SIZE(dos_fields);
0c5d095e
KZ
2364
2365 return lb;
2366}
852ce62b 2367
b7d101a2
KZ
2368/**
2369 * fdisk_dos_enable_compatible:
2370 * @lb: DOS label (see fdisk_get_label())
2371 * @enable: 0 or 1
2372 *
2373 * Enables deprecated DOS compatible mode, in this mode library checks for
2374 * cylinders boundary, cases about CHS addressing and another obscure things.
2375 *
2376 * Returns: 0 on success, <0 on error.
852ce62b 2377 */
852ce62b
KZ
2378int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
2379{
2380 struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
2381
2382 if (!lb)
2383 return -EINVAL;
2384
2385 dos->compatible = enable;
0b52b94c
KZ
2386 if (enable)
2387 lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
852ce62b
KZ
2388 return 0;
2389}
2390
b7d101a2
KZ
2391/**
2392 * fdisk_dos_is_compatible:
2393 * @lb: DOS label
2394 *
2395 * Returns: 0 if DOS compatibility disabled, 1 if enabled
2396 */
852ce62b
KZ
2397int fdisk_dos_is_compatible(struct fdisk_label *lb)
2398{
2399 return ((struct fdisk_dos_label *) lb)->compatible;
2400}