1 /*
2  *
3  * Copyright (C) 2007-2013 Karel Zak <kzak@redhat.com>
4  *                    2012 Davidlohr Bueso <dave@gnu.org>
5  *
6  * This is re-written version for libfdisk, the original was fdiskdoslabel.c
7  * from util-linux fdisk.
8  */
9 #include "c.h"
10 #include "randutils.h"
11 #include "pt-mbr.h"
12 #include "strutils.h"
13 
14 #include "fdiskP.h"
15 
16 #include <ctype.h>
17 
18 #define MAXIMUM_PARTS	60
19 #define ACTIVE_FLAG     0x80
20 
21 /**
22  * SECTION: dos
23  * @title: DOS
24  * @short_description: disk label specific functions
25  *
26  */
27 
28 
29 #define IS_EXTENDED(i) \
30 	((i) == MBR_DOS_EXTENDED_PARTITION \
31 	 || (i) == MBR_W95_EXTENDED_PARTITION \
32 	 || (i) == MBR_LINUX_EXTENDED_PARTITION)
33 
34 /*
35  * per partition table entry data
36  *
37  * The four primary partitions have the same sectorbuffer
38  * and have NULL ex_entry.
39  *
40  * Each logical partition table entry has two pointers, one for the
41  * partition and one link to the next one.
42  */
43 struct pte {
44 	struct dos_partition *pt_entry;	/* on-disk MBR entry */
45 	struct dos_partition *ex_entry;	/* on-disk EBR entry */
46 	fdisk_sector_t offset;	        /* disk sector number */
47 	unsigned char *sectorbuffer;	/* disk sector contents */
48 
49 	unsigned int changed : 1,
50 		     private_sectorbuffer : 1;
51 };
52 
53 /*
54  * in-memory fdisk GPT stuff
55  */
56 struct fdisk_dos_label {
57 	struct fdisk_label	head;		/* generic part */
58 
59 	struct pte	ptes[MAXIMUM_PARTS];	/* partition */
60 	fdisk_sector_t	ext_offset;		/* start of the ext.partition */
61 	size_t		ext_index;		/* ext.partition index (if ext_offset is set) */
62 	unsigned int	compatible : 1,		/* is DOS compatible? */
63 			non_pt_changed : 1;	/* MBR, but no PT changed */
64 };
65 
66 /*
67  * Partition types
68  */
69 static struct fdisk_parttype dos_parttypes[] = {
70 	#include "pt-mbr-partnames.h"
71 };
72 
73 static const struct fdisk_shortcut dos_parttype_cuts[] =
74 {
75 	{ .shortcut = "L", .alias = "linux",    .data = "83" },
76 	{ .shortcut = "S", .alias = "swap",     .data = "82" },
77 	{ .shortcut = "E", .alias = "extended", .data = "05", .deprecated = 1 }, /* collision with 0x0e type */
78 	{ .shortcut = "Ex",.alias = "extended", .data = "05" }, /* MBR extended */
79 	{ .shortcut = "U", .alias = "uefi",     .data = "EF" }, /* UEFI system */
80 	{ .shortcut = "R", .alias = "raid",     .data = "FD" }, /* Linux RAID */
81 	{ .shortcut = "V", .alias = "lvm",      .data = "8E" }, /* LVM */
82 	{ .shortcut = "X", .alias = "linuxex",  .data = "85" }  /* Linux extended */
83 };
84 
85 #define set_hsc(h,s,c,sector) { \
86 		s = sector % cxt->geom.sectors + 1;			\
87 		sector /= cxt->geom.sectors;				\
88 		h = sector % cxt->geom.heads;				\
89 		sector /= cxt->geom.heads;				\
90 		c = sector & 0xff;					\
91 		s |= (sector >> 2) & 0xc0;				\
92 	}
93 
94 
95 #define sector(s)	((s) & 0x3f)
96 #define cylinder(s, c)	((c) | (((s) & 0xc0) << 2))
97 
98 #define alignment_required(_x)	((_x)->grain != (_x)->sector_size)
99 
100 #define is_dos_compatible(_x) \
101 		   (fdisk_is_label(_x, DOS) && \
102                     fdisk_dos_is_compatible(fdisk_get_label(_x, NULL)))
103 
104 #define cround(c, n)	fdisk_cround(c, n)
105 
106 
self_label(struct fdisk_context * cxt)107 static inline struct fdisk_dos_label *self_label(struct fdisk_context *cxt)
108 {
109 	assert(cxt);
110 	assert(cxt->label);
111 	assert(fdisk_is_label(cxt, DOS));
112 
113 	return (struct fdisk_dos_label *) cxt->label;
114 }
115 
self_pte(struct fdisk_context * cxt,size_t i)116 static inline struct pte *self_pte(struct fdisk_context *cxt, size_t i)
117 {
118 	struct fdisk_dos_label *l = self_label(cxt);
119 
120 	if (i >= ARRAY_SIZE(l->ptes))
121 		return NULL;
122 
123 	return &l->ptes[i];
124 }
125 
self_partition(struct fdisk_context * cxt,size_t i)126 static inline struct dos_partition *self_partition(
127 				struct fdisk_context *cxt,
128 				size_t i)
129 {
130 	struct pte *pe = self_pte(cxt, i);
131 	return pe ? pe->pt_entry : NULL;
132 }
133 
fdisk_dos_get_partition(struct fdisk_context * cxt,size_t i)134 struct dos_partition *fdisk_dos_get_partition(
135 				struct fdisk_context *cxt,
136 				size_t i)
137 {
138 	assert(cxt);
139 	assert(cxt->label);
140 	assert(fdisk_is_label(cxt, DOS));
141 
142 	return self_partition(cxt, i);
143 }
144 
dos_partition_parttype(struct fdisk_context * cxt,struct dos_partition * p)145 static struct fdisk_parttype *dos_partition_parttype(
146 		struct fdisk_context *cxt,
147 		struct dos_partition *p)
148 {
149 	struct fdisk_parttype *t
150 		= fdisk_label_get_parttype_from_code(cxt->label, p->sys_ind);
151 	return t ? : fdisk_new_unknown_parttype(p->sys_ind, NULL);
152 }
153 
154 /*
155  * Linux kernel cares about partition size only. Things like
156  * partition type or so are completely irrelevant -- kzak Nov-2013
157  */
is_used_partition(struct dos_partition * p)158 static int is_used_partition(struct dos_partition *p)
159 {
160 	return p && dos_partition_get_size(p) != 0;
161 }
162 
partition_set_changed(struct fdisk_context * cxt,size_t i,int changed)163 static void partition_set_changed(
164 				struct fdisk_context *cxt,
165 				size_t i,
166 				int changed)
167 {
168 	struct pte *pe = self_pte(cxt, i);
169 
170 	if (!pe)
171 		return;
172 
173 	DBG(LABEL, ul_debug("DOS: setting %zu partition to %s", i,
174 				changed ? "changed" : "unchanged"));
175 
176 	pe->changed = changed ? 1 : 0;
177 	if (changed)
178 		fdisk_label_set_changed(cxt->label, 1);
179 }
180 
get_abs_partition_start(struct pte * pe)181 static fdisk_sector_t get_abs_partition_start(struct pte *pe)
182 {
183 	assert(pe);
184 	assert(pe->pt_entry);
185 
186 	return pe->offset + dos_partition_get_start(pe->pt_entry);
187 }
188 
get_abs_partition_end(struct pte * pe)189 static fdisk_sector_t get_abs_partition_end(struct pte *pe)
190 {
191 	fdisk_sector_t size;
192 
193 	assert(pe);
194 	assert(pe->pt_entry);
195 
196 	size = dos_partition_get_size(pe->pt_entry);
197 	return get_abs_partition_start(pe) + size - (size ? 1 : 0);
198 }
199 
is_cleared_partition(struct dos_partition * p)200 static int is_cleared_partition(struct dos_partition *p)
201 {
202 	return !(!p || p->boot_ind || p->bh || p->bs || p->bc ||
203 		 p->sys_ind || p->eh || p->es || p->ec ||
204 		 dos_partition_get_start(p) || dos_partition_get_size(p));
205 }
206 
get_partition_unused_primary(struct fdisk_context * cxt,struct fdisk_partition * pa,size_t * partno)207 static int get_partition_unused_primary(struct fdisk_context *cxt,
208 					struct fdisk_partition *pa,
209 					size_t *partno)
210 {
211 	size_t org, n;
212 	int rc;
213 
214 	assert(cxt);
215 	assert(cxt->label);
216 	assert(partno);
217 
218 	org = cxt->label->nparts_max;
219 
220 	cxt->label->nparts_max = 4;
221 	rc = fdisk_partition_next_partno(pa, cxt, &n);
222 	cxt->label->nparts_max = org;
223 
224 	if (rc == 1) {
225 		fdisk_info(cxt, _("All primary partitions have been defined already."));
226 		rc = -1;
227 	} else if (rc == -ERANGE) {
228 		fdisk_warnx(cxt, _("Primary partition not available."));
229 	} else if (rc == 0)
230 		*partno = n;
231 
232 	return rc;
233 }
234 
seek_sector(struct fdisk_context * cxt,fdisk_sector_t secno)235 static int seek_sector(struct fdisk_context *cxt, fdisk_sector_t secno)
236 {
237 	off_t offset = (off_t) secno * cxt->sector_size;
238 
239 	return lseek(cxt->dev_fd, offset, SEEK_SET) == (off_t) -1 ? -errno : 0;
240 }
241 
read_sector(struct fdisk_context * cxt,fdisk_sector_t secno,unsigned char * buf)242 static int read_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
243 			unsigned char *buf)
244 {
245 	int rc = seek_sector(cxt, secno);
246 	ssize_t r;
247 
248 	if (rc < 0)
249 		return rc;
250 
251 	r = read(cxt->dev_fd, buf, cxt->sector_size);
252 	if (r == (ssize_t) cxt->sector_size)
253 		return 0;
254 	if (r < 0)
255 		return -errno;
256 	return -1;
257 }
258 
259 /* Allocate a buffer and read a partition table sector */
read_pte(struct fdisk_context * cxt,size_t pno,fdisk_sector_t offset)260 static int read_pte(struct fdisk_context *cxt, size_t pno, fdisk_sector_t offset)
261 {
262 	int rc;
263 	unsigned char *buf;
264 	struct pte *pe = self_pte(cxt, pno);
265 
266 	if (!pe)
267 		return -EINVAL;
268 
269 	buf = calloc(1, cxt->sector_size);
270 	if (!buf)
271 		return -ENOMEM;
272 
273 	DBG(LABEL, ul_debug("DOS: reading EBR %zu: offset=%ju, buffer=%p",
274 				pno, (uintmax_t) offset, buf));
275 
276 	pe->offset = offset;
277 	pe->sectorbuffer = buf;
278 	pe->private_sectorbuffer = 1;
279 
280 	rc = read_sector(cxt, offset, pe->sectorbuffer);
281 	if (rc) {
282 		fdisk_warn(cxt, _("Failed to read extended partition table "
283 				"(offset=%ju)"), (uintmax_t) offset);
284 		return rc;
285 	}
286 
287 	pe->changed = 0;
288 	pe->pt_entry = pe->ex_entry = NULL;
289 	return 0;
290 }
291 
292 
clear_partition(struct dos_partition * p)293 static void clear_partition(struct dos_partition *p)
294 {
295 	if (!p)
296 		return;
297 	p->boot_ind = 0;
298 	p->bh = 0;
299 	p->bs = 0;
300 	p->bc = 0;
301 	p->sys_ind = 0;
302 	p->eh = 0;
303 	p->es = 0;
304 	p->ec = 0;
305 	dos_partition_set_start(p,0);
306 	dos_partition_set_size(p,0);
307 }
308 
dos_init(struct fdisk_context * cxt)309 static void dos_init(struct fdisk_context *cxt)
310 {
311 	struct fdisk_dos_label *l = self_label(cxt);
312 	size_t i;
313 
314 	assert(cxt);
315 	assert(cxt->label);
316 	assert(fdisk_is_label(cxt, DOS));
317 
318 	DBG(LABEL, ul_debug("DOS: initialize, first sector buffer %p", cxt->firstsector));
319 
320 	cxt->label->nparts_max = 4;	/* default, unlimited number of logical */
321 
322 	l->ext_index = 0;
323 	l->ext_offset = 0;
324 	l->non_pt_changed = 0;
325 
326 	memset(l->ptes, 0, sizeof(l->ptes));
327 
328 	for (i = 0; i < 4; i++) {
329 		struct pte *pe = self_pte(cxt, i);
330 
331 		assert(pe);
332 		pe->pt_entry = mbr_get_partition(cxt->firstsector, i);
333 		pe->ex_entry = NULL;
334 		pe->offset = 0;
335 		pe->sectorbuffer = cxt->firstsector;
336 		pe->private_sectorbuffer = 0;
337 		pe->changed = 0;
338 
339 		DBG(LABEL, ul_debug("DOS: initialize: #%zu start=%u size=%u sysid=%02x",
340 					i + 1,
341 					dos_partition_get_start(pe->pt_entry),
342 					dos_partition_get_size(pe->pt_entry),
343 					pe->pt_entry->sys_ind));
344 	}
345 
346 	if (fdisk_is_listonly(cxt))
347 		return;
348 	/*
349 	 * Various warnings...
350 	 */
351 	if (fdisk_missing_geometry(cxt))
352 		fdisk_warnx(cxt, _("You can set geometry from the extra functions menu."));
353 
354 	if (is_dos_compatible(cxt)) {
355 		fdisk_warnx(cxt, _("DOS-compatible mode is deprecated."));
356 
357 		if (cxt->sector_size != cxt->phy_sector_size)
358 			fdisk_info(cxt, _(
359 		"The device presents a logical sector size that is smaller than "
360 		"the physical sector size. Aligning to a physical sector (or optimal "
361 		"I/O) size boundary is recommended, or performance may be impacted."));
362 	}
363 
364 	if (fdisk_use_cylinders(cxt))
365 		fdisk_warnx(cxt, _("Cylinders as display units are deprecated."));
366 
367 	if (cxt->total_sectors > UINT_MAX) {
368 		uint64_t bytes = cxt->total_sectors * cxt->sector_size;
369 		char *szstr = size_to_human_string(SIZE_SUFFIX_SPACE
370 					   | SIZE_SUFFIX_3LETTER, bytes);
371 		fdisk_warnx(cxt,
372 		_("The size of this disk is %s (%ju bytes). DOS "
373 		  "partition table format cannot be used on drives for "
374 		  "volumes larger than %lu bytes for %lu-byte "
375 		  "sectors. Use GUID partition table format (GPT)."),
376 			szstr, bytes,
377 			UINT_MAX * cxt->sector_size,
378 			cxt->sector_size);
379 		free(szstr);
380 	}
381 }
382 
383 /* callback called by libfdisk */
dos_deinit(struct fdisk_label * lb)384 static void dos_deinit(struct fdisk_label *lb)
385 {
386 	size_t i;
387 	struct fdisk_dos_label *l = (struct fdisk_dos_label *) lb;
388 
389 	for (i = 0; i < ARRAY_SIZE(l->ptes); i++) {
390 		struct pte *pe = &l->ptes[i];
391 
392 		if (pe->private_sectorbuffer && pe->sectorbuffer) {
393 			DBG(LABEL, ul_debug("DOS: freeing pte %zu sector buffer %p",
394 						i, pe->sectorbuffer));
395 			free(pe->sectorbuffer);
396 		}
397 		pe->sectorbuffer = NULL;
398 		pe->private_sectorbuffer = 0;
399 	}
400 
401 	memset(l->ptes, 0, sizeof(l->ptes));
402 }
403 
reset_pte(struct pte * pe)404 static void reset_pte(struct pte *pe)
405 {
406 	assert(pe);
407 
408 	if (pe->private_sectorbuffer) {
409 		DBG(LABEL, ul_debug("   --> freeing pte sector buffer %p",
410 					pe->sectorbuffer));
411 		free(pe->sectorbuffer);
412 	}
413 	memset(pe, 0, sizeof(struct pte));
414 }
415 
delete_partition(struct fdisk_context * cxt,size_t partnum)416 static int delete_partition(struct fdisk_context *cxt, size_t partnum)
417 {
418 	struct fdisk_dos_label *l;
419 	struct pte *pe;
420 	struct dos_partition *p;
421 	struct dos_partition *q;
422 
423 	assert(cxt);
424 	assert(cxt->label);
425 	assert(fdisk_is_label(cxt, DOS));
426 
427 	pe = self_pte(cxt, partnum);
428 	if (!pe)
429 		return -EINVAL;
430 
431 	DBG(LABEL, ul_debug("DOS: delete partition %zu (max=%zu)", partnum,
432 				cxt->label->nparts_max));
433 
434 	l = self_label(cxt);
435 	p = pe->pt_entry;
436 	q = pe->ex_entry;
437 
438 	/* Note that for the fifth partition (partnum == 4) we don't actually
439 	   decrement partitions. */
440 	if (partnum < 4) {
441 		DBG(LABEL, ul_debug("--> delete primary"));
442 		if (IS_EXTENDED(p->sys_ind) && partnum == l->ext_index) {
443 			size_t i;
444 			DBG(LABEL, ul_debug(" --> delete extended"));
445 			for (i = 4; i < cxt->label->nparts_max; i++) {
446 				DBG(LABEL, ul_debug("  --> delete logical #%zu", i));
447 				reset_pte(&l->ptes[i]);
448 
449 			}
450 			cxt->label->nparts_max = 4;
451 			l->ptes[l->ext_index].ex_entry = NULL;
452 			l->ext_offset = 0;
453 			l->ext_index = 0;
454 		}
455 		partition_set_changed(cxt, partnum, 1);
456 		clear_partition(p);
457 	} else if (!q->sys_ind && partnum > 4) {
458 		DBG(LABEL, ul_debug("--> delete logical [last in the chain]"));
459 		reset_pte(&l->ptes[partnum]);
460 		--cxt->label->nparts_max;
461 		--partnum;
462 		/* clear link to deleted partition */
463 		clear_partition(l->ptes[partnum].ex_entry);
464 		partition_set_changed(cxt, partnum, 1);
465 	} else {
466 		DBG(LABEL, ul_debug("--> delete logical [move down]"));
467 		if (partnum > 4) {
468 			DBG(LABEL, ul_debug(" --> delete %zu logical link", partnum));
469 			p = l->ptes[partnum - 1].ex_entry;
470 			*p = *q;
471 			dos_partition_set_start(p, dos_partition_get_start(q));
472 			dos_partition_set_size(p, dos_partition_get_size(q));
473 			partition_set_changed(cxt, partnum - 1, 1);
474 
475 		} else if (cxt->label->nparts_max > 5) {
476 			DBG(LABEL, ul_debug(" --> delete first logical link"));
477 			pe = &l->ptes[5];	/* second logical */
478 
479 			if (pe->pt_entry)	/* prevent SEGFAULT */
480 				dos_partition_set_start(pe->pt_entry,
481 					       get_abs_partition_start(pe) -
482 					       l->ext_offset);
483 			pe->offset = l->ext_offset;
484 			partition_set_changed(cxt, 5, 1);
485 		}
486 
487 		if (cxt->label->nparts_max > 5) {
488 			DBG(LABEL, ul_debug(" --> move ptes"));
489 			cxt->label->nparts_max--;
490 			reset_pte(&l->ptes[partnum]);
491 			while (partnum < cxt->label->nparts_max) {
492 				DBG(LABEL, ul_debug("  --> moving pte %zu <-- %zu", partnum, partnum + 1));
493 				l->ptes[partnum] = l->ptes[partnum + 1];
494 				partnum++;
495 			}
496 			memset(&l->ptes[partnum], 0, sizeof(struct pte));
497 		} else {
498 			DBG(LABEL, ul_debug(" --> the only logical: clear only"));
499 			clear_partition(l->ptes[partnum].pt_entry);
500 			cxt->label->nparts_max--;
501 
502 			if (partnum == 4) {
503 				DBG(LABEL, ul_debug("  --> clear last logical"));
504 				reset_pte(&l->ptes[partnum]);
505 				partition_set_changed(cxt, l->ext_index, 1);
506 			}
507 		}
508 	}
509 
510 	fdisk_label_set_changed(cxt->label, 1);
511 	return 0;
512 }
513 
dos_delete_partition(struct fdisk_context * cxt,size_t partnum)514 static int dos_delete_partition(struct fdisk_context *cxt, size_t partnum)
515 {
516 	struct pte *pe;
517 
518 	assert(cxt);
519 	assert(cxt->label);
520 	assert(fdisk_is_label(cxt, DOS));
521 
522 	pe = self_pte(cxt, partnum);
523 	if (!pe || !is_used_partition(pe->pt_entry))
524 		return -EINVAL;
525 
526 	return delete_partition(cxt, partnum);
527 }
528 
read_extended(struct fdisk_context * cxt,size_t ext)529 static void read_extended(struct fdisk_context *cxt, size_t ext)
530 {
531 	size_t i;
532 	struct pte *pex, *pe;
533 	struct dos_partition *p, *q;
534 	struct fdisk_dos_label *l = self_label(cxt);
535 
536 	l->ext_index = ext;
537 	pex = self_pte(cxt, ext);
538 	if (!pex) {
539 		DBG(LABEL, ul_debug("DOS: uninitialized pointer to %zu pex", ext));
540 		return;
541 	}
542 	pex->ex_entry = pex->pt_entry;
543 
544 	p = pex->pt_entry;
545 	if (!dos_partition_get_start(p)) {
546 		fdisk_warnx(cxt, _("Bad offset in primary extended partition."));
547 		return;
548 	}
549 
550 	DBG(LABEL, ul_debug("DOS: Reading extended %zu", ext));
551 
552 	while (IS_EXTENDED (p->sys_ind)) {
553 		if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
554 			/* This is not a Linux restriction, but
555 			   this program uses arrays of size MAXIMUM_PARTS.
556 			   Do not try to `improve' this test. */
557 			struct pte *pre = self_pte(cxt,
558 						cxt->label->nparts_max - 1);
559 			fdisk_warnx(cxt,
560 			_("Omitting partitions after #%zu. They will be deleted "
561 			  "if you save this partition table."),
562 				cxt->label->nparts_max);
563 
564 			if (pre) {
565 				clear_partition(pre->ex_entry);
566 				partition_set_changed(cxt,
567 						cxt->label->nparts_max - 1, 1);
568 			}
569 			return;
570 		}
571 
572 		pe = self_pte(cxt, cxt->label->nparts_max);
573 		if (!pe)
574 			return;
575 
576 		if (read_pte(cxt, cxt->label->nparts_max, l->ext_offset +
577 						dos_partition_get_start(p)))
578 			return;
579 
580 		if (!l->ext_offset)
581 			l->ext_offset = dos_partition_get_start(p);
582 
583 		assert(pe->sectorbuffer);
584 		q = p = mbr_get_partition(pe->sectorbuffer, 0);
585 
586 		for (i = 0; i < 4; i++, p++) {
587 			if (!dos_partition_get_size(p))
588 				continue;
589 
590 			if (IS_EXTENDED (p->sys_ind)) {
591 				if (pe->ex_entry)
592 					fdisk_warnx(cxt, _(
593 					"Extra link pointer in partition "
594 					"table %zu."),
595 						cxt->label->nparts_max + 1);
596 				else
597 					pe->ex_entry = p;
598 			} else if (p->sys_ind) {
599 				if (pe->pt_entry)
600 					fdisk_warnx(cxt, _(
601 					"Ignoring extra data in partition "
602 					"table %zu."),
603 						cxt->label->nparts_max + 1);
604 				else
605 					pe->pt_entry = p;
606 			}
607 		}
608 
609 		/* very strange code here... */
610 		if (!pe->pt_entry) {
611 			if (q != pe->ex_entry)
612 				pe->pt_entry = q;
613 			else
614 				pe->pt_entry = q + 1;
615 		}
616 		if (!pe->ex_entry) {
617 			if (q != pe->pt_entry)
618 				pe->ex_entry = q;
619 			else
620 				pe->ex_entry = q + 1;
621 		}
622 
623 		p = pe->ex_entry;
624 		cxt->label->nparts_cur = ++cxt->label->nparts_max;
625 
626 		DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: link: type=%x,  start=%u, size=%u; "
627 				                         " data: type=%x, start=%u, size=%u",
628 				    (uintmax_t) pe->offset,
629 				    pe->ex_entry->sys_ind,
630 				    dos_partition_get_start(pe->ex_entry),
631 				    dos_partition_get_size(pe->ex_entry),
632 				    pe->pt_entry->sys_ind,
633 				    dos_partition_get_start(pe->pt_entry),
634 				    dos_partition_get_size(pe->pt_entry)));
635 
636 	}
637 
638 	/* remove last empty EBR */
639 	pe = self_pte(cxt, cxt->label->nparts_max - 1);
640 	if (pe &&
641 	    is_cleared_partition(pe->ex_entry) &&
642 	    is_cleared_partition(pe->pt_entry)) {
643 		DBG(LABEL, ul_debug("DOS: EBR[offset=%ju]: empty, remove", (uintmax_t) pe->offset));
644 		reset_pte(pe);
645 		cxt->label->nparts_max--;
646 		cxt->label->nparts_cur--;
647 	}
648 
649 	/* remove empty links */
650  remove:
651 	q = self_partition(cxt, 4);
652 	for (i = 4; i < cxt->label->nparts_max; i++) {
653 		p = self_partition(cxt, i);
654 
655 		if (p && !dos_partition_get_size(p) &&
656 		    (cxt->label->nparts_max > 5 || (q && q->sys_ind))) {
657 			fdisk_info(cxt, _("omitting empty partition (%zu)"), i+1);
658 			delete_partition(cxt, i);
659 			goto remove; 	/* numbering changed */
660 		}
661 	}
662 
663 	DBG(LABEL, ul_debug("DOS: nparts_max: %zu", cxt->label->nparts_max));
664 }
665 
dos_create_disklabel(struct fdisk_context * cxt)666 static int dos_create_disklabel(struct fdisk_context *cxt)
667 {
668 	unsigned int id = 0;
669 	int rc, has_id = 0;
670 	struct fdisk_dos_label *l;
671 
672 	assert(cxt);
673 	assert(cxt->label);
674 	assert(fdisk_is_label(cxt, DOS));
675 
676 	DBG(LABEL, ul_debug("DOS: creating new disklabel"));
677 
678 	if (cxt->script) {
679 		char *end = NULL;
680 		const char *s = fdisk_script_get_header(cxt->script, "label-id");
681 
682 		if (s) {
683 			errno = 0;
684 			id = strtoul(s, &end, 16);
685 			if (!errno && end && s < end) {
686 				has_id = 1;
687 				DBG(LABEL, ul_debug("DOS: re-use ID from script (0x%08x)", id));
688 			} else
689 				DBG(LABEL, ul_debug("DOS: failed to parse label=id '%s'", s));
690 		}
691 	}
692 
693 	/* random disk signature */
694 	if (!has_id) {
695 		DBG(LABEL, ul_debug("DOS: generate new ID"));
696 		random_get_bytes(&id, sizeof(id));
697 	}
698 
699 	if (fdisk_has_protected_bootbits(cxt))
700 		rc = fdisk_init_firstsector_buffer(cxt, 0, MBR_PT_BOOTBITS_SIZE);
701 	else
702 		rc = fdisk_init_firstsector_buffer(cxt, 0, 0);
703 	if (rc)
704 		return rc;
705 	dos_init(cxt);
706 
707 	l = self_label(cxt);
708 
709 	/* Generate an MBR ID for this disk */
710 	mbr_set_id(cxt->firstsector, id);
711 	l->non_pt_changed = 1;
712 	fdisk_label_set_changed(cxt->label, 1);
713 
714 	/* Put MBR signature */
715 	mbr_set_magic(cxt->firstsector);
716 
717 	fdisk_info(cxt, _("Created a new DOS disklabel with disk "
718 			 "identifier 0x%08x."), id);
719 	return 0;
720 }
721 
dos_set_disklabel_id(struct fdisk_context * cxt,const char * str)722 static int dos_set_disklabel_id(struct fdisk_context *cxt, const char *str)
723 {
724 	char *buf = NULL;
725 	unsigned int id, old;
726 	struct fdisk_dos_label *l;
727 	int rc = 0;
728 
729 	assert(cxt);
730 	assert(cxt->label);
731 	assert(fdisk_is_label(cxt, DOS));
732 
733 	DBG(LABEL, ul_debug("DOS: setting Id"));
734 
735 	l = self_label(cxt);
736 	old = mbr_get_id(cxt->firstsector);
737 
738 	if (!str) {
739 		rc = fdisk_ask_string(cxt,
740 			_("Enter the new disk identifier"), &buf);
741 		str = buf;
742 	}
743 	if (!rc) {
744 		char *end = NULL;
745 
746 		errno = 0;
747 		id = strtoul(str, &end, 0);
748 		if (errno || str == end || (end && *end)) {
749 			fdisk_warnx(cxt, _("Incorrect value."));
750 			rc = -EINVAL;
751 		}
752 	}
753 
754 	free(buf);
755 	if (rc)
756 		return -EINVAL;
757 
758 	mbr_set_id(cxt->firstsector, id);
759 	l->non_pt_changed = 1;
760 	fdisk_label_set_changed(cxt->label, 1);
761 
762 	fdisk_info(cxt, _("Disk identifier changed from 0x%08x to 0x%08x."),
763 			old, id);
764 	return 0;
765 }
766 
get_partition_table_geometry(struct fdisk_context * cxt,unsigned int * ph,unsigned int * ps)767 static void get_partition_table_geometry(struct fdisk_context *cxt,
768 			unsigned int *ph, unsigned int *ps)
769 {
770 	unsigned char *bufp = cxt->firstsector;
771 	struct dos_partition *p;
772 	int i, h, s, hh, ss;
773 	int first = 1;
774 	int bad = 0;
775 
776 	hh = ss = 0;
777 	for (i = 0; i < 4; i++) {
778 		p = mbr_get_partition(bufp, i);
779 		if (p->sys_ind != 0) {
780 			h = p->eh + 1;
781 			s = (p->es & 077);
782 			if (first) {
783 				hh = h;
784 				ss = s;
785 				first = 0;
786 			} else if (hh != h || ss != s)
787 				bad = 1;
788 		}
789 	}
790 
791 	if (!first && !bad) {
792 		*ph = hh;
793 		*ps = ss;
794 	}
795 
796 	DBG(LABEL, ul_debug("DOS PT geometry: heads=%u, sectors=%u", *ph, *ps));
797 }
798 
dos_reset_alignment(struct fdisk_context * cxt)799 static int dos_reset_alignment(struct fdisk_context *cxt)
800 {
801 	assert(cxt);
802 	assert(cxt->label);
803 	assert(fdisk_is_label(cxt, DOS));
804 
805 	/* overwrite necessary stuff by DOS deprecated stuff */
806 	if (is_dos_compatible(cxt)) {
807 		DBG(LABEL, ul_debug("DOS: resetting alignment for DOS-compatible PT"));
808 		if (cxt->geom.sectors)
809 			cxt->first_lba = cxt->geom.sectors;	/* usually 63 */
810 
811 		cxt->grain = cxt->sector_size;			/* usually 512 */
812 	}
813 
814 	return 0;
815 }
816 
817 /* TODO: move to include/pt-dos.h and share with libblkid */
818 #define AIX_MAGIC_STRING	"\xC9\xC2\xD4\xC1"
819 #define AIX_MAGIC_STRLEN	(sizeof(AIX_MAGIC_STRING) - 1)
820 
dos_probe_label(struct fdisk_context * cxt)821 static int dos_probe_label(struct fdisk_context *cxt)
822 {
823 	size_t i;
824 	unsigned int h = 0, s = 0;
825 
826 	assert(cxt);
827 	assert(cxt->label);
828 	assert(fdisk_is_label(cxt, DOS));
829 
830 	/* ignore disks with AIX magic number */
831 	if (memcmp(cxt->firstsector, AIX_MAGIC_STRING, AIX_MAGIC_STRLEN) == 0)
832 		return 0;
833 
834 	if (!mbr_is_valid_magic(cxt->firstsector))
835 		return 0;
836 
837 	/* ignore disks with FAT */
838 	if (cxt->collision &&
839 	    (strcmp(cxt->collision, "vfat") == 0 ||
840 	     strcmp(cxt->collision, "ntfs") == 0))
841 		return 0;
842 
843 	dos_init(cxt);
844 
845 	get_partition_table_geometry(cxt, &h, &s);
846 	if (h && s) {
847 		cxt->geom.heads = h;
848 	        cxt->geom.sectors = s;
849 
850 		if (fdisk_has_user_device_geometry(cxt))
851 			fdisk_apply_user_device_properties(cxt);
852 	}
853 
854 	for (i = 0; i < 4; i++) {
855 		struct pte *pe = self_pte(cxt, i);
856 
857 		assert(pe);
858 		if (is_used_partition(pe->pt_entry))
859 			cxt->label->nparts_cur++;
860 
861 		if (IS_EXTENDED (pe->pt_entry->sys_ind)) {
862 			if (cxt->label->nparts_max != 4)
863 				fdisk_warnx(cxt, _(
864 				"Ignoring extra extended partition %zu"),
865 					i + 1);
866 			else
867 				read_extended(cxt, i);
868 		}
869 	}
870 
871 	for (i = 3; i < cxt->label->nparts_max; i++) {
872 		struct pte *pe = self_pte(cxt, i);
873 		struct fdisk_dos_label *l = self_label(cxt);
874 
875 		assert(pe);
876 		if (!mbr_is_valid_magic(pe->sectorbuffer)) {
877 			fdisk_info(cxt, _(
878 			"Invalid flag 0x%02x%02x of EBR (for partition %zu) will "
879 			"be corrected by w(rite)."),
880 				pe->sectorbuffer[510],
881 				pe->sectorbuffer[511],
882 				i + 1);
883 			partition_set_changed(cxt, i, 1);
884 
885 			/* mark also extended as changed to update the first EBR
886 			 * in situation that there is no logical partitions at all */
887 			partition_set_changed(cxt, l->ext_index, 1);
888 		}
889 	}
890 
891 	return 1;
892 }
893 
set_partition(struct fdisk_context * cxt,int i,int doext,fdisk_sector_t start,fdisk_sector_t stop,int sysid,int boot)894 static void set_partition(struct fdisk_context *cxt,
895 			  int i, int doext, fdisk_sector_t start,
896 			  fdisk_sector_t stop, int sysid, int boot)
897 {
898 	struct pte *pe = self_pte(cxt, i);
899 	struct dos_partition *p;
900 	fdisk_sector_t offset;
901 
902 	assert(!FDISK_IS_UNDEF(start));
903 	assert(!FDISK_IS_UNDEF(stop));
904 	assert(pe);
905 
906 	if (doext) {
907 		struct fdisk_dos_label *l = self_label(cxt);
908 		p = pe->ex_entry;
909 		offset = l->ext_offset;
910 	} else {
911 		p = pe->pt_entry;
912 		offset = pe->offset;
913 	}
914 
915 	DBG(LABEL, ul_debug("DOS: setting partition %d%s, offset=%zu, start=%zu, size=%zu, sysid=%02x",
916 				i, doext ? " [extended]" : "",
917 				(size_t) offset,
918 				(size_t) (start -  offset),
919 				(size_t) (stop - start + 1),
920 				sysid));
921 
922 	p->boot_ind = boot ? ACTIVE_FLAG : 0;
923 	p->sys_ind = sysid;
924 	dos_partition_set_start(p, start - offset);
925 	dos_partition_set_size(p, stop - start + 1);
926 
927 	if (is_dos_compatible(cxt) && (start/(cxt->geom.sectors*cxt->geom.heads) > 1023))
928 		start = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
929 	set_hsc(p->bh, p->bs, p->bc, start);
930 	if (is_dos_compatible(cxt) && (stop/(cxt->geom.sectors*cxt->geom.heads) > 1023))
931 		stop = cxt->geom.heads*cxt->geom.sectors*1024 - 1;
932 	set_hsc(p->eh, p->es, p->ec, stop);
933 	partition_set_changed(cxt, i, 1);
934 }
935 
936 
get_start_from_user(struct fdisk_context * cxt,fdisk_sector_t * start,fdisk_sector_t low,fdisk_sector_t dflt,fdisk_sector_t limit,struct fdisk_partition * pa)937 static int get_start_from_user(	struct fdisk_context *cxt,
938 				fdisk_sector_t *start,
939 				fdisk_sector_t low,
940 				fdisk_sector_t dflt,
941 				fdisk_sector_t limit,
942 				struct fdisk_partition *pa)
943 {
944 	assert(start);
945 
946 	/* try to use template from 'pa' */
947 	if (pa && pa->start_follow_default)
948 		*start = dflt;
949 
950 	else if (pa && fdisk_partition_has_start(pa)) {
951 		DBG(LABEL, ul_debug("DOS: start: wanted=%ju, low=%ju, limit=%ju",
952 				(uintmax_t) pa->start, (uintmax_t) low, (uintmax_t) limit));
953 		*start = pa->start;
954 		if (*start < low || *start > limit) {
955 			fdisk_warnx(cxt, _("Start sector %ju out of range."),
956 					(uintmax_t) *start);
957 			return -ERANGE;
958 		}
959 	} else {
960 		/* ask user by dialog */
961 		struct fdisk_ask *ask = fdisk_new_ask();
962 		int rc;
963 
964 		if (!ask)
965 			return -ENOMEM;
966 		fdisk_ask_set_query(ask,
967 			fdisk_use_cylinders(cxt) ?
968 				_("First cylinder") : _("First sector"));
969 		fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
970 		fdisk_ask_number_set_low(ask, fdisk_cround(cxt, low));
971 		fdisk_ask_number_set_default(ask, fdisk_cround(cxt, dflt));
972 		fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
973 
974 		rc = fdisk_do_ask(cxt, ask);
975 		*start = fdisk_ask_number_get_result(ask);
976 		fdisk_unref_ask(ask);
977 		if (rc)
978 			return rc;
979 		if (fdisk_use_cylinders(cxt)) {
980 		        *start = (*start - 1)
981 				* fdisk_get_units_per_sector(cxt);
982 			if (*start < low)
983 				*start = low;
984 		}
985 	}
986 
987 	DBG(LABEL, ul_debug("DOS: start is %ju", (uintmax_t) *start));
988 	return 0;
989 }
990 
991 /* Returns last available sector in the free space pointed to by start. */
find_last_free(struct fdisk_context * cxt,int logical,fdisk_sector_t begin,fdisk_sector_t stop,fdisk_sector_t * result)992 static int find_last_free(
993 			struct fdisk_context *cxt,
994 			int logical,
995 			fdisk_sector_t begin,
996 			fdisk_sector_t stop,
997 			fdisk_sector_t *result)
998 {
999 	fdisk_sector_t last = stop;
1000 
1001 	size_t i = logical ? 4 : 0;
1002 
1003 	for ( ; i < cxt->label->nparts_max; i++) {
1004 		struct pte *pe = self_pte(cxt, i);
1005 
1006 		assert(pe);
1007 		fdisk_sector_t p_start = get_abs_partition_start(pe);
1008 		fdisk_sector_t p_end = get_abs_partition_end(pe);
1009 
1010 		if (is_cleared_partition(pe->pt_entry))
1011 			continue;
1012 		/* count EBR and begin of the logical partition as used area */
1013 		if (pe->offset)
1014 			p_start -= cxt->first_lba;
1015 
1016 		if ((p_start >= begin && p_start <= last) ||
1017 		    (p_end >= begin && p_end <= last)) {
1018 			last = p_start - 1;
1019 		}
1020 		if (last < begin) {
1021 			DBG(LABEL, ul_debug("no free space <%ju,%ju>",
1022 					(uintmax_t) begin, (uintmax_t) stop));
1023 			return -ENOSPC;
1024 		}
1025 	}
1026 
1027 	if (last == begin)
1028 		last = stop;
1029 
1030 	DBG(LABEL, ul_debug("DOS: last free sector  <%ju,%ju>: %ju",
1031 			(uintmax_t) begin, (uintmax_t) stop, (uintmax_t) last));
1032 
1033 	*result = last;
1034 	return 0;
1035 }
1036 
find_last_free_sector_in_range(struct fdisk_context * cxt,int logical,fdisk_sector_t begin,fdisk_sector_t end,fdisk_sector_t * result)1037 static int find_last_free_sector_in_range(
1038 			struct fdisk_context *cxt,
1039 			int logical,
1040 			fdisk_sector_t begin,
1041 			fdisk_sector_t end,
1042 			fdisk_sector_t *result)
1043 {
1044 	int last_moved;
1045 	fdisk_sector_t last = end;
1046 
1047 	do {
1048 		size_t i = logical ? 4 : 0;
1049 
1050 		last_moved = 0;
1051 		for ( ; i < cxt->label->nparts_max; i++) {
1052 			struct pte *pe = self_pte(cxt, i);
1053 
1054 			assert(pe);
1055 			fdisk_sector_t p_start = get_abs_partition_start(pe);
1056 			fdisk_sector_t p_end = get_abs_partition_end(pe);
1057 
1058 			if (is_cleared_partition(pe->pt_entry))
1059 				continue;
1060 
1061 			/* count EBR and begin of the logical partition as used area */
1062 			if (pe->offset)
1063 				p_start -= cxt->first_lba;
1064 
1065 			if (last >= p_start && last <= p_end) {
1066 				last = p_start - 1;
1067 				last_moved = 1;
1068 
1069 				if (last < begin) {
1070 					DBG(LABEL, ul_debug("DOS: last free out of range <%ju,%ju>: %ju",
1071 						(uintmax_t) begin, (uintmax_t) end, (uintmax_t) last));
1072 
1073 					return -ENOSPC;
1074 				}
1075 			}
1076 		}
1077 	} while (last_moved == 1);
1078 
1079 	DBG(LABEL, ul_debug("DOS: last unused sector in range <%ju,%ju>: %ju",
1080 			(uintmax_t) begin, (uintmax_t) end, (uintmax_t) last));
1081 
1082 	*result = last;
1083 	return 0;
1084 }
1085 
find_first_free_sector_in_range(struct fdisk_context * cxt,int logical,fdisk_sector_t begin,fdisk_sector_t end,fdisk_sector_t * result)1086 static int find_first_free_sector_in_range(
1087 			struct fdisk_context *cxt,
1088 			int logical,
1089 			fdisk_sector_t begin,
1090 			fdisk_sector_t end,
1091 			fdisk_sector_t *result)
1092 {
1093 	int first_moved = 0;
1094 	fdisk_sector_t first = begin;
1095 
1096 	do {
1097 		size_t i = logical ? 4 : 0;
1098 
1099 		first_moved = 0;
1100 		for (; i < cxt->label->nparts_max; i++) {
1101 			struct pte *pe = self_pte(cxt, i);
1102 
1103 			assert(pe);
1104 			fdisk_sector_t p_start = get_abs_partition_start(pe);
1105 			fdisk_sector_t p_end = get_abs_partition_end(pe);
1106 
1107 			if (is_cleared_partition(pe->pt_entry))
1108 				continue;
1109 			/* count EBR and begin of the logical partition as used area */
1110 			if (pe->offset)
1111 				p_start -= cxt->first_lba;
1112 			if (first < p_start)
1113 				continue;
1114 			if (first <= p_end) {
1115 				first = p_end + 1 + (logical ? cxt->first_lba : 0);
1116 				first_moved = 1;
1117 
1118 				if (first > end) {
1119 					DBG(LABEL, ul_debug("DOS: first free out of range <%ju,%ju>: %ju",
1120 						(uintmax_t) begin, (uintmax_t) end, (uintmax_t) first));
1121 					return -ENOSPC;
1122 				}
1123 			}
1124 		}
1125 	} while (first_moved == 1);
1126 
1127 	DBG(LABEL, ul_debug("DOS: first unused sector in range <%ju,%ju>: %ju",
1128 			(uintmax_t) begin, (uintmax_t) end, (uintmax_t) first));
1129 	*result = first;
1130 	return 0;
1131 }
1132 
get_disk_ranges(struct fdisk_context * cxt,int logical,fdisk_sector_t * first,fdisk_sector_t * last)1133 static int get_disk_ranges(struct fdisk_context *cxt, int logical,
1134 			   fdisk_sector_t *first, fdisk_sector_t *last)
1135 {
1136 	if (logical) {
1137 		/* logical partitions */
1138 		struct fdisk_dos_label *l = self_label(cxt);
1139 		struct pte *ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1140 
1141 		if (!ext_pe)
1142 			return -EINVAL;
1143 
1144 		*first = l->ext_offset + cxt->first_lba;
1145 		*last = get_abs_partition_end(ext_pe);
1146 
1147 	} else {
1148 		/* primary partitions */
1149 		if (fdisk_use_cylinders(cxt) || !cxt->total_sectors)
1150 			*last = cxt->geom.heads * cxt->geom.sectors * cxt->geom.cylinders - 1;
1151 		else
1152 			*last = cxt->total_sectors - 1;
1153 
1154 		if (*last > UINT_MAX)
1155 			*last = UINT_MAX;
1156 		*first = cxt->first_lba;
1157 	}
1158 
1159 	return 0;
1160 }
1161 
1162 /* first free sector on disk */
find_first_free_sector(struct fdisk_context * cxt,int logical,fdisk_sector_t start,fdisk_sector_t * result)1163 static int find_first_free_sector(struct fdisk_context *cxt,
1164 				int logical,
1165 				fdisk_sector_t start,
1166 				fdisk_sector_t *result)
1167 {
1168 	fdisk_sector_t first, last;
1169 	int rc;
1170 
1171 	rc = get_disk_ranges(cxt, logical, &first, &last);
1172 	if (rc)
1173 		return rc;
1174 
1175 	return find_first_free_sector_in_range(cxt, logical, start, last, result);
1176 }
1177 
add_partition(struct fdisk_context * cxt,size_t n,struct fdisk_partition * pa)1178 static int add_partition(struct fdisk_context *cxt, size_t n,
1179 			 struct fdisk_partition *pa)
1180 {
1181 	int sys, read = 0, rc, isrel = 0, is_logical;
1182 	struct fdisk_dos_label *l = self_label(cxt);
1183 	struct dos_partition *p = self_partition(cxt, n);
1184 	struct fdisk_ask *ask = NULL;
1185 
1186 	fdisk_sector_t start, stop, limit, temp;
1187 
1188 	DBG(LABEL, ul_debug("DOS: adding partition %zu", n));
1189 
1190 	sys = pa && pa->type ? pa->type->code : MBR_LINUX_DATA_PARTITION;
1191 	is_logical = n >= 4;
1192 
1193 	if (p && is_used_partition(p)) {
1194 		fdisk_warnx(cxt, _("Partition %zu is already defined.  "
1195 			           "Delete it before re-adding it."),
1196 				n + 1);
1197 		return -EINVAL;
1198 	}
1199 
1200 	rc = get_disk_ranges(cxt, is_logical, &start, &stop);
1201 	if (rc)
1202 		return rc;
1203 
1204 	if (!is_logical && cxt->parent && fdisk_is_label(cxt->parent, GPT))
1205 		start = 1;		/* Bad boy modifies hybrid MBR */
1206 
1207 	rc = find_last_free_sector_in_range(cxt, is_logical, start, stop, &limit);
1208 	if (rc == -ENOSPC)
1209 		fdisk_warnx(cxt, _("No free sectors available."));
1210 	if (rc)
1211 		return rc;
1212 
1213 	if ((is_logical || !cxt->parent || !fdisk_is_label(cxt->parent, GPT))
1214 	    && cxt->script && pa && fdisk_partition_has_start(pa)
1215 	    && pa->start >= (is_logical ? l->ext_offset : 1)
1216 	    && pa->start < start) {
1217 		fdisk_set_first_lba(cxt, 1);
1218 
1219 		rc = get_disk_ranges(cxt, is_logical, &start, &stop);
1220 		if (rc) /* won't happen, but checking to be proper */
1221 			return rc;
1222 	}
1223 
1224 	/*
1225 	 * Ask for first sector
1226 	 */
1227 	do {
1228 		fdisk_sector_t dflt, aligned;
1229 
1230 		temp = start;
1231 
1232 		DBG(LABEL, ul_debug("DOS: >>> search for first free from %ju", start));
1233 		rc = find_first_free_sector(cxt, is_logical, start, &dflt);
1234 		if (rc == -ENOSPC)
1235 			fdisk_warnx(cxt, _("No free sectors available."));
1236 		if (rc)
1237 			return rc;
1238 		start = dflt;
1239 
1240 		if (n >= 4 && pa && fdisk_partition_has_start(pa) && cxt->script
1241 		    && cxt->first_lba > 1
1242 		    && temp == start - cxt->first_lba) {
1243 			fdisk_set_first_lba(cxt, 1);
1244 			start = pa->start;
1245 		}
1246 
1247 		/* the default sector should be aligned and unused */
1248 		do {
1249 			aligned = fdisk_align_lba_in_range(cxt, dflt, dflt, limit);
1250 			find_first_free_sector(cxt, is_logical, aligned, &dflt);
1251 		} while (dflt != aligned && dflt > aligned && dflt < limit);
1252 
1253 		if (dflt >= limit)
1254 			dflt = start;
1255 		if (start > limit)
1256 			break;
1257 		if (start >= temp + fdisk_get_units_per_sector(cxt)
1258 		    && read) {
1259 			if (!pa || !pa->start_follow_default)
1260 				fdisk_info(cxt, _("Sector %llu is already allocated."), temp);
1261 			temp = start;
1262 			read = 0;
1263 			if (pa && fdisk_partition_has_start(pa))
1264 				break;
1265 		}
1266 
1267 		if (!read && start == temp) {
1268 			rc = get_start_from_user(cxt, &start, temp, dflt, limit, pa);
1269 			if (rc)
1270 				return rc;
1271 			read = 1;
1272 		}
1273 		if (pa && fdisk_partition_has_size(pa)) {
1274 			fdisk_sector_t last;
1275 
1276 			rc = find_last_free(cxt, is_logical, start, limit, &last);
1277 
1278 			if (rc == 0 && last - start + 1 < fdisk_partition_get_size(pa)) {
1279 				DBG(LABEL, ul_debug("DOS: area <%ju,%ju> too small [wanted=%ju aval=%ju]",
1280 							(uintmax_t) start, (uintmax_t) last,
1281 							fdisk_partition_get_size(pa),
1282 							last - start));
1283 
1284 				if (fdisk_partition_has_start(pa))
1285 					rc = -ENOSPC;
1286 				else {
1287 					start = last + 1;
1288 					continue;
1289 				}
1290 			}
1291 			if (rc == -ENOSPC) {
1292 				fdisk_warnx(cxt, _("No free sectors available."));
1293 				return rc;
1294 			}
1295 		}
1296 
1297 	} while (start != temp || !read);
1298 
1299 	if (n == 4) {
1300 		/* The first EBR is stored at begin of the extended partition */
1301 		struct pte *pe = self_pte(cxt, n);
1302 
1303 		assert(pe);
1304 		pe->offset = l->ext_offset;
1305 	} else if (n > 4) {
1306 		/* The second (and another) EBR */
1307 		struct pte *pe = self_pte(cxt, n);
1308 
1309 		assert(pe);
1310 		assert(start >= cxt->first_lba);
1311 
1312 		pe->offset = start - cxt->first_lba;
1313 		DBG(LABEL, ul_debug("DOS: setting EBR offset to %ju [start=%ju]", pe->offset, start));
1314 
1315 		if (pe->offset == l->ext_offset) { /* must be corrected */
1316 			pe->offset++;
1317 			if (cxt->first_lba == 1)
1318 				start++;
1319 		}
1320 	}
1321 
1322 	rc = find_last_free(cxt, is_logical, start, limit, &stop);
1323 	if (rc == -ENOSPC)
1324 		fdisk_warnx(cxt, _("No free sectors available."));
1325 	if (rc)
1326 		return rc;
1327 	limit = stop;
1328 
1329 	/*
1330 	 * Ask for last sector
1331 	 */
1332 	if (fdisk_cround(cxt, start) == fdisk_cround(cxt, limit))
1333 		stop = limit;
1334 	else if (pa && pa->end_follow_default)
1335 		stop = limit;
1336 	else if (pa && fdisk_partition_has_size(pa)) {
1337 		stop = start + pa->size;
1338 		isrel = pa->size_explicit ? 0 : 1;
1339 		if ((!isrel || !alignment_required(cxt)) && stop > start)
1340 			stop -= 1;
1341 	} else {
1342 		/* ask user by dialog */
1343 		for (;;) {
1344 			if (!ask)
1345 				ask = fdisk_new_ask();
1346 			else
1347 				fdisk_reset_ask(ask);
1348 			if (!ask)
1349 				return -ENOMEM;
1350 			fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET);
1351 
1352 			if (fdisk_use_cylinders(cxt)) {
1353 				fdisk_ask_set_query(ask, _("Last cylinder, +/-cylinders or +/-size{K,M,G,T,P}"));
1354 				fdisk_ask_number_set_unit(ask,
1355 					     cxt->sector_size *
1356 					     fdisk_get_units_per_sector(cxt));
1357 			} else {
1358 				fdisk_ask_set_query(ask, _("Last sector, +/-sectors or +/-size{K,M,G,T,P}"));
1359 				fdisk_ask_number_set_unit(ask,cxt->sector_size);
1360 			}
1361 
1362 			fdisk_ask_number_set_low(ask, fdisk_cround(cxt, start));
1363 			fdisk_ask_number_set_default(ask, fdisk_cround(cxt, limit));
1364 			fdisk_ask_number_set_high(ask, fdisk_cround(cxt, limit));
1365 			fdisk_ask_number_set_base(ask, fdisk_cround(cxt, start));	/* base for relative input */
1366 			fdisk_ask_number_set_wrap_negative(ask, 1); /* wrap negative around high */
1367 
1368 			rc = fdisk_do_ask(cxt, ask);
1369 			if (rc)
1370 				goto done;
1371 
1372 			stop = fdisk_ask_number_get_result(ask);
1373 			isrel = fdisk_ask_number_is_relative(ask);
1374 			if (fdisk_use_cylinders(cxt)) {
1375 				stop = stop * fdisk_get_units_per_sector(cxt) - 1;
1376 				if (stop >limit)
1377 					stop = limit;
1378 			}
1379 
1380 			if (stop >= start && stop <= limit)
1381 				break;
1382 			fdisk_warnx(cxt, _("Value out of range."));
1383 		}
1384 	}
1385 
1386 	DBG(LABEL, ul_debug("DOS: raw stop: %ju [limit %ju]", (uintmax_t) stop, (uintmax_t) limit));
1387 
1388 	if (stop > limit)
1389 		stop = limit;
1390 
1391 	if (isrel && stop - start < (cxt->grain / fdisk_get_sector_size(cxt))) {
1392 		/* Don't try to be smart on very small partitions and don't align so small sizes */
1393 		isrel = 0;
1394 		DBG(LABEL, ul_debug("DOS: don't align end of tiny partition [start=%ju, stop=%ju, grain=%lu]",
1395 			    (uintmax_t)start,  (uintmax_t)stop, cxt->grain));
1396 	}
1397 
1398 	if (stop < limit && isrel && alignment_required(cxt)) {
1399 		/* the last sector has not been exactly requested (but
1400 		 * defined by +size{K,M,G} convention), so be smart and
1401 		 * align the end of the partition. The next partition
1402 		 * will start at phy.block boundary.
1403 		 */
1404 		stop = fdisk_align_lba_in_range(cxt, stop, start, limit);
1405 		if (stop > start)
1406 			stop -= 1;	/* end one sector before aligned offset */
1407 		if (stop > limit)
1408 			stop = limit;
1409 		DBG(LABEL, ul_debug("DOS: aligned stop: %ju", (uintmax_t) stop));
1410 	}
1411 
1412 	set_partition(cxt, n, 0, start, stop, sys, fdisk_partition_is_bootable(pa));
1413 	if (n > 4) {
1414 		struct pte *pe = self_pte(cxt, n);
1415 		assert(pe);
1416 		set_partition(cxt, n - 1, 1, pe->offset, stop,
1417 					MBR_DOS_EXTENDED_PARTITION, 0);
1418 	}
1419 
1420 	/* report */
1421 	{
1422 		struct fdisk_parttype *t =
1423 			fdisk_label_get_parttype_from_code(cxt->label, sys);
1424 		fdisk_info_new_partition(cxt, n + 1, start, stop, t);
1425 		fdisk_unref_parttype(t);
1426 	}
1427 
1428 
1429 	if (IS_EXTENDED(sys)) {
1430 		struct pte *pen = self_pte(cxt, n);
1431 
1432 		assert(pen);
1433 		l->ext_index = n;
1434 		l->ext_offset = start;
1435 		pen->ex_entry = p;
1436 	}
1437 
1438 	fdisk_label_set_changed(cxt->label, 1);
1439 	rc = 0;
1440 done:
1441 	fdisk_unref_ask(ask);
1442 	return rc;
1443 }
1444 
add_logical(struct fdisk_context * cxt,struct fdisk_partition * pa,size_t * partno)1445 static int add_logical(struct fdisk_context *cxt,
1446 		       struct fdisk_partition *pa,
1447 		       size_t *partno)
1448 {
1449 	struct pte *pe;
1450 	int rc;
1451 
1452 	assert(cxt);
1453 	assert(partno);
1454 	assert(cxt->label);
1455 	assert(self_label(cxt)->ext_offset);
1456 
1457 	DBG(LABEL, ul_debug("DOS: nparts max: %zu", cxt->label->nparts_max));
1458 	pe = self_pte(cxt, cxt->label->nparts_max);
1459 	assert(pe);
1460 
1461 	if (!pe->sectorbuffer) {
1462 		pe->sectorbuffer = calloc(1, cxt->sector_size);
1463 		if (!pe->sectorbuffer)
1464 			return -ENOMEM;
1465 		DBG(LABEL, ul_debug("DOS: logical: %zu: new EBR sector buffer %p",
1466 					cxt->label->nparts_max, pe->sectorbuffer));
1467 		pe->private_sectorbuffer = 1;
1468 	}
1469 	pe->pt_entry = mbr_get_partition(pe->sectorbuffer, 0);
1470 	pe->ex_entry = pe->pt_entry + 1;
1471 	pe->offset = 0;
1472 	partition_set_changed(cxt, cxt->label->nparts_max, 1);
1473 
1474 	cxt->label->nparts_max++;
1475 
1476 	/* this message makes sense only when we use extended/primary/logical
1477 	 * dialog. The dialog is disable for scripts, see dos_add_partition() */
1478 	if (!cxt->script)
1479 		fdisk_info(cxt, _("Adding logical partition %zu"),
1480 				cxt->label->nparts_max);
1481 	*partno = cxt->label->nparts_max - 1;
1482 	rc = add_partition(cxt, *partno, pa);
1483 
1484 	if (rc) {
1485 		/* reset on error */
1486 		cxt->label->nparts_max--;
1487 		pe->pt_entry = NULL;
1488 		pe->ex_entry = NULL;
1489 		pe->offset = 0;
1490 		pe->changed = 0;
1491 	}
1492 
1493 	return rc;
1494 }
1495 
check(struct fdisk_context * cxt,size_t n,unsigned int h,unsigned int s,unsigned int c,unsigned int start)1496 static void check(struct fdisk_context *cxt, size_t n,
1497 	   unsigned int h, unsigned int s, unsigned int c,
1498 	   unsigned int start)
1499 {
1500 	unsigned int total, real_s, real_c;
1501 
1502 	if (!is_dos_compatible(cxt))
1503 		return;
1504 
1505 	real_s = sector(s) - 1;
1506 	real_c = cylinder(s, c);
1507 	total = (real_c * cxt->geom.heads + h) * cxt->geom.sectors + real_s;
1508 
1509 	if (!total)
1510 		fdisk_warnx(cxt, _("Partition %zu: contains sector 0"), n);
1511 	if (h >= cxt->geom.heads)
1512 		fdisk_warnx(cxt, _("Partition %zu: head %d greater than "
1513 				   "maximum %d"), n, h + 1, cxt->geom.heads);
1514 	if (real_s >= cxt->geom.sectors)
1515 		fdisk_warnx(cxt, _("Partition %zu: sector %d greater than "
1516 				   "maximum %llu"), n, s, cxt->geom.sectors);
1517 	if (real_c >= cxt->geom.cylinders)
1518 		fdisk_warnx(cxt, _("Partition %zu: cylinder %d greater than "
1519 				   "maximum %llu"),
1520 				n, real_c + 1,
1521 				cxt->geom.cylinders);
1522 
1523 	if (cxt->geom.cylinders <= 1024 && start != total)
1524 		fdisk_warnx(cxt, _("Partition %zu: previous sectors %u "
1525 				   "disagrees with total %u"), n, start, total);
1526 }
1527 
1528 /* check_consistency() and long2chs() added Sat Mar 6 12:28:16 1993,
1529  * faith@cs.unc.edu, based on code fragments from pfdisk by Gordon W. Ross,
1530  * Jan.  1990 (version 1.2.1 by Gordon W. Ross Aug. 1990; Modified by S.
1531  * Lubkin Oct.  1991). */
1532 
1533 static void
long2chs(struct fdisk_context * cxt,unsigned long ls,unsigned int * c,unsigned int * h,unsigned int * s)1534 long2chs(struct fdisk_context *cxt, unsigned long ls,
1535 	 unsigned int *c, unsigned int *h, unsigned int *s) {
1536 	int spc = cxt->geom.heads * cxt->geom.sectors;
1537 
1538 	*c = ls / spc;
1539 	ls = ls % spc;
1540 	*h = ls / cxt->geom.sectors;
1541 	*s = ls % cxt->geom.sectors + 1;	/* sectors count from 1 */
1542 }
1543 
check_consistency(struct fdisk_context * cxt,struct dos_partition * p,size_t partition)1544 static void check_consistency(struct fdisk_context *cxt, struct dos_partition *p,
1545 			      size_t partition)
1546 {
1547 	unsigned int pbc, pbh, pbs;	/* physical beginning c, h, s */
1548 	unsigned int pec, peh, pes;	/* physical ending c, h, s */
1549 	unsigned int lbc, lbh, lbs;	/* logical beginning c, h, s */
1550 	unsigned int lec, leh, les;	/* logical ending c, h, s */
1551 
1552 	if (!is_dos_compatible(cxt))
1553 		return;
1554 
1555 	if (!cxt->geom.heads || !cxt->geom.sectors || (partition >= 4))
1556 		return;		/* do not check extended partitions */
1557 
1558 	/* physical beginning c, h, s */
1559 	pbc = (p->bc & 0xff) | ((p->bs << 2) & 0x300);
1560 	pbh = p->bh;
1561 	pbs = p->bs & 0x3f;
1562 
1563 	/* physical ending c, h, s */
1564 	pec = (p->ec & 0xff) | ((p->es << 2) & 0x300);
1565 	peh = p->eh;
1566 	pes = p->es & 0x3f;
1567 
1568 	/* compute logical beginning (c, h, s) */
1569 	long2chs(cxt, dos_partition_get_start(p), &lbc, &lbh, &lbs);
1570 
1571 	/* compute logical ending (c, h, s) */
1572 	long2chs(cxt, dos_partition_get_start(p) + dos_partition_get_size(p) - 1, &lec, &leh, &les);
1573 
1574 	/* Same physical / logical beginning? */
1575 	if (cxt->geom.cylinders <= 1024
1576 	    && (pbc != lbc || pbh != lbh || pbs != lbs)) {
1577 		fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
1578 			"beginnings (non-Linux?): "
1579 			"phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1580 			partition + 1,
1581 			pbc, pbh, pbs,
1582 			lbc, lbh, lbs);
1583 	}
1584 
1585 	/* Same physical / logical ending? */
1586 	if (cxt->geom.cylinders <= 1024
1587 	    && (pec != lec || peh != leh || pes != les)) {
1588 		fdisk_warnx(cxt, _("Partition %zu: different physical/logical "
1589 			"endings: phys=(%d, %d, %d), logical=(%d, %d, %d)"),
1590 			partition + 1,
1591 			pec, peh, pes,
1592 			lec, leh, les);
1593 	}
1594 
1595 	/* Ending on cylinder boundary? */
1596 	if (peh != (cxt->geom.heads - 1) || pes != cxt->geom.sectors) {
1597 		fdisk_warnx(cxt, _("Partition %zu: does not end on "
1598 				   "cylinder boundary."),
1599 			partition + 1);
1600 	}
1601 }
1602 
fill_bounds(struct fdisk_context * cxt,fdisk_sector_t * first,fdisk_sector_t * last)1603 static void fill_bounds(struct fdisk_context *cxt,
1604 			fdisk_sector_t *first, fdisk_sector_t *last)
1605 {
1606 	size_t i;
1607 	struct pte *pe = self_pte(cxt, 0);
1608 	struct dos_partition *p;
1609 
1610 	assert(pe);
1611 	for (i = 0; i < cxt->label->nparts_max; pe++,i++) {
1612 		p = pe->pt_entry;
1613 		if (is_cleared_partition(p) || IS_EXTENDED (p->sys_ind)) {
1614 			first[i] = SIZE_MAX;
1615 			last[i] = 0;
1616 		} else {
1617 			first[i] = get_abs_partition_start(pe);
1618 			last[i]  = get_abs_partition_end(pe);
1619 		}
1620 	}
1621 }
1622 
dos_verify_disklabel(struct fdisk_context * cxt)1623 static int dos_verify_disklabel(struct fdisk_context *cxt)
1624 {
1625 	size_t i, j;
1626 	fdisk_sector_t total = 1, n_sectors = cxt->total_sectors;
1627 	fdisk_sector_t first[cxt->label->nparts_max],
1628 		       last[cxt->label->nparts_max];
1629 	struct dos_partition *p;
1630 	struct fdisk_dos_label *l = self_label(cxt);
1631 	int nerrors = 0;
1632 
1633 	assert(fdisk_is_label(cxt, DOS));
1634 
1635 	fill_bounds(cxt, first, last);
1636 	for (i = 0; i < cxt->label->nparts_max; i++) {
1637 		struct pte *pe = self_pte(cxt, i);
1638 
1639 		p = self_partition(cxt, i);
1640 		if (p && is_used_partition(p) && !IS_EXTENDED(p->sys_ind)) {
1641 			check_consistency(cxt, p, i);
1642 			assert(pe);
1643 			if (get_abs_partition_start(pe) < first[i]) {
1644 				fdisk_warnx(cxt, _(
1645 					"Partition %zu: bad start-of-data."),
1646 					 i + 1);
1647 				nerrors++;
1648 			}
1649 
1650 			check(cxt, i + 1, p->eh, p->es, p->ec, last[i]);
1651 			total += last[i] + 1 - first[i];
1652 
1653 			if (i == 0)
1654 				total += get_abs_partition_start(pe) - 1;
1655 
1656 			for (j = 0; j < i; j++) {
1657 				if ((first[i] >= first[j] && first[i] <= last[j])
1658 				    || ((last[i] <= last[j] && last[i] >= first[j]))) {
1659 
1660 					fdisk_warnx(cxt, _("Partition %zu: "
1661 						"overlaps partition %zu."),
1662 						j + 1, i + 1);
1663 					nerrors++;
1664 
1665 					total += first[i] >= first[j] ?
1666 						first[i] : first[j];
1667 					total -= last[i] <= last[j] ?
1668 						last[i] : last[j];
1669 				}
1670 			}
1671 		}
1672 	}
1673 
1674 	if (l->ext_offset) {
1675 		fdisk_sector_t e_last;
1676 		struct pte *ext_pe = self_pte(cxt, l->ext_index);
1677 
1678 		assert(ext_pe);
1679 		e_last = get_abs_partition_end(ext_pe);
1680 
1681 		for (i = 4; i < cxt->label->nparts_max; i++) {
1682 			total++;
1683 			p = self_partition(cxt, i);
1684 			assert(p);
1685 
1686 			if (!p->sys_ind) {
1687 				if (i != 4 || i + 1 < cxt->label->nparts_max) {
1688 					fdisk_warnx(cxt,
1689 						_("Partition %zu: empty."),
1690 						i + 1);
1691 					nerrors++;
1692 				}
1693 			} else if (first[i] < l->ext_offset
1694 				   || last[i] > e_last) {
1695 
1696 				fdisk_warnx(cxt, _("Logical partition %zu: "
1697 					"not entirely in partition %zu."),
1698 					i + 1, l->ext_index + 1);
1699 				nerrors++;
1700 			}
1701 		}
1702 	}
1703 
1704 	if (!nerrors) {
1705 		fdisk_info(cxt, _("No errors detected."));
1706 		if (total > n_sectors)
1707 			fdisk_info(cxt, _("Total allocated sectors %llu greater "
1708 				"than the maximum %llu."), total, n_sectors);
1709 		else if (total < n_sectors)
1710 			fdisk_info(cxt, _("Remaining %lld unallocated %ld-byte "
1711 				"sectors."), n_sectors - total, cxt->sector_size);
1712 	} else
1713 		fdisk_warnx(cxt,
1714 			P_("%d error detected.", "%d errors detected.", nerrors),
1715 			nerrors);
1716 
1717 	return nerrors;
1718 }
1719 
1720 /*
1721  * Ask the user for new partition type information (logical, extended).
1722  * This function calls the actual partition adding logic - add_partition.
1723  *
1724  * API callback.
1725  */
dos_add_partition(struct fdisk_context * cxt,struct fdisk_partition * pa,size_t * partno)1726 static int dos_add_partition(struct fdisk_context *cxt,
1727 			     struct fdisk_partition *pa,
1728 			     size_t *partno)
1729 {
1730 	size_t i;
1731 	uint8_t free_primary = 0, free_sectors = 0;
1732 	fdisk_sector_t last = 0, grain;
1733 	int rc = 0;
1734 	struct fdisk_dos_label *l;
1735 	struct pte *ext_pe;
1736 	size_t res = 0;		/* partno */
1737 
1738 	assert(cxt);
1739 	assert(cxt->label);
1740 	assert(fdisk_is_label(cxt, DOS));
1741 
1742 	DBG(LABEL, ul_debug("DOS: new partition wanted"));
1743 
1744 	l = self_label(cxt);
1745 
1746 	if (cxt->label->nparts_max >= MAXIMUM_PARTS) {
1747 		fdisk_warnx(cxt, _("The maximum number of partitions has "
1748 				  "been created."));
1749 		return -EINVAL;
1750 	}
1751 
1752 	ext_pe = l->ext_offset ? self_pte(cxt, l->ext_index) : NULL;
1753 
1754 	/*
1755 	 * partition template (@pa) based partitioning
1756 	 */
1757 
1758 	/* A) template specifies start within extended partition; add logical */
1759 	if (pa && fdisk_partition_has_start(pa) && ext_pe
1760 	    && pa->start >= l->ext_offset
1761 	    && pa->start <= get_abs_partition_end(ext_pe)) {
1762 		DBG(LABEL, ul_debug("DOS: pa template %p: add logical (by offset)", pa));
1763 
1764 		if (fdisk_partition_has_partno(pa) && fdisk_partition_get_partno(pa) < 4) {
1765 			DBG(LABEL, ul_debug("DOS: pa template specifies partno<4 for logical partition"));
1766 			return -EINVAL;
1767 		}
1768 		rc = add_logical(cxt, pa, &res);
1769 		goto done;
1770 
1771 	/* B) template specifies start out of extended partition; add primary */
1772 	} else if (pa && fdisk_partition_has_start(pa) && ext_pe) {
1773 		DBG(LABEL, ul_debug("DOS: pa template %p: add primary (by offset)", pa));
1774 
1775 		if (fdisk_partition_has_partno(pa) && fdisk_partition_get_partno(pa) >= 4) {
1776 			DBG(LABEL, ul_debug("DOS: pa template specifies partno>=4 for primary partition"));
1777 			return -EINVAL;
1778 		}
1779 		if (ext_pe && pa->type && IS_EXTENDED(pa->type->code)) {
1780 			fdisk_warnx(cxt, _("Extended partition already exists."));
1781 			return -EINVAL;
1782 		}
1783 		rc = get_partition_unused_primary(cxt, pa, &res);
1784 		if (rc == 0)
1785 			rc = add_partition(cxt, res, pa);
1786 		goto done;
1787 
1788 	/* C) template specifies start (or default), partno < 4; add primary */
1789 	} else if (pa && (fdisk_partition_start_is_default(pa) || fdisk_partition_has_start(pa))
1790 		   && fdisk_partition_has_partno(pa)
1791 		   && pa->partno < 4) {
1792 		DBG(LABEL, ul_debug("DOS: pa template %p: add primary (by partno)", pa));
1793 
1794 		if (ext_pe && pa->type && IS_EXTENDED(pa->type->code)) {
1795 			fdisk_warnx(cxt, _("Extended partition already exists."));
1796 			return -EINVAL;
1797 		}
1798 		rc = get_partition_unused_primary(cxt, pa, &res);
1799 		if (rc == 0)
1800 			rc = add_partition(cxt, res, pa);
1801 		goto done;
1802 
1803 	/* D) template specifies start (or default), partno >= 4; add logical */
1804 	} else if (pa && (fdisk_partition_start_is_default(pa) || fdisk_partition_has_start(pa))
1805 		   && fdisk_partition_has_partno(pa)
1806 		   && pa->partno >= 4) {
1807 		DBG(LABEL, ul_debug("DOS: pa template %p: add logical (by partno)", pa));
1808 
1809 		if (!ext_pe) {
1810 			fdisk_warnx(cxt, _("Extended partition does not exists. Failed to add logical partition."));
1811 			return -EINVAL;
1812 		}
1813 
1814 		if (fdisk_partition_has_start(pa)
1815 			   && pa->start < l->ext_offset
1816 			   && pa->start > get_abs_partition_end(ext_pe)) {
1817 			DBG(LABEL, ul_debug("DOS: pa template specifies partno>=4, but start out of extended"));
1818 			return -EINVAL;
1819 		}
1820 
1821 		rc = add_logical(cxt, pa, &res);
1822 		goto done;
1823 	}
1824 
1825 	DBG(LABEL, ul_debug("DOS: dialog driven partitioning"));
1826 	/* Note @pa may be still used for things like partition type, etc */
1827 
1828 	/* check if there is space for primary partition */
1829 	grain = cxt->grain > cxt->sector_size ? cxt->grain / cxt->sector_size : 1;
1830 	last = cxt->first_lba;
1831 
1832 	if (cxt->parent && fdisk_is_label(cxt->parent, GPT)) {
1833 		/* modifying a hybrid MBR, which throws out the rules */
1834 		grain = 1;
1835 		last = 1;
1836 	}
1837 
1838 	for (i = 0; i < 4; i++) {
1839 		struct dos_partition *p = self_partition(cxt, i);
1840 
1841 		assert(p);
1842 		if (is_used_partition(p)) {
1843 			fdisk_sector_t start = dos_partition_get_start(p);
1844 			if (last + grain <= start)
1845 				free_sectors = 1;
1846 			last = start + dos_partition_get_size(p);
1847 		} else
1848 			free_primary++;
1849 	}
1850 	if (last + grain < cxt->total_sectors - 1)
1851 		free_sectors = 1;
1852 
1853 
1854 	if (!free_primary || !free_sectors) {
1855 		DBG(LABEL, ul_debug("DOS: primary impossible, add logical"));
1856 		if (l->ext_offset) {
1857 			if (!pa || fdisk_partition_has_start(pa)) {
1858 				/* See above case A); here we have start, but
1859 				 * out of extended partition */
1860 				const char *msg;
1861 				if (!free_primary)
1862 					msg = _("All primary partitions are in use.");
1863 				else
1864 					msg =  _("All space for primary partitions is in use.");
1865 
1866 				if (pa && fdisk_partition_has_start(pa)) {
1867 					fdisk_warnx(cxt, msg);
1868 					return -EINVAL;
1869 				}
1870 				fdisk_info(cxt, msg);
1871 			}
1872 			rc = add_logical(cxt, pa, &res);
1873 		} else {
1874 			if (free_primary)
1875 				fdisk_info(cxt, _("All space for primary partitions is in use."));
1876 			else
1877 			/* TRANSLATORS: Try to keep this within 80 characters. */
1878 				fdisk_info(cxt, _("To create more partitions, first replace "
1879 					  "a primary with an extended partition."));
1880 			return -EINVAL;
1881 		}
1882 	} else {
1883 		char hint[BUFSIZ];
1884 		struct fdisk_ask *ask;
1885 		int c = 0;
1886 
1887 		/* the default layout for scripts is to create primary partitions */
1888 		if (cxt->script || !fdisk_has_dialogs(cxt)) {
1889 			rc = get_partition_unused_primary(cxt, pa, &res);
1890 			if (rc == 0)
1891 				rc = add_partition(cxt, res, pa);
1892 			goto done;
1893 		}
1894 
1895 		ask = fdisk_new_ask();
1896 		if (!ask)
1897 			return -ENOMEM;
1898 		fdisk_ask_set_type(ask, FDISK_ASKTYPE_MENU);
1899 		fdisk_ask_set_query(ask, _("Partition type"));
1900 		fdisk_ask_menu_set_default(ask, free_primary == 1
1901 						&& !l->ext_offset ? 'e' : 'p');
1902 		snprintf(hint, sizeof(hint),
1903 				_("%u primary, %d extended, %u free"),
1904 				4 - (l->ext_offset ? 1 : 0) - free_primary,
1905 				l->ext_offset ? 1 : 0,
1906 				free_primary);
1907 
1908 		fdisk_ask_menu_add_item(ask, 'p', _("primary"), hint);
1909 		if (!l->ext_offset)
1910 			fdisk_ask_menu_add_item(ask, 'e', _("extended"), _("container for logical partitions"));
1911 		else
1912 			fdisk_ask_menu_add_item(ask, 'l', _("logical"), _("numbered from 5"));
1913 
1914 		rc = fdisk_do_ask(cxt, ask);
1915 		if (!rc)
1916 			fdisk_ask_menu_get_result(ask, &c);
1917 		fdisk_unref_ask(ask);
1918 		if (rc)
1919 			return rc;
1920 
1921 		if (c == 'p') {
1922 			rc = get_partition_unused_primary(cxt, pa, &res);
1923 			if (rc == 0)
1924 				rc = add_partition(cxt, res, pa);
1925 			goto done;
1926 		} else if (c == 'l' && l->ext_offset) {
1927 			rc = add_logical(cxt, pa, &res);
1928 			goto done;
1929 		} else if (c == 'e' && !l->ext_offset) {
1930 			rc = get_partition_unused_primary(cxt, pa, &res);
1931 			if (rc == 0) {
1932 				struct fdisk_partition *xpa = NULL;
1933 				struct fdisk_parttype *t;
1934 
1935 				t = fdisk_label_get_parttype_from_code(cxt->label,
1936 						MBR_DOS_EXTENDED_PARTITION);
1937 				if (!pa) {
1938 					pa = xpa = fdisk_new_partition();
1939 					if (!xpa)
1940 						return -ENOMEM;
1941 				}
1942 				fdisk_partition_set_type(pa, t);
1943 				rc = add_partition(cxt, res, pa);
1944 				if (xpa) {
1945 					fdisk_unref_partition(xpa);
1946 					pa = NULL;
1947 				}
1948 			}
1949 			goto done;
1950 		} else
1951 			fdisk_warnx(cxt, _("Invalid partition type `%c'."), c);
1952 	}
1953 done:
1954 	if (rc == 0) {
1955 		cxt->label->nparts_cur++;
1956 		if (partno)
1957 			*partno = res;
1958 	}
1959 	return rc;
1960 }
1961 
write_sector(struct fdisk_context * cxt,fdisk_sector_t secno,unsigned char * buf)1962 static int write_sector(struct fdisk_context *cxt, fdisk_sector_t secno,
1963 			       unsigned char *buf)
1964 {
1965 	int rc;
1966 
1967 	rc = seek_sector(cxt, secno);
1968 	if (rc != 0) {
1969 		fdisk_warn(cxt, _("Cannot write sector %jd: seek failed"),
1970 				(uintmax_t) secno);
1971 		return rc;
1972 	}
1973 
1974 	DBG(LABEL, ul_debug("DOS: writing to sector %ju", (uintmax_t) secno));
1975 
1976 	if (write(cxt->dev_fd, buf, cxt->sector_size) != (ssize_t) cxt->sector_size)
1977 		return -errno;
1978 	return 0;
1979 }
1980 
dos_write_disklabel(struct fdisk_context * cxt)1981 static int dos_write_disklabel(struct fdisk_context *cxt)
1982 {
1983 	struct fdisk_dos_label *l = self_label(cxt);
1984 	size_t i;
1985 	int rc = 0, mbr_changed = 0;
1986 
1987 	assert(cxt);
1988 	assert(cxt->label);
1989 	assert(fdisk_is_label(cxt, DOS));
1990 
1991 	DBG(LABEL, ul_debug("DOS: write PT requested [label-changed: %d, non-pt-changed: %d]",
1992 				cxt->label->changed, l->non_pt_changed));
1993 
1994 	mbr_changed = l->non_pt_changed;
1995 
1996 	/* MBR (primary partitions) */
1997 	if (!mbr_changed) {
1998 		for (i = 0; i < 4; i++) {
1999 			struct pte *pe = self_pte(cxt, i);
2000 
2001 			assert(pe);
2002 			if (pe->changed)
2003 				mbr_changed = 1;
2004 		}
2005 	}
2006 	if (mbr_changed) {
2007 		DBG(LABEL, ul_debug("DOS: MBR changed, writing"));
2008 		mbr_set_magic(cxt->firstsector);
2009 		rc = write_sector(cxt, 0, cxt->firstsector);
2010 		if (rc)
2011 			goto done;
2012 	}
2013 
2014 	if (cxt->label->nparts_max <= 4 && l->ext_offset) {
2015 		/* we have empty extended partition, check if the partition has
2016 		 * been modified and then cleanup possible remaining EBR  */
2017 		struct pte *pe = self_pte(cxt, l->ext_index);
2018 		unsigned char empty[512] = { 0 };
2019 		fdisk_sector_t off = pe ? get_abs_partition_start(pe) : 0;
2020 
2021 		if (off && pe->changed) {
2022 			mbr_set_magic(empty);
2023 			write_sector(cxt, off, empty);
2024 		}
2025 	}
2026 
2027 	/* EBR (logical partitions) */
2028 	for (i = 4; i < cxt->label->nparts_max; i++) {
2029 		struct pte *pe = self_pte(cxt, i);
2030 
2031 		assert(pe);
2032 		if (!pe->changed || !pe->offset || !pe->sectorbuffer)
2033 			continue;
2034 
2035 		mbr_set_magic(pe->sectorbuffer);
2036 		rc = write_sector(cxt, pe->offset, pe->sectorbuffer);
2037 		if (rc)
2038 			goto done;
2039 	}
2040 
2041 done:
2042 	return rc;
2043 }
2044 
dos_locate_disklabel(struct fdisk_context * cxt,int n,const char ** name,uint64_t * offset,size_t * size)2045 static int dos_locate_disklabel(struct fdisk_context *cxt, int n,
2046 		const char **name, uint64_t *offset, size_t *size)
2047 {
2048 	assert(cxt);
2049 
2050 	*name = NULL;
2051 	*offset = 0;
2052 	*size = 0;
2053 
2054 	switch (n) {
2055 	case 0:
2056 		*name = "MBR";
2057 		*offset = 0;
2058 		*size = 512;
2059 		break;
2060 	default:
2061 		/* extended partitions */
2062 		if ((size_t)n - 1 + 4 < cxt->label->nparts_max) {
2063 			struct pte *pe = self_pte(cxt, n - 1 + 4);
2064 
2065 			assert(pe);
2066 			assert(pe->private_sectorbuffer);
2067 
2068 			*name = "EBR";
2069 			*offset = (uint64_t) pe->offset * cxt->sector_size;
2070 			*size = 512;
2071 		} else
2072 			return 1;
2073 		break;
2074 	}
2075 
2076 	return 0;
2077 }
2078 
2079 /*
2080  * Check whether partition entries are ordered by their starting positions.
2081  * Return 0 if OK. Return i if partition i should have been earlier.
2082  * Two separate checks: primary and logical partitions.
2083  */
wrong_p_order(struct fdisk_context * cxt,size_t * prev)2084 static int wrong_p_order(struct fdisk_context *cxt, size_t *prev)
2085 {
2086 	size_t last_p_start_pos = 0, p_start_pos;
2087 	size_t i, last_i = 0;
2088 
2089 	for (i = 0 ; i < cxt->label->nparts_max; i++) {
2090 
2091 		struct pte *pe = self_pte(cxt, i);
2092 		struct dos_partition *p;
2093 
2094 		assert(pe);
2095 		p = pe->pt_entry;
2096 
2097 		if (i == 4) {
2098 			last_i = 4;
2099 			last_p_start_pos = 0;
2100 		}
2101 		if (is_used_partition(p)) {
2102 			p_start_pos = get_abs_partition_start(pe);
2103 
2104 			if (last_p_start_pos > p_start_pos) {
2105 				if (prev)
2106 					*prev = last_i;
2107 				return i;
2108 			}
2109 
2110 			last_p_start_pos = p_start_pos;
2111 			last_i = i;
2112 		}
2113 	}
2114 	return 0;
2115 }
2116 
dos_get_disklabel_item(struct fdisk_context * cxt,struct fdisk_labelitem * item)2117 static int dos_get_disklabel_item(struct fdisk_context *cxt, struct fdisk_labelitem *item)
2118 {
2119 	int rc = 0;
2120 
2121 	assert(cxt);
2122 	assert(cxt->label);
2123 	assert(fdisk_is_label(cxt, DOS));
2124 
2125 	switch (item->id) {
2126 	case FDISK_LABELITEM_ID:
2127 	{
2128 		unsigned int num = mbr_get_id(cxt->firstsector);
2129 		item->name = _("Disk identifier");
2130 		item->type = 's';
2131 		if (asprintf(&item->data.str, "0x%08x", num) < 0)
2132 			rc = -ENOMEM;
2133 		break;
2134 	}
2135 	default:
2136 		if (item->id < __FDISK_NLABELITEMS)
2137 			rc = 1;	/* unsupported generic item */
2138 		else
2139 			rc = 2;	/* out of range */
2140 		break;
2141 	}
2142 
2143 	return rc;
2144 
2145 }
2146 
dos_get_partition(struct fdisk_context * cxt,size_t n,struct fdisk_partition * pa)2147 static int dos_get_partition(struct fdisk_context *cxt, size_t n,
2148 			     struct fdisk_partition *pa)
2149 {
2150 	struct dos_partition *p;
2151 	struct pte *pe;
2152 	struct fdisk_dos_label *lb;
2153 
2154 	assert(cxt);
2155 	assert(pa);
2156 	assert(cxt->label);
2157 	assert(fdisk_is_label(cxt, DOS));
2158 
2159 	lb = self_label(cxt);
2160 
2161 	pe = self_pte(cxt, n);
2162 	assert(pe);
2163 
2164 	p = pe->pt_entry;
2165 	pa->used = !is_cleared_partition(p);
2166 	if (!pa->used)
2167 		return 0;
2168 
2169 	pa->type = dos_partition_parttype(cxt, p);
2170 	pa->boot = p->boot_ind == ACTIVE_FLAG ? 1 : 0;
2171 	pa->start = get_abs_partition_start(pe);
2172 	pa->size = dos_partition_get_size(p);
2173 	pa->container = lb->ext_offset && n == lb->ext_index;
2174 
2175 	if (n >= 4)
2176 		pa->parent_partno = lb->ext_index;
2177 
2178 	if (p->boot_ind && asprintf(&pa->attrs, "%02x", p->boot_ind) < 0)
2179 		return -ENOMEM;
2180 
2181 	/* start C/H/S */
2182 	if (asprintf(&pa->start_chs, "%d/%d/%d",
2183 				cylinder(p->bs, p->bc),
2184 				p->bh,
2185 				sector(p->bs)) < 0)
2186 		return -ENOMEM;
2187 
2188 	/* end C/H/S */
2189 	if (asprintf(&pa->end_chs, "%d/%d/%d",
2190 				cylinder(p->es, p->ec),
2191 				p->eh,
2192 				sector(p->es)) < 0)
2193 		return -ENOMEM;
2194 
2195 	return 0;
2196 }
2197 
has_logical(struct fdisk_context * cxt)2198 static int has_logical(struct fdisk_context *cxt)
2199 {
2200 	size_t i;
2201 	struct fdisk_dos_label *l = self_label(cxt);
2202 
2203 	for (i = 4; i < cxt->label->nparts_max; i++) {
2204 		if (l->ptes[i].pt_entry)
2205 			return 1;
2206 	}
2207 	return 0;
2208 }
2209 
dos_set_partition(struct fdisk_context * cxt,size_t n,struct fdisk_partition * pa)2210 static int dos_set_partition(struct fdisk_context *cxt, size_t n,
2211 			     struct fdisk_partition *pa)
2212 {
2213 	struct fdisk_dos_label *l;
2214 	struct dos_partition *p;
2215 	struct pte *pe;
2216 	int orgtype;
2217 	fdisk_sector_t start, size;
2218 
2219 	assert(cxt);
2220 	assert(pa);
2221 	assert(cxt->label);
2222 	assert(fdisk_is_label(cxt, DOS));
2223 
2224 	if (n >= cxt->label->nparts_max)
2225 		return -EINVAL;
2226 
2227 	l = self_label(cxt);
2228 	p = self_partition(cxt, n);
2229 
2230 	pe = self_pte(cxt, n);
2231 	if (!pe)
2232 		return -EINVAL;
2233 
2234 	orgtype = p->sys_ind;
2235 
2236 	if (pa->type) {
2237 		if (IS_EXTENDED(pa->type->code) && l->ext_offset && l->ext_index != n) {
2238 			fdisk_warnx(cxt, _("Extended partition already exists."));
2239 			return -EINVAL;
2240 		}
2241 
2242 		if (!pa->type->code)
2243 			fdisk_warnx(cxt, _("Type 0 means free space to many systems. "
2244 				   "Having partitions of type 0 is probably unwise."));
2245 
2246 		if (IS_EXTENDED(p->sys_ind) && !IS_EXTENDED(pa->type->code) && has_logical(cxt)) {
2247 			fdisk_warnx(cxt, _(
2248 				"Cannot change type of the extended partition which is "
2249 				"already used by logical partitions. Delete logical "
2250 				"partitions first."));
2251 			return -EINVAL;
2252 		}
2253 	}
2254 
2255 	FDISK_INIT_UNDEF(start);
2256 	FDISK_INIT_UNDEF(size);
2257 
2258 	if (fdisk_partition_has_start(pa))
2259 		start = pa->start;
2260 	if (fdisk_partition_has_size(pa))
2261 		size = pa->size;
2262 
2263 	if (!FDISK_IS_UNDEF(start) || !FDISK_IS_UNDEF(size)) {
2264 		DBG(LABEL, ul_debug("DOS: resize partition"));
2265 
2266 		if (FDISK_IS_UNDEF(start))
2267 			start = get_abs_partition_start(pe);
2268 		if (FDISK_IS_UNDEF(size))
2269 			size = dos_partition_get_size(p);
2270 
2271 		set_partition(cxt, n, 0, start, start + size - 1,
2272 				pa->type  ? pa->type->code : p->sys_ind,
2273 				FDISK_IS_UNDEF(pa->boot) ?
2274 					p->boot_ind == ACTIVE_FLAG :
2275 					fdisk_partition_is_bootable(pa));
2276 	} else {
2277 		DBG(LABEL, ul_debug("DOS: keep size, modify properties"));
2278 		if (pa->type)
2279 			p->sys_ind = pa->type->code;
2280 		if (!FDISK_IS_UNDEF(pa->boot))
2281 			p->boot_ind = fdisk_partition_is_bootable(pa) ? ACTIVE_FLAG : 0;
2282 	}
2283 
2284 	if (pa->type) {
2285 		if (IS_EXTENDED(pa->type->code) && !IS_EXTENDED(orgtype)) {
2286 			/* new extended partition - create a reference  */
2287 			l->ext_index = n;
2288 			l->ext_offset = dos_partition_get_start(p);
2289 			pe->ex_entry = p;
2290 		 } else if (IS_EXTENDED(orgtype)) {
2291 			/* remove extended partition */
2292 			cxt->label->nparts_max = 4;
2293 			l->ptes[l->ext_index].ex_entry = NULL;
2294 			l->ext_offset = 0;
2295 			l->ext_index = 0;
2296 		 }
2297 	}
2298 
2299 	partition_set_changed(cxt, n, 1);
2300 	return 0;
2301 }
2302 
print_chain_of_logicals(struct fdisk_context * cxt)2303 static void print_chain_of_logicals(struct fdisk_context *cxt)
2304 {
2305 	size_t i;
2306 	struct fdisk_dos_label *l = self_label(cxt);
2307 
2308 	fputc('\n', stdout);
2309 
2310 	for (i = 4; i < cxt->label->nparts_max; i++) {
2311 		struct pte *pe = self_pte(cxt, i);
2312 
2313 		assert(pe);
2314 		fprintf(stderr, "#%02zu EBR [%10ju], "
2315 			"data[start=%10ju (%10ju), size=%10ju], "
2316 			"link[start=%10ju (%10ju), size=%10ju]\n",
2317 			i, (uintmax_t) pe->offset,
2318 			/* data */
2319 			(uintmax_t) dos_partition_get_start(pe->pt_entry),
2320 			(uintmax_t) get_abs_partition_start(pe),
2321 			(uintmax_t) dos_partition_get_size(pe->pt_entry),
2322 			/* link */
2323 			(uintmax_t) dos_partition_get_start(pe->ex_entry),
2324 			(uintmax_t) l->ext_offset + dos_partition_get_start(pe->ex_entry),
2325 			(uintmax_t) dos_partition_get_size(pe->ex_entry));
2326 	}
2327 }
2328 
cmp_ebr_offsets(const void * a,const void * b)2329 static int cmp_ebr_offsets(const void *a, const void *b)
2330 {
2331 	const struct pte *ae = (const struct pte *) a,
2332 			 *be = (const struct pte *) b;
2333 
2334 	if (ae->offset == 0 && be->offset == 0)
2335 		return 0;
2336 	if (ae->offset == 0)
2337 		return 1;
2338 	if (be->offset == 0)
2339 		return -1;
2340 
2341 	return cmp_numbers(ae->offset, be->offset);
2342 }
2343 
2344 /*
2345  * Fix the chain of logicals.
2346  *
2347  * The function does not modify data partitions within EBR tables
2348  * (pte->pt_entry). It sorts the chain by EBR offsets and then update links
2349  * (pte->ex_entry) between EBR tables.
2350  *
2351  */
fix_chain_of_logicals(struct fdisk_context * cxt)2352 static void fix_chain_of_logicals(struct fdisk_context *cxt)
2353 {
2354 	struct fdisk_dos_label *l = self_label(cxt);
2355 	struct pte *last;
2356 	size_t i;
2357 
2358 	DBG(LABEL, print_chain_of_logicals(cxt));
2359 
2360 	/* Sort chain by EBR offsets */
2361 	qsort(&l->ptes[4], cxt->label->nparts_max - 4, sizeof(struct pte),
2362 			cmp_ebr_offsets);
2363 
2364 again:
2365 	/* Sort data partitions by start */
2366 	for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2367 		struct pte *cur = self_pte(cxt, i),
2368 			   *nxt = self_pte(cxt, i + 1);
2369 
2370 		assert(cur);
2371 		assert(nxt);
2372 
2373 		if (get_abs_partition_start(cur) >
2374 		    get_abs_partition_start(nxt)) {
2375 
2376 			struct dos_partition tmp = *cur->pt_entry;
2377 			fdisk_sector_t cur_start = get_abs_partition_start(cur),
2378 				 nxt_start = get_abs_partition_start(nxt);
2379 
2380 			/* swap data partitions */
2381 			*cur->pt_entry = *nxt->pt_entry;
2382 			*nxt->pt_entry = tmp;
2383 
2384 			/* Recount starts according to EBR offsets, the absolute
2385 			 * address still has to be the same! */
2386 			dos_partition_set_start(cur->pt_entry, nxt_start - cur->offset);
2387 			dos_partition_set_start(nxt->pt_entry, cur_start - nxt->offset);
2388 
2389 			partition_set_changed(cxt, i, 1);
2390 			partition_set_changed(cxt, i + 1, 1);
2391 			goto again;
2392 		}
2393 	}
2394 
2395 	/* Update EBR links */
2396 	for (i = 4; i < cxt->label->nparts_max - 1; i++) {
2397 		struct pte *cur = self_pte(cxt, i),
2398 			   *nxt = self_pte(cxt, i + 1);
2399 
2400 		assert(cur);
2401 		assert(nxt);
2402 
2403 		fdisk_sector_t noff = nxt->offset - l->ext_offset,
2404 		 ooff = dos_partition_get_start(cur->ex_entry);
2405 
2406 		if (noff == ooff)
2407 			continue;
2408 
2409 		DBG(LABEL, ul_debug("DOS: fix EBR [%10ju] link %ju -> %ju",
2410 			(uintmax_t) cur->offset,
2411 			(uintmax_t) ooff, (uintmax_t) noff));
2412 
2413 		set_partition(cxt, i, 1, nxt->offset,
2414 				get_abs_partition_end(nxt),
2415 				MBR_DOS_EXTENDED_PARTITION, 0);
2416 	}
2417 
2418 	/* always terminate the chain ! */
2419 	last = self_pte(cxt, cxt->label->nparts_max - 1);
2420 	if (last) {
2421 		clear_partition(last->ex_entry);
2422 		partition_set_changed(cxt, cxt->label->nparts_max - 1, 1);
2423 	}
2424 
2425 	DBG(LABEL, print_chain_of_logicals(cxt));
2426 }
2427 
dos_reorder(struct fdisk_context * cxt)2428 static int dos_reorder(struct fdisk_context *cxt)
2429 {
2430 	struct pte *pei, *pek;
2431 	size_t i,k;
2432 
2433 	if (!wrong_p_order(cxt, NULL)) {
2434 		fdisk_info(cxt, _("Nothing to do. Ordering is correct already."));
2435 		return 1;
2436 	}
2437 
2438 	while ((i = wrong_p_order(cxt, &k)) != 0 && i < 4) {
2439 		/* partition i should have come earlier, move it */
2440 		/* We have to move data in the MBR */
2441 		struct dos_partition *pi, *pk, *pe, pbuf;
2442 		pei = self_pte(cxt, i);
2443 		pek = self_pte(cxt, k);
2444 
2445 		assert(pei);
2446 		assert(pek);
2447 
2448 		pe = pei->ex_entry;
2449 		pei->ex_entry = pek->ex_entry;
2450 		pek->ex_entry = pe;
2451 
2452 		pi = pei->pt_entry;
2453 		pk = pek->pt_entry;
2454 
2455 		memmove(&pbuf, pi, sizeof(struct dos_partition));
2456 		memmove(pi, pk, sizeof(struct dos_partition));
2457 		memmove(pk, &pbuf, sizeof(struct dos_partition));
2458 
2459 		partition_set_changed(cxt, i, 1);
2460 		partition_set_changed(cxt, k, 1);
2461 	}
2462 
2463 	if (i)
2464 		fix_chain_of_logicals(cxt);
2465 
2466 	return 0;
2467 }
2468 
2469 /* TODO: use fdisk_set_partition() API */
fdisk_dos_move_begin(struct fdisk_context * cxt,size_t i)2470 int fdisk_dos_move_begin(struct fdisk_context *cxt, size_t i)
2471 {
2472 	struct pte *pe;
2473 	struct dos_partition *p;
2474 	unsigned int new, free_start, curr_start, last;
2475 	uintmax_t res = 0;
2476 	size_t x;
2477 	int rc;
2478 
2479 	assert(cxt);
2480 	assert(fdisk_is_label(cxt, DOS));
2481 
2482 	pe = self_pte(cxt, i);
2483 	if (!pe)
2484 		return -EINVAL;
2485 
2486 	p = pe->pt_entry;
2487 
2488 	if (!is_used_partition(p) || IS_EXTENDED (p->sys_ind)) {
2489 		fdisk_warnx(cxt, _("Partition %zu: no data area."), i + 1);
2490 		return 0;
2491 	}
2492 
2493 	/* the default start is at the second sector of the disk or at the
2494 	 * second sector of the extended partition
2495 	 */
2496 	free_start = pe->offset ? pe->offset + 1 : 1;
2497 
2498 	curr_start = get_abs_partition_start(pe);
2499 
2500 	/* look for a free space before the current start of the partition */
2501 	for (x = 0; x < cxt->label->nparts_max; x++) {
2502 		unsigned int end;
2503 		struct pte *prev_pe = self_pte(cxt, x);
2504 		struct dos_partition *prev_p;
2505 
2506 		assert(prev_pe);
2507 
2508 		prev_p = prev_pe->pt_entry;
2509 		if (!prev_p)
2510 			continue;
2511 		end = get_abs_partition_start(prev_pe)
2512 		      + dos_partition_get_size(prev_p);
2513 
2514 		if (is_used_partition(prev_p) &&
2515 		    end > free_start && end <= curr_start)
2516 			free_start = end;
2517 	}
2518 
2519 	last = get_abs_partition_end(pe);
2520 
2521 	rc = fdisk_ask_number(cxt, free_start, curr_start, last,
2522 			_("New beginning of data"), &res);
2523 	if (rc)
2524 		return rc;
2525 
2526 	new = res - pe->offset;
2527 
2528 	if (new != dos_partition_get_size(p)) {
2529 		unsigned int sects = dos_partition_get_size(p)
2530 				+ dos_partition_get_start(p) - new;
2531 
2532 		dos_partition_set_size(p, sects);
2533 		dos_partition_set_start(p, new);
2534 
2535 		partition_set_changed(cxt, i, 1);
2536 	}
2537 
2538 	return rc;
2539 }
2540 
dos_partition_is_used(struct fdisk_context * cxt,size_t i)2541 static int dos_partition_is_used(
2542 		struct fdisk_context *cxt,
2543 		size_t i)
2544 {
2545 	struct dos_partition *p;
2546 
2547 	assert(cxt);
2548 	assert(cxt->label);
2549 	assert(fdisk_is_label(cxt, DOS));
2550 
2551 	if (i >= cxt->label->nparts_max)
2552 		return 0;
2553 
2554 	p = self_partition(cxt, i);
2555 
2556 	return p && !is_cleared_partition(p);
2557 }
2558 
dos_toggle_partition_flag(struct fdisk_context * cxt,size_t i,unsigned long flag)2559 static int dos_toggle_partition_flag(
2560 		struct fdisk_context *cxt,
2561 		size_t i,
2562 		unsigned long flag)
2563 {
2564 	struct dos_partition *p;
2565 
2566 	assert(cxt);
2567 	assert(cxt->label);
2568 	assert(fdisk_is_label(cxt, DOS));
2569 
2570 	if (i >= cxt->label->nparts_max)
2571 		return -EINVAL;
2572 
2573 	p = self_partition(cxt, i);
2574 
2575 	switch (flag) {
2576 	case DOS_FLAG_ACTIVE:
2577 		if (IS_EXTENDED(p->sys_ind) && !p->boot_ind)
2578 			fdisk_warnx(cxt, _("Partition %zu: is an extended "
2579 					"partition."), i + 1);
2580 
2581 		p->boot_ind = (p->boot_ind ? 0 : ACTIVE_FLAG);
2582 		partition_set_changed(cxt, i, 1);
2583 		fdisk_info(cxt,	p->boot_ind ?
2584 			_("The bootable flag on partition %zu is enabled now.") :
2585 			_("The bootable flag on partition %zu is disabled now."),
2586 			i + 1);
2587 		break;
2588 	default:
2589 		return 1;
2590 	}
2591 
2592 	return 0;
2593 }
2594 
2595 static const struct fdisk_field dos_fields[] =
2596 {
2597 	/* basic */
2598 	{ FDISK_FIELD_DEVICE,	N_("Device"),	 10,	0 },
2599 	{ FDISK_FIELD_BOOT,	N_("Boot"),	  1,	0 },
2600 	{ FDISK_FIELD_START,	N_("Start"),	  5,	FDISK_FIELDFL_NUMBER },
2601 	{ FDISK_FIELD_END,	N_("End"),	  5,	FDISK_FIELDFL_NUMBER },
2602 	{ FDISK_FIELD_SECTORS,	N_("Sectors"),	  5,	FDISK_FIELDFL_NUMBER },
2603 	{ FDISK_FIELD_CYLINDERS,N_("Cylinders"),  5,	FDISK_FIELDFL_NUMBER },
2604 	{ FDISK_FIELD_SIZE,	N_("Size"),	  5,	FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_EYECANDY },
2605 	{ FDISK_FIELD_TYPEID,	N_("Id"),	  2,	FDISK_FIELDFL_NUMBER },
2606 	{ FDISK_FIELD_TYPE,	N_("Type"),	0.1,	0 },
2607 
2608 	/* expert mode */
2609 	{ FDISK_FIELD_SADDR,	N_("Start-C/H/S"), 1,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2610 	{ FDISK_FIELD_EADDR,	N_("End-C/H/S"),   1,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL },
2611 	{ FDISK_FIELD_ATTR,	N_("Attrs"),	   2,   FDISK_FIELDFL_NUMBER | FDISK_FIELDFL_DETAIL }
2612 
2613 };
2614 
2615 static const struct fdisk_label_operations dos_operations =
2616 {
2617 	.probe		= dos_probe_label,
2618 	.write		= dos_write_disklabel,
2619 	.verify		= dos_verify_disklabel,
2620 	.create		= dos_create_disklabel,
2621 	.locate		= dos_locate_disklabel,
2622 	.get_item	= dos_get_disklabel_item,
2623 	.set_id		= dos_set_disklabel_id,
2624 
2625 	.get_part	= dos_get_partition,
2626 	.set_part	= dos_set_partition,
2627 	.add_part	= dos_add_partition,
2628 	.del_part	= dos_delete_partition,
2629 	.reorder	= dos_reorder,
2630 
2631 	.part_toggle_flag = dos_toggle_partition_flag,
2632 	.part_is_used	= dos_partition_is_used,
2633 
2634 	.reset_alignment = dos_reset_alignment,
2635 
2636 	.deinit		= dos_deinit,
2637 };
2638 
2639 /*
2640  * allocates DOS in-memory stuff
2641  */
fdisk_new_dos_label(struct fdisk_context * cxt)2642 struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt __attribute__ ((__unused__)))
2643 {
2644 	struct fdisk_label *lb;
2645 	struct fdisk_dos_label *dos;
2646 
2647 	dos = calloc(1, sizeof(*dos));
2648 	if (!dos)
2649 		return NULL;
2650 
2651 	/* initialize generic part of the driver */
2652 	lb = (struct fdisk_label *) dos;
2653 	lb->name = "dos";
2654 	lb->id = FDISK_DISKLABEL_DOS;
2655 	lb->op = &dos_operations;
2656 
2657 	lb->parttypes = dos_parttypes;
2658 	lb->nparttypes = ARRAY_SIZE(dos_parttypes) - 1;
2659 	lb->parttype_cuts = dos_parttype_cuts;
2660 	lb->nparttype_cuts = ARRAY_SIZE(dos_parttype_cuts);
2661 
2662 	lb->fields = dos_fields;
2663 	lb->nfields = ARRAY_SIZE(dos_fields);
2664 
2665 	lb->geom_min.sectors = 1;
2666 	lb->geom_min.heads = 1;
2667 	lb->geom_min.cylinders = 1;
2668 
2669 	lb->geom_max.sectors = 63;
2670 	lb->geom_max.heads = 255;
2671 	lb->geom_max.cylinders = 1048576;
2672 
2673 	return lb;
2674 }
2675 
2676 /**
2677  * fdisk_dos_enable_compatible:
2678  * @lb: DOS label (see fdisk_get_label())
2679  * @enable: 0 or 1
2680  *
2681  * Enables deprecated DOS compatible mode, in this mode library checks for
2682  * cylinders boundary, cases about CHS addressing and another obscure things.
2683  *
2684  * Returns: 0 on success, <0 on error.
2685  */
fdisk_dos_enable_compatible(struct fdisk_label * lb,int enable)2686 int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable)
2687 {
2688 	struct fdisk_dos_label *dos = (struct fdisk_dos_label *) lb;
2689 
2690 	if (!lb)
2691 		return -EINVAL;
2692 
2693 	dos->compatible = enable;
2694 	if (enable)
2695 		lb->flags |= FDISK_LABEL_FL_REQUIRE_GEOMETRY;
2696 	return 0;
2697 }
2698 
2699 /**
2700  * fdisk_dos_is_compatible:
2701  * @lb: DOS label
2702  *
2703  * Returns: 0 if DOS compatibility disabled, 1 if enabled
2704  */
fdisk_dos_is_compatible(struct fdisk_label * lb)2705 int fdisk_dos_is_compatible(struct fdisk_label *lb)
2706 {
2707 	return ((struct fdisk_dos_label *) lb)->compatible;
2708 }
2709