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