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