summaryrefslogtreecommitdiff
path: root/usr/src/common/fs/bootfsops.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/common/fs/bootfsops.c')
-rw-r--r--usr/src/common/fs/bootfsops.c329
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
+};