summaryrefslogtreecommitdiffstats
path: root/sys/geom/part/g_part.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/geom/part/g_part.c')
-rw-r--r--sys/geom/part/g_part.c1385
1 files changed, 1385 insertions, 0 deletions
diff --git a/sys/geom/part/g_part.c b/sys/geom/part/g_part.c
new file mode 100644
index 0000000..2293e21
--- /dev/null
+++ b/sys/geom/part/g_part.c
@@ -0,0 +1,1385 @@
+/*-
+ * Copyright (c) 2002, 2005, 2006, 2007 Marcel Moolenaar
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bio.h>
+#include <sys/diskmbr.h>
+#include <sys/endian.h>
+#include <sys/kernel.h>
+#include <sys/kobj.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/systm.h>
+#include <sys/uuid.h>
+#include <geom/geom.h>
+#include <geom/geom_ctl.h>
+#include <geom/part/g_part.h>
+
+#include "g_part_if.h"
+
+static kobj_method_t g_part_null_methods[] = {
+ { 0, 0 }
+};
+
+static struct g_part_scheme g_part_null_scheme = {
+ NULL,
+ g_part_null_methods,
+ sizeof(struct g_part_table),
+};
+G_PART_SCHEME_DECLARE(g_part_null_scheme);
+
+SET_DECLARE(g_part_scheme_set, struct g_part_scheme);
+
+struct g_part_alias_list {
+ const char *lexeme;
+ enum g_part_alias alias;
+} g_part_alias_list[G_PART_ALIAS_COUNT] = {
+ { "@efi", G_PART_ALIAS_EFI },
+ { "@freebsd", G_PART_ALIAS_FREEBSD },
+ { "@freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP },
+ { "@freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS },
+ { "@freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM },
+ { "@mbr", G_PART_ALIAS_MBR }
+};
+
+/*
+ * The GEOM partitioning class.
+ */
+static g_ctl_req_t g_part_ctlreq;
+static g_ctl_destroy_geom_t g_part_destroy_geom;
+static g_taste_t g_part_taste;
+
+static g_access_t g_part_access;
+static g_dumpconf_t g_part_dumpconf;
+static g_orphan_t g_part_orphan;
+static g_spoiled_t g_part_spoiled;
+static g_start_t g_part_start;
+
+static struct g_class g_part_class = {
+ .name = "PART",
+ .version = G_VERSION,
+ /* Class methods. */
+ .ctlreq = g_part_ctlreq,
+ .destroy_geom = g_part_destroy_geom,
+ .taste = g_part_taste,
+ /* Geom methods. */
+ .access = g_part_access,
+ .dumpconf = g_part_dumpconf,
+ .orphan = g_part_orphan,
+ .spoiled = g_part_spoiled,
+ .start = g_part_start,
+};
+
+DECLARE_GEOM_CLASS(g_part_class, g_part);
+
+enum g_part_ctl {
+ G_PART_CTL_NONE,
+ G_PART_CTL_ADD,
+ G_PART_CTL_COMMIT,
+ G_PART_CTL_CREATE,
+ G_PART_CTL_DELETE,
+ G_PART_CTL_DESTROY,
+ G_PART_CTL_MODIFY,
+ G_PART_CTL_MOVE,
+ G_PART_CTL_QUERY,
+ G_PART_CTL_RECOVER,
+ G_PART_CTL_RESIZE,
+ G_PART_CTL_UNDO
+};
+
+/*
+ * Support functions.
+ */
+
+static void g_part_wither(struct g_geom *, int);
+
+const char *
+g_part_alias_name(enum g_part_alias alias)
+{
+ int i;
+
+ for (i = 0; i < G_PART_ALIAS_COUNT; i++) {
+ if (g_part_alias_list[i].alias != alias)
+ continue;
+ return (g_part_alias_list[i].lexeme);
+ }
+
+ return (NULL);
+}
+
+struct g_part_entry *
+g_part_new_entry(struct g_part_table *table, int index, quad_t start,
+ quad_t end)
+{
+ struct g_part_entry *entry, *last;
+
+ last = NULL;
+ LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
+ if (entry->gpe_index == index)
+ break;
+ if (entry->gpe_index > index) {
+ entry = NULL;
+ break;
+ }
+ last = entry;
+ }
+ if (entry == NULL) {
+ entry = g_malloc(table->gpt_scheme->gps_entrysz,
+ M_WAITOK | M_ZERO);
+ entry->gpe_index = index;
+ if (last == NULL)
+ LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
+ else
+ LIST_INSERT_AFTER(last, entry, gpe_entry);
+ }
+ entry->gpe_start = start;
+ entry->gpe_end = end;
+ return (entry);
+}
+
+static void
+g_part_new_provider(struct g_geom *gp, struct g_part_table *table,
+ struct g_part_entry *entry)
+{
+ char buf[32];
+ struct g_consumer *cp;
+ struct g_provider *pp;
+
+ cp = LIST_FIRST(&gp->consumer);
+ pp = cp->provider;
+
+ entry->gpe_offset = entry->gpe_start * pp->sectorsize;
+
+ if (entry->gpe_pp == NULL) {
+ entry->gpe_pp = g_new_providerf(gp, "%s%s", gp->name,
+ G_PART_NAME(table, entry, buf, sizeof(buf)));
+ entry->gpe_pp->private = entry; /* Close the circle. */
+ }
+ entry->gpe_pp->index = entry->gpe_index - 1; /* index is 1-based. */
+ entry->gpe_pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) *
+ pp->sectorsize;
+ entry->gpe_pp->sectorsize = pp->sectorsize;
+ entry->gpe_pp->flags = pp->flags & G_PF_CANDELETE;
+ if (pp->stripesize > 0) {
+ entry->gpe_pp->stripesize = pp->stripesize;
+ entry->gpe_pp->stripeoffset = (pp->stripeoffset +
+ entry->gpe_offset) % pp->stripesize;
+ }
+ g_error_provider(entry->gpe_pp, 0);
+}
+
+static int
+g_part_parm_geom(const char *p, struct g_geom **v)
+{
+ struct g_geom *gp;
+
+ LIST_FOREACH(gp, &g_part_class.geom, geom) {
+ if (!strcmp(p, gp->name))
+ break;
+ }
+ if (gp == NULL)
+ return (EINVAL);
+ *v = gp;
+ return (0);
+}
+
+static int
+g_part_parm_provider(const char *p, struct g_provider **v)
+{
+ struct g_provider *pp;
+
+ pp = g_provider_by_name(p);
+ if (pp == NULL)
+ return (EINVAL);
+ *v = pp;
+ return (0);
+}
+
+static int
+g_part_parm_quad(const char *p, quad_t *v)
+{
+ char *x;
+ quad_t q;
+
+ q = strtoq(p, &x, 0);
+ if (*x != '\0' || q < 0)
+ return (EINVAL);
+ *v = q;
+ return (0);
+}
+
+static int
+g_part_parm_scheme(const char *p, struct g_part_scheme **v)
+{
+ struct g_part_scheme **iter, *s;
+
+ s = NULL;
+ SET_FOREACH(iter, g_part_scheme_set) {
+ if ((*iter)->name == NULL)
+ continue;
+ if (!strcmp((*iter)->name, p)) {
+ s = *iter;
+ break;
+ }
+ }
+ if (s == NULL)
+ return (EINVAL);
+ *v = s;
+ return (0);
+}
+
+static int
+g_part_parm_str(const char *p, const char **v)
+{
+
+ if (p[0] == '\0')
+ return (EINVAL);
+ *v = p;
+ return (0);
+}
+
+static int
+g_part_parm_uint(const char *p, u_int *v)
+{
+ char *x;
+ long l;
+
+ l = strtol(p, &x, 0);
+ if (*x != '\0' || l < 0 || l > INT_MAX)
+ return (EINVAL);
+ *v = (unsigned int)l;
+ return (0);
+}
+
+static int
+g_part_probe(struct g_geom *gp, struct g_consumer *cp, int depth)
+{
+ struct g_part_scheme **iter, *scheme;
+ struct g_part_table *table;
+ int pri, probe;
+
+ table = gp->softc;
+ scheme = (table != NULL) ? table->gpt_scheme : &g_part_null_scheme;
+ pri = (scheme != &g_part_null_scheme) ? G_PART_PROBE(table, cp) :
+ INT_MIN;
+ if (pri == 0)
+ goto done;
+ if (pri > 0) { /* error */
+ scheme = &g_part_null_scheme;
+ pri = INT_MIN;
+ }
+
+ SET_FOREACH(iter, g_part_scheme_set) {
+ if ((*iter) == &g_part_null_scheme)
+ continue;
+ table = (void *)kobj_create((kobj_class_t)(*iter), M_GEOM,
+ M_WAITOK);
+ table->gpt_gp = gp;
+ table->gpt_scheme = *iter;
+ table->gpt_depth = depth;
+ probe = G_PART_PROBE(table, cp);
+ if (probe <= 0 && probe > pri) {
+ pri = probe;
+ scheme = *iter;
+ if (gp->softc != NULL)
+ kobj_delete((kobj_t)gp->softc, M_GEOM);
+ gp->softc = table;
+ if (pri == 0)
+ goto done;
+ } else
+ kobj_delete((kobj_t)table, M_GEOM);
+ }
+
+done:
+ return ((scheme == &g_part_null_scheme) ? ENXIO : 0);
+}
+
+/*
+ * Control request functions.
+ */
+
+static int
+g_part_ctl_add(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ char buf[16];
+ struct g_geom *gp;
+ struct g_provider *pp;
+ struct g_part_entry *delent, *last, *entry;
+ struct g_part_table *table;
+ quad_t end;
+ unsigned int index;
+ int error;
+
+ gp = gpp->gpp_geom;
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
+ g_topology_assert();
+
+ pp = LIST_FIRST(&gp->consumer)->provider;
+ table = gp->softc;
+ end = gpp->gpp_start + gpp->gpp_size - 1;
+
+ if (gpp->gpp_start < table->gpt_first ||
+ gpp->gpp_start > table->gpt_last) {
+ gctl_error(req, "%d start '%jd'", EINVAL,
+ (intmax_t)gpp->gpp_start);
+ return (EINVAL);
+ }
+ if (end < gpp->gpp_start || end > table->gpt_last) {
+ gctl_error(req, "%d size '%jd'", EINVAL,
+ (intmax_t)gpp->gpp_size);
+ return (EINVAL);
+ }
+ if (gpp->gpp_index > table->gpt_entries) {
+ gctl_error(req, "%d index '%d'", EINVAL, gpp->gpp_index);
+ return (EINVAL);
+ }
+
+ delent = last = NULL;
+ index = (gpp->gpp_index > 0) ? gpp->gpp_index : 1;
+ LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
+ if (entry->gpe_deleted) {
+ if (entry->gpe_index == index)
+ delent = entry;
+ continue;
+ }
+ if (entry->gpe_index == index) {
+ index = entry->gpe_index + 1;
+ last = entry;
+ }
+ if (gpp->gpp_start >= entry->gpe_start &&
+ gpp->gpp_start <= entry->gpe_end) {
+ gctl_error(req, "%d start '%jd'", ENOSPC,
+ (intmax_t)gpp->gpp_start);
+ return (ENOSPC);
+ }
+ if (end >= entry->gpe_start && end <= entry->gpe_end) {
+ gctl_error(req, "%d end '%jd'", ENOSPC, (intmax_t)end);
+ return (ENOSPC);
+ }
+ if (gpp->gpp_start < entry->gpe_start && end > entry->gpe_end) {
+ gctl_error(req, "%d size '%jd'", ENOSPC,
+ (intmax_t)gpp->gpp_size);
+ return (ENOSPC);
+ }
+ }
+ if (gpp->gpp_index > 0 && index != gpp->gpp_index) {
+ gctl_error(req, "%d index '%d'", EEXIST, gpp->gpp_index);
+ return (EEXIST);
+ }
+ snprintf(buf, sizeof(buf), "%d", index);
+ gctl_set_param(req, "index", buf, strlen(buf) + 1);
+
+ entry = (delent == NULL) ? g_malloc(table->gpt_scheme->gps_entrysz,
+ M_WAITOK | M_ZERO) : delent;
+ entry->gpe_index = index;
+ entry->gpe_start = gpp->gpp_start;
+ entry->gpe_end = end;
+ error = G_PART_ADD(table, entry, gpp);
+ if (error) {
+ gctl_error(req, "%d", error);
+ if (delent == NULL)
+ g_free(entry);
+ return (error);
+ }
+ if (delent == NULL) {
+ if (last == NULL)
+ LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
+ else
+ LIST_INSERT_AFTER(last, entry, gpe_entry);
+ entry->gpe_created = 1;
+ } else {
+ entry->gpe_deleted = 0;
+ entry->gpe_modified = 1;
+ }
+ g_part_new_provider(gp, table, entry);
+ return (0);
+}
+
+static int
+g_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ struct g_consumer *cp;
+ struct g_geom *gp;
+ struct g_provider *pp;
+ struct g_part_entry *entry, *tmp;
+ struct g_part_table *table;
+ char *buf;
+ int error, i;
+
+ gp = gpp->gpp_geom;
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
+ g_topology_assert();
+
+ table = gp->softc;
+ if (!table->gpt_opened) {
+ gctl_error(req, "%d", EPERM);
+ return (EPERM);
+ }
+
+ cp = LIST_FIRST(&gp->consumer);
+ if ((table->gpt_smhead | table->gpt_smtail) != 0) {
+ pp = cp->provider;
+ buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
+ while (table->gpt_smhead != 0) {
+ i = ffs(table->gpt_smhead) - 1;
+ error = g_write_data(cp, i * pp->sectorsize, buf,
+ pp->sectorsize);
+ if (error) {
+ g_free(buf);
+ goto fail;
+ }
+ table->gpt_smhead &= ~(1 << i);
+ }
+ while (table->gpt_smtail != 0) {
+ i = ffs(table->gpt_smtail) - 1;
+ error = g_write_data(cp, pp->mediasize - (i + 1) *
+ pp->sectorsize, buf, pp->sectorsize);
+ if (error) {
+ g_free(buf);
+ goto fail;
+ }
+ table->gpt_smtail &= ~(1 << i);
+ }
+ g_free(buf);
+ }
+
+ if (table->gpt_scheme == &g_part_null_scheme) {
+ g_access(cp, -1, -1, -1);
+ g_part_wither(gp, ENXIO);
+ return (0);
+ }
+
+ error = G_PART_WRITE(table, cp);
+ if (error)
+ goto fail;
+
+ LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
+ if (!entry->gpe_deleted) {
+ entry->gpe_created = 0;
+ entry->gpe_modified = 0;
+ continue;
+ }
+ LIST_REMOVE(entry, gpe_entry);
+ g_free(entry);
+ }
+ table->gpt_created = 0;
+ table->gpt_opened = 0;
+ g_access(cp, -1, -1, -1);
+ return (0);
+
+fail:
+ gctl_error(req, "%d", error);
+ return (error);
+}
+
+static int
+g_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ struct g_consumer *cp;
+ struct g_geom *gp;
+ struct g_provider *pp;
+ struct g_part_scheme *scheme;
+ struct g_part_table *null, *table;
+ int attr, error;
+
+ pp = gpp->gpp_provider;
+ scheme = gpp->gpp_scheme;
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
+ g_topology_assert();
+
+ /* Check that there isn't already a g_part geom on the provider. */
+ error = g_part_parm_geom(pp->name, &gp);
+ if (!error) {
+ null = gp->softc;
+ if (null->gpt_scheme != &g_part_null_scheme) {
+ gctl_error(req, "%d geom '%s'", EEXIST, pp->name);
+ return (EEXIST);
+ }
+ } else
+ null = NULL;
+
+ if ((gpp->gpp_parms & G_PART_PARM_ENTRIES) &&
+ (gpp->gpp_entries < scheme->gps_minent ||
+ gpp->gpp_entries > scheme->gps_maxent)) {
+ gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries);
+ return (EINVAL);
+ }
+
+ if (null == NULL)
+ gp = g_new_geomf(&g_part_class, "%s", pp->name);
+ gp->softc = kobj_create((kobj_class_t)gpp->gpp_scheme, M_GEOM,
+ M_WAITOK);
+ table = gp->softc;
+ table->gpt_gp = gp;
+ table->gpt_scheme = gpp->gpp_scheme;
+ table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ?
+ gpp->gpp_entries : scheme->gps_minent;
+ LIST_INIT(&table->gpt_entry);
+ if (null == NULL) {
+ cp = g_new_consumer(gp);
+ error = g_attach(cp, pp);
+ if (error == 0)
+ error = g_access(cp, 1, 1, 1);
+ if (error != 0) {
+ g_part_wither(gp, error);
+ gctl_error(req, "%d geom '%s'", error, pp->name);
+ return (error);
+ }
+ table->gpt_opened = 1;
+ } else {
+ cp = LIST_FIRST(&gp->consumer);
+ table->gpt_opened = null->gpt_opened;
+ table->gpt_smhead = null->gpt_smhead;
+ table->gpt_smtail = null->gpt_smtail;
+ }
+
+ g_topology_unlock();
+
+ /* Make sure we can nest and if so, determine our depth. */
+ error = g_getattr("PART::isleaf", cp, &attr);
+ if (!error && attr) {
+ error = ENODEV;
+ goto fail;
+ }
+ error = g_getattr("PART::depth", cp, &attr);
+ table->gpt_depth = (!error) ? attr + 1 : 0;
+
+ error = G_PART_CREATE(table, gpp);
+ if (error)
+ goto fail;
+
+ g_topology_lock();
+
+ table->gpt_created = 1;
+ if (null != NULL)
+ kobj_delete((kobj_t)null, M_GEOM);
+ return (0);
+
+fail:
+ g_topology_lock();
+ if (null == NULL) {
+ g_access(cp, -1, -1, -1);
+ g_part_wither(gp, error);
+ } else {
+ kobj_delete((kobj_t)gp->softc, M_GEOM);
+ gp->softc = null;
+ }
+ gctl_error(req, "%d provider", error);
+ return (error);
+}
+
+static int
+g_part_ctl_delete(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ struct g_geom *gp;
+ struct g_provider *pp;
+ struct g_part_entry *entry;
+ struct g_part_table *table;
+
+ gp = gpp->gpp_geom;
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
+ g_topology_assert();
+
+ table = gp->softc;
+
+ LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
+ if (entry->gpe_deleted)
+ continue;
+ if (entry->gpe_index == gpp->gpp_index)
+ break;
+ }
+ if (entry == NULL) {
+ gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
+ return (ENOENT);
+ }
+
+ pp = entry->gpe_pp;
+ if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) {
+ gctl_error(req, "%d", EBUSY);
+ return (EBUSY);
+ }
+
+ pp->private = NULL;
+ entry->gpe_pp = NULL;
+ if (entry->gpe_created) {
+ LIST_REMOVE(entry, gpe_entry);
+ g_free(entry);
+ } else {
+ entry->gpe_modified = 0;
+ entry->gpe_deleted = 1;
+ }
+ g_wither_provider(pp, ENXIO);
+ return (0);
+}
+
+static int
+g_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ struct g_geom *gp;
+ struct g_part_entry *entry;
+ struct g_part_table *null, *table;
+ int error;
+
+ gp = gpp->gpp_geom;
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
+ g_topology_assert();
+
+ table = gp->softc;
+ LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
+ if (entry->gpe_deleted)
+ continue;
+ gctl_error(req, "%d", EBUSY);
+ return (EBUSY);
+ }
+
+ error = G_PART_DESTROY(table, gpp);
+ if (error) {
+ gctl_error(req, "%d", error);
+ return (error);
+ }
+
+ gp->softc = kobj_create((kobj_class_t)&g_part_null_scheme, M_GEOM,
+ M_WAITOK);
+ null = gp->softc;
+ null->gpt_gp = gp;
+ null->gpt_scheme = &g_part_null_scheme;
+ LIST_INIT(&null->gpt_entry);
+ null->gpt_depth = table->gpt_depth;
+ null->gpt_opened = table->gpt_opened;
+ null->gpt_smhead = table->gpt_smhead;
+ null->gpt_smtail = table->gpt_smtail;
+
+ while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
+ LIST_REMOVE(entry, gpe_entry);
+ g_free(entry);
+ }
+ kobj_delete((kobj_t)table, M_GEOM);
+
+ return (0);
+}
+
+static int
+g_part_ctl_modify(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ struct g_geom *gp;
+ struct g_part_entry *entry;
+ struct g_part_table *table;
+ int error;
+
+ gp = gpp->gpp_geom;
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
+ g_topology_assert();
+
+ table = gp->softc;
+
+ LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
+ if (entry->gpe_deleted)
+ continue;
+ if (entry->gpe_index == gpp->gpp_index)
+ break;
+ }
+ if (entry == NULL) {
+ gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
+ return (ENOENT);
+ }
+
+ error = G_PART_MODIFY(table, entry, gpp);
+ if (error) {
+ gctl_error(req, "%d", error);
+ return (error);
+ }
+
+ if (!entry->gpe_created)
+ entry->gpe_modified = 1;
+ return (0);
+}
+
+static int
+g_part_ctl_move(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ gctl_error(req, "%d verb 'move'", ENOSYS);
+ return (ENOSYS);
+}
+
+static int
+g_part_ctl_query(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ gctl_error(req, "%d verb 'query'", ENOSYS);
+ return (ENOSYS);
+}
+
+static int
+g_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ gctl_error(req, "%d verb 'recover'", ENOSYS);
+ return (ENOSYS);
+}
+
+static int
+g_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ gctl_error(req, "%d verb 'resize'", ENOSYS);
+ return (ENOSYS);
+}
+
+static int
+g_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp)
+{
+ struct g_consumer *cp;
+ struct g_provider *pp;
+ struct g_geom *gp;
+ struct g_part_entry *entry, *tmp;
+ struct g_part_table *table;
+ int error, reprobe;
+
+ gp = gpp->gpp_geom;
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
+ g_topology_assert();
+
+ table = gp->softc;
+ if (!table->gpt_opened) {
+ gctl_error(req, "%d", EPERM);
+ return (EPERM);
+ }
+
+ cp = LIST_FIRST(&gp->consumer);
+ LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
+ entry->gpe_modified = 0;
+ if (entry->gpe_created) {
+ pp = entry->gpe_pp;
+ pp->private = NULL;
+ entry->gpe_pp = NULL;
+ g_wither_provider(pp, ENXIO);
+ entry->gpe_deleted = 1;
+ }
+ if (entry->gpe_deleted) {
+ LIST_REMOVE(entry, gpe_entry);
+ g_free(entry);
+ }
+ }
+
+ g_topology_unlock();
+
+ reprobe = (table->gpt_scheme == &g_part_null_scheme ||
+ table->gpt_created) ? 1 : 0;
+
+ if (reprobe) {
+ if (!LIST_EMPTY(&table->gpt_entry)) {
+ error = EBUSY;
+ goto fail;
+ }
+ error = g_part_probe(gp, cp, table->gpt_depth);
+ if (error)
+ goto fail;
+ table = gp->softc;
+ }
+
+ error = G_PART_READ(table, cp);
+ if (error)
+ goto fail;
+
+ g_topology_lock();
+
+ LIST_FOREACH(entry, &table->gpt_entry, gpe_entry)
+ g_part_new_provider(gp, table, entry);
+
+ table->gpt_opened = 0;
+ g_access(cp, -1, -1, -1);
+ return (0);
+
+fail:
+ g_topology_lock();
+ gctl_error(req, "%d", error);
+ return (error);
+}
+
+static void
+g_part_wither(struct g_geom *gp, int error)
+{
+ struct g_part_entry *entry;
+ struct g_part_table *table;
+
+ table = gp->softc;
+ if (table != NULL) {
+ while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
+ LIST_REMOVE(entry, gpe_entry);
+ g_free(entry);
+ }
+ if (gp->softc != NULL) {
+ kobj_delete((kobj_t)gp->softc, M_GEOM);
+ gp->softc = NULL;
+ }
+ }
+ g_wither_geom(gp, error);
+}
+
+/*
+ * Class methods.
+ */
+
+static void
+g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
+{
+ struct g_part_parms gpp;
+ struct g_part_table *table;
+ struct gctl_req_arg *ap;
+ const char *p;
+ enum g_part_ctl ctlreq;
+ unsigned int i, mparms, oparms, parm;
+ int error, modifies;
+
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb));
+ g_topology_assert();
+
+ ctlreq = G_PART_CTL_NONE;
+ modifies = 0;
+ mparms = oparms = 0;
+ switch (*verb) {
+ case 'a':
+ if (!strcmp(verb, "add")) {
+ ctlreq = G_PART_CTL_ADD;
+ modifies = 1;
+ mparms = G_PART_PARM_GEOM | G_PART_PARM_SIZE |
+ G_PART_PARM_START | G_PART_PARM_TYPE;
+ oparms = G_PART_PARM_FLAGS | G_PART_PARM_INDEX |
+ G_PART_PARM_LABEL;
+ }
+ break;
+ case 'c':
+ if (!strcmp(verb, "commit")) {
+ ctlreq = G_PART_CTL_COMMIT;
+ mparms = G_PART_PARM_GEOM;
+ oparms = G_PART_PARM_FLAGS;
+ } else if (!strcmp(verb, "create")) {
+ ctlreq = G_PART_CTL_CREATE;
+ modifies = 1;
+ mparms = G_PART_PARM_PROVIDER |
+ G_PART_PARM_SCHEME;
+ oparms = G_PART_PARM_ENTRIES | G_PART_PARM_FLAGS;
+ }
+ break;
+ case 'd':
+ if (!strcmp(verb, "delete")) {
+ ctlreq = G_PART_CTL_DELETE;
+ modifies = 1;
+ mparms = G_PART_PARM_GEOM | G_PART_PARM_INDEX;
+ oparms = G_PART_PARM_FLAGS;
+ } else if (!strcmp(verb, "destroy")) {
+ ctlreq = G_PART_CTL_DESTROY;
+ modifies = 1;
+ mparms = G_PART_PARM_GEOM;
+ oparms = G_PART_PARM_FLAGS;
+ }
+ break;
+ case 'm':
+ if (!strcmp(verb, "modify")) {
+ ctlreq = G_PART_CTL_MODIFY;
+ modifies = 1;
+ mparms = G_PART_PARM_GEOM | G_PART_PARM_INDEX;
+ oparms = G_PART_PARM_FLAGS | G_PART_PARM_LABEL |
+ G_PART_PARM_TYPE;
+ } else if (!strcmp(verb, "move")) {
+ ctlreq = G_PART_CTL_MOVE;
+ modifies = 1;
+ mparms = G_PART_PARM_GEOM | G_PART_PARM_INDEX;
+ oparms = G_PART_PARM_FLAGS;
+ }
+ break;
+ case 'q':
+ if (!strcmp(verb, "query")) {
+ ctlreq = G_PART_CTL_QUERY;
+ mparms = G_PART_PARM_REQUEST | G_PART_PARM_RESPONSE;
+ oparms = G_PART_PARM_FLAGS | G_PART_PARM_GEOM;
+ }
+ break;
+ case 'r':
+ if (!strcmp(verb, "recover")) {
+ ctlreq = G_PART_CTL_RECOVER;
+ modifies = 1;
+ mparms = G_PART_PARM_GEOM;
+ oparms = G_PART_PARM_FLAGS;
+ } else if (!strcmp(verb, "resize")) {
+ ctlreq = G_PART_CTL_RESIZE;
+ modifies = 1;
+ mparms = G_PART_PARM_GEOM | G_PART_PARM_INDEX;
+ oparms = G_PART_PARM_FLAGS;
+ }
+ break;
+ case 'u':
+ if (!strcmp(verb, "undo")) {
+ ctlreq = G_PART_CTL_UNDO;
+ mparms = G_PART_PARM_GEOM;
+ oparms = G_PART_PARM_FLAGS;
+ }
+ break;
+ }
+ if (ctlreq == G_PART_CTL_NONE) {
+ gctl_error(req, "%d verb '%s'", EINVAL, verb);
+ return;
+ }
+
+ bzero(&gpp, sizeof(gpp));
+ for (i = 0; i < req->narg; i++) {
+ ap = &req->arg[i];
+ parm = 0;
+ switch (ap->name[0]) {
+ case 'c':
+ if (!strcmp(ap->name, "class"))
+ continue;
+ break;
+ case 'e':
+ if (!strcmp(ap->name, "entries"))
+ parm = G_PART_PARM_ENTRIES;
+ break;
+ case 'f':
+ if (!strcmp(ap->name, "flags"))
+ parm = G_PART_PARM_FLAGS;
+ break;
+ case 'g':
+ if (!strcmp(ap->name, "geom"))
+ parm = G_PART_PARM_GEOM;
+ break;
+ case 'i':
+ if (!strcmp(ap->name, "index"))
+ parm = G_PART_PARM_INDEX;
+ break;
+ case 'l':
+ if (!strcmp(ap->name, "label"))
+ parm = G_PART_PARM_LABEL;
+ break;
+ case 'p':
+ if (!strcmp(ap->name, "provider"))
+ parm = G_PART_PARM_PROVIDER;
+ break;
+ case 'r':
+ if (!strcmp(ap->name, "request"))
+ parm = G_PART_PARM_REQUEST;
+ else if (!strcmp(ap->name, "response"))
+ parm = G_PART_PARM_RESPONSE;
+ break;
+ case 's':
+ if (!strcmp(ap->name, "scheme"))
+ parm = G_PART_PARM_SCHEME;
+ else if (!strcmp(ap->name, "size"))
+ parm = G_PART_PARM_SIZE;
+ else if (!strcmp(ap->name, "start"))
+ parm = G_PART_PARM_START;
+ break;
+ case 't':
+ if (!strcmp(ap->name, "type"))
+ parm = G_PART_PARM_TYPE;
+ break;
+ case 'v':
+ if (!strcmp(ap->name, "verb"))
+ continue;
+ break;
+ }
+ if ((parm & (mparms | oparms)) == 0) {
+ gctl_error(req, "%d param '%s'", EINVAL, ap->name);
+ return;
+ }
+ p = gctl_get_asciiparam(req, ap->name);
+ if (p == NULL) {
+ gctl_error(req, "%d param '%s'", ENOATTR, ap->name);
+ return;
+ }
+ switch (parm) {
+ case G_PART_PARM_ENTRIES:
+ error = g_part_parm_uint(p, &gpp.gpp_entries);
+ break;
+ case G_PART_PARM_FLAGS:
+ error = g_part_parm_str(p, &gpp.gpp_flags);
+ break;
+ case G_PART_PARM_GEOM:
+ error = g_part_parm_geom(p, &gpp.gpp_geom);
+ break;
+ case G_PART_PARM_INDEX:
+ error = g_part_parm_uint(p, &gpp.gpp_index);
+ break;
+ case G_PART_PARM_LABEL:
+ error = g_part_parm_str(p, &gpp.gpp_label);
+ break;
+ case G_PART_PARM_PROVIDER:
+ error = g_part_parm_provider(p, &gpp.gpp_provider);
+ break;
+ case G_PART_PARM_REQUEST:
+ error = g_part_parm_str(p, &gpp.gpp_request);
+ break;
+ case G_PART_PARM_RESPONSE:
+ error = 0; /* Write-only parameter. */
+ break;
+ case G_PART_PARM_SCHEME:
+ error = g_part_parm_scheme(p, &gpp.gpp_scheme);
+ break;
+ case G_PART_PARM_SIZE:
+ error = g_part_parm_quad(p, &gpp.gpp_size);
+ break;
+ case G_PART_PARM_START:
+ error = g_part_parm_quad(p, &gpp.gpp_start);
+ break;
+ case G_PART_PARM_TYPE:
+ error = g_part_parm_str(p, &gpp.gpp_type);
+ break;
+ default:
+ error = EDOOFUS;
+ break;
+ }
+ if (error) {
+ gctl_error(req, "%d %s '%s'", error, ap->name, p);
+ return;
+ }
+ gpp.gpp_parms |= parm;
+ }
+ if ((gpp.gpp_parms & mparms) != mparms) {
+ parm = mparms - (gpp.gpp_parms & mparms);
+ gctl_error(req, "%d param '%x'", ENOATTR, parm);
+ return;
+ }
+
+ /* Obtain permissions if possible/necessary. */
+ if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) {
+ table = gpp.gpp_geom->softc;
+ if (table != NULL && !table->gpt_opened) {
+ error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer),
+ 1, 1, 1);
+ if (error) {
+ gctl_error(req, "%d geom '%s'", error,
+ gpp.gpp_geom->name);
+ return;
+ }
+ table->gpt_opened = 1;
+ }
+ }
+
+ error = EDOOFUS; /* Prevent bogus uninit. warning. */
+ switch (ctlreq) {
+ case G_PART_CTL_NONE:
+ panic("%s", __func__);
+ case G_PART_CTL_ADD:
+ error = g_part_ctl_add(req, &gpp);
+ break;
+ case G_PART_CTL_COMMIT:
+ error = g_part_ctl_commit(req, &gpp);
+ break;
+ case G_PART_CTL_CREATE:
+ error = g_part_ctl_create(req, &gpp);
+ break;
+ case G_PART_CTL_DELETE:
+ error = g_part_ctl_delete(req, &gpp);
+ break;
+ case G_PART_CTL_DESTROY:
+ error = g_part_ctl_destroy(req, &gpp);
+ break;
+ case G_PART_CTL_MODIFY:
+ error = g_part_ctl_modify(req, &gpp);
+ break;
+ case G_PART_CTL_MOVE:
+ error = g_part_ctl_move(req, &gpp);
+ break;
+ case G_PART_CTL_QUERY:
+ error = g_part_ctl_query(req, &gpp);
+ break;
+ case G_PART_CTL_RECOVER:
+ error = g_part_ctl_recover(req, &gpp);
+ break;
+ case G_PART_CTL_RESIZE:
+ error = g_part_ctl_resize(req, &gpp);
+ break;
+ case G_PART_CTL_UNDO:
+ error = g_part_ctl_undo(req, &gpp);
+ break;
+ }
+}
+
+static int
+g_part_destroy_geom(struct gctl_req *req, struct g_class *mp,
+ struct g_geom *gp)
+{
+
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name));
+ g_topology_assert();
+
+ g_part_wither(gp, EINVAL);
+ return (0);
+}
+
+static struct g_geom *
+g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
+{
+ struct g_consumer *cp;
+ struct g_geom *gp;
+ struct g_part_entry *entry;
+ struct g_part_table *table;
+ int attr, depth;
+ int error;
+
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name));
+ g_topology_assert();
+
+ /*
+ * Create a GEOM with consumer and hook it up to the provider.
+ * With that we become part of the topology. Optain read access
+ * to the provider.
+ */
+ gp = g_new_geomf(mp, "%s", pp->name);
+ cp = g_new_consumer(gp);
+ error = g_attach(cp, pp);
+ if (error == 0)
+ error = g_access(cp, 1, 0, 0);
+ if (error != 0) {
+ g_part_wither(gp, error);
+ return (NULL);
+ }
+
+ g_topology_unlock();
+
+ /* Make sure we can nest and if so, determine our depth. */
+ error = g_getattr("PART::isleaf", cp, &attr);
+ if (!error && attr) {
+ error = ENODEV;
+ goto fail;
+ }
+ error = g_getattr("PART::depth", cp, &attr);
+ depth = (!error) ? attr + 1 : 0;
+
+ error = g_part_probe(gp, cp, depth);
+ if (error)
+ goto fail;
+
+ table = gp->softc;
+ error = G_PART_READ(table, cp);
+ if (error)
+ goto fail;
+
+ g_topology_lock();
+ LIST_FOREACH(entry, &table->gpt_entry, gpe_entry)
+ g_part_new_provider(gp, table, entry);
+
+ g_access(cp, -1, 0, 0);
+ return (gp);
+
+ fail:
+ g_topology_lock();
+ g_access(cp, -1, 0, 0);
+ g_part_wither(gp, error);
+ return (NULL);
+}
+
+/*
+ * Geom methods.
+ */
+
+static int
+g_part_access(struct g_provider *pp, int dr, int dw, int de)
+{
+ struct g_consumer *cp;
+
+ G_PART_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr,
+ dw, de));
+
+ cp = LIST_FIRST(&pp->geom->consumer);
+
+ /* We always gain write-exclusive access. */
+ return (g_access(cp, dr, dw, dw + de));
+}
+
+static void
+g_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
+ struct g_consumer *cp, struct g_provider *pp)
+{
+ char buf[64];
+ struct g_part_entry *entry;
+ struct g_part_table *table;
+
+ KASSERT(sb != NULL && gp != NULL, (__func__));
+ table = gp->softc;
+
+ if (indent == NULL) {
+ KASSERT(cp == NULL && pp != NULL, (__func__));
+ entry = pp->private;
+ if (entry == NULL)
+ return;
+ sbuf_printf(sb, " i %u o %ju ty %s", entry->gpe_index,
+ (uintmax_t)entry->gpe_offset,
+ G_PART_TYPE(table, entry, buf, sizeof(buf)));
+ } else if (cp != NULL) { /* Consumer configuration. */
+ KASSERT(pp == NULL, (__func__));
+ /* none */
+ } else if (pp != NULL) { /* Provider configuration. */
+ entry = pp->private;
+ if (entry == NULL)
+ return;
+ sbuf_printf(sb, "%s<index>%u</index>\n", indent,
+ entry->gpe_index);
+ sbuf_printf(sb, "%s<type>%s</type>\n", indent,
+ G_PART_TYPE(table, entry, buf, sizeof(buf)));
+ sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent,
+ (uintmax_t)entry->gpe_offset);
+ sbuf_printf(sb, "%s<length>%ju</length>\n", indent,
+ (uintmax_t)pp->mediasize);
+ G_PART_DUMPCONF(table, entry, sb, indent);
+ } else { /* Geom configuration. */
+ sbuf_printf(sb, "%s<entries>%u</entries>\n", indent,
+ table->gpt_entries);
+ sbuf_printf(sb, "%s<first>%ju</first>\n", indent,
+ (uintmax_t)table->gpt_first);
+ sbuf_printf(sb, "%s<last>%ju</last>\n", indent,
+ (uintmax_t)table->gpt_last);
+ G_PART_DUMPCONF(table, NULL, sb, indent);
+ }
+}
+
+static void
+g_part_orphan(struct g_consumer *cp)
+{
+ struct g_provider *pp;
+
+ pp = cp->provider;
+ KASSERT(pp != NULL, (__func__));
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
+ g_topology_assert();
+
+ KASSERT(pp->error != 0, (__func__));
+ g_part_wither(cp->geom, pp->error);
+}
+
+static void
+g_part_spoiled(struct g_consumer *cp)
+{
+
+ G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
+ g_topology_assert();
+
+ g_part_wither(cp->geom, ENXIO);
+}
+
+static void
+g_part_start(struct bio *bp)
+{
+ struct bio *bp2;
+ struct g_consumer *cp;
+ struct g_geom *gp;
+ struct g_part_entry *entry;
+ struct g_part_table *table;
+ struct g_kerneldump *gkd;
+ struct g_provider *pp;
+ int attr;
+
+ pp = bp->bio_to;
+ gp = pp->geom;
+ table = gp->softc;
+ cp = LIST_FIRST(&gp->consumer);
+
+ G_PART_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd,
+ pp->name));
+
+ entry = pp->private;
+ if (entry == NULL) {
+ g_io_deliver(bp, ENXIO);
+ return;
+ }
+
+ switch(bp->bio_cmd) {
+ case BIO_DELETE:
+ case BIO_READ:
+ case BIO_WRITE:
+ if (bp->bio_offset >= pp->mediasize) {
+ g_io_deliver(bp, EIO);
+ return;
+ }
+ bp2 = g_clone_bio(bp);
+ if (bp2 == NULL) {
+ g_io_deliver(bp, ENOMEM);
+ return;
+ }
+ if (bp2->bio_offset + bp2->bio_length > pp->mediasize)
+ bp2->bio_length = pp->mediasize - bp2->bio_offset;
+ bp2->bio_done = g_std_done;
+ bp2->bio_offset += entry->gpe_offset;
+ g_io_request(bp2, cp);
+ return;
+ case BIO_FLUSH:
+ break;
+ case BIO_GETATTR:
+ if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) {
+ /*
+ * Check that the partition is suitable for kernel
+ * dumps. Typically only swap partitions should be
+ * used.
+ */
+ if (!G_PART_DUMPTO(table, entry)) {
+ g_io_deliver(bp, ENXIO);
+ return;
+ }
+ gkd = (struct g_kerneldump *)bp->bio_data;
+ if (gkd->offset >= pp->mediasize) {
+ g_io_deliver(bp, EIO);
+ return;
+ }
+ if (gkd->offset + gkd->length > pp->mediasize)
+ gkd->length = pp->mediasize - gkd->offset;
+ gkd->offset += entry->gpe_offset;
+ } else if (!strcmp("PART::isleaf", bp->bio_attribute)) {
+ if (bp->bio_length != sizeof(int)) {
+ g_io_deliver(bp, EFAULT);
+ return;
+ }
+ attr = table->gpt_isleaf ? 1 : 0;
+ bcopy(&attr, bp->bio_data, sizeof(int));
+ bp->bio_completed = sizeof(int);
+ g_io_deliver(bp, 0);
+ return;
+ } else if (!strcmp("PART::depth", bp->bio_attribute)) {
+ if (bp->bio_length != sizeof(int)) {
+ g_io_deliver(bp, EFAULT);
+ return;
+ }
+ bcopy(&table->gpt_depth, bp->bio_data, sizeof(int));
+ bp->bio_completed = sizeof(int);
+ g_io_deliver(bp, 0);
+ return;
+ }
+ break;
+ default:
+ g_io_deliver(bp, EOPNOTSUPP);
+ return;
+ }
+
+ bp2 = g_clone_bio(bp);
+ if (bp2 == NULL) {
+ g_io_deliver(bp, ENOMEM);
+ return;
+ }
+ bp2->bio_done = g_std_done;
+ g_io_request(bp2, cp);
+}
OpenPOWER on IntegriCloud