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