xref: /openbsd/sys/arch/powerpc64/stand/rdboot/disk.c (revision 1a98aacb)
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