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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
29 * This file contains miscellaneous device validation routines.
33 #include <sys/mnttab.h>
34 #include <sys/mntent.h>
35 #include <sys/autoconf.h>
44 #include <sys/ioctl.h>
45 #include <sys/fcntl.h>
48 #include <sys/sysmacros.h>
49 #include <sys/mkdev.h>
50 #include <sys/modctl.h>
52 #include <libdiskmgt.h>
53 #include <libnvpair.h>
56 #include <sys/efi_partition.h>
58 /* Function prototypes */
61 static struct swaptable
*getswapentries(void);
62 static void freeswapentries(struct swaptable
*);
63 static int getpartition(char *pathname
);
64 static int checkpartitions(int bm_mounted
);
68 static struct swaptable
*getswapentries();
69 static void freeswapentries();
70 static int getpartition();
71 static int checkpartitions();
75 extern char *getfullname();
77 static struct swaptable
*
80 register struct swaptable
*st
;
81 register struct swapent
*swapent
;
83 char fullpathname
[MAXPATHLEN
];
86 * get the number of swap entries
88 if ((num
= swapctl(SC_GETNSWP
, NULL
)) == -1) {
89 err_print("swapctl error ");
94 if ((st
= (swaptbl_t
*)malloc(num
* sizeof (swapent_t
) + sizeof (int)))
96 err_print("getswapentries: malloc failed.\n");
99 swapent
= st
->swt_ent
;
100 for (i
= 0; i
< num
; i
++, swapent
++) {
101 if ((swapent
->ste_path
= malloc(MAXPATHLEN
)) == NULL
) {
102 err_print("getswapentries: malloc failed.\n");
107 if ((num
= swapctl(SC_LIST
, (void *)st
)) == -1) {
108 err_print("swapctl error ");
111 swapent
= st
->swt_ent
;
112 for (i
= 0; i
< num
; i
++, swapent
++) {
113 if (*swapent
->ste_path
!= '/') {
114 (void) snprintf(fullpathname
, sizeof (fullpathname
),
115 "/dev/%s", swapent
->ste_path
);
116 (void) strcpy(swapent
->ste_path
, fullpathname
);
124 struct swaptable
*st
;
126 register struct swapent
*swapent
;
129 swapent
= st
->swt_ent
;
130 for (i
= 0; i
< st
->swt_n
; i
++, swapent
++)
131 free(swapent
->ste_path
);
137 * function getpartition:
140 getpartition(pathname
)
144 struct dk_cinfo dkinfo
;
146 char raw_device
[MAXPATHLEN
];
150 * Map the block device name to the raw device name.
151 * If it doesn't appear to be a device name, skip it.
153 if (match_substr(pathname
, "/dev/") == 0)
155 (void) strcpy(raw_device
, "/dev/r");
156 (void) strcat(raw_device
, pathname
+ strlen("/dev/"));
158 * Determine if this appears to be a disk device.
159 * First attempt to open the device. If if fails, skip it.
161 if ((mfd
= open(raw_device
, O_RDWR
| O_NDELAY
)) < 0) {
165 * Must be a character device
167 if (fstat(mfd
, &stbuf
) == -1 || !S_ISCHR(stbuf
.st_mode
)) {
172 * Attempt to read the configuration info on the disk.
174 if (ioctl(mfd
, DKIOCINFO
, &dkinfo
) < 0) {
179 * Finished with the opened device
184 * If it's not the disk we're interested in, it doesn't apply.
186 if (cur_disk
->disk_dkinfo
.dki_ctype
!= dkinfo
.dki_ctype
||
187 cur_disk
->disk_dkinfo
.dki_cnum
!= dkinfo
.dki_cnum
||
188 cur_disk
->disk_dkinfo
.dki_unit
!= dkinfo
.dki_unit
||
189 strcmp(cur_disk
->disk_dkinfo
.dki_dname
,
190 dkinfo
.dki_dname
) != 0) {
195 * Extract the partition that is mounted.
197 return (PARTITION(stbuf
.st_rdev
));
201 * This Routine checks to see if there are partitions used for swapping overlaps
202 * a given portion of a disk. If the start parameter is < 0, it means
203 * that the entire disk should be checked
206 checkswap(start
, end
)
207 diskaddr_t start
, end
;
209 struct swaptable
*st
;
210 struct swapent
*swapent
;
213 struct dk_map32
*map
;
217 * If we are only checking part of the disk, the disk must
218 * have a partition map to check against. If it doesn't,
219 * we hope for the best.
221 if (cur_parts
== NULL
)
225 * check for swap entries
227 st
= getswapentries();
229 * if there are no swap entries return.
233 swapent
= st
->swt_ent
;
234 for (i
= 0; i
< st
->swt_n
; i
++, swapent
++) {
235 if ((part
= getpartition(swapent
->ste_path
)) != -1) {
236 if (start
== UINT_MAX64
) {
240 map
= &cur_parts
->pinfo_map
[part
];
241 if ((start
>= (int)(map
->dkl_cylno
* spc() +
242 map
->dkl_nblk
)) || (end
< (int)(map
->dkl_cylno
252 * If we found trouble and we're running from a command file,
253 * quit before doing something we really regret.
256 if (found
&& option_f
) {
258 "Operation on disks being used for swapping must be interactive.\n");
267 * Determines if there are partitions that are a part of an SVM, VxVM, zpool
268 * volume or a live upgrade device, overlapping a given portion of a disk.
269 * Mounts and swap devices are checked in legacy format code.
272 checkdevinuse(char *cur_disk_path
, diskaddr_t start
, diskaddr_t end
, int print
,
282 uint64_t slice_start
, slice_size
;
283 dm_descriptor_t
*slices
= NULL
;
284 nvlist_t
*attrs
= NULL
;
289 * If the user does not want to do in use checking, return immediately.
290 * Normally, this is handled in libdiskmgt. For format, there is more
291 * processing required, so we want to bypass the in use checking
299 * Skip if it is not a real disk
301 * There could be two kinds of strings in cur_disk_path
302 * One starts with c?t?d?, while the other is a absolute path of a
306 if (*cur_disk_path
!= 'c') {
311 (void) stat(cur_disk_path
, &stbuf
);
312 majornum
= major(stbuf
.st_rdev
);
313 (void) modctl(MODGETNAME
, majorname
, sizeof (majorname
),
316 if (strcmp(majorname
, "sd"))
317 if (strcmp(majorname
, "ssd"))
318 if (strcmp(majorname
, "cmdk"))
323 * Truncate the characters following "d*", such as "s*" or "p*"
325 cur_disk_path
= basename(cur_disk_path
);
326 name
= strrchr(cur_disk_path
, 'd');
329 for (; (*name
<= '9') && (*name
>= '0'); name
++) {
336 * For format, we get basic 'in use' details from libdiskmgt. After
337 * that we must do the appropriate checking to see if the 'in use'
338 * details require a bit of additional work.
341 dm_get_slices(cur_disk_path
, &slices
, &error
);
344 * If ENODEV, it actually means the device is not in use.
345 * We will return 0 without displaying error.
347 if (error
!= ENODEV
) {
348 err_print("Error occurred with device in use"
349 "checking: %s\n", strerror(error
));
356 for (i
= 0; slices
[i
] != (uintptr_t)NULL
; i
++) {
358 * If we are checking the whole disk
359 * then any and all in use data is
362 if (start
== UINT_MAX64
) {
363 name
= dm_get_name(slices
[i
], &error
);
364 if (error
!= 0 || !name
) {
365 err_print("Error occurred with device "
366 "in use checking: %s\n", strerror(error
));
369 if (dm_inuse(name
, &usage
, DM_WHO_FORMAT
, &error
) ||
374 err_print("Error occurred with "
375 "device in use checking: "
376 "%s\n", strerror(error
));
382 * If this is a dump device, then it is
383 * a failure. You cannot format a slice
384 * that is a dedicated dump device.
387 if (strstr(usage
, DM_USE_DUMP
)) {
392 dm_free_descriptors(slices
);
396 * We really found a device that is in use.
397 * Set 'found' for the return value, and set
398 * 'check' to indicate below that we must
399 * get the partition number to set bm_inuse
400 * in the event we are trying to label this
401 * device. check_label is set when we are
402 * checking modifications for in use slices
414 * Before getting the in use data, verify that the
415 * current slice is within the range we are checking.
417 attrs
= dm_get_attributes(slices
[i
], &error
);
419 err_print("Error occurred with device in use "
420 "checking: %s\n", strerror(error
));
427 (void) nvlist_lookup_uint64(attrs
, DM_START
,
429 (void) nvlist_lookup_uint64(attrs
, DM_SIZE
,
431 if (start
>= (slice_start
+ slice_size
) ||
432 (end
< slice_start
)) {
437 name
= dm_get_name(slices
[i
], &error
);
438 if (error
!= 0 || !name
) {
439 err_print("Error occurred with device "
440 "in use checking: %s\n", strerror(error
));
445 if (dm_inuse(name
, &usage
,
446 DM_WHO_FORMAT
, &error
) || error
) {
450 err_print("Error occurred with "
451 "device in use checking: "
452 "%s\n", strerror(error
));
460 * If this is a dump device, then it is
461 * a failure. You cannot format a slice
462 * that is a dedicated dump device.
464 if (strstr(usage
, DM_USE_DUMP
)) {
469 dm_free_descriptors(slices
);
474 * We really found a device that is in use.
475 * Set 'found' for the return value, and set
476 * 'check' to indicate below that we must
477 * get the partition number to set bm_inuse
478 * in the event we are trying to label this
479 * device. check_label is set when we are
480 * checking modifications for in use slices
492 * If check is set it means we found a slice(the current slice)
493 * on this device in use in some way. We potentially want
494 * to check this slice when labeling is
495 * requested. We set bm_inuse with this partition value
496 * for use later if check_label was set when called.
499 name
= dm_get_name(slices
[i
], &error
);
500 if (error
!= 0 || !name
) {
501 err_print("Error occurred with device "
502 "in use checking: %s\n", strerror(error
));
507 part
= getpartition(name
);
511 bm_inuse
|= 1 << part
;
516 * If we have attributes then we have successfully
517 * found the slice we were looking for and we also
518 * know this means we are not searching the whole
519 * disk so break out of the loop
529 dm_free_descriptors(slices
);
533 * The user is trying to label the disk. We have to do special
534 * checking here to ensure they are not trying to modify a slice
535 * that is in use in an incompatible way.
537 if (check_label
&& bm_inuse
) {
539 * !0 indicates that we found a
540 * problem. In this case, we have overloaded
541 * the use of checkpartitions to work for
542 * in use devices. bm_inuse is representative
543 * of the slice that is in use, not that
544 * is mounted as is in the case of the normal
545 * use of checkpartitions.
547 * The call to checkpartitions will return !0 if
548 * we are trying to shrink a device that we have found
549 * to be in use above.
551 return (checkpartitions(bm_inuse
));
557 * This routine checks to see if there are mounted partitions overlapping
558 * a given portion of a disk. If the start parameter is < 0, it means
559 * that the entire disk should be checked.
562 checkmount(start
, end
)
563 diskaddr_t start
, end
;
567 struct dk_map32
*map
;
569 struct mnttab mnt_record
;
570 struct mnttab
*mp
= &mnt_record
;
573 * If we are only checking part of the disk, the disk must
574 * have a partition map to check against. If it doesn't,
575 * we hope for the best.
577 if (cur_parts
== NULL
)
581 * Lock out interrupts because of the mntent protocol.
585 * Open the mount table.
587 fp
= fopen(MNTTAB
, "r");
589 err_print("Unable to open mount table.\n");
593 * Loop through the mount table until we run out of entries.
595 while ((getmntent(fp
, mp
)) != -1) {
597 if ((part
= getpartition(mp
->mnt_special
)) == -1)
601 * It's a mount on the disk we're checking. If we are
602 * checking whole disk, then we found trouble. We can
605 if (start
== UINT_MAX64
) {
611 * If the partition overlaps the zone we're checking,
612 * then we found trouble. We can quit searching.
614 map
= &cur_parts
->pinfo_map
[part
];
615 if ((start
>= (int)(map
->dkl_cylno
* spc() + map
->dkl_nblk
)) ||
616 (end
< (int)(map
->dkl_cylno
* spc()))) {
623 * Close down the mount table.
629 * If we found trouble and we're running from a command file,
630 * quit before doing something we really regret.
633 if (found
&& option_f
) {
634 err_print("Operation on mounted disks must be interactive.\n");
644 check_label_with_swap()
647 struct swaptable
*st
;
648 struct swapent
*swapent
;
653 * If we are only checking part of the disk, the disk must
654 * have a partition map to check against. If it doesn't,
655 * we hope for the best.
657 if (cur_parts
== NULL
)
658 return (0); /* Will be checked later */
661 * Check for swap entries
663 st
= getswapentries();
665 * if there are no swap entries return.
669 swapent
= st
->swt_ent
;
670 for (i
= 0; i
< st
->swt_n
; i
++, swapent
++)
671 if ((part
= getpartition(swapent
->ste_path
)) != -1)
672 bm_swap
|= (1 << part
);
675 return (checkpartitions(bm_swap
));
679 * Check the new label with the existing label on the disk,
680 * to make sure that any mounted partitions are not being
681 * affected by writing the new label.
684 check_label_with_mount()
688 struct mnttab mnt_record
;
689 struct mnttab
*mp
= &mnt_record
;
694 * If we are only checking part of the disk, the disk must
695 * have a partition map to check against. If it doesn't,
696 * we hope for the best.
698 if (cur_parts
== NULL
)
699 return (0); /* Will be checked later */
702 * Lock out interrupts because of the mntent protocol.
706 * Open the mount table.
708 fp
= fopen(MNTTAB
, "r");
710 err_print("Unable to open mount table.\n");
714 * Loop through the mount table until we run out of entries.
716 while ((getmntent(fp
, mp
)) != -1) {
717 if ((part
= getpartition(mp
->mnt_special
)) != -1)
718 bm_mounted
|= (1 << part
);
721 * Close down the mount table.
726 return (checkpartitions(bm_mounted
));
731 * This Routine checks if any partitions specified
732 * are affected by writing the new label
735 checkpartitions(int bm_mounted
)
739 struct dk_allmap old_map
;
741 struct partition64 o_efi
;
744 * Now we need to check that the current partition list and the
745 * previous partition list (which there must be if we actually
746 * have partitions mounted) overlap in any way on the mounted
751 * Check if the user wants to online-label an
752 * existing EFI label.
754 if (cur_label
== L_TYPE_EFI
) {
755 for (i
= 0; i
< EFI_NUMPAR
; i
++) {
756 if (bm_mounted
& (1 << i
)) {
758 if (ioctl(cur_file
, DKIOCPARTITION
, &o_efi
)
760 err_print("Unable to get information "
761 "for EFI partition %d.\n", i
);
766 * Partition can grow or remain same.
768 if (o_efi
.p_start
== cur_parts
->etoc
->
769 efi_parts
[i
].p_start
&& o_efi
.p_size
770 <= cur_parts
->etoc
->efi_parts
[i
].p_size
) {
783 * Get the "real" (on-disk) version of the partition table
785 if (ioctl(cur_file
, DKIOCGAPART
, &old_map
) == -1) {
786 err_print("Unable to get current partition map.\n");
789 for (i
= 0; i
< NDKMAP
; i
++) {
790 if (bm_mounted
& (1 << i
)) {
792 * This partition is mounted
794 o
= &old_map
.dka_map
[i
];
795 n
= &cur_parts
->pinfo_map
[i
];
798 "checkpartitions :checking partition '%c' \n", i
+ PARTITION_BASE
);
801 * If partition is identical, we're fine.
802 * If the partition grows, we're also fine,
803 * because the routines in partition.c check
804 * for overflow. It will (ultimately) be up
805 * to the routines in partition.c to warn
806 * about creation of overlapping partitions.
808 if (o
->dkl_cylno
== n
->dkl_cylno
&&
809 o
->dkl_nblk
<= n
->dkl_nblk
) {
811 if (o
->dkl_nblk
< n
->dkl_nblk
) {
813 "- new partition larger by %d blocks", n
->dkl_nblk
-o
->dkl_nblk
);
820 fmt_print("- changes; old (%d,%d)->new "
821 "(%d,%d)\n", o
->dkl_cylno
, o
->dkl_nblk
, n
->dkl_cylno
, n
->dkl_nblk
);
831 * If we found trouble and we're running from a command file,
832 * quit before doing something we really regret.
835 if (found
&& option_f
) {
836 err_print("Operation on mounted disks or \
837 disks currently being used for swapping must be interactive.\n");