]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/gpt/boot.c
Perform explicit label type checks for externalize entry points, rather than
[FreeBSD/FreeBSD.git] / sbin / gpt / boot.c
1 /*-
2  * Copyright (c) 2007 Yahoo!, Inc.
3  * All rights reserved.
4  * Written by: John Baldwin <jhb@FreeBSD.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the author nor the names of any co-contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #include <assert.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44
45 #include "map.h"
46 #include "gpt.h"
47
48 static uuid_t boot_uuid = GPT_ENT_TYPE_FREEBSD_BOOT;
49 static const char *pmbr_path = "/boot/pmbr";
50 static const char *gptboot_path = "/boot/gptboot";
51 static u_long boot_size;
52
53 static void
54 usage_boot(void)
55 {
56
57         fprintf(stderr,
58             "usage: %s [-b pmbr] [-g gptboot] [-s count] device ...\n",
59             getprogname());
60         exit(1);
61 }
62
63 static int
64 gpt_find(uuid_t *type, map_t **mapp)
65 {
66         map_t *gpt, *tbl, *map;
67         struct gpt_hdr *hdr;
68         struct gpt_ent *ent;
69         unsigned int i;
70
71         /* Find a GPT partition with the requested UUID type. */
72         gpt = map_find(MAP_TYPE_PRI_GPT_HDR);
73         if (gpt == NULL) {
74                 warnx("%s: error: no primary GPT header", device_name);
75                 return (ENXIO);
76         }
77
78         tbl = map_find(MAP_TYPE_PRI_GPT_TBL);
79         if (tbl == NULL) {
80                 warnx("%s: error: no primary partition table", device_name);
81                 return (ENXIO);
82         }
83
84         hdr = gpt->map_data;
85         for (i = 0; i < le32toh(hdr->hdr_entries); i++) {
86                 ent = (void *)((char *)tbl->map_data + i *
87                     le32toh(hdr->hdr_entsz));
88                 if (uuid_equal(&ent->ent_type, type, NULL))
89                         break;
90         }
91         if (i == le32toh(hdr->hdr_entries)) {
92                 *mapp = NULL;
93                 return (0);
94         }
95
96         /* Lookup the map corresponding to this partition. */
97         for (map = map_find(MAP_TYPE_GPT_PART); map != NULL;
98              map = map->map_next) {
99                 if (map->map_type != MAP_TYPE_GPT_PART)
100                         continue;
101                 if (map->map_start == (off_t)le64toh(ent->ent_lba_start)) {
102                         assert(map->map_start + map->map_size - 1LL ==
103                             (off_t)le64toh(ent->ent_lba_end));
104                         *mapp = map;
105                         return (0);
106                 }
107         }
108
109         /* Hmm, the map list is not in sync with the GPT table. */
110         errx(1, "internal map list is corrupted");
111 }
112
113 static void
114 boot(int fd)
115 {
116         struct stat sb;
117         off_t bsize, ofs;
118         map_t *pmbr, *gptboot;
119         struct mbr *mbr;
120         char *buf;
121         ssize_t nbytes;
122         unsigned int entry;
123         int bfd;
124
125         /* First step: verify boot partition size. */
126         if (boot_size == 0)
127                 /* Default to 64k. */
128                 bsize = 65536 / secsz;
129         else {
130                 if (boot_size * secsz < 16384) {
131                         warnx("invalid boot partition size %lu", boot_size);
132                         return;
133                 }
134                 bsize = boot_size;
135         }
136         
137         /* Second step: write the PMBR boot loader into the PMBR. */
138         pmbr = map_find(MAP_TYPE_PMBR);
139         if (pmbr == NULL) {
140                 warnx("%s: error: PMBR not found", device_name);
141                 return;
142         }
143         bfd = open(pmbr_path, O_RDONLY);
144         if (bfd < 0 || fstat(bfd, &sb) < 0) {
145                 warn("unable to open PMBR boot loader");
146                 return;
147         }
148         if (sb.st_size != secsz) {
149                 warnx("invalid PMBR boot loader");
150                 return;
151         }
152         mbr = pmbr->map_data;
153         nbytes = read(bfd, mbr->mbr_code, sizeof(mbr->mbr_code));
154         if (nbytes < 0) {
155                 warn("unable to read PMBR boot loader");
156                 return;
157         }
158         if (nbytes != sizeof(mbr->mbr_code)) {
159                 warnx("short read of PMBR boot loader");
160                 return;
161         }
162         close(bfd);
163         gpt_write(fd, pmbr);
164
165         /* Third step: open gptboot and obtain its size. */
166         bfd = open(gptboot_path, O_RDONLY);
167         if (bfd < 0 || fstat(bfd, &sb) < 0) {
168                 warn("unable to open GPT boot loader");
169                 return;
170         }
171         
172
173         /* Fourth step: find an existing boot partition or create one. */
174         if (gpt_find(&boot_uuid, &gptboot) != 0)
175                 return;
176         if (gptboot != NULL) {
177                 if (gptboot->map_size * secsz < sb.st_size) {
178                         warnx("%s: error: boot partition is too small",
179                             device_name);
180                         return;
181                 }
182         } else if (bsize * secsz < sb.st_size) {
183                 warnx(
184                     "%s: error: proposed size for boot partition is too small",
185                     device_name);
186                 return;
187         } else {
188                 entry = 0;
189                 gptboot = gpt_add_part(fd, boot_uuid, 0, bsize, &entry);
190                 if (gptboot == NULL)
191                         return;
192         }
193
194         /* Fourth step, write out the gptboot binary to the boot partition. */  
195         buf = malloc(sb.st_size);
196         nbytes = read(bfd, buf, sb.st_size);
197         if (nbytes < 0) {
198                 warn("unable to read GPT boot loader");
199                 return;
200         }
201         if (nbytes != sb.st_size) {
202                 warnx("short read of GPT boot loader");
203                 return;
204         }
205         close(bfd);
206         ofs = gptboot->map_start * secsz;
207         if (lseek(fd, ofs, SEEK_SET) != ofs) {
208                 warn("%s: error: unable to seek to boot partition",
209                     device_name);
210                 return;
211         }
212         nbytes = write(fd, buf, sb.st_size);
213         if (nbytes < 0) {
214                 warn("unable to write GPT boot loader");
215                 return;
216         }
217         if (nbytes != sb.st_size) {
218                 warnx("short write of GPT boot loader");
219                 return;
220         }
221         free(buf);
222 }
223
224 int
225 cmd_boot(int argc, char *argv[])
226 {
227         char *p;
228         int ch, fd;
229
230         while ((ch = getopt(argc, argv, "b:g:s:")) != -1) {
231                 switch (ch) {
232                 case 'b':
233                         pmbr_path = optarg;
234                         break;
235                 case 'g':
236                         gptboot_path = optarg;
237                         break;
238                 case 's':
239                         if (boot_size > 0)
240                                 usage_boot();
241                         boot_size = strtol(optarg, &p, 10);
242                         if (*p != '\0' || boot_size < 1)
243                                 usage_boot();
244                         break;
245                 default:
246                         usage_boot();
247                 }
248         }
249
250         if (argc == optind)
251                 usage_boot();
252
253         while (optind < argc) {
254                 fd = gpt_open(argv[optind++]);
255                 if (fd < 0) {
256                         warn("unable to open device '%s'", device_name);
257                         continue;
258                 }
259
260                 boot(fd);
261
262                 gpt_close(fd);
263         }
264
265         return (0);
266 }