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.
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.
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]
22 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2017 The MathWorks, Inc. All rights reserved.
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/systeminfo.h>
38 #include <sys/efi_partition.h>
39 #include <sys/byteorder.h>
43 #include <sys/dktp/fdisk.h>
45 #include <sys/mnttab.h>
48 #define DEFAULT_PATH_PREFIX "/dev/rdsk/"
50 static void fdisk_free_ld_nodes(ext_part_t
*epp
);
51 static void fdisk_ext_place_in_sorted_list(ext_part_t
*epp
,
52 logical_drive_t
*newld
);
53 static void fdisk_ext_remove_from_sorted_list(ext_part_t
*epp
,
54 logical_drive_t
*delld
);
55 static int fdisk_ext_overlapping_parts(ext_part_t
*epp
, uint32_t begsec
,
57 static int fdisk_read_extpart(ext_part_t
*epp
);
58 static void fdisk_set_CHS_values(ext_part_t
*epp
, struct ipart
*part
);
59 static int fdisk_init_master_part_table(ext_part_t
*epp
);
60 static struct ipart
*fdisk_alloc_part_table();
61 static int fdisk_read_master_part_table(ext_part_t
*epp
);
64 fdisk_init_disk_geom(ext_part_t
*epp
)
66 struct dk_geom disk_geom
;
67 struct dk_minfo disk_info
;
68 int no_virtgeom_ioctl
= 0, no_physgeom_ioctl
= 0;
70 /* Get disk's HBA (virtual) geometry */
72 if (ioctl(epp
->dev_fd
, DKIOCG_VIRTGEOM
, &disk_geom
)) {
73 if (errno
== ENOTTY
) {
74 no_virtgeom_ioctl
= 1;
75 } else if (errno
== EINVAL
) {
77 * This means that the ioctl exists, but
78 * is invalid for this disk, meaning the
79 * disk doesn't have an HBA geometry
80 * (like, say, it's larger than 8GB).
82 epp
->disk_geom
.virt_cyl
= epp
->disk_geom
.virt_heads
=
83 epp
->disk_geom
.virt_sec
= 0;
85 return (FDISK_ENOVGEOM
);
88 /* save virtual geometry values obtained by ioctl */
89 epp
->disk_geom
.virt_cyl
= disk_geom
.dkg_ncyl
;
90 epp
->disk_geom
.virt_heads
= disk_geom
.dkg_nhead
;
91 epp
->disk_geom
.virt_sec
= disk_geom
.dkg_nsect
;
95 if (ioctl(epp
->dev_fd
, DKIOCG_PHYGEOM
, &disk_geom
)) {
96 if (errno
== ENOTTY
) {
97 no_physgeom_ioctl
= 1;
99 return (FDISK_ENOPGEOM
);
103 * Call DKIOCGGEOM if the ioctls for physical and virtual
104 * geometry fail. Get both from this generic call.
106 if (no_virtgeom_ioctl
&& no_physgeom_ioctl
) {
108 if (ioctl(epp
->dev_fd
, DKIOCGGEOM
, &disk_geom
)) {
109 return (FDISK_ENOLGEOM
);
113 epp
->disk_geom
.phys_cyl
= disk_geom
.dkg_ncyl
;
114 epp
->disk_geom
.phys_heads
= disk_geom
.dkg_nhead
;
115 epp
->disk_geom
.phys_sec
= disk_geom
.dkg_nsect
;
116 epp
->disk_geom
.alt_cyl
= disk_geom
.dkg_acyl
;
119 * If DKIOCGMEDIAINFO ioctl succeeds, set the dki_lbsize as the
120 * size of the sector, else default to 512
122 if (ioctl(epp
->dev_fd
, DKIOCGMEDIAINFO
, (caddr_t
)&disk_info
) < 0) {
123 /* ioctl failed, falling back to default value of 512 bytes */
124 epp
->disk_geom
.sectsize
= 512;
126 epp
->disk_geom
.sectsize
= ((disk_info
.dki_lbsize
) ?
127 disk_info
.dki_lbsize
: 512);
131 * if hba geometry was not set by DKIOC_VIRTGEOM
132 * or we got an invalid hba geometry
133 * then set hba geometry based on max values
135 if (no_virtgeom_ioctl
|| disk_geom
.dkg_ncyl
== 0 ||
136 disk_geom
.dkg_nhead
== 0 || disk_geom
.dkg_nsect
== 0 ||
137 disk_geom
.dkg_ncyl
> MAX_CYL
|| disk_geom
.dkg_nhead
> MAX_HEAD
||
138 disk_geom
.dkg_nsect
> MAX_SECT
) {
139 epp
->disk_geom
.virt_sec
= MAX_SECT
;
140 epp
->disk_geom
.virt_heads
= MAX_HEAD
+ 1;
141 epp
->disk_geom
.virt_cyl
= (epp
->disk_geom
.phys_cyl
*
142 epp
->disk_geom
.phys_heads
* epp
->disk_geom
.phys_sec
) /
143 (epp
->disk_geom
.virt_sec
* epp
->disk_geom
.virt_heads
);
145 return (FDISK_SUCCESS
);
149 * Initialise important members of the ext_part_t structure and
150 * other data structures vital to functionality of libfdisk
153 libfdisk_init(ext_part_t
**epp
, char *devstr
, struct ipart
*parttab
, int opflag
)
157 int rval
= FDISK_SUCCESS
;
158 int found_bad_magic
= 0;
160 if ((temp
= calloc(1, sizeof (ext_part_t
))) == NULL
) {
165 (void) strncpy(temp
->device_name
, devstr
,
166 sizeof (temp
->device_name
));
168 /* Try to stat the node as provided */
169 if (stat(temp
->device_name
, &sbuf
) != 0) {
171 /* Prefix /dev/rdsk/ and stat again */
172 (void) snprintf(temp
->device_name
, sizeof (temp
->device_name
),
173 "%s%s", DEFAULT_PATH_PREFIX
, devstr
);
175 if (stat(temp
->device_name
, &sbuf
) != 0) {
178 * In case of an EFI labeled disk, the device name
179 * could be cN[tN]dN. There is no pN. So we add "p0"
180 * at the end if we do not find it and stat again.
182 if (strrchr(temp
->device_name
, 'p') == NULL
) {
183 (void) strcat(temp
->device_name
, "p0");
186 if (stat(temp
->device_name
, &sbuf
) != 0) {
188 /* Failed all options, give up */
195 /* Make sure the device is a raw device */
196 if ((sbuf
.st_mode
& S_IFMT
) != S_IFCHR
) {
201 temp
->ld_head
= NULL
;
202 temp
->sorted_ld_head
= NULL
;
204 if ((temp
->dev_fd
= open(temp
->device_name
, O_RDWR
, 0666)) < 0) {
209 if ((temp
->mtable
= parttab
) == NULL
) {
210 if ((rval
= fdisk_init_master_part_table(temp
)) !=
213 * When we have no fdisk magic 0xAA55 on the disk,
214 * we return FDISK_EBADMAGIC after successfully
215 * obtaining the disk geometry.
217 if (rval
!= FDISK_EBADMAGIC
)
224 temp
->op_flag
= opflag
;
226 if ((rval
= fdisk_init_disk_geom(temp
)) != FDISK_SUCCESS
) {
232 if (found_bad_magic
!= 0) {
233 return (FDISK_EBADMAGIC
);
236 if (opflag
& FDISK_READ_DISK
) {
237 rval
= fdisk_read_extpart(*epp
);
248 libfdisk_reset(ext_part_t
*epp
)
250 int rval
= FDISK_SUCCESS
;
252 fdisk_free_ld_nodes(epp
);
253 epp
->first_ebr_is_null
= 1;
254 epp
->corrupt_logical_drives
= 0;
255 epp
->logical_drive_count
= 0;
256 epp
->invalid_bb_sig
[0] = 0;
257 if (epp
->op_flag
& FDISK_READ_DISK
) {
258 rval
= fdisk_read_extpart(epp
);
264 libfdisk_fini(ext_part_t
**epp
)
269 fdisk_free_ld_nodes(*epp
);
270 (void) close((*epp
)->dev_fd
);
276 fdisk_is_linux_swap(ext_part_t
*epp
, uint32_t part_start
, uint64_t *lsm_offset
)
281 uint32_t linux_pg_size
;
282 char *buf
, *linux_swap_magic
;
283 int sec_sz
= fdisk_get_disk_geom(epp
, PHYSGEOM
, SSIZE
);
287 * Known linux kernel page sizes
288 * The linux swap magic is found as the last 10 bytes of a disk chunk
289 * at the beginning of the linux swap partition whose size is that of
292 uint32_t linux_pg_size_arr
[] = {4096, };
294 if ((buf
= calloc(1, sec_sz
)) == NULL
) {
299 * Check if there is a sane Solaris VTOC
300 * If there is a valid vtoc, no need to lookup
301 * for the linux swap signature.
303 label_offset
= (part_start
+ DK_LABEL_LOC
) * sec_sz
;
304 if (lseek(epp
->dev_fd
, label_offset
, SEEK_SET
) < 0) {
309 if ((rval
= read(epp
->dev_fd
, buf
, sec_sz
)) < sec_sz
) {
315 if ((((struct dk_label
*)buf
)->dkl_magic
== DKL_MAGIC
) &&
316 (((struct dk_label
*)buf
)->dkl_vtoc
.v_sanity
== VTOC_SANE
)) {
321 /* No valid vtoc, so check for linux swap signature */
322 linux_swap_magic
= buf
+ sec_sz
- LINUX_SWAP_MAGIC_LENGTH
;
324 for (i
= 0; i
< sizeof (linux_pg_size_arr
)/sizeof (uint32_t); i
++) {
325 linux_pg_size
= linux_pg_size_arr
[i
];
326 seek_offset
= linux_pg_size
/sec_sz
- 1;
327 seek_offset
+= part_start
;
328 seek_offset
*= sec_sz
;
330 if (lseek(epp
->dev_fd
, seek_offset
, SEEK_SET
) < 0) {
335 if ((rval
= read(epp
->dev_fd
, buf
, sec_sz
)) < sec_sz
) {
340 if ((strncmp(linux_swap_magic
, "SWAP-SPACE",
341 LINUX_SWAP_MAGIC_LENGTH
) == 0) ||
342 (strncmp(linux_swap_magic
, "SWAPSPACE2",
343 LINUX_SWAP_MAGIC_LENGTH
) == 0)) {
344 /* Found a linux swap */
346 if (lsm_offset
!= NULL
)
347 *lsm_offset
= (uint64_t)seek_offset
;
358 fdisk_get_solaris_part(ext_part_t
*epp
, int *pnum
, uint32_t *begsec
,
361 logical_drive_t
*temp
= fdisk_get_ld_head(epp
);
366 for (pno
= 5; temp
!= NULL
; temp
= temp
->next
, pno
++) {
367 if (fdisk_is_solaris_part(LE_8(temp
->parts
[0].systid
))) {
368 part_start
= temp
->abs_secnum
+ temp
->logdrive_offset
;
369 if ((temp
->parts
[0].systid
== SUNIXOS
) &&
370 (fdisk_is_linux_swap(epp
, part_start
,
375 *begsec
= part_start
;
376 *numsec
= temp
->numsect
;
377 rval
= FDISK_SUCCESS
;
384 fdisk_get_part_info(ext_part_t
*epp
, int pnum
, uchar_t
*sysid
, uint32_t *begsec
,
387 logical_drive_t
*temp
= fdisk_get_ld_head(epp
);
390 if ((pnum
< 5) || (pnum
>= MAX_EXT_PARTS
+ 5)) {
394 for (pno
= 5; (pno
< pnum
) && (temp
!= NULL
); temp
= temp
->next
, pno
++)
401 *sysid
= LE_8(temp
->parts
[0].systid
);
402 *begsec
= temp
->abs_secnum
+ temp
->logdrive_offset
;
403 *numsec
= temp
->numsect
;
404 return (FDISK_SUCCESS
);
408 * Allocate a node of type logical_drive_t and return the pointer to it
410 static logical_drive_t
*
411 fdisk_alloc_ld_node()
413 logical_drive_t
*temp
;
415 if ((temp
= calloc(1, sizeof (logical_drive_t
))) == NULL
) {
423 * Free all the logical_drive_t's allocated during the run
426 fdisk_free_ld_nodes(ext_part_t
*epp
)
428 logical_drive_t
*temp
;
430 for (temp
= epp
->ld_head
; temp
!= NULL
; ) {
431 temp
= epp
->ld_head
-> next
;
436 epp
->sorted_ld_head
= NULL
;
440 * Find the first free sector within the extended partition
443 fdisk_ext_find_first_free_sec(ext_part_t
*epp
, uint32_t *first_free_sec
)
445 logical_drive_t
*temp
;
446 uint32_t last_free_sec
;
448 *first_free_sec
= epp
->ext_beg_sec
;
450 if (epp
->ld_head
== NULL
) {
451 return (FDISK_SUCCESS
);
455 * When the first logical drive is out of order, we need to adjust
456 * first_free_sec accordingly. In this case, the first extended
457 * partition sector is not free even though the actual logical drive
458 * does not occupy space from the beginning of the extended partition.
459 * The next free sector would be the second sector of the extended
462 if (epp
->ld_head
->abs_secnum
> epp
->ext_beg_sec
+
463 MAX_LOGDRIVE_OFFSET
) {
467 while (*first_free_sec
<= epp
->ext_end_sec
) {
468 for (temp
= epp
->sorted_ld_head
; temp
!= NULL
; temp
=
470 if (temp
->abs_secnum
== *first_free_sec
) {
471 *first_free_sec
= temp
->abs_secnum
+
472 temp
->logdrive_offset
+ temp
->numsect
;
476 last_free_sec
= fdisk_ext_find_last_free_sec(epp
,
479 if ((last_free_sec
- *first_free_sec
) < MAX_LOGDRIVE_OFFSET
) {
481 * Minimum size of a partition assumed to be atleast one
484 *first_free_sec
= last_free_sec
+ 1;
491 if (*first_free_sec
> epp
->ext_end_sec
) {
492 return (FDISK_EOOBOUND
);
495 return (FDISK_SUCCESS
);
499 * Find the last free sector within the extended partition given, a beginning
500 * sector (so that the range - "begsec to last_free_sec" is contiguous)
503 fdisk_ext_find_last_free_sec(ext_part_t
*epp
, uint32_t begsec
)
505 logical_drive_t
*temp
;
506 uint32_t last_free_sec
;
508 last_free_sec
= epp
->ext_end_sec
;
509 for (temp
= epp
->sorted_ld_head
; temp
!= NULL
;
510 temp
= temp
->sorted_next
) {
511 if (temp
->abs_secnum
> begsec
) {
512 last_free_sec
= temp
->abs_secnum
- 1;
516 return (last_free_sec
);
520 * Place the given ext_part_t structure in a sorted list, sorted in the
521 * ascending order of their beginning sectors.
524 fdisk_ext_place_in_sorted_list(ext_part_t
*epp
, logical_drive_t
*newld
)
526 logical_drive_t
*pre
, *cur
;
528 if (newld
->abs_secnum
< epp
->sorted_ld_head
->abs_secnum
) {
529 newld
->sorted_next
= epp
->sorted_ld_head
;
530 epp
->sorted_ld_head
= newld
;
533 pre
= cur
= epp
->sorted_ld_head
;
535 for (; cur
!= NULL
; pre
= cur
, cur
= cur
->sorted_next
) {
536 if (newld
->abs_secnum
< cur
->abs_secnum
) {
541 newld
->sorted_next
= cur
;
542 pre
->sorted_next
= newld
;
546 fdisk_ext_remove_from_sorted_list(ext_part_t
*epp
, logical_drive_t
*delld
)
548 logical_drive_t
*pre
, *cur
;
550 if (delld
== epp
->sorted_ld_head
) {
551 epp
->sorted_ld_head
= delld
->sorted_next
;
555 pre
= cur
= epp
->sorted_ld_head
;
557 for (; cur
!= NULL
; pre
= cur
, cur
= cur
->sorted_next
) {
558 if (cur
->abs_secnum
== delld
->abs_secnum
) {
564 pre
->sorted_next
= cur
->sorted_next
;
568 fdisk_ext_overlapping_parts(ext_part_t
*epp
, uint32_t begsec
, uint32_t endsec
)
570 logical_drive_t
*temp
;
571 uint32_t firstsec
, lastsec
, last_free_sec
;
573 for (temp
= epp
->ld_head
; temp
!= NULL
; temp
= temp
->next
) {
574 firstsec
= temp
->abs_secnum
;
575 lastsec
= firstsec
+ temp
->logdrive_offset
+ temp
->numsect
- 1;
576 if ((begsec
>= firstsec
) &&
577 (begsec
<= lastsec
)) {
583 * Find the maximum possible end sector value
584 * given a beginning sector value
586 last_free_sec
= fdisk_ext_find_last_free_sec(epp
, begsec
);
588 if (endsec
> last_free_sec
) {
595 * Check if the logical drive boundaries are sane
598 fdisk_validate_logical_drive(ext_part_t
*epp
, uint32_t begsec
,
599 uint32_t offset
, uint32_t numsec
)
603 endsec
= begsec
+ offset
+ numsec
- 1;
604 if (begsec
< epp
->ext_beg_sec
||
605 begsec
> epp
->ext_end_sec
||
606 endsec
< epp
->ext_beg_sec
||
607 endsec
> epp
->ext_end_sec
||
609 fdisk_ext_overlapping_parts(epp
, begsec
, endsec
)) {
617 * Procedure to walk through the extended partitions and build a Singly
618 * Linked List out of the data.
621 fdisk_read_extpart(ext_part_t
*epp
)
623 struct ipart
*fdp
, *ext_fdp
;
624 int i
= 0, j
= 0, ext_part_found
= 0, lpart
= 5;
625 off_t secnum
, offset
;
626 logical_drive_t
*temp
, *ep_ptr
;
627 unsigned char *ext_buf
;
628 int sectsize
= epp
->disk_geom
.sectsize
;
630 if ((ext_buf
= (uchar_t
*)malloc(sectsize
)) == NULL
) {
635 for (i
= 0; (i
< FD_NUMPART
) && (!ext_part_found
); i
++, fdp
++) {
636 if (fdisk_is_dos_extended(LE_8(fdp
->systid
))) {
638 secnum
= LE_32(fdp
->relsect
);
639 offset
= secnum
* sectsize
;
640 epp
->ext_beg_sec
= secnum
;
641 epp
->ext_end_sec
= secnum
+ LE_32(fdp
->numsect
) - 1;
643 FDISK_SECT_TO_CYL(epp
, epp
->ext_beg_sec
);
645 FDISK_SECT_TO_CYL(epp
, epp
->ext_end_sec
);
649 if (lseek(epp
->dev_fd
, offset
, SEEK_SET
) < 0) {
652 if (read(epp
->dev_fd
, ext_buf
, sectsize
) <
657 ext_fdp
= (struct ipart
*)
658 (&ext_buf
[FDISK_PART_TABLE_START
]);
659 if ((LE_32(ext_fdp
->relsect
) == 0) &&
660 (epp
->logical_drive_count
== 0)) {
661 /* No logical drives defined */
662 epp
->first_ebr_is_null
= 0;
663 return (FDISK_ENOLOGDRIVE
);
666 temp
= fdisk_alloc_ld_node();
667 temp
->abs_secnum
= secnum
;
668 temp
->logdrive_offset
=
669 LE_32(ext_fdp
->relsect
);
670 temp
->numsect
= LE_32(ext_fdp
->numsect
);
671 if (epp
->ld_head
== NULL
) {
672 /* adding first logical drive */
673 if (temp
->logdrive_offset
>
674 MAX_LOGDRIVE_OFFSET
) {
677 temp
->logdrive_offset
;
678 temp
->logdrive_offset
= 0;
682 FDISK_SECT_TO_CYL(epp
, temp
->abs_secnum
);
683 temp
->endcyl
= FDISK_SECT_TO_CYL(epp
,
685 temp
->logdrive_offset
+
689 * Check for sanity of logical drives
691 if (fdisk_validate_logical_drive(epp
,
692 temp
->abs_secnum
, temp
->logdrive_offset
,
694 epp
->corrupt_logical_drives
= 1;
696 return (FDISK_EBADLOGDRIVE
);
699 temp
->parts
[0] = *ext_fdp
;
701 temp
->parts
[1] = *ext_fdp
;
703 if (epp
->ld_head
== NULL
) {
705 epp
->sorted_ld_head
= temp
;
707 epp
->logical_drive_count
= 1;
711 fdisk_ext_place_in_sorted_list(epp
,
713 epp
->logical_drive_count
++;
717 if (LE_16((*(uint16_t *)&ext_buf
[510])) !=
719 epp
->invalid_bb_sig
[j
++] = lpart
;
720 temp
->modified
= FDISK_MINOR_WRITE
;
723 if (LE_32(ext_fdp
->relsect
) == 0)
726 secnum
= LE_32(fdp
->relsect
) +
727 LE_32(ext_fdp
->relsect
);
728 offset
= secnum
* sectsize
;
734 return (FDISK_SUCCESS
);
738 fdisk_init_master_part_table(ext_part_t
*epp
)
741 if ((epp
->mtable
= fdisk_alloc_part_table()) == NULL
) {
744 rval
= fdisk_read_master_part_table(epp
);
748 return (FDISK_SUCCESS
);
751 static struct ipart
*
752 fdisk_alloc_part_table()
754 int size
= sizeof (struct ipart
);
757 if ((table
= calloc(4, size
)) == NULL
) {
765 * Reads the master fdisk partition table from the device assuming that it has
767 * MBR is supposed to be of 512 bytes no matter what the device block size is.
770 fdisk_read_master_part_table(ext_part_t
*epp
)
772 struct dk_minfo_ext dkmp_ext
;
773 struct dk_minfo dkmp
;
776 int size
= sizeof (struct ipart
);
777 int cpcnt
= FD_NUMPART
* size
;
779 if (lseek(epp
->dev_fd
, 0, SEEK_SET
) < 0) {
782 if (ioctl(epp
->dev_fd
, DKIOCGMEDIAINFOEXT
, &dkmp_ext
) < 0) {
783 if (ioctl(epp
->dev_fd
, DKIOCGMEDIAINFO
, &dkmp
) < 0) {
786 sectsize
= dkmp
.dki_lbsize
;
788 sectsize
= dkmp_ext
.dki_lbsize
;
790 if (sectsize
< 512) {
793 buf
= calloc(sectsize
, sizeof (uchar_t
));
797 if (read(epp
->dev_fd
, buf
, sectsize
) < sectsize
) {
803 if (LE_16((*(uint16_t *)&buf
[510])) != MBB_MAGIC
) {
804 bzero(epp
->mtable
, cpcnt
);
806 return (FDISK_EBADMAGIC
);
809 bcopy(&buf
[FDISK_PART_TABLE_START
], epp
->mtable
, cpcnt
);
812 return (FDISK_SUCCESS
);
816 fdisk_ext_part_exists(ext_part_t
*epp
)
819 struct ipart
*part_table
= epp
->mtable
;
821 if (part_table
== NULL
) {
822 /* No extended partition found */
826 for (i
= 0; i
< FD_NUMPART
; i
++) {
827 if (fdisk_is_dos_extended(LE_8(part_table
[i
].systid
))) {
832 if (i
== FD_NUMPART
) {
833 /* No extended partition found */
840 fdisk_ext_validate_part_start(ext_part_t
*epp
, uint32_t begcyl
,
843 logical_drive_t
*temp
;
844 uint32_t first_free_sec
;
845 uint32_t first_free_cyl
;
848 rval
= fdisk_ext_find_first_free_sec(epp
, &first_free_sec
);
849 if (rval
!= FDISK_SUCCESS
) {
853 first_free_cyl
= FDISK_SECT_TO_CYL(epp
, first_free_sec
);
854 if (begcyl
== first_free_cyl
) {
855 *begsec
= first_free_sec
;
856 return (FDISK_SUCCESS
);
859 /* Check if the cylinder number is beyond the extended partition */
860 if ((begcyl
< epp
->ext_beg_cyl
) || (begcyl
> epp
->ext_end_cyl
)) {
861 return (FDISK_EOOBOUND
);
864 for (temp
= epp
->ld_head
; temp
!= NULL
; temp
= temp
->next
) {
865 if ((begcyl
>= temp
->begcyl
) &&
866 (begcyl
<= temp
->endcyl
)) {
867 return (FDISK_EOVERLAP
);
870 *begsec
= FDISK_CYL_TO_SECT(epp
, begcyl
);
872 return (FDISK_SUCCESS
);
876 fdisk_change_logical_drive_id(ext_part_t
*epp
, int pno
, uchar_t partid
)
878 logical_drive_t
*temp
;
882 for (temp
= epp
->ld_head
; i
< pno
; temp
= temp
->next
, i
++)
885 temp
->parts
[0].systid
= LE_8(partid
);
886 temp
->modified
= FDISK_MAJOR_WRITE
;
890 * A couple of special scenarios :
891 * 1. Since the first logical drive's EBR is always at the beginning of the
892 * extended partition, any specification that starts the first logical drive
893 * out of order will need to address the following issue :
894 * If the beginning of the drive is not coinciding with the beginning of the
895 * extended partition and :
896 * a) The start is within MAX_LOGDRIVE_OFFSET, the offset changes from the
897 * default of 63 to less than 63.
898 * logdrive_offset is updated to keep track of the space between
899 * the beginning of the logical drive and extended partition. abs_secnum
900 * points to the beginning of the extended partition.
901 * b) The start is greater than MAX_LOGDRIVE_OFFSET, the offset changes from
902 * the default of 63 to greater than 63.
903 * logdrive_offset is set to 0. abs_secnum points to the beginning of the
904 * logical drive, which is at an offset from the extended partition.
907 fdisk_add_logical_drive(ext_part_t
*epp
, uint32_t begsec
, uint32_t endsec
,
910 logical_drive_t
*temp
, *pre
, *cur
;
913 temp
= fdisk_alloc_ld_node();
914 temp
->abs_secnum
= begsec
;
915 temp
->logdrive_offset
= MAX_LOGDRIVE_OFFSET
;
916 temp
->numsect
= endsec
- begsec
+ 1 - MAX_LOGDRIVE_OFFSET
;
917 temp
->begcyl
= FDISK_SECT_TO_CYL(epp
, begsec
);
918 temp
->endcyl
= FDISK_SECT_TO_CYL(epp
, endsec
);
919 temp
->modified
= FDISK_MAJOR_WRITE
;
921 part
= &temp
->parts
[0];
923 part
->systid
= LE_8(partid
);
924 part
->relsect
= MAX_LOGDRIVE_OFFSET
;
925 part
->numsect
= LE_32(temp
->numsect
);
927 fdisk_set_CHS_values(epp
, part
);
929 if (epp
->ld_head
== NULL
) {
930 epp
->corrupt_logical_drives
= 0;
931 if (begsec
!= epp
->ext_beg_sec
) {
932 part
->relsect
= LE_32(begsec
- epp
->ext_beg_sec
);
933 temp
->numsect
= endsec
- begsec
+ 1;
934 part
->numsect
= LE_32(temp
->numsect
);
935 if (LE_32(part
->relsect
) > MAX_LOGDRIVE_OFFSET
) {
936 temp
->logdrive_offset
= 0;
938 temp
->abs_secnum
= epp
->ext_beg_sec
;
939 temp
->logdrive_offset
= LE_32(part
->relsect
);
942 epp
->first_ebr_is_null
= 0;
944 epp
->sorted_ld_head
= temp
;
945 epp
->logical_drive_count
= 1;
949 if (temp
->abs_secnum
== epp
->ext_beg_sec
) {
950 part
->relsect
= LE_32(LE_32(part
->relsect
) - 1);
951 temp
->logdrive_offset
--;
955 for (pre
= cur
= epp
->ld_head
; cur
!= NULL
; pre
= cur
, cur
= cur
->next
)
958 part
= &pre
->parts
[1];
960 part
->systid
= LE_8(EXTDOS
);
961 part
->relsect
= LE_32(temp
->abs_secnum
- epp
->ext_beg_sec
);
962 part
->numsect
= LE_32(temp
->numsect
+ temp
->logdrive_offset
);
964 fdisk_set_CHS_values(epp
, part
);
967 pre
->modified
= FDISK_MAJOR_WRITE
;
968 epp
->logical_drive_count
++;
969 fdisk_ext_place_in_sorted_list(epp
, temp
);
973 * There are 2 cases that need to be handled.
974 * 1. Deleting the first extended partition :
975 * The peculiarity of this case is that the offset of the first extended
976 * partition is always indicated by the entry in the master boot record.
977 * (MBR). This never changes, unless the extended partition itself is
978 * deleted. Hence, the location of the first EBR is fixed.
979 * It is only the logical drive which is deleted. This first EBR now gives
980 * information of the next logical drive and the info about the subsequent
981 * extended partition. Hence the "relsect" of the first EBR is modified to
982 * point to the next logical drive.
984 * 2. Deleting an intermediate extended partition.
985 * This is quite normal and follows the semantics of a normal linked list
986 * delete operation. The node being deleted has the information about the
987 * logical drive that it houses and the location and the size of the next
988 * extended partition. This informationis transferred to the node previous
989 * to the node being deleted.
994 fdisk_delete_logical_drive(ext_part_t
*epp
, int pno
)
996 logical_drive_t
*pre
, *cur
;
1000 pre
= cur
= epp
->ld_head
;
1001 for (; i
< pno
; i
++) {
1006 if (cur
== epp
->ld_head
) {
1007 /* Deleting the first logical drive */
1008 if (cur
->next
== NULL
) {
1009 /* Deleting the only logical drive left */
1011 epp
->ld_head
= NULL
;
1012 epp
->sorted_ld_head
= NULL
;
1013 epp
->logical_drive_count
= 0;
1014 epp
->first_ebr_is_null
= 1;
1018 cur
->parts
[0].relsect
=
1019 LE_32(LE_32(cur
->parts
[0].relsect
) +
1020 LE_32(pre
->parts
[1].relsect
));
1021 /* Corner case when partitions are out of order */
1022 if ((pre
->abs_secnum
!= epp
->ext_beg_sec
) &&
1023 (cur
->abs_secnum
== epp
->ext_beg_sec
+ 1)) {
1024 cur
->logdrive_offset
++;
1025 cur
->abs_secnum
= epp
->ext_beg_sec
;
1027 cur
->abs_secnum
= LE_32(cur
->parts
[0].relsect
) +
1029 cur
->logdrive_offset
= 0;
1031 fdisk_ext_remove_from_sorted_list(epp
, pre
);
1033 epp
->ld_head
->modified
= FDISK_MAJOR_WRITE
;
1034 epp
->logical_drive_count
--;
1038 pre
->parts
[1] = cur
->parts
[1];
1039 pre
->next
= cur
->next
;
1040 fdisk_ext_remove_from_sorted_list(epp
, cur
);
1041 pre
->modified
= FDISK_MAJOR_WRITE
;
1043 epp
->logical_drive_count
--;
1048 fdisk_set_CHS_values(ext_part_t
*epp
, struct ipart
*part
)
1050 uint32_t lba
, cy
, hd
, sc
;
1051 uint32_t sectors
= epp
->disk_geom
.virt_sec
;
1052 uint32_t heads
= epp
->disk_geom
.virt_heads
;
1054 lba
= LE_32(part
->relsect
) + epp
->ext_beg_sec
;
1055 if (lba
>= heads
* sectors
* MAX_CYL
) {
1057 * the lba address cannot be expressed in CHS value
1058 * so store the maximum CHS field values in the CHS fields.
1064 cy
= lba
/ sectors
/ heads
;
1065 hd
= lba
/ sectors
% heads
;
1066 sc
= lba
% sectors
+ 1;
1069 part
->begcyl
= cy
& 0xff;
1070 part
->beghead
= (uchar_t
)hd
;
1071 part
->begsect
= (uchar_t
)(((cy
>> 2) & 0xc0) | sc
);
1074 * This code is identical to the code above
1075 * except that it works on ending CHS values
1077 lba
+= LE_32(part
->numsect
- 1);
1078 if (lba
>= heads
* sectors
* MAX_CYL
) {
1083 cy
= lba
/ sectors
/ heads
;
1084 hd
= lba
/ sectors
% heads
;
1085 sc
= lba
% sectors
+ 1;
1087 part
->endcyl
= cy
& 0xff;
1088 part
->endhead
= (uchar_t
)hd
;
1089 part
->endsect
= (uchar_t
)(((cy
>> 2) & 0xc0) | sc
);
1093 read_modify_write_ebr(ext_part_t
*epp
, unsigned char *ebr_buf
,
1094 struct ipart
*ebr_tab
, uint32_t sec_offset
)
1097 int sectsize
= epp
->disk_geom
.sectsize
;
1099 seek_offset
= (off_t
)sec_offset
* sectsize
;
1101 if (lseek(epp
->dev_fd
, seek_offset
, SEEK_SET
) < 0) {
1104 if (read(epp
->dev_fd
, ebr_buf
, sectsize
) < sectsize
) {
1108 bzero(&ebr_buf
[FDISK_PART_TABLE_START
], 4 * sizeof (struct ipart
));
1109 if (ebr_tab
!= NULL
) {
1110 bcopy(ebr_tab
, &ebr_buf
[FDISK_PART_TABLE_START
],
1111 2 * sizeof (struct ipart
));
1113 ebr_buf
[510] = 0x55;
1114 ebr_buf
[511] = 0xAA;
1115 if (lseek(epp
->dev_fd
, seek_offset
, SEEK_SET
) < 0) {
1118 if (write(epp
->dev_fd
, ebr_buf
, sectsize
) < sectsize
) {
1125 * XXX - ZFS mounts not detected. Needs to come in as a feature.
1126 * Currently only /etc/mnttab entries are being checked
1129 fdisk_mounted_logical_drives(ext_part_t
*epp
)
1131 char *part_str
, *canonp
;
1132 char compare_pdev_str
[PATH_MAX
];
1133 char compare_sdev_str
[PATH_MAX
];
1137 int look_for_mounted_slices
= 0;
1138 uint32_t begsec
, numsec
;
1141 * Do not check for mounted logical drives for
1142 * devices other than /dev/rdsk/
1144 if (strstr(epp
->device_name
, DEFAULT_PATH_PREFIX
) == NULL
) {
1148 if ((fp
= fopen(MNTTAB
, "r")) == NULL
) {
1152 canonp
= epp
->device_name
+ strlen(DEFAULT_PATH_PREFIX
);
1153 (void) snprintf(compare_pdev_str
, PATH_MAX
, "%s%s", "/dev/dsk/",
1155 part_str
= strrchr(compare_pdev_str
, 'p');
1156 *(part_str
+ 1) = '\0';
1157 (void) strcpy(compare_sdev_str
, compare_pdev_str
);
1158 part_str
= strrchr(compare_sdev_str
, 'p');
1161 if (fdisk_get_solaris_part(epp
, &part
, &begsec
, &numsec
) ==
1163 if (part
> FD_NUMPART
) {
1165 * Solaris partition is on a logical drive. Look for
1168 look_for_mounted_slices
= 1;
1172 while (getmntent(fp
, &mt
) == 0) {
1173 if (strstr(mt
.mnt_special
, compare_pdev_str
) == NULL
) {
1174 if (strstr(mt
.mnt_special
, compare_sdev_str
) == NULL
) {
1177 if (look_for_mounted_slices
) {
1178 return (FDISK_EMOUNTED
);
1184 * Get the partition number that is mounted, which would be
1185 * found just beyond the last 'p' in the device string.
1186 * For example, in /dev/dsk/c0t0d0p12, partition number 12
1187 * is just beyond the last 'p'.
1189 part_str
= strrchr(mt
.mnt_special
, 'p');
1190 if (part_str
!= NULL
) {
1192 part
= atoi(part_str
);
1193 /* Extended partition numbers start from 5 */
1195 return (FDISK_EMOUNTED
);
1203 fdisk_commit_ext_part(ext_part_t
*epp
)
1205 logical_drive_t
*temp
;
1206 int wflag
= 0; /* write flag */
1208 int sectsize
= epp
->disk_geom
.sectsize
;
1209 unsigned char *ebr_buf
;
1211 uint32_t abs_secnum
;
1212 int check_mounts
= 0;
1214 if ((ebr_buf
= (unsigned char *)malloc(sectsize
)) == NULL
) {
1218 if (epp
->first_ebr_is_null
) {
1220 * Indicator that the extended partition as a whole was
1221 * modifies (either created or deleted. Must check for mounts
1228 * Pass1 through the logical drives to make sure that commit of minor
1229 * written block dont get held up due to mounts.
1231 for (temp
= epp
->ld_head
; temp
!= NULL
; temp
= temp
->next
) {
1232 if (temp
== epp
->ld_head
) {
1233 abs_secnum
= epp
->ext_beg_sec
;
1235 abs_secnum
= temp
->abs_secnum
;
1237 if (temp
->modified
== FDISK_MINOR_WRITE
) {
1238 rval
= read_modify_write_ebr(epp
, ebr_buf
,
1239 temp
->parts
, abs_secnum
);
1244 } else if (temp
->modified
== FDISK_MAJOR_WRITE
) {
1249 if (!check_mounts
) {
1250 goto skip_check_mounts
;
1253 if ((rval
= fdisk_mounted_logical_drives(epp
)) != 0) {
1254 /* One/more extended partitions are mounted */
1263 if (epp
->first_ebr_is_null
) {
1264 rval
= read_modify_write_ebr(epp
, ebr_buf
, NULL
,
1272 if (epp
->logical_drive_count
== 0) {
1274 * Can hit this case when there is just an extended
1275 * partition with no logical drives, and the user
1276 * committed without making any changes
1277 * We dont have anything to commit. Return success
1282 return (FDISK_SUCCESS
);
1286 * Make sure that the first EBR is written with the first
1287 * logical drive's data, which might not be the first in disk
1290 for (temp
= epp
->ld_head
, ld_count
= 0; temp
!= NULL
;
1291 temp
= temp
->next
, ld_count
++) {
1292 if (ld_count
== 0) {
1293 abs_secnum
= epp
->ext_beg_sec
;
1295 abs_secnum
= temp
->abs_secnum
;
1297 if (temp
->modified
) {
1298 rval
= read_modify_write_ebr(epp
, ebr_buf
,
1299 temp
->parts
, abs_secnum
);
1303 * There was atleast one
1304 * write to the disk before
1305 * this failure. Make sure that
1306 * the kernel is notified.
1313 if ((!wflag
) && (temp
->modified
==
1314 FDISK_MAJOR_WRITE
)) {
1321 /* No changes made */
1322 rval
= FDISK_SUCCESS
;
1327 /* Issue ioctl to the driver to update extended partition info */
1328 rval
= ioctl(epp
->dev_fd
, DKIOCSETEXTPART
);
1331 * Certain devices ex:lofi do not support DKIOCSETEXTPART.
1332 * Extended partitions are still created on these devices.
1334 if (errno
== ENOTTY
)
1335 rval
= FDISK_SUCCESS
;
1345 fdisk_init_ext_part(ext_part_t
*epp
, uint32_t rsect
, uint32_t nsect
)
1347 epp
->first_ebr_is_null
= 1;
1348 epp
->corrupt_logical_drives
= 0;
1349 epp
->logical_drive_count
= 0;
1350 epp
->ext_beg_sec
= rsect
;
1351 epp
->ext_end_sec
= rsect
+ nsect
- 1;
1352 epp
->ext_beg_cyl
= FDISK_SECT_TO_CYL(epp
, epp
->ext_beg_sec
);
1353 epp
->ext_end_cyl
= FDISK_SECT_TO_CYL(epp
, epp
->ext_end_sec
);
1354 epp
->invalid_bb_sig
[0] = 0;
1359 fdisk_delete_ext_part(ext_part_t
*epp
)
1361 epp
->first_ebr_is_null
= 1;
1362 /* Clear the logical drive information */
1363 fdisk_free_ld_nodes(epp
);
1364 epp
->logical_drive_count
= 0;
1365 epp
->corrupt_logical_drives
= 0;
1366 epp
->invalid_bb_sig
[0] = 0;
1371 fdisk_get_disk_geom(ext_part_t
*epp
, int type
, int what
)
1377 return ((int)epp
->disk_geom
.phys_cyl
);
1379 return ((int)epp
->disk_geom
.phys_heads
);
1381 return ((int)epp
->disk_geom
.phys_sec
);
1383 return ((int)epp
->disk_geom
.sectsize
);
1385 return ((int)epp
->disk_geom
.alt_cyl
);
1392 return ((int)epp
->disk_geom
.virt_cyl
);
1394 return ((int)epp
->disk_geom
.virt_heads
);
1396 return ((int)epp
->disk_geom
.virt_sec
);
1398 return ((int)epp
->disk_geom
.sectsize
);
1400 return ((int)epp
->disk_geom
.alt_cyl
);
1410 fdisk_invalid_bb_sig(ext_part_t
*epp
, uchar_t
**bbsig_arr
)
1412 *bbsig_arr
= &(epp
->invalid_bb_sig
[0]);
1413 return (epp
->invalid_bb_sig
[0]);