xref: /illumos-gate/usr/src/lib/libdiskmgt/common/partition.c (revision 18c2aff776a775d34a4c9893a4c72e0434d68e36)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <fcntl.h>
29 #include <libdevinfo.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/sunddi.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <dirent.h>
37 #include <unistd.h>
38 #include <sys/dkio.h>
39 
40 #include "libdiskmgt.h"
41 #include "disks_private.h"
42 #include "partition.h"
43 
44 #ifdef sparc
45 #define	les(val)	((((val)&0xFF)<<8)|(((val)>>8)&0xFF))
46 #define	lel(val)	(((unsigned)(les((val)&0x0000FFFF))<<16) | \
47 			    (les((unsigned)((val)&0xffff0000)>>16)))
48 #else
49 #define	les(val)	(val)
50 #define	lel(val)	(val)
51 #endif
52 
53 #define	ISIZE		FD_NUMPART * sizeof (struct ipart)
54 
55 static int	desc_ok(descriptor_t *dp);
56 static int	get_attrs(descriptor_t *dp, struct ipart *iparts,
57 		    nvlist_t *attrs);
58 static int	get_parts(disk_t *disk, struct ipart *iparts, char *opath,
59 		    int opath_len);
60 static int	open_disk(disk_t *diskp, char *opath, int len);
61 static int	has_slices(descriptor_t *desc, int *errp);
62 
63 descriptor_t **
64 partition_get_assoc_descriptors(descriptor_t *desc, dm_desc_type_t type,
65     int *errp)
66 {
67 	if (!desc_ok(desc)) {
68 	    *errp = ENODEV;
69 	    return (NULL);
70 	}
71 
72 	switch (type) {
73 	case DM_MEDIA:
74 	    return (media_get_assocs(desc, errp));
75 	case DM_SLICE:
76 	    if (!has_slices(desc, errp)) {
77 		if (*errp != 0) {
78 		    return (NULL);
79 		}
80 		return (libdiskmgt_empty_desc_array(errp));
81 	    }
82 	    return (slice_get_assocs(desc, errp));
83 	}
84 
85 	*errp = EINVAL;
86 	return (NULL);
87 }
88 
89 /*
90  * This is called by media/slice to get the associated partitions.
91  * For a media desc. we just get all the partitions, but for a slice desc.
92  * we just get the active solaris partition.
93  */
94 descriptor_t **
95 partition_get_assocs(descriptor_t *desc, int *errp)
96 {
97 	descriptor_t	**partitions;
98 	int		pos;
99 	int		i;
100 	struct ipart	iparts[FD_NUMPART];
101 	char		pname[MAXPATHLEN];
102 	int		conv_flag = 0;
103 #if defined(i386) || defined(__amd64)
104 	int		len;
105 #endif
106 
107 	if (get_parts(desc->p.disk, iparts, pname, sizeof (pname)) != 0) {
108 	    return (libdiskmgt_empty_desc_array(errp));
109 	}
110 
111 	/* allocate the array for the descriptors */
112 	partitions = (descriptor_t **)calloc(FD_NUMPART + 1,
113 	    sizeof (descriptor_t *));
114 	if (partitions == NULL) {
115 	    *errp = ENOMEM;
116 	    return (NULL);
117 	}
118 
119 #if defined(i386) || defined(__amd64)
120 	    /* convert part. name (e.g. c0d0p0) */
121 	    len = strlen(pname);
122 	    if (len > 1 && *(pname + (len - 2)) == 'p') {
123 		conv_flag = 1;
124 		*(pname + (len - 1)) = 0;
125 	    }
126 #endif
127 
128 	/*
129 	 * If this is a slice desc. we need the first active solaris partition
130 	 * and if there isn't one then we need the first solaris partition.
131 	 */
132 	if (desc->type == DM_SLICE) {
133 	    for (i = 0; i < FD_NUMPART; i++) {
134 		if (iparts[i].bootid == ACTIVE &&
135 		    (iparts[i].systid == SUNIXOS ||
136 		    iparts[i].systid == SUNIXOS2)) {
137 			break;
138 		}
139 	    }
140 
141 	    /* no active solaris part., try to get the first solaris part. */
142 	    if (i >= FD_NUMPART) {
143 		for (i = 0; i < FD_NUMPART; i++) {
144 		    if (iparts[i].systid == SUNIXOS ||
145 			iparts[i].systid == SUNIXOS2) {
146 			    break;
147 		    }
148 		}
149 	    }
150 
151 	    if (i < FD_NUMPART) {
152 		/* we found a solaris partition to use */
153 		char	part_name[MAXPATHLEN];
154 
155 		if (conv_flag) {
156 		    /* convert part. name (e.g. c0d0p0) */
157 		    (void) snprintf(part_name, sizeof (part_name), "%s%d",
158 			pname, i);
159 		} else {
160 		    (void) snprintf(part_name, sizeof (part_name), "%d", i);
161 		}
162 
163 		/* the media name comes from the slice desc. */
164 		partitions[0] = cache_get_desc(DM_PARTITION, desc->p.disk,
165 		    part_name, desc->secondary_name, errp);
166 		if (*errp != 0) {
167 		    cache_free_descriptors(partitions);
168 		    return (NULL);
169 		}
170 		partitions[1] = NULL;
171 
172 		return (partitions);
173 
174 	    }
175 
176 	    return (libdiskmgt_empty_desc_array(errp));
177 	}
178 
179 	/* Must be for media, so get all the parts. */
180 
181 	pos = 0;
182 	for (i = 0; i < FD_NUMPART; i++) {
183 	    if (iparts[i].systid != 0) {
184 		char	part_name[MAXPATHLEN];
185 
186 		if (conv_flag) {
187 		    /* convert part. name (e.g. c0d0p0) */
188 		    (void) snprintf(part_name, sizeof (part_name), "%s%d",
189 			pname, i);
190 		} else {
191 		    (void) snprintf(part_name, sizeof (part_name), "%d", i);
192 		}
193 
194 		/* the media name comes from the media desc. */
195 		partitions[pos] = cache_get_desc(DM_PARTITION, desc->p.disk,
196 		    part_name, desc->name, errp);
197 		if (*errp != 0) {
198 		    cache_free_descriptors(partitions);
199 		    return (NULL);
200 		}
201 
202 		pos++;
203 	    }
204 	}
205 	partitions[pos] = NULL;
206 
207 	*errp = 0;
208 	return (partitions);
209 }
210 
211 nvlist_t *
212 partition_get_attributes(descriptor_t *dp, int *errp)
213 {
214 	nvlist_t	*attrs = NULL;
215 	struct ipart	iparts[FD_NUMPART];
216 
217 	if (!desc_ok(dp)) {
218 	    *errp = ENODEV;
219 	    return (NULL);
220 	}
221 
222 	if ((*errp = get_parts(dp->p.disk, iparts, NULL, 0)) != 0) {
223 	    return (NULL);
224 	}
225 
226 	if (nvlist_alloc(&attrs, NVATTRS, 0) != 0) {
227 	    *errp = ENOMEM;
228 	    return (NULL);
229 	}
230 
231 	if ((*errp = get_attrs(dp, iparts, attrs)) != 0) {
232 	    nvlist_free(attrs);
233 	    attrs = NULL;
234 	}
235 
236 	return (attrs);
237 }
238 
239 /*
240  * Look for the partition by the partition number (which is not too useful).
241  */
242 descriptor_t *
243 partition_get_descriptor_by_name(char *name, int *errp)
244 {
245 	descriptor_t	**partitions;
246 	int		i;
247 	descriptor_t	*partition = NULL;
248 
249 	partitions = cache_get_descriptors(DM_PARTITION, errp);
250 	if (*errp != 0) {
251 	    return (NULL);
252 	}
253 
254 	for (i = 0; partitions[i]; i++) {
255 	    if (libdiskmgt_str_eq(name, partitions[i]->name)) {
256 		partition = partitions[i];
257 	    } else {
258 		/* clean up the unused descriptors */
259 		cache_free_descriptor(partitions[i]);
260 	    }
261 	}
262 	free(partitions);
263 
264 	if (partition == NULL) {
265 	    *errp = ENODEV;
266 	}
267 
268 	return (partition);
269 }
270 
271 /* ARGSUSED */
272 descriptor_t **
273 partition_get_descriptors(int filter[], int *errp)
274 {
275 	return (cache_get_descriptors(DM_PARTITION, errp));
276 }
277 
278 char *
279 partition_get_name(descriptor_t *desc)
280 {
281 	return (desc->name);
282 }
283 
284 /* ARGSUSED */
285 nvlist_t *
286 partition_get_stats(descriptor_t *dp, int stat_type, int *errp)
287 {
288 	/* There are no stat types defined for partitions */
289 	*errp = EINVAL;
290 	return (NULL);
291 }
292 
293 /* ARGSUSED */
294 int
295 partition_has_fdisk(disk_t *dp, int fd)
296 {
297 	char		bootsect[512 * 3]; /* 3 sectors to be safe */
298 
299 #ifdef sparc
300 	if (dp->drv_type == DM_DT_FIXED) {
301 	    /* on sparc, only removable media can have fdisk parts. */
302 	    return (0);
303 	}
304 #endif
305 
306 	/*
307 	 * We assume the caller already made sure media was inserted and
308 	 * spun up.
309 	 */
310 
311 	if ((ioctl(fd, DKIOCGMBOOT, bootsect) < 0) && (errno != ENOTTY)) {
312 	    return (0);
313 	}
314 
315 	return (1);
316 }
317 
318 /*
319  * A partition descriptor points to a disk, the name is the partition number
320  * and the secondary name is the media name.
321  */
322 int
323 partition_make_descriptors()
324 {
325 	int		error;
326 	disk_t		*dp;
327 
328 	dp = cache_get_disklist();
329 	while (dp != NULL) {
330 	    struct ipart	iparts[FD_NUMPART];
331 	    char		pname[MAXPATHLEN];
332 
333 	    if (get_parts(dp, iparts, pname, sizeof (pname)) == 0) {
334 		int	i;
335 		char	mname[MAXPATHLEN];
336 		int	conv_flag = 0;
337 #if defined(i386) || defined(__amd64)
338 		/* convert part. name (e.g. c0d0p0) */
339 		int	len;
340 
341 		len = strlen(pname);
342 		if (len > 1 && *(pname + (len - 2)) == 'p') {
343 		    conv_flag = 1;
344 		    *(pname + (len - 1)) = 0;
345 		}
346 #endif
347 
348 		mname[0] = 0;
349 		(void) media_read_name(dp, mname, sizeof (mname));
350 
351 		for (i = 0; i < FD_NUMPART; i++) {
352 		    if (iparts[i].systid != 0) {
353 			char	part_name[MAXPATHLEN];
354 
355 			if (conv_flag) {
356 			    /* convert part. name (e.g. c0d0p0) */
357 			    (void) snprintf(part_name, sizeof (part_name),
358 				"%s%d", pname, i);
359 			} else {
360 			    (void) snprintf(part_name, sizeof (part_name),
361 				"%d", i);
362 			}
363 
364 			cache_load_desc(DM_PARTITION, dp, part_name, mname,
365 			    &error);
366 			if (error != 0) {
367 			    return (error);
368 			}
369 		    }
370 		}
371 	    }
372 	    dp = dp->next;
373 	}
374 
375 	return (0);
376 }
377 
378 static int
379 get_attrs(descriptor_t *dp, struct ipart *iparts, nvlist_t *attrs)
380 {
381 	char		*p;
382 	int		part_num;
383 
384 	/*
385 	 * We already made sure the media was loaded and ready in the
386 	 * get_parts call within partition_get_attributes.
387 	 */
388 
389 	p = strrchr(dp->name, 'p');
390 	if (p == NULL) {
391 	    p = dp->name;
392 	} else {
393 	    p++;
394 	}
395 	part_num = atoi(p);
396 	if (part_num >= FD_NUMPART || iparts[part_num].systid == 0) {
397 	    return (ENODEV);
398 	}
399 
400 	/* we found the partition */
401 
402 	if (nvlist_add_uint32(attrs, DM_BOOTID,
403 	    (unsigned int)iparts[part_num].bootid) != 0) {
404 	    return (ENOMEM);
405 	}
406 
407 	if (nvlist_add_uint32(attrs, DM_PTYPE,
408 	    (unsigned int)iparts[part_num].systid) != 0) {
409 	    return (ENOMEM);
410 	}
411 
412 	if (nvlist_add_uint32(attrs, DM_BHEAD,
413 	    (unsigned int)iparts[part_num].beghead) != 0) {
414 	    return (ENOMEM);
415 	}
416 
417 	if (nvlist_add_uint32(attrs, DM_BSECT,
418 	    (unsigned int)((iparts[part_num].begsect) & 0x3f)) != 0) {
419 	    return (ENOMEM);
420 	}
421 
422 	if (nvlist_add_uint32(attrs, DM_BCYL, (unsigned int)
423 	    ((iparts[part_num].begcyl & 0xff) |
424 	    ((iparts[part_num].begsect & 0xc0) << 2))) != 0) {
425 	    return (ENOMEM);
426 	}
427 
428 	if (nvlist_add_uint32(attrs, DM_EHEAD,
429 	    (unsigned int)iparts[part_num].endhead) != 0) {
430 	    return (ENOMEM);
431 	}
432 
433 	if (nvlist_add_uint32(attrs, DM_ESECT,
434 	    (unsigned int)((iparts[part_num].endsect) & 0x3f)) != 0) {
435 	    return (ENOMEM);
436 	}
437 
438 	if (nvlist_add_uint32(attrs, DM_ECYL, (unsigned int)
439 	    ((iparts[part_num].endcyl & 0xff) |
440 	    ((iparts[part_num].endsect & 0xc0) << 2))) != 0) {
441 	    return (ENOMEM);
442 	}
443 
444 	if (nvlist_add_uint32(attrs, DM_RELSECT,
445 	    (unsigned int)iparts[part_num].relsect) != 0) {
446 	    return (ENOMEM);
447 	}
448 
449 	if (nvlist_add_uint32(attrs, DM_NSECTORS,
450 	    (unsigned int)iparts[part_num].numsect) != 0) {
451 	    return (ENOMEM);
452 	}
453 
454 	return (0);
455 }
456 
457 static int
458 get_parts(disk_t *disk, struct ipart *iparts, char *opath, int opath_len)
459 {
460 	int		fd;
461 	struct dk_minfo	minfo;
462 	struct mboot	bootblk;
463 	char		bootsect[512];
464 	int		i;
465 
466 	/* Can't use drive_open_disk since we need the partition dev name. */
467 	if ((fd = open_disk(disk, opath, opath_len)) < 0) {
468 	    return (ENODEV);
469 	}
470 
471 	/* First make sure media is inserted and spun up. */
472 	if (!media_read_info(fd, &minfo)) {
473 	    (void) close(fd);
474 	    return (ENODEV);
475 	}
476 
477 	if (!partition_has_fdisk(disk, fd)) {
478 	    (void) close(fd);
479 	    return (ENOTTY);
480 	}
481 
482 	if (lseek(fd, 0, 0) == -1) {
483 	    (void) close(fd);
484 	    return (ENODEV);
485 	}
486 
487 	if (read(fd, bootsect, 512) != 512) {
488 	    (void) close(fd);
489 	    return (ENODEV);
490 	}
491 	(void) close(fd);
492 
493 	(void) memcpy(&bootblk, bootsect, sizeof (bootblk));
494 
495 	if (les(bootblk.signature) != MBB_MAGIC)  {
496 	    return (ENOTTY);
497 	}
498 
499 	(void) memcpy(iparts, bootblk.parts, ISIZE);
500 
501 	for (i = 0; i < FD_NUMPART; i++) {
502 	    if (iparts[i].systid != 0) {
503 		iparts[i].relsect = lel(iparts[i].relsect);
504 		iparts[i].numsect = lel(iparts[i].numsect);
505 	    }
506 	}
507 
508 	return (0);
509 }
510 /* return 1 if the partition descriptor is still valid, 0 if not. */
511 static int
512 desc_ok(descriptor_t *dp)
513 {
514 	/* First verify the media name for removable media */
515 	if (dp->p.disk->removable) {
516 	    char	mname[MAXPATHLEN];
517 
518 	    if (!media_read_name(dp->p.disk, mname, sizeof (mname))) {
519 		return (0);
520 	    }
521 
522 	    if (mname[0] == 0) {
523 		return (libdiskmgt_str_eq(dp->secondary_name, NULL));
524 	    } else {
525 		return (libdiskmgt_str_eq(dp->secondary_name, mname));
526 	    }
527 	}
528 
529 	/*
530 	 * We could verify the partition is still there but this is kind of
531 	 * expensive and other code down the line will do that (e.g. see
532 	 * get_attrs).
533 	 */
534 
535 	return (1);
536 }
537 
538 /*
539  * Return 1 if partition has slices, 0 if not.
540  */
541 static int
542 has_slices(descriptor_t *desc, int *errp)
543 {
544 	int		pnum;
545 	int		i;
546 	char		*p;
547 	struct ipart	iparts[FD_NUMPART];
548 
549 	if (get_parts(desc->p.disk, iparts, NULL, 0) != 0) {
550 	    *errp = ENODEV;
551 	    return (0);
552 	}
553 
554 	p = strrchr(desc->name, 'p');
555 	if (p == NULL) {
556 	    p = desc->name;
557 	} else {
558 	    p++;
559 	}
560 	pnum = atoi(p);
561 
562 	/*
563 	 * Slices are associated with the active solaris partition or if there
564 	 * is no active solaris partition, then the first solaris partition.
565 	 */
566 
567 	*errp = 0;
568 	if (iparts[pnum].bootid == ACTIVE &&
569 	    (iparts[pnum].systid == SUNIXOS ||
570 	    iparts[pnum].systid == SUNIXOS2)) {
571 		return (1);
572 	} else {
573 	    int	active = 0;
574 
575 	    /* Check if there are no active solaris partitions. */
576 	    for (i = 0; i < FD_NUMPART; i++) {
577 		if (iparts[i].bootid == ACTIVE &&
578 		    (iparts[i].systid == SUNIXOS ||
579 		    iparts[i].systid == SUNIXOS2)) {
580 			active = 1;
581 			break;
582 		}
583 	    }
584 
585 	    if (!active) {
586 		/* Check if this is the first solaris partition. */
587 		for (i = 0; i < FD_NUMPART; i++) {
588 		    if (iparts[i].systid == SUNIXOS ||
589 			iparts[i].systid == SUNIXOS2) {
590 			    break;
591 		    }
592 		}
593 
594 		if (i < FD_NUMPART && i == pnum) {
595 		    return (1);
596 		}
597 	    }
598 	}
599 
600 	return (0);
601 }
602 
603 static int
604 open_disk(disk_t *diskp, char *opath, int len)
605 {
606 	/*
607 	 * Just open the first devpath.
608 	 */
609 	if (diskp->aliases != NULL && diskp->aliases->devpaths != NULL) {
610 #ifdef sparc
611 	    if (opath != NULL) {
612 		(void) strlcpy(opath, diskp->aliases->devpaths->devpath, len);
613 	    }
614 	    return (open(diskp->aliases->devpaths->devpath, O_RDONLY|O_NDELAY));
615 #else
616 	    /* On intel we need to open partition device (e.g. c0d0p0). */
617 	    char	part_dev[MAXPATHLEN];
618 	    char	*p;
619 
620 	    (void) strlcpy(part_dev, diskp->aliases->devpaths->devpath,
621 		sizeof (part_dev));
622 	    p = strrchr(part_dev, '/');
623 	    if (p == NULL) {
624 		p = strrchr(part_dev, 's');
625 		if (p != NULL) {
626 		    *p = 'p';
627 		}
628 	    } else {
629 		char *ps;
630 
631 		*p = 0;
632 		ps = strrchr((p + 1), 's');
633 		if (ps != NULL) {
634 		    *ps = 'p';
635 		}
636 		*p = '/';
637 	    }
638 
639 	    if (opath != NULL) {
640 		(void) strlcpy(opath, part_dev, len);
641 	    }
642 	    return (open(part_dev, O_RDONLY|O_NDELAY));
643 #endif
644 	}
645 
646 	return (-1);
647 }
648