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
39 int disk_proberoot(const char *);
40
41 int mounted = 0;
42 int rdroot = -1; /* fd that points to the root of the ramdisk */
43
44 const u_char zeroduid[8];
45
46 void
disk_init(void)47 disk_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
124 int
disk_proberoot(const char * devname)125 disk_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
153 const char *
disk_open(const char * path)154 disk_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
216 void
disk_close(void)217 disk_close(void)
218 {
219 if (mounted) {
220 (void)fchdir(rdroot);
221 (void)chroot(".");
222 mounted = 0;
223 (void)unmount("/mnt", 0);
224 }
225 }
226