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 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 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 * 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 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