g_part.c revision 177510
198186Sgordon/*- 298186Sgordon * Copyright (c) 2002, 2005, 2006, 2007 Marcel Moolenaar 378344Sobrien * All rights reserved. 498186Sgordon * 578344Sobrien * Redistribution and use in source and binary forms, with or without 678344Sobrien * modification, are permitted provided that the following conditions 778344Sobrien * are met: 878344Sobrien * 978344Sobrien * 1. Redistributions of source code must retain the above copyright 1078344Sobrien * notice, this list of conditions and the following disclaimer. 1178344Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1278344Sobrien * notice, this list of conditions and the following disclaimer in the 1378344Sobrien * documentation and/or other materials provided with the distribution. 1478344Sobrien * 1578344Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1678344Sobrien * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1778344Sobrien * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1878344Sobrien * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1978344Sobrien * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2078344Sobrien * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2178344Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2278344Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2378344Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2478344Sobrien * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2578344Sobrien */ 2678344Sobrien 2778344Sobrien#include <sys/cdefs.h> 2878344Sobrien__FBSDID("$FreeBSD: head/sys/geom/part/g_part.c 177510 2008-03-23 01:31:59Z marcel $"); 2978344Sobrien 3078344Sobrien#include <sys/param.h> 3178344Sobrien#include <sys/bio.h> 3278344Sobrien#include <sys/diskmbr.h> 3378344Sobrien#include <sys/endian.h> 3478344Sobrien#include <sys/kernel.h> 3578344Sobrien#include <sys/kobj.h> 3678344Sobrien#include <sys/limits.h> 3778344Sobrien#include <sys/lock.h> 3878344Sobrien#include <sys/malloc.h> 3978344Sobrien#include <sys/mutex.h> 4078344Sobrien#include <sys/queue.h> 4178344Sobrien#include <sys/sbuf.h> 4278344Sobrien#include <sys/systm.h> 4398186Sgordon#include <sys/uuid.h> 4498186Sgordon#include <geom/geom.h> 4598186Sgordon#include <geom/geom_ctl.h> 4698186Sgordon#include <geom/geom_int.h> 4798186Sgordon#include <geom/part/g_part.h> 4898186Sgordon 49103018Sgordon#include "g_part_if.h" 5098186Sgordon 51103018Sgordonstatic kobj_method_t g_part_null_methods[] = { 5298186Sgordon { 0, 0 } 5398186Sgordon}; 5498186Sgordon 5598186Sgordonstatic struct g_part_scheme g_part_null_scheme = { 5698186Sgordon "(none)", 5798186Sgordon g_part_null_methods, 5898186Sgordon sizeof(struct g_part_table), 5998186Sgordon}; 6098186Sgordon 6178344SobrienTAILQ_HEAD(, g_part_scheme) g_part_schemes = 6278344Sobrien TAILQ_HEAD_INITIALIZER(g_part_schemes); 6378344Sobrien 6478344Sobrienstruct g_part_alias_list { 6598186Sgordon const char *lexeme; 6698186Sgordon enum g_part_alias alias; 6798186Sgordon} g_part_alias_list[G_PART_ALIAS_COUNT] = { 6898186Sgordon { "efi", G_PART_ALIAS_EFI }, 6998186Sgordon { "freebsd", G_PART_ALIAS_FREEBSD }, 7098186Sgordon { "freebsd-boot", G_PART_ALIAS_FREEBSD_BOOT }, 7198186Sgordon { "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP }, 7298186Sgordon { "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS }, 7398186Sgordon { "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM }, 7498186Sgordon { "freebsd-zfs", G_PART_ALIAS_FREEBSD_ZFS }, 7598186Sgordon { "mbr", G_PART_ALIAS_MBR } 7698186Sgordon}; 7798186Sgordon 7898186Sgordon/* 7998186Sgordon * The GEOM partitioning class. 8098186Sgordon */ 8198186Sgordonstatic g_ctl_req_t g_part_ctlreq; 82103018Sgordonstatic g_ctl_destroy_geom_t g_part_destroy_geom; 8398186Sgordonstatic g_fini_t g_part_fini; 8498186Sgordonstatic g_init_t g_part_init; 8598186Sgordonstatic g_taste_t g_part_taste; 8698186Sgordon 8798186Sgordonstatic g_access_t g_part_access; 8898186Sgordonstatic g_dumpconf_t g_part_dumpconf; 8998186Sgordonstatic g_orphan_t g_part_orphan; 9098186Sgordonstatic g_spoiled_t g_part_spoiled; 9198186Sgordonstatic g_start_t g_part_start; 9298186Sgordon 9398186Sgordonstatic struct g_class g_part_class = { 9498186Sgordon .name = "PART", 9598186Sgordon .version = G_VERSION, 9698186Sgordon /* Class methods. */ 9798186Sgordon .ctlreq = g_part_ctlreq, 9898186Sgordon .destroy_geom = g_part_destroy_geom, 9998186Sgordon .fini = g_part_fini, 10098186Sgordon .init = g_part_init, 10198186Sgordon .taste = g_part_taste, 10298186Sgordon /* Geom methods. */ 10398186Sgordon .access = g_part_access, 10498186Sgordon .dumpconf = g_part_dumpconf, 10598186Sgordon .orphan = g_part_orphan, 10698186Sgordon .spoiled = g_part_spoiled, 10798186Sgordon .start = g_part_start, 10898186Sgordon}; 10998186Sgordon 11098186SgordonDECLARE_GEOM_CLASS(g_part_class, g_part); 11198186Sgordon 11298186Sgordonenum g_part_ctl { 11398186Sgordon G_PART_CTL_NONE, 11498186Sgordon G_PART_CTL_ADD, 11578344Sobrien G_PART_CTL_COMMIT, 11678344Sobrien G_PART_CTL_CREATE, 11778344Sobrien G_PART_CTL_DELETE, 11878344Sobrien G_PART_CTL_DESTROY, 11978344Sobrien G_PART_CTL_MODIFY, 12078344Sobrien G_PART_CTL_MOVE, 12178344Sobrien G_PART_CTL_RECOVER, 12298186Sgordon G_PART_CTL_RESIZE, 12378344Sobrien G_PART_CTL_UNDO 12478344Sobrien}; 12578344Sobrien 12678344Sobrien/* 12778344Sobrien * Support functions. 12878344Sobrien */ 12978344Sobrien 13078344Sobrienstatic void g_part_wither(struct g_geom *, int); 13178344Sobrien 13278344Sobrienconst char * 13378344Sobrieng_part_alias_name(enum g_part_alias alias) 13478344Sobrien{ 135106643Sgordon int i; 13678344Sobrien 13778344Sobrien for (i = 0; i < G_PART_ALIAS_COUNT; i++) { 13878344Sobrien if (g_part_alias_list[i].alias != alias) 13978344Sobrien continue; 14078344Sobrien return (g_part_alias_list[i].lexeme); 14198186Sgordon } 14298186Sgordon 14378344Sobrien return (NULL); 14498186Sgordon} 14598186Sgordon 14698186Sgordonvoid 14798186Sgordong_part_geometry_heads(off_t blocks, u_int sectors, off_t *bestchs, 14898186Sgordon u_int *bestheads) 14998186Sgordon{ 15098186Sgordon static u_int candidate_heads[] = { 1, 2, 16, 32, 64, 128, 255, 0 }; 15198186Sgordon off_t chs, cylinders; 15298186Sgordon u_int heads; 15378344Sobrien int idx; 15498186Sgordon 15598186Sgordon *bestchs = 0; 15698186Sgordon *bestheads = 0; 15798186Sgordon for (idx = 0; candidate_heads[idx] != 0; idx++) { 15898186Sgordon heads = candidate_heads[idx]; 15978344Sobrien cylinders = blocks / heads / sectors; 16078344Sobrien if (cylinders < heads || cylinders < sectors) 16198186Sgordon break; 16278344Sobrien if (cylinders > 1023) 16378344Sobrien continue; 16478344Sobrien chs = cylinders * heads * sectors; 16578344Sobrien if (chs > *bestchs || (chs == *bestchs && *bestheads == 1)) { 16678344Sobrien *bestchs = chs; 16778344Sobrien *bestheads = heads; 16878344Sobrien } 16978344Sobrien } 17098186Sgordon} 17178344Sobrien 17278344Sobrienstatic void 17398186Sgordong_part_geometry(struct g_part_table *table, struct g_consumer *cp, 17478344Sobrien off_t blocks) 17578344Sobrien{ 17678344Sobrien static u_int candidate_sectors[] = { 1, 9, 17, 33, 63, 0 }; 17778344Sobrien off_t chs, bestchs; 17898186Sgordon u_int heads, sectors; 17998186Sgordon int idx; 18078344Sobrien 18198186Sgordon if (g_getattr("GEOM::fwsectors", cp, §ors) != 0 || 18298186Sgordon sectors < 1 || sectors > 63 || 18378344Sobrien g_getattr("GEOM::fwheads", cp, &heads) != 0 || 18478344Sobrien heads < 1 || heads > 255) { 18578344Sobrien table->gpt_fixgeom = 0; 18678344Sobrien table->gpt_heads = 0; 18778344Sobrien table->gpt_sectors = 0; 18898186Sgordon bestchs = 0; 18978344Sobrien for (idx = 0; candidate_sectors[idx] != 0; idx++) { 19098186Sgordon sectors = candidate_sectors[idx]; 19178344Sobrien g_part_geometry_heads(blocks, sectors, &chs, &heads); 19278344Sobrien if (chs == 0) 19398186Sgordon continue; 19478344Sobrien /* 19578344Sobrien * Prefer a geometry with sectors > 1, but only if 19678344Sobrien * it doesn't bump down the numbver of heads to 1. 19778344Sobrien */ 19898186Sgordon if (chs > bestchs || (chs == bestchs && heads > 1 && 19978344Sobrien table->gpt_sectors == 1)) { 20078344Sobrien bestchs = chs; 20198186Sgordon table->gpt_heads = heads; 20278344Sobrien table->gpt_sectors = sectors; 20378344Sobrien } 20478344Sobrien } 20598186Sgordon /* 20678344Sobrien * If we didn't find a geometry at all, then the disk is 20798186Sgordon * too big. This means we can use the maximum number of 20898186Sgordon * heads and sectors. 20978344Sobrien */ 21078344Sobrien if (bestchs == 0) { 21178344Sobrien table->gpt_heads = 255; 21278344Sobrien table->gpt_sectors = 63; 21398186Sgordon } 21478344Sobrien } else { 21598186Sgordon table->gpt_fixgeom = 1; 21678344Sobrien table->gpt_heads = heads; 21798186Sgordon table->gpt_sectors = sectors; 21898186Sgordon } 21998186Sgordon} 22098186Sgordon 22198186Sgordonstruct g_part_entry * 22298186Sgordong_part_new_entry(struct g_part_table *table, int index, quad_t start, 22398186Sgordon quad_t end) 22498186Sgordon{ 22598186Sgordon struct g_part_entry *entry, *last; 22698186Sgordon 22798186Sgordon last = NULL; 22898186Sgordon LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 22998186Sgordon if (entry->gpe_index == index) 23098186Sgordon break; 23198186Sgordon if (entry->gpe_index > index) { 23298186Sgordon entry = NULL; 23398186Sgordon break; 23498186Sgordon } 23598186Sgordon last = entry; 23698186Sgordon } 23798186Sgordon if (entry == NULL) { 23898186Sgordon entry = g_malloc(table->gpt_scheme->gps_entrysz, 23998186Sgordon M_WAITOK | M_ZERO); 24098186Sgordon entry->gpe_index = index; 24198186Sgordon if (last == NULL) 24298186Sgordon LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry); 24398186Sgordon else 24498186Sgordon LIST_INSERT_AFTER(last, entry, gpe_entry); 24598186Sgordon } 24678344Sobrien entry->gpe_start = start; 24798186Sgordon entry->gpe_end = end; 24898186Sgordon return (entry); 24998186Sgordon} 25098186Sgordon 25198186Sgordonstatic void 25298186Sgordong_part_new_provider(struct g_geom *gp, struct g_part_table *table, 25378344Sobrien struct g_part_entry *entry) 25498186Sgordon{ 25598186Sgordon char buf[32]; 25698186Sgordon struct g_consumer *cp; 25798186Sgordon struct g_provider *pp; 25898186Sgordon 25998186Sgordon cp = LIST_FIRST(&gp->consumer); 26098186Sgordon pp = cp->provider; 26198186Sgordon 26298186Sgordon entry->gpe_offset = entry->gpe_start * pp->sectorsize; 26398186Sgordon 26498186Sgordon if (entry->gpe_pp == NULL) { 26598186Sgordon entry->gpe_pp = g_new_providerf(gp, "%s%s", gp->name, 26698186Sgordon G_PART_NAME(table, entry, buf, sizeof(buf))); 26798186Sgordon entry->gpe_pp->private = entry; /* Close the circle. */ 26898186Sgordon } 26998186Sgordon entry->gpe_pp->index = entry->gpe_index - 1; /* index is 1-based. */ 27098186Sgordon entry->gpe_pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) * 27198186Sgordon pp->sectorsize; 27298186Sgordon entry->gpe_pp->sectorsize = pp->sectorsize; 27398186Sgordon entry->gpe_pp->flags = pp->flags & G_PF_CANDELETE; 27498186Sgordon if (pp->stripesize > 0) { 27598186Sgordon entry->gpe_pp->stripesize = pp->stripesize; 27698186Sgordon entry->gpe_pp->stripeoffset = (pp->stripeoffset + 27798186Sgordon entry->gpe_offset) % pp->stripesize; 27898186Sgordon } 27998186Sgordon g_error_provider(entry->gpe_pp, 0); 28098186Sgordon} 28198186Sgordon 28298186Sgordonstatic int 28398186Sgordong_part_parm_geom(const char *p, struct g_geom **v) 28498186Sgordon{ 28598186Sgordon struct g_geom *gp; 28698186Sgordon 28798186Sgordon LIST_FOREACH(gp, &g_part_class.geom, geom) { 28898186Sgordon if (!strcmp(p, gp->name)) 28998186Sgordon break; 29098186Sgordon } 29198186Sgordon if (gp == NULL) 29298186Sgordon return (EINVAL); 29398186Sgordon *v = gp; 29498186Sgordon return (0); 29598186Sgordon} 29698186Sgordon 29798186Sgordonstatic int 29898186Sgordong_part_parm_provider(const char *p, struct g_provider **v) 29998186Sgordon{ 30098186Sgordon struct g_provider *pp; 30198186Sgordon 30278344Sobrien pp = g_provider_by_name(p); 30398186Sgordon if (pp == NULL) 30498186Sgordon return (EINVAL); 30598186Sgordon *v = pp; 30698186Sgordon return (0); 30778344Sobrien} 30898186Sgordon 30998186Sgordonstatic int 31098186Sgordong_part_parm_quad(const char *p, quad_t *v) 31178344Sobrien{ 31278344Sobrien char *x; 31378344Sobrien quad_t q; 31498186Sgordon 31598186Sgordon q = strtoq(p, &x, 0); 31698186Sgordon if (*x != '\0' || q < 0) 31798186Sgordon return (EINVAL); 31898186Sgordon *v = q; 31978344Sobrien return (0); 32098186Sgordon} 32198186Sgordon 32278344Sobrienstatic int 32398186Sgordong_part_parm_scheme(const char *p, struct g_part_scheme **v) 32498186Sgordon{ 32578344Sobrien struct g_part_scheme *s; 32678344Sobrien 32778344Sobrien TAILQ_FOREACH(s, &g_part_schemes, scheme_list) { 32898186Sgordon if (s == &g_part_null_scheme) 32998186Sgordon continue; 33078344Sobrien if (!strcasecmp(s->name, p)) 33178344Sobrien break; 33278344Sobrien } 33398186Sgordon if (s == NULL) 33478344Sobrien return (EINVAL); 33578344Sobrien *v = s; 33678344Sobrien return (0); 33778344Sobrien} 33898186Sgordon 33998186Sgordonstatic int 34098186Sgordong_part_parm_str(const char *p, const char **v) 34178344Sobrien{ 34278344Sobrien 34398186Sgordon if (p[0] == '\0') 34498186Sgordon return (EINVAL); 34598186Sgordon *v = p; 34678344Sobrien return (0); 34798186Sgordon} 34898186Sgordon 34978344Sobrienstatic int 35078344Sobrieng_part_parm_uint(const char *p, u_int *v) 35178344Sobrien{ 35278344Sobrien char *x; 35398186Sgordon long l; 35478344Sobrien 35578344Sobrien l = strtol(p, &x, 0); 35678344Sobrien if (*x != '\0' || l < 0 || l > INT_MAX) 35778344Sobrien return (EINVAL); 35878344Sobrien *v = (unsigned int)l; 35978344Sobrien return (0); 36078344Sobrien} 36178344Sobrien 36278344Sobrienstatic int 36378344Sobrieng_part_probe(struct g_geom *gp, struct g_consumer *cp, int depth) 36478344Sobrien{ 36578344Sobrien struct g_part_scheme *iter, *scheme; 36698186Sgordon struct g_part_table *table; 36778344Sobrien int pri, probe; 36878344Sobrien 36998186Sgordon table = gp->softc; 37078344Sobrien scheme = (table != NULL) ? table->gpt_scheme : NULL; 37198186Sgordon pri = (scheme != NULL) ? G_PART_PROBE(table, cp) : INT_MIN; 37298186Sgordon if (pri == 0) 37398186Sgordon goto done; 37478344Sobrien if (pri > 0) { /* error */ 37598186Sgordon scheme = NULL; 37678344Sobrien pri = INT_MIN; 37778344Sobrien } 37898186Sgordon 37998186Sgordon TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) { 38098186Sgordon if (iter == &g_part_null_scheme) 38198186Sgordon continue; 38278344Sobrien table = (void *)kobj_create((kobj_class_t)iter, M_GEOM, 38398186Sgordon M_WAITOK); 38478344Sobrien table->gpt_gp = gp; 38598186Sgordon table->gpt_scheme = iter; 38698186Sgordon table->gpt_depth = depth; 38798186Sgordon probe = G_PART_PROBE(table, cp); 38898186Sgordon if (probe <= 0 && probe > pri) { 38978344Sobrien pri = probe; 39078344Sobrien scheme = iter; 39178344Sobrien if (gp->softc != NULL) 39278344Sobrien kobj_delete((kobj_t)gp->softc, M_GEOM); 39378344Sobrien gp->softc = table; 39478344Sobrien if (pri == 0) 39578344Sobrien goto done; 39678344Sobrien } else 39778344Sobrien kobj_delete((kobj_t)table, M_GEOM); 39878344Sobrien } 39978344Sobrien 40078344Sobriendone: 40198186Sgordon return ((scheme == NULL) ? ENXIO : 0); 40298186Sgordon} 40378344Sobrien 40498186Sgordon/* 40598186Sgordon * Control request functions. 40678344Sobrien */ 40778344Sobrien 40878344Sobrienstatic int 40978344Sobrieng_part_ctl_add(struct gctl_req *req, struct g_part_parms *gpp) 41098186Sgordon{ 41178344Sobrien char buf[32]; 41298186Sgordon struct g_geom *gp; 41398186Sgordon struct g_provider *pp; 41498186Sgordon struct g_part_entry *delent, *last, *entry; 41598186Sgordon struct g_part_table *table; 41678344Sobrien struct sbuf *sb; 41798186Sgordon quad_t end; 41898186Sgordon unsigned int index; 41978344Sobrien int error; 42078344Sobrien 42178344Sobrien gp = gpp->gpp_geom; 42278344Sobrien G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 42398186Sgordon g_topology_assert(); 42478344Sobrien 42598186Sgordon pp = LIST_FIRST(&gp->consumer)->provider; 42698186Sgordon table = gp->softc; 42798186Sgordon end = gpp->gpp_start + gpp->gpp_size - 1; 42898186Sgordon 42998186Sgordon if (gpp->gpp_start < table->gpt_first || 43098186Sgordon gpp->gpp_start > table->gpt_last) { 43198186Sgordon gctl_error(req, "%d start '%jd'", EINVAL, 43298186Sgordon (intmax_t)gpp->gpp_start); 43398186Sgordon return (EINVAL); 43498186Sgordon } 43598186Sgordon if (end < gpp->gpp_start || end > table->gpt_last) { 43698186Sgordon gctl_error(req, "%d size '%jd'", EINVAL, 43798186Sgordon (intmax_t)gpp->gpp_size); 43898186Sgordon return (EINVAL); 43998186Sgordon } 44098186Sgordon if (gpp->gpp_index > table->gpt_entries) { 44198186Sgordon gctl_error(req, "%d index '%d'", EINVAL, gpp->gpp_index); 44298186Sgordon return (EINVAL); 44398186Sgordon } 44498186Sgordon 44598186Sgordon delent = last = NULL; 44698186Sgordon index = (gpp->gpp_index > 0) ? gpp->gpp_index : 1; 44798186Sgordon LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 44898186Sgordon if (entry->gpe_deleted) { 44978344Sobrien if (entry->gpe_index == index) 45078344Sobrien delent = entry; 45198186Sgordon continue; 45278344Sobrien } 45398186Sgordon if (entry->gpe_index == index) 45478344Sobrien index = entry->gpe_index + 1; 45578344Sobrien if (entry->gpe_index < index) 45698186Sgordon last = entry; 45778344Sobrien if (entry->gpe_internal) 45898186Sgordon continue; 45998186Sgordon if (gpp->gpp_start >= entry->gpe_start && 46078344Sobrien gpp->gpp_start <= entry->gpe_end) { 46178344Sobrien gctl_error(req, "%d start '%jd'", ENOSPC, 46298186Sgordon (intmax_t)gpp->gpp_start); 46398186Sgordon return (ENOSPC); 46478344Sobrien } 46578344Sobrien if (end >= entry->gpe_start && end <= entry->gpe_end) { 46678344Sobrien gctl_error(req, "%d end '%jd'", ENOSPC, (intmax_t)end); 46778344Sobrien return (ENOSPC); 46878344Sobrien } 46978344Sobrien if (gpp->gpp_start < entry->gpe_start && end > entry->gpe_end) { 47098186Sgordon gctl_error(req, "%d size '%jd'", ENOSPC, 47198186Sgordon (intmax_t)gpp->gpp_size); 47298186Sgordon return (ENOSPC); 47398186Sgordon } 47498186Sgordon } 47578344Sobrien if (gpp->gpp_index > 0 && index != gpp->gpp_index) { 47698186Sgordon gctl_error(req, "%d index '%d'", EEXIST, gpp->gpp_index); 47778344Sobrien return (EEXIST); 47898186Sgordon } 47998186Sgordon 48078344Sobrien entry = (delent == NULL) ? g_malloc(table->gpt_scheme->gps_entrysz, 48198186Sgordon M_WAITOK | M_ZERO) : delent; 48278344Sobrien entry->gpe_index = index; 48398186Sgordon entry->gpe_start = gpp->gpp_start; 48498186Sgordon entry->gpe_end = end; 48598186Sgordon error = G_PART_ADD(table, entry, gpp); 48678344Sobrien if (error) { 48778344Sobrien gctl_error(req, "%d", error); 48898186Sgordon if (delent == NULL) 48978344Sobrien g_free(entry); 49078344Sobrien return (error); 49178344Sobrien } 49298186Sgordon if (delent == NULL) { 49378344Sobrien if (last == NULL) 49478344Sobrien LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry); 49578344Sobrien else 49678344Sobrien LIST_INSERT_AFTER(last, entry, gpe_entry); 49798186Sgordon entry->gpe_created = 1; 49878344Sobrien } else { 49998186Sgordon entry->gpe_deleted = 0; 50078344Sobrien entry->gpe_modified = 1; 50198186Sgordon } 50298186Sgordon g_part_new_provider(gp, table, entry); 50398186Sgordon 50478344Sobrien /* Provide feedback if so requested. */ 50598186Sgordon if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 50698186Sgordon sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 50798186Sgordon sbuf_printf(sb, "%s%s added\n", gp->name, 50898186Sgordon G_PART_NAME(table, entry, buf, sizeof(buf))); 50998186Sgordon sbuf_finish(sb); 51098186Sgordon gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 51178344Sobrien sbuf_delete(sb); 51298186Sgordon } 51378344Sobrien return (0); 51478344Sobrien} 51578344Sobrien 51698186Sgordonstatic int 51778344Sobrieng_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp) 51878344Sobrien{ 51978344Sobrien struct g_consumer *cp; 52078344Sobrien struct g_geom *gp; 52178344Sobrien struct g_provider *pp; 52278344Sobrien struct g_part_entry *entry, *tmp; 52378344Sobrien struct g_part_table *table; 52478344Sobrien char *buf; 52598186Sgordon int error, i; 52678344Sobrien 52778344Sobrien gp = gpp->gpp_geom; 52878344Sobrien G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 52978344Sobrien g_topology_assert(); 53078344Sobrien 53178344Sobrien table = gp->softc; 53298186Sgordon if (!table->gpt_opened) { 53398186Sgordon gctl_error(req, "%d", EPERM); 53478344Sobrien return (EPERM); 53598186Sgordon } 53678344Sobrien 53778344Sobrien cp = LIST_FIRST(&gp->consumer); 53878344Sobrien if ((table->gpt_smhead | table->gpt_smtail) != 0) { 53998186Sgordon pp = cp->provider; 54078344Sobrien buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO); 54178344Sobrien while (table->gpt_smhead != 0) { 54278344Sobrien i = ffs(table->gpt_smhead) - 1; 54398186Sgordon error = g_write_data(cp, i * pp->sectorsize, buf, 54498186Sgordon pp->sectorsize); 54598186Sgordon if (error) { 54698186Sgordon g_free(buf); 54778344Sobrien goto fail; 54878344Sobrien } 54978344Sobrien table->gpt_smhead &= ~(1 << i); 55098186Sgordon } 55178344Sobrien while (table->gpt_smtail != 0) { 55278344Sobrien i = ffs(table->gpt_smtail) - 1; 55398186Sgordon error = g_write_data(cp, pp->mediasize - (i + 1) * 55498186Sgordon pp->sectorsize, buf, pp->sectorsize); 55578344Sobrien if (error) { 55678344Sobrien g_free(buf); 55778344Sobrien goto fail; 55878344Sobrien } 55978344Sobrien table->gpt_smtail &= ~(1 << i); 56078344Sobrien } 56178344Sobrien g_free(buf); 56298186Sgordon } 56398186Sgordon 56478344Sobrien if (table->gpt_scheme == &g_part_null_scheme) { 56578344Sobrien g_access(cp, -1, -1, -1); 56678344Sobrien g_part_wither(gp, ENXIO); 56778344Sobrien return (0); 56898186Sgordon } 56978344Sobrien 57078344Sobrien error = G_PART_WRITE(table, cp); 57178344Sobrien if (error) 57278344Sobrien goto fail; 57378344Sobrien 57478344Sobrien LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) { 57578344Sobrien if (!entry->gpe_deleted) { 57678344Sobrien entry->gpe_created = 0; 57778344Sobrien entry->gpe_modified = 0; 57898186Sgordon continue; 57978344Sobrien } 58078344Sobrien LIST_REMOVE(entry, gpe_entry); 58178344Sobrien g_free(entry); 58278344Sobrien } 58378344Sobrien table->gpt_created = 0; 58478344Sobrien table->gpt_opened = 0; 58578344Sobrien g_access(cp, -1, -1, -1); 58698186Sgordon return (0); 58778344Sobrien 58878344Sobrienfail: 58978344Sobrien gctl_error(req, "%d", error); 59078344Sobrien return (error); 59178344Sobrien} 59278344Sobrien 59378344Sobrienstatic int 59498186Sgordong_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp) 59578344Sobrien{ 59678344Sobrien struct g_consumer *cp; 59778344Sobrien struct g_geom *gp; 59878344Sobrien struct g_provider *pp; 59978344Sobrien struct g_part_scheme *scheme; 60078344Sobrien struct g_part_table *null, *table; 60178344Sobrien struct sbuf *sb; 60278344Sobrien int attr, error; 60398186Sgordon 60478344Sobrien pp = gpp->gpp_provider; 60578344Sobrien scheme = gpp->gpp_scheme; 60678344Sobrien G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name)); 60778344Sobrien g_topology_assert(); 60878344Sobrien 60978344Sobrien /* Check that there isn't already a g_part geom on the provider. */ 61078344Sobrien error = g_part_parm_geom(pp->name, &gp); 61178344Sobrien if (!error) { 61278344Sobrien null = gp->softc; 61378344Sobrien if (null->gpt_scheme != &g_part_null_scheme) { 61498186Sgordon gctl_error(req, "%d geom '%s'", EEXIST, pp->name); 61578344Sobrien return (EEXIST); 61678344Sobrien } 61778344Sobrien } else 61878344Sobrien null = NULL; 61998186Sgordon 62098186Sgordon if ((gpp->gpp_parms & G_PART_PARM_ENTRIES) && 62198186Sgordon (gpp->gpp_entries < scheme->gps_minent || 62298186Sgordon gpp->gpp_entries > scheme->gps_maxent)) { 62378344Sobrien gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries); 62498186Sgordon return (EINVAL); 62598186Sgordon } 62698186Sgordon 62798186Sgordon if (null == NULL) 62898186Sgordon gp = g_new_geomf(&g_part_class, "%s", pp->name); 62998186Sgordon gp->softc = kobj_create((kobj_class_t)gpp->gpp_scheme, M_GEOM, 63098186Sgordon M_WAITOK); 63198186Sgordon table = gp->softc; 63298186Sgordon table->gpt_gp = gp; 63398186Sgordon table->gpt_scheme = gpp->gpp_scheme; 63498186Sgordon table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ? 63598186Sgordon gpp->gpp_entries : scheme->gps_minent; 63678344Sobrien LIST_INIT(&table->gpt_entry); 63778344Sobrien if (null == NULL) { 63878344Sobrien cp = g_new_consumer(gp); 63998186Sgordon error = g_attach(cp, pp); 64078344Sobrien if (error == 0) 64178344Sobrien error = g_access(cp, 1, 1, 1); 64278344Sobrien if (error != 0) { 64378344Sobrien g_part_wither(gp, error); 64478344Sobrien gctl_error(req, "%d geom '%s'", error, pp->name); 64578344Sobrien return (error); 64678344Sobrien } 64778344Sobrien table->gpt_opened = 1; 64878344Sobrien } else { 64998186Sgordon cp = LIST_FIRST(&gp->consumer); 65098186Sgordon table->gpt_opened = null->gpt_opened; 65198186Sgordon table->gpt_smhead = null->gpt_smhead; 65298186Sgordon table->gpt_smtail = null->gpt_smtail; 65378344Sobrien } 65478344Sobrien 65598186Sgordon g_topology_unlock(); 65698186Sgordon 65798186Sgordon /* Make sure the provider has media. */ 65878344Sobrien if (pp->mediasize == 0 || pp->sectorsize == 0) { 65998186Sgordon error = ENODEV; 66098186Sgordon goto fail; 66198186Sgordon } 66298186Sgordon 66398186Sgordon /* Make sure we can nest and if so, determine our depth. */ 66498186Sgordon error = g_getattr("PART::isleaf", cp, &attr); 66598186Sgordon if (!error && attr) { 66698186Sgordon error = ENODEV; 66798186Sgordon goto fail; 66898186Sgordon } 66998186Sgordon error = g_getattr("PART::depth", cp, &attr); 67098186Sgordon table->gpt_depth = (!error) ? attr + 1 : 0; 67198186Sgordon 67298186Sgordon /* If we're nested, get the absolute sector offset on disk. */ 67398186Sgordon if (table->gpt_depth) { 67498186Sgordon error = g_getattr("PART::offset", cp, &attr); 67578344Sobrien if (error) 67678344Sobrien goto fail; 67778344Sobrien table->gpt_offset = attr; 67898186Sgordon } 67978344Sobrien 68078344Sobrien /* 68178344Sobrien * Synthesize a disk geometry. Some partitioning schemes 68278344Sobrien * depend on it and since some file systems need it even 68378344Sobrien * when the partitition scheme doesn't, we do it here in 68478344Sobrien * scheme-independent code. 68578344Sobrien */ 68678344Sobrien g_part_geometry(table, cp, pp->mediasize / pp->sectorsize); 68778344Sobrien 68898186Sgordon error = G_PART_CREATE(table, gpp); 68978344Sobrien if (error) 69078344Sobrien goto fail; 69198186Sgordon 69298186Sgordon g_topology_lock(); 69398186Sgordon 69498186Sgordon table->gpt_created = 1; 69598186Sgordon if (null != NULL) 69698186Sgordon kobj_delete((kobj_t)null, M_GEOM); 69798186Sgordon 69898186Sgordon /* 69978344Sobrien * Support automatic commit by filling in the gpp_geom 70078344Sobrien * parameter. 70178344Sobrien */ 70298186Sgordon gpp->gpp_parms |= G_PART_PARM_GEOM; 70378344Sobrien gpp->gpp_geom = gp; 70478344Sobrien 70578344Sobrien /* Provide feedback if so requested. */ 70678344Sobrien if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 70778344Sobrien sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 70878344Sobrien sbuf_printf(sb, "%s created\n", gp->name); 70978344Sobrien sbuf_finish(sb); 71078344Sobrien gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 71178344Sobrien sbuf_delete(sb); 71278344Sobrien } 71398186Sgordon return (0); 71498186Sgordon 71598186Sgordonfail: 71698186Sgordon g_topology_lock(); 71778344Sobrien if (null == NULL) { 71878344Sobrien g_access(cp, -1, -1, -1); 71998186Sgordon g_part_wither(gp, error); 72098186Sgordon } else { 72198186Sgordon kobj_delete((kobj_t)gp->softc, M_GEOM); 72298186Sgordon gp->softc = null; 72398186Sgordon } 72498186Sgordon gctl_error(req, "%d provider", error); 72578344Sobrien return (error); 72678344Sobrien} 72778344Sobrien 72878344Sobrienstatic int 72978344Sobrieng_part_ctl_delete(struct gctl_req *req, struct g_part_parms *gpp) 73078344Sobrien{ 73178344Sobrien char buf[32]; 73278344Sobrien struct g_geom *gp; 73378344Sobrien struct g_provider *pp; 73478344Sobrien struct g_part_entry *entry; 73578344Sobrien struct g_part_table *table; 73678344Sobrien struct sbuf *sb; 73778344Sobrien 73878344Sobrien gp = gpp->gpp_geom; 73978344Sobrien G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 74078344Sobrien g_topology_assert(); 74178344Sobrien 74278344Sobrien table = gp->softc; 74378344Sobrien 74498186Sgordon LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 74578344Sobrien if (entry->gpe_deleted || entry->gpe_internal) 74678344Sobrien continue; 74778344Sobrien if (entry->gpe_index == gpp->gpp_index) 74878344Sobrien break; 74978344Sobrien } 75078344Sobrien if (entry == NULL) { 75178344Sobrien gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index); 75278344Sobrien return (ENOENT); 75398186Sgordon } 75498186Sgordon 75598186Sgordon pp = entry->gpe_pp; 75678344Sobrien if (pp != NULL) { 75778344Sobrien if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) { 75878344Sobrien gctl_error(req, "%d", EBUSY); 75978344Sobrien return (EBUSY); 76078344Sobrien } 76178344Sobrien 76278344Sobrien pp->private = NULL; 76378344Sobrien entry->gpe_pp = NULL; 76478344Sobrien } 76598186Sgordon 76698186Sgordon if (entry->gpe_created) { 76798186Sgordon LIST_REMOVE(entry, gpe_entry); 76898186Sgordon g_free(entry); 76998186Sgordon } else { 77098186Sgordon entry->gpe_modified = 0; 77198186Sgordon entry->gpe_deleted = 1; 77278344Sobrien } 77378344Sobrien 77478344Sobrien if (pp != NULL) 77578344Sobrien g_wither_provider(pp, ENXIO); 77698186Sgordon 77798186Sgordon /* Provide feedback if so requested. */ 77898186Sgordon if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 77978344Sobrien sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 78098186Sgordon sbuf_printf(sb, "%s%s deleted\n", gp->name, 78198186Sgordon G_PART_NAME(table, entry, buf, sizeof(buf))); 78298186Sgordon sbuf_finish(sb); 78398186Sgordon gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 78498186Sgordon sbuf_delete(sb); 78598186Sgordon } 78698186Sgordon return (0); 78798186Sgordon} 78878344Sobrien 78978344Sobrienstatic int 79078344Sobrieng_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp) 79178344Sobrien{ 79278344Sobrien struct g_geom *gp; 79378344Sobrien struct g_part_entry *entry; 79478344Sobrien struct g_part_table *null, *table; 79578344Sobrien struct sbuf *sb; 79678344Sobrien int error; 79778344Sobrien 79878344Sobrien gp = gpp->gpp_geom; 79978344Sobrien G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 80078344Sobrien g_topology_assert(); 80178344Sobrien 80278344Sobrien table = gp->softc; 80398186Sgordon LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 80498186Sgordon if (entry->gpe_deleted || entry->gpe_internal) 80598186Sgordon continue; 80698186Sgordon gctl_error(req, "%d", EBUSY); 80798186Sgordon return (EBUSY); 80898186Sgordon } 80998186Sgordon 81098186Sgordon error = G_PART_DESTROY(table, gpp); 81198186Sgordon if (error) { 81298186Sgordon gctl_error(req, "%d", error); 81398186Sgordon return (error); 81478344Sobrien } 81598186Sgordon 81678344Sobrien gp->softc = kobj_create((kobj_class_t)&g_part_null_scheme, M_GEOM, 81778344Sobrien M_WAITOK); 818101850Sgordon null = gp->softc; 819101850Sgordon null->gpt_gp = gp; 820101850Sgordon null->gpt_scheme = &g_part_null_scheme; 821103018Sgordon LIST_INIT(&null->gpt_entry); 822101850Sgordon null->gpt_depth = table->gpt_depth; 823101850Sgordon null->gpt_opened = table->gpt_opened; 824101850Sgordon null->gpt_smhead = table->gpt_smhead; 825101850Sgordon null->gpt_smtail = table->gpt_smtail; 826101850Sgordon 827101850Sgordon while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) { 828101850Sgordon LIST_REMOVE(entry, gpe_entry); 829101850Sgordon g_free(entry); 830101850Sgordon } 831101850Sgordon kobj_delete((kobj_t)table, M_GEOM); 832101850Sgordon 83378344Sobrien /* Provide feedback if so requested. */ 83478344Sobrien if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 83578344Sobrien sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 83678344Sobrien sbuf_printf(sb, "%s destroyed\n", gp->name); 83778344Sobrien sbuf_finish(sb); 83878344Sobrien gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 83978344Sobrien sbuf_delete(sb); 84078344Sobrien } 84178344Sobrien return (0); 842106643Sgordon} 84378344Sobrien 84478344Sobrienstatic int 84578344Sobrieng_part_ctl_modify(struct gctl_req *req, struct g_part_parms *gpp) 84678344Sobrien{ 84778344Sobrien char buf[32]; 84878344Sobrien struct g_geom *gp; 84978344Sobrien struct g_part_entry *entry; 85078344Sobrien struct g_part_table *table; 85178344Sobrien struct sbuf *sb; 85278344Sobrien int error; 85378344Sobrien 85478344Sobrien gp = gpp->gpp_geom; 85578344Sobrien G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 85678344Sobrien g_topology_assert(); 85778344Sobrien 85878344Sobrien table = gp->softc; 85978344Sobrien 86078344Sobrien LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 86178344Sobrien if (entry->gpe_deleted || entry->gpe_internal) 862106643Sgordon continue; 863106643Sgordon if (entry->gpe_index == gpp->gpp_index) 864106643Sgordon break; 865106643Sgordon } 86678344Sobrien if (entry == NULL) { 86778344Sobrien gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index); 86878344Sobrien return (ENOENT); 86978344Sobrien } 87078344Sobrien 87178344Sobrien error = G_PART_MODIFY(table, entry, gpp); 87278344Sobrien if (error) { 87378344Sobrien gctl_error(req, "%d", error); 87478344Sobrien return (error); 875106643Sgordon } 876106643Sgordon 877106643Sgordon if (!entry->gpe_created) 878106643Sgordon entry->gpe_modified = 1; 87978344Sobrien 88098186Sgordon /* Provide feedback if so requested. */ 88198186Sgordon if (gpp->gpp_parms & G_PART_PARM_OUTPUT) { 88298186Sgordon sb = sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND); 88398186Sgordon sbuf_printf(sb, "%s%s modified\n", gp->name, 88498186Sgordon G_PART_NAME(table, entry, buf, sizeof(buf))); 88598186Sgordon sbuf_finish(sb); 88698186Sgordon gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1); 887106643Sgordon sbuf_delete(sb); 888106643Sgordon } 889106643Sgordon return (0); 890106643Sgordon} 89198186Sgordon 89298186Sgordonstatic int 89398186Sgordong_part_ctl_move(struct gctl_req *req, struct g_part_parms *gpp) 89498186Sgordon{ 895106643Sgordon gctl_error(req, "%d verb 'move'", ENOSYS); 89698186Sgordon return (ENOSYS); 89798186Sgordon} 89898186Sgordon 89998186Sgordonstatic int 90098186Sgordong_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp) 90198186Sgordon{ 90298186Sgordon gctl_error(req, "%d verb 'recover'", ENOSYS); 903106700Sgordon return (ENOSYS); 904106700Sgordon} 905106700Sgordon 906106643Sgordonstatic int 90798186Sgordong_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp) 90898186Sgordon{ 90998186Sgordon gctl_error(req, "%d verb 'resize'", ENOSYS); 91098186Sgordon return (ENOSYS); 91198186Sgordon} 91298186Sgordon 91398186Sgordonstatic int 91498186Sgordong_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp) 91598186Sgordon{ 91698186Sgordon struct g_consumer *cp; 91798186Sgordon struct g_provider *pp; 91898186Sgordon struct g_geom *gp; 91998186Sgordon struct g_part_entry *entry, *tmp; 92098186Sgordon struct g_part_table *table; 92198186Sgordon int error, reprobe; 92298186Sgordon 92398186Sgordon gp = gpp->gpp_geom; 92498186Sgordon G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name)); 92598186Sgordon g_topology_assert(); 92698186Sgordon 92798186Sgordon table = gp->softc; 92898186Sgordon if (!table->gpt_opened) { 92998186Sgordon gctl_error(req, "%d", EPERM); 93098186Sgordon return (EPERM); 93198186Sgordon } 93298186Sgordon 93398186Sgordon cp = LIST_FIRST(&gp->consumer); 93498186Sgordon LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) { 93598186Sgordon entry->gpe_modified = 0; 93698186Sgordon if (entry->gpe_created) { 93798186Sgordon pp = entry->gpe_pp; 93898186Sgordon if (pp != NULL) { 93998186Sgordon pp->private = NULL; 94098186Sgordon entry->gpe_pp = NULL; 94198186Sgordon g_wither_provider(pp, ENXIO); 94298186Sgordon } 94398186Sgordon entry->gpe_deleted = 1; 94498186Sgordon } 94598186Sgordon if (entry->gpe_deleted) { 94698186Sgordon LIST_REMOVE(entry, gpe_entry); 94798186Sgordon g_free(entry); 94898186Sgordon } 94998186Sgordon } 95098186Sgordon 95198186Sgordon g_topology_unlock(); 95298186Sgordon 95398186Sgordon reprobe = (table->gpt_scheme == &g_part_null_scheme || 95498186Sgordon table->gpt_created) ? 1 : 0; 95598186Sgordon 95698186Sgordon if (reprobe) { 95798186Sgordon if (!LIST_EMPTY(&table->gpt_entry)) { 95898186Sgordon error = EBUSY; 95998186Sgordon goto fail; 96098186Sgordon } 96198186Sgordon error = g_part_probe(gp, cp, table->gpt_depth); 96298186Sgordon if (error) { 96398186Sgordon g_topology_lock(); 96498186Sgordon g_access(cp, -1, -1, -1); 96598186Sgordon g_part_wither(gp, error); 96698186Sgordon return (0); 96798186Sgordon } 96898186Sgordon table = gp->softc; 96998186Sgordon } 97098186Sgordon 97198186Sgordon error = G_PART_READ(table, cp); 97298186Sgordon if (error) 97398186Sgordon goto fail; 97498186Sgordon 97598186Sgordon g_topology_lock(); 97698186Sgordon 97798186Sgordon LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 97898186Sgordon if (!entry->gpe_internal) 97998186Sgordon g_part_new_provider(gp, table, entry); 98098186Sgordon } 98198186Sgordon 98298186Sgordon table->gpt_opened = 0; 98398186Sgordon g_access(cp, -1, -1, -1); 98498186Sgordon return (0); 98598186Sgordon 98698186Sgordonfail: 98798186Sgordon g_topology_lock(); 98898186Sgordon gctl_error(req, "%d", error); 98998186Sgordon return (error); 99098186Sgordon} 99198186Sgordon 99298186Sgordonstatic void 99398186Sgordong_part_wither(struct g_geom *gp, int error) 99498186Sgordon{ 99598186Sgordon struct g_part_entry *entry; 996 struct g_part_table *table; 997 998 table = gp->softc; 999 if (table != NULL) { 1000 while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) { 1001 LIST_REMOVE(entry, gpe_entry); 1002 g_free(entry); 1003 } 1004 if (gp->softc != NULL) { 1005 kobj_delete((kobj_t)gp->softc, M_GEOM); 1006 gp->softc = NULL; 1007 } 1008 } 1009 g_wither_geom(gp, error); 1010} 1011 1012/* 1013 * Class methods. 1014 */ 1015 1016static void 1017g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb) 1018{ 1019 struct g_part_parms gpp; 1020 struct g_part_table *table; 1021 struct gctl_req_arg *ap; 1022 const char *p; 1023 enum g_part_ctl ctlreq; 1024 unsigned int i, mparms, oparms, parm; 1025 int auto_commit, close_on_error; 1026 int error, modifies; 1027 1028 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb)); 1029 g_topology_assert(); 1030 1031 ctlreq = G_PART_CTL_NONE; 1032 modifies = 1; 1033 mparms = 0; 1034 oparms = G_PART_PARM_FLAGS | G_PART_PARM_OUTPUT | G_PART_PARM_VERSION; 1035 switch (*verb) { 1036 case 'a': 1037 if (!strcmp(verb, "add")) { 1038 ctlreq = G_PART_CTL_ADD; 1039 mparms |= G_PART_PARM_GEOM | G_PART_PARM_SIZE | 1040 G_PART_PARM_START | G_PART_PARM_TYPE; 1041 oparms |= G_PART_PARM_INDEX | G_PART_PARM_LABEL; 1042 } 1043 break; 1044 case 'c': 1045 if (!strcmp(verb, "commit")) { 1046 ctlreq = G_PART_CTL_COMMIT; 1047 mparms |= G_PART_PARM_GEOM; 1048 modifies = 0; 1049 } else if (!strcmp(verb, "create")) { 1050 ctlreq = G_PART_CTL_CREATE; 1051 mparms |= G_PART_PARM_PROVIDER | G_PART_PARM_SCHEME; 1052 oparms |= G_PART_PARM_ENTRIES; 1053 } 1054 break; 1055 case 'd': 1056 if (!strcmp(verb, "delete")) { 1057 ctlreq = G_PART_CTL_DELETE; 1058 mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX; 1059 } else if (!strcmp(verb, "destroy")) { 1060 ctlreq = G_PART_CTL_DESTROY; 1061 mparms |= G_PART_PARM_GEOM; 1062 } 1063 break; 1064 case 'm': 1065 if (!strcmp(verb, "modify")) { 1066 ctlreq = G_PART_CTL_MODIFY; 1067 mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX; 1068 oparms |= G_PART_PARM_LABEL | G_PART_PARM_TYPE; 1069 } else if (!strcmp(verb, "move")) { 1070 ctlreq = G_PART_CTL_MOVE; 1071 mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX; 1072 } 1073 break; 1074 case 'r': 1075 if (!strcmp(verb, "recover")) { 1076 ctlreq = G_PART_CTL_RECOVER; 1077 mparms |= G_PART_PARM_GEOM; 1078 } else if (!strcmp(verb, "resize")) { 1079 ctlreq = G_PART_CTL_RESIZE; 1080 mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX; 1081 } 1082 break; 1083 case 'u': 1084 if (!strcmp(verb, "undo")) { 1085 ctlreq = G_PART_CTL_UNDO; 1086 mparms |= G_PART_PARM_GEOM; 1087 modifies = 0; 1088 } 1089 break; 1090 } 1091 if (ctlreq == G_PART_CTL_NONE) { 1092 gctl_error(req, "%d verb '%s'", EINVAL, verb); 1093 return; 1094 } 1095 1096 bzero(&gpp, sizeof(gpp)); 1097 for (i = 0; i < req->narg; i++) { 1098 ap = &req->arg[i]; 1099 parm = 0; 1100 switch (ap->name[0]) { 1101 case 'c': 1102 if (!strcmp(ap->name, "class")) 1103 continue; 1104 break; 1105 case 'e': 1106 if (!strcmp(ap->name, "entries")) 1107 parm = G_PART_PARM_ENTRIES; 1108 break; 1109 case 'f': 1110 if (!strcmp(ap->name, "flags")) 1111 parm = G_PART_PARM_FLAGS; 1112 break; 1113 case 'g': 1114 if (!strcmp(ap->name, "geom")) 1115 parm = G_PART_PARM_GEOM; 1116 break; 1117 case 'i': 1118 if (!strcmp(ap->name, "index")) 1119 parm = G_PART_PARM_INDEX; 1120 break; 1121 case 'l': 1122 if (!strcmp(ap->name, "label")) 1123 parm = G_PART_PARM_LABEL; 1124 break; 1125 case 'o': 1126 if (!strcmp(ap->name, "output")) 1127 parm = G_PART_PARM_OUTPUT; 1128 break; 1129 case 'p': 1130 if (!strcmp(ap->name, "provider")) 1131 parm = G_PART_PARM_PROVIDER; 1132 break; 1133 case 's': 1134 if (!strcmp(ap->name, "scheme")) 1135 parm = G_PART_PARM_SCHEME; 1136 else if (!strcmp(ap->name, "size")) 1137 parm = G_PART_PARM_SIZE; 1138 else if (!strcmp(ap->name, "start")) 1139 parm = G_PART_PARM_START; 1140 break; 1141 case 't': 1142 if (!strcmp(ap->name, "type")) 1143 parm = G_PART_PARM_TYPE; 1144 break; 1145 case 'v': 1146 if (!strcmp(ap->name, "verb")) 1147 continue; 1148 else if (!strcmp(ap->name, "version")) 1149 parm = G_PART_PARM_VERSION; 1150 break; 1151 } 1152 if ((parm & (mparms | oparms)) == 0) { 1153 gctl_error(req, "%d param '%s'", EINVAL, ap->name); 1154 return; 1155 } 1156 p = gctl_get_asciiparam(req, ap->name); 1157 if (p == NULL) { 1158 gctl_error(req, "%d param '%s'", ENOATTR, ap->name); 1159 return; 1160 } 1161 switch (parm) { 1162 case G_PART_PARM_ENTRIES: 1163 error = g_part_parm_uint(p, &gpp.gpp_entries); 1164 break; 1165 case G_PART_PARM_FLAGS: 1166 if (p[0] == '\0') 1167 continue; 1168 error = g_part_parm_str(p, &gpp.gpp_flags); 1169 break; 1170 case G_PART_PARM_GEOM: 1171 error = g_part_parm_geom(p, &gpp.gpp_geom); 1172 break; 1173 case G_PART_PARM_INDEX: 1174 error = g_part_parm_uint(p, &gpp.gpp_index); 1175 break; 1176 case G_PART_PARM_LABEL: 1177 /* An empty label is always valid. */ 1178 gpp.gpp_label = p; 1179 error = 0; 1180 break; 1181 case G_PART_PARM_OUTPUT: 1182 error = 0; /* Write-only parameter */ 1183 break; 1184 case G_PART_PARM_PROVIDER: 1185 error = g_part_parm_provider(p, &gpp.gpp_provider); 1186 break; 1187 case G_PART_PARM_SCHEME: 1188 error = g_part_parm_scheme(p, &gpp.gpp_scheme); 1189 break; 1190 case G_PART_PARM_SIZE: 1191 error = g_part_parm_quad(p, &gpp.gpp_size); 1192 break; 1193 case G_PART_PARM_START: 1194 error = g_part_parm_quad(p, &gpp.gpp_start); 1195 break; 1196 case G_PART_PARM_TYPE: 1197 error = g_part_parm_str(p, &gpp.gpp_type); 1198 break; 1199 case G_PART_PARM_VERSION: 1200 error = g_part_parm_uint(p, &gpp.gpp_version); 1201 break; 1202 default: 1203 error = EDOOFUS; 1204 break; 1205 } 1206 if (error) { 1207 gctl_error(req, "%d %s '%s'", error, ap->name, p); 1208 return; 1209 } 1210 gpp.gpp_parms |= parm; 1211 } 1212 if ((gpp.gpp_parms & mparms) != mparms) { 1213 parm = mparms - (gpp.gpp_parms & mparms); 1214 gctl_error(req, "%d param '%x'", ENOATTR, parm); 1215 return; 1216 } 1217 1218 /* Obtain permissions if possible/necessary. */ 1219 close_on_error = 0; 1220 table = NULL; /* Suppress uninit. warning. */ 1221 if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) { 1222 table = gpp.gpp_geom->softc; 1223 if (table != NULL && !table->gpt_opened) { 1224 error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer), 1225 1, 1, 1); 1226 if (error) { 1227 gctl_error(req, "%d geom '%s'", error, 1228 gpp.gpp_geom->name); 1229 return; 1230 } 1231 table->gpt_opened = 1; 1232 close_on_error = 1; 1233 } 1234 } 1235 1236 error = EDOOFUS; /* Prevent bogus uninit. warning. */ 1237 switch (ctlreq) { 1238 case G_PART_CTL_NONE: 1239 panic("%s", __func__); 1240 case G_PART_CTL_ADD: 1241 error = g_part_ctl_add(req, &gpp); 1242 break; 1243 case G_PART_CTL_COMMIT: 1244 error = g_part_ctl_commit(req, &gpp); 1245 break; 1246 case G_PART_CTL_CREATE: 1247 error = g_part_ctl_create(req, &gpp); 1248 break; 1249 case G_PART_CTL_DELETE: 1250 error = g_part_ctl_delete(req, &gpp); 1251 break; 1252 case G_PART_CTL_DESTROY: 1253 error = g_part_ctl_destroy(req, &gpp); 1254 break; 1255 case G_PART_CTL_MODIFY: 1256 error = g_part_ctl_modify(req, &gpp); 1257 break; 1258 case G_PART_CTL_MOVE: 1259 error = g_part_ctl_move(req, &gpp); 1260 break; 1261 case G_PART_CTL_RECOVER: 1262 error = g_part_ctl_recover(req, &gpp); 1263 break; 1264 case G_PART_CTL_RESIZE: 1265 error = g_part_ctl_resize(req, &gpp); 1266 break; 1267 case G_PART_CTL_UNDO: 1268 error = g_part_ctl_undo(req, &gpp); 1269 break; 1270 } 1271 1272 /* Implement automatic commit. */ 1273 if (!error) { 1274 auto_commit = (modifies && 1275 (gpp.gpp_parms & G_PART_PARM_FLAGS) && 1276 strchr(gpp.gpp_flags, 'C') != NULL) ? 1 : 0; 1277 if (auto_commit) { 1278 KASSERT(gpp.gpp_parms & G_PART_PARM_GEOM, (__func__)); 1279 error = g_part_ctl_commit(req, &gpp); 1280 } 1281 } 1282 1283 if (error && close_on_error) { 1284 g_access(LIST_FIRST(&gpp.gpp_geom->consumer), -1, -1, -1); 1285 table->gpt_opened = 0; 1286 } 1287} 1288 1289static int 1290g_part_destroy_geom(struct gctl_req *req, struct g_class *mp, 1291 struct g_geom *gp) 1292{ 1293 1294 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name)); 1295 g_topology_assert(); 1296 1297 g_part_wither(gp, EINVAL); 1298 return (0); 1299} 1300 1301static struct g_geom * 1302g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 1303{ 1304 struct g_consumer *cp; 1305 struct g_geom *gp; 1306 struct g_part_entry *entry; 1307 struct g_part_table *table; 1308 int attr, depth; 1309 int error; 1310 1311 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name)); 1312 g_topology_assert(); 1313 1314 /* 1315 * Create a GEOM with consumer and hook it up to the provider. 1316 * With that we become part of the topology. Optain read access 1317 * to the provider. 1318 */ 1319 gp = g_new_geomf(mp, "%s", pp->name); 1320 cp = g_new_consumer(gp); 1321 error = g_attach(cp, pp); 1322 if (error == 0) 1323 error = g_access(cp, 1, 0, 0); 1324 if (error != 0) { 1325 g_part_wither(gp, error); 1326 return (NULL); 1327 } 1328 1329 g_topology_unlock(); 1330 1331 /* 1332 * Short-circuit the whole probing galore when there's no 1333 * media present. 1334 */ 1335 if (pp->mediasize == 0 || pp->sectorsize == 0) { 1336 error = ENODEV; 1337 goto fail; 1338 } 1339 1340 /* Make sure we can nest and if so, determine our depth. */ 1341 error = g_getattr("PART::isleaf", cp, &attr); 1342 if (!error && attr) { 1343 error = ENODEV; 1344 goto fail; 1345 } 1346 error = g_getattr("PART::depth", cp, &attr); 1347 depth = (!error) ? attr + 1 : 0; 1348 1349 error = g_part_probe(gp, cp, depth); 1350 if (error) 1351 goto fail; 1352 1353 table = gp->softc; 1354 1355 /* If we're nested, get the absolute sector offset on disk. */ 1356 if (table->gpt_depth) { 1357 error = g_getattr("PART::offset", cp, &attr); 1358 if (error) 1359 goto fail; 1360 table->gpt_offset = attr; 1361 } 1362 1363 /* 1364 * Synthesize a disk geometry. Some partitioning schemes 1365 * depend on it and since some file systems need it even 1366 * when the partitition scheme doesn't, we do it here in 1367 * scheme-independent code. 1368 */ 1369 g_part_geometry(table, cp, pp->mediasize / pp->sectorsize); 1370 1371 error = G_PART_READ(table, cp); 1372 if (error) 1373 goto fail; 1374 1375 g_topology_lock(); 1376 LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) { 1377 if (!entry->gpe_internal) 1378 g_part_new_provider(gp, table, entry); 1379 } 1380 1381 g_access(cp, -1, 0, 0); 1382 return (gp); 1383 1384 fail: 1385 g_topology_lock(); 1386 g_access(cp, -1, 0, 0); 1387 g_part_wither(gp, error); 1388 return (NULL); 1389} 1390 1391/* 1392 * Geom methods. 1393 */ 1394 1395static int 1396g_part_access(struct g_provider *pp, int dr, int dw, int de) 1397{ 1398 struct g_consumer *cp; 1399 1400 G_PART_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr, 1401 dw, de)); 1402 1403 cp = LIST_FIRST(&pp->geom->consumer); 1404 1405 /* We always gain write-exclusive access. */ 1406 return (g_access(cp, dr, dw, dw + de)); 1407} 1408 1409static void 1410g_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp, 1411 struct g_consumer *cp, struct g_provider *pp) 1412{ 1413 char buf[64]; 1414 struct g_part_entry *entry; 1415 struct g_part_table *table; 1416 1417 KASSERT(sb != NULL && gp != NULL, (__func__)); 1418 table = gp->softc; 1419 1420 if (indent == NULL) { 1421 KASSERT(cp == NULL && pp != NULL, (__func__)); 1422 entry = pp->private; 1423 if (entry == NULL) 1424 return; 1425 sbuf_printf(sb, " i %u o %ju ty %s", entry->gpe_index, 1426 (uintmax_t)entry->gpe_offset, 1427 G_PART_TYPE(table, entry, buf, sizeof(buf))); 1428 } else if (cp != NULL) { /* Consumer configuration. */ 1429 KASSERT(pp == NULL, (__func__)); 1430 /* none */ 1431 } else if (pp != NULL) { /* Provider configuration. */ 1432 entry = pp->private; 1433 if (entry == NULL) 1434 return; 1435 sbuf_printf(sb, "%s<index>%u</index>\n", indent, 1436 entry->gpe_index); 1437 sbuf_printf(sb, "%s<type>%s</type>\n", indent, 1438 G_PART_TYPE(table, entry, buf, sizeof(buf))); 1439 sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent, 1440 (uintmax_t)entry->gpe_offset); 1441 sbuf_printf(sb, "%s<length>%ju</length>\n", indent, 1442 (uintmax_t)pp->mediasize); 1443 G_PART_DUMPCONF(table, entry, sb, indent); 1444 } else { /* Geom configuration. */ 1445 sbuf_printf(sb, "%s<scheme>%s</scheme>\n", indent, 1446 table->gpt_scheme->name); 1447 sbuf_printf(sb, "%s<entries>%u</entries>\n", indent, 1448 table->gpt_entries); 1449 sbuf_printf(sb, "%s<first>%ju</first>\n", indent, 1450 (uintmax_t)table->gpt_first); 1451 sbuf_printf(sb, "%s<last>%ju</last>\n", indent, 1452 (uintmax_t)table->gpt_last); 1453 sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", indent, 1454 table->gpt_sectors); 1455 sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent, 1456 table->gpt_heads); 1457 G_PART_DUMPCONF(table, NULL, sb, indent); 1458 } 1459} 1460 1461static void 1462g_part_orphan(struct g_consumer *cp) 1463{ 1464 struct g_provider *pp; 1465 1466 pp = cp->provider; 1467 KASSERT(pp != NULL, (__func__)); 1468 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name)); 1469 g_topology_assert(); 1470 1471 KASSERT(pp->error != 0, (__func__)); 1472 g_part_wither(cp->geom, pp->error); 1473} 1474 1475static void 1476g_part_spoiled(struct g_consumer *cp) 1477{ 1478 1479 G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name)); 1480 g_topology_assert(); 1481 1482 g_part_wither(cp->geom, ENXIO); 1483} 1484 1485static void 1486g_part_start(struct bio *bp) 1487{ 1488 struct bio *bp2; 1489 struct g_consumer *cp; 1490 struct g_geom *gp; 1491 struct g_part_entry *entry; 1492 struct g_part_table *table; 1493 struct g_kerneldump *gkd; 1494 struct g_provider *pp; 1495 1496 pp = bp->bio_to; 1497 gp = pp->geom; 1498 table = gp->softc; 1499 cp = LIST_FIRST(&gp->consumer); 1500 1501 G_PART_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd, 1502 pp->name)); 1503 1504 entry = pp->private; 1505 if (entry == NULL) { 1506 g_io_deliver(bp, ENXIO); 1507 return; 1508 } 1509 1510 switch(bp->bio_cmd) { 1511 case BIO_DELETE: 1512 case BIO_READ: 1513 case BIO_WRITE: 1514 if (bp->bio_offset >= pp->mediasize) { 1515 g_io_deliver(bp, EIO); 1516 return; 1517 } 1518 bp2 = g_clone_bio(bp); 1519 if (bp2 == NULL) { 1520 g_io_deliver(bp, ENOMEM); 1521 return; 1522 } 1523 if (bp2->bio_offset + bp2->bio_length > pp->mediasize) 1524 bp2->bio_length = pp->mediasize - bp2->bio_offset; 1525 bp2->bio_done = g_std_done; 1526 bp2->bio_offset += entry->gpe_offset; 1527 g_io_request(bp2, cp); 1528 return; 1529 case BIO_FLUSH: 1530 break; 1531 case BIO_GETATTR: 1532 if (g_handleattr_int(bp, "GEOM::fwheads", table->gpt_heads)) 1533 return; 1534 if (g_handleattr_int(bp, "GEOM::fwsectors", table->gpt_sectors)) 1535 return; 1536 if (g_handleattr_int(bp, "PART::isleaf", table->gpt_isleaf)) 1537 return; 1538 if (g_handleattr_int(bp, "PART::depth", table->gpt_depth)) 1539 return; 1540 if (g_handleattr_int(bp, "PART::offset", 1541 table->gpt_offset + entry->gpe_start)) 1542 return; 1543 if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) { 1544 /* 1545 * Check that the partition is suitable for kernel 1546 * dumps. Typically only swap partitions should be 1547 * used. 1548 */ 1549 if (!G_PART_DUMPTO(table, entry)) { 1550 g_io_deliver(bp, ENXIO); 1551 return; 1552 } 1553 gkd = (struct g_kerneldump *)bp->bio_data; 1554 if (gkd->offset >= pp->mediasize) { 1555 g_io_deliver(bp, EIO); 1556 return; 1557 } 1558 if (gkd->offset + gkd->length > pp->mediasize) 1559 gkd->length = pp->mediasize - gkd->offset; 1560 gkd->offset += entry->gpe_offset; 1561 } 1562 break; 1563 default: 1564 g_io_deliver(bp, EOPNOTSUPP); 1565 return; 1566 } 1567 1568 bp2 = g_clone_bio(bp); 1569 if (bp2 == NULL) { 1570 g_io_deliver(bp, ENOMEM); 1571 return; 1572 } 1573 bp2->bio_done = g_std_done; 1574 g_io_request(bp2, cp); 1575} 1576 1577static void 1578g_part_init(struct g_class *mp) 1579{ 1580 1581 TAILQ_INSERT_TAIL(&g_part_schemes, &g_part_null_scheme, scheme_list); 1582} 1583 1584static void 1585g_part_fini(struct g_class *mp) 1586{ 1587 1588 TAILQ_REMOVE(&g_part_schemes, &g_part_null_scheme, scheme_list); 1589} 1590 1591static void 1592g_part_unload_event(void *arg, int flag) 1593{ 1594 struct g_consumer *cp; 1595 struct g_geom *gp; 1596 struct g_provider *pp; 1597 struct g_part_scheme *scheme; 1598 struct g_part_table *table; 1599 uintptr_t *xchg; 1600 int acc, error; 1601 1602 if (flag == EV_CANCEL) 1603 return; 1604 1605 xchg = arg; 1606 error = 0; 1607 scheme = (void *)(*xchg); 1608 1609 g_topology_assert(); 1610 1611 LIST_FOREACH(gp, &g_part_class.geom, geom) { 1612 table = gp->softc; 1613 if (table->gpt_scheme != scheme) 1614 continue; 1615 1616 acc = 0; 1617 LIST_FOREACH(pp, &gp->provider, provider) 1618 acc += pp->acr + pp->acw + pp->ace; 1619 LIST_FOREACH(cp, &gp->consumer, consumer) 1620 acc += cp->acr + cp->acw + cp->ace; 1621 1622 if (!acc) 1623 g_part_wither(gp, ENOSYS); 1624 else 1625 error = EBUSY; 1626 } 1627 1628 if (!error) 1629 TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list); 1630 1631 *xchg = error; 1632} 1633 1634int 1635g_part_modevent(module_t mod, int type, struct g_part_scheme *scheme) 1636{ 1637 uintptr_t arg; 1638 int error; 1639 1640 switch (type) { 1641 case MOD_LOAD: 1642 TAILQ_INSERT_TAIL(&g_part_schemes, scheme, scheme_list); 1643 1644 error = g_retaste(&g_part_class); 1645 if (error) 1646 TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list); 1647 break; 1648 case MOD_UNLOAD: 1649 arg = (uintptr_t)scheme; 1650 error = g_waitfor_event(g_part_unload_event, &arg, M_WAITOK, 1651 NULL); 1652 if (!error) 1653 error = (arg == (uintptr_t)scheme) ? EDOOFUS : arg; 1654 break; 1655 default: 1656 error = EOPNOTSUPP; 1657 break; 1658 } 1659 1660 return (error); 1661} 1662