1 /* $OpenBSD: disk.c,v 1.3 2023/10/20 18:53:12 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 void
disk_init(void)45 disk_init(void)
46 {
47 char rootdevs[1024];
48 char *devname, *disknames, *ptr;
49 size_t size;
50 int mib[2];
51
52 rdroot = open("/", O_RDONLY);
53 if (rdroot == -1)
54 err(1, "failed to open root directory fd");
55
56 if (strlen(cmd.bootdev) != 0)
57 return;
58
59 mib[0] = CTL_HW;
60 mib[1] = HW_DISKNAMES;
61 size = 0;
62 if (sysctl(mib, 2, NULL, &size, NULL, 0) == -1) {
63 fprintf(stderr, "%s: cannot get hw.disknames: %s\n", __func__,
64 strerror(errno));
65 return;
66 }
67 disknames = malloc(size);
68 if (disknames == NULL) {
69 fprintf(stderr, "%s: out of memory\n", __func__);
70 return;
71 }
72 if (sysctl(mib, 2, disknames, &size, NULL, 0) == -1) {
73 fprintf(stderr, "%s: cannot get hw.disknames: %s\n", __func__,
74 strerror(errno));
75 free(disknames);
76 return;
77 }
78
79 printf("probing disks\n");
80 rootdevs[0] = '\0';
81 ptr = disknames;
82 while ((devname = strsep(&ptr, ",")) != NULL) {
83 char *duid;
84
85 duid = strchr(devname, ':');
86 if (duid == NULL)
87 continue;
88 *duid++ = '\0';
89
90 /* Disk without a duid cannot be a root device. */
91 if (strlen(duid) == 0)
92 continue;
93
94 if (disk_proberoot(devname)) {
95 if (strlen(cmd.bootdev) == 0) {
96 snprintf(cmd.bootdev, sizeof(cmd.bootdev),
97 "%sa", devname);
98 }
99 (void)strlcat(rootdevs, " ", sizeof(rootdevs));
100 (void)strlcat(rootdevs, devname, sizeof(rootdevs));
101 }
102 }
103 if (strlen(rootdevs) != 0)
104 printf("available root devices:%s\n", rootdevs);
105 else
106 printf("no root devices found\n");
107 }
108
109 int
disk_proberoot(const char * devname)110 disk_proberoot(const char *devname)
111 {
112 static const char *const names[] = {
113 "bin", "dev", "etc", "home", "mnt", "root", "sbin", "tmp",
114 "usr", "var", NULL
115 };
116 struct ufs_args ffs_args;
117 struct stat st;
118 char path[32];
119 int i, is_root = 1;
120
121 snprintf(path, sizeof(path), "/dev/%sa", devname);
122 memset(&ffs_args, 0, sizeof(ffs_args));
123 ffs_args.fspec = path;
124 if (mount(MOUNT_FFS, "/mnt", MNT_RDONLY, &ffs_args) == -1)
125 return 0;
126 for (i = 0; names[i] != NULL; i++) {
127 snprintf(path, sizeof(path), "/mnt/%s", names[i]);
128 if (stat(path, &st) == -1 || !S_ISDIR(st.st_mode)) {
129 is_root = 0;
130 break;
131 }
132 }
133 (void)unmount("/mnt", 0);
134
135 return is_root;
136 }
137
138 const char *
disk_open(const char * path)139 disk_open(const char *path)
140 {
141 struct ufs_args ffs_args;
142 struct disklabel label;
143 char devname[32];
144 char *devpath;
145 const char *ptr;
146 int fd;
147
148 if (mounted) {
149 fprintf(stderr, "%s: cannot nest\n", __func__);
150 return NULL;
151 }
152
153 ptr = strchr(path, ':');
154 if (ptr != NULL) {
155 snprintf(devname, sizeof(devname), "%.*s",
156 (int)(ptr - path), path);
157 ptr++; /* skip ':' */
158 } else {
159 strlcpy(devname, cmd.bootdev, sizeof(devname));
160 ptr = path;
161 }
162 if (strlen(devname) == 0) {
163 fprintf(stderr, "no device specified\n");
164 return NULL;
165 }
166
167 cmd.hasduid = 0;
168 fd = opendev(devname, O_RDONLY, OPENDEV_BLCK, &devpath);
169 if (fd != -1) {
170 if (ioctl(fd, DIOCGDINFO, &label) != -1) {
171 memcpy(cmd.bootduid, label.d_uid, 8);
172 cmd.hasduid = 1;
173 }
174 close(fd);
175 } else {
176 fprintf(stderr, "failed to open device %s: %s\n", devname,
177 strerror(errno));
178 return NULL;
179 }
180
181 memset(&ffs_args, 0, sizeof(ffs_args));
182 ffs_args.fspec = devpath;
183 if (mount(MOUNT_FFS, "/mnt", MNT_NOATIME, &ffs_args) == -1) {
184 if (mount(MOUNT_FFS, "/mnt", MNT_RDONLY, &ffs_args) == -1) {
185 fprintf(stderr, "failed to mount %s: %s\n", devpath,
186 strerror(errno));
187 return NULL;
188 }
189 fprintf(stderr, "%s: mounted read-only\n", devpath);
190 }
191 if (chroot("/mnt") == -1) {
192 fprintf(stderr, "failed to chroot: %s\n", strerror(errno));
193 (void)unmount("/mnt", 0);
194 return NULL;
195 }
196 mounted = 1;
197
198 return ptr;
199 }
200
201 void
disk_close(void)202 disk_close(void)
203 {
204 if (mounted) {
205 (void)fchdir(rdroot);
206 (void)chroot(".");
207 mounted = 0;
208 (void)unmount("/mnt", 0);
209 }
210 }
211