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