1/*	$OpenBSD: disk.c,v 1.4 2023/10/18 22:44:42 kettenis Exp $	*/
2
3/*
4 * Copyright (c) 2019 Visa Hankala
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/types.h>
20#include <sys/param.h>
21#include <sys/disklabel.h>
22#include <sys/dkio.h>
23#include <sys/ioctl.h>
24#include <sys/mount.h>
25#include <sys/stat.h>
26#include <sys/sysctl.h>
27
28#include <err.h>
29#include <errno.h>
30#include <fcntl.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35#include <util.h>
36
37#include "cmd.h"
38
39int	disk_proberoot(const char *);
40
41int mounted = 0;
42int rdroot = -1;		/* fd that points to the root of the ramdisk */
43
44const u_char zeroduid[8];
45
46void
47disk_init(void)
48{
49	char rootdevs[1024];
50	char bootduid[17];
51	char *devname, *disknames, *ptr;
52	size_t size;
53	int mib[2];
54
55	rdroot = open("/", O_RDONLY);
56	if (rdroot == -1)
57		err(1, "failed to open root directory fd");
58
59	if (strlen(cmd.bootdev) != 0)
60		return;
61
62	mib[0] = CTL_HW;
63	mib[1] = HW_DISKNAMES;
64	size = 0;
65	if (sysctl(mib, 2, NULL, &size, NULL, 0) == -1) {
66		fprintf(stderr, "%s: cannot get hw.disknames: %s\n", __func__,
67		    strerror(errno));
68		return;
69	}
70	disknames = malloc(size);
71	if (disknames == NULL) {
72		fprintf(stderr, "%s: out of memory\n", __func__);
73		return;
74	}
75	if (sysctl(mib, 2, disknames, &size, NULL, 0) == -1) {
76		fprintf(stderr, "%s: cannot get hw.disknames: %s\n", __func__,
77		    strerror(errno));
78		free(disknames);
79		return;
80	}
81
82	snprintf(bootduid, sizeof(bootduid),
83	    "%02x%02x%02x%02x%02x%02x%02x%02x", cmd.bootduid[0],
84	    cmd.bootduid[1], cmd.bootduid[2], cmd.bootduid[3], cmd.bootduid[4],
85	    cmd.bootduid[5], cmd.bootduid[6], cmd.bootduid[7]);
86
87	printf("probing disks\n");
88	rootdevs[0] = '\0';
89	ptr = disknames;
90	while ((devname = strsep(&ptr, ",")) != NULL) {
91		char *duid;
92
93		duid = strchr(devname, ':');
94		if (duid == NULL)
95			continue;
96		*duid++ = '\0';
97
98		/* Disk without a duid cannot be a root device. */
99		if (strlen(duid) == 0)
100			continue;
101
102		/* If we have a bootduid match, nail it down! */
103		if (strcmp(duid, bootduid) == 0) {
104			snprintf(cmd.bootdev, sizeof(cmd.bootdev),
105			    "%sa", devname);
106		}
107
108		/* Otherwise pick the first potential root disk. */
109		if (disk_proberoot(devname)) {
110			if (memcmp(cmd.bootduid, zeroduid, 8) == 0) {
111				snprintf(cmd.bootdev, sizeof(cmd.bootdev),
112				    "%sa", devname);
113			}
114			(void)strlcat(rootdevs, " ", sizeof(rootdevs));
115			(void)strlcat(rootdevs, devname, sizeof(rootdevs));
116		}
117	}
118	if (strlen(rootdevs) != 0)
119		printf("available root devices:%s\n", rootdevs);
120	else
121		printf("no root devices found\n");
122}
123
124int
125disk_proberoot(const char *devname)
126{
127	static const char *const names[] = {
128		"bin", "dev", "etc", "home", "mnt", "root", "sbin", "tmp",
129		"usr", "var", NULL
130	};
131	struct ufs_args ffs_args;
132	struct stat st;
133	char path[32];
134	int i, is_root = 1;
135
136	snprintf(path, sizeof(path), "/dev/%sa", devname);
137	memset(&ffs_args, 0, sizeof(ffs_args));
138	ffs_args.fspec = path;
139	if (mount(MOUNT_FFS, "/mnt", MNT_RDONLY, &ffs_args) == -1)
140		return 0;
141	for (i = 0; names[i] != NULL; i++) {
142		snprintf(path, sizeof(path), "/mnt/%s", names[i]);
143		if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
144			is_root = 0;
145			break;
146		}
147	}
148	(void)unmount("/mnt", 0);
149
150	return is_root;
151}
152
153const char *
154disk_open(const char *path)
155{
156	struct ufs_args ffs_args;
157	struct disklabel label;
158	char devname[32];
159	char *devpath;
160	const char *ptr;
161	int fd;
162
163	if (mounted) {
164		fprintf(stderr, "%s: cannot nest\n", __func__);
165		return NULL;
166	}
167
168	ptr = strchr(path, ':');
169	if (ptr != NULL) {
170		snprintf(devname, sizeof(devname), "%.*s",
171		    (int)(ptr - path), path);
172		ptr++;	/* skip ':' */
173	} else {
174		strlcpy(devname, cmd.bootdev, sizeof(devname));
175		ptr = path;
176	}
177	if (strlen(devname) == 0) {
178		fprintf(stderr, "no device specified\n");
179		return NULL;
180	}
181
182	cmd.hasduid = 0;
183	fd = opendev(devname, O_RDONLY, OPENDEV_BLCK, &devpath);
184	if (fd != -1) {
185		if (ioctl(fd, DIOCGDINFO, &label) != -1) {
186			memcpy(cmd.bootduid, label.d_uid, 8);
187			cmd.hasduid = 1;
188		}
189		close(fd);
190	} else {
191		fprintf(stderr, "failed to open device %s: %s\n", devname,
192		    strerror(errno));
193		return NULL;
194	}
195
196	memset(&ffs_args, 0, sizeof(ffs_args));
197	ffs_args.fspec = devpath;
198	if (mount(MOUNT_FFS, "/mnt", MNT_NOATIME, &ffs_args) == -1) {
199		if (mount(MOUNT_FFS, "/mnt", MNT_RDONLY, &ffs_args) == -1) {
200			fprintf(stderr, "failed to mount %s: %s\n", devpath,
201			    strerror(errno));
202			return NULL;
203		}
204		fprintf(stderr, "%s: mounted read-only\n", devpath);
205	}
206	if (chroot("/mnt") == -1) {
207		fprintf(stderr, "failed to chroot: %s\n", strerror(errno));
208		(void)unmount("/mnt", 0);
209		return NULL;
210	}
211	mounted = 1;
212
213	return ptr;
214}
215
216void
217disk_close(void)
218{
219	if (mounted) {
220		(void)fchdir(rdroot);
221		(void)chroot(".");
222		mounted = 0;
223		(void)unmount("/mnt", 0);
224	}
225}
226