1 /* $OpenBSD: disk.c,v 1.3 2020/08/29 11:46:54 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 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 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 * 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_FORCE | MNT_NOATIME, 199 &ffs_args) == -1) { 200 fprintf(stderr, "failed to mount %s: %s\n", devpath, 201 strerror(errno)); 202 return NULL; 203 } 204 if (chroot("/mnt") == -1) { 205 fprintf(stderr, "failed to chroot: %s\n", strerror(errno)); 206 (void)unmount("/mnt", 0); 207 return NULL; 208 } 209 mounted = 1; 210 211 return ptr; 212 } 213 214 void 215 disk_close(void) 216 { 217 if (mounted) { 218 (void)fchdir(rdroot); 219 (void)chroot("."); 220 mounted = 0; 221 (void)unmount("/mnt", 0); 222 } 223 } 224