xref: /titanic_44/usr/src/lib/libfdisk/common/libfdisk.c (revision e6f8def1ace27f327240a0b4b090911007f71137)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <strings.h>
29 #include <unistd.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <ctype.h>
33 #include <sys/stat.h>
34 #include <sys/types.h>
35 #include <sys/param.h>
36 #include <sys/systeminfo.h>
37 #include <sys/efi_partition.h>
38 #include <sys/byteorder.h>
39 
40 #include <sys/vtoc.h>
41 #include <sys/tty.h>
42 #include <sys/dktp/fdisk.h>
43 #include <sys/dkio.h>
44 #include <sys/mnttab.h>
45 #include "libfdisk.h"
46 
47 #define	DEFAULT_PATH_PREFIX	"/dev/rdsk/"
48 
49 static void fdisk_free_ld_nodes(ext_part_t *epp);
50 static void fdisk_ext_place_in_sorted_list(ext_part_t *epp,
51     logical_drive_t *newld);
52 static void fdisk_ext_remove_from_sorted_list(ext_part_t *epp,
53     logical_drive_t *delld);
54 static int fdisk_ext_overlapping_parts(ext_part_t *epp, uint32_t begsec,
55     uint32_t endsec);
56 static int fdisk_read_extpart(ext_part_t *epp);
57 static void fdisk_set_CHS_values(ext_part_t *epp, struct ipart *part);
58 static int fdisk_init_master_part_table(ext_part_t *epp);
59 static struct ipart *fdisk_alloc_part_table();
60 static int fdisk_read_master_part_table(ext_part_t *epp);
61 
62 static int
fdisk_init_disk_geom(ext_part_t * epp)63 fdisk_init_disk_geom(ext_part_t *epp)
64 {
65 	struct dk_geom disk_geom;
66 	struct dk_minfo disk_info;
67 	int no_virtgeom_ioctl = 0, no_physgeom_ioctl = 0;
68 
69 	/* Get disk's HBA (virtual) geometry */
70 	errno = 0;
71 	if (ioctl(epp->dev_fd, DKIOCG_VIRTGEOM, &disk_geom)) {
72 		if (errno == ENOTTY) {
73 			no_virtgeom_ioctl = 1;
74 		} else if (errno == EINVAL) {
75 			/*
76 			 * This means that the ioctl exists, but
77 			 * is invalid for this disk, meaning the
78 			 * disk doesn't have an HBA geometry
79 			 * (like, say, it's larger than 8GB).
80 			 */
81 			epp->disk_geom.virt_cyl = epp->disk_geom.virt_heads =
82 			    epp->disk_geom.virt_sec = 0;
83 		} else {
84 			return (FDISK_ENOVGEOM);
85 		}
86 	} else {
87 		/* save virtual geometry values obtained by ioctl */
88 		epp->disk_geom.virt_cyl = disk_geom.dkg_ncyl;
89 		epp->disk_geom.virt_heads = disk_geom.dkg_nhead;
90 		epp->disk_geom.virt_sec = disk_geom.dkg_nsect;
91 	}
92 
93 	errno = 0;
94 	if (ioctl(epp->dev_fd, DKIOCG_PHYGEOM, &disk_geom)) {
95 		if (errno == ENOTTY) {
96 			no_physgeom_ioctl = 1;
97 		} else {
98 			return (FDISK_ENOPGEOM);
99 		}
100 	}
101 	/*
102 	 * Call DKIOCGGEOM if the ioctls for physical and virtual
103 	 * geometry fail. Get both from this generic call.
104 	 */
105 	if (no_virtgeom_ioctl && no_physgeom_ioctl) {
106 		errno = 0;
107 		if (ioctl(epp->dev_fd, DKIOCGGEOM, &disk_geom)) {
108 			return (FDISK_ENOLGEOM);
109 		}
110 	}
111 
112 	epp->disk_geom.phys_cyl = disk_geom.dkg_ncyl;
113 	epp->disk_geom.phys_heads = disk_geom.dkg_nhead;
114 	epp->disk_geom.phys_sec = disk_geom.dkg_nsect;
115 	epp->disk_geom.alt_cyl = disk_geom.dkg_acyl;
116 
117 	/*
118 	 * If DKIOCGMEDIAINFO ioctl succeeds, set the dki_lbsize as the
119 	 * size of the sector, else default to 512
120 	 */
121 	if (ioctl(epp->dev_fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info) < 0) {
122 		/* ioctl failed, falling back to default value of 512 bytes */
123 		epp->disk_geom.sectsize = 512;
124 	} else {
125 		epp->disk_geom.sectsize = ((disk_info.dki_lbsize) ?
126 		    disk_info.dki_lbsize : 512);
127 	}
128 
129 	/*
130 	 * if hba geometry was not set by DKIOC_VIRTGEOM
131 	 * or we got an invalid hba geometry
132 	 * then set hba geometry based on max values
133 	 */
134 	if (no_virtgeom_ioctl || disk_geom.dkg_ncyl == 0 ||
135 	    disk_geom.dkg_nhead == 0 || disk_geom.dkg_nsect == 0 ||
136 	    disk_geom.dkg_ncyl > MAX_CYL || disk_geom.dkg_nhead > MAX_HEAD ||
137 	    disk_geom.dkg_nsect > MAX_SECT) {
138 		epp->disk_geom.virt_sec	= MAX_SECT;
139 		epp->disk_geom.virt_heads	= MAX_HEAD + 1;
140 		epp->disk_geom.virt_cyl	= (epp->disk_geom.phys_cyl *
141 		    epp->disk_geom.phys_heads * epp->disk_geom.phys_sec) /
142 		    (epp->disk_geom.virt_sec * epp->disk_geom.virt_heads);
143 	}
144 	return (FDISK_SUCCESS);
145 }
146 
147 /*
148  * Initialise important members of the ext_part_t structure and
149  * other data structures vital to functionality of libfdisk
150  */
151 int
libfdisk_init(ext_part_t ** epp,char * devstr,struct ipart * parttab,int opflag)152 libfdisk_init(ext_part_t **epp, char *devstr, struct ipart *parttab, int opflag)
153 {
154 	ext_part_t *temp;
155 	struct stat sbuf;
156 	int rval = FDISK_SUCCESS;
157 	int found_bad_magic = 0;
158 
159 	if ((temp = calloc(1, sizeof (ext_part_t))) == NULL) {
160 		*epp = NULL;
161 		return (ENOMEM);
162 	}
163 
164 	(void) strncpy(temp->device_name, devstr,
165 	    sizeof (temp->device_name));
166 
167 	/* Try to stat the node as provided */
168 	if (stat(temp->device_name, &sbuf) != 0) {
169 
170 		/* Prefix /dev/rdsk/ and stat again */
171 		(void) snprintf(temp->device_name, sizeof (temp->device_name),
172 		    "%s%s", DEFAULT_PATH_PREFIX, devstr);
173 
174 		if (stat(temp->device_name, &sbuf) != 0) {
175 
176 			/*
177 			 * In case of an EFI labeled disk, the device name
178 			 * could be cN[tN]dN. There is no pN. So we add "p0"
179 			 * at the end if we do not find it and stat again.
180 			 */
181 			if (strrchr(temp->device_name, 'p') == NULL) {
182 				(void) strcat(temp->device_name, "p0");
183 			}
184 
185 			if (stat(temp->device_name, &sbuf) != 0) {
186 
187 				/* Failed all options, give up */
188 				rval = EINVAL;
189 				goto fail;
190 			}
191 		}
192 	}
193 
194 	/* Make sure the device is a raw device */
195 	if ((sbuf.st_mode & S_IFMT) != S_IFCHR) {
196 		rval = EINVAL;
197 		goto fail;
198 	}
199 
200 	temp->ld_head = NULL;
201 	temp->sorted_ld_head = NULL;
202 
203 	if ((temp->dev_fd = open(temp->device_name, O_RDWR, 0666)) < 0) {
204 		rval = EINVAL;
205 		goto fail;
206 	}
207 
208 	if ((temp->mtable = parttab) == NULL) {
209 		if ((rval = fdisk_init_master_part_table(temp)) !=
210 		    FDISK_SUCCESS) {
211 			/*
212 			 * When we have no fdisk magic 0xAA55 on the disk,
213 			 * we return FDISK_EBADMAGIC after successfully
214 			 * obtaining the disk geometry.
215 			 */
216 			if (rval != FDISK_EBADMAGIC)
217 				goto fail;
218 			else
219 				found_bad_magic = 1;
220 		}
221 	}
222 
223 	temp->op_flag = opflag;
224 
225 	if ((rval = fdisk_init_disk_geom(temp)) != FDISK_SUCCESS) {
226 		goto fail;
227 	}
228 
229 	*epp = temp;
230 
231 	if (found_bad_magic != 0) {
232 		return (FDISK_EBADMAGIC);
233 	}
234 
235 	if (opflag & FDISK_READ_DISK) {
236 		rval = fdisk_read_extpart(*epp);
237 	}
238 	return (rval);
239 
240 fail:
241 	*epp = NULL;
242 	free(temp);
243 	return (rval);
244 }
245 
246 int
libfdisk_reset(ext_part_t * epp)247 libfdisk_reset(ext_part_t *epp)
248 {
249 	int rval = FDISK_SUCCESS;
250 
251 	fdisk_free_ld_nodes(epp);
252 	epp->first_ebr_is_null = 1;
253 	epp->corrupt_logical_drives = 0;
254 	epp->logical_drive_count = 0;
255 	epp->invalid_bb_sig[0] = 0;
256 	if (epp->op_flag & FDISK_READ_DISK) {
257 		rval = fdisk_read_extpart(epp);
258 	}
259 	return (rval);
260 }
261 
262 void
libfdisk_fini(ext_part_t ** epp)263 libfdisk_fini(ext_part_t **epp)
264 {
265 	if (*epp == NULL)
266 		return;
267 
268 	fdisk_free_ld_nodes(*epp);
269 	(void) close((*epp)->dev_fd);
270 	free(*epp);
271 	*epp = NULL;
272 }
273 
274 int
fdisk_is_linux_swap(ext_part_t * epp,uint32_t part_start,uint64_t * lsm_offset)275 fdisk_is_linux_swap(ext_part_t *epp, uint32_t part_start, uint64_t *lsm_offset)
276 {
277 	int		i;
278 	int		rval = -1;
279 	off_t		seek_offset;
280 	uint32_t	linux_pg_size;
281 	char		*buf, *linux_swap_magic;
282 	int		sec_sz = fdisk_get_disk_geom(epp, PHYSGEOM, SSIZE);
283 	off_t		label_offset;
284 
285 	/*
286 	 * Known linux kernel page sizes
287 	 * The linux swap magic is found as the last 10 bytes of a disk chunk
288 	 * at the beginning of the linux swap partition whose size is that of
289 	 * kernel page size.
290 	 */
291 	uint32_t	linux_pg_size_arr[] = {4096, };
292 
293 	if ((buf = calloc(1, sec_sz)) == NULL) {
294 		return (ENOMEM);
295 	}
296 
297 	/*
298 	 * Check if there is a sane Solaris VTOC
299 	 * If there is a valid vtoc, no need to lookup
300 	 * for the linux swap signature.
301 	 */
302 	label_offset = (part_start + DK_LABEL_LOC) * sec_sz;
303 	if (lseek(epp->dev_fd, label_offset, SEEK_SET) < 0) {
304 		rval = EIO;
305 		goto done;
306 	}
307 
308 	if ((rval = read(epp->dev_fd, buf, sec_sz)) < sec_sz) {
309 		rval = EIO;
310 		goto done;
311 	}
312 
313 
314 	if ((((struct dk_label *)buf)->dkl_magic == DKL_MAGIC) &&
315 	    (((struct dk_label *)buf)->dkl_vtoc.v_sanity == VTOC_SANE)) {
316 		rval = -1;
317 		goto done;
318 	}
319 
320 	/* No valid vtoc, so check for linux swap signature */
321 	linux_swap_magic = buf + sec_sz - LINUX_SWAP_MAGIC_LENGTH;
322 
323 	for (i = 0; i < sizeof (linux_pg_size_arr)/sizeof (uint32_t); i++) {
324 		linux_pg_size = linux_pg_size_arr[i];
325 		seek_offset = linux_pg_size/sec_sz - 1;
326 		seek_offset += part_start;
327 		seek_offset *= sec_sz;
328 
329 		if (lseek(epp->dev_fd, seek_offset, SEEK_SET) < 0) {
330 			rval = EIO;
331 			break;
332 		}
333 
334 		if ((rval = read(epp->dev_fd, buf, sec_sz)) < sec_sz) {
335 			rval = EIO;
336 			break;
337 		}
338 
339 		if ((strncmp(linux_swap_magic, "SWAP-SPACE",
340 		    LINUX_SWAP_MAGIC_LENGTH) == 0) ||
341 		    (strncmp(linux_swap_magic, "SWAPSPACE2",
342 		    LINUX_SWAP_MAGIC_LENGTH) == 0)) {
343 			/* Found a linux swap */
344 			rval = 0;
345 			if (lsm_offset != NULL)
346 				*lsm_offset = (uint64_t)seek_offset;
347 			break;
348 		}
349 	}
350 
351 done:
352 	free(buf);
353 	return (rval);
354 }
355 
356 int
fdisk_get_solaris_part(ext_part_t * epp,int * pnum,uint32_t * begsec,uint32_t * numsec)357 fdisk_get_solaris_part(ext_part_t *epp, int *pnum, uint32_t *begsec,
358     uint32_t *numsec)
359 {
360 	logical_drive_t *temp = fdisk_get_ld_head(epp);
361 	uint32_t part_start;
362 	int pno;
363 	int rval = -1;
364 
365 	for (pno = 5; temp != NULL; temp = temp->next, pno++) {
366 		if (fdisk_is_solaris_part(LE_8(temp->parts[0].systid))) {
367 			part_start = temp->abs_secnum + temp->logdrive_offset;
368 			if ((temp->parts[0].systid == SUNIXOS) &&
369 			    (fdisk_is_linux_swap(epp, part_start,
370 			    NULL) == 0)) {
371 				continue;
372 			}
373 			*pnum = pno;
374 			*begsec = part_start;
375 			*numsec = temp->numsect;
376 			rval = FDISK_SUCCESS;
377 		}
378 	}
379 	return (rval);
380 }
381 
382 int
fdisk_get_part_info(ext_part_t * epp,int pnum,uchar_t * sysid,uint32_t * begsec,uint32_t * numsec)383 fdisk_get_part_info(ext_part_t *epp, int pnum, uchar_t *sysid, uint32_t *begsec,
384     uint32_t *numsec)
385 {
386 	logical_drive_t *temp = fdisk_get_ld_head(epp);
387 	int pno;
388 
389 	if ((pnum < 5) || (pnum >= MAX_EXT_PARTS + 5)) {
390 		return (EINVAL);
391 	}
392 
393 	for (pno = 5; (pno < pnum) && (temp != NULL); temp = temp->next, pno++)
394 		;
395 
396 	if (temp == NULL) {
397 		return (EINVAL);
398 	}
399 
400 	*sysid = LE_8(temp->parts[0].systid);
401 	*begsec = temp->abs_secnum + temp->logdrive_offset;
402 	*numsec = temp->numsect;
403 	return (FDISK_SUCCESS);
404 }
405 
406 /*
407  * Allocate a node of type logical_drive_t and return the pointer to it
408  */
409 static logical_drive_t *
fdisk_alloc_ld_node()410 fdisk_alloc_ld_node()
411 {
412 	logical_drive_t *temp;
413 
414 	if ((temp = calloc(1, sizeof (logical_drive_t))) == NULL) {
415 		return (NULL);
416 	}
417 	temp->next = NULL;
418 	return (temp);
419 }
420 
421 /*
422  * Free all the logical_drive_t's allocated during the run
423  */
424 static void
fdisk_free_ld_nodes(ext_part_t * epp)425 fdisk_free_ld_nodes(ext_part_t *epp)
426 {
427 	logical_drive_t *temp;
428 
429 	for (temp = epp->ld_head; temp != NULL; ) {
430 		temp = epp->ld_head -> next;
431 		free(epp->ld_head);
432 		epp->ld_head = temp;
433 	}
434 	epp->ld_head = NULL;
435 	epp->sorted_ld_head = NULL;
436 }
437 
438 /*
439  * Find the first free sector within the extended partition
440  */
441 int
fdisk_ext_find_first_free_sec(ext_part_t * epp,uint32_t * first_free_sec)442 fdisk_ext_find_first_free_sec(ext_part_t *epp, uint32_t *first_free_sec)
443 {
444 	logical_drive_t *temp;
445 	uint32_t last_free_sec;
446 
447 	*first_free_sec = epp->ext_beg_sec;
448 
449 	if (epp->ld_head == NULL) {
450 		return (FDISK_SUCCESS);
451 	}
452 
453 	/*
454 	 * When the first logical drive is out of order, we need to adjust
455 	 * first_free_sec accordingly. In this case, the first extended
456 	 * partition sector is not free even though the actual logical drive
457 	 * does not occupy space from the beginning of the extended partition.
458 	 * The next free sector would be the second sector of the extended
459 	 * partition.
460 	 */
461 	if (epp->ld_head->abs_secnum > epp->ext_beg_sec +
462 	    MAX_LOGDRIVE_OFFSET) {
463 		(*first_free_sec)++;
464 	}
465 
466 	while (*first_free_sec <= epp->ext_end_sec) {
467 		for (temp = epp->sorted_ld_head; temp != NULL; temp =
468 		    temp->sorted_next) {
469 			if (temp->abs_secnum == *first_free_sec) {
470 				*first_free_sec = temp->abs_secnum +
471 				    temp->logdrive_offset + temp->numsect;
472 			}
473 		}
474 
475 		last_free_sec = fdisk_ext_find_last_free_sec(epp,
476 		    *first_free_sec);
477 
478 		if ((last_free_sec - *first_free_sec) < MAX_LOGDRIVE_OFFSET) {
479 			/*
480 			 * Minimum size of a partition assumed to be atleast one
481 			 * sector.
482 			 */
483 			*first_free_sec = last_free_sec + 1;
484 			continue;
485 		}
486 
487 		break;
488 	}
489 
490 	if (*first_free_sec > epp->ext_end_sec) {
491 		return (FDISK_EOOBOUND);
492 	}
493 
494 	return (FDISK_SUCCESS);
495 }
496 
497 /*
498  * Find the last free sector within the extended partition given, a beginning
499  * sector (so that the range - "begsec to last_free_sec" is contiguous)
500  */
501 uint32_t
fdisk_ext_find_last_free_sec(ext_part_t * epp,uint32_t begsec)502 fdisk_ext_find_last_free_sec(ext_part_t *epp, uint32_t begsec)
503 {
504 	logical_drive_t *temp;
505 	uint32_t last_free_sec;
506 
507 	last_free_sec = epp->ext_end_sec;
508 	for (temp = epp->sorted_ld_head; temp != NULL;
509 	    temp = temp->sorted_next) {
510 		if (temp->abs_secnum > begsec) {
511 			last_free_sec = temp->abs_secnum - 1;
512 			break;
513 		}
514 	}
515 	return (last_free_sec);
516 }
517 
518 /*
519  * Place the given ext_part_t structure in a sorted list, sorted in the
520  * ascending order of their beginning sectors.
521  */
522 static void
fdisk_ext_place_in_sorted_list(ext_part_t * epp,logical_drive_t * newld)523 fdisk_ext_place_in_sorted_list(ext_part_t *epp, logical_drive_t *newld)
524 {
525 	logical_drive_t *pre, *cur;
526 
527 	if (newld->abs_secnum < epp->sorted_ld_head->abs_secnum) {
528 		newld->sorted_next = epp->sorted_ld_head;
529 		epp->sorted_ld_head = newld;
530 		return;
531 	}
532 	pre = cur = epp->sorted_ld_head;
533 
534 	for (; cur != NULL; pre = cur, cur = cur->sorted_next) {
535 		if (newld->abs_secnum < cur->abs_secnum) {
536 			break;
537 		}
538 	}
539 
540 	newld->sorted_next = cur;
541 	pre->sorted_next = newld;
542 }
543 
544 static void
fdisk_ext_remove_from_sorted_list(ext_part_t * epp,logical_drive_t * delld)545 fdisk_ext_remove_from_sorted_list(ext_part_t *epp, logical_drive_t *delld)
546 {
547 	logical_drive_t *pre, *cur;
548 
549 	if (delld == epp->sorted_ld_head) {
550 		epp->sorted_ld_head = delld->sorted_next;
551 		return;
552 	}
553 
554 	pre = cur = epp->sorted_ld_head;
555 
556 	for (; cur != NULL; pre = cur, cur = cur->sorted_next) {
557 		if (cur->abs_secnum == delld->abs_secnum) {
558 			/* Found */
559 			break;
560 		}
561 	}
562 
563 	pre->sorted_next = cur->sorted_next;
564 }
565 
566 static int
fdisk_ext_overlapping_parts(ext_part_t * epp,uint32_t begsec,uint32_t endsec)567 fdisk_ext_overlapping_parts(ext_part_t *epp, uint32_t begsec, uint32_t endsec)
568 {
569 	logical_drive_t *temp;
570 	uint32_t firstsec, lastsec, last_free_sec;
571 
572 	for (temp = epp->ld_head; temp != NULL; temp = temp->next) {
573 		firstsec = temp->abs_secnum;
574 		lastsec = firstsec + temp->logdrive_offset + temp->numsect - 1;
575 		if ((begsec >= firstsec) &&
576 		    (begsec <= lastsec)) {
577 			return (1);
578 		}
579 	}
580 
581 	/*
582 	 * Find the maximum possible end sector value
583 	 * given a beginning sector value
584 	 */
585 	last_free_sec = fdisk_ext_find_last_free_sec(epp, begsec);
586 
587 	if (endsec > last_free_sec) {
588 		return (1);
589 	}
590 	return (0);
591 }
592 
593 /*
594  * Check if the logical drive boundaries are sane
595  */
596 int
fdisk_validate_logical_drive(ext_part_t * epp,uint32_t begsec,uint32_t offset,uint32_t numsec)597 fdisk_validate_logical_drive(ext_part_t *epp, uint32_t begsec,
598     uint32_t offset, uint32_t numsec)
599 {
600 	uint32_t endsec;
601 
602 	endsec = begsec + offset + numsec - 1;
603 	if (begsec < epp->ext_beg_sec ||
604 	    begsec > epp->ext_end_sec ||
605 	    endsec < epp->ext_beg_sec ||
606 	    endsec > epp->ext_end_sec ||
607 	    endsec < begsec ||
608 	    fdisk_ext_overlapping_parts(epp, begsec, endsec)) {
609 		return (1);
610 	}
611 
612 	return (0);
613 }
614 
615 /*
616  * Procedure to walk through the extended partitions and build a Singly
617  * Linked List out of the data.
618  */
619 static int
fdisk_read_extpart(ext_part_t * epp)620 fdisk_read_extpart(ext_part_t *epp)
621 {
622 	struct ipart *fdp, *ext_fdp;
623 	int i = 0, j = 0, ext_part_found = 0, lpart = 5;
624 	off_t secnum, offset;
625 	logical_drive_t *temp, *ep_ptr;
626 	unsigned char *ext_buf;
627 	int sectsize = epp->disk_geom.sectsize;
628 
629 	if ((ext_buf = (uchar_t *)malloc(sectsize)) == NULL) {
630 		return (ENOMEM);
631 	}
632 	fdp = epp->mtable;
633 
634 	for (i = 0; (i < FD_NUMPART) && (!ext_part_found); i++, fdp++) {
635 		if (fdisk_is_dos_extended(LE_8(fdp->systid))) {
636 			ext_part_found = 1;
637 			secnum = LE_32(fdp->relsect);
638 			offset = secnum * sectsize;
639 			epp->ext_beg_sec = secnum;
640 			epp->ext_end_sec = secnum + LE_32(fdp->numsect) - 1;
641 			epp->ext_beg_cyl =
642 			    FDISK_SECT_TO_CYL(epp, epp->ext_beg_sec);
643 			epp->ext_end_cyl =
644 			    FDISK_SECT_TO_CYL(epp, epp->ext_end_sec);
645 
646 			/*LINTED*/
647 			while (B_TRUE) {
648 				if (lseek(epp->dev_fd, offset, SEEK_SET) < 0) {
649 					return (EIO);
650 				}
651 				if (read(epp->dev_fd, ext_buf, sectsize) <
652 				    sectsize) {
653 					return (EIO);
654 				}
655 				/*LINTED*/
656 				ext_fdp = (struct ipart *)
657 				    (&ext_buf[FDISK_PART_TABLE_START]);
658 				if ((LE_32(ext_fdp->relsect) == 0) &&
659 				    (epp->logical_drive_count == 0)) {
660 					/* No logical drives defined */
661 					epp->first_ebr_is_null = 0;
662 					return (FDISK_ENOLOGDRIVE);
663 				}
664 
665 				temp = fdisk_alloc_ld_node();
666 				temp->abs_secnum = secnum;
667 				temp->logdrive_offset =
668 				    LE_32(ext_fdp->relsect);
669 				temp ->numsect = LE_32(ext_fdp->numsect);
670 				if (epp->ld_head == NULL) {
671 					/* adding first logical drive */
672 					if (temp->logdrive_offset >
673 					    MAX_LOGDRIVE_OFFSET) {
674 						/* out of order */
675 						temp->abs_secnum +=
676 						    temp->logdrive_offset;
677 						temp->logdrive_offset = 0;
678 					}
679 				}
680 				temp->begcyl =
681 				    FDISK_SECT_TO_CYL(epp, temp->abs_secnum);
682 				temp->endcyl = FDISK_SECT_TO_CYL(epp,
683 				    temp->abs_secnum +
684 				    temp->logdrive_offset +
685 				    temp->numsect - 1);
686 
687 				/*
688 				 * Check for sanity of logical drives
689 				 */
690 				if (fdisk_validate_logical_drive(epp,
691 				    temp->abs_secnum, temp->logdrive_offset,
692 				    temp->numsect)) {
693 					epp->corrupt_logical_drives = 1;
694 					free(temp);
695 					return (FDISK_EBADLOGDRIVE);
696 				}
697 
698 				temp->parts[0] = *ext_fdp;
699 				ext_fdp++;
700 				temp->parts[1] = *ext_fdp;
701 
702 				if (epp->ld_head == NULL) {
703 					epp->ld_head = temp;
704 					epp->sorted_ld_head = temp;
705 					ep_ptr = temp;
706 					epp->logical_drive_count = 1;
707 				} else {
708 					ep_ptr->next = temp;
709 					ep_ptr = temp;
710 					fdisk_ext_place_in_sorted_list(epp,
711 					    temp);
712 					epp->logical_drive_count++;
713 				}
714 
715 				/*LINTED*/
716 				if (LE_16((*(uint16_t *)&ext_buf[510])) !=
717 				    MBB_MAGIC) {
718 					epp->invalid_bb_sig[j++] = lpart;
719 					temp->modified = FDISK_MINOR_WRITE;
720 				}
721 
722 				if (LE_32(ext_fdp->relsect) == 0)
723 					break;
724 				else {
725 					secnum = LE_32(fdp->relsect) +
726 					    LE_32(ext_fdp->relsect);
727 					offset = secnum * sectsize;
728 				}
729 				lpart++;
730 			}
731 		}
732 	}
733 	return (FDISK_SUCCESS);
734 }
735 
736 static int
fdisk_init_master_part_table(ext_part_t * epp)737 fdisk_init_master_part_table(ext_part_t *epp)
738 {
739 	int rval;
740 	if ((epp->mtable = fdisk_alloc_part_table()) == NULL) {
741 		return (ENOMEM);
742 	}
743 	rval = fdisk_read_master_part_table(epp);
744 	if (rval) {
745 		return (rval);
746 	}
747 	return (FDISK_SUCCESS);
748 }
749 
750 static struct ipart *
fdisk_alloc_part_table()751 fdisk_alloc_part_table()
752 {
753 	int size = sizeof (struct ipart);
754 	struct ipart *table;
755 
756 	if ((table = calloc(4, size)) == NULL) {
757 		return (NULL);
758 	}
759 
760 	return (table);
761 }
762 
763 /*
764  * Reads the master fdisk partition table from the device assuming that it has
765  * a valid table.
766  * MBR is supposed to be of 512 bytes no matter what the device block size is.
767  */
768 static int
fdisk_read_master_part_table(ext_part_t * epp)769 fdisk_read_master_part_table(ext_part_t *epp)
770 {
771 	uchar_t buf[512];
772 	int sectsize = 512;
773 	int size = sizeof (struct ipart);
774 	int cpcnt = FD_NUMPART * size;
775 
776 	if (lseek(epp->dev_fd, 0, SEEK_SET) < 0) {
777 		return (EIO);
778 	}
779 	if (read(epp->dev_fd, buf, sectsize) < sectsize) {
780 		return (EIO);
781 	}
782 
783 	/*LINTED*/
784 	if (LE_16((*(uint16_t *)&buf[510])) != MBB_MAGIC) {
785 		bzero(epp->mtable, cpcnt);
786 		return (FDISK_EBADMAGIC);
787 	}
788 
789 	bcopy(&buf[FDISK_PART_TABLE_START], epp->mtable, cpcnt);
790 
791 	return (FDISK_SUCCESS);
792 }
793 
794 int
fdisk_ext_part_exists(ext_part_t * epp)795 fdisk_ext_part_exists(ext_part_t *epp)
796 {
797 	int i;
798 	struct ipart *part_table = epp->mtable;
799 
800 	if (part_table == NULL) {
801 		/* No extended partition found */
802 		return (0);
803 	}
804 
805 	for (i = 0; i < FD_NUMPART; i++) {
806 		if (fdisk_is_dos_extended(LE_8(part_table[i].systid))) {
807 			break;
808 		}
809 	}
810 
811 	if (i == FD_NUMPART) {
812 		/* No extended partition found */
813 		return (0);
814 	}
815 	return (1);
816 }
817 
818 int
fdisk_ext_validate_part_start(ext_part_t * epp,uint32_t begcyl,uint32_t * begsec)819 fdisk_ext_validate_part_start(ext_part_t *epp, uint32_t begcyl,
820     uint32_t *begsec)
821 {
822 	logical_drive_t *temp;
823 	uint32_t first_free_sec;
824 	uint32_t first_free_cyl;
825 	int rval;
826 
827 	rval = fdisk_ext_find_first_free_sec(epp, &first_free_sec);
828 	if (rval != FDISK_SUCCESS) {
829 		return (rval);
830 	}
831 
832 	first_free_cyl = FDISK_SECT_TO_CYL(epp, first_free_sec);
833 	if (begcyl == first_free_cyl) {
834 		*begsec = first_free_sec;
835 		return (FDISK_SUCCESS);
836 	}
837 
838 	/* Check if the cylinder number is beyond the extended partition */
839 	if ((begcyl < epp->ext_beg_cyl) || (begcyl > epp->ext_end_cyl)) {
840 		return (FDISK_EOOBOUND);
841 	}
842 
843 	for (temp = epp->ld_head; temp != NULL; temp = temp->next) {
844 		if ((begcyl >= temp->begcyl) &&
845 		    (begcyl <= temp->endcyl)) {
846 			return (FDISK_EOVERLAP);
847 		}
848 	}
849 	*begsec = FDISK_CYL_TO_SECT(epp, begcyl);
850 
851 	return (FDISK_SUCCESS);
852 }
853 
854 void
fdisk_change_logical_drive_id(ext_part_t * epp,int pno,uchar_t partid)855 fdisk_change_logical_drive_id(ext_part_t *epp, int pno, uchar_t partid)
856 {
857 	logical_drive_t *temp;
858 	int i;
859 
860 	i = FD_NUMPART + 1;
861 	for (temp = epp->ld_head; i < pno; temp = temp->next, i++)
862 		;
863 
864 	temp->parts[0].systid = LE_8(partid);
865 	temp->modified = FDISK_MAJOR_WRITE;
866 }
867 
868 /*
869  * A couple of special scenarios :
870  * 1. Since the first logical drive's EBR is always at the beginning of the
871  * extended partition, any specification that starts the first logical drive
872  * out of order will need to address the following issue :
873  * If the beginning of the drive is not coinciding with the beginning of the
874  * extended partition  and :
875  * a) The start is within MAX_LOGDRIVE_OFFSET, the offset changes from the
876  *	default of 63 to less than 63.
877  *	logdrive_offset is updated to keep track of the space between
878  *	the beginning of the logical drive and extended partition. abs_secnum
879  *	points to the beginning of the extended partition.
880  * b) The start is greater than MAX_LOGDRIVE_OFFSET, the offset changes from
881  *	the default of 63 to greater than 63.
882  *	logdrive_offset is set to 0. abs_secnum points to the beginning of the
883  *	logical drive, which is at an offset from the extended partition.
884  */
885 void
fdisk_add_logical_drive(ext_part_t * epp,uint32_t begsec,uint32_t endsec,uchar_t partid)886 fdisk_add_logical_drive(ext_part_t *epp, uint32_t begsec, uint32_t endsec,
887     uchar_t partid)
888 {
889 	logical_drive_t *temp, *pre, *cur;
890 	struct ipart *part;
891 
892 	temp = fdisk_alloc_ld_node();
893 	temp->abs_secnum = begsec;
894 	temp->logdrive_offset = MAX_LOGDRIVE_OFFSET;
895 	temp->numsect = endsec - begsec + 1 - MAX_LOGDRIVE_OFFSET;
896 	temp->begcyl = FDISK_SECT_TO_CYL(epp, begsec);
897 	temp->endcyl = FDISK_SECT_TO_CYL(epp, endsec);
898 	temp->modified = FDISK_MAJOR_WRITE;
899 
900 	part 		= &temp->parts[0];
901 	part->bootid	= 0;
902 	part->systid	= LE_8(partid);
903 	part->relsect	= MAX_LOGDRIVE_OFFSET;
904 	part->numsect	= LE_32(temp->numsect);
905 
906 	fdisk_set_CHS_values(epp, part);
907 
908 	if (epp->ld_head == NULL) {
909 		epp->corrupt_logical_drives = 0;
910 		if (begsec != epp->ext_beg_sec) {
911 			part->relsect = LE_32(begsec - epp->ext_beg_sec);
912 			temp->numsect = endsec - begsec + 1;
913 			part->numsect = LE_32(temp->numsect);
914 			if (LE_32(part->relsect) > MAX_LOGDRIVE_OFFSET) {
915 				temp->logdrive_offset = 0;
916 			} else {
917 				temp->abs_secnum = epp->ext_beg_sec;
918 				temp->logdrive_offset = LE_32(part->relsect);
919 			}
920 		}
921 		epp->first_ebr_is_null = 0;
922 		epp->ld_head = temp;
923 		epp->sorted_ld_head = temp;
924 		epp->logical_drive_count = 1;
925 		return;
926 	}
927 
928 	if (temp->abs_secnum == epp->ext_beg_sec) {
929 		part->relsect = LE_32(LE_32(part->relsect) - 1);
930 		temp->logdrive_offset--;
931 		temp->abs_secnum++;
932 	}
933 
934 	for (pre = cur = epp->ld_head; cur != NULL; pre = cur, cur = cur->next)
935 		;
936 
937 	part = &pre->parts[1];
938 	part->bootid	= 0;
939 	part->systid	= LE_8(EXTDOS);
940 	part->relsect	= LE_32(temp->abs_secnum - epp->ext_beg_sec);
941 	part->numsect	= LE_32(temp->numsect + temp->logdrive_offset);
942 
943 	fdisk_set_CHS_values(epp, part);
944 
945 	pre->next = temp;
946 	pre->modified = FDISK_MAJOR_WRITE;
947 	epp->logical_drive_count++;
948 	fdisk_ext_place_in_sorted_list(epp, temp);
949 }
950 
951 /*
952  * There are 2 cases that need to be handled.
953  * 1. Deleting the first extended partition :
954  *	The peculiarity of this case is that the offset of the first extended
955  *	partition is always indicated by the entry in the master boot record.
956  *	(MBR). This never changes, unless the extended partition itself is
957  *	deleted. Hence, the location of the first EBR is fixed.
958  *	It is only the logical drive which is deleted. This first EBR now gives
959  *	information of the next logical drive and the info about the subsequent
960  *	extended partition. Hence the "relsect" of the first EBR is modified to
961  *	point to the next logical drive.
962  *
963  * 2. Deleting an intermediate extended partition.
964  *	This is quite normal and follows the semantics of a normal linked list
965  *	delete operation. The node being deleted has the information about the
966  *	logical drive that it houses and the location and the size of the next
967  *	extended partition. This informationis transferred to the node previous
968  *	to the node being deleted.
969  *
970  */
971 
972 void
fdisk_delete_logical_drive(ext_part_t * epp,int pno)973 fdisk_delete_logical_drive(ext_part_t *epp, int pno)
974 {
975 	logical_drive_t *pre, *cur;
976 	int i;
977 
978 	i = FD_NUMPART + 1;
979 	pre = cur = epp->ld_head;
980 	for (; i < pno; i++) {
981 		pre = cur;
982 		cur = cur->next;
983 	}
984 
985 	if (cur == epp->ld_head) {
986 		/* Deleting the first logical drive */
987 		if (cur->next == NULL) {
988 			/* Deleting the only logical drive left */
989 			free(cur);
990 			epp->ld_head = NULL;
991 			epp->sorted_ld_head = NULL;
992 			epp->logical_drive_count = 0;
993 			epp->first_ebr_is_null = 1;
994 		} else {
995 			pre = epp->ld_head;
996 			cur = pre->next;
997 			cur->parts[0].relsect =
998 			    LE_32(LE_32(cur->parts[0].relsect) +
999 			    LE_32(pre->parts[1].relsect));
1000 			/* Corner case when partitions are out of order */
1001 			if ((pre->abs_secnum != epp->ext_beg_sec) &&
1002 			    (cur->abs_secnum == epp->ext_beg_sec + 1)) {
1003 				cur->logdrive_offset++;
1004 				cur->abs_secnum = epp->ext_beg_sec;
1005 			} else {
1006 				cur->abs_secnum = LE_32(cur->parts[0].relsect) +
1007 				    epp->ext_beg_sec;
1008 				cur->logdrive_offset = 0;
1009 			}
1010 			fdisk_ext_remove_from_sorted_list(epp, pre);
1011 			epp->ld_head = cur;
1012 			epp->ld_head->modified = FDISK_MAJOR_WRITE;
1013 			epp->logical_drive_count--;
1014 			free(pre);
1015 		}
1016 	} else {
1017 		pre->parts[1] = cur->parts[1];
1018 		pre->next = cur->next;
1019 		fdisk_ext_remove_from_sorted_list(epp, cur);
1020 		pre->modified = FDISK_MAJOR_WRITE;
1021 		free(cur);
1022 		epp->logical_drive_count--;
1023 	}
1024 }
1025 
1026 static void
fdisk_set_CHS_values(ext_part_t * epp,struct ipart * part)1027 fdisk_set_CHS_values(ext_part_t *epp, struct ipart *part)
1028 {
1029 	uint32_t	lba, cy, hd, sc;
1030 	uint32_t	sectors = epp->disk_geom.virt_sec;
1031 	uint32_t	heads = epp->disk_geom.virt_heads;
1032 
1033 	lba = LE_32(part->relsect) + epp->ext_beg_sec;
1034 	if (lba >= heads * sectors * MAX_CYL) {
1035 		/*
1036 		 * the lba address cannot be expressed in CHS value
1037 		 * so store the maximum CHS field values in the CHS fields.
1038 		 */
1039 		cy = MAX_CYL + 1;
1040 		hd = MAX_HEAD;
1041 		sc = MAX_SECT;
1042 	} else {
1043 		cy = lba / sectors / heads;
1044 		hd = lba / sectors % heads;
1045 		sc = lba % sectors + 1;
1046 	}
1047 
1048 	part->begcyl = cy & 0xff;
1049 	part->beghead = (uchar_t)hd;
1050 	part->begsect = (uchar_t)(((cy >> 2) & 0xc0) | sc);
1051 
1052 	/*
1053 	 * This code is identical to the code above
1054 	 * except that it works on ending CHS values
1055 	 */
1056 	lba += LE_32(part->numsect - 1);
1057 	if (lba >= heads * sectors * MAX_CYL) {
1058 		cy = MAX_CYL + 1;
1059 		hd = MAX_HEAD;
1060 		sc = MAX_SECT;
1061 	} else {
1062 		cy = lba / sectors / heads;
1063 		hd = lba / sectors % heads;
1064 		sc = lba % sectors + 1;
1065 	}
1066 	part->endcyl = cy & 0xff;
1067 	part->endhead = (uchar_t)hd;
1068 	part->endsect = (uchar_t)(((cy >> 2) & 0xc0) | sc);
1069 }
1070 
1071 static int
read_modify_write_ebr(ext_part_t * epp,unsigned char * ebr_buf,struct ipart * ebr_tab,uint32_t sec_offset)1072 read_modify_write_ebr(ext_part_t *epp, unsigned char *ebr_buf,
1073     struct ipart *ebr_tab, uint32_t sec_offset)
1074 {
1075 	off_t seek_offset;
1076 	int sectsize = epp->disk_geom.sectsize;
1077 
1078 	seek_offset = (off_t)sec_offset * sectsize;
1079 
1080 	if (lseek(epp->dev_fd, seek_offset, SEEK_SET) < 0) {
1081 		return (EIO);
1082 	}
1083 	if (read(epp->dev_fd, ebr_buf, sectsize) < sectsize) {
1084 		return (EIO);
1085 	}
1086 
1087 	bzero(&ebr_buf[FDISK_PART_TABLE_START], 4 * sizeof (struct ipart));
1088 	if (ebr_tab != NULL) {
1089 		bcopy(ebr_tab, &ebr_buf[FDISK_PART_TABLE_START],
1090 		    2 * sizeof (struct ipart));
1091 	}
1092 	ebr_buf[510] = 0x55;
1093 	ebr_buf[511] = 0xAA;
1094 	if (lseek(epp->dev_fd, seek_offset, SEEK_SET) < 0) {
1095 		return (EIO);
1096 	}
1097 	if (write(epp->dev_fd, ebr_buf, sectsize) < sectsize) {
1098 		return (EIO);
1099 	}
1100 	return (0);
1101 }
1102 
1103 /*
1104  * XXX - ZFS mounts not detected. Needs to come in as a feature.
1105  * Currently only /etc/mnttab entries are being checked
1106  */
1107 int
fdisk_mounted_logical_drives(ext_part_t * epp)1108 fdisk_mounted_logical_drives(ext_part_t *epp)
1109 {
1110 	char *part_str, *canonp;
1111 	char compare_pdev_str[PATH_MAX];
1112 	char compare_sdev_str[PATH_MAX];
1113 	FILE *fp;
1114 	struct mnttab mt;
1115 	int part;
1116 	int look_for_mounted_slices = 0;
1117 	uint32_t begsec, numsec;
1118 
1119 	/*
1120 	 * Do not check for mounted logical drives for
1121 	 * devices other than /dev/rdsk/
1122 	 */
1123 	if (strstr(epp->device_name, DEFAULT_PATH_PREFIX) == NULL) {
1124 		return (0);
1125 	}
1126 
1127 	if ((fp = fopen(MNTTAB, "r")) == NULL) {
1128 		return (ENOENT);
1129 	}
1130 
1131 	canonp = epp->device_name + strlen(DEFAULT_PATH_PREFIX);
1132 	(void) snprintf(compare_pdev_str, PATH_MAX, "%s%s", "/dev/dsk/",
1133 	    canonp);
1134 	part_str = strrchr(compare_pdev_str, 'p');
1135 	*(part_str + 1) = '\0';
1136 	(void) strcpy(compare_sdev_str, compare_pdev_str);
1137 	part_str = strrchr(compare_sdev_str, 'p');
1138 	*part_str = 's';
1139 
1140 	if (fdisk_get_solaris_part(epp, &part, &begsec, &numsec) ==
1141 	    FDISK_SUCCESS) {
1142 		if (part > FD_NUMPART) {
1143 			/*
1144 			 * Solaris partition is on a logical drive. Look for
1145 			 * mounted slices.
1146 			 */
1147 			look_for_mounted_slices = 1;
1148 		}
1149 	}
1150 
1151 	while (getmntent(fp, &mt) == 0) {
1152 		if (strstr(mt.mnt_special, compare_pdev_str) == NULL) {
1153 			if (strstr(mt.mnt_special, compare_sdev_str) == NULL) {
1154 				continue;
1155 			} else {
1156 				if (look_for_mounted_slices) {
1157 					return (FDISK_EMOUNTED);
1158 				}
1159 			}
1160 		}
1161 
1162 		/*
1163 		 * Get the partition number that is mounted, which would be
1164 		 * found just beyond the last 'p' in the device string.
1165 		 * For example, in /dev/dsk/c0t0d0p12, partition number 12
1166 		 * is just beyond the last 'p'.
1167 		 */
1168 		part_str = strrchr(mt.mnt_special, 'p');
1169 		if (part_str != NULL) {
1170 			part_str++;
1171 			part = atoi(part_str);
1172 			/* Extended partition numbers start from 5 */
1173 			if (part >= 5) {
1174 				return (FDISK_EMOUNTED);
1175 			}
1176 		}
1177 	}
1178 	return (0);
1179 }
1180 
1181 int
fdisk_commit_ext_part(ext_part_t * epp)1182 fdisk_commit_ext_part(ext_part_t *epp)
1183 {
1184 	logical_drive_t *temp;
1185 	int wflag = 0;		/* write flag */
1186 	int rval;
1187 	int sectsize = epp->disk_geom.sectsize;
1188 	unsigned char *ebr_buf;
1189 	int ld_count;
1190 	uint32_t abs_secnum;
1191 	int check_mounts = 0;
1192 
1193 	if ((ebr_buf = (unsigned char *)malloc(sectsize)) == NULL) {
1194 		return (ENOMEM);
1195 	}
1196 
1197 	if (epp->first_ebr_is_null) {
1198 		/*
1199 		 * Indicator that the extended partition as a whole was
1200 		 * modifies (either created or deleted. Must check for mounts
1201 		 * and must commit
1202 		 */
1203 		check_mounts = 1;
1204 	}
1205 
1206 	/*
1207 	 * Pass1 through the logical drives to make sure that commit of minor
1208 	 * written block dont get held up due to mounts.
1209 	 */
1210 	for (temp = epp->ld_head; temp != NULL; temp = temp->next) {
1211 		if (temp == epp->ld_head) {
1212 			abs_secnum = epp->ext_beg_sec;
1213 		} else {
1214 			abs_secnum = temp->abs_secnum;
1215 		}
1216 		if (temp->modified == FDISK_MINOR_WRITE) {
1217 			rval = read_modify_write_ebr(epp, ebr_buf,
1218 			    temp->parts, abs_secnum);
1219 			if (rval) {
1220 				goto error;
1221 			}
1222 			temp->modified = 0;
1223 		} else if (temp->modified == FDISK_MAJOR_WRITE) {
1224 			check_mounts = 1;
1225 		}
1226 	}
1227 
1228 	if (!check_mounts) {
1229 		goto skip_check_mounts;
1230 	}
1231 
1232 	if ((rval = fdisk_mounted_logical_drives(epp)) != 0) {
1233 		/* One/more extended partitions are mounted */
1234 		if (ebr_buf) {
1235 			free(ebr_buf);
1236 		}
1237 		return (rval);
1238 	}
1239 
1240 skip_check_mounts:
1241 
1242 	if (epp->first_ebr_is_null) {
1243 		rval = read_modify_write_ebr(epp, ebr_buf, NULL,
1244 		    epp->ext_beg_sec);
1245 		if (rval) {
1246 			goto error;
1247 		}
1248 		wflag = 1;
1249 		ld_count = 0;
1250 	} else {
1251 		if (epp->logical_drive_count == 0) {
1252 			/*
1253 			 * Can hit this case when there is just an extended
1254 			 * partition with no logical drives, and the user
1255 			 * committed without making any changes
1256 			 * We dont have anything to commit. Return success
1257 			 */
1258 			if (ebr_buf) {
1259 				free(ebr_buf);
1260 			}
1261 			return (FDISK_SUCCESS);
1262 		}
1263 
1264 		/*
1265 		 * Make sure that the first EBR is written with the first
1266 		 * logical drive's data, which might not be the first in disk
1267 		 * order.
1268 		 */
1269 		for (temp = epp->ld_head, ld_count = 0; temp != NULL;
1270 		    temp = temp->next, ld_count++) {
1271 			if (ld_count == 0) {
1272 				abs_secnum = epp->ext_beg_sec;
1273 			} else {
1274 				abs_secnum = temp->abs_secnum;
1275 			}
1276 			if (temp->modified) {
1277 				rval = read_modify_write_ebr(epp, ebr_buf,
1278 				    temp->parts, abs_secnum);
1279 				if (rval) {
1280 					if (ld_count) {
1281 						/*
1282 						 * There was atleast one
1283 						 * write to the disk before
1284 						 * this failure. Make sure that
1285 						 * the kernel is notified.
1286 						 * Issue the ioctl.
1287 						 */
1288 						break;
1289 					}
1290 					goto error;
1291 				}
1292 				if ((!wflag) && (temp->modified ==
1293 				    FDISK_MAJOR_WRITE)) {
1294 					wflag = 1;
1295 				}
1296 			}
1297 		}
1298 
1299 		if (wflag == 0) {
1300 			/* No changes made */
1301 			rval = FDISK_SUCCESS;
1302 			goto error;
1303 		}
1304 	}
1305 
1306 	/* Issue ioctl to the driver to update extended partition info */
1307 	rval = ioctl(epp->dev_fd, DKIOCSETEXTPART);
1308 
1309 	/*
1310 	 * Certain devices ex:lofi do not support DKIOCSETEXTPART.
1311 	 * Extended partitions are still created on these devices.
1312 	 */
1313 	if (errno == ENOTTY)
1314 		rval = FDISK_SUCCESS;
1315 
1316 error:
1317 	if (ebr_buf) {
1318 		free(ebr_buf);
1319 	}
1320 	return (rval);
1321 }
1322 
1323 int
fdisk_init_ext_part(ext_part_t * epp,uint32_t rsect,uint32_t nsect)1324 fdisk_init_ext_part(ext_part_t *epp, uint32_t rsect, uint32_t nsect)
1325 {
1326 	epp->first_ebr_is_null = 1;
1327 	epp->corrupt_logical_drives = 0;
1328 	epp->logical_drive_count = 0;
1329 	epp->ext_beg_sec = rsect;
1330 	epp->ext_end_sec = rsect + nsect - 1;
1331 	epp->ext_beg_cyl = FDISK_SECT_TO_CYL(epp, epp->ext_beg_sec);
1332 	epp->ext_end_cyl = FDISK_SECT_TO_CYL(epp, epp->ext_end_sec);
1333 	epp->invalid_bb_sig[0] = 0;
1334 	return (0);
1335 }
1336 
1337 int
fdisk_delete_ext_part(ext_part_t * epp)1338 fdisk_delete_ext_part(ext_part_t *epp)
1339 {
1340 	epp->first_ebr_is_null = 1;
1341 	/* Clear the logical drive information */
1342 	fdisk_free_ld_nodes(epp);
1343 	epp->logical_drive_count = 0;
1344 	epp->corrupt_logical_drives = 0;
1345 	epp->invalid_bb_sig[0] = 0;
1346 	return (0);
1347 }
1348 
1349 int
fdisk_get_disk_geom(ext_part_t * epp,int type,int what)1350 fdisk_get_disk_geom(ext_part_t *epp, int type, int what)
1351 {
1352 	switch (type) {
1353 		case PHYSGEOM:
1354 			switch (what) {
1355 				case NCYL:
1356 					return ((int)epp->disk_geom.phys_cyl);
1357 				case NHEADS:
1358 					return ((int)epp->disk_geom.phys_heads);
1359 				case NSECTPT:
1360 					return ((int)epp->disk_geom.phys_sec);
1361 				case SSIZE:
1362 					return ((int)epp->disk_geom.sectsize);
1363 				case ACYL:
1364 					return ((int)epp->disk_geom.alt_cyl);
1365 				default:
1366 					return (EINVAL);
1367 			}
1368 		case VIRTGEOM:
1369 			switch (what) {
1370 				case NCYL:
1371 					return ((int)epp->disk_geom.virt_cyl);
1372 				case NHEADS:
1373 					return ((int)epp->disk_geom.virt_heads);
1374 				case NSECTPT:
1375 					return ((int)epp->disk_geom.virt_sec);
1376 				case SSIZE:
1377 					return ((int)epp->disk_geom.sectsize);
1378 				case ACYL:
1379 					return ((int)epp->disk_geom.alt_cyl);
1380 				default:
1381 					return (EINVAL);
1382 			}
1383 		default:
1384 			return (EINVAL);
1385 	}
1386 }
1387 
1388 int
fdisk_invalid_bb_sig(ext_part_t * epp,uchar_t ** bbsig_arr)1389 fdisk_invalid_bb_sig(ext_part_t *epp, uchar_t **bbsig_arr)
1390 {
1391 	*bbsig_arr = &(epp->invalid_bb_sig[0]);
1392 	return (epp->invalid_bb_sig[0]);
1393 }
1394