xref: /onnv-gate/usr/src/lib/libparted/common/libparted/labels/dos.c (revision 9663:ace9a2ac3683)
1 /*
2     libparted - a library for manipulating disk partitions
3     Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007
4     Free Software Foundation, Inc.
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 3 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License
17     along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include <config.h>
21 
22 #include <sys/time.h>
23 #include <stdbool.h>
24 #include <parted/parted.h>
25 #include <parted/debug.h>
26 #include <parted/endian.h>
27 
28 #if ENABLE_NLS
29 #  include <libintl.h>
30 #  define _(String) dgettext (PACKAGE, String)
31 #else
32 #  define _(String) (String)
33 #endif /* ENABLE_NLS */
34 
35 /* this MBR boot code is loaded into 0000:7c00 by the BIOS.  See mbr.s for
36  * the source, and how to build it
37  */
38 
39 static const unsigned char MBR_BOOT_CODE[] = {
40 	0xfa, 0xb8, 0x00, 0x10, 0x8e, 0xd0, 0xbc, 0x00,
41 	0xb0, 0xb8, 0x00, 0x00, 0x8e, 0xd8, 0x8e, 0xc0,
42 	0xfb, 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x06, 0xb9,
43 	0x00, 0x02, 0xf3, 0xa4, 0xea, 0x21, 0x06, 0x00,
44 	0x00, 0xbe, 0xbe, 0x07, 0x38, 0x04, 0x75, 0x0b,
45 	0x83, 0xc6, 0x10, 0x81, 0xfe, 0xfe, 0x07, 0x75,
46 	0xf3, 0xeb, 0x16, 0xb4, 0x02, 0xb0, 0x01, 0xbb,
47 	0x00, 0x7c, 0xb2, 0x80, 0x8a, 0x74, 0x01, 0x8b,
48 	0x4c, 0x02, 0xcd, 0x13, 0xea, 0x00, 0x7c, 0x00,
49 	0x00, 0xeb, 0xfe
50 };
51 
52 #define MSDOS_MAGIC		0xAA55
53 #define PARTITION_MAGIC_MAGIC	0xf6f6
54 
55 #define PARTITION_EMPTY		0x00
56 #define PARTITION_FAT12		0x01
57 #define PARTITION_FAT16_SM	0x04
58 #define PARTITION_DOS_EXT	0x05
59 #define PARTITION_FAT16		0x06
60 #define PARTITION_NTFS		0x07
61 #define PARTITION_HPFS		0x07
62 #define PARTITION_FAT32		0x0b
63 #define PARTITION_FAT32_LBA	0x0c
64 #define PARTITION_FAT16_LBA	0x0e
65 #define PARTITION_EXT_LBA	0x0f
66 
67 #define PART_FLAG_HIDDEN	0x10	/* Valid for FAT/NTFS only */
68 #define PARTITION_FAT12_H	(PARTITION_FAT12	| PART_FLAG_HIDDEN)
69 #define PARTITION_FAT16_SM_H	(PARTITION_FAT16_SM	| PART_FLAG_HIDDEN)
70 #define PARTITION_DOS_EXT_H	(PARTITION_DOS_EXT	| PART_FLAG_HIDDEN)
71 #define PARTITION_FAT16_H	(PARTITION_FAT16	| PART_FLAG_HIDDEN)
72 #define PARTITION_NTFS_H	(PARTITION_NTFS		| PART_FLAG_HIDDEN)
73 #define PARTITION_FAT32_H	(PARTITION_FAT32	| PART_FLAG_HIDDEN)
74 #define PARTITION_FAT32_LBA_H	(PARTITION_FAT32_LBA	| PART_FLAG_HIDDEN)
75 #define PARTITION_FAT16_LBA_H	(PARTITION_FAT16_LBA	| PART_FLAG_HIDDEN)
76 
77 #define PARTITION_COMPAQ_DIAG	0x12
78 #define PARTITION_LDM		0x42
79 #define PARTITION_LINUX_SWAP	0x82
80 #define PARTITION_LINUX		0x83
81 #define PARTITION_LINUX_EXT	0x85
82 #define PARTITION_LINUX_LVM	0x8e
83 #define PARTITION_SUN_UFS	0xbf
84 #define PARTITION_DELL_DIAG	0xde
85 #define PARTITION_GPT		0xee
86 #define PARTITION_PALO		0xf0
87 #define PARTITION_PREP		0x41
88 #define PARTITION_LINUX_RAID	0xfd
89 #define PARTITION_LINUX_LVM_OLD 0xfe
90 
91 /* This constant contains the maximum cylinder number that can be represented
92  * in (C,H,S) notation.  Higher cylinder numbers are reserved for
93  * "too big" indicators (in which case only LBA addressing can be used).
94  * 	Some partition tables in the wild indicate this number is 1021.
95  * (i.e. 1022 is sometimes used to indicate "use LBA").
96  */
97 #define MAX_CHS_CYLINDER	1021
98 
99 typedef struct _DosRawPartition		DosRawPartition;
100 typedef struct _DosRawTable		DosRawTable;
101 
102 #ifdef __sun
103 #define __attribute__(X)	/*nothing*/
104 #endif /* __sun */
105 
106 /* note: lots of bit-bashing here, thus, you shouldn't look inside it.
107  * Use chs_to_sector() and sector_to_chs() instead.
108  */
109 #ifdef __sun
110 #pragma pack(1)
111 #endif
112 typedef struct {
113 	uint8_t		head;
114 	uint8_t		sector;
115 	uint8_t		cylinder;
116 } __attribute__((packed)) RawCHS;
117 
118 /* ripped from Linux source */
119 struct _DosRawPartition {
120         uint8_t		boot_ind;	/* 00:  0x80 - active */
121 	RawCHS		chs_start;	/* 01: */
122 	uint8_t		type;		/* 04: partition type */
123 	RawCHS		chs_end;	/* 05: */
124 	uint32_t	start;		/* 08: starting sector counting from 0 */
125 	uint32_t	length;		/* 0c: nr of sectors in partition */
126 } __attribute__((packed));
127 
128 struct _DosRawTable {
129 	char			boot_code [440];
130 	uint32_t                mbr_signature;	/* really a unique ID */
131 	uint16_t                Unknown;
132 	DosRawPartition		partitions [4];
133 	uint16_t		magic;
134 } __attribute__((packed));
135 #ifdef __sun
136 #pragma pack()
137 #endif
138 
139 
140 /* OrigState is information we want to preserve about the partition for
141  * dealing with CHS issues
142  */
143 typedef struct {
144 	PedGeometry	geom;
145 	DosRawPartition	raw_part;
146 	PedSector	lba_offset;	/* needed for computing start/end for
147 					 * logical partitions */
148 } OrigState;
149 
150 typedef struct {
151 	unsigned char	system;
152 	int		boot;
153 	int		hidden;
154 	int		raid;
155 	int		lvm;
156 	int		lba;
157 	int		palo;
158 	int		prep;
159 	OrigState*	orig;			/* used for CHS stuff */
160 } DosPartitionData;
161 
162 static PedDiskType msdos_disk_type;
163 
164 /* FIXME: factor out this function: copied from aix.c, with changes to
165    the description, and an added sector number argument.
166    Read sector, SECTOR_NUM (which has length DEV->sector_size) into malloc'd
167    storage.  If the read fails, free the memory and return zero without
168    modifying *BUF.  Otherwise, set *BUF to the new buffer and return 1.  */
169 static int
read_sector(const PedDevice * dev,PedSector sector_num,char ** buf)170 read_sector (const PedDevice *dev, PedSector sector_num, char **buf)
171 {
172 	char *b = ped_malloc (dev->sector_size);
173 	PED_ASSERT (b != NULL, return 0);
174 	if (!ped_device_read (dev, b, sector_num, 1)) {
175 		ped_free (b);
176 		return 0;
177 	}
178 	*buf = b;
179 	return 1;
180 }
181 
182 static int
msdos_probe(const PedDevice * dev)183 msdos_probe (const PedDevice *dev)
184 {
185 	PedDiskType*	disk_type;
186 	DosRawTable*	part_table;
187 	int		i;
188 
189 	PED_ASSERT (dev != NULL, return 0);
190 
191         if (dev->sector_size < sizeof *part_table)
192                 return 0;
193 
194 	char *label;
195 	if (!read_sector (dev, 0, &label))
196 		return 0;
197 
198 	part_table = (DosRawTable *) label;
199 
200 	/* check magic */
201 	if (PED_LE16_TO_CPU (part_table->magic) != MSDOS_MAGIC)
202 		goto probe_fail;
203 
204 	/* if this is a FAT fs, fail here.  Note that the Smart Boot Manager
205 	 * Loader (SBML) signature indicates a partition table, not a file
206 	 * system.
207 	 */
208 	if ((!strncmp (part_table->boot_code + 0x36, "FAT", 3)
209 	    && strncmp (part_table->boot_code + 0x40, "SBML", 4) != 0)
210 	    || !strncmp (part_table->boot_code + 0x52, "FAT", 3))
211 		goto probe_fail;
212 
213 	/* If this is a GPT disk, fail here */
214 	for (i = 0; i < 4; i++) {
215 		if (part_table->partitions[i].type == PARTITION_GPT)
216 			goto probe_fail;
217 	}
218 
219 	/* If this is an AIX Physical Volume, fail here.  IBMA in EBCDIC */
220 	if (part_table->boot_code[0] == (char) 0xc9 &&
221 	    part_table->boot_code[1] == (char) 0xc2 &&
222 	    part_table->boot_code[2] == (char) 0xd4 &&
223 	    part_table->boot_code[3] == (char) 0xc1)
224 		goto probe_fail;
225 
226 #ifdef ENABLE_PC98
227 	/* HACK: it's impossible to tell PC98 and msdos disk labels apart.
228 	 * Someone made the signatures the same (very clever).  Since
229 	 * PC98 has some idiosyncracies with it's boot-loader, it's detection
230 	 * is more reliable */
231 	disk_type = ped_disk_type_get ("pc98");
232 	if (disk_type && disk_type->ops->probe (dev))
233 		goto probe_fail;
234 #endif /* ENABLE_PC98 */
235 
236 	free (label);
237 	return 1;
238 
239  probe_fail:
240 	free (label);
241 	return 0;
242 }
243 
244 static PedDisk*
msdos_alloc(const PedDevice * dev)245 msdos_alloc (const PedDevice* dev)
246 {
247 	PedDisk* disk;
248 	PED_ASSERT (dev != NULL, return NULL);
249 
250 	disk = _ped_disk_alloc ((PedDevice*)dev, &msdos_disk_type);
251 	if (disk)
252 		disk->disk_specific = NULL;
253 	return disk;
254 }
255 
256 static PedDisk*
msdos_duplicate(const PedDisk * disk)257 msdos_duplicate (const PedDisk* disk)
258 {
259 	PedDisk*	new_disk;
260 
261 	new_disk = ped_disk_new_fresh (disk->dev, &msdos_disk_type);
262 	if (!new_disk)
263 		return NULL;
264 	new_disk->disk_specific = NULL;
265 	return new_disk;
266 }
267 
268 static void
msdos_free(PedDisk * disk)269 msdos_free (PedDisk* disk)
270 {
271 	PED_ASSERT (disk != NULL, return);
272 
273 	_ped_disk_free (disk);
274 }
275 
276 #ifndef DISCOVER_ONLY
277 static int
msdos_clobber(PedDevice * dev)278 msdos_clobber (PedDevice* dev)
279 {
280 	DosRawTable		table;
281 
282 	PED_ASSERT (dev != NULL, return 0);
283 	PED_ASSERT (msdos_probe (dev), return 0);
284 
285 	if (!ped_device_read (dev, &table, 0, 1))
286 		return 0;
287 	table.magic = 0;
288 	return ped_device_write (dev, (void*) &table, 0, 1);
289 }
290 #endif /* !DISCOVER_ONLY */
291 
292 static int
chs_get_cylinder(const RawCHS * chs)293 chs_get_cylinder (const RawCHS* chs)
294 {
295 	return chs->cylinder + ((chs->sector >> 6) << 8);
296 }
297 
298 static int
chs_get_head(const RawCHS * chs)299 chs_get_head (const RawCHS* chs)
300 {
301 	return chs->head;
302 }
303 
304 /* counts from 0 */
305 static int
chs_get_sector(const RawCHS * chs)306 chs_get_sector (const RawCHS* chs)
307 {
308 	return (chs->sector & 0x3f) - 1;
309 }
310 
311 static PedSector
chs_to_sector(const PedDevice * dev,const PedCHSGeometry * bios_geom,const RawCHS * chs)312 chs_to_sector (const PedDevice* dev, const PedCHSGeometry *bios_geom,
313 	       const RawCHS* chs)
314 {
315 	PedSector	c;		/* not measured in sectors, but need */
316 	PedSector	h;		/* lots of bits */
317 	PedSector	s;
318 
319 	PED_ASSERT (bios_geom != NULL, return 0);
320 	PED_ASSERT (chs != NULL, return 0);
321 
322 	c = chs_get_cylinder (chs);
323 	h = chs_get_head (chs);
324 	s = chs_get_sector (chs);
325 
326 	if (c > MAX_CHS_CYLINDER)		/* MAGIC: C/H/S is irrelevant */
327 		return 0;
328 	if (s < 0)
329 		return 0;
330 	return ((c * bios_geom->heads + h) * bios_geom->sectors + s)
331 		* (dev->sector_size / 512);
332 }
333 
334 static void
sector_to_chs(const PedDevice * dev,const PedCHSGeometry * bios_geom,PedSector sector,RawCHS * chs)335 sector_to_chs (const PedDevice* dev, const PedCHSGeometry* bios_geom,
336 	       PedSector sector, RawCHS* chs)
337 {
338 	PedSector	real_c, real_h, real_s;
339 
340 	PED_ASSERT (dev != NULL, return);
341 	PED_ASSERT (chs != NULL, return);
342 
343 	if (!bios_geom)
344 		bios_geom = &dev->bios_geom;
345 
346 	sector /= (dev->sector_size / 512);
347 
348 	real_c = sector / (bios_geom->heads * bios_geom->sectors);
349 	real_h = (sector / bios_geom->sectors) % bios_geom->heads;
350 	real_s = sector % bios_geom->sectors;
351 
352 	if (real_c > MAX_CHS_CYLINDER) {
353 		real_c = 1023;
354 		real_h = bios_geom->heads - 1;
355 		real_s = bios_geom->sectors - 1;
356 	}
357 
358 	chs->cylinder = real_c % 0x100;
359 	chs->head = real_h;
360 	chs->sector = real_s + 1 + (real_c >> 8 << 6);
361 }
362 
363 static PedSector
legacy_start(const PedDisk * disk,const PedCHSGeometry * bios_geom,const DosRawPartition * raw_part)364 legacy_start (const PedDisk* disk, const PedCHSGeometry* bios_geom,
365 	      const DosRawPartition* raw_part)
366 {
367 	PED_ASSERT (disk != NULL, return 0);
368 	PED_ASSERT (raw_part != NULL, return 0);
369 
370 	return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_start);
371 }
372 
373 static PedSector
legacy_end(const PedDisk * disk,const PedCHSGeometry * bios_geom,const DosRawPartition * raw_part)374 legacy_end (const PedDisk* disk, const PedCHSGeometry* bios_geom,
375 	    const DosRawPartition* raw_part)
376 {
377 	PED_ASSERT (disk != NULL, return 0);
378 	PED_ASSERT (raw_part != NULL, return 0);
379 
380 	return chs_to_sector (disk->dev, bios_geom, &raw_part->chs_end);
381 }
382 
383 static PedSector
linear_start(const PedDisk * disk,const DosRawPartition * raw_part,PedSector offset)384 linear_start (const PedDisk* disk, const DosRawPartition* raw_part,
385 	      PedSector offset)
386 {
387 	PED_ASSERT (disk != NULL, return 0);
388 	PED_ASSERT (raw_part != NULL, return 0);
389 
390 	return offset
391 	       + PED_LE32_TO_CPU (raw_part->start)
392 	       	 	* (disk->dev->sector_size / 512);
393 }
394 
395 static PedSector
linear_end(const PedDisk * disk,const DosRawPartition * raw_part,PedSector offset)396 linear_end (const PedDisk* disk, const DosRawPartition* raw_part,
397 	    PedSector offset)
398 {
399 	PED_ASSERT (disk != NULL, return 0);
400 	PED_ASSERT (raw_part != NULL, return 0);
401 
402 	return linear_start (disk, raw_part, offset)
403 	       + (PED_LE32_TO_CPU (raw_part->length) - 1)
404 	       	 	* (disk->dev->sector_size / 512);
405 }
406 
407 #ifndef DISCOVER_ONLY
408 static int
partition_check_bios_geometry(PedPartition * part,PedCHSGeometry * bios_geom)409 partition_check_bios_geometry (PedPartition* part, PedCHSGeometry* bios_geom)
410 {
411 	PedSector		leg_start, leg_end;
412 	DosPartitionData*	dos_data;
413 	PedDisk*		disk;
414 
415 	PED_ASSERT (part != NULL, return 0);
416 	PED_ASSERT (part->disk != NULL, return 0);
417 	PED_ASSERT (part->disk_specific != NULL, return 0);
418 	dos_data = part->disk_specific;
419 
420 	if (!dos_data->orig)
421 		return 1;
422 
423 	disk = part->disk;
424 	leg_start = legacy_start (disk, bios_geom, &dos_data->orig->raw_part);
425 	leg_end = legacy_end (disk, bios_geom, &dos_data->orig->raw_part);
426 
427 	if (leg_start && leg_start != dos_data->orig->geom.start)
428 		return 0;
429 	if (leg_end && leg_end != dos_data->orig->geom.end)
430 		return 0;
431 	return 1;
432 }
433 
434 static int
disk_check_bios_geometry(const PedDisk * disk,PedCHSGeometry * bios_geom)435 disk_check_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom)
436 {
437 	PedPartition* part = NULL;
438 
439 	PED_ASSERT (disk != NULL, return 0);
440 
441 	while ((part = ped_disk_next_partition (disk, part))) {
442 		if (ped_partition_is_active (part)) {
443 			if (!partition_check_bios_geometry (part, bios_geom))
444 				return 0;
445 		}
446 	}
447 
448 	return 1;
449 }
450 
451 static int
probe_filesystem_for_geom(const PedPartition * part,PedCHSGeometry * bios_geom)452 probe_filesystem_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom)
453 {
454 	const char* ms_types[] = {"ntfs", "fat16", "fat32", NULL};
455 	int i;
456 	int found;
457 	unsigned char* buf;
458 	int sectors;
459 	int heads;
460 	int res = 0;
461 
462 	PED_ASSERT (bios_geom        != NULL, return 0);
463         PED_ASSERT (part             != NULL, return 0);
464         PED_ASSERT (part->disk       != NULL, return 0);
465         PED_ASSERT (part->disk->dev  != NULL, return 0);
466         PED_ASSERT (part->disk->dev->sector_size % PED_SECTOR_SIZE_DEFAULT == 0,
467                     return 0);
468 
469         buf = ped_malloc (part->disk->dev->sector_size);
470 
471 	if (!buf)
472 		return 0;
473 
474 	if (!part->fs_type)
475 		goto end;
476 
477 	found = 0;
478 	for (i = 0; ms_types[i]; i++) {
479 		if (!strcmp(ms_types[i], part->fs_type->name))
480 			found = 1;
481 	}
482 	if (!found)
483 		goto end;
484 
485 	if (!ped_geometry_read(&part->geom, buf, 0, 1))
486 		goto end;
487 
488 	/* shared by the start of all Microsoft file systems */
489 	sectors = buf[0x18] + (buf[0x19] << 8);
490 	heads = buf[0x1a] + (buf[0x1b] << 8);
491 
492 	if (sectors < 1 || sectors > 63)
493 		goto end;
494 	if (heads > 255 || heads < 1)
495 		goto end;
496 
497 	bios_geom->sectors = sectors;
498 	bios_geom->heads = heads;
499 	bios_geom->cylinders = part->disk->dev->length / (sectors * heads);
500 	res = 1;
501 end:
502 	ped_free(buf);
503 	return res;
504 }
505 
506 /* This function attempts to infer the BIOS CHS geometry of the hard disk
507  * from the CHS + LBA information contained in the partition table from
508  * a single partition's entry.
509  *
510  * This involves some maths.  Let (c,h,s,a) be the starting cylinder,
511  * starting head, starting sector and LBA start address of the partition.
512  * Likewise, (C,H,S,A) the end addresses.  Using both of these pieces
513  * of information, we want to deduce cyl_sectors and head_sectors which
514  * are the sizes of a single cylinder and a single head, respectively.
515  *
516  * The relationships are:
517  * c*cyl_sectors + h * head_sectors + s = a
518  * C*cyl_sectors + H * head_sectors + S = A
519  *
520  * We can rewrite this in matrix form:
521  *
522  * [ c h ] [ cyl_sectors  ]  =  [ s - a ]  =  [ a_ ]
523  * [ C H ] [ head_sectors ]     [ S - A ]     [ A_ ].
524  *
525  * (s - a is abbreviated to a_to simplify the notation.)
526  *
527  * This can be abbreviated into augmented matrix form:
528  *
529  * [ c h | a_ ]
530  * [ C H | A_ ].
531  *
532  * Solving these equations requires following the row reduction algorithm.  We
533  * need to be careful about a few things though:
534  * 	- the equations might be linearly dependent, in which case there
535  * 	are many solutions.
536  * 	- the equations might be inconsistent, in which case there
537  * 	are no solutions.  (Inconsistent partition table entry!)
538  * 	- there might be zeros, so we need to be careful about applying
539  * 	the algorithm.  We know, however, that C > 0.
540  */
541 static int
probe_partition_for_geom(const PedPartition * part,PedCHSGeometry * bios_geom)542 probe_partition_for_geom (const PedPartition* part, PedCHSGeometry* bios_geom)
543 {
544 	DosPartitionData* dos_data;
545 	RawCHS* start_chs;
546 	RawCHS* end_chs;
547 	PedSector c, h, s, a, a_;	/* start */
548 	PedSector C, H, S, A, A_;	/* end */
549 	PedSector dont_overflow, denum;
550 	PedSector cyl_size, head_size;
551 	PedSector cylinders, heads, sectors;
552 
553 	PED_ASSERT (part != NULL, return 0);
554 	PED_ASSERT (part->disk_specific != NULL, return 0);
555 	PED_ASSERT (bios_geom != NULL, return 0);
556 
557 	dos_data = part->disk_specific;
558 
559 	if (!dos_data->orig)
560 		return 0;
561 
562 	start_chs = &dos_data->orig->raw_part.chs_start;
563 	c = chs_get_cylinder (start_chs);
564 	h = chs_get_head (start_chs);
565 	s = chs_get_sector (start_chs);
566 	a = dos_data->orig->geom.start;
567 	a_ = a - s;
568 
569 	end_chs = &dos_data->orig->raw_part.chs_end;
570 	C = chs_get_cylinder (end_chs);
571 	H = chs_get_head (end_chs);
572 	S = chs_get_sector (end_chs);
573 	A = dos_data->orig->geom.end;
574 	A_ = A - S;
575 
576 	if (h < 0 || H < 0 || h > 254 || H > 254)
577 		return 0;
578 	if (c > C)
579 		return 0;
580 
581 	/* If no geometry is feasible, then don't even bother.
582 	 * Useful for eliminating assertions for broken partition
583 	 * tables generated by Norton Ghost et al.
584 	 */
585 	if (A > (C+1) * 255 * 63)
586 		return 0;
587 
588 	/* Not enough information.  In theory, we can do better.  Should we? */
589 	if (C > MAX_CHS_CYLINDER)
590 		return 0;
591 	if (C == 0)
592 		return 0;
593 
594 	/* Calculate the maximum number that can be multiplied by
595 	 * any head count without overflowing a PedSector
596 	 * 2^8 = 256, 8 bits + 1(sign bit) = 9
597 	 */
598 	dont_overflow = 1;
599 	dont_overflow <<= (8*sizeof(dont_overflow)) - 9;
600 	dont_overflow--;
601 
602 	if (a_ > dont_overflow || A_ > dont_overflow)
603 		return 0;
604 
605 	/* The matrix is solved by :
606 	 *
607 	 * [ c h | a_]			R1
608 	 * [ C H | A_]			R2
609 	 *
610 	 * (cH - Ch) cyl_size = a_H - A_h		H R1 - h R2
611 	 * => (if cH - Ch != 0) cyl_size = (a_H - A_h) / (cH - Ch)
612 	 *
613 	 * (Hc - hC) head_size = A_c - a_C		c R2 - C R1
614 	 * => (if cH - Ch != 0) head_size = (A_c - a_C) / (cH - Ch)
615 	 *
616 	 *   But this calculation of head_size would need
617 	 *   not overflowing A_c or a_C
618 	 *   So substitution is use instead, to minimize dimension
619 	 *   of temporary results :
620 	 *
621 	 * If h != 0 : head_size = ( a_ - c cyl_size ) / h
622 	 * If H != 0 : head_size = ( A_ - C cyl_size ) / H
623 	 *
624 	 */
625 	denum = c * H - C * h;
626 	if (denum == 0)
627 		return 0;
628 
629 	cyl_size = (a_*H - A_*h) / denum;
630 	/* Check for non integer result */
631 	if (cyl_size * denum != a_*H - A_*h)
632 		return 0;
633 
634 	PED_ASSERT (cyl_size > 0, return 0);
635  	PED_ASSERT (cyl_size <= 255 * 63, return 0);
636 
637 	if (h > 0)
638 		head_size = ( a_ - c * cyl_size ) / h;
639 	else if (H > 0)
640 		head_size = ( A_ - C * cyl_size ) / H;
641 	else {
642 		/* should not happen because denum != 0 */
643 		head_size = 0;
644 		PED_ASSERT (0, return 0);
645 	}
646 
647 	PED_ASSERT (head_size > 0, return 0);
648 	PED_ASSERT (head_size <= 63, return 0);
649 
650 	cylinders = part->disk->dev->length / cyl_size;
651 	heads = cyl_size / head_size;
652 	sectors = head_size;
653 
654 	PED_ASSERT (heads > 0, return 0);
655 	PED_ASSERT (heads < 256, return 0);
656 
657 	PED_ASSERT (sectors > 0, return 0);
658 	PED_ASSERT (sectors <= 63, return 0);
659 
660 	/* Some broken OEM partitioning program(s) seem to have an out-by-one
661 	 * error on the end of partitions.  We should offer to fix the
662 	 * partition table...
663 	 */
664 	if (((C + 1) * heads + H) * sectors + S == A)
665 		C++;
666 
667 	PED_ASSERT ((c * heads + h) * sectors + s == a, return 0);
668 	PED_ASSERT ((C * heads + H) * sectors + S == A, return 0);
669 
670 	bios_geom->cylinders = cylinders;
671 	bios_geom->heads = heads;
672 	bios_geom->sectors = sectors;
673 
674 	return 1;
675 }
676 
677 static void
partition_probe_bios_geometry(const PedPartition * part,PedCHSGeometry * bios_geom)678 partition_probe_bios_geometry (const PedPartition* part,
679                                PedCHSGeometry* bios_geom)
680 {
681 	PED_ASSERT (part != NULL, return);
682 	PED_ASSERT (part->disk != NULL, return);
683 	PED_ASSERT (bios_geom != NULL, return);
684 
685 	if (ped_partition_is_active (part)) {
686 		if (probe_partition_for_geom (part, bios_geom))
687 			return;
688 		if (part->type & PED_PARTITION_EXTENDED) {
689 			if (probe_filesystem_for_geom (part, bios_geom))
690 				return;
691 		}
692 	}
693 	if (part->type & PED_PARTITION_LOGICAL) {
694 		PedPartition* ext_part;
695 		ext_part = ped_disk_extended_partition (part->disk);
696 		PED_ASSERT (ext_part != NULL, return);
697 		partition_probe_bios_geometry (ext_part, bios_geom);
698 	} else {
699 		*bios_geom = part->disk->dev->bios_geom;
700 	}
701 }
702 
703 static void
disk_probe_bios_geometry(const PedDisk * disk,PedCHSGeometry * bios_geom)704 disk_probe_bios_geometry (const PedDisk* disk, PedCHSGeometry* bios_geom)
705 {
706 	PedPartition*	part;
707 
708 	/* first look at the boot partition */
709 	part = NULL;
710 	while ((part = ped_disk_next_partition (disk, part))) {
711 		if (!ped_partition_is_active (part))
712 			continue;
713 		if (ped_partition_get_flag (part, PED_PARTITION_BOOT)) {
714 			if (probe_filesystem_for_geom (part, bios_geom))
715 				return;
716 			if (probe_partition_for_geom (part, bios_geom))
717 				return;
718 		}
719 	}
720 
721 	/* that didn't work... try all partition table entries */
722 	part = NULL;
723 	while ((part = ped_disk_next_partition (disk, part))) {
724 		if (ped_partition_is_active (part)) {
725 			if (probe_partition_for_geom (part, bios_geom))
726 				return;
727 		}
728 	}
729 
730 	/* that didn't work... look at all file systems */
731 	part = NULL;
732 	while ((part = ped_disk_next_partition (disk, part))) {
733 		if (ped_partition_is_active (part)) {
734 			if (probe_filesystem_for_geom (part, bios_geom))
735 				return;
736 		}
737 	}
738 }
739 #endif /* !DISCOVER_ONLY */
740 
741 static int
raw_part_is_extended(const DosRawPartition * raw_part)742 raw_part_is_extended (const DosRawPartition* raw_part)
743 {
744 	PED_ASSERT (raw_part != NULL, return 0);
745 
746 	switch (raw_part->type) {
747 	case PARTITION_DOS_EXT:
748 	case PARTITION_EXT_LBA:
749 	case PARTITION_LINUX_EXT:
750 		return 1;
751 
752 	default:
753 		return 0;
754 	}
755 
756 	return 0;
757 }
758 
759 static int
raw_part_is_hidden(const DosRawPartition * raw_part)760 raw_part_is_hidden (const DosRawPartition* raw_part)
761 {
762 	PED_ASSERT (raw_part != NULL, return 0);
763 
764 	switch (raw_part->type) {
765 	case PARTITION_FAT12_H:
766 	case PARTITION_FAT16_SM_H:
767 	case PARTITION_FAT16_H:
768 	case PARTITION_FAT32_H:
769 	case PARTITION_NTFS_H:
770 	case PARTITION_FAT32_LBA_H:
771 	case PARTITION_FAT16_LBA_H:
772 		return 1;
773 
774 	default:
775 		return 0;
776 	}
777 
778 	return 0;
779 }
780 
781 static int
raw_part_is_lba(const DosRawPartition * raw_part)782 raw_part_is_lba (const DosRawPartition* raw_part)
783 {
784 	PED_ASSERT (raw_part != NULL, return 0);
785 
786 	switch (raw_part->type) {
787 	case PARTITION_FAT32_LBA:
788 	case PARTITION_FAT16_LBA:
789 	case PARTITION_EXT_LBA:
790 	case PARTITION_FAT32_LBA_H:
791 	case PARTITION_FAT16_LBA_H:
792 		return 1;
793 
794 	default:
795 		return 0;
796 	}
797 
798 	return 0;
799 }
800 
801 static PedPartition*
raw_part_parse(const PedDisk * disk,const DosRawPartition * raw_part,PedSector lba_offset,PedPartitionType type)802 raw_part_parse (const PedDisk* disk, const DosRawPartition* raw_part,
803 	        PedSector lba_offset, PedPartitionType type)
804 {
805 	PedPartition* part;
806 	DosPartitionData* dos_data;
807 
808 	PED_ASSERT (disk != NULL, return NULL);
809 	PED_ASSERT (raw_part != NULL, return NULL);
810 
811 	part = ped_partition_new (
812 		disk, type, NULL,
813 		linear_start (disk, raw_part, lba_offset),
814 		linear_end (disk, raw_part, lba_offset));
815 	if (!part)
816 		return NULL;
817 	dos_data = part->disk_specific;
818 	dos_data->system = raw_part->type;
819 	dos_data->boot = raw_part->boot_ind != 0;
820 	dos_data->hidden = raw_part_is_hidden (raw_part);
821 	dos_data->raid = raw_part->type == PARTITION_LINUX_RAID;
822 	dos_data->lvm = raw_part->type == PARTITION_LINUX_LVM_OLD
823 			|| raw_part->type == PARTITION_LINUX_LVM;
824 	dos_data->lba = raw_part_is_lba (raw_part);
825 	dos_data->palo = raw_part->type == PARTITION_PALO;
826 	dos_data->prep = raw_part->type == PARTITION_PREP;
827 	dos_data->orig = ped_malloc (sizeof (OrigState));
828 	if (!dos_data->orig) {
829 		ped_partition_destroy (part);
830 		return NULL;
831 	}
832 	dos_data->orig->geom = part->geom;
833 	dos_data->orig->raw_part = *raw_part;
834 	dos_data->orig->lba_offset = lba_offset;
835 	return part;
836 }
837 
838 static int
read_table(PedDisk * disk,PedSector sector,int is_extended_table)839 read_table (PedDisk* disk, PedSector sector, int is_extended_table)
840 {
841 	int			i;
842 	DosRawTable*		table;
843 	DosRawPartition*	raw_part;
844 	PedPartition*		part;
845 	PedPartitionType	type;
846 	PedSector		lba_offset;
847 	PedConstraint*		constraint_exact;
848 
849 	PED_ASSERT (disk != NULL, return 0);
850 	PED_ASSERT (disk->dev != NULL, return 0);
851 
852 	char *label = NULL;
853 	if (!read_sector (disk->dev, sector, &label))
854 		goto error;
855 
856         table = (DosRawTable *) label;
857 
858 	/* weird: empty extended partitions are filled with 0xf6 by PM */
859 	if (is_extended_table
860 	    && PED_LE16_TO_CPU (table->magic) == PARTITION_MAGIC_MAGIC)
861 		goto read_ok;
862 
863 #ifndef DISCOVER_ONLY
864 	if (PED_LE16_TO_CPU (table->magic) != MSDOS_MAGIC) {
865 		if (ped_exception_throw (
866 			PED_EXCEPTION_ERROR, PED_EXCEPTION_IGNORE_CANCEL,
867 			_("Invalid partition table on %s "
868 			  "-- wrong signature %x."),
869 			disk->dev->path,
870 			PED_LE16_TO_CPU (table->magic))
871 				!= PED_EXCEPTION_IGNORE)
872 			goto error;
873 		goto read_ok;
874 	}
875 #endif
876 
877 	/* parse the partitions from this table */
878 	for (i = 0; i < 4; i++) {
879 		raw_part = &table->partitions [i];
880 		if (raw_part->type == PARTITION_EMPTY || !raw_part->length)
881 			continue;
882 
883 		/* process nested extended partitions after normal logical
884 		 * partitions, to make sure we get the order right.
885 		 */
886 		if (is_extended_table && raw_part_is_extended (raw_part))
887 			continue;
888 
889 		lba_offset = is_extended_table ? sector : 0;
890 
891 		if (linear_start (disk, raw_part, lba_offset) == sector) {
892 			if (ped_exception_throw (
893 				PED_EXCEPTION_ERROR,
894 				PED_EXCEPTION_IGNORE_CANCEL,
895 				_("Invalid partition table - recursive "
896 				"partition on %s."),
897 				disk->dev->path)
898 					!= PED_EXCEPTION_IGNORE)
899 				goto error;
900 			continue;	/* avoid infinite recursion */
901 		}
902 
903 		if (is_extended_table)
904 			type = PED_PARTITION_LOGICAL;
905 		else if (raw_part_is_extended (raw_part))
906 			type = PED_PARTITION_EXTENDED;
907 		else
908 			type = PED_PARTITION_NORMAL;
909 
910 		part = raw_part_parse (disk, raw_part, lba_offset, type);
911 		if (!part)
912 			goto error;
913 		if (!is_extended_table)
914 			part->num = i + 1;
915 		if (type != PED_PARTITION_EXTENDED)
916 			part->fs_type = ped_file_system_probe (&part->geom);
917 
918 		constraint_exact = ped_constraint_exact (&part->geom);
919 		if (!ped_disk_add_partition (disk, part, constraint_exact))
920 			goto error;
921 		ped_constraint_destroy (constraint_exact);
922 
923 		/* non-nested extended partition */
924 		if (part->type == PED_PARTITION_EXTENDED) {
925 			if (!read_table (disk, part->geom.start, 1))
926 				goto error;
927 		}
928 	}
929 
930 	if (is_extended_table) {
931 		/* process the nested extended partitions */
932 		for (i = 0; i < 4; i++) {
933 			PedSector part_start;
934 
935 			raw_part = &table->partitions [i];
936 			if (!raw_part_is_extended (raw_part))
937 				continue;
938 
939 			lba_offset = ped_disk_extended_partition
940 					(disk)->geom.start;
941 			part_start = linear_start (disk, raw_part, lba_offset);
942 			if (part_start == sector) {
943 				/* recursive table - already threw an
944 				 * exception above.
945 				 */
946 				continue;
947 			}
948 			if (!read_table (disk, part_start, 1))
949 				goto error;
950 		}
951 	}
952 
953 read_ok:
954 	free (label);
955 	return 1;
956 
957 error:
958 	free (label);
959 	ped_disk_delete_all (disk);
960 	return 0;
961 }
962 
963 static int
msdos_read(PedDisk * disk)964 msdos_read (PedDisk* disk)
965 {
966 	PED_ASSERT (disk != NULL, return 0);
967 	PED_ASSERT (disk->dev != NULL, return 0);
968 
969 	ped_disk_delete_all (disk);
970 	if (!read_table (disk, 0, 0))
971 		return 0;
972 
973 #ifndef DISCOVER_ONLY
974 	/* try to figure out the correct BIOS CHS values */
975 	if (!disk_check_bios_geometry (disk, &disk->dev->bios_geom)) {
976 		PedCHSGeometry bios_geom = disk->dev->bios_geom;
977 		disk_probe_bios_geometry (disk, &bios_geom);
978 
979 		/* if the geometry was wrong, then we should reread, to
980 		 * make sure the metadata is allocated in the right places.
981 		 */
982 		if (disk->dev->bios_geom.cylinders != bios_geom.cylinders
983 		    || disk->dev->bios_geom.heads != bios_geom.heads
984 		    || disk->dev->bios_geom.sectors != bios_geom.sectors) {
985 			disk->dev->bios_geom = bios_geom;
986 			return msdos_read (disk);
987 		}
988 	}
989 #endif
990 
991 	return 1;
992 }
993 
994 #ifndef DISCOVER_ONLY
995 static int
fill_raw_part(DosRawPartition * raw_part,const PedPartition * part,PedSector offset)996 fill_raw_part (DosRawPartition* raw_part,
997                const PedPartition* part, PedSector offset)
998 {
999 	DosPartitionData*	dos_data;
1000 	PedCHSGeometry		bios_geom;
1001 
1002 	PED_ASSERT (raw_part != NULL, return 0);
1003 	PED_ASSERT (part != NULL, return 0);
1004 
1005 	partition_probe_bios_geometry (part, &bios_geom);
1006 
1007 	dos_data = part->disk_specific;
1008 
1009 	raw_part->boot_ind = 0x80 * dos_data->boot;
1010 	raw_part->type = dos_data->system;
1011 	raw_part->start = PED_CPU_TO_LE32 ((part->geom.start - offset)
1012 				/ (part->disk->dev->sector_size / 512));
1013 	raw_part->length = PED_CPU_TO_LE32 (part->geom.length
1014 				/ (part->disk->dev->sector_size / 512));
1015 
1016 	sector_to_chs (part->disk->dev, &bios_geom, part->geom.start,
1017 		       &raw_part->chs_start);
1018 	sector_to_chs (part->disk->dev, &bios_geom, part->geom.end,
1019 		       &raw_part->chs_end);
1020 
1021 	if (dos_data->orig) {
1022 		DosRawPartition* orig_raw_part = &dos_data->orig->raw_part;
1023 		if (dos_data->orig->geom.start == part->geom.start)
1024 			raw_part->chs_start = orig_raw_part->chs_start;
1025 		if (dos_data->orig->geom.end == part->geom.end)
1026 			raw_part->chs_end = orig_raw_part->chs_end;
1027 	}
1028 
1029 	return 1;
1030 }
1031 
1032 static int
fill_ext_raw_part_geom(DosRawPartition * raw_part,const PedCHSGeometry * bios_geom,const PedGeometry * geom,PedSector offset)1033 fill_ext_raw_part_geom (DosRawPartition* raw_part,
1034                         const PedCHSGeometry* bios_geom,
1035 			const PedGeometry* geom, PedSector offset)
1036 {
1037 	PED_ASSERT (raw_part != NULL, return 0);
1038 	PED_ASSERT (geom != NULL, return 0);
1039 	PED_ASSERT (geom->dev != NULL, return 0);
1040 
1041 	raw_part->boot_ind = 0;
1042 	raw_part->type = PARTITION_DOS_EXT;
1043 	raw_part->start = PED_CPU_TO_LE32 ((geom->start - offset)
1044 				/ (geom->dev->sector_size / 512));
1045 	raw_part->length = PED_CPU_TO_LE32 (geom->length
1046 				/ (geom->dev->sector_size / 512));
1047 
1048 	sector_to_chs (geom->dev, bios_geom, geom->start, &raw_part->chs_start);
1049 	sector_to_chs (geom->dev, bios_geom, geom->start + geom->length - 1,
1050 		       &raw_part->chs_end);
1051 
1052 	return 1;
1053 }
1054 
1055 static int
write_ext_table(const PedDisk * disk,PedSector sector,const PedPartition * logical)1056 write_ext_table (const PedDisk* disk,
1057                  PedSector sector, const PedPartition* logical)
1058 {
1059 	DosRawTable		table;
1060 	PedPartition*		part;
1061 	PedSector		lba_offset;
1062 
1063 	PED_ASSERT (disk != NULL, return 0);
1064 	PED_ASSERT (ped_disk_extended_partition (disk) != NULL, return 0);
1065 	PED_ASSERT (logical != NULL, return 0);
1066 
1067 	lba_offset = ped_disk_extended_partition (disk)->geom.start;
1068 
1069 	memset (&table, 0, sizeof (DosRawTable));
1070 	table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
1071 
1072 	if (!fill_raw_part (&table.partitions[0], logical, sector))
1073 		return 0;
1074 
1075 	part = ped_disk_get_partition (disk, logical->num + 1);
1076 	if (part) {
1077 		PedGeometry*		geom;
1078 		PedCHSGeometry		bios_geom;
1079 
1080 		geom = ped_geometry_new (disk->dev, part->prev->geom.start,
1081 				part->geom.end - part->prev->geom.start + 1);
1082 		if (!geom)
1083 			return 0;
1084 		partition_probe_bios_geometry (part, &bios_geom);
1085 		fill_ext_raw_part_geom (&table.partitions[1], &bios_geom,
1086 				        geom, lba_offset);
1087 		ped_geometry_destroy (geom);
1088 
1089 		if (!write_ext_table (disk, part->prev->geom.start, part))
1090 			return 0;
1091 	}
1092 
1093 	return ped_device_write (disk->dev, (void*) &table, sector, 1);
1094 }
1095 
1096 static int
write_empty_table(const PedDisk * disk,PedSector sector)1097 write_empty_table (const PedDisk* disk, PedSector sector)
1098 {
1099 	DosRawTable		table;
1100 
1101 	PED_ASSERT (disk != NULL, return 0);
1102 
1103 	memset (&table, 0, sizeof (DosRawTable));
1104 	table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
1105 
1106 	return ped_device_write (disk->dev, (void*) &table, sector, 1);
1107 }
1108 
1109 /* Find the first logical partition, and write the partition table for it.
1110  */
1111 static int
write_extended_partitions(const PedDisk * disk)1112 write_extended_partitions (const PedDisk* disk)
1113 {
1114 	PedPartition*		ext_part;
1115 	PedPartition*		part;
1116 	PedCHSGeometry		bios_geom;
1117 
1118 	PED_ASSERT (disk != NULL, return 0);
1119 
1120 	ext_part = ped_disk_extended_partition (disk);
1121 	partition_probe_bios_geometry (ext_part, &bios_geom);
1122 	part = ped_disk_get_partition (disk, 5);
1123 	if (part)
1124 		return write_ext_table (disk, ext_part->geom.start, part);
1125 	else
1126 		return write_empty_table (disk, ext_part->geom.start);
1127 }
1128 
generate_random_id(void)1129 static inline uint32_t generate_random_id (void)
1130 {
1131 	struct timeval tv;
1132 	int rc;
1133 	rc = gettimeofday(&tv, NULL);
1134 	if (rc == -1)
1135 		return 0;
1136 	return (uint32_t)(tv.tv_usec & 0xFFFFFFFFUL);
1137 }
1138 
1139 static int
msdos_write(const PedDisk * disk)1140 msdos_write (const PedDisk* disk)
1141 {
1142 	DosRawTable		table;
1143 	PedPartition*		part;
1144 	int			i;
1145 
1146 	PED_ASSERT (disk != NULL, return 0);
1147 	PED_ASSERT (disk->dev != NULL, return 0);
1148 
1149 	ped_device_read (disk->dev, &table, 0, 1);
1150 
1151 	if (!table.boot_code[0]) {
1152 		memset (table.boot_code, 0, 512);
1153 		memcpy (table.boot_code, MBR_BOOT_CODE, sizeof (MBR_BOOT_CODE));
1154 	}
1155 
1156 	/* If there is no unique identifier, generate a random one */
1157 	if (!table.mbr_signature)
1158 		table.mbr_signature = generate_random_id();
1159 
1160 	memset (table.partitions, 0, sizeof (DosRawPartition) * 4);
1161 	table.magic = PED_CPU_TO_LE16 (MSDOS_MAGIC);
1162 
1163 	for (i=1; i<=4; i++) {
1164 		part = ped_disk_get_partition (disk, i);
1165 		if (!part)
1166 			continue;
1167 
1168 		if (!fill_raw_part (&table.partitions [i - 1], part, 0))
1169 			return 0;
1170 
1171 		if (part->type == PED_PARTITION_EXTENDED) {
1172 			if (!write_extended_partitions (disk))
1173 				return 0;
1174 		}
1175 	}
1176 
1177 	if (!ped_device_write (disk->dev, (void*) &table, 0, 1))
1178 		return 0;
1179 	return ped_device_sync (disk->dev);
1180 }
1181 #endif /* !DISCOVER_ONLY */
1182 
1183 static PedPartition*
msdos_partition_new(const PedDisk * disk,PedPartitionType part_type,const PedFileSystemType * fs_type,PedSector start,PedSector end)1184 msdos_partition_new (const PedDisk* disk, PedPartitionType part_type,
1185 		     const PedFileSystemType* fs_type,
1186 		     PedSector start, PedSector end)
1187 {
1188 	PedPartition*		part;
1189 	DosPartitionData*	dos_data;
1190 
1191 	part = _ped_partition_alloc (disk, part_type, fs_type, start, end);
1192 	if (!part)
1193 		goto error;
1194 
1195 	if (ped_partition_is_active (part)) {
1196 		part->disk_specific
1197 		       	= dos_data = ped_malloc (sizeof (DosPartitionData));
1198 		if (!dos_data)
1199 			goto error_free_part;
1200 		dos_data->orig = NULL;
1201 		dos_data->system = PARTITION_LINUX;
1202 		dos_data->hidden = 0;
1203 		dos_data->boot = 0;
1204 		dos_data->raid = 0;
1205 		dos_data->lvm = 0;
1206 		dos_data->lba = 0;
1207 		dos_data->palo = 0;
1208 		dos_data->prep = 0;
1209 	} else {
1210 		part->disk_specific = NULL;
1211 	}
1212 	return part;
1213 
1214 	ped_free (dos_data);
1215 error_free_part:
1216 	ped_free (part);
1217 error:
1218 	return 0;
1219 }
1220 
1221 static PedPartition*
msdos_partition_duplicate(const PedPartition * part)1222 msdos_partition_duplicate (const PedPartition* part)
1223 {
1224 	PedPartition*		new_part;
1225 	DosPartitionData*	new_dos_data;
1226 	DosPartitionData*	old_dos_data;
1227 
1228 	new_part = ped_partition_new (part->disk, part->type, part->fs_type,
1229 				      part->geom.start, part->geom.end);
1230 	if (!new_part)
1231 		return NULL;
1232 	new_part->num = part->num;
1233 
1234 	old_dos_data = (DosPartitionData*) part->disk_specific;
1235 	new_dos_data = (DosPartitionData*) new_part->disk_specific;
1236 	new_dos_data->system = old_dos_data->system;
1237 	new_dos_data->boot = old_dos_data->boot;
1238 	new_dos_data->hidden = old_dos_data->hidden;
1239 	new_dos_data->raid = old_dos_data->raid;
1240 	new_dos_data->lvm = old_dos_data->lvm;
1241 	new_dos_data->lba = old_dos_data->lba;
1242 	new_dos_data->palo = old_dos_data->palo;
1243 	new_dos_data->prep = old_dos_data->prep;
1244 
1245 	if (old_dos_data->orig) {
1246 		new_dos_data->orig = ped_malloc (sizeof (OrigState));
1247 		if (!new_dos_data->orig) {
1248 			ped_partition_destroy (new_part);
1249 			return NULL;
1250 		}
1251 		new_dos_data->orig->geom = old_dos_data->orig->geom;
1252 		new_dos_data->orig->raw_part = old_dos_data->orig->raw_part;
1253 		new_dos_data->orig->lba_offset = old_dos_data->orig->lba_offset;
1254 	}
1255 	return new_part;
1256 }
1257 
1258 static void
msdos_partition_destroy(PedPartition * part)1259 msdos_partition_destroy (PedPartition* part)
1260 {
1261 	PED_ASSERT (part != NULL, return);
1262 
1263 	if (ped_partition_is_active (part)) {
1264 		DosPartitionData* dos_data;
1265 		dos_data = (DosPartitionData*) part->disk_specific;
1266 		if (dos_data->orig)
1267 			ped_free (dos_data->orig);
1268 		ped_free (part->disk_specific);
1269 	}
1270 	ped_free (part);
1271 }
1272 
1273 static int
msdos_partition_set_system(PedPartition * part,const PedFileSystemType * fs_type)1274 msdos_partition_set_system (PedPartition* part,
1275 			    const PedFileSystemType* fs_type)
1276 {
1277 	DosPartitionData* dos_data = part->disk_specific;
1278 
1279 	part->fs_type = fs_type;
1280 
1281 	if (dos_data->hidden
1282 		    && fs_type
1283 		    && strncmp (fs_type->name, "fat", 3) != 0
1284 		    && strcmp (fs_type->name, "ntfs") != 0)
1285 		dos_data->hidden = 0;
1286 
1287 	if (part->type & PED_PARTITION_EXTENDED) {
1288 		dos_data->raid = 0;
1289 		dos_data->lvm = 0;
1290 		dos_data->palo = 0;
1291 		dos_data->prep = 0;
1292 		if (dos_data->lba)
1293 			dos_data->system = PARTITION_EXT_LBA;
1294 		else
1295 			dos_data->system = PARTITION_DOS_EXT;
1296 		return 1;
1297 	}
1298 
1299 	if (dos_data->lvm) {
1300 		dos_data->system = PARTITION_LINUX_LVM;
1301 		return 1;
1302 	}
1303 	if (dos_data->raid) {
1304 		dos_data->system = PARTITION_LINUX_RAID;
1305 		return 1;
1306 	}
1307 	if (dos_data->palo) {
1308 		dos_data->system = PARTITION_PALO;
1309 		return 1;
1310 	}
1311 	if (dos_data->prep) {
1312 		dos_data->system = PARTITION_PREP;
1313 		return 1;
1314 	}
1315 
1316 	if (!fs_type)
1317 		dos_data->system = PARTITION_LINUX;
1318 	else if (!strcmp (fs_type->name, "fat16")) {
1319 		dos_data->system = dos_data->lba
1320 				   ? PARTITION_FAT16_LBA : PARTITION_FAT16;
1321 		dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0;
1322 	} else if (!strcmp (fs_type->name, "fat32")) {
1323 		dos_data->system = dos_data->lba
1324 				   ? PARTITION_FAT32_LBA : PARTITION_FAT32;
1325 		dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0;
1326 	} else if (!strcmp (fs_type->name, "ntfs")
1327 		   || !strcmp (fs_type->name, "hpfs")) {
1328 		dos_data->system = PARTITION_NTFS;
1329 		dos_data->system |= dos_data->hidden ? PART_FLAG_HIDDEN : 0;
1330 	} else if (!strcmp (fs_type->name, "sun-ufs"))
1331 		dos_data->system = PARTITION_SUN_UFS;
1332 	else if (!strcmp (fs_type->name, "solaris"))
1333 		dos_data->system = PARTITION_SUN_UFS;
1334 	else if (!strcmp (fs_type->name, "linux-swap"))
1335 		dos_data->system = PARTITION_LINUX_SWAP;
1336 	else
1337 		dos_data->system = PARTITION_LINUX;
1338 
1339 	return 1;
1340 }
1341 
1342 static int
msdos_partition_set_flag(PedPartition * part,PedPartitionFlag flag,int state)1343 msdos_partition_set_flag (PedPartition* part,
1344                           PedPartitionFlag flag, int state)
1345 {
1346 	PedDisk*			disk;
1347 	PedPartition*			walk;
1348 	DosPartitionData*		dos_data;
1349 
1350 	PED_ASSERT (part != NULL, return 0);
1351 	PED_ASSERT (part->disk_specific != NULL, return 0);
1352 	PED_ASSERT (part->disk != NULL, return 0);
1353 
1354 	dos_data = part->disk_specific;
1355 	disk = part->disk;
1356 
1357 	switch (flag) {
1358 	case PED_PARTITION_HIDDEN:
1359 		if (part->type == PED_PARTITION_EXTENDED) {
1360 			ped_exception_throw (
1361 				PED_EXCEPTION_ERROR,
1362 				PED_EXCEPTION_CANCEL,
1363 				_("Extended partitions cannot be hidden on "
1364 				  "msdos disk labels."));
1365 			return 0;
1366 		}
1367 		dos_data->hidden = state;
1368 		return ped_partition_set_system (part, part->fs_type);
1369 
1370 	case PED_PARTITION_BOOT:
1371 		dos_data->boot = state;
1372 		if (!state)
1373 			return 1;
1374 
1375 		walk = ped_disk_next_partition (disk, NULL);
1376 		for (; walk; walk = ped_disk_next_partition (disk, walk)) {
1377 			if (walk == part || !ped_partition_is_active (walk))
1378 				continue;
1379 			msdos_partition_set_flag (walk, PED_PARTITION_BOOT, 0);
1380 		}
1381 		return 1;
1382 
1383 	case PED_PARTITION_RAID:
1384 		if (state) {
1385 			dos_data->hidden = 0;
1386 			dos_data->lvm = 0;
1387 			dos_data->palo = 0;
1388 			dos_data->prep = 0;
1389 		}
1390 		dos_data->raid = state;
1391 		return ped_partition_set_system (part, part->fs_type);
1392 
1393 	case PED_PARTITION_LVM:
1394 		if (state) {
1395 			dos_data->hidden = 0;
1396 			dos_data->raid = 0;
1397 			dos_data->palo = 0;
1398 			dos_data->prep = 0;
1399 		}
1400 		dos_data->lvm = state;
1401 		return ped_partition_set_system (part, part->fs_type);
1402 
1403 	case PED_PARTITION_LBA:
1404 		dos_data->lba = state;
1405 		return ped_partition_set_system (part, part->fs_type);
1406 
1407 	case PED_PARTITION_PALO:
1408 		if (state) {
1409 			dos_data->hidden = 0;
1410 			dos_data->raid = 0;
1411 			dos_data->lvm = 0;
1412 		}
1413 		dos_data->palo = state;
1414 		return ped_partition_set_system (part, part->fs_type);
1415 
1416 	case PED_PARTITION_PREP:
1417 		if (state) {
1418 			dos_data->hidden = 0;
1419 			dos_data->raid = 0;
1420 			dos_data->lvm = 0;
1421 		}
1422 		dos_data->prep = state;
1423 		return ped_partition_set_system (part, part->fs_type);
1424 
1425 	default:
1426 		return 0;
1427 	}
1428 }
1429 
1430 static int
msdos_partition_get_flag(const PedPartition * part,PedPartitionFlag flag)1431 msdos_partition_get_flag (const PedPartition* part, PedPartitionFlag flag)
1432 {
1433 	DosPartitionData*	dos_data;
1434 
1435 	PED_ASSERT (part != NULL, return 0);
1436 	PED_ASSERT (part->disk_specific != NULL, return 0);
1437 
1438 	dos_data = part->disk_specific;
1439 	switch (flag) {
1440 	case PED_PARTITION_HIDDEN:
1441 		return dos_data->hidden;
1442 
1443 	case PED_PARTITION_BOOT:
1444 		return dos_data->boot;
1445 
1446 	case PED_PARTITION_RAID:
1447 		return dos_data->raid;
1448 
1449 	case PED_PARTITION_LVM:
1450 		return dos_data->lvm;
1451 
1452 	case PED_PARTITION_LBA:
1453 		return dos_data->lba;
1454 
1455 	case PED_PARTITION_PALO:
1456 		return dos_data->palo;
1457 
1458 	case PED_PARTITION_PREP:
1459 		return dos_data->prep;
1460 
1461 	default:
1462 		return 0;
1463 	}
1464 }
1465 
1466 static int
msdos_partition_is_flag_available(const PedPartition * part,PedPartitionFlag flag)1467 msdos_partition_is_flag_available (const PedPartition* part,
1468 				   PedPartitionFlag flag)
1469 {
1470 	switch (flag) {
1471 	case PED_PARTITION_HIDDEN:
1472 	case PED_PARTITION_BOOT:
1473 	case PED_PARTITION_RAID:
1474 	case PED_PARTITION_LVM:
1475 	case PED_PARTITION_LBA:
1476 	case PED_PARTITION_PALO:
1477 	case PED_PARTITION_PREP:
1478 		return 1;
1479 
1480 	default:
1481 		return 0;
1482 	}
1483 }
1484 
1485 static PedGeometry*
_try_constraint(const PedPartition * part,const PedConstraint * external,PedConstraint * internal)1486 _try_constraint (const PedPartition* part, const PedConstraint* external,
1487 		 PedConstraint* internal)
1488 {
1489 	PedConstraint*		intersection;
1490 	PedGeometry*		solution;
1491 
1492 	intersection = ped_constraint_intersect (external, internal);
1493 	ped_constraint_destroy (internal);
1494 	if (!intersection)
1495 		return NULL;
1496 
1497 	solution = ped_constraint_solve_nearest (intersection, &part->geom);
1498 	ped_constraint_destroy (intersection);
1499 	return solution;
1500 }
1501 
1502 static PedGeometry*
_best_solution(const PedPartition * part,const PedCHSGeometry * bios_geom,PedGeometry * a,PedGeometry * b)1503 _best_solution (const PedPartition* part, const PedCHSGeometry* bios_geom,
1504 		PedGeometry* a, PedGeometry* b)
1505 {
1506 	PedSector	cyl_size = bios_geom->heads * bios_geom->sectors;
1507 	int		a_cylinder;
1508 	int		b_cylinder;
1509 
1510 	if (!a)
1511 		return b;
1512 	if (!b)
1513 		return a;
1514 
1515 	a_cylinder = a->start / cyl_size;
1516 	b_cylinder = b->start / cyl_size;
1517 
1518 	if (a_cylinder == b_cylinder) {
1519 		if ( (a->start / bios_geom->sectors) % bios_geom->heads
1520 			  < (b->start / bios_geom->sectors) % bios_geom->heads)
1521 	       		goto choose_a;
1522 		else
1523 			goto choose_b;
1524 	} else {
1525 		PedSector	a_delta;
1526 		PedSector	b_delta;
1527 
1528 		a_delta = abs (part->geom.start - a->start);
1529 		b_delta = abs (part->geom.start - b->start);
1530 
1531 		if (a_delta < b_delta)
1532 			goto choose_a;
1533 		else
1534 			goto choose_b;
1535 	}
1536 
1537 	return NULL;	/* never get here! */
1538 
1539 choose_a:
1540 	ped_geometry_destroy (b);
1541 	return a;
1542 
1543 choose_b:
1544 	ped_geometry_destroy (a);
1545 	return b;
1546 }
1547 
1548 /* This constraint is for "normal" primary partitions, that start at the
1549  * beginning of a cylinder, and end at the end of a cylinder.
1550  * 	Note: you can't start a partition at the beginning of the 1st
1551  * cylinder, because that's where the partition table is!  There are different
1552  * rules for that - see the _primary_start_constraint.
1553  */
1554 static PedConstraint*
_primary_constraint(const PedDisk * disk,const PedCHSGeometry * bios_geom,PedGeometry * min_geom)1555 _primary_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom,
1556 		     PedGeometry* min_geom)
1557 {
1558 	PedDevice*	dev = disk->dev;
1559 	PedSector	cylinder_size = bios_geom->sectors * bios_geom->heads;
1560 	PedAlignment	start_align;
1561 	PedAlignment	end_align;
1562 	PedGeometry	start_geom;
1563 	PedGeometry	end_geom;
1564 
1565 	if (!ped_alignment_init (&start_align, 0, cylinder_size))
1566 		return NULL;
1567 	if (!ped_alignment_init (&end_align, -1, cylinder_size))
1568 		return NULL;
1569 
1570 	if (min_geom) {
1571 		if (min_geom->start < cylinder_size)
1572 			return NULL;
1573 		if (!ped_geometry_init (&start_geom, dev, cylinder_size,
1574 			       		min_geom->start + 1 - cylinder_size))
1575 			return NULL;
1576 		if (!ped_geometry_init (&end_geom, dev, min_geom->end,
1577 			       		dev->length - min_geom->end))
1578 			return NULL;
1579 	} else {
1580 		if (!ped_geometry_init (&start_geom, dev, cylinder_size,
1581 			       		dev->length - cylinder_size))
1582 			return NULL;
1583 		if (!ped_geometry_init (&end_geom, dev, 0, dev->length))
1584 			return NULL;
1585 	}
1586 
1587 	return ped_constraint_new (&start_align, &end_align, &start_geom,
1588 				   &end_geom, 1, dev->length);
1589 }
1590 
1591 /* This constraint is for partitions starting on the first cylinder.  They
1592  * must start on the 2nd head of the 1st cylinder.
1593  *
1594  * NOTE: We don't always start on the 2nd head of the 1st cylinder.  Windows
1595  * Vista aligns starting partitions at sector 2048 (0x800) by default.  See:
1596  * http://support.microsoft.com/kb/923332
1597  */
1598 static PedConstraint*
_primary_start_constraint(const PedDisk * disk,const PedPartition * part,const PedCHSGeometry * bios_geom,const PedGeometry * min_geom)1599 _primary_start_constraint (const PedDisk* disk,
1600                            const PedPartition *part,
1601                            const PedCHSGeometry* bios_geom,
1602                            const PedGeometry* min_geom)
1603 {
1604 	PedDevice*	dev = disk->dev;
1605 	PedSector	cylinder_size = bios_geom->sectors * bios_geom->heads;
1606 	PedAlignment	start_align;
1607 	PedAlignment	end_align;
1608 	PedGeometry	start_geom;
1609 	PedGeometry	end_geom;
1610 	PedSector start_pos;
1611 
1612 	if (part->geom.start == 2048)
1613 		/* check for known Windows Vista (NTFS >= 3.1) alignments */
1614 		/* sector 0x800 == 2048                                   */
1615 		start_pos = 2048;
1616 	else
1617 		/* all other primary partitions on a DOS label align to   */
1618 		/* the 2nd head of the first cylinder (0x3F == 63)        */
1619 		start_pos = bios_geom->sectors;
1620 
1621 	if (!ped_alignment_init (&start_align, start_pos, 0))
1622 		return NULL;
1623 	if (!ped_alignment_init (&end_align, -1, cylinder_size))
1624 		return NULL;
1625 	if (min_geom) {
1626 		if (!ped_geometry_init (&start_geom, dev, start_pos, 1))
1627 			return NULL;
1628 		if (!ped_geometry_init (&end_geom, dev, min_geom->end,
1629 			       		dev->length - min_geom->end))
1630 			return NULL;
1631 	} else {
1632 		if (!ped_geometry_init (&start_geom, dev, start_pos,
1633 			dev->length - start_pos))
1634 			return NULL;
1635 		if (!ped_geometry_init (&end_geom, dev, 0, dev->length))
1636 			return NULL;
1637 	}
1638 
1639 	return ped_constraint_new (&start_align, &end_align, &start_geom,
1640 				   &end_geom, 1, dev->length);
1641 }
1642 
1643 /* constraints for logical partitions:
1644  * 	- start_offset is the offset in the start alignment.  "normally",
1645  * this is bios_geom->sectors.  exceptions: MINOR > 5 at the beginning of the
1646  * extended partition, or MINOR == 5 in the middle of the extended partition
1647  * 	- is_start_part == 1 if the constraint is for the first cylinder of
1648  * the extended partition, or == 0 if the constraint is for the second cylinder
1649  * onwards of the extended partition.
1650  */
1651 static PedConstraint*
_logical_constraint(const PedDisk * disk,const PedCHSGeometry * bios_geom,PedSector start_offset,int is_start_part)1652 _logical_constraint (const PedDisk* disk, const PedCHSGeometry* bios_geom,
1653 		     PedSector start_offset, int is_start_part)
1654 {
1655 	PedPartition*	ext_part = ped_disk_extended_partition (disk);
1656 	PedDevice*	dev = disk->dev;
1657 	PedSector	cylinder_size = bios_geom->sectors * bios_geom->heads;
1658 	PedAlignment	start_align;
1659 	PedAlignment	end_align;
1660 	PedGeometry	max_geom;
1661 
1662 	PED_ASSERT (ext_part != NULL, return NULL);
1663 
1664 	if (!ped_alignment_init (&start_align, start_offset, cylinder_size))
1665 		return NULL;
1666 	if (!ped_alignment_init (&end_align, -1, cylinder_size))
1667 		return NULL;
1668 	if (is_start_part) {
1669 		if (!ped_geometry_init (&max_geom, dev,
1670 					ext_part->geom.start,
1671 					ext_part->geom.length))
1672 			return NULL;
1673 	} else {
1674 		PedSector	min_start;
1675 		PedSector	max_length;
1676 
1677 		min_start = ped_round_up_to (ext_part->geom.start + 1,
1678 					     cylinder_size);
1679 		max_length = ext_part->geom.end - min_start + 1;
1680 		if (min_start >= ext_part->geom.end)
1681 			return NULL;
1682 
1683 		if (!ped_geometry_init (&max_geom, dev, min_start, max_length))
1684 			return NULL;
1685 	}
1686 
1687 	return ped_constraint_new (&start_align, &end_align, &max_geom,
1688 		       		   &max_geom, 1, dev->length);
1689 }
1690 
1691 /* returns the minimum geometry for the extended partition, given that the
1692  * extended partition must contain:
1693  *   * all logical partitions
1694  *   * all partition tables for all logical partitions (except the first)
1695  *   * the extended partition table
1696  */
1697 static PedGeometry*
_get_min_extended_part_geom(const PedPartition * ext_part,const PedCHSGeometry * bios_geom)1698 _get_min_extended_part_geom (const PedPartition* ext_part,
1699 			     const PedCHSGeometry* bios_geom)
1700 {
1701 	PedDisk*		disk = ext_part->disk;
1702 	PedSector		head_size = bios_geom ? bios_geom->sectors : 1;
1703 	PedPartition*		walk;
1704 	PedGeometry*		min_geom;
1705 
1706 	walk = ped_disk_get_partition (disk, 5);
1707 	if (!walk)
1708 		return NULL;
1709 
1710 	min_geom = ped_geometry_duplicate (&walk->geom);
1711 	if (!min_geom)
1712 		return NULL;
1713 	ped_geometry_set_start (min_geom, walk->geom.start - 1 * head_size);
1714 
1715 	for (walk = ext_part->part_list; walk; walk = walk->next) {
1716 		if (!ped_partition_is_active (walk) || walk->num == 5)
1717 			continue;
1718 		if (walk->geom.start < min_geom->start)
1719 			ped_geometry_set_start (min_geom,
1720 					walk->geom.start - 2 * head_size);
1721 		if (walk->geom.end > min_geom->end)
1722 			ped_geometry_set_end (min_geom, walk->geom.end);
1723 	}
1724 
1725 	return min_geom;
1726 }
1727 
1728 static int
_align_primary(PedPartition * part,const PedCHSGeometry * bios_geom,const PedConstraint * constraint)1729 _align_primary (PedPartition* part, const PedCHSGeometry* bios_geom,
1730 		const PedConstraint* constraint)
1731 {
1732 	PedDisk*	disk = part->disk;
1733 	PedGeometry*	min_geom = NULL;
1734 	PedGeometry*	solution = NULL;
1735 
1736 	if (part->type == PED_PARTITION_EXTENDED)
1737 		min_geom = _get_min_extended_part_geom (part, bios_geom);
1738 
1739 	solution = _best_solution (part, bios_geom, solution,
1740 			_try_constraint (part, constraint,
1741 					 _primary_start_constraint (disk, part,
1742 						 bios_geom, min_geom)));
1743 
1744 	solution = _best_solution (part, bios_geom, solution,
1745 			_try_constraint (part, constraint,
1746 				_primary_constraint (disk, bios_geom,
1747 				min_geom)));
1748 
1749 	if (min_geom)
1750 		ped_geometry_destroy (min_geom);
1751 
1752 	if (solution) {
1753 		ped_geometry_set (&part->geom, solution->start,
1754 				  solution->length);
1755 		ped_geometry_destroy (solution);
1756 		return 1;
1757 	}
1758 
1759 	return 0;
1760 }
1761 
1762 static int
_logical_min_start_head(const PedPartition * part,const PedCHSGeometry * bios_geom,const PedPartition * ext_part,int is_start_ext_part)1763 _logical_min_start_head (const PedPartition* part,
1764                          const PedCHSGeometry* bios_geom,
1765 			 const PedPartition* ext_part,
1766                          int is_start_ext_part)
1767 {
1768 	PedSector	cylinder_size = bios_geom->sectors * bios_geom->heads;
1769 	PedSector	base_head;
1770 
1771 	if (is_start_ext_part)
1772 		base_head = 1 + (ext_part->geom.start % cylinder_size)
1773 					/ bios_geom->sectors;
1774 	else
1775 		base_head = 0;
1776 
1777 	if (part->num == 5)
1778 		return base_head + 0;
1779 	else
1780 		return base_head + 1;
1781 }
1782 
1783 /* Shamelessly copied and adapted from _partition_get_overlap_constraint
1784  * (in disk.c)
1785  * This should get ride of the infamous Assertion (metadata_length > 0) failed
1786  * bug for extended msdos disklabels generated by Parted.
1787  * 1) There always is a partition table at the start of ext_part, so we leave
1788  *    a one sector gap there.
1789  * 2)*The partition table of part5 is always at the beginning of the ext_part
1790  *    so there is no need to leave a one sector gap before part5.
1791  *   *There always is a partition table at the beginning of each partition != 5.
1792  * We don't need to worry to much about consistency with
1793  * _partition_get_overlap_constraint because missing it means we are in edge
1794  * cases anyway, and we don't lose anything by just refusing to do the job in
1795  * those cases.
1796  */
1797 static PedConstraint*
_log_meta_overlap_constraint(PedPartition * part,const PedGeometry * geom)1798 _log_meta_overlap_constraint (PedPartition* part, const PedGeometry* geom)
1799 {
1800 	PedGeometry	safe_space;
1801 	PedSector	min_start;
1802 	PedSector	max_end;
1803 	PedPartition*	ext_part = ped_disk_extended_partition (part->disk);
1804 	PedPartition*	walk;
1805 	int		not_5 = (part->num != 5);
1806 
1807 	PED_ASSERT (ext_part != NULL, return NULL);
1808 
1809 	walk = ext_part->part_list;
1810 
1811 	/*                                 1)  2)     */
1812 	min_start = ext_part->geom.start + 1 + not_5;
1813 	max_end = ext_part->geom.end;
1814 
1815 	while (walk != NULL             /*      2)                         2) */
1816 		&& (   walk->geom.start - (walk->num != 5) < geom->start - not_5
1817 		    || walk->geom.start - (walk->num != 5) <= min_start )) {
1818 		if (walk != part && ped_partition_is_active (walk))
1819 			min_start = walk->geom.end + 1 + not_5; /* 2) */
1820 		walk = walk->next;
1821 	}
1822 
1823 	while (walk && (walk == part || !ped_partition_is_active (walk)))
1824 		walk = walk->next;
1825 
1826 	if (walk)
1827 		max_end = walk->geom.start - 1 - (walk->num != 5); /* 2) */
1828 
1829 	if (min_start >= max_end)
1830 		return NULL;
1831 
1832 	ped_geometry_init (&safe_space, part->disk->dev,
1833 			   min_start, max_end - min_start + 1);
1834 	return ped_constraint_new_from_max (&safe_space);
1835 }
1836 
1837 static int
_align_logical(PedPartition * part,const PedCHSGeometry * bios_geom,const PedConstraint * constraint)1838 _align_logical (PedPartition* part, const PedCHSGeometry* bios_geom,
1839 		const PedConstraint* constraint)
1840 {
1841 	PedDisk*	disk = part->disk;
1842 	PedPartition*	ext_part = ped_disk_extended_partition (disk);
1843 	PedSector	cyl_size = bios_geom->sectors * bios_geom->heads;
1844 	PedSector	start_base;
1845 	int		head;
1846 	PedGeometry*	solution = NULL;
1847 	PedConstraint   *intersect, *log_meta_overlap;
1848 
1849 	PED_ASSERT (ext_part != NULL, return 0);
1850 
1851 	log_meta_overlap = _log_meta_overlap_constraint(part, &part->geom);
1852 	intersect = ped_constraint_intersect (constraint, log_meta_overlap);
1853 	ped_constraint_destroy (log_meta_overlap);
1854 	if (!intersect)
1855 		return 0;
1856 
1857 	start_base = ped_round_down_to (part->geom.start, cyl_size);
1858 
1859 	for (head = _logical_min_start_head (part, bios_geom, ext_part, 0);
1860 	     head < PED_MIN (5, bios_geom->heads); head++) {
1861 		PedConstraint*	disk_constraint;
1862 		PedSector	start = start_base + head * bios_geom->sectors;
1863 
1864 		if (head >= _logical_min_start_head (part, bios_geom,
1865 						     ext_part, 1))
1866 			disk_constraint =
1867 				_logical_constraint (disk, bios_geom, start, 1);
1868 		else
1869 			disk_constraint =
1870 				_logical_constraint (disk, bios_geom, start, 0);
1871 
1872 		solution = _best_solution (part, bios_geom, solution,
1873 				_try_constraint (part, intersect,
1874 						 disk_constraint));
1875 	}
1876 
1877 	ped_constraint_destroy (intersect);
1878 
1879 	if (solution) {
1880 		ped_geometry_set (&part->geom, solution->start,
1881 				  solution->length);
1882 		ped_geometry_destroy (solution);
1883 		return 1;
1884 	}
1885 
1886 	return 0;
1887 }
1888 
1889 static int
_align(PedPartition * part,const PedCHSGeometry * bios_geom,const PedConstraint * constraint)1890 _align (PedPartition* part, const PedCHSGeometry* bios_geom,
1891 	const PedConstraint* constraint)
1892 {
1893 	if (part->type == PED_PARTITION_LOGICAL)
1894 		return _align_logical (part, bios_geom, constraint);
1895 	else
1896 		return _align_primary (part, bios_geom, constraint);
1897 }
1898 
1899 static PedConstraint*
_no_geom_constraint(const PedDisk * disk,PedSector start,PedSector end)1900 _no_geom_constraint (const PedDisk* disk, PedSector start, PedSector end)
1901 {
1902 	PedGeometry	 max;
1903 
1904 	ped_geometry_init (&max, disk->dev, start, end - start + 1);
1905 	return ped_constraint_new_from_max (&max);
1906 }
1907 
1908 static PedConstraint*
_no_geom_extended_constraint(const PedPartition * part)1909 _no_geom_extended_constraint (const PedPartition* part)
1910 {
1911 	PedDevice*	dev = part->disk->dev;
1912 	PedGeometry*	min = _get_min_extended_part_geom (part, NULL);
1913 	PedGeometry	start_range;
1914 	PedGeometry	end_range;
1915 	PedConstraint*	constraint;
1916 
1917 	if (min) {
1918 		ped_geometry_init (&start_range, dev, 1, min->start);
1919 		ped_geometry_init (&end_range, dev, min->end,
1920 				   dev->length - min->end);
1921 		ped_geometry_destroy (min);
1922 	} else {
1923 		ped_geometry_init (&start_range, dev, 1, dev->length - 1);
1924 		ped_geometry_init (&end_range, dev, 1, dev->length - 1);
1925 	}
1926 	constraint = ped_constraint_new (ped_alignment_any, ped_alignment_any,
1927 			&start_range, &end_range, 1, dev->length);
1928 	return constraint;
1929 }
1930 
1931 static int
_align_primary_no_geom(PedPartition * part,const PedConstraint * constraint)1932 _align_primary_no_geom (PedPartition* part, const PedConstraint* constraint)
1933 {
1934 	PedDisk*	disk = part->disk;
1935 	PedGeometry*	solution;
1936 
1937 	if (part->type == PED_PARTITION_EXTENDED) {
1938 		solution = _try_constraint (part, constraint,
1939 				_no_geom_extended_constraint (part));
1940 	} else {
1941 		solution = _try_constraint (part, constraint,
1942 				_no_geom_constraint (disk, 1,
1943 						     disk->dev->length - 1));
1944 	}
1945 
1946 	if (solution) {
1947 		ped_geometry_set (&part->geom, solution->start,
1948 				  solution->length);
1949 		ped_geometry_destroy (solution);
1950 		return 1;
1951 	}
1952 	return 0;
1953 }
1954 
1955 static int
_align_logical_no_geom(PedPartition * part,const PedConstraint * constraint)1956 _align_logical_no_geom (PedPartition* part, const PedConstraint* constraint)
1957 {
1958 	PedGeometry*	solution;
1959 
1960 	solution = _try_constraint (part, constraint,
1961 			_log_meta_overlap_constraint (part, &part->geom));
1962 
1963 	if (solution) {
1964 		ped_geometry_set (&part->geom, solution->start,
1965 				  solution->length);
1966 		ped_geometry_destroy (solution);
1967 		return 1;
1968 	}
1969 	return 0;
1970 }
1971 
1972 static int
_align_no_geom(PedPartition * part,const PedConstraint * constraint)1973 _align_no_geom (PedPartition* part, const PedConstraint* constraint)
1974 {
1975 	if (part->type == PED_PARTITION_LOGICAL)
1976 		return _align_logical_no_geom (part, constraint);
1977 	else
1978 		return _align_primary_no_geom (part, constraint);
1979 }
1980 
1981 static int
msdos_partition_align(PedPartition * part,const PedConstraint * constraint)1982 msdos_partition_align (PedPartition* part, const PedConstraint* constraint)
1983 {
1984 	PedCHSGeometry	bios_geom;
1985 	DosPartitionData* dos_data;
1986 
1987  	PED_ASSERT (part != NULL, return 0);
1988 	PED_ASSERT (part->disk_specific != NULL, return 0);
1989 
1990 	dos_data = part->disk_specific;
1991 	if (dos_data->system == PARTITION_LDM && dos_data->orig) {
1992 		PedGeometry *orig_geom = &dos_data->orig->geom;
1993 
1994 		if (ped_geometry_test_equal (&part->geom, orig_geom)
1995 		    && ped_constraint_is_solution (constraint, &part->geom))
1996 			return 1;
1997 
1998 		ped_geometry_set (&part->geom, orig_geom->start,
1999 				  orig_geom->length);
2000 		ped_exception_throw (
2001 			PED_EXCEPTION_ERROR,
2002 			PED_EXCEPTION_CANCEL,
2003 			_("Parted can't resize partitions managed by "
2004 			  "Windows Dynamic Disk."));
2005 		return 0;
2006 	}
2007 
2008 	partition_probe_bios_geometry (part, &bios_geom);
2009 
2010 	if (_align (part, &bios_geom, constraint))
2011 		return 1;
2012 	if (_align_no_geom (part, constraint))
2013 		return 1;
2014 
2015 #ifndef DISCOVER_ONLY
2016 	ped_exception_throw (
2017 		PED_EXCEPTION_ERROR,
2018 		PED_EXCEPTION_CANCEL,
2019 		_("Unable to satisfy all constraints on the partition."));
2020 #endif
2021 	return 0;
2022 }
2023 
2024 static int
add_metadata_part(PedDisk * disk,PedPartitionType type,PedSector start,PedSector end)2025 add_metadata_part (PedDisk* disk, PedPartitionType type, PedSector start,
2026 		   PedSector end)
2027 {
2028 	PedPartition*		new_part;
2029 
2030 	PED_ASSERT (disk != NULL, return 0);
2031 
2032 	new_part = ped_partition_new (disk, type | PED_PARTITION_METADATA, NULL,
2033 				      start, end);
2034 	if (!new_part)
2035 		goto error;
2036 	if (!ped_disk_add_partition (disk, new_part, NULL))
2037 		goto error_destroy_new_part;
2038 
2039 	return 1;
2040 
2041 error_destroy_new_part:
2042 	ped_partition_destroy (new_part);
2043 error:
2044 	return 0;
2045 }
2046 
2047 /* There are a few objectives here:
2048  * 	- avoid having lots of "free space" partitions lying around, to confuse
2049  * the front end.
2050  * 	- ensure that there's enough room to put in the extended partition
2051  * tables, etc.
2052  */
2053 static int
add_logical_part_metadata(PedDisk * disk,const PedPartition * log_part)2054 add_logical_part_metadata (PedDisk* disk, const PedPartition* log_part)
2055 {
2056 	PedPartition*	ext_part = ped_disk_extended_partition (disk);
2057 	PedPartition*	prev = log_part->prev;
2058 	PedCHSGeometry	bios_geom;
2059 	PedSector	cyl_size;
2060 	PedSector	metadata_start;
2061 	PedSector	metadata_end;
2062 	PedSector	metadata_length;
2063 
2064 	partition_probe_bios_geometry (ext_part, &bios_geom);
2065 	cyl_size = bios_geom.sectors * bios_geom.heads;
2066 
2067 	/* if there's metadata shortly before the partition (on the same
2068 	 * cylinder), then make this new metadata partition touch the end of
2069 	 * the other.  No point having 63 bytes (or whatever) of free space
2070 	 * partition - just confuses front-ends, etc.
2071 	 * 	Otherwise, start the metadata at the start of the cylinder
2072 	 */
2073 
2074 	metadata_end = log_part->geom.start - 1;
2075 	metadata_start = ped_round_down_to (metadata_end, cyl_size);
2076 	if (prev)
2077 		metadata_start = PED_MAX (metadata_start, prev->geom.end + 1);
2078 	else
2079 		metadata_start = PED_MAX (metadata_start,
2080 					  ext_part->geom.start + 1);
2081 	metadata_length = metadata_end - metadata_start + 1;
2082 
2083 	/* partition 5 doesn't need to have any metadata */
2084 	if (log_part->num == 5 && metadata_length < bios_geom.sectors)
2085 		return 1;
2086 
2087 	PED_ASSERT (metadata_length > 0, return 0);
2088 
2089 	return add_metadata_part (disk, PED_PARTITION_LOGICAL,
2090 				  metadata_start, metadata_end);
2091 }
2092 
2093 static PedPartition*
get_last_part(const PedDisk * disk)2094 get_last_part (const PedDisk* disk)
2095 {
2096 	PedPartition* first_part = disk->part_list;
2097 	PedPartition* walk;
2098 
2099 	if (!first_part)
2100 		return NULL;
2101 	for (walk = first_part; walk->next; walk = walk->next);
2102 	return walk;
2103 }
2104 
2105 /* Adds metadata placeholder partitions to cover the partition table (and
2106  * "free" space after it that often has bootloader stuff), and the last
2107  * incomplete cylinder at the end of the disk.
2108  * 	Parted has to be mindful of the uncertainty of dev->bios_geom.
2109  * It therefore makes sure this metadata doesn't overlap with partitions.
2110  */
2111 static int
add_startend_metadata(PedDisk * disk)2112 add_startend_metadata (PedDisk* disk)
2113 {
2114 	PedDevice* dev = disk->dev;
2115 	PedSector cyl_size = dev->bios_geom.sectors * dev->bios_geom.heads;
2116 	PedPartition* first_part = disk->part_list;
2117 	PedPartition* last_part = get_last_part (disk);
2118 	PedSector start, end;
2119 
2120 	if (!first_part)
2121 		return 1;
2122 
2123 	start = 0;
2124 	end = PED_MIN (dev->bios_geom.sectors - 1, first_part->geom.start - 1);
2125 	if (!add_metadata_part (disk, PED_PARTITION_NORMAL, start, end))
2126 		return 0;
2127 
2128 	start = PED_MAX (last_part->geom.end + 1,
2129 			 ped_round_down_to (dev->length, cyl_size));
2130 	end = dev->length - 1;
2131 	if (start < end) {
2132 		if (!add_metadata_part (disk, PED_PARTITION_NORMAL, start, end))
2133 			return 0;
2134 	}
2135 
2136 	return 1;
2137 }
2138 
2139 static int
msdos_alloc_metadata(PedDisk * disk)2140 msdos_alloc_metadata (PedDisk* disk)
2141 {
2142 	PedPartition*		ext_part;
2143 
2144 	PED_ASSERT (disk != NULL, return 0);
2145 	PED_ASSERT (disk->dev != NULL, return 0);
2146 
2147 	if (!add_startend_metadata (disk))
2148 		return 0;
2149 
2150 	ext_part = ped_disk_extended_partition (disk);
2151 	if (ext_part) {
2152 		int		i;
2153 		PedSector	start, end;
2154 		PedCHSGeometry	bios_geom;
2155 
2156 		for (i=5; 1; i++) {
2157 			PedPartition* log_part;
2158 			log_part = ped_disk_get_partition (disk, i);
2159 			if (!log_part)
2160 				break;
2161 			if (!add_logical_part_metadata (disk, log_part))
2162 				return 0;
2163 		}
2164 
2165 		partition_probe_bios_geometry (ext_part, &bios_geom);
2166 		start = ext_part->geom.start;
2167 		end = start + bios_geom.sectors - 1;
2168 		if (ext_part->part_list)
2169 			end = PED_MIN (end,
2170 				       ext_part->part_list->geom.start - 1);
2171 		if (!add_metadata_part (disk, PED_PARTITION_LOGICAL,
2172 					start, end))
2173 			return 0;
2174 	}
2175 
2176 	return 1;
2177 }
2178 
2179 static int
next_primary(const PedDisk * disk)2180 next_primary (const PedDisk* disk)
2181 {
2182 	int	i;
2183 	for (i=1; i<=4; i++) {
2184 		if (!ped_disk_get_partition (disk, i))
2185 			return i;
2186 	}
2187 	return 0;
2188 }
2189 
2190 static int
next_logical(const PedDisk * disk)2191 next_logical (const PedDisk* disk)
2192 {
2193 	int	i;
2194 	for (i=5; 1; i++) {
2195 		if (!ped_disk_get_partition (disk, i))
2196 			return i;
2197 	}
2198 }
2199 
2200 static int
msdos_partition_enumerate(PedPartition * part)2201 msdos_partition_enumerate (PedPartition* part)
2202 {
2203 	PED_ASSERT (part != NULL, return 0);
2204 	PED_ASSERT (part->disk != NULL, return 0);
2205 
2206 	/* don't re-number a primary partition */
2207 	if (part->num != -1 && part->num <= 4)
2208 		return 1;
2209 
2210 	part->num = -1;
2211 
2212 	if (part->type & PED_PARTITION_LOGICAL)
2213 		part->num = next_logical (part->disk);
2214 	else
2215 		part->num = next_primary (part->disk);
2216 
2217 	return 1;
2218 }
2219 
2220 static int
msdos_get_max_primary_partition_count(const PedDisk * disk)2221 msdos_get_max_primary_partition_count (const PedDisk* disk)
2222 {
2223 	return 4;
2224 }
2225 
2226 static PedDiskOps msdos_disk_ops = {
2227 	.probe =		msdos_probe,
2228 #ifndef DISCOVER_ONLY
2229 	.clobber =		msdos_clobber,
2230 #else
2231 	.clobber =		NULL,
2232 #endif
2233 	.alloc =		msdos_alloc,
2234 	.duplicate =		msdos_duplicate,
2235 	.free =			msdos_free,
2236 	.read =			msdos_read,
2237 #ifndef DISCOVER_ONLY
2238 	.write =		msdos_write,
2239 #else
2240 	.write =		NULL,
2241 #endif
2242 
2243 	.partition_new =	msdos_partition_new,
2244 	.partition_duplicate =	msdos_partition_duplicate,
2245 	.partition_destroy =	msdos_partition_destroy,
2246 	.partition_set_system =	msdos_partition_set_system,
2247 	.partition_set_flag =	msdos_partition_set_flag,
2248 	.partition_get_flag =	msdos_partition_get_flag,
2249 	.partition_is_flag_available =	msdos_partition_is_flag_available,
2250 	.partition_set_name =	NULL,
2251 	.partition_get_name =	NULL,
2252 	.partition_align =	msdos_partition_align,
2253 	.partition_enumerate =	msdos_partition_enumerate,
2254 
2255 	.alloc_metadata =	msdos_alloc_metadata,
2256 	.get_max_primary_partition_count =
2257 				msdos_get_max_primary_partition_count
2258 };
2259 
2260 static PedDiskType msdos_disk_type = {
2261 	.next =			NULL,
2262 	.name =			"msdos",
2263 	.ops =			&msdos_disk_ops,
2264 	.features =		PED_DISK_TYPE_EXTENDED
2265 };
2266 
2267 void
ped_disk_msdos_init()2268 ped_disk_msdos_init ()
2269 {
2270 	PED_ASSERT (sizeof (DosRawPartition) == 16, return);
2271 	PED_ASSERT (sizeof (DosRawTable) == 512, return);
2272 
2273 	ped_disk_type_register (&msdos_disk_type);
2274 }
2275 
2276 void
ped_disk_msdos_done()2277 ped_disk_msdos_done ()
2278 {
2279 	ped_disk_type_unregister (&msdos_disk_type);
2280 }
2281