diff options
Diffstat (limited to 'usr/src/common/fs/bootfsops.c')
-rw-r--r-- | usr/src/common/fs/bootfsops.c | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/usr/src/common/fs/bootfsops.c b/usr/src/common/fs/bootfsops.c new file mode 100644 index 0000000000..5a693b80e5 --- /dev/null +++ b/usr/src/common/fs/bootfsops.c @@ -0,0 +1,329 @@ +/* + * 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 <sys/bootconf.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/vnode.h> +#include <sys/fs/ufs_fsdir.h> +#include <sys/fs/ufs_fs.h> +#include <sys/fs/ufs_inode.h> +#include <sys/sysmacros.h> +#include <sys/bootvfs.h> +#include <sys/bootinfo.h> +#include <sys/filep.h> + +#ifdef _BOOT +#include "../common/util.h" +#else +#include <sys/sunddi.h> +#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 +}; |