1 /*- 2 * Copyright (c) 2022 Netflix, Inc 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 #include <sys/types.h> 8 #include "stand.h" 9 #include "host_syscall.h" 10 #include "kboot.h" 11 12 #define HOST_PATH_MAX 1025 13 14 extern struct devsw host_dev; 15 16 const char *hostfs_root = "/"; 17 18 enum FTYPE { 19 regular, 20 dir, 21 }; 22 23 typedef struct _hostfs_file { 24 enum FTYPE hf_type; 25 int hf_fd; 26 /* The following are only used for FTYPE == dir */ 27 char hf_dents[2048]; 28 char *hf_curdent; 29 int hf_dentlen; /* Valid part of hf_dents */ 30 } hostfs_file; 31 32 static hostfs_file * 33 hostfs_alloc(void) 34 { 35 hostfs_file *hf; 36 37 hf = malloc(sizeof(*hf)); 38 if (hf != NULL) 39 memset(hf, 0, sizeof(*hf)); 40 return (hf); 41 } 42 43 static void 44 hostfs_free(hostfs_file *hf) 45 { 46 free(hf); 47 } 48 49 static int 50 hostfs_open(const char *fn, struct open_file *f) 51 { 52 hostfs_file *hf; 53 struct host_kstat ksb; 54 char path[HOST_PATH_MAX]; 55 56 if (f->f_dev != &host_dev) { 57 return (EINVAL); 58 } 59 60 /* 61 * Normally, we root everything at hostfs_root. However, there are two 62 * exceptions that make it easier to write code. First is /sys and /proc 63 * are special Linux filesystems, so we pass those paths 64 * through. Second, if the path starts with //, then we strip off the 65 * first / and pass it through (in a weird way, this is actually in 66 * POSIX: hosts are allowed to do specail things with paths that start 67 * with two //, but one or three or more are required to be treated as 68 * one). 69 */ 70 if (strncmp("/sys/", fn, 5) == 0 || strncmp("/proc/", fn, 6) == 0) 71 strlcpy(path, fn, sizeof(path)); 72 else if (fn[0] == '/' && fn[1] == '/' && fn[2] != '/') 73 strlcpy(path, fn + 1, sizeof(path)); 74 else 75 snprintf(path, sizeof(path), "%s/%s", hostfs_root, fn); 76 hf = hostfs_alloc(); 77 hf->hf_fd = host_open(path, HOST_O_RDONLY, 0); 78 if (hf->hf_fd < 0) { 79 hostfs_free(hf); 80 return (EINVAL); 81 } 82 83 if (host_fstat(hf->hf_fd, &ksb) < 0) { 84 hostfs_free(hf); 85 return (EINVAL); 86 } 87 if (S_ISDIR(hf->hf_fd)) { 88 hf->hf_type = dir; 89 } else { 90 hf->hf_type = regular; 91 } 92 f->f_fsdata = hf; 93 return (0); 94 } 95 96 static int 97 hostfs_close(struct open_file *f) 98 { 99 hostfs_file *hf = f->f_fsdata; 100 101 host_close(hf->hf_fd); 102 hostfs_free(hf); 103 f->f_fsdata = NULL; 104 105 return (0); 106 } 107 108 static int 109 hostfs_read(struct open_file *f, void *start, size_t size, size_t *resid) 110 { 111 hostfs_file *hf = f->f_fsdata; 112 ssize_t sz; 113 114 sz = host_read(hf->hf_fd, start, size); 115 if (sz < 0) 116 return (host_to_stand_errno(sz)); 117 *resid = size - sz; 118 119 return (0); 120 } 121 122 static off_t 123 hostfs_seek(struct open_file *f, off_t offset, int whence) 124 { 125 hostfs_file *hf = f->f_fsdata; 126 uint32_t offl, offh; 127 long err; 128 uint64_t res; 129 130 /* 131 * Assumes Linux host with 'reduced' system call wrappers. Also assume 132 * host and libstand have same whence encoding (safe since it all comes 133 * from V7 later ISO-C). Also assumes we have to support powerpc still, 134 * it's interface is weird for legacy reasons.... 135 */ 136 res = (uint64_t)offset; 137 offl = res & 0xfffffffful; 138 offh = (res >> 32) & 0xfffffffful; 139 err = host_llseek(hf->hf_fd, offh, offl, &res, whence); 140 /* 141 * Since we're interfacing to the raw linux system call, we have to 142 * carefully check. We have to translate the errno value from the host 143 * to libsa's conventions. 144 */ 145 if (is_linux_error(err)) { 146 errno = host_to_stand_errno(err); 147 return (-1); 148 } 149 return (res); 150 } 151 152 static int 153 hostfs_stat(struct open_file *f, struct stat *sb) 154 { 155 struct host_kstat ksb; 156 hostfs_file *hf = f->f_fsdata; 157 158 if (host_fstat(hf->hf_fd, &ksb) < 0) 159 return (EINVAL); 160 /* 161 * Translate Linux stat info to lib stand's notion (which uses FreeBSD's 162 * stat structure, missing fields are zero and commented below). 163 */ 164 memset(sb, 0, sizeof(*sb)); 165 sb->st_dev = ksb.st_dev; 166 sb->st_ino = ksb.st_ino; 167 sb->st_nlink = ksb.st_nlink; 168 sb->st_mode = ksb.st_mode; 169 sb->st_uid = ksb.st_uid; 170 sb->st_gid = ksb.st_gid; 171 sb->st_rdev = ksb.st_rdev; 172 /* No st_?time_ext on i386 */ 173 sb->st_atim.tv_sec = ksb.st_atime_sec; 174 sb->st_atim.tv_nsec = ksb.st_atime_nsec; 175 sb->st_mtim.tv_sec = ksb.st_mtime_sec; 176 sb->st_mtim.tv_nsec = ksb.st_mtime_nsec; 177 sb->st_ctim.tv_sec = ksb.st_ctime_sec; 178 sb->st_ctim.tv_nsec = ksb.st_ctime_nsec; 179 /* No st_birthtim */ 180 sb->st_size = ksb.st_size; 181 sb->st_blocks = ksb.st_blocks; 182 sb->st_blksize = ksb.st_blksize; 183 /* no st_flags */ 184 /* no st_get */ 185 186 return (0); 187 } 188 189 static int 190 hostfs_readdir(struct open_file *f, struct dirent *d) 191 { 192 hostfs_file *hf = f->f_fsdata; 193 int dentlen; 194 struct host_dirent64 *dent; 195 196 if (hf->hf_curdent == NULL) { 197 dentlen = host_getdents64(hf->hf_fd, hf->hf_dents, sizeof(hf->hf_dents)); 198 if (dentlen <= 0) 199 return (EINVAL); 200 hf->hf_dentlen = dentlen; 201 hf->hf_curdent = hf->hf_dents; 202 } 203 dent = (struct host_dirent64 *)hf->hf_curdent; 204 d->d_fileno = dent->d_ino; 205 d->d_type = dent->d_type; /* HOST_DT_XXXX == DX_XXXX for all values */ 206 strlcpy(d->d_name, dent->d_name, sizeof(d->d_name)); /* d_name is NUL terminated */ 207 d->d_namlen = strlen(d->d_name); 208 hf->hf_curdent += dent->d_reclen; 209 if (hf->hf_curdent >= hf->hf_dents + hf->hf_dentlen) { 210 hf->hf_curdent = NULL; 211 hf->hf_dentlen = 0; 212 } 213 214 return (0); 215 } 216 217 struct fs_ops hostfs_fsops = { 218 .fs_name = "host", 219 .fo_open = hostfs_open, 220 .fo_close = hostfs_close, 221 .fo_read = hostfs_read, 222 .fo_write = null_write, 223 .fo_seek = hostfs_seek, 224 .fo_stat = hostfs_stat, 225 .fo_readdir = hostfs_readdir 226 }; 227 228 /* 229 * Generic "null" host device. This goes hand and hand with the host fs object 230 * 231 * XXXX This and userboot for bhyve both use exactly the same code, modulo some 232 * formatting nits. Make them common. We mostly use it to 'gate' the open of the 233 * filesystem to only this device. 234 */ 235 236 static int 237 host_dev_init(void) 238 { 239 return (0); 240 } 241 242 static int 243 host_dev_print(int verbose) 244 { 245 char line[80]; 246 247 printf("%s devices:", host_dev.dv_name); 248 if (pager_output("\n") != 0) 249 return (1); 250 251 snprintf(line, sizeof(line), " host%d: Host filesystem\n", 0); 252 return (pager_output(line)); 253 } 254 255 /* 256 * 'Open' the host device. 257 */ 258 static int 259 host_dev_open(struct open_file *f, ...) 260 { 261 return (0); 262 } 263 264 static int 265 host_dev_close(struct open_file *f) 266 { 267 return (0); 268 } 269 270 static int 271 host_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size, 272 char *buf, size_t *rsize) 273 { 274 return (ENOSYS); 275 } 276 277 struct devsw host_dev = { 278 .dv_name = "host", 279 .dv_type = DEVT_NET, 280 .dv_init = host_dev_init, 281 .dv_strategy = host_dev_strategy, 282 .dv_open = host_dev_open, 283 .dv_close = host_dev_close, 284 .dv_ioctl = noioctl, 285 .dv_print = host_dev_print, 286 .dv_cleanup = NULL 287 }; 288