]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libfdisk/src/dos.c
libfdisk: (gpt) allow to specify uuid and name when create new partition
[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,
24def09d 818 sector_t stop, int sysid)
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
9dea2923
DB
841 p->boot_ind = 0;
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
949 return 0;
950}
951
952static int add_partition(struct fdisk_context *cxt, size_t n,
953 struct fdisk_partition *pa)
954{
955 int sys, read = 0, rc, isrel = 0;
e3661531 956 size_t i;
fcea064b 957 struct fdisk_dos_label *l = self_label(cxt);
af0df606 958 struct dos_partition *p = self_partition(cxt, n);
1b92d076 959 struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
af0df606 960
24def09d 961 sector_t start, stop = 0, limit, temp,
e3661531
KZ
962 first[cxt->label->nparts_max],
963 last[cxt->label->nparts_max];
9dea2923 964
88141067 965 DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
9c825fa0 966
a745611d 967 sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
ed470672 968
d0a870c5 969 if (is_used_partition(p)) {
829f4206 970 fdisk_warnx(cxt, _("Partition %zu is already defined. "
e39966c6 971 "Delete it before re-adding it."),
829f4206 972 n + 1);
8254c3a5 973 return -EINVAL;
9dea2923 974 }
e3661531 975 fill_bounds(cxt, first, last);
9dea2923 976 if (n < 4) {
aa36c2cf 977 if (cxt->parent && fdisk_is_label(cxt->parent, GPT))
433d05ff
KZ
978 start = 1; /* Bad boy modifies hybrid MBR */
979 else
980 start = cxt->first_lba;
981
6a632136 982 if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
24cd580b 983 limit = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
9dea2923 984 else
618882d6 985 limit = cxt->total_sectors - 1;
9dea2923
DB
986
987 if (limit > UINT_MAX)
988 limit = UINT_MAX;
989
fcea064b 990 if (l->ext_offset) {
1b92d076 991 assert(ext_pe);
fcea064b 992 first[l->ext_index] = l->ext_offset;
1b92d076 993 last[l->ext_index] = get_abs_partition_end(ext_pe);
9dea2923
DB
994 }
995 } else {
1b92d076 996 assert(ext_pe);
fcea064b 997 start = l->ext_offset + cxt->first_lba;
1b92d076 998 limit = get_abs_partition_end(ext_pe);
9dea2923 999 }
6a632136 1000 if (fdisk_use_cylinders(cxt))
80063996 1001 for (i = 0; i < cxt->label->nparts_max; i++) {
77d6a70a 1002 first[i] = (fdisk_cround(cxt, first[i]) - 1)
6a632136 1003 * fdisk_get_units_per_sector(cxt);
80063996 1004 }
9dea2923 1005
0e07540d
KZ
1006 /*
1007 * Ask for first sector
1008 */
9dea2923 1009 do {
24def09d 1010 sector_t dflt, aligned;
9dea2923
DB
1011
1012 temp = start;
cf3808e4 1013 dflt = start = get_unused_start(cxt, n, start, first, last);
9dea2923
DB
1014
1015 /* the default sector should be aligned and unused */
1016 do {
9475cc78 1017 aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
cf3808e4 1018 dflt = get_unused_start(cxt, n, aligned, first, last);
9dea2923
DB
1019 } while (dflt != aligned && dflt > aligned && dflt < limit);
1020
1021 if (dflt >= limit)
1022 dflt = start;
1023 if (start > limit)
1024 break;
6a632136 1025 if (start >= temp + fdisk_get_units_per_sector(cxt)
fcea064b 1026 && read) {
ac1a559a 1027 fdisk_info(cxt, _("Sector %llu is already allocated."),
fcea064b 1028 temp);
9dea2923
DB
1029 temp = start;
1030 read = 0;
1031 }
0e07540d 1032
9dea2923 1033 if (!read && start == temp) {
77d6a70a 1034 rc = get_start_from_user(cxt, &start, temp, dflt, limit, pa);
0e07540d
KZ
1035 if (rc)
1036 return rc;
9dea2923
DB
1037 read = 1;
1038 }
1039 } while (start != temp || !read);
0e07540d 1040
613596b3
KZ
1041 if (n == 4) {
1042 /* The first EBR is stored at begin of the extended partition */
1043 struct pte *pe = self_pte(cxt, n);
1044 pe->offset = l->ext_offset;
1045
1046 } else if (n > 4) {
1047 /* The second (and another) EBR */
af0df606 1048 struct pte *pe = self_pte(cxt, n);
9dea2923 1049
cf3808e4 1050 pe->offset = start - cxt->first_lba;
fcea064b 1051 if (pe->offset == l->ext_offset) { /* must be corrected */
9dea2923 1052 pe->offset++;
cf3808e4 1053 if (cxt->first_lba == 1)
9dea2923
DB
1054 start++;
1055 }
1056 }
1057
e3661531 1058 for (i = 0; i < cxt->label->nparts_max; i++) {
af0df606 1059 struct pte *pe = self_pte(cxt, i);
9dea2923
DB
1060
1061 if (start < pe->offset && limit >= pe->offset)
1062 limit = pe->offset - 1;
1063 if (start < first[i] && limit >= first[i])
1064 limit = first[i] - 1;
1065 }
1066 if (start > limit) {
ac1a559a 1067 fdisk_info(cxt, _("No free sectors available."));
9dea2923 1068 if (n > 4)
e3661531 1069 cxt->label->nparts_max--;
8254c3a5 1070 return -ENOSPC;
9dea2923 1071 }
77d6a70a
KZ
1072
1073 /*
1074 * Ask for last sector
1075 */
1076 if (fdisk_cround(cxt, start) == fdisk_cround(cxt, limit))
1077 stop = limit;
1078 else if (pa && pa->end_follow_default)
9dea2923 1079 stop = limit;
77d6a70a
KZ
1080 else if (pa && pa->size) {
1081 stop = start + pa->size;
77d6a70a 1082 isrel = 1;
9dea2923 1083 } else {
77d6a70a 1084 /* ask user by dialog */
0e07540d 1085 struct fdisk_ask *ask = fdisk_new_ask();
9dea2923 1086
77d6a70a
KZ
1087 if (!ask)
1088 return -ENOMEM;
0e07540d 1089 fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
9dea2923 1090
6a632136 1091 if (fdisk_use_cylinders(cxt)) {
0e07540d
KZ
1092 fdisk_ask_set_query(ask, _("Last cylinder, +cylinders or +size{K,M,G,T,P}"));
1093 fdisk_ask_number_set_unit(ask,
1094 cxt->sector_size *
6a632136 1095 fdisk_get_units_per_sector(cxt));
0e07540d
KZ
1096 } else {
1097 fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}"));
1098 fdisk_ask_number_set_unit(ask,cxt->sector_size);
1099 }
1100
77d6a70a
KZ
1101 fdisk_ask_number_set_low(ask, fdisk_cround(cxt, start));
1102 fdisk_ask_number_set_default(ask, fdisk_cround(cxt, limit));
1103 fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
1104 fdisk_ask_number_set_base(ask, fdisk_cround(cxt, start)); /* base for relative input */
0e07540d
KZ
1105
1106 rc = fdisk_do_ask(cxt, ask);
0e07540d 1107 stop = fdisk_ask_number_get_result(ask);
77d6a70a
KZ
1108 isrel = fdisk_ask_number_is_relative(ask);
1109 fdisk_free_ask(ask);
1110 if (rc)
1111 return rc;
6a632136
KZ
1112 if (fdisk_use_cylinders(cxt)) {
1113 stop = stop * fdisk_get_units_per_sector(cxt) - 1;
77d6a70a
KZ
1114 if (stop >limit)
1115 stop = limit;
80063996 1116 }
77d6a70a
KZ
1117 }
1118
1119 if (stop > limit)
1120 stop = limit;
1121
1122 if (stop < limit) {
1123 if (isrel && alignment_required(cxt)) {
9dea2923 1124 /* the last sector has not been exactly requested (but
0e07540d
KZ
1125 * defined by +size{K,M,G} convention), so be smart and
1126 * align the end of the partition. The next partition
1127 * will start at phy.block boundary.
9dea2923 1128 */
9475cc78 1129 stop = fdisk_align_lba_in_range(cxt, stop, start, limit) - 1;
9dea2923
DB
1130 if (stop > limit)
1131 stop = limit;
1132 }
1133 }
1134
e53ced85 1135 set_partition(cxt, n, 0, start, stop, sys);
af0df606
KZ
1136 if (n > 4) {
1137 struct pte *pe = self_pte(cxt, n);
bb58112e
KZ
1138 set_partition(cxt, n - 1, 1, pe->offset, stop,
1139 MBR_DOS_EXTENDED_PARTITION);
af0df606 1140 }
9dea2923 1141
bb58112e 1142 if (IS_EXTENDED(sys)) {
af0df606 1143 struct pte *pen = self_pte(cxt, n);
9dea2923 1144
fcea064b 1145 l->ext_index = n;
613596b3 1146 l->ext_offset = start;
6864f168 1147 pen->ex_entry = p;
9dea2923 1148 }
8254c3a5 1149
bddd84e7 1150 fdisk_label_set_changed(cxt->label, 1);
8254c3a5 1151 return 0;
9dea2923
DB
1152}
1153
77d6a70a 1154static int add_logical(struct fdisk_context *cxt, struct fdisk_partition *pa)
9dea2923 1155{
613596b3 1156 struct pte *pe;
af0df606 1157
e3661531
KZ
1158 assert(cxt);
1159 assert(cxt->label);
613596b3
KZ
1160 assert(self_label(cxt)->ext_offset);
1161 assert(self_label(cxt)->ext_index);
e3661531 1162
613596b3
KZ
1163 DBG(LABEL, ul_debug("DOS: nparts max: %zu", cxt->label->nparts_max));
1164 pe = self_pte(cxt, cxt->label->nparts_max);
af0df606 1165
613596b3 1166 if (!pe->sectorbuffer) {
af0df606
KZ
1167 pe->sectorbuffer = calloc(1, cxt->sector_size);
1168 if (!pe->sectorbuffer)
1169 return -ENOMEM;
613596b3
KZ
1170 DBG(LABEL, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1171 cxt->label->nparts_max, pe->sectorbuffer));
ddec0e40 1172 pe->private_sectorbuffer = 1;
9dea2923 1173 }
613596b3
KZ
1174 pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
1175 pe->ex_entry = pe->pt_entry + 1;
1176 pe->offset = 0;
1177 partition_set_changed(cxt, cxt->label->nparts_max, 1);
1178
1179 cxt->label->nparts_max++;
1180
829f4206 1181 fdisk_info(cxt, _("Adding logical partition %zu"),
fcea064b 1182 cxt->label->nparts_max);
77d6a70a 1183 return add_partition(cxt, cxt->label->nparts_max - 1, pa);
9dea2923
DB
1184}
1185
e3661531 1186static void check(struct fdisk_context *cxt, size_t n,
5dfca634
KZ
1187 unsigned int h, unsigned int s, unsigned int c,
1188 unsigned int start)
1189{
1190 unsigned int total, real_s, real_c;
1191
63fb717d
KZ
1192 if (!is_dos_compatible(cxt))
1193 return;
1194
5dfca634
KZ
1195 real_s = sector(s) - 1;
1196 real_c = cylinder(s, c);
9bb8caff 1197 total = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
fcea064b 1198
5dfca634 1199 if (!total)
829f4206 1200 fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
5dfca634 1201 if (h >= cxt->geom.heads)
829f4206 1202 fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
fcea064b 1203 "maximum %d"), n, h + 1, cxt->geom.heads);
5dfca634 1204 if (real_s >= cxt->geom.sectors)
829f4206 1205 fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
fcea064b 1206 "maximum %llu"), n, s, cxt->geom.sectors);
5dfca634 1207 if (real_c >= cxt->geom.cylinders)
829f4206 1208 fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
fcea064b
KZ
1209 "maximum %llu"),
1210 n, real_c + 1,
1211 cxt->geom.cylinders);
1212
5dfca634 1213 if (cxt->geom.cylinders <= 1024 && start != total)
829f4206
KZ
1214 fdisk_warnx(cxt, _("Partition %zu: previous sectors %u "
1215 "disagrees with total %u"), n, start, total);
5dfca634
KZ
1216}
1217
1218/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1219 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1220 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1221 * Lubkin Oct. 1991). */
1222
1223static void
1224long2chs(struct fdisk_context *cxt, unsigned long ls,
1225 unsigned int *c, unsigned int *h, unsigned int *s) {
1226 int spc = cxt->geom.heads * cxt->geom.sectors;
1227
1228 *c = ls / spc;
1229 ls = ls % spc;
1230 *h = ls / cxt->geom.sectors;
1231 *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
1232}
1233
f540fe15 1234static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
e3661531 1235 size_t partition)
5dfca634
KZ
1236{
1237 unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
1238 unsigned int pec, peh, pes; /* physical ending c, h, s */
1239 unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
1240 unsigned int lec, leh, les; /* logical ending c, h, s */
1241
1242 if (!is_dos_compatible(cxt))
1243 return;
1244
1245 if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
1246 return; /* do not check extended partitions */
1247
60c1b036 1248 /* physical beginning c, h, s */
da967236
KZ
1249 pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
1250 pbh = p->bh;
1251 pbs = p->bs & 0x3f;
5dfca634 1252
60c1b036 1253 /* physical ending c, h, s */
da967236
KZ
1254 pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
1255 peh = p->eh;
1256 pes = p->es & 0x3f;
5dfca634 1257
60c1b036 1258 /* compute logical beginning (c, h, s) */
b44244cb 1259 long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
5dfca634 1260
60c1b036 1261 /* compute logical ending (c, h, s) */
b44244cb 1262 long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
5dfca634 1263
60c1b036 1264 /* Same physical / logical beginning? */
fcea064b
KZ
1265 if (cxt->geom.cylinders <= 1024
1266 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
829f4206 1267 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
fcea064b
KZ
1268 "beginnings (non-Linux?): "
1269 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1270 partition + 1,
1271 pbc, pbh, pbs,
1272 lbc, lbh, lbs);
5dfca634
KZ
1273 }
1274
60c1b036 1275 /* Same physical / logical ending? */
fcea064b
KZ
1276 if (cxt->geom.cylinders <= 1024
1277 && (pec != lec || peh != leh || pes != les)) {
829f4206 1278 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
fcea064b
KZ
1279 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1280 partition + 1,
1281 pec, peh, pes,
1282 lec, leh, les);
5dfca634
KZ
1283 }
1284
60c1b036 1285 /* Ending on cylinder boundary? */
5dfca634 1286 if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
829f4206 1287 fdisk_warnx(cxt, _("Partition %zu: does not end on "
fcea064b 1288 "cylinder boundary."),
5dfca634
KZ
1289 partition + 1);
1290 }
1291}
1292
9ffeb235 1293static int dos_verify_disklabel(struct fdisk_context *cxt)
2ca61a61 1294{
e3661531 1295 size_t i, j;
2ca61a61 1296 sector_t total = 1, n_sectors = cxt->total_sectors;
e3661531
KZ
1297 unsigned long long first[cxt->label->nparts_max],
1298 last[cxt->label->nparts_max];
f540fe15 1299 struct dos_partition *p;
fcea064b 1300 struct fdisk_dos_label *l = self_label(cxt);
2ca61a61 1301
aa36c2cf 1302 assert(fdisk_is_label(cxt, DOS));
9ffeb235 1303
e3661531
KZ
1304 fill_bounds(cxt, first, last);
1305 for (i = 0; i < cxt->label->nparts_max; i++) {
af0df606 1306 struct pte *pe = self_pte(cxt, i);
2ca61a61 1307
af0df606 1308 p = self_partition(cxt, i);
d0a870c5 1309 if (is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
2ca61a61 1310 check_consistency(cxt, p, i);
61eaad50 1311 if (get_abs_partition_start(pe) < first[i])
fcea064b 1312 fdisk_warnx(cxt, _(
829f4206 1313 "Partition %zu: bad start-of-data."),
fcea064b
KZ
1314 i + 1);
1315
1316 check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
2ca61a61 1317 total += last[i] + 1 - first[i];
fcea064b 1318
11712b66
KZ
1319 if (i == 0)
1320 total += get_abs_partition_start(pe) - 1;
1321
fcea064b 1322 for (j = 0; j < i; j++) {
2ca61a61
DB
1323 if ((first[i] >= first[j] && first[i] <= last[j])
1324 || ((last[i] <= last[j] && last[i] >= first[j]))) {
fcea064b 1325
829f4206
KZ
1326 fdisk_warnx(cxt, _("Partition %zu: "
1327 "overlaps partition %zu."),
fcea064b
KZ
1328 j + 1, i + 1);
1329
2ca61a61
DB
1330 total += first[i] >= first[j] ?
1331 first[i] : first[j];
1332 total -= last[i] <= last[j] ?
1333 last[i] : last[j];
1334 }
fcea064b 1335 }
2ca61a61
DB
1336 }
1337 }
1338
fcea064b 1339 if (l->ext_offset) {
5d324c6b 1340 sector_t e_last;
1b92d076
KZ
1341 struct pte *ext_pe = self_pte(cxt, l->ext_index);
1342
1343 e_last = get_abs_partition_end(ext_pe);
2ca61a61 1344
e3661531 1345 for (i = 4; i < cxt->label->nparts_max; i++) {
2ca61a61 1346 total++;
af0df606
KZ
1347 p = self_partition(cxt, i);
1348
2ca61a61 1349 if (!p->sys_ind) {
e3661531 1350 if (i != 4 || i + 1 < cxt->label->nparts_max)
fcea064b 1351 fdisk_warnx(cxt,
829f4206 1352 _("Partition %zu: empty."),
fcea064b
KZ
1353 i + 1);
1354 } else if (first[i] < l->ext_offset
1355 || last[i] > e_last) {
1356
829f4206
KZ
1357 fdisk_warnx(cxt, _("Logical partition %zu: "
1358 "not entirely in partition %zu."),
fcea064b 1359 i + 1, l->ext_index + 1);
2ca61a61 1360 }
2ca61a61
DB
1361 }
1362 }
1363
1364 if (total > n_sectors)
fcea064b
KZ
1365 fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
1366 "than the maximum %llu."), total, n_sectors);
2ca61a61 1367 else if (total < n_sectors)
fcea064b 1368 fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
ac1a559a 1369 "sectors."), n_sectors - total, cxt->sector_size);
2ca61a61
DB
1370
1371 return 0;
1372}
1373
9dea2923
DB
1374/*
1375 * Ask the user for new partition type information (logical, extended).
0f639e54
DB
1376 * This function calls the actual partition adding logic - add_partition.
1377 *
1378 * API callback.
9dea2923 1379 */
77d6a70a
KZ
1380static int dos_add_partition(struct fdisk_context *cxt,
1381 struct fdisk_partition *pa)
9dea2923 1382{
e3661531
KZ
1383 size_t i, free_primary = 0;
1384 int rc = 0;
e77313a6 1385 struct fdisk_dos_label *l;
1b92d076 1386 struct pte *ext_pe;
9dea2923 1387
9ffeb235
KZ
1388 assert(cxt);
1389 assert(cxt->label);
aa36c2cf 1390 assert(fdisk_is_label(cxt, DOS));
9ffeb235 1391
e77313a6 1392 l = self_label(cxt);
1b92d076 1393 ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
a3e37fa7
KZ
1394
1395 /* pa specifies start within extended partition, add logical */
1b92d076 1396 if (pa && pa->start && ext_pe
a3e37fa7 1397 && pa->start >= l->ext_offset
1b92d076 1398 && pa->start <= get_abs_partition_end(ext_pe)) {
a3e37fa7
KZ
1399 rc = add_logical(cxt, pa);
1400 goto done;
1401
1402 /* pa specifies start, but outside extended partition */
1403 } else if (pa && pa->start && l->ext_offset) {
1404 int j;
1405
1406 j = get_partition_unused_primary(cxt, pa);
1407 if (j >= 0) {
1408 rc = add_partition(cxt, j, pa);
1409 goto done;
1410 }
1411
1412 }
20f878fe 1413
af0df606
KZ
1414 for (i = 0; i < 4; i++) {
1415 struct dos_partition *p = self_partition(cxt, i);
28a5bccc 1416 free_primary += !is_used_partition(p);
af0df606 1417 }
9dea2923 1418
e3661531 1419 if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
fcea064b
KZ
1420 fdisk_info(cxt, _("The maximum number of partitions has "
1421 "been created."));
8254c3a5 1422 return -EINVAL;
9dea2923 1423 }
1bf3a034 1424 rc = 1;
fcea064b 1425
9dea2923 1426 if (!free_primary) {
fcea064b
KZ
1427 if (l->ext_offset) {
1428 fdisk_info(cxt, _("All primary partitions are in use."));
77d6a70a 1429 rc = add_logical(cxt, pa);
9dea2923 1430 } else
fcea064b 1431 fdisk_info(cxt, _("If you want to create more than "
0d0d12ad 1432 "four partitions, you must replace a "
fcea064b
KZ
1433 "primary partition with an extended "
1434 "partition first."));
1435
e3661531 1436 } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
c9f63764 1437 int j;
1bf3a034 1438
fcea064b
KZ
1439 fdisk_info(cxt, _("All logical partitions are in use. "
1440 "Adding a primary partition."));
77d6a70a 1441 j = get_partition_unused_primary(cxt, pa);
c9f63764 1442 if (j >= 0)
77d6a70a 1443 rc = add_partition(cxt, j, pa);
9dea2923 1444 } else {
20f878fe
KZ
1445 char hint[BUFSIZ];
1446 struct fdisk_ask *ask;
1447 int c;
1448
1449 ask = fdisk_new_ask();
1450 if (!ask)
1451 return -ENOMEM;
1452 fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
1453 fdisk_ask_set_query(ask, _("Partition type"));
1454 fdisk_ask_menu_set_default(ask, free_primary == 1
1455 && !l->ext_offset ? 'e' : 'p');
1456 snprintf(hint, sizeof(hint),
1457 _("%zu primary, %d extended, %zu free"),
1458 4 - (l->ext_offset ? 1 : 0) - free_primary,
1459 l->ext_offset ? 1 : 0,
1460 free_primary);
1461
1462 fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
1463 if (!l->ext_offset)
1464 fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
1465 else
1466 fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
1467
1468 rc = fdisk_do_ask(cxt, ask);
bd0d850b
KZ
1469 if (rc)
1470 return rc;
20f878fe
KZ
1471 fdisk_ask_menu_get_result(ask, &c);
1472 fdisk_free_ask(ask);
bd0d850b 1473
9dea2923 1474 if (c == 'p') {
77d6a70a 1475 int j = get_partition_unused_primary(cxt, pa);
c9f63764 1476 if (j >= 0)
77d6a70a 1477 rc = add_partition(cxt, j, pa);
8254c3a5 1478 goto done;
fcea064b 1479 } else if (c == 'l' && l->ext_offset) {
77d6a70a 1480 rc = add_logical(cxt, pa);
8254c3a5 1481 goto done;
fcea064b 1482 } else if (c == 'e' && !l->ext_offset) {
77d6a70a 1483 int j = get_partition_unused_primary(cxt, pa);
c9f63764 1484 if (j >= 0) {
77d6a70a
KZ
1485 struct fdisk_partition xpa = { .type = NULL };
1486 struct fdisk_parttype *t;
1487
a745611d 1488 t = fdisk_label_get_parttype_from_code(cxt->label,
bb58112e 1489 MBR_DOS_EXTENDED_PARTITION);
77d6a70a
KZ
1490 if (!pa)
1491 pa = &xpa;
1492 fdisk_partition_set_type(pa, t);
1493 rc = add_partition(cxt, j, pa);
ed470672 1494 }
8254c3a5 1495 goto done;
9dea2923 1496 } else
ac1a559a 1497 fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
9dea2923 1498 }
8254c3a5 1499done:
e3661531
KZ
1500 if (rc == 0)
1501 cxt->label->nparts_cur++;
8254c3a5 1502 return rc;
9dea2923 1503}
0dc13a38 1504
fae7b1bc
DB
1505static int write_sector(struct fdisk_context *cxt, sector_t secno,
1506 unsigned char *buf)
0dc13a38 1507{
21770662
KZ
1508 int rc;
1509
1510 rc = seek_sector(cxt, secno);
1511 if (rc != 0) {
98aeae96 1512 fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
21770662
KZ
1513 (uintmax_t) secno);
1514 return rc;
1515 }
bcddbe96 1516
88141067 1517 DBG(LABEL, ul_debug("DOS: writting to sector %ju", (uintmax_t) secno));
bcddbe96 1518
fae7b1bc
DB
1519 if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
1520 return -errno;
1521 return 0;
1522}
1523
9ffeb235 1524static int dos_write_disklabel(struct fdisk_context *cxt)
fae7b1bc 1525{
8b3d9241 1526 struct fdisk_dos_label *l = self_label(cxt);
e3661531 1527 size_t i;
8b3d9241 1528 int rc = 0, mbr_changed = 0;
0dc13a38 1529
9ffeb235
KZ
1530 assert(cxt);
1531 assert(cxt->label);
aa36c2cf 1532 assert(fdisk_is_label(cxt, DOS));
9ffeb235 1533
8b3d9241
KZ
1534 mbr_changed = l->non_pt_changed;
1535
0dc13a38 1536 /* MBR (primary partitions) */
8b3d9241 1537 if (!mbr_changed) {
af0df606
KZ
1538 for (i = 0; i < 4; i++) {
1539 struct pte *pe = self_pte(cxt, i);
1540 if (pe->changed)
8b3d9241 1541 mbr_changed = 1;
af0df606 1542 }
0dc13a38 1543 }
8b3d9241 1544 if (mbr_changed) {
67987b47 1545 mbr_set_magic(cxt->firstsector);
fae7b1bc
DB
1546 rc = write_sector(cxt, 0, cxt->firstsector);
1547 if (rc)
1548 goto done;
0dc13a38 1549 }
8b3d9241 1550
bcddbe96
KZ
1551 if (cxt->label->nparts_max <= 4 && l->ext_offset) {
1552 /* we have empty extended partition, check if the partition has
1553 * been modified and then cleanup possible remaining EBR */
1554 struct pte *pe = self_pte(cxt, l->ext_index);
1555 unsigned char empty[512] = { 0 };
1556 sector_t off = pe ? get_abs_partition_start(pe) : 0;
1557
1558 if (off && pe->changed) {
1559 mbr_set_magic(empty);
1560 write_sector(cxt, off, empty);
1561 }
1562 }
1563
0dc13a38 1564 /* EBR (logical partitions) */
e3661531 1565 for (i = 4; i < cxt->label->nparts_max; i++) {
af0df606 1566 struct pte *pe = self_pte(cxt, i);
0dc13a38 1567
613596b3
KZ
1568 if (!pe->changed)
1569 continue;
1570
1571 assert(pe->sectorbuffer);
1572 assert(pe->offset);
1573
1574 mbr_set_magic(pe->sectorbuffer);
1575 rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
1576 if (rc)
1577 goto done;
0dc13a38 1578 }
fae7b1bc
DB
1579
1580done:
1581 return rc;
0dc13a38 1582}
b8855c86 1583
f32a9be4
KZ
1584static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
1585 const char **name, off_t *offset, size_t *size)
1586{
1587 assert(cxt);
1588
1589 *name = NULL;
1590 *offset = 0;
1591 *size = 0;
1592
1593 switch (n) {
1594 case 0:
1595 *name = "MBR";
1596 *offset = 0;
1597 *size = 512;
1598 break;
1599 default:
1600 /* extended partitions */
1601 if (n - 1 + 4 < cxt->label->nparts_max) {
1602 struct pte *pe = self_pte(cxt, n - 1 + 4);
1603
1604 assert(pe->private_sectorbuffer);
1605
1606 *name = "EBR";
1607 *offset = pe->offset * cxt->sector_size;
1608 *size = 512;
1609 } else
1610 return 1;
1611 break;
1612 }
1613
1614 return 0;
1615}
1616
8a95621d 1617static int dos_set_parttype(
e3661531 1618 struct fdisk_context *cxt,
9ffeb235 1619 size_t partnum,
8a95621d 1620 struct fdisk_parttype *t)
02460b8a 1621{
f540fe15 1622 struct dos_partition *p;
02460b8a 1623
9ffeb235
KZ
1624 assert(cxt);
1625 assert(cxt->label);
aa36c2cf 1626 assert(fdisk_is_label(cxt, DOS));
9ffeb235 1627
a745611d 1628 if (partnum >= cxt->label->nparts_max || !t || t->code > UINT8_MAX)
02460b8a
KZ
1629 return -EINVAL;
1630
af0df606 1631 p = self_partition(cxt, partnum);
a745611d 1632 if (t->code == p->sys_ind)
02460b8a
KZ
1633 return 0;
1634
a745611d 1635 if (IS_EXTENDED(p->sys_ind) || IS_EXTENDED(t->code)) {
fcea064b
KZ
1636 fdisk_warnx(cxt, _("You cannot change a partition into an "
1637 "extended one or vice versa. Delete it first."));
02460b8a
KZ
1638 return -EINVAL;
1639 }
1640
a745611d 1641 if (is_dos_partition(t->code) || is_dos_partition(p->sys_ind))
fcea064b 1642 fdisk_info(cxt, _("If you have created or modified any DOS 6.x "
0d0d12ad 1643 "partitions, please see the fdisk documentation for additional "
fcea064b 1644 "information."));
02460b8a 1645
a745611d 1646 if (!t->code)
b37ab36e
KZ
1647 fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
1648 "Having partitions of type 0 is probably unwise."));
a745611d 1649 p->sys_ind = t->code;
af0df606
KZ
1650
1651 partition_set_changed(cxt, partnum, 1);
02460b8a
KZ
1652 return 0;
1653}
1654
dfc96cbf
KZ
1655/*
1656 * Check whether partition entries are ordered by their starting positions.
1657 * Return 0 if OK. Return i if partition i should have been earlier.
1658 * Two separate checks: primary and logical partitions.
1659 */
e3661531 1660static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
dfc96cbf 1661{
e3661531
KZ
1662 size_t last_p_start_pos = 0, p_start_pos;
1663 size_t i, last_i = 0;
dfc96cbf 1664
e3661531 1665 for (i = 0 ; i < cxt->label->nparts_max; i++) {
af0df606
KZ
1666
1667 struct pte *pe = self_pte(cxt, i);
1668 struct dos_partition *p = pe->pt_entry;
1669
dfc96cbf
KZ
1670 if (i == 4) {
1671 last_i = 4;
1672 last_p_start_pos = 0;
1673 }
d0a870c5 1674 if (is_used_partition(p)) {
61eaad50 1675 p_start_pos = get_abs_partition_start(pe);
dfc96cbf
KZ
1676
1677 if (last_p_start_pos > p_start_pos) {
1678 if (prev)
1679 *prev = last_i;
1680 return i;
1681 }
1682
1683 last_p_start_pos = p_start_pos;
1684 last_i = i;
1685 }
1686 }
1687 return 0;
1688}
6d864a49 1689
1cf67f1c
KZ
1690static int dos_list_disklabel(struct fdisk_context *cxt)
1691{
6d864a49 1692 assert(cxt);
9ffeb235 1693 assert(cxt->label);
aa36c2cf 1694 assert(fdisk_is_label(cxt, DOS));
6d864a49 1695
6c89f750 1696 return 0;
262002ee 1697}
1cf67f1c 1698
262002ee
KZ
1699static int dos_get_partition(struct fdisk_context *cxt, size_t n,
1700 struct fdisk_partition *pa)
1701{
1702 struct dos_partition *p;
1703 struct pte *pe;
bd5e8291 1704 struct fdisk_dos_label *lb;
1cf67f1c 1705
262002ee
KZ
1706 assert(cxt);
1707 assert(pa);
1708 assert(cxt->label);
aa36c2cf 1709 assert(fdisk_is_label(cxt, DOS));
1cf67f1c 1710
bd5e8291 1711 lb = self_label(cxt);
262002ee
KZ
1712 pe = self_pte(cxt, n);
1713 p = pe->pt_entry;
1714 pa->used = !is_cleared_partition(p);
1715 if (!pa->used)
1716 return 0;
1cf67f1c 1717
7f01ec7a 1718 pa->type = dos_partition_parttype(cxt, p);
cb4d6804 1719 pa->boot = p->boot_ind == ACTIVE_FLAG ? 1 : 0;
77d6a70a 1720 pa->start = get_abs_partition_start(pe);
1b92d076
KZ
1721 pa->end = get_abs_partition_end(pe);
1722 pa->size = dos_partition_get_size(p);
e77313a6 1723 pa->container = lb->ext_offset && n == lb->ext_index;
1cf67f1c 1724
bd5e8291
KZ
1725 if (n >= 4)
1726 pa->parent_partno = lb->ext_index;
03643931 1727
cb4d6804 1728 if (p->boot_ind && asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
262002ee 1729 return -ENOMEM;
6d864a49 1730
262002ee
KZ
1731 /* start C/H/S */
1732 if (asprintf(&pa->start_addr, "%d/%d/%d",
1733 cylinder(p->bs, p->bc),
1734 sector(p->bs),
1735 p->bh) < 0)
1736 return -ENOMEM;
1cf67f1c 1737
262002ee
KZ
1738 /* end C/H/S */
1739 if (asprintf(&pa->end_addr, "%d/%d/%d",
1740 cylinder(p->es, p->ec),
1741 sector(p->es),
1742 p->eh) < 0)
1743 return -ENOMEM;
6d864a49 1744
262002ee 1745 return 0;
6d864a49
KZ
1746}
1747
a60087aa
KZ
1748static void print_chain_of_logicals(struct fdisk_context *cxt)
1749{
1750 size_t i;
6c64adee 1751 struct fdisk_dos_label *l = self_label(cxt);
a60087aa
KZ
1752
1753 fputc('\n', stdout);
1754
1755 for (i = 4; i < cxt->label->nparts_max; i++) {
1756 struct pte *pe = self_pte(cxt, i);
1757
1758 printf("#%02zu EBR [%10ju], "
6c64adee
KZ
1759 "data[start=%10ju (%10ju), size=%10ju], "
1760 "link[start=%10ju (%10ju), size=%10ju]\n",
a60087aa 1761 i, (uintmax_t) pe->offset,
6c64adee 1762 /* data */
a60087aa 1763 (uintmax_t) dos_partition_get_start(pe->pt_entry),
6c64adee 1764 (uintmax_t) get_abs_partition_start(pe),
a60087aa 1765 (uintmax_t) dos_partition_get_size(pe->pt_entry),
6c64adee 1766 /* link */
a60087aa 1767 (uintmax_t) dos_partition_get_start(pe->ex_entry),
6c64adee 1768 (uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
a60087aa
KZ
1769 (uintmax_t) dos_partition_get_size(pe->ex_entry));
1770 }
1771}
1772
c1156d85
KZ
1773static int cmp_ebr_offsets(const void *a, const void *b)
1774{
1775 struct pte *ae = (struct pte *) a,
1776 *be = (struct pte *) b;
1777
99cacc6a 1778 if (ae->offset == 0 && be->offset == 0)
c1156d85
KZ
1779 return 0;
1780 if (ae->offset == 0)
1781 return 1;
1782 if (be->offset == 0)
1783 return -1;
1784
1785 return ae->offset - be->offset;
1786}
1787
dfc96cbf
KZ
1788/*
1789 * Fix the chain of logicals.
dfc96cbf 1790 *
a60087aa
KZ
1791 * The function does not modify data partitions within EBR tables
1792 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
1793 * (pte->ex_entry) between EBR tables.
1794 *
dfc96cbf 1795 */
e3661531 1796static void fix_chain_of_logicals(struct fdisk_context *cxt)
dfc96cbf 1797{
af0df606 1798 struct fdisk_dos_label *l = self_label(cxt);
a60087aa
KZ
1799 size_t i;
1800
a60087aa
KZ
1801 DBG(LABEL, print_chain_of_logicals(cxt));
1802
c1156d85
KZ
1803 /* Sort chain by EBR offsets */
1804 qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
1805 cmp_ebr_offsets);
1806
a60087aa 1807again:
c1156d85 1808 /* Sort data partitions by start */
a60087aa
KZ
1809 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
1810 struct pte *cur = self_pte(cxt, i),
1811 *nxt = self_pte(cxt, i + 1);
a60087aa 1812
c1156d85
KZ
1813 if (get_abs_partition_start(cur) >
1814 get_abs_partition_start(nxt)) {
a60087aa 1815
c1156d85
KZ
1816 struct dos_partition tmp = *cur->pt_entry;
1817 sector_t cur_start = get_abs_partition_start(cur),
1818 nxt_start = get_abs_partition_start(nxt);
a60087aa 1819
c1156d85
KZ
1820 /* swap data partitions */
1821 *cur->pt_entry = *nxt->pt_entry;
1822 *nxt->pt_entry = tmp;
a60087aa 1823
c1156d85
KZ
1824 /* Recount starts according to EBR offsets, the absolute
1825 * address tas to be still the same! */
1826 dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
1827 dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
a60087aa 1828
c1156d85
KZ
1829 partition_set_changed(cxt, i, 1);
1830 partition_set_changed(cxt, i + 1, 1);
1831 goto again;
1832 }
dfc96cbf
KZ
1833 }
1834
c1156d85 1835 /* Update EBR links */
a60087aa
KZ
1836 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
1837 struct pte *cur = self_pte(cxt, i),
1838 *nxt = self_pte(cxt, i + 1);
1839
1840 sector_t noff = nxt->offset - l->ext_offset,
1841 ooff = dos_partition_get_start(cur->ex_entry);
1842
a60087aa
KZ
1843 if (noff == ooff)
1844 continue;
1845
1846 DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
1847 (uintmax_t) cur->offset,
1848 (uintmax_t) ooff, (uintmax_t) noff));
1849
1850 set_partition(cxt, i, 1, nxt->offset,
1851 get_abs_partition_end(nxt), MBR_DOS_EXTENDED_PARTITION);
6c64adee
KZ
1852
1853 if (i + 1 == cxt->label->nparts_max - 1) {
1854 clear_partition(nxt->ex_entry);
1855 partition_set_changed(cxt, i + 1, 1);
1856 }
1857
a60087aa
KZ
1858 }
1859 DBG(LABEL, print_chain_of_logicals(cxt));
dfc96cbf
KZ
1860}
1861
dd7ba604 1862static int dos_reorder(struct fdisk_context *cxt)
dfc96cbf
KZ
1863{
1864 struct pte *pei, *pek;
e3661531 1865 size_t i,k;
dfc96cbf 1866
e3661531 1867 if (!wrong_p_order(cxt, NULL)) {
fcea064b 1868 fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
f8ad3899 1869 return 0;
dfc96cbf
KZ
1870 }
1871
e3661531 1872 while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
dfc96cbf
KZ
1873 /* partition i should have come earlier, move it */
1874 /* We have to move data in the MBR */
f540fe15 1875 struct dos_partition *pi, *pk, *pe, pbuf;
af0df606
KZ
1876 pei = self_pte(cxt, i);
1877 pek = self_pte(cxt, k);
dfc96cbf 1878
6864f168
KZ
1879 pe = pei->ex_entry;
1880 pei->ex_entry = pek->ex_entry;
1881 pek->ex_entry = pe;
dfc96cbf 1882
6864f168
KZ
1883 pi = pei->pt_entry;
1884 pk = pek->pt_entry;
dfc96cbf 1885
f540fe15
KZ
1886 memmove(&pbuf, pi, sizeof(struct dos_partition));
1887 memmove(pi, pk, sizeof(struct dos_partition));
1888 memmove(pk, &pbuf, sizeof(struct dos_partition));
dfc96cbf 1889
af0df606
KZ
1890 partition_set_changed(cxt, i, 1);
1891 partition_set_changed(cxt, k, 1);
dfc96cbf
KZ
1892 }
1893
1894 if (i)
e3661531 1895 fix_chain_of_logicals(cxt);
dfc96cbf 1896
f61d2632 1897 fdisk_info(cxt, _("Done."));
f8ad3899 1898 return 0;
dfc96cbf 1899}
6d864a49 1900
829f4206 1901int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
71ee484e 1902{
f8ad3899
KZ
1903 struct pte *pe;
1904 struct dos_partition *p;
71ee484e 1905 unsigned int new, free_start, curr_start, last;
bd0d850b 1906 uintmax_t res = 0;
e3661531 1907 size_t x;
f8ad3899 1908 int rc;
71ee484e
KZ
1909
1910 assert(cxt);
aa36c2cf 1911 assert(fdisk_is_label(cxt, DOS));
71ee484e 1912
f8ad3899
KZ
1913 pe = self_pte(cxt, i);
1914 p = pe->pt_entry;
1915
d0a870c5 1916 if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
829f4206 1917 fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
f8ad3899 1918 return 0;
71ee484e
KZ
1919 }
1920
1921 /* the default start is at the second sector of the disk or at the
1922 * second sector of the extended partition
1923 */
1924 free_start = pe->offset ? pe->offset + 1 : 1;
1925
61eaad50 1926 curr_start = get_abs_partition_start(pe);
71ee484e
KZ
1927
1928 /* look for a free space before the current start of the partition */
e3661531 1929 for (x = 0; x < cxt->label->nparts_max; x++) {
71ee484e 1930 unsigned int end;
af0df606 1931 struct pte *prev_pe = self_pte(cxt, x);
f540fe15 1932 struct dos_partition *prev_p = prev_pe->pt_entry;
71ee484e
KZ
1933
1934 if (!prev_p)
1935 continue;
af0df606
KZ
1936 end = get_abs_partition_start(prev_pe)
1937 + dos_partition_get_size(prev_p);
71ee484e 1938
d0a870c5 1939 if (is_used_partition(prev_p) &&
71ee484e
KZ
1940 end > free_start && end <= curr_start)
1941 free_start = end;
1942 }
1943
1b92d076 1944 last = get_abs_partition_end(pe);
71ee484e 1945
f8ad3899
KZ
1946 rc = fdisk_ask_number(cxt, free_start, curr_start, last,
1947 _("New beginning of data"), &res);
1948 if (rc)
1949 return rc;
1950
bd0d850b 1951 new = res - pe->offset;
71ee484e 1952
b44244cb 1953 if (new != dos_partition_get_size(p)) {
af0df606
KZ
1954 unsigned int sects = dos_partition_get_size(p)
1955 + dos_partition_get_start(p) - new;
1956
b44244cb
KZ
1957 dos_partition_set_size(p, sects);
1958 dos_partition_set_start(p, new);
af0df606
KZ
1959
1960 partition_set_changed(cxt, i, 1);
71ee484e 1961 }
f8ad3899
KZ
1962
1963 return rc;
71ee484e 1964}
6d864a49 1965
8c0a7f91 1966static int dos_partition_is_used(
47b8e7c0 1967 struct fdisk_context *cxt,
8c0a7f91 1968 size_t i)
47b8e7c0 1969{
f540fe15 1970 struct dos_partition *p;
47b8e7c0
KZ
1971
1972 assert(cxt);
9ffeb235 1973 assert(cxt->label);
aa36c2cf 1974 assert(fdisk_is_label(cxt, DOS));
47b8e7c0 1975
8c0a7f91
KZ
1976 if (i >= cxt->label->nparts_max)
1977 return 0;
47b8e7c0 1978
af0df606 1979 p = self_partition(cxt, i);
47b8e7c0 1980
8c0a7f91 1981 return p && !is_cleared_partition(p);
47b8e7c0
KZ
1982}
1983
fb1caca7
KZ
1984static int dos_toggle_partition_flag(
1985 struct fdisk_context *cxt,
1986 size_t i,
1987 unsigned long flag)
1988{
f540fe15 1989 struct dos_partition *p;
fb1caca7
KZ
1990
1991 assert(cxt);
1992 assert(cxt->label);
aa36c2cf 1993 assert(fdisk_is_label(cxt, DOS));
fb1caca7
KZ
1994
1995 if (i >= cxt->label->nparts_max)
1996 return -EINVAL;
1997
af0df606 1998 p = self_partition(cxt, i);
fb1caca7
KZ
1999
2000 switch (flag) {
2001 case DOS_FLAG_ACTIVE:
2002 if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
829f4206
KZ
2003 fdisk_warnx(cxt, _("Partition %zu: is an extended "
2004 "partition."), i + 1);
fb1caca7
KZ
2005
2006 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
af0df606 2007 partition_set_changed(cxt, i, 1);
f736ae51
KZ
2008 fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
2009 p->boot_ind ?
2010 _("The bootable flag on partition %zu is enabled now.") :
2011 _("The bootable flag on partition %zu is disabled now."),
2012 i + 1);
fb1caca7
KZ
2013 break;
2014 default:
2015 return 1;
2016 }
2017
2018 return 0;
2019}
2020
bd85d11f 2021static const struct fdisk_field dos_fields[] =
262002ee
KZ
2022{
2023 /* basic */
bd85d11f
KZ
2024 { FDISK_FIELD_DEVICE, N_("Device"), 10, 0 },
2025 { FDISK_FIELD_BOOT, N_("Boot"), 1, 0 },
2026 { FDISK_FIELD_START, N_("Start"), 5, FDISK_FIELDFL_NUMBER },
2027 { FDISK_FIELD_END, N_("End"), 5, FDISK_FIELDFL_NUMBER },
2028 { FDISK_FIELD_SECTORS, N_("Sectors"), 5, FDISK_FIELDFL_NUMBER },
2029 { FDISK_FIELD_CYLINDERS,N_("Cylinders"), 5, FDISK_FIELDFL_NUMBER },
2030 { FDISK_FIELD_SIZE, N_("Size"), 5, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
2031 { FDISK_FIELD_TYPEID, N_("Id"), 2, FDISK_FIELDFL_NUMBER },
2032 { FDISK_FIELD_TYPE, N_("Type"), 0.1, 0 },
262002ee
KZ
2033
2034 /* expert mode */
bd85d11f
KZ
2035 { FDISK_FIELD_SADDR, N_("Start-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2036 { FDISK_FIELD_EADDR, N_("End-C/H/S"), 1, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2037 { FDISK_FIELD_ATTR, N_("Attrs"), 2, FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL }
262002ee
KZ
2038
2039};
2040
0c5d095e 2041static const struct fdisk_label_operations dos_operations =
b8855c86 2042{
0c5d095e
KZ
2043 .probe = dos_probe_label,
2044 .write = dos_write_disklabel,
2045 .verify = dos_verify_disklabel,
2046 .create = dos_create_disklabel,
f32a9be4 2047 .locate = dos_locate_disklabel,
1cf67f1c 2048 .list = dos_list_disklabel,
dd7ba604 2049 .reorder = dos_reorder,
21fe3dde 2050 .get_id = dos_get_disklabel_id,
35b1f0a4 2051 .set_id = dos_set_disklabel_id,
1cf67f1c 2052
262002ee 2053 .get_part = dos_get_partition,
77d6a70a 2054 .add_part = dos_add_partition,
262002ee 2055
0c5d095e 2056 .part_delete = dos_delete_partition,
0c5d095e 2057 .part_set_type = dos_set_parttype,
47b8e7c0 2058
fb1caca7 2059 .part_toggle_flag = dos_toggle_partition_flag,
8c0a7f91 2060 .part_is_used = dos_partition_is_used,
47b8e7c0 2061
cf3808e4 2062 .reset_alignment = dos_reset_alignment,
ddec0e40
KZ
2063
2064 .deinit = dos_deinit,
b8855c86 2065};
0c5d095e
KZ
2066
2067/*
2068 * allocates DOS in-memory stuff
2069 */
2070struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt)
2071{
2072 struct fdisk_label *lb;
2073 struct fdisk_dos_label *dos;
2074
2075 assert(cxt);
2076
2077 dos = calloc(1, sizeof(*dos));
2078 if (!dos)
2079 return NULL;
2080
2081 /* initialize generic part of the driver */
2082 lb = (struct fdisk_label *) dos;
2083 lb->name = "dos";
53b422ab 2084 lb->id = FDISK_DISKLABEL_DOS;
0c5d095e
KZ
2085 lb->op = &dos_operations;
2086 lb->parttypes = dos_parttypes;
a745611d 2087 lb->nparttypes = ARRAY_SIZE(dos_parttypes) - 1;
bd85d11f
KZ
2088 lb->fields = dos_fields;
2089 lb->nfields = ARRAY_SIZE(dos_fields);
0c5d095e
KZ
2090
2091 return lb;
2092}
852ce62b
KZ
2093
2094/*
2095 * Public label specific functions
2096 */
2097
2098int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
2099{
2100 struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
2101
2102 if (!lb)
2103 return -EINVAL;
2104
2105 dos->compatible = enable;
0b52b94c
KZ
2106 if (enable)
2107 lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
852ce62b
KZ
2108 return 0;
2109}
2110
2111int fdisk_dos_is_compatible(struct fdisk_label *lb)
2112{
2113 return ((struct fdisk_dos_label *) lb)->compatible;
2114}