]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libfdisk/src/dos.c
whereis: avoid printing uninitialized string
[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
63fb717d
KZ
1156 if (!is_dos_compatible(cxt))
1157 return;
1158
5dfca634
KZ
1159 real_s = sector(s) - 1;
1160 real_c = cylinder(s, c);
9bb8caff 1161 total = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
fcea064b 1162
5dfca634 1163 if (!total)
829f4206 1164 fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
5dfca634 1165 if (h >= cxt->geom.heads)
829f4206 1166 fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
fcea064b 1167 "maximum %d"), n, h + 1, cxt->geom.heads);
5dfca634 1168 if (real_s >= cxt->geom.sectors)
829f4206 1169 fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
fcea064b 1170 "maximum %llu"), n, s, cxt->geom.sectors);
5dfca634 1171 if (real_c >= cxt->geom.cylinders)
829f4206 1172 fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
fcea064b
KZ
1173 "maximum %llu"),
1174 n, real_c + 1,
1175 cxt->geom.cylinders);
1176
5dfca634 1177 if (cxt->geom.cylinders <= 1024 && start != total)
829f4206
KZ
1178 fdisk_warnx(cxt, _("Partition %zu: previous sectors %u "
1179 "disagrees with total %u"), n, start, total);
5dfca634
KZ
1180}
1181
1182/* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1183 * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1184 * Jan. 1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1185 * Lubkin Oct. 1991). */
1186
1187static void
1188long2chs(struct fdisk_context *cxt, unsigned long ls,
1189 unsigned int *c, unsigned int *h, unsigned int *s) {
1190 int spc = cxt->geom.heads * cxt->geom.sectors;
1191
1192 *c = ls / spc;
1193 ls = ls % spc;
1194 *h = ls / cxt->geom.sectors;
1195 *s = ls % cxt->geom.sectors + 1; /* sectors count from 1 */
1196}
1197
f540fe15 1198static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
e3661531 1199 size_t partition)
5dfca634
KZ
1200{
1201 unsigned int pbc, pbh, pbs; /* physical beginning c, h, s */
1202 unsigned int pec, peh, pes; /* physical ending c, h, s */
1203 unsigned int lbc, lbh, lbs; /* logical beginning c, h, s */
1204 unsigned int lec, leh, les; /* logical ending c, h, s */
1205
1206 if (!is_dos_compatible(cxt))
1207 return;
1208
1209 if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
1210 return; /* do not check extended partitions */
1211
60c1b036 1212 /* physical beginning c, h, s */
da967236
KZ
1213 pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
1214 pbh = p->bh;
1215 pbs = p->bs & 0x3f;
5dfca634 1216
60c1b036 1217 /* physical ending c, h, s */
da967236
KZ
1218 pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
1219 peh = p->eh;
1220 pes = p->es & 0x3f;
5dfca634 1221
60c1b036 1222 /* compute logical beginning (c, h, s) */
b44244cb 1223 long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
5dfca634 1224
60c1b036 1225 /* compute logical ending (c, h, s) */
b44244cb 1226 long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
5dfca634 1227
60c1b036 1228 /* Same physical / logical beginning? */
fcea064b
KZ
1229 if (cxt->geom.cylinders <= 1024
1230 && (pbc != lbc || pbh != lbh || pbs != lbs)) {
829f4206 1231 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
fcea064b
KZ
1232 "beginnings (non-Linux?): "
1233 "phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1234 partition + 1,
1235 pbc, pbh, pbs,
1236 lbc, lbh, lbs);
5dfca634
KZ
1237 }
1238
60c1b036 1239 /* Same physical / logical ending? */
fcea064b
KZ
1240 if (cxt->geom.cylinders <= 1024
1241 && (pec != lec || peh != leh || pes != les)) {
829f4206 1242 fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
fcea064b
KZ
1243 "endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1244 partition + 1,
1245 pec, peh, pes,
1246 lec, leh, les);
5dfca634
KZ
1247 }
1248
60c1b036 1249 /* Ending on cylinder boundary? */
5dfca634 1250 if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
829f4206 1251 fdisk_warnx(cxt, _("Partition %zu: does not end on "
fcea064b 1252 "cylinder boundary."),
5dfca634
KZ
1253 partition + 1);
1254 }
1255}
1256
9ffeb235 1257static int dos_verify_disklabel(struct fdisk_context *cxt)
2ca61a61 1258{
e3661531 1259 size_t i, j;
2ca61a61 1260 sector_t total = 1, n_sectors = cxt->total_sectors;
e3661531
KZ
1261 unsigned long long first[cxt->label->nparts_max],
1262 last[cxt->label->nparts_max];
f540fe15 1263 struct dos_partition *p;
fcea064b 1264 struct fdisk_dos_label *l = self_label(cxt);
2ca61a61 1265
9ffeb235
KZ
1266 assert(fdisk_is_disklabel(cxt, DOS));
1267
e3661531
KZ
1268 fill_bounds(cxt, first, last);
1269 for (i = 0; i < cxt->label->nparts_max; i++) {
af0df606 1270 struct pte *pe = self_pte(cxt, i);
2ca61a61 1271
af0df606 1272 p = self_partition(cxt, i);
d0a870c5 1273 if (is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
2ca61a61 1274 check_consistency(cxt, p, i);
61eaad50
KZ
1275 fdisk_warn_alignment(cxt, get_abs_partition_start(pe), i);
1276 if (get_abs_partition_start(pe) < first[i])
fcea064b 1277 fdisk_warnx(cxt, _(
829f4206 1278 "Partition %zu: bad start-of-data."),
fcea064b
KZ
1279 i + 1);
1280
1281 check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
2ca61a61 1282 total += last[i] + 1 - first[i];
fcea064b 1283
11712b66
KZ
1284 if (i == 0)
1285 total += get_abs_partition_start(pe) - 1;
1286
fcea064b 1287 for (j = 0; j < i; j++) {
2ca61a61
DB
1288 if ((first[i] >= first[j] && first[i] <= last[j])
1289 || ((last[i] <= last[j] && last[i] >= first[j]))) {
fcea064b 1290
829f4206
KZ
1291 fdisk_warnx(cxt, _("Partition %zu: "
1292 "overlaps partition %zu."),
fcea064b
KZ
1293 j + 1, i + 1);
1294
2ca61a61
DB
1295 total += first[i] >= first[j] ?
1296 first[i] : first[j];
1297 total -= last[i] <= last[j] ?
1298 last[i] : last[j];
1299 }
fcea064b 1300 }
2ca61a61
DB
1301 }
1302 }
1303
fcea064b 1304 if (l->ext_offset) {
5d324c6b 1305 sector_t e_last;
1b92d076
KZ
1306 struct pte *ext_pe = self_pte(cxt, l->ext_index);
1307
1308 e_last = get_abs_partition_end(ext_pe);
2ca61a61 1309
e3661531 1310 for (i = 4; i < cxt->label->nparts_max; i++) {
2ca61a61 1311 total++;
af0df606
KZ
1312 p = self_partition(cxt, i);
1313
2ca61a61 1314 if (!p->sys_ind) {
e3661531 1315 if (i != 4 || i + 1 < cxt->label->nparts_max)
fcea064b 1316 fdisk_warnx(cxt,
829f4206 1317 _("Partition %zu: empty."),
fcea064b
KZ
1318 i + 1);
1319 } else if (first[i] < l->ext_offset
1320 || last[i] > e_last) {
1321
829f4206
KZ
1322 fdisk_warnx(cxt, _("Logical partition %zu: "
1323 "not entirely in partition %zu."),
fcea064b 1324 i + 1, l->ext_index + 1);
2ca61a61 1325 }
2ca61a61
DB
1326 }
1327 }
1328
1329 if (total > n_sectors)
fcea064b
KZ
1330 fdisk_warnx(cxt, _("Total allocated sectors %llu greater "
1331 "than the maximum %llu."), total, n_sectors);
2ca61a61 1332 else if (total < n_sectors)
fcea064b 1333 fdisk_warnx(cxt, _("Remaining %lld unallocated %ld-byte "
ac1a559a 1334 "sectors."), n_sectors - total, cxt->sector_size);
2ca61a61
DB
1335
1336 return 0;
1337}
1338
9dea2923
DB
1339/*
1340 * Ask the user for new partition type information (logical, extended).
0f639e54
DB
1341 * This function calls the actual partition adding logic - add_partition.
1342 *
1343 * API callback.
9dea2923 1344 */
77d6a70a
KZ
1345static int dos_add_partition(struct fdisk_context *cxt,
1346 struct fdisk_partition *pa)
9dea2923 1347{
e3661531
KZ
1348 size_t i, free_primary = 0;
1349 int rc = 0;
e77313a6 1350 struct fdisk_dos_label *l;
1b92d076 1351 struct pte *ext_pe;
9dea2923 1352
9ffeb235
KZ
1353 assert(cxt);
1354 assert(cxt->label);
1355 assert(fdisk_is_disklabel(cxt, DOS));
1356
e77313a6 1357 l = self_label(cxt);
1b92d076 1358 ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
a3e37fa7
KZ
1359
1360 /* pa specifies start within extended partition, add logical */
1b92d076 1361 if (pa && pa->start && ext_pe
a3e37fa7 1362 && pa->start >= l->ext_offset
1b92d076 1363 && pa->start <= get_abs_partition_end(ext_pe)) {
a3e37fa7
KZ
1364 rc = add_logical(cxt, pa);
1365 goto done;
1366
1367 /* pa specifies start, but outside extended partition */
1368 } else if (pa && pa->start && l->ext_offset) {
1369 int j;
1370
1371 j = get_partition_unused_primary(cxt, pa);
1372 if (j >= 0) {
1373 rc = add_partition(cxt, j, pa);
1374 goto done;
1375 }
1376
1377 }
20f878fe 1378
af0df606
KZ
1379 for (i = 0; i < 4; i++) {
1380 struct dos_partition *p = self_partition(cxt, i);
28a5bccc 1381 free_primary += !is_used_partition(p);
af0df606 1382 }
9dea2923 1383
e3661531 1384 if (!free_primary && cxt->label->nparts_max >= MAXIMUM_PARTS) {
fcea064b
KZ
1385 fdisk_info(cxt, _("The maximum number of partitions has "
1386 "been created."));
8254c3a5 1387 return -EINVAL;
9dea2923 1388 }
1bf3a034 1389 rc = 1;
fcea064b 1390
9dea2923 1391 if (!free_primary) {
fcea064b
KZ
1392 if (l->ext_offset) {
1393 fdisk_info(cxt, _("All primary partitions are in use."));
77d6a70a 1394 rc = add_logical(cxt, pa);
9dea2923 1395 } else
fcea064b 1396 fdisk_info(cxt, _("If you want to create more than "
0d0d12ad 1397 "four partitions, you must replace a "
fcea064b
KZ
1398 "primary partition with an extended "
1399 "partition first."));
1400
e3661531 1401 } else if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
c9f63764 1402 int j;
1bf3a034 1403
fcea064b
KZ
1404 fdisk_info(cxt, _("All logical partitions are in use. "
1405 "Adding a primary partition."));
77d6a70a 1406 j = get_partition_unused_primary(cxt, pa);
c9f63764 1407 if (j >= 0)
77d6a70a 1408 rc = add_partition(cxt, j, pa);
9dea2923 1409 } else {
20f878fe
KZ
1410 char hint[BUFSIZ];
1411 struct fdisk_ask *ask;
1412 int c;
1413
1414 ask = fdisk_new_ask();
1415 if (!ask)
1416 return -ENOMEM;
1417 fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
1418 fdisk_ask_set_query(ask, _("Partition type"));
1419 fdisk_ask_menu_set_default(ask, free_primary == 1
1420 && !l->ext_offset ? 'e' : 'p');
1421 snprintf(hint, sizeof(hint),
1422 _("%zu primary, %d extended, %zu free"),
1423 4 - (l->ext_offset ? 1 : 0) - free_primary,
1424 l->ext_offset ? 1 : 0,
1425 free_primary);
1426
1427 fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
1428 if (!l->ext_offset)
1429 fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
1430 else
1431 fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
1432
1433 rc = fdisk_do_ask(cxt, ask);
bd0d850b
KZ
1434 if (rc)
1435 return rc;
20f878fe
KZ
1436 fdisk_ask_menu_get_result(ask, &c);
1437 fdisk_free_ask(ask);
bd0d850b 1438
9dea2923 1439 if (c == 'p') {
77d6a70a 1440 int j = get_partition_unused_primary(cxt, pa);
c9f63764 1441 if (j >= 0)
77d6a70a 1442 rc = add_partition(cxt, j, pa);
8254c3a5 1443 goto done;
fcea064b 1444 } else if (c == 'l' && l->ext_offset) {
77d6a70a 1445 rc = add_logical(cxt, pa);
8254c3a5 1446 goto done;
fcea064b 1447 } else if (c == 'e' && !l->ext_offset) {
77d6a70a 1448 int j = get_partition_unused_primary(cxt, pa);
c9f63764 1449 if (j >= 0) {
77d6a70a
KZ
1450 struct fdisk_partition xpa = { .type = NULL };
1451 struct fdisk_parttype *t;
1452
bb58112e
KZ
1453 t = fdisk_get_parttype_from_code(cxt,
1454 MBR_DOS_EXTENDED_PARTITION);
77d6a70a
KZ
1455 if (!pa)
1456 pa = &xpa;
1457 fdisk_partition_set_type(pa, t);
1458 rc = add_partition(cxt, j, pa);
ed470672 1459 }
8254c3a5 1460 goto done;
9dea2923 1461 } else
ac1a559a 1462 fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
9dea2923 1463 }
8254c3a5 1464done:
e3661531
KZ
1465 if (rc == 0)
1466 cxt->label->nparts_cur++;
8254c3a5 1467 return rc;
9dea2923 1468}
0dc13a38 1469
fae7b1bc
DB
1470static int write_sector(struct fdisk_context *cxt, sector_t secno,
1471 unsigned char *buf)
0dc13a38 1472{
21770662
KZ
1473 int rc;
1474
1475 rc = seek_sector(cxt, secno);
1476 if (rc != 0) {
98aeae96 1477 fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
21770662
KZ
1478 (uintmax_t) secno);
1479 return rc;
1480 }
bcddbe96 1481
88141067 1482 DBG(LABEL, ul_debug("DOS: writting to sector %ju", (uintmax_t) secno));
bcddbe96 1483
fae7b1bc
DB
1484 if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
1485 return -errno;
1486 return 0;
1487}
1488
9ffeb235 1489static int dos_write_disklabel(struct fdisk_context *cxt)
fae7b1bc 1490{
8b3d9241 1491 struct fdisk_dos_label *l = self_label(cxt);
e3661531 1492 size_t i;
8b3d9241 1493 int rc = 0, mbr_changed = 0;
0dc13a38 1494
9ffeb235
KZ
1495 assert(cxt);
1496 assert(cxt->label);
1497 assert(fdisk_is_disklabel(cxt, DOS));
1498
8b3d9241
KZ
1499 mbr_changed = l->non_pt_changed;
1500
0dc13a38 1501 /* MBR (primary partitions) */
8b3d9241 1502 if (!mbr_changed) {
af0df606
KZ
1503 for (i = 0; i < 4; i++) {
1504 struct pte *pe = self_pte(cxt, i);
1505 if (pe->changed)
8b3d9241 1506 mbr_changed = 1;
af0df606 1507 }
0dc13a38 1508 }
8b3d9241 1509 if (mbr_changed) {
67987b47 1510 mbr_set_magic(cxt->firstsector);
fae7b1bc
DB
1511 rc = write_sector(cxt, 0, cxt->firstsector);
1512 if (rc)
1513 goto done;
0dc13a38 1514 }
8b3d9241 1515
bcddbe96
KZ
1516 if (cxt->label->nparts_max <= 4 && l->ext_offset) {
1517 /* we have empty extended partition, check if the partition has
1518 * been modified and then cleanup possible remaining EBR */
1519 struct pte *pe = self_pte(cxt, l->ext_index);
1520 unsigned char empty[512] = { 0 };
1521 sector_t off = pe ? get_abs_partition_start(pe) : 0;
1522
1523 if (off && pe->changed) {
1524 mbr_set_magic(empty);
1525 write_sector(cxt, off, empty);
1526 }
1527 }
1528
0dc13a38 1529 /* EBR (logical partitions) */
e3661531 1530 for (i = 4; i < cxt->label->nparts_max; i++) {
af0df606 1531 struct pte *pe = self_pte(cxt, i);
0dc13a38
DB
1532
1533 if (pe->changed) {
87a97832 1534 mbr_set_magic(pe->sectorbuffer);
fae7b1bc
DB
1535 rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
1536 if (rc)
1537 goto done;
0dc13a38
DB
1538 }
1539 }
fae7b1bc
DB
1540
1541done:
1542 return rc;
0dc13a38 1543}
b8855c86 1544
f32a9be4
KZ
1545static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
1546 const char **name, off_t *offset, size_t *size)
1547{
1548 assert(cxt);
1549
1550 *name = NULL;
1551 *offset = 0;
1552 *size = 0;
1553
1554 switch (n) {
1555 case 0:
1556 *name = "MBR";
1557 *offset = 0;
1558 *size = 512;
1559 break;
1560 default:
1561 /* extended partitions */
1562 if (n - 1 + 4 < cxt->label->nparts_max) {
1563 struct pte *pe = self_pte(cxt, n - 1 + 4);
1564
1565 assert(pe->private_sectorbuffer);
1566
1567 *name = "EBR";
1568 *offset = pe->offset * cxt->sector_size;
1569 *size = 512;
1570 } else
1571 return 1;
1572 break;
1573 }
1574
1575 return 0;
1576}
1577
8a95621d 1578static int dos_set_parttype(
e3661531 1579 struct fdisk_context *cxt,
9ffeb235 1580 size_t partnum,
8a95621d 1581 struct fdisk_parttype *t)
02460b8a 1582{
f540fe15 1583 struct dos_partition *p;
02460b8a 1584
9ffeb235
KZ
1585 assert(cxt);
1586 assert(cxt->label);
e09435aa 1587 assert(fdisk_is_disklabel(cxt, DOS));
9ffeb235
KZ
1588
1589 if (partnum >= cxt->label->nparts_max || !t || t->type > UINT8_MAX)
02460b8a
KZ
1590 return -EINVAL;
1591
af0df606 1592 p = self_partition(cxt, partnum);
02460b8a
KZ
1593 if (t->type == p->sys_ind)
1594 return 0;
1595
1596 if (IS_EXTENDED(p->sys_ind) || IS_EXTENDED(t->type)) {
fcea064b
KZ
1597 fdisk_warnx(cxt, _("You cannot change a partition into an "
1598 "extended one or vice versa. Delete it first."));
02460b8a
KZ
1599 return -EINVAL;
1600 }
1601
1602 if (is_dos_partition(t->type) || is_dos_partition(p->sys_ind))
fcea064b 1603 fdisk_info(cxt, _("If you have created or modified any DOS 6.x "
0d0d12ad 1604 "partitions, please see the fdisk documentation for additional "
fcea064b 1605 "information."));
02460b8a 1606
b37ab36e
KZ
1607 if (!t->type)
1608 fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
1609 "Having partitions of type 0 is probably unwise."));
02460b8a 1610 p->sys_ind = t->type;
af0df606
KZ
1611
1612 partition_set_changed(cxt, partnum, 1);
02460b8a
KZ
1613 return 0;
1614}
1615
dfc96cbf
KZ
1616/*
1617 * Check whether partition entries are ordered by their starting positions.
1618 * Return 0 if OK. Return i if partition i should have been earlier.
1619 * Two separate checks: primary and logical partitions.
1620 */
e3661531 1621static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
dfc96cbf 1622{
e3661531
KZ
1623 size_t last_p_start_pos = 0, p_start_pos;
1624 size_t i, last_i = 0;
dfc96cbf 1625
e3661531 1626 for (i = 0 ; i < cxt->label->nparts_max; i++) {
af0df606
KZ
1627
1628 struct pte *pe = self_pte(cxt, i);
1629 struct dos_partition *p = pe->pt_entry;
1630
dfc96cbf
KZ
1631 if (i == 4) {
1632 last_i = 4;
1633 last_p_start_pos = 0;
1634 }
d0a870c5 1635 if (is_used_partition(p)) {
61eaad50 1636 p_start_pos = get_abs_partition_start(pe);
dfc96cbf
KZ
1637
1638 if (last_p_start_pos > p_start_pos) {
1639 if (prev)
1640 *prev = last_i;
1641 return i;
1642 }
1643
1644 last_p_start_pos = p_start_pos;
1645 last_i = i;
1646 }
1647 }
1648 return 0;
1649}
6d864a49 1650
1cf67f1c
KZ
1651static int dos_list_disklabel(struct fdisk_context *cxt)
1652{
6d864a49 1653 assert(cxt);
9ffeb235 1654 assert(cxt->label);
6d864a49
KZ
1655 assert(fdisk_is_disklabel(cxt, DOS));
1656
6c89f750 1657 return 0;
262002ee 1658}
1cf67f1c 1659
262002ee
KZ
1660static int dos_get_partition(struct fdisk_context *cxt, size_t n,
1661 struct fdisk_partition *pa)
1662{
1663 struct dos_partition *p;
1664 struct pte *pe;
bd5e8291 1665 struct fdisk_dos_label *lb;
1cf67f1c 1666
262002ee
KZ
1667 assert(cxt);
1668 assert(pa);
1669 assert(cxt->label);
1670 assert(fdisk_is_disklabel(cxt, DOS));
1cf67f1c 1671
bd5e8291 1672 lb = self_label(cxt);
262002ee
KZ
1673 pe = self_pte(cxt, n);
1674 p = pe->pt_entry;
1675 pa->used = !is_cleared_partition(p);
1676 if (!pa->used)
1677 return 0;
1cf67f1c 1678
7f01ec7a 1679 pa->type = dos_partition_parttype(cxt, p);
262002ee 1680 pa->boot = p->boot_ind ? p->boot_ind == ACTIVE_FLAG ? '*' : '?' : ' ';
77d6a70a 1681 pa->start = get_abs_partition_start(pe);
1b92d076
KZ
1682 pa->end = get_abs_partition_end(pe);
1683 pa->size = dos_partition_get_size(p);
e77313a6 1684 pa->container = lb->ext_offset && n == lb->ext_index;
1cf67f1c 1685
bd5e8291
KZ
1686 if (n >= 4)
1687 pa->parent_partno = lb->ext_index;
03643931 1688
262002ee
KZ
1689 if (asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
1690 return -ENOMEM;
6d864a49 1691
262002ee
KZ
1692 /* start C/H/S */
1693 if (asprintf(&pa->start_addr, "%d/%d/%d",
1694 cylinder(p->bs, p->bc),
1695 sector(p->bs),
1696 p->bh) < 0)
1697 return -ENOMEM;
1cf67f1c 1698
262002ee
KZ
1699 /* end C/H/S */
1700 if (asprintf(&pa->end_addr, "%d/%d/%d",
1701 cylinder(p->es, p->ec),
1702 sector(p->es),
1703 p->eh) < 0)
1704 return -ENOMEM;
6d864a49 1705
262002ee 1706 return 0;
6d864a49
KZ
1707}
1708
a60087aa
KZ
1709static void print_chain_of_logicals(struct fdisk_context *cxt)
1710{
1711 size_t i;
6c64adee 1712 struct fdisk_dos_label *l = self_label(cxt);
a60087aa
KZ
1713
1714 fputc('\n', stdout);
1715
1716 for (i = 4; i < cxt->label->nparts_max; i++) {
1717 struct pte *pe = self_pte(cxt, i);
1718
1719 printf("#%02zu EBR [%10ju], "
6c64adee
KZ
1720 "data[start=%10ju (%10ju), size=%10ju], "
1721 "link[start=%10ju (%10ju), size=%10ju]\n",
a60087aa 1722 i, (uintmax_t) pe->offset,
6c64adee 1723 /* data */
a60087aa 1724 (uintmax_t) dos_partition_get_start(pe->pt_entry),
6c64adee 1725 (uintmax_t) get_abs_partition_start(pe),
a60087aa 1726 (uintmax_t) dos_partition_get_size(pe->pt_entry),
6c64adee 1727 /* link */
a60087aa 1728 (uintmax_t) dos_partition_get_start(pe->ex_entry),
6c64adee 1729 (uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
a60087aa
KZ
1730 (uintmax_t) dos_partition_get_size(pe->ex_entry));
1731 }
1732}
1733
c1156d85
KZ
1734static int cmp_ebr_offsets(const void *a, const void *b)
1735{
1736 struct pte *ae = (struct pte *) a,
1737 *be = (struct pte *) b;
1738
1739 if (ae->offset == 0 && ae->offset == 0)
1740 return 0;
1741 if (ae->offset == 0)
1742 return 1;
1743 if (be->offset == 0)
1744 return -1;
1745
1746 return ae->offset - be->offset;
1747}
1748
dfc96cbf
KZ
1749/*
1750 * Fix the chain of logicals.
dfc96cbf 1751 *
a60087aa
KZ
1752 * The function does not modify data partitions within EBR tables
1753 * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
1754 * (pte->ex_entry) between EBR tables.
1755 *
dfc96cbf 1756 */
e3661531 1757static void fix_chain_of_logicals(struct fdisk_context *cxt)
dfc96cbf 1758{
af0df606 1759 struct fdisk_dos_label *l = self_label(cxt);
a60087aa
KZ
1760 size_t i;
1761
a60087aa
KZ
1762 DBG(LABEL, print_chain_of_logicals(cxt));
1763
c1156d85
KZ
1764 /* Sort chain by EBR offsets */
1765 qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
1766 cmp_ebr_offsets);
1767
a60087aa 1768again:
c1156d85 1769 /* Sort data partitions by start */
a60087aa
KZ
1770 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
1771 struct pte *cur = self_pte(cxt, i),
1772 *nxt = self_pte(cxt, i + 1);
a60087aa 1773
c1156d85
KZ
1774 if (get_abs_partition_start(cur) >
1775 get_abs_partition_start(nxt)) {
a60087aa 1776
c1156d85
KZ
1777 struct dos_partition tmp = *cur->pt_entry;
1778 sector_t cur_start = get_abs_partition_start(cur),
1779 nxt_start = get_abs_partition_start(nxt);
a60087aa 1780
c1156d85
KZ
1781 /* swap data partitions */
1782 *cur->pt_entry = *nxt->pt_entry;
1783 *nxt->pt_entry = tmp;
a60087aa 1784
c1156d85
KZ
1785 /* Recount starts according to EBR offsets, the absolute
1786 * address tas to be still the same! */
1787 dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
1788 dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
a60087aa 1789
c1156d85
KZ
1790 partition_set_changed(cxt, i, 1);
1791 partition_set_changed(cxt, i + 1, 1);
1792 goto again;
1793 }
dfc96cbf
KZ
1794 }
1795
c1156d85 1796 /* Update EBR links */
a60087aa
KZ
1797 for (i = 4; i < cxt->label->nparts_max - 1; i++) {
1798 struct pte *cur = self_pte(cxt, i),
1799 *nxt = self_pte(cxt, i + 1);
1800
1801 sector_t noff = nxt->offset - l->ext_offset,
1802 ooff = dos_partition_get_start(cur->ex_entry);
1803
a60087aa
KZ
1804 if (noff == ooff)
1805 continue;
1806
1807 DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
1808 (uintmax_t) cur->offset,
1809 (uintmax_t) ooff, (uintmax_t) noff));
1810
1811 set_partition(cxt, i, 1, nxt->offset,
1812 get_abs_partition_end(nxt), MBR_DOS_EXTENDED_PARTITION);
6c64adee
KZ
1813
1814 if (i + 1 == cxt->label->nparts_max - 1) {
1815 clear_partition(nxt->ex_entry);
1816 partition_set_changed(cxt, i + 1, 1);
1817 }
1818
a60087aa
KZ
1819 }
1820 DBG(LABEL, print_chain_of_logicals(cxt));
dfc96cbf
KZ
1821}
1822
dd7ba604 1823static int dos_reorder(struct fdisk_context *cxt)
dfc96cbf
KZ
1824{
1825 struct pte *pei, *pek;
e3661531 1826 size_t i,k;
dfc96cbf 1827
e3661531 1828 if (!wrong_p_order(cxt, NULL)) {
fcea064b 1829 fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
f8ad3899 1830 return 0;
dfc96cbf
KZ
1831 }
1832
e3661531 1833 while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
dfc96cbf
KZ
1834 /* partition i should have come earlier, move it */
1835 /* We have to move data in the MBR */
f540fe15 1836 struct dos_partition *pi, *pk, *pe, pbuf;
af0df606
KZ
1837 pei = self_pte(cxt, i);
1838 pek = self_pte(cxt, k);
dfc96cbf 1839
6864f168
KZ
1840 pe = pei->ex_entry;
1841 pei->ex_entry = pek->ex_entry;
1842 pek->ex_entry = pe;
dfc96cbf 1843
6864f168
KZ
1844 pi = pei->pt_entry;
1845 pk = pek->pt_entry;
dfc96cbf 1846
f540fe15
KZ
1847 memmove(&pbuf, pi, sizeof(struct dos_partition));
1848 memmove(pi, pk, sizeof(struct dos_partition));
1849 memmove(pk, &pbuf, sizeof(struct dos_partition));
dfc96cbf 1850
af0df606
KZ
1851 partition_set_changed(cxt, i, 1);
1852 partition_set_changed(cxt, k, 1);
dfc96cbf
KZ
1853 }
1854
1855 if (i)
e3661531 1856 fix_chain_of_logicals(cxt);
dfc96cbf 1857
f61d2632 1858 fdisk_info(cxt, _("Done."));
f8ad3899 1859 return 0;
dfc96cbf 1860}
6d864a49 1861
829f4206 1862int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
71ee484e 1863{
f8ad3899
KZ
1864 struct pte *pe;
1865 struct dos_partition *p;
71ee484e 1866 unsigned int new, free_start, curr_start, last;
bd0d850b 1867 uintmax_t res = 0;
e3661531 1868 size_t x;
f8ad3899 1869 int rc;
71ee484e
KZ
1870
1871 assert(cxt);
1872 assert(fdisk_is_disklabel(cxt, DOS));
1873
f8ad3899
KZ
1874 pe = self_pte(cxt, i);
1875 p = pe->pt_entry;
1876
d0a870c5 1877 if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
829f4206 1878 fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
f8ad3899 1879 return 0;
71ee484e
KZ
1880 }
1881
1882 /* the default start is at the second sector of the disk or at the
1883 * second sector of the extended partition
1884 */
1885 free_start = pe->offset ? pe->offset + 1 : 1;
1886
61eaad50 1887 curr_start = get_abs_partition_start(pe);
71ee484e
KZ
1888
1889 /* look for a free space before the current start of the partition */
e3661531 1890 for (x = 0; x < cxt->label->nparts_max; x++) {
71ee484e 1891 unsigned int end;
af0df606 1892 struct pte *prev_pe = self_pte(cxt, x);
f540fe15 1893 struct dos_partition *prev_p = prev_pe->pt_entry;
71ee484e
KZ
1894
1895 if (!prev_p)
1896 continue;
af0df606
KZ
1897 end = get_abs_partition_start(prev_pe)
1898 + dos_partition_get_size(prev_p);
71ee484e 1899
d0a870c5 1900 if (is_used_partition(prev_p) &&
71ee484e
KZ
1901 end > free_start && end <= curr_start)
1902 free_start = end;
1903 }
1904
1b92d076 1905 last = get_abs_partition_end(pe);
71ee484e 1906
f8ad3899
KZ
1907 rc = fdisk_ask_number(cxt, free_start, curr_start, last,
1908 _("New beginning of data"), &res);
1909 if (rc)
1910 return rc;
1911
bd0d850b 1912 new = res - pe->offset;
71ee484e 1913
b44244cb 1914 if (new != dos_partition_get_size(p)) {
af0df606
KZ
1915 unsigned int sects = dos_partition_get_size(p)
1916 + dos_partition_get_start(p) - new;
1917
b44244cb
KZ
1918 dos_partition_set_size(p, sects);
1919 dos_partition_set_start(p, new);
af0df606
KZ
1920
1921 partition_set_changed(cxt, i, 1);
71ee484e 1922 }
f8ad3899
KZ
1923
1924 return rc;
71ee484e 1925}
6d864a49 1926
8c0a7f91 1927static int dos_partition_is_used(
47b8e7c0 1928 struct fdisk_context *cxt,
8c0a7f91 1929 size_t i)
47b8e7c0 1930{
f540fe15 1931 struct dos_partition *p;
47b8e7c0
KZ
1932
1933 assert(cxt);
9ffeb235 1934 assert(cxt->label);
47b8e7c0
KZ
1935 assert(fdisk_is_disklabel(cxt, DOS));
1936
8c0a7f91
KZ
1937 if (i >= cxt->label->nparts_max)
1938 return 0;
47b8e7c0 1939
af0df606 1940 p = self_partition(cxt, i);
47b8e7c0 1941
8c0a7f91 1942 return p && !is_cleared_partition(p);
47b8e7c0
KZ
1943}
1944
fb1caca7
KZ
1945static int dos_toggle_partition_flag(
1946 struct fdisk_context *cxt,
1947 size_t i,
1948 unsigned long flag)
1949{
f540fe15 1950 struct dos_partition *p;
fb1caca7
KZ
1951
1952 assert(cxt);
1953 assert(cxt->label);
1954 assert(fdisk_is_disklabel(cxt, DOS));
1955
1956 if (i >= cxt->label->nparts_max)
1957 return -EINVAL;
1958
af0df606 1959 p = self_partition(cxt, i);
fb1caca7
KZ
1960
1961 switch (flag) {
1962 case DOS_FLAG_ACTIVE:
1963 if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
829f4206
KZ
1964 fdisk_warnx(cxt, _("Partition %zu: is an extended "
1965 "partition."), i + 1);
fb1caca7
KZ
1966
1967 p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
af0df606 1968 partition_set_changed(cxt, i, 1);
f736ae51
KZ
1969 fdisk_sinfo(cxt, FDISK_INFO_SUCCESS,
1970 p->boot_ind ?
1971 _("The bootable flag on partition %zu is enabled now.") :
1972 _("The bootable flag on partition %zu is disabled now."),
1973 i + 1);
fb1caca7
KZ
1974 break;
1975 default:
1976 return 1;
1977 }
1978
1979 return 0;
1980}
1981
262002ee
KZ
1982static const struct fdisk_column dos_columns[] =
1983{
1984 /* basic */
1985 { FDISK_COL_DEVICE, N_("Device"), 10, 0 },
1986 { FDISK_COL_BOOT, N_("Boot"), 1, 0 },
baa3b270
OO
1987 { FDISK_COL_START, N_("Start"), 5, SCOLS_FL_RIGHT },
1988 { FDISK_COL_END, N_("End"), 5, SCOLS_FL_RIGHT },
1989 { FDISK_COL_SECTORS, N_("Sectors"), 5, SCOLS_FL_RIGHT },
1990 { FDISK_COL_CYLINDERS, N_("Cylinders"), 5, SCOLS_FL_RIGHT },
1991 { FDISK_COL_SIZE, N_("Size"), 5, SCOLS_FL_RIGHT, FDISK_COLFL_EYECANDY },
1992 { FDISK_COL_TYPEID, N_("Id"), 2, SCOLS_FL_RIGHT },
1993 { FDISK_COL_TYPE, N_("Type"), 0.1, SCOLS_FL_TRUNC },
262002ee
KZ
1994
1995 /* expert mode */
baa3b270
OO
1996 { FDISK_COL_SADDR, N_("Start-C/H/S"), 1, SCOLS_FL_RIGHT, FDISK_COLFL_DETAIL },
1997 { FDISK_COL_EADDR, N_("End-C/H/S"), 1, SCOLS_FL_RIGHT, FDISK_COLFL_DETAIL },
1998 { FDISK_COL_ATTR, N_("Attrs"), 2, SCOLS_FL_RIGHT, FDISK_COLFL_DETAIL }
262002ee
KZ
1999
2000};
2001
0c5d095e 2002static const struct fdisk_label_operations dos_operations =
b8855c86 2003{
0c5d095e
KZ
2004 .probe = dos_probe_label,
2005 .write = dos_write_disklabel,
2006 .verify = dos_verify_disklabel,
2007 .create = dos_create_disklabel,
f32a9be4 2008 .locate = dos_locate_disklabel,
1cf67f1c 2009 .list = dos_list_disklabel,
dd7ba604 2010 .reorder = dos_reorder,
21fe3dde 2011 .get_id = dos_get_disklabel_id,
35b1f0a4 2012 .set_id = dos_set_disklabel_id,
1cf67f1c 2013
262002ee 2014 .get_part = dos_get_partition,
77d6a70a 2015 .add_part = dos_add_partition,
262002ee 2016
0c5d095e 2017 .part_delete = dos_delete_partition,
0c5d095e 2018 .part_set_type = dos_set_parttype,
47b8e7c0 2019
fb1caca7 2020 .part_toggle_flag = dos_toggle_partition_flag,
8c0a7f91 2021 .part_is_used = dos_partition_is_used,
47b8e7c0 2022
cf3808e4 2023 .reset_alignment = dos_reset_alignment,
ddec0e40
KZ
2024
2025 .deinit = dos_deinit,
b8855c86 2026};
0c5d095e
KZ
2027
2028/*
2029 * allocates DOS in-memory stuff
2030 */
2031struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt)
2032{
2033 struct fdisk_label *lb;
2034 struct fdisk_dos_label *dos;
2035
2036 assert(cxt);
2037
2038 dos = calloc(1, sizeof(*dos));
2039 if (!dos)
2040 return NULL;
2041
2042 /* initialize generic part of the driver */
2043 lb = (struct fdisk_label *) dos;
2044 lb->name = "dos";
53b422ab 2045 lb->id = FDISK_DISKLABEL_DOS;
0c5d095e
KZ
2046 lb->op = &dos_operations;
2047 lb->parttypes = dos_parttypes;
2048 lb->nparttypes = ARRAY_SIZE(dos_parttypes);
262002ee
KZ
2049 lb->columns = dos_columns;
2050 lb->ncolumns = ARRAY_SIZE(dos_columns);
0c5d095e
KZ
2051
2052 return lb;
2053}
852ce62b
KZ
2054
2055/*
2056 * Public label specific functions
2057 */
2058
2059int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
2060{
2061 struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
2062
2063 if (!lb)
2064 return -EINVAL;
2065
2066 dos->compatible = enable;
0b52b94c
KZ
2067 if (enable)
2068 lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
852ce62b
KZ
2069 return 0;
2070}
2071
2072int fdisk_dos_is_compatible(struct fdisk_label *lb)
2073{
2074 return ((struct fdisk_dos_label *) lb)->compatible;
2075}