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