1107b029884e8957f536c19462d07645e34f82f7
[zfs.git] / zfs / lib / libzpool / spa_boot.c
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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26
27 #pragma ident   "@(#)spa_boot.c 1.1     08/04/09 SMI"
28
29 #include <sys/spa.h>
30 #include <sys/sunddi.h>
31
32 char *
33 spa_get_bootfs()
34 {
35         char *zfs_bp;
36
37         if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(),
38             DDI_PROP_DONTPASS, "zfs-bootfs", &zfs_bp) !=
39             DDI_SUCCESS)
40                 return (NULL);
41         return (zfs_bp);
42 }
43
44 void
45 spa_free_bootfs(char *bootfs)
46 {
47         ddi_prop_free(bootfs);
48 }
49
50 /*
51  * Calculate how many device pathnames are in devpath_list.
52  * The devpath_list could look like this:
53  *
54  *      "/pci@1f,0/ide@d/disk@0,0:a /pci@1f,o/ide@d/disk@2,0:a"
55  */
56 static int
57 spa_count_devpath(char *devpath_list)
58 {
59         int numpath;
60         char *tmp_path, *blank;
61
62         numpath = 0;
63         tmp_path = devpath_list;
64
65         /* skip leading blanks */
66         while (*tmp_path == ' ')
67                 tmp_path++;
68
69         while ((blank = strchr(tmp_path, ' ')) != NULL) {
70
71                 numpath++;
72                 /* skip contiguous blanks */
73                 while (*blank == ' ')
74                         blank++;
75                 tmp_path = blank;
76         }
77
78         if (strlen(tmp_path) > 0)
79                 numpath++;
80
81         return (numpath);
82 }
83
84 /*
85  * Only allow booting the device if it has the same vdev information as
86  * the most recently updated vdev (highest txg) and is in a valid state.
87  *
88  * GRUB passes online/active device path names, e.g.
89  *      "/pci@1f,0/ide@d/disk@0,0:a /pci@1f,o/ide@d/disk@2,0:a"
90  * to the kernel. The best vdev should have the same matching online/active
91  * list as what GRUB passes in.
92  */
93 static int
94 spa_check_devstate(char *devpath_list, char *dev, nvlist_t *conf)
95 {
96         nvlist_t *nvtop, **child;
97         uint_t label_path, grub_path, c, children;
98         char *type;
99
100         VERIFY(nvlist_lookup_nvlist(conf, ZPOOL_CONFIG_VDEV_TREE,
101             &nvtop) == 0);
102         VERIFY(nvlist_lookup_string(nvtop, ZPOOL_CONFIG_TYPE, &type) == 0);
103
104         if (strcmp(type, VDEV_TYPE_DISK) == 0)
105                 return (spa_rootdev_validate(nvtop)? 0 : EINVAL);
106
107         ASSERT(strcmp(type, VDEV_TYPE_MIRROR) == 0);
108
109         VERIFY(nvlist_lookup_nvlist_array(nvtop, ZPOOL_CONFIG_CHILDREN,
110             &child, &children) == 0);
111
112         /*
113          * Check if the devpath_list is the same as the path list in conf.
114          * If these two lists are different, then the booting device is not an
115          * up-to-date device that can be booted.
116          */
117         label_path = 0;
118         for (c = 0; c < children; c++) {
119                 char *physpath;
120
121                 if (nvlist_lookup_string(child[c], ZPOOL_CONFIG_PHYS_PATH,
122                     &physpath) != 0)
123                         return (EINVAL);
124
125                 if (spa_rootdev_validate(child[c])) {
126                         if (strstr(devpath_list, physpath) == NULL)
127                                 return (EINVAL);
128                         label_path++;
129                 } else {
130                         char *blank;
131
132                         if (blank = strchr(dev, ' '))
133                                 *blank = '\0';
134                         if (strcmp(physpath, dev) == 0)
135                                 return (EINVAL);
136                         if (blank)
137                                 *blank = ' ';
138                 }
139         }
140
141         grub_path = spa_count_devpath(devpath_list);
142
143         if (label_path != grub_path)
144                 return (EINVAL);
145
146         return (0);
147 }
148
149 /*
150  * Given a list of vdev physpath names, pick the vdev with the most recent txg,
151  * and return the point of the device's physpath in the list and the device's
152  * label configuration. The content of the label would be the most recent
153  * updated information.
154  */
155 int
156 spa_get_rootconf(char *devpath_list, char **bestdev, nvlist_t **bestconf)
157 {
158         nvlist_t *conf = NULL;
159         char *dev = NULL;
160         uint64_t txg = 0;
161         char *devpath, *blank;
162
163         devpath = devpath_list;
164         dev = devpath;
165
166         while (devpath[0] == ' ')
167                 devpath++;
168
169         while ((blank = strchr(devpath, ' ')) != NULL) {
170                 *blank = '\0';
171                 spa_check_rootconf(devpath, &dev, &conf, &txg);
172                 *blank = ' ';
173
174                 while (*blank == ' ')
175                         blank++;
176                 devpath = blank;
177         }
178
179         /* for the only or the last devpath in the devpath_list */
180         if (strlen(devpath) > 0)
181                 spa_check_rootconf(devpath, &dev, &conf, &txg);
182
183         if (conf == NULL)
184                 return (EINVAL);
185
186         /*
187          * dev/conf is the vdev with the most recent txg.
188          * Check if the device is in a bootable state.
189          * dev may have a trailing blank since it points to a string
190          * in the devpath_list.
191          */
192         if (spa_check_devstate(devpath_list, dev, conf) != 0)
193                 return (EINVAL);
194
195         *bestdev = dev;
196         *bestconf = conf;
197         return (0);
198 }