1/*	$OpenBSD: octeon_installboot.c,v 1.11 2023/04/26 18:04:21 kn Exp $	*/
2
3/*
4 * Copyright (c) 2011 Joel Sing <jsing@openbsd.org>
5 * Copyright (c) 2010 Otto Moerbeek <otto@openbsd.org>
6 * Copyright (c) 2003 Tom Cosgrove <tom.cosgrove@arches-consulting.com>
7 * Copyright (c) 1997 Michael Shalayeff
8 * Copyright (c) 1994 Paul Kranenburg
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgement:
21 *      This product includes software developed by Paul Kranenburg.
22 * 4. The name of the author may not be used to endorse or promote products
23 *    derived from this software without specific prior written permission
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
26 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
29 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
31 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
32 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include <sys/param.h>	/* DEV_BSIZE */
38#include <sys/disklabel.h>
39#include <sys/dkio.h>
40#include <sys/ioctl.h>
41#include <sys/mount.h>
42#include <sys/stat.h>
43
44#include <err.h>
45#include <errno.h>
46#include <fcntl.h>
47#include <stdlib.h>
48#include <stdio.h>
49#include <string.h>
50#include <unistd.h>
51#include <util.h>
52#include <endian.h>
53
54#include "installboot.h"
55
56static int	create_filesystem(struct disklabel *, char);
57static void	write_filesystem(struct disklabel *, char);
58static int	findmbrfat(int, struct disklabel *);
59
60void
61md_init(void)
62{
63	stages = 1;
64	stage1 = "/usr/mdec/boot";
65}
66
67void
68md_loadboot(void)
69{
70}
71
72void
73md_prepareboot(int devfd, char *dev)
74{
75	struct disklabel dl;
76	int part;
77
78	/* Get and check disklabel. */
79	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
80		err(1, "disklabel: %s", dev);
81	if (dl.d_magic != DISKMAGIC)
82		errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
83
84	/* Warn on unknown disklabel types. */
85	if (dl.d_type == 0)
86		warnx("disklabel type unknown");
87
88	part = findmbrfat(devfd, &dl);
89	if (part != -1) {
90		create_filesystem(&dl, (char)part);
91		return;
92	}
93}
94
95void
96md_installboot(int devfd, char *dev)
97{
98	struct disklabel dl;
99	int part;
100
101	/* Get and check disklabel. */
102	if (ioctl(devfd, DIOCGDINFO, &dl) == -1)
103		err(1, "disklabel: %s", dev);
104	if (dl.d_magic != DISKMAGIC)
105		errx(1, "bad disklabel magic=0x%08x", dl.d_magic);
106
107	/* Warn on unknown disklabel types. */
108	if (dl.d_type == 0)
109		warnx("disklabel type unknown");
110
111	part = findmbrfat(devfd, &dl);
112	if (part != -1) {
113		write_filesystem(&dl, (char)part);
114		return;
115	}
116}
117
118static int
119create_filesystem(struct disklabel *dl, char part)
120{
121	static const char *newfsfmt = "/sbin/newfs -t msdos %s >/dev/null";
122	struct msdosfs_args args;
123	char cmd[60];
124	int rslt;
125
126	/* Newfs <duid>.<part> as msdos filesystem. */
127	memset(&args, 0, sizeof(args));
128	rslt = asprintf(&args.fspec,
129	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
130            dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
131            dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
132	    part);
133	if (rslt == -1) {
134		warn("bad special device");
135		return rslt;
136	}
137
138	rslt = snprintf(cmd, sizeof(cmd), newfsfmt, args.fspec);
139	if (rslt >= sizeof(cmd)) {
140		warnx("can't build newfs command");
141		free(args.fspec);
142		rslt = -1;
143		return rslt;
144	}
145
146	if (verbose)
147		fprintf(stderr, "%s %s\n",
148		    (nowrite ? "would newfs" : "newfsing"), args.fspec);
149	if (!nowrite) {
150		rslt = system(cmd);
151		if (rslt == -1) {
152			warn("system('%s') failed", cmd);
153			free(args.fspec);
154			return rslt;
155		}
156	}
157
158	free(args.fspec);
159	return 0;
160}
161
162static void
163write_filesystem(struct disklabel *dl, char part)
164{
165	static char *fsckfmt = "/sbin/fsck -t msdos %s >/dev/null";
166	struct msdosfs_args args;
167	char cmd[60];
168	char dst[PATH_MAX];
169	size_t mntlen;
170	int rslt;
171
172	/* Create directory for temporary mount point. */
173	strlcpy(dst, "/tmp/installboot.XXXXXXXXXX", sizeof(dst));
174	if (mkdtemp(dst) == NULL)
175		err(1, "mkdtemp('%s') failed", dst);
176	mntlen = strlen(dst);
177
178	/* Mount <duid>.<part> as msdos filesystem. */
179	memset(&args, 0, sizeof(args));
180	rslt = asprintf(&args.fspec,
181	    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx.%c",
182            dl->d_uid[0], dl->d_uid[1], dl->d_uid[2], dl->d_uid[3],
183            dl->d_uid[4], dl->d_uid[5], dl->d_uid[6], dl->d_uid[7],
184	    part);
185	if (rslt == -1) {
186		warn("bad special device");
187		goto rmdir;
188	}
189
190	args.export_info.ex_root = -2;
191	args.export_info.ex_flags = 0;
192	args.flags = MSDOSFSMNT_LONGNAME;
193
194	if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) {
195		/* Try fsck'ing it. */
196		rslt = snprintf(cmd, sizeof(cmd), fsckfmt, args.fspec);
197		if (rslt >= sizeof(cmd)) {
198			warnx("can't build fsck command");
199			rslt = -1;
200			goto rmdir;
201		}
202		rslt = system(cmd);
203		if (rslt == -1) {
204			warn("system('%s') failed", cmd);
205			goto rmdir;
206		}
207		if (mount(MOUNT_MSDOS, dst, 0, &args) == -1) {
208			/* Try newfs'ing it. */
209			rslt = create_filesystem(dl, part);
210			if (rslt == -1)
211				goto rmdir;
212			rslt = mount(MOUNT_MSDOS, dst, 0, &args);
213			if (rslt == -1) {
214				warn("unable to mount MSDOS partition");
215				goto rmdir;
216			}
217		}
218	}
219
220	/*
221	 * Copy /usr/mdec/boot to /mnt/boot.
222	 */
223	if (strlcat(dst, "/boot", sizeof(dst)) >= sizeof(dst)) {
224		rslt = -1;
225		warn("unable to build /boot path");
226		goto umount;
227	}
228	if (verbose)
229		fprintf(stderr, "%s %s to %s\n",
230		    (nowrite ? "would copy" : "copying"), stage1, dst);
231	if (!nowrite) {
232		rslt = filecopy(stage1, dst);
233		if (rslt == -1)
234			goto umount;
235	}
236
237	rslt = 0;
238
239umount:
240	dst[mntlen] = '\0';
241	if (unmount(dst, MNT_FORCE) == -1)
242		err(1, "unmount('%s') failed", dst);
243
244rmdir:
245	free(args.fspec);
246	dst[mntlen] = '\0';
247	if (rmdir(dst) == -1)
248		err(1, "rmdir('%s') failed", dst);
249
250	if (rslt == -1)
251		exit(1);
252}
253
254int
255findmbrfat(int devfd, struct disklabel *dl)
256{
257	struct dos_partition	 dp[NDOSPART];
258	ssize_t			 len;
259	u_int64_t		 start = 0;
260	int			 i;
261	u_int8_t		*secbuf;
262
263	if ((secbuf = malloc(dl->d_secsize)) == NULL)
264		err(1, NULL);
265
266	/* Read MBR. */
267	len = pread(devfd, secbuf, dl->d_secsize, 0);
268	if (len != dl->d_secsize)
269		err(4, "can't read mbr");
270	memcpy(dp, &secbuf[DOSPARTOFF], sizeof(dp));
271
272	for (i = 0; i < NDOSPART; i++) {
273		if (dp[i].dp_typ == DOSPTYP_UNUSED)
274			continue;
275		if (dp[i].dp_typ == DOSPTYP_FAT16L ||
276		    dp[i].dp_typ == DOSPTYP_FAT32L ||
277		    dp[i].dp_typ == DOSPTYP_FAT16B)
278			start = letoh32(dp[i].dp_start);
279	}
280
281	free(secbuf);
282
283	if (start) {
284		for (i = 0; i < MAXPARTITIONS; i++) {
285			if (DL_GETPSIZE(&dl->d_partitions[i]) > 0 &&
286			    DL_GETPOFFSET(&dl->d_partitions[i]) == start)
287				return ('a' + i);
288		}
289	}
290
291	return (-1);
292}
293