/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2013 Joyent, Inc. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #ifdef _BOOT #include "../common/util.h" #else #include #endif #define MAX_FILES MAX_BOOT_MODULES #define MAX_FDS 256 extern void *bkmem_alloc(size_t); extern void bkmem_free(void *, size_t); /* * TODO: Replace these declarations with inclusion of the ordinary userland * bootfs headers once they're available. */ typedef struct bfile { char bf_name[MAXPATHLEN]; caddr_t bf_addr; size_t bf_size; struct bfile *bf_next; uint64_t bf_ino; } bfile_t; typedef struct bf_fd { bfile_t *fd_file; off_t fd_pos; } bf_fd_t; static bfile_t *head; static uint_t init_done; static bf_fd_t fds[MAX_FDS]; static char cpath[MAXPATHLEN]; /* For canonicalising filenames */ static void bbootfs_closeall(int); static void canonicalise(const char *fn, char *out) { const char *p; char *q, *s; char *last; char *oc; int is_slash = 0; static char scratch[MAXPATHLEN]; if (fn == NULL) { *out = '\0'; return; } /* * Remove leading slashes and condense all multiple slashes into one. */ p = fn; while (*p == '/') ++p; for (q = scratch; *p != '\0'; p++) { if (*p == '/' && !is_slash) { *q++ = '/'; is_slash = 1; } else if (*p != '/') { *q++ = *p; is_slash = 0; } } *q = '\0'; if (strncmp(scratch, "system/boot/", 12) == 0 || strcmp(scratch, "system/boot") == 0) { s = scratch + 12; } else { s = scratch; } for (last = strsep(&s, "/"), q = oc = out; last != NULL; last = strsep(&s, "/")) { if (strcmp(last, ".") == 0) continue; if (strcmp(last, "..") == 0) { for (oc = q; oc > out && *oc != '/'; oc--) ; q = oc; continue; } if (q > out) *q++ = '/'; q += snprintf(q, MAXPATHLEN - (q - out), "%s", last); } *q = '\0'; } /* ARGSUSED */ static int bbootfs_mountroot(char *str) { return (-1); } static int bbootfs_unmountroot(void) { return (-1); } static int bbootfs_init(void) { bfile_t *fp; char propname[32]; uint64_t propval; uint_t i; for (i = 0; i < MAX_FILES; i++) { (void) snprintf(propname, sizeof (propname), "module-name-%u", i); if (do_bsys_getproplen(NULL, propname) < 0) break; if ((fp = bkmem_alloc(sizeof (bfile_t))) == NULL) { bbootfs_closeall(1); return (-1); } (void) do_bsys_getprop(NULL, propname, cpath); canonicalise(cpath, fp->bf_name); (void) snprintf(propname, sizeof (propname), "module-addr-%u", i); if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) { bkmem_free(fp, sizeof (bfile_t)); continue; } (void) do_bsys_getprop(NULL, propname, &propval); fp->bf_addr = (void *)(uintptr_t)propval; (void) snprintf(propname, sizeof (propname), "module-size-%u", i); if (do_bsys_getproplen(NULL, propname) != sizeof (uint64_t)) { bkmem_free(fp, sizeof (bfile_t)); continue; } (void) do_bsys_getprop(NULL, propname, &propval); fp->bf_size = (size_t)propval; fp->bf_ino = i; fp->bf_next = head; head = fp; } return (0); } /*ARGSUSED*/ static int bbootfs_open(char *fn, int flags) { uint_t i; bfile_t *fp; if (!init_done) { if (bbootfs_init() != 0) return (-1); init_done = 1; } canonicalise(fn, cpath); for (fp = head; fp != NULL; fp = fp->bf_next) { if (strcmp(fp->bf_name, cpath) == 0) break; } if (fp == NULL) return (-1); for (i = 0; i < MAX_FDS; i++) { if (fds[i].fd_file == NULL) { fds[i].fd_file = fp; fds[i].fd_pos = 0; return (i); } } return (-1); } static int bbootfs_close(int fd) { if (fds[fd].fd_file == NULL) return (-1); fds[fd].fd_file = NULL; fds[fd].fd_pos = 0; return (0); } static ssize_t bbootfs_read(int fd, caddr_t buf, size_t size) { ssize_t len; bf_fd_t *fdp = &fds[fd]; if (fdp->fd_file == NULL) return (-1); if (fdp->fd_pos >= fdp->fd_file->bf_size) return (-1); if (fdp->fd_pos + size > fdp->fd_file->bf_size) len = fdp->fd_file->bf_size - fdp->fd_pos; else len = size; bcopy(fdp->fd_file->bf_addr + fdp->fd_pos, buf, len); fdp->fd_pos += len; return (len); } static off_t bbootfs_lseek(int fd, off_t addr, int whence) { bf_fd_t *fdp = &fds[fd]; if (fdp->fd_file == NULL) return (-1); switch (whence) { case SEEK_CUR: fdp->fd_pos += addr; break; case SEEK_SET: fdp->fd_pos = addr; break; case SEEK_END: fdp->fd_pos = fdp->fd_file->bf_size; break; default: return (-1); } return (0); } static int bbootfs_fstat(int fd, struct bootstat *bsp) { bf_fd_t *fdp = &fds[fd]; if (fdp->fd_file == NULL) return (-1); bsp->st_dev = 1; bsp->st_ino = fdp->fd_file->bf_ino; bsp->st_mode = 0444; bsp->st_nlink = 1; bsp->st_uid = bsp->st_gid = 0; bsp->st_rdev = 0; bsp->st_size = fdp->fd_file->bf_size; bsp->st_blksize = 1; bsp->st_blocks = fdp->fd_file->bf_size; (void) strcpy(bsp->st_fstype, "bootfs"); return (0); } /* ARGSUSED */ static void bbootfs_closeall(int flag) { bfile_t *fp; while (head != NULL) { fp = head; head = head->bf_next; bkmem_free(fp, sizeof (bfile_t)); } init_done = 0; } struct boot_fs_ops bbootfs_ops = { "bootfs", bbootfs_mountroot, bbootfs_unmountroot, bbootfs_open, bbootfs_close, bbootfs_read, bbootfs_lseek, bbootfs_fstat, bbootfs_closeall, NULL };