]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - sys/geom/part/g_part.c
MFC r362623:
[FreeBSD/stable/8.git] / sys / geom / part / g_part.c
1 /*-
2  * Copyright (c) 2002, 2005-2009 Marcel Moolenaar
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/bio.h>
32 #include <sys/diskmbr.h>
33 #include <sys/endian.h>
34 #include <sys/kernel.h>
35 #include <sys/kobj.h>
36 #include <sys/limits.h>
37 #include <sys/lock.h>
38 #include <sys/malloc.h>
39 #include <sys/mutex.h>
40 #include <sys/queue.h>
41 #include <sys/sbuf.h>
42 #include <sys/sysctl.h>
43 #include <sys/systm.h>
44 #include <sys/uuid.h>
45 #include <geom/geom.h>
46 #include <geom/geom_ctl.h>
47 #include <geom/geom_int.h>
48 #include <geom/part/g_part.h>
49
50 #include "g_part_if.h"
51
52 #ifndef _PATH_DEV
53 #define _PATH_DEV "/dev/"
54 #endif
55
56 static kobj_method_t g_part_null_methods[] = {
57         { 0, 0 }
58 };
59
60 static struct g_part_scheme g_part_null_scheme = {
61         "(none)",
62         g_part_null_methods,
63         sizeof(struct g_part_table),
64 };
65
66 TAILQ_HEAD(, g_part_scheme) g_part_schemes =
67     TAILQ_HEAD_INITIALIZER(g_part_schemes);
68
69 struct g_part_alias_list {
70         const char *lexeme;
71         enum g_part_alias alias;
72 } g_part_alias_list[G_PART_ALIAS_COUNT] = {
73         { "apple-boot", G_PART_ALIAS_APPLE_BOOT },
74         { "apple-hfs", G_PART_ALIAS_APPLE_HFS },
75         { "apple-label", G_PART_ALIAS_APPLE_LABEL },
76         { "apple-raid", G_PART_ALIAS_APPLE_RAID },
77         { "apple-raid-offline", G_PART_ALIAS_APPLE_RAID_OFFLINE },
78         { "apple-tv-recovery", G_PART_ALIAS_APPLE_TV_RECOVERY },
79         { "apple-ufs", G_PART_ALIAS_APPLE_UFS },
80         { "bios-boot", G_PART_ALIAS_BIOS_BOOT },
81         { "ebr", G_PART_ALIAS_EBR },
82         { "efi", G_PART_ALIAS_EFI },
83         { "fat32", G_PART_ALIAS_MS_FAT32 },
84         { "freebsd", G_PART_ALIAS_FREEBSD },
85         { "freebsd-boot", G_PART_ALIAS_FREEBSD_BOOT },
86         { "freebsd-swap", G_PART_ALIAS_FREEBSD_SWAP },
87         { "freebsd-ufs", G_PART_ALIAS_FREEBSD_UFS },
88         { "freebsd-vinum", G_PART_ALIAS_FREEBSD_VINUM },
89         { "freebsd-zfs", G_PART_ALIAS_FREEBSD_ZFS },
90         { "linux-data", G_PART_ALIAS_LINUX_DATA },
91         { "linux-lvm", G_PART_ALIAS_LINUX_LVM },
92         { "linux-raid", G_PART_ALIAS_LINUX_RAID },
93         { "linux-swap", G_PART_ALIAS_LINUX_SWAP },
94         { "mbr", G_PART_ALIAS_MBR },
95         { "ms-basic-data", G_PART_ALIAS_MS_BASIC_DATA },
96         { "ms-ldm-data", G_PART_ALIAS_MS_LDM_DATA },
97         { "ms-ldm-metadata", G_PART_ALIAS_MS_LDM_METADATA },
98         { "ms-reserved", G_PART_ALIAS_MS_RESERVED },
99         { "ntfs", G_PART_ALIAS_MS_NTFS },
100         { "netbsd-ccd", G_PART_ALIAS_NETBSD_CCD },
101         { "netbsd-cgd", G_PART_ALIAS_NETBSD_CGD },
102         { "netbsd-ffs", G_PART_ALIAS_NETBSD_FFS },
103         { "netbsd-lfs", G_PART_ALIAS_NETBSD_LFS },
104         { "netbsd-raid", G_PART_ALIAS_NETBSD_RAID },
105         { "netbsd-swap", G_PART_ALIAS_NETBSD_SWAP },
106         { "vmware-vmfs", G_PART_ALIAS_VMFS },
107         { "vmware-vmkdiag", G_PART_ALIAS_VMKDIAG },
108         { "vmware-reserved", G_PART_ALIAS_VMRESERVED },
109         { "vmware-vsanhdr", G_PART_ALIAS_VMVSANHDR },
110 };
111
112 SYSCTL_DECL(_kern_geom);
113 SYSCTL_NODE(_kern_geom, OID_AUTO, part, CTLFLAG_RW, 0,
114     "GEOM_PART stuff");
115 static u_int check_integrity = 0;
116 TUNABLE_INT("kern.geom.part.check_integrity", &check_integrity);
117 SYSCTL_UINT(_kern_geom_part, OID_AUTO, check_integrity,
118     CTLFLAG_RW | CTLFLAG_TUN, &check_integrity, 0,
119     "Enable integrity checking");
120
121 /*
122  * The GEOM partitioning class.
123  */
124 static g_ctl_req_t g_part_ctlreq;
125 static g_ctl_destroy_geom_t g_part_destroy_geom;
126 static g_fini_t g_part_fini;
127 static g_init_t g_part_init;
128 static g_taste_t g_part_taste;
129
130 static g_access_t g_part_access;
131 static g_dumpconf_t g_part_dumpconf;
132 static g_orphan_t g_part_orphan;
133 static g_spoiled_t g_part_spoiled;
134 static g_start_t g_part_start;
135
136 static struct g_class g_part_class = {
137         .name = "PART",
138         .version = G_VERSION,
139         /* Class methods. */
140         .ctlreq = g_part_ctlreq,
141         .destroy_geom = g_part_destroy_geom,
142         .fini = g_part_fini,
143         .init = g_part_init,
144         .taste = g_part_taste,
145         /* Geom methods. */
146         .access = g_part_access,
147         .dumpconf = g_part_dumpconf,
148         .orphan = g_part_orphan,
149         .spoiled = g_part_spoiled,
150         .start = g_part_start,
151 };
152
153 DECLARE_GEOM_CLASS(g_part_class, g_part);
154 MODULE_VERSION(g_part, 0);
155
156 /*
157  * Support functions.
158  */
159
160 static void g_part_wither(struct g_geom *, int);
161
162 const char *
163 g_part_alias_name(enum g_part_alias alias)
164 {
165         int i;
166
167         for (i = 0; i < G_PART_ALIAS_COUNT; i++) {
168                 if (g_part_alias_list[i].alias != alias)
169                         continue;
170                 return (g_part_alias_list[i].lexeme);
171         }
172
173         return (NULL);
174 }
175
176 void
177 g_part_geometry_heads(off_t blocks, u_int sectors, off_t *bestchs,
178     u_int *bestheads)
179 {
180         static u_int candidate_heads[] = { 1, 2, 16, 32, 64, 128, 255, 0 };
181         off_t chs, cylinders;
182         u_int heads;
183         int idx;
184
185         *bestchs = 0;
186         *bestheads = 0;
187         for (idx = 0; candidate_heads[idx] != 0; idx++) {
188                 heads = candidate_heads[idx];
189                 cylinders = blocks / heads / sectors;
190                 if (cylinders < heads || cylinders < sectors)
191                         break;
192                 if (cylinders > 1023)
193                         continue;
194                 chs = cylinders * heads * sectors;
195                 if (chs > *bestchs || (chs == *bestchs && *bestheads == 1)) {
196                         *bestchs = chs;
197                         *bestheads = heads;
198                 }
199         }
200 }
201
202 static void
203 g_part_geometry(struct g_part_table *table, struct g_consumer *cp,
204     off_t blocks)
205 {
206         static u_int candidate_sectors[] = { 1, 9, 17, 33, 63, 0 };
207         off_t chs, bestchs;
208         u_int heads, sectors;
209         int idx;
210
211         if (g_getattr("GEOM::fwsectors", cp, &sectors) != 0 || sectors == 0 ||
212             g_getattr("GEOM::fwheads", cp, &heads) != 0 || heads == 0) {
213                 table->gpt_fixgeom = 0;
214                 table->gpt_heads = 0;
215                 table->gpt_sectors = 0;
216                 bestchs = 0;
217                 for (idx = 0; candidate_sectors[idx] != 0; idx++) {
218                         sectors = candidate_sectors[idx];
219                         g_part_geometry_heads(blocks, sectors, &chs, &heads);
220                         if (chs == 0)
221                                 continue;
222                         /*
223                          * Prefer a geometry with sectors > 1, but only if
224                          * it doesn't bump down the numbver of heads to 1.
225                          */
226                         if (chs > bestchs || (chs == bestchs && heads > 1 &&
227                             table->gpt_sectors == 1)) {
228                                 bestchs = chs;
229                                 table->gpt_heads = heads;
230                                 table->gpt_sectors = sectors;
231                         }
232                 }
233                 /*
234                  * If we didn't find a geometry at all, then the disk is
235                  * too big. This means we can use the maximum number of
236                  * heads and sectors.
237                  */
238                 if (bestchs == 0) {
239                         table->gpt_heads = 255;
240                         table->gpt_sectors = 63;
241                 }
242         } else {
243                 table->gpt_fixgeom = 1;
244                 table->gpt_heads = heads;
245                 table->gpt_sectors = sectors;
246         }
247 }
248
249 #define DPRINTF(...)    if (bootverbose) {      \
250         printf("GEOM_PART: " __VA_ARGS__);      \
251 }
252
253 static int
254 g_part_check_integrity(struct g_part_table *table, struct g_consumer *cp)
255 {
256         struct g_part_entry *e1, *e2;
257         struct g_provider *pp;
258         off_t offset;
259         int failed;
260
261         failed = 0;
262         pp = cp->provider;
263         if (table->gpt_last < table->gpt_first) {
264                 DPRINTF("last LBA is below first LBA: %jd < %jd\n",
265                     (intmax_t)table->gpt_last, (intmax_t)table->gpt_first);
266                 failed++;
267         }
268         if (table->gpt_last > pp->mediasize / pp->sectorsize - 1) {
269                 DPRINTF("last LBA extends beyond mediasize: "
270                     "%jd > %jd\n", (intmax_t)table->gpt_last,
271                     (intmax_t)pp->mediasize / pp->sectorsize - 1);
272                 failed++;
273         }
274         LIST_FOREACH(e1, &table->gpt_entry, gpe_entry) {
275                 if (e1->gpe_deleted || e1->gpe_internal)
276                         continue;
277                 if (e1->gpe_start < table->gpt_first) {
278                         DPRINTF("partition %d has start offset below first "
279                             "LBA: %jd < %jd\n", e1->gpe_index,
280                             (intmax_t)e1->gpe_start,
281                             (intmax_t)table->gpt_first);
282                         failed++;
283                 }
284                 if (e1->gpe_start > table->gpt_last) {
285                         DPRINTF("partition %d has start offset beyond last "
286                             "LBA: %jd > %jd\n", e1->gpe_index,
287                             (intmax_t)e1->gpe_start,
288                             (intmax_t)table->gpt_last);
289                         failed++;
290                 }
291                 if (e1->gpe_end < e1->gpe_start) {
292                         DPRINTF("partition %d has end offset below start "
293                             "offset: %jd < %jd\n", e1->gpe_index,
294                             (intmax_t)e1->gpe_end,
295                             (intmax_t)e1->gpe_start);
296                         failed++;
297                 }
298                 if (e1->gpe_end > table->gpt_last) {
299                         DPRINTF("partition %d has end offset beyond last "
300                             "LBA: %jd > %jd\n", e1->gpe_index,
301                             (intmax_t)e1->gpe_end,
302                             (intmax_t)table->gpt_last);
303                         failed++;
304                 }
305                 if (pp->stripesize > 0) {
306                         offset = e1->gpe_start * pp->sectorsize;
307                         if (e1->gpe_offset > offset)
308                                 offset = e1->gpe_offset;
309                         if ((offset + pp->stripeoffset) % pp->stripesize) {
310                                 DPRINTF("partition %d is not aligned on %u "
311                                     "bytes\n", e1->gpe_index, pp->stripesize);
312                                 /* Don't treat this as a critical failure */
313                         }
314                 }
315                 e2 = e1;
316                 while ((e2 = LIST_NEXT(e2, gpe_entry)) != NULL) {
317                         if (e2->gpe_deleted || e2->gpe_internal)
318                                 continue;
319                         if (e1->gpe_start >= e2->gpe_start &&
320                             e1->gpe_start <= e2->gpe_end) {
321                                 DPRINTF("partition %d has start offset inside "
322                                     "partition %d: start[%d] %jd >= start[%d] "
323                                     "%jd <= end[%d] %jd\n",
324                                     e1->gpe_index, e2->gpe_index,
325                                     e2->gpe_index, (intmax_t)e2->gpe_start,
326                                     e1->gpe_index, (intmax_t)e1->gpe_start,
327                                     e2->gpe_index, (intmax_t)e2->gpe_end);
328                                 failed++;
329                         }
330                         if (e1->gpe_end >= e2->gpe_start &&
331                             e1->gpe_end <= e2->gpe_end) {
332                                 DPRINTF("partition %d has end offset inside "
333                                     "partition %d: start[%d] %jd >= end[%d] "
334                                     "%jd <= end[%d] %jd\n",
335                                     e1->gpe_index, e2->gpe_index,
336                                     e2->gpe_index, (intmax_t)e2->gpe_start,
337                                     e1->gpe_index, (intmax_t)e1->gpe_end,
338                                     e2->gpe_index, (intmax_t)e2->gpe_end);
339                                 failed++;
340                         }
341                         if (e1->gpe_start < e2->gpe_start &&
342                             e1->gpe_end > e2->gpe_end) {
343                                 DPRINTF("partition %d contains partition %d: "
344                                     "start[%d] %jd > start[%d] %jd, end[%d] "
345                                     "%jd < end[%d] %jd\n",
346                                     e1->gpe_index, e2->gpe_index,
347                                     e1->gpe_index, (intmax_t)e1->gpe_start,
348                                     e2->gpe_index, (intmax_t)e2->gpe_start,
349                                     e2->gpe_index, (intmax_t)e2->gpe_end,
350                                     e1->gpe_index, (intmax_t)e1->gpe_end);
351                                 failed++;
352                         }
353                 }
354         }
355         if (failed != 0) {
356                 printf("GEOM_PART: integrity check failed (%s, %s)\n",
357                     pp->name, table->gpt_scheme->name);
358                 if (check_integrity != 0)
359                         return (EINVAL);
360                 table->gpt_corrupt = 1;
361         }
362         return (0);
363 }
364 #undef  DPRINTF
365
366 struct g_part_entry *
367 g_part_new_entry(struct g_part_table *table, int index, quad_t start,
368     quad_t end)
369 {
370         struct g_part_entry *entry, *last;
371
372         last = NULL;
373         LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
374                 if (entry->gpe_index == index)
375                         break;
376                 if (entry->gpe_index > index) {
377                         entry = NULL;
378                         break;
379                 }
380                 last = entry;
381         }
382         if (entry == NULL) {
383                 entry = g_malloc(table->gpt_scheme->gps_entrysz,
384                     M_WAITOK | M_ZERO);
385                 entry->gpe_index = index;
386                 if (last == NULL)
387                         LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
388                 else
389                         LIST_INSERT_AFTER(last, entry, gpe_entry);
390         } else
391                 entry->gpe_offset = 0;
392         entry->gpe_start = start;
393         entry->gpe_end = end;
394         return (entry);
395 }
396
397 static void
398 g_part_new_provider(struct g_geom *gp, struct g_part_table *table,
399     struct g_part_entry *entry)
400 {
401         struct g_consumer *cp;
402         struct g_provider *pp;
403         struct sbuf *sb;
404         off_t offset;
405
406         cp = LIST_FIRST(&gp->consumer);
407         pp = cp->provider;
408
409         offset = entry->gpe_start * pp->sectorsize;
410         if (entry->gpe_offset < offset)
411                 entry->gpe_offset = offset;
412
413         if (entry->gpe_pp == NULL) {
414                 sb = sbuf_new_auto();
415                 G_PART_FULLNAME(table, entry, sb, gp->name);
416                 sbuf_finish(sb);
417                 entry->gpe_pp = g_new_providerf(gp, "%s", sbuf_data(sb));
418                 sbuf_delete(sb);
419                 entry->gpe_pp->private = entry;         /* Close the circle. */
420         }
421         entry->gpe_pp->index = entry->gpe_index - 1;    /* index is 1-based. */
422         entry->gpe_pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) *
423             pp->sectorsize;
424         entry->gpe_pp->mediasize -= entry->gpe_offset - offset;
425         entry->gpe_pp->sectorsize = pp->sectorsize;
426         entry->gpe_pp->flags = pp->flags & G_PF_CANDELETE;
427         entry->gpe_pp->stripesize = pp->stripesize;
428         entry->gpe_pp->stripeoffset = pp->stripeoffset + entry->gpe_offset;
429         if (pp->stripesize > 0)
430                 entry->gpe_pp->stripeoffset %= pp->stripesize;
431         g_error_provider(entry->gpe_pp, 0);
432 }
433
434 static struct g_geom*
435 g_part_find_geom(const char *name)
436 {
437         struct g_geom *gp;
438         LIST_FOREACH(gp, &g_part_class.geom, geom) {
439                 if (!strcmp(name, gp->name))
440                         break;
441         }
442         return (gp);
443 }
444
445 static int
446 g_part_parm_geom(const char *name, struct g_geom **v)
447 {
448         struct g_geom *gp;
449
450         if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
451                 name += strlen(_PATH_DEV);
452         gp = g_part_find_geom(name);
453         if (gp == NULL)
454                 return (EINVAL);
455         if ((gp->flags & G_GEOM_WITHER) != 0)
456                 return (ENXIO);
457         *v = gp;
458         return (0);
459 }
460
461 static int
462 g_part_parm_provider(const char *name, struct g_provider **v)
463 {
464         struct g_provider *pp;
465
466         if (strncmp(name, _PATH_DEV, strlen(_PATH_DEV)) == 0)
467                 name += strlen(_PATH_DEV);
468         pp = g_provider_by_name(name);
469         if (pp == NULL)
470                 return (EINVAL);
471         *v = pp;
472         return (0);
473 }
474
475 static int
476 g_part_parm_quad(const char *p, quad_t *v)
477 {
478         char *x;
479         quad_t q;
480
481         q = strtoq(p, &x, 0);
482         if (*x != '\0' || q < 0)
483                 return (EINVAL);
484         *v = q;
485         return (0);
486 }
487
488 static int
489 g_part_parm_scheme(const char *p, struct g_part_scheme **v)
490 {
491         struct g_part_scheme *s;
492
493         TAILQ_FOREACH(s, &g_part_schemes, scheme_list) {
494                 if (s == &g_part_null_scheme)
495                         continue;
496                 if (!strcasecmp(s->name, p))
497                         break;
498         }
499         if (s == NULL)
500                 return (EINVAL);
501         *v = s;
502         return (0);
503 }
504
505 static int
506 g_part_parm_str(const char *p, const char **v)
507 {
508
509         if (p[0] == '\0')
510                 return (EINVAL);
511         *v = p;
512         return (0);
513 }
514
515 static int
516 g_part_parm_uint(const char *p, u_int *v)
517 {
518         char *x;
519         long l;
520
521         l = strtol(p, &x, 0);
522         if (*x != '\0' || l < 0 || l > INT_MAX)
523                 return (EINVAL);
524         *v = (unsigned int)l;
525         return (0);
526 }
527
528 static int
529 g_part_probe(struct g_geom *gp, struct g_consumer *cp, int depth)
530 {
531         struct g_part_scheme *iter, *scheme;
532         struct g_part_table *table;
533         int pri, probe;
534
535         table = gp->softc;
536         scheme = (table != NULL) ? table->gpt_scheme : NULL;
537         pri = (scheme != NULL) ? G_PART_PROBE(table, cp) : INT_MIN;
538         if (pri == 0)
539                 goto done;
540         if (pri > 0) {  /* error */
541                 scheme = NULL;
542                 pri = INT_MIN;
543         }
544
545         TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) {
546                 if (iter == &g_part_null_scheme)
547                         continue;
548                 table = (void *)kobj_create((kobj_class_t)iter, M_GEOM,
549                     M_WAITOK);
550                 table->gpt_gp = gp;
551                 table->gpt_scheme = iter;
552                 table->gpt_depth = depth;
553                 probe = G_PART_PROBE(table, cp);
554                 if (probe <= 0 && probe > pri) {
555                         pri = probe;
556                         scheme = iter;
557                         if (gp->softc != NULL)
558                                 kobj_delete((kobj_t)gp->softc, M_GEOM);
559                         gp->softc = table;
560                         if (pri == 0)
561                                 goto done;
562                 } else
563                         kobj_delete((kobj_t)table, M_GEOM);
564         }
565
566 done:
567         return ((scheme == NULL) ? ENXIO : 0);
568 }
569
570 /*
571  * Control request functions.
572  */
573
574 static int
575 g_part_ctl_add(struct gctl_req *req, struct g_part_parms *gpp)
576 {
577         struct g_geom *gp;
578         struct g_provider *pp;
579         struct g_part_entry *delent, *last, *entry;
580         struct g_part_table *table;
581         struct sbuf *sb;
582         quad_t end;
583         unsigned int index;
584         int error;
585
586         gp = gpp->gpp_geom;
587         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
588         g_topology_assert();
589
590         pp = LIST_FIRST(&gp->consumer)->provider;
591         table = gp->softc;
592         end = gpp->gpp_start + gpp->gpp_size - 1;
593
594         if (gpp->gpp_start < table->gpt_first ||
595             gpp->gpp_start > table->gpt_last) {
596                 gctl_error(req, "%d start '%jd'", EINVAL,
597                     (intmax_t)gpp->gpp_start);
598                 return (EINVAL);
599         }
600         if (end < gpp->gpp_start || end > table->gpt_last) {
601                 gctl_error(req, "%d size '%jd'", EINVAL,
602                     (intmax_t)gpp->gpp_size);
603                 return (EINVAL);
604         }
605         if (gpp->gpp_index > table->gpt_entries) {
606                 gctl_error(req, "%d index '%d'", EINVAL, gpp->gpp_index);
607                 return (EINVAL);
608         }
609
610         delent = last = NULL;
611         index = (gpp->gpp_index > 0) ? gpp->gpp_index : 1;
612         LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
613                 if (entry->gpe_deleted) {
614                         if (entry->gpe_index == index)
615                                 delent = entry;
616                         continue;
617                 }
618                 if (entry->gpe_index == index)
619                         index = entry->gpe_index + 1;
620                 if (entry->gpe_index < index)
621                         last = entry;
622                 if (entry->gpe_internal)
623                         continue;
624                 if (gpp->gpp_start >= entry->gpe_start &&
625                     gpp->gpp_start <= entry->gpe_end) {
626                         gctl_error(req, "%d start '%jd'", ENOSPC,
627                             (intmax_t)gpp->gpp_start);
628                         return (ENOSPC);
629                 }
630                 if (end >= entry->gpe_start && end <= entry->gpe_end) {
631                         gctl_error(req, "%d end '%jd'", ENOSPC, (intmax_t)end);
632                         return (ENOSPC);
633                 }
634                 if (gpp->gpp_start < entry->gpe_start && end > entry->gpe_end) {
635                         gctl_error(req, "%d size '%jd'", ENOSPC,
636                             (intmax_t)gpp->gpp_size);
637                         return (ENOSPC);
638                 }
639         }
640         if (gpp->gpp_index > 0 && index != gpp->gpp_index) {
641                 gctl_error(req, "%d index '%d'", EEXIST, gpp->gpp_index);
642                 return (EEXIST);
643         }
644         if (index > table->gpt_entries) {
645                 gctl_error(req, "%d index '%d'", ENOSPC, index);
646                 return (ENOSPC);
647         }
648
649         entry = (delent == NULL) ? g_malloc(table->gpt_scheme->gps_entrysz,
650             M_WAITOK | M_ZERO) : delent;
651         entry->gpe_index = index;
652         entry->gpe_start = gpp->gpp_start;
653         entry->gpe_end = end;
654         error = G_PART_ADD(table, entry, gpp);
655         if (error) {
656                 gctl_error(req, "%d", error);
657                 if (delent == NULL)
658                         g_free(entry);
659                 return (error);
660         }
661         if (delent == NULL) {
662                 if (last == NULL)
663                         LIST_INSERT_HEAD(&table->gpt_entry, entry, gpe_entry);
664                 else
665                         LIST_INSERT_AFTER(last, entry, gpe_entry);
666                 entry->gpe_created = 1;
667         } else {
668                 entry->gpe_deleted = 0;
669                 entry->gpe_modified = 1;
670         }
671         g_part_new_provider(gp, table, entry);
672
673         /* Provide feedback if so requested. */
674         if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
675                 sb = sbuf_new_auto();
676                 G_PART_FULLNAME(table, entry, sb, gp->name);
677                 if (pp->stripesize > 0 && entry->gpe_pp->stripeoffset != 0)
678                         sbuf_printf(sb, " added, but partition is not "
679                             "aligned on %u bytes\n", pp->stripesize);
680                 else
681                         sbuf_cat(sb, " added\n");
682                 sbuf_finish(sb);
683                 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
684                 sbuf_delete(sb);
685         }
686         return (0);
687 }
688
689 static int
690 g_part_ctl_bootcode(struct gctl_req *req, struct g_part_parms *gpp)
691 {
692         struct g_geom *gp;
693         struct g_part_table *table;
694         struct sbuf *sb;
695         int error, sz;
696
697         gp = gpp->gpp_geom;
698         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
699         g_topology_assert();
700
701         table = gp->softc;
702         sz = table->gpt_scheme->gps_bootcodesz;
703         if (sz == 0) {
704                 error = ENODEV;
705                 goto fail;
706         }
707         if (gpp->gpp_codesize > sz) {
708                 error = EFBIG;
709                 goto fail;
710         }
711
712         error = G_PART_BOOTCODE(table, gpp);
713         if (error)
714                 goto fail;
715
716         /* Provide feedback if so requested. */
717         if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
718                 sb = sbuf_new_auto();
719                 sbuf_printf(sb, "bootcode written to %s\n", gp->name);
720                 sbuf_finish(sb);
721                 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
722                 sbuf_delete(sb);
723         }
724         return (0);
725
726  fail:
727         gctl_error(req, "%d", error);
728         return (error);
729 }
730
731 static int
732 g_part_ctl_commit(struct gctl_req *req, struct g_part_parms *gpp)
733 {
734         struct g_consumer *cp;
735         struct g_geom *gp;
736         struct g_provider *pp;
737         struct g_part_entry *entry, *tmp;
738         struct g_part_table *table;
739         char *buf;
740         int error, i;
741
742         gp = gpp->gpp_geom;
743         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
744         g_topology_assert();
745
746         table = gp->softc;
747         if (!table->gpt_opened) {
748                 gctl_error(req, "%d", EPERM);
749                 return (EPERM);
750         }
751
752         g_topology_unlock();
753
754         cp = LIST_FIRST(&gp->consumer);
755         if ((table->gpt_smhead | table->gpt_smtail) != 0) {
756                 pp = cp->provider;
757                 buf = g_malloc(pp->sectorsize, M_WAITOK | M_ZERO);
758                 while (table->gpt_smhead != 0) {
759                         i = ffs(table->gpt_smhead) - 1;
760                         error = g_write_data(cp, i * pp->sectorsize, buf,
761                             pp->sectorsize);
762                         if (error) {
763                                 g_free(buf);
764                                 goto fail;
765                         }
766                         table->gpt_smhead &= ~(1 << i);
767                 }
768                 while (table->gpt_smtail != 0) {
769                         i = ffs(table->gpt_smtail) - 1;
770                         error = g_write_data(cp, pp->mediasize - (i + 1) *
771                             pp->sectorsize, buf, pp->sectorsize);
772                         if (error) {
773                                 g_free(buf);
774                                 goto fail;
775                         }
776                         table->gpt_smtail &= ~(1 << i);
777                 }
778                 g_free(buf);
779         }
780
781         if (table->gpt_scheme == &g_part_null_scheme) {
782                 g_topology_lock();
783                 g_access(cp, -1, -1, -1);
784                 g_part_wither(gp, ENXIO);
785                 return (0);
786         }
787
788         error = G_PART_WRITE(table, cp);
789         if (error)
790                 goto fail;
791
792         LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
793                 if (!entry->gpe_deleted) {
794                         entry->gpe_created = 0;
795                         entry->gpe_modified = 0;
796                         continue;
797                 }
798                 LIST_REMOVE(entry, gpe_entry);
799                 g_free(entry);
800         }
801         table->gpt_created = 0;
802         table->gpt_opened = 0;
803
804         g_topology_lock();
805         g_access(cp, -1, -1, -1);
806         return (0);
807
808 fail:
809         g_topology_lock();
810         gctl_error(req, "%d", error);
811         return (error);
812 }
813
814 static int
815 g_part_ctl_create(struct gctl_req *req, struct g_part_parms *gpp)
816 {
817         struct g_consumer *cp;
818         struct g_geom *gp;
819         struct g_provider *pp;
820         struct g_part_scheme *scheme;
821         struct g_part_table *null, *table;
822         struct sbuf *sb;
823         int attr, error;
824
825         pp = gpp->gpp_provider;
826         scheme = gpp->gpp_scheme;
827         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
828         g_topology_assert();
829
830         /* Check that there isn't already a g_part geom on the provider. */
831         gp = g_part_find_geom(pp->name);
832         if (gp != NULL) {
833                 null = gp->softc;
834                 if (null->gpt_scheme != &g_part_null_scheme) {
835                         gctl_error(req, "%d geom '%s'", EEXIST, pp->name);
836                         return (EEXIST);
837                 }
838         } else
839                 null = NULL;
840
841         if ((gpp->gpp_parms & G_PART_PARM_ENTRIES) &&
842             (gpp->gpp_entries < scheme->gps_minent ||
843              gpp->gpp_entries > scheme->gps_maxent)) {
844                 gctl_error(req, "%d entries '%d'", EINVAL, gpp->gpp_entries);
845                 return (EINVAL);
846         }
847
848         if (null == NULL)
849                 gp = g_new_geomf(&g_part_class, "%s", pp->name);
850         gp->softc = kobj_create((kobj_class_t)gpp->gpp_scheme, M_GEOM,
851             M_WAITOK);
852         table = gp->softc;
853         table->gpt_gp = gp;
854         table->gpt_scheme = gpp->gpp_scheme;
855         table->gpt_entries = (gpp->gpp_parms & G_PART_PARM_ENTRIES) ?
856             gpp->gpp_entries : scheme->gps_minent;
857         LIST_INIT(&table->gpt_entry);
858         if (null == NULL) {
859                 cp = g_new_consumer(gp);
860                 error = g_attach(cp, pp);
861                 if (error == 0)
862                         error = g_access(cp, 1, 1, 1);
863                 if (error != 0) {
864                         g_part_wither(gp, error);
865                         gctl_error(req, "%d geom '%s'", error, pp->name);
866                         return (error);
867                 }
868                 table->gpt_opened = 1;
869         } else {
870                 cp = LIST_FIRST(&gp->consumer);
871                 table->gpt_opened = null->gpt_opened;
872                 table->gpt_smhead = null->gpt_smhead;
873                 table->gpt_smtail = null->gpt_smtail;
874         }
875
876         g_topology_unlock();
877
878         /* Make sure the provider has media. */
879         if (pp->mediasize == 0 || pp->sectorsize == 0) {
880                 error = ENODEV;
881                 goto fail;
882         }
883
884         /* Make sure we can nest and if so, determine our depth. */
885         error = g_getattr("PART::isleaf", cp, &attr);
886         if (!error && attr) {
887                 error = ENODEV;
888                 goto fail;
889         }
890         error = g_getattr("PART::depth", cp, &attr);
891         table->gpt_depth = (!error) ? attr + 1 : 0;
892
893         /*
894          * Synthesize a disk geometry. Some partitioning schemes
895          * depend on it and since some file systems need it even
896          * when the partitition scheme doesn't, we do it here in
897          * scheme-independent code.
898          */
899         g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
900
901         error = G_PART_CREATE(table, gpp);
902         if (error)
903                 goto fail;
904
905         g_topology_lock();
906
907         table->gpt_created = 1;
908         if (null != NULL)
909                 kobj_delete((kobj_t)null, M_GEOM);
910
911         /*
912          * Support automatic commit by filling in the gpp_geom
913          * parameter.
914          */
915         gpp->gpp_parms |= G_PART_PARM_GEOM;
916         gpp->gpp_geom = gp;
917
918         /* Provide feedback if so requested. */
919         if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
920                 sb = sbuf_new_auto();
921                 sbuf_printf(sb, "%s created\n", gp->name);
922                 sbuf_finish(sb);
923                 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
924                 sbuf_delete(sb);
925         }
926         return (0);
927
928 fail:
929         g_topology_lock();
930         if (null == NULL) {
931                 g_access(cp, -1, -1, -1);
932                 g_part_wither(gp, error);
933         } else {
934                 kobj_delete((kobj_t)gp->softc, M_GEOM);
935                 gp->softc = null;
936         }
937         gctl_error(req, "%d provider", error);
938         return (error);
939 }
940
941 static int
942 g_part_ctl_delete(struct gctl_req *req, struct g_part_parms *gpp)
943 {
944         struct g_geom *gp;
945         struct g_provider *pp;
946         struct g_part_entry *entry;
947         struct g_part_table *table;
948         struct sbuf *sb;
949
950         gp = gpp->gpp_geom;
951         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
952         g_topology_assert();
953
954         table = gp->softc;
955
956         LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
957                 if (entry->gpe_deleted || entry->gpe_internal)
958                         continue;
959                 if (entry->gpe_index == gpp->gpp_index)
960                         break;
961         }
962         if (entry == NULL) {
963                 gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
964                 return (ENOENT);
965         }
966
967         pp = entry->gpe_pp;
968         if (pp != NULL) {
969                 if (pp->acr > 0 || pp->acw > 0 || pp->ace > 0) {
970                         gctl_error(req, "%d", EBUSY);
971                         return (EBUSY);
972                 }
973
974                 pp->private = NULL;
975                 entry->gpe_pp = NULL;
976         }
977
978         if (pp != NULL)
979                 g_wither_provider(pp, ENXIO);
980
981         /* Provide feedback if so requested. */
982         if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
983                 sb = sbuf_new_auto();
984                 G_PART_FULLNAME(table, entry, sb, gp->name);
985                 sbuf_cat(sb, " deleted\n");
986                 sbuf_finish(sb);
987                 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
988                 sbuf_delete(sb);
989         }
990
991         if (entry->gpe_created) {
992                 LIST_REMOVE(entry, gpe_entry);
993                 g_free(entry);
994         } else {
995                 entry->gpe_modified = 0;
996                 entry->gpe_deleted = 1;
997         }
998         return (0);
999 }
1000
1001 static int
1002 g_part_ctl_destroy(struct gctl_req *req, struct g_part_parms *gpp)
1003 {
1004         struct g_consumer *cp;
1005         struct g_geom *gp;
1006         struct g_provider *pp;
1007         struct g_part_entry *entry, *tmp;
1008         struct g_part_table *null, *table;
1009         struct sbuf *sb;
1010         int error;
1011
1012         gp = gpp->gpp_geom;
1013         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1014         g_topology_assert();
1015
1016         table = gp->softc;
1017         /* Check for busy providers. */
1018         LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1019                 if (entry->gpe_deleted || entry->gpe_internal)
1020                         continue;
1021                 if (gpp->gpp_force) {
1022                         pp = entry->gpe_pp;
1023                         if (pp == NULL)
1024                                 continue;
1025                         if (pp->acr == 0 && pp->acw == 0 && pp->ace == 0)
1026                                 continue;
1027                 }
1028                 gctl_error(req, "%d", EBUSY);
1029                 return (EBUSY);
1030         }
1031
1032         if (gpp->gpp_force) {
1033                 /* Destroy all providers. */
1034                 LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
1035                         pp = entry->gpe_pp;
1036                         if (pp != NULL) {
1037                                 pp->private = NULL;
1038                                 g_wither_provider(pp, ENXIO);
1039                         }
1040                         LIST_REMOVE(entry, gpe_entry);
1041                         g_free(entry);
1042                 }
1043         }
1044
1045         error = G_PART_DESTROY(table, gpp);
1046         if (error) {
1047                 gctl_error(req, "%d", error);
1048                 return (error);
1049         }
1050
1051         gp->softc = kobj_create((kobj_class_t)&g_part_null_scheme, M_GEOM,
1052             M_WAITOK);
1053         null = gp->softc;
1054         null->gpt_gp = gp;
1055         null->gpt_scheme = &g_part_null_scheme;
1056         LIST_INIT(&null->gpt_entry);
1057
1058         cp = LIST_FIRST(&gp->consumer);
1059         pp = cp->provider;
1060         null->gpt_last = pp->mediasize / pp->sectorsize - 1;
1061
1062         null->gpt_depth = table->gpt_depth;
1063         null->gpt_opened = table->gpt_opened;
1064         null->gpt_smhead = table->gpt_smhead;
1065         null->gpt_smtail = table->gpt_smtail;
1066
1067         while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1068                 LIST_REMOVE(entry, gpe_entry);
1069                 g_free(entry);
1070         }
1071         kobj_delete((kobj_t)table, M_GEOM);
1072
1073         /* Provide feedback if so requested. */
1074         if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1075                 sb = sbuf_new_auto();
1076                 sbuf_printf(sb, "%s destroyed\n", gp->name);
1077                 sbuf_finish(sb);
1078                 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1079                 sbuf_delete(sb);
1080         }
1081         return (0);
1082 }
1083
1084 static int
1085 g_part_ctl_modify(struct gctl_req *req, struct g_part_parms *gpp)
1086 {
1087         struct g_geom *gp;
1088         struct g_part_entry *entry;
1089         struct g_part_table *table;
1090         struct sbuf *sb;
1091         int error;
1092
1093         gp = gpp->gpp_geom;
1094         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1095         g_topology_assert();
1096
1097         table = gp->softc;
1098
1099         LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1100                 if (entry->gpe_deleted || entry->gpe_internal)
1101                         continue;
1102                 if (entry->gpe_index == gpp->gpp_index)
1103                         break;
1104         }
1105         if (entry == NULL) {
1106                 gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1107                 return (ENOENT);
1108         }
1109
1110         error = G_PART_MODIFY(table, entry, gpp);
1111         if (error) {
1112                 gctl_error(req, "%d", error);
1113                 return (error);
1114         }
1115
1116         if (!entry->gpe_created)
1117                 entry->gpe_modified = 1;
1118
1119         /* Provide feedback if so requested. */
1120         if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1121                 sb = sbuf_new_auto();
1122                 G_PART_FULLNAME(table, entry, sb, gp->name);
1123                 sbuf_cat(sb, " modified\n");
1124                 sbuf_finish(sb);
1125                 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1126                 sbuf_delete(sb);
1127         }
1128         return (0);
1129 }
1130
1131 static int
1132 g_part_ctl_move(struct gctl_req *req, struct g_part_parms *gpp)
1133 {
1134         gctl_error(req, "%d verb 'move'", ENOSYS);
1135         return (ENOSYS);
1136 }
1137
1138 static int
1139 g_part_ctl_recover(struct gctl_req *req, struct g_part_parms *gpp)
1140 {
1141         struct g_part_table *table;
1142         struct g_geom *gp;
1143         struct sbuf *sb;
1144         int error, recovered;
1145
1146         gp = gpp->gpp_geom;
1147         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1148         g_topology_assert();
1149         table = gp->softc;
1150         error = recovered = 0;
1151
1152         if (table->gpt_corrupt) {
1153                 error = G_PART_RECOVER(table);
1154                 if (error == 0)
1155                         error = g_part_check_integrity(table,
1156                             LIST_FIRST(&gp->consumer));
1157                 if (error) {
1158                         gctl_error(req, "%d recovering '%s' failed",
1159                             error, gp->name);
1160                         return (error);
1161                 }
1162                 recovered = 1;
1163         }
1164         /* Provide feedback if so requested. */
1165         if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1166                 sb = sbuf_new_auto();
1167                 if (recovered)
1168                         sbuf_printf(sb, "%s recovered\n", gp->name);
1169                 else
1170                         sbuf_printf(sb, "%s recovering is not needed\n",
1171                             gp->name);
1172                 sbuf_finish(sb);
1173                 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1174                 sbuf_delete(sb);
1175         }
1176         return (0);
1177 }
1178
1179 static int
1180 g_part_ctl_resize(struct gctl_req *req, struct g_part_parms *gpp)
1181 {
1182         struct g_geom *gp;
1183         struct g_provider *pp;
1184         struct g_part_entry *pe, *entry;
1185         struct g_part_table *table;
1186         struct sbuf *sb;
1187         quad_t end;
1188         int error;
1189
1190         gp = gpp->gpp_geom;
1191         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1192         g_topology_assert();
1193         table = gp->softc;
1194
1195         /* check gpp_index */
1196         LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1197                 if (entry->gpe_deleted || entry->gpe_internal)
1198                         continue;
1199                 if (entry->gpe_index == gpp->gpp_index)
1200                         break;
1201         }
1202         if (entry == NULL) {
1203                 gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1204                 return (ENOENT);
1205         }
1206
1207         /* check gpp_size */
1208         end = entry->gpe_start + gpp->gpp_size - 1;
1209         if (gpp->gpp_size < 1 || end > table->gpt_last) {
1210                 gctl_error(req, "%d size '%jd'", EINVAL,
1211                     (intmax_t)gpp->gpp_size);
1212                 return (EINVAL);
1213         }
1214
1215         LIST_FOREACH(pe, &table->gpt_entry, gpe_entry) {
1216                 if (pe->gpe_deleted || pe->gpe_internal || pe == entry)
1217                         continue;
1218                 if (end >= pe->gpe_start && end <= pe->gpe_end) {
1219                         gctl_error(req, "%d end '%jd'", ENOSPC,
1220                             (intmax_t)end);
1221                         return (ENOSPC);
1222                 }
1223                 if (entry->gpe_start < pe->gpe_start && end > pe->gpe_end) {
1224                         gctl_error(req, "%d size '%jd'", ENOSPC,
1225                             (intmax_t)gpp->gpp_size);
1226                         return (ENOSPC);
1227                 }
1228         }
1229
1230         pp = entry->gpe_pp;
1231         if ((g_debugflags & 16) == 0 &&
1232             (pp->acr > 0 || pp->acw > 0 || pp->ace > 0)) {
1233                 gctl_error(req, "%d", EBUSY);
1234                 return (EBUSY);
1235         }
1236
1237         error = G_PART_RESIZE(table, entry, gpp);
1238         if (error) {
1239                 gctl_error(req, "%d", error);
1240                 return (error);
1241         }
1242
1243         if (!entry->gpe_created)
1244                 entry->gpe_modified = 1;
1245
1246         /* update mediasize of changed provider */
1247         pp->mediasize = (entry->gpe_end - entry->gpe_start + 1) *
1248                 pp->sectorsize;
1249
1250         /* Provide feedback if so requested. */
1251         if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1252                 sb = sbuf_new_auto();
1253                 G_PART_FULLNAME(table, entry, sb, gp->name);
1254                 sbuf_cat(sb, " resized\n");
1255                 sbuf_finish(sb);
1256                 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1257                 sbuf_delete(sb);
1258         }
1259         return (0);
1260 }
1261
1262 static int
1263 g_part_ctl_setunset(struct gctl_req *req, struct g_part_parms *gpp,
1264     unsigned int set)
1265 {
1266         struct g_geom *gp;
1267         struct g_part_entry *entry;
1268         struct g_part_table *table;
1269         struct sbuf *sb;
1270         int error;
1271
1272         gp = gpp->gpp_geom;
1273         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1274         g_topology_assert();
1275
1276         table = gp->softc;
1277
1278         LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1279                 if (entry->gpe_deleted || entry->gpe_internal)
1280                         continue;
1281                 if (entry->gpe_index == gpp->gpp_index)
1282                         break;
1283         }
1284         if (entry == NULL) {
1285                 gctl_error(req, "%d index '%d'", ENOENT, gpp->gpp_index);
1286                 return (ENOENT);
1287         }
1288
1289         error = G_PART_SETUNSET(table, entry, gpp->gpp_attrib, set);
1290         if (error) {
1291                 gctl_error(req, "%d attrib '%s'", error, gpp->gpp_attrib);
1292                 return (error);
1293         }
1294
1295         /* Provide feedback if so requested. */
1296         if (gpp->gpp_parms & G_PART_PARM_OUTPUT) {
1297                 sb = sbuf_new_auto();
1298                 sbuf_printf(sb, "%s %sset on ", gpp->gpp_attrib,
1299                     (set) ? "" : "un");
1300                 G_PART_FULLNAME(table, entry, sb, gp->name);
1301                 sbuf_printf(sb, "\n");
1302                 sbuf_finish(sb);
1303                 gctl_set_param(req, "output", sbuf_data(sb), sbuf_len(sb) + 1);
1304                 sbuf_delete(sb);
1305         }
1306         return (0);
1307 }
1308
1309 static int
1310 g_part_ctl_undo(struct gctl_req *req, struct g_part_parms *gpp)
1311 {
1312         struct g_consumer *cp;
1313         struct g_provider *pp;
1314         struct g_geom *gp;
1315         struct g_part_entry *entry, *tmp;
1316         struct g_part_table *table;
1317         int error, reprobe;
1318
1319         gp = gpp->gpp_geom;
1320         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, gp->name));
1321         g_topology_assert();
1322
1323         table = gp->softc;
1324         if (!table->gpt_opened) {
1325                 gctl_error(req, "%d", EPERM);
1326                 return (EPERM);
1327         }
1328
1329         cp = LIST_FIRST(&gp->consumer);
1330         LIST_FOREACH_SAFE(entry, &table->gpt_entry, gpe_entry, tmp) {
1331                 entry->gpe_modified = 0;
1332                 if (entry->gpe_created) {
1333                         pp = entry->gpe_pp;
1334                         if (pp != NULL) {
1335                                 pp->private = NULL;
1336                                 entry->gpe_pp = NULL;
1337                                 g_wither_provider(pp, ENXIO);
1338                         }
1339                         entry->gpe_deleted = 1;
1340                 }
1341                 if (entry->gpe_deleted) {
1342                         LIST_REMOVE(entry, gpe_entry);
1343                         g_free(entry);
1344                 }
1345         }
1346
1347         g_topology_unlock();
1348
1349         reprobe = (table->gpt_scheme == &g_part_null_scheme ||
1350             table->gpt_created) ? 1 : 0;
1351
1352         if (reprobe) {
1353                 LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1354                         if (entry->gpe_internal)
1355                                 continue;
1356                         error = EBUSY;
1357                         goto fail;
1358                 }
1359                 while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1360                         LIST_REMOVE(entry, gpe_entry);
1361                         g_free(entry);
1362                 }
1363                 error = g_part_probe(gp, cp, table->gpt_depth);
1364                 if (error) {
1365                         g_topology_lock();
1366                         g_access(cp, -1, -1, -1);
1367                         g_part_wither(gp, error);
1368                         return (0);
1369                 }
1370                 table = gp->softc;
1371
1372                 /*
1373                  * Synthesize a disk geometry. Some partitioning schemes
1374                  * depend on it and since some file systems need it even
1375                  * when the partitition scheme doesn't, we do it here in
1376                  * scheme-independent code.
1377                  */
1378                 pp = cp->provider;
1379                 g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1380         }
1381
1382         error = G_PART_READ(table, cp);
1383         if (error)
1384                 goto fail;
1385         error = g_part_check_integrity(table, cp);
1386         if (error)
1387                 goto fail;
1388
1389         g_topology_lock();
1390         LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1391                 if (!entry->gpe_internal)
1392                         g_part_new_provider(gp, table, entry);
1393         }
1394
1395         table->gpt_opened = 0;
1396         g_access(cp, -1, -1, -1);
1397         return (0);
1398
1399 fail:
1400         g_topology_lock();
1401         gctl_error(req, "%d", error);
1402         return (error);
1403 }
1404
1405 static void
1406 g_part_wither(struct g_geom *gp, int error)
1407 {
1408         struct g_part_entry *entry;
1409         struct g_part_table *table;
1410
1411         table = gp->softc;
1412         if (table != NULL) {
1413                 G_PART_DESTROY(table, NULL);
1414                 while ((entry = LIST_FIRST(&table->gpt_entry)) != NULL) {
1415                         LIST_REMOVE(entry, gpe_entry);
1416                         g_free(entry);
1417                 }
1418                 if (gp->softc != NULL) {
1419                         kobj_delete((kobj_t)gp->softc, M_GEOM);
1420                         gp->softc = NULL;
1421                 }
1422         }
1423         g_wither_geom(gp, error);
1424 }
1425
1426 /*
1427  * Class methods.
1428  */
1429
1430 static void
1431 g_part_ctlreq(struct gctl_req *req, struct g_class *mp, const char *verb)
1432 {
1433         struct g_part_parms gpp;
1434         struct g_part_table *table;
1435         struct gctl_req_arg *ap;
1436         const char *p;
1437         enum g_part_ctl ctlreq;
1438         unsigned int i, mparms, oparms, parm;
1439         int auto_commit, close_on_error;
1440         int error, len, modifies;
1441
1442         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, verb));
1443         g_topology_assert();
1444
1445         ctlreq = G_PART_CTL_NONE;
1446         modifies = 1;
1447         mparms = 0;
1448         oparms = G_PART_PARM_FLAGS | G_PART_PARM_OUTPUT | G_PART_PARM_VERSION;
1449         switch (*verb) {
1450         case 'a':
1451                 if (!strcmp(verb, "add")) {
1452                         ctlreq = G_PART_CTL_ADD;
1453                         mparms |= G_PART_PARM_GEOM | G_PART_PARM_SIZE |
1454                             G_PART_PARM_START | G_PART_PARM_TYPE;
1455                         oparms |= G_PART_PARM_INDEX | G_PART_PARM_LABEL;
1456                 }
1457                 break;
1458         case 'b':
1459                 if (!strcmp(verb, "bootcode")) {
1460                         ctlreq = G_PART_CTL_BOOTCODE;
1461                         mparms |= G_PART_PARM_GEOM | G_PART_PARM_BOOTCODE;
1462                 }
1463                 break;
1464         case 'c':
1465                 if (!strcmp(verb, "commit")) {
1466                         ctlreq = G_PART_CTL_COMMIT;
1467                         mparms |= G_PART_PARM_GEOM;
1468                         modifies = 0;
1469                 } else if (!strcmp(verb, "create")) {
1470                         ctlreq = G_PART_CTL_CREATE;
1471                         mparms |= G_PART_PARM_PROVIDER | G_PART_PARM_SCHEME;
1472                         oparms |= G_PART_PARM_ENTRIES;
1473                 }
1474                 break;
1475         case 'd':
1476                 if (!strcmp(verb, "delete")) {
1477                         ctlreq = G_PART_CTL_DELETE;
1478                         mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1479                 } else if (!strcmp(verb, "destroy")) {
1480                         ctlreq = G_PART_CTL_DESTROY;
1481                         mparms |= G_PART_PARM_GEOM;
1482                         oparms |= G_PART_PARM_FORCE;
1483                 }
1484                 break;
1485         case 'm':
1486                 if (!strcmp(verb, "modify")) {
1487                         ctlreq = G_PART_CTL_MODIFY;
1488                         mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1489                         oparms |= G_PART_PARM_LABEL | G_PART_PARM_TYPE;
1490                 } else if (!strcmp(verb, "move")) {
1491                         ctlreq = G_PART_CTL_MOVE;
1492                         mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX;
1493                 }
1494                 break;
1495         case 'r':
1496                 if (!strcmp(verb, "recover")) {
1497                         ctlreq = G_PART_CTL_RECOVER;
1498                         mparms |= G_PART_PARM_GEOM;
1499                 } else if (!strcmp(verb, "resize")) {
1500                         ctlreq = G_PART_CTL_RESIZE;
1501                         mparms |= G_PART_PARM_GEOM | G_PART_PARM_INDEX |
1502                             G_PART_PARM_SIZE;
1503                 }
1504                 break;
1505         case 's':
1506                 if (!strcmp(verb, "set")) {
1507                         ctlreq = G_PART_CTL_SET;
1508                         mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM |
1509                             G_PART_PARM_INDEX;
1510                 }
1511                 break;
1512         case 'u':
1513                 if (!strcmp(verb, "undo")) {
1514                         ctlreq = G_PART_CTL_UNDO;
1515                         mparms |= G_PART_PARM_GEOM;
1516                         modifies = 0;
1517                 } else if (!strcmp(verb, "unset")) {
1518                         ctlreq = G_PART_CTL_UNSET;
1519                         mparms |= G_PART_PARM_ATTRIB | G_PART_PARM_GEOM |
1520                             G_PART_PARM_INDEX;
1521                 }
1522                 break;
1523         }
1524         if (ctlreq == G_PART_CTL_NONE) {
1525                 gctl_error(req, "%d verb '%s'", EINVAL, verb);
1526                 return;
1527         }
1528
1529         bzero(&gpp, sizeof(gpp));
1530         for (i = 0; i < req->narg; i++) {
1531                 ap = &req->arg[i];
1532                 parm = 0;
1533                 switch (ap->name[0]) {
1534                 case 'a':
1535                         if (!strcmp(ap->name, "attrib"))
1536                                 parm = G_PART_PARM_ATTRIB;
1537                         break;
1538                 case 'b':
1539                         if (!strcmp(ap->name, "bootcode"))
1540                                 parm = G_PART_PARM_BOOTCODE;
1541                         break;
1542                 case 'c':
1543                         if (!strcmp(ap->name, "class"))
1544                                 continue;
1545                         break;
1546                 case 'e':
1547                         if (!strcmp(ap->name, "entries"))
1548                                 parm = G_PART_PARM_ENTRIES;
1549                         break;
1550                 case 'f':
1551                         if (!strcmp(ap->name, "flags"))
1552                                 parm = G_PART_PARM_FLAGS;
1553                         else if (!strcmp(ap->name, "force"))
1554                                 parm = G_PART_PARM_FORCE;
1555                         break;
1556                 case 'g':
1557                         if (!strcmp(ap->name, "geom"))
1558                                 parm = G_PART_PARM_GEOM;
1559                         break;
1560                 case 'i':
1561                         if (!strcmp(ap->name, "index"))
1562                                 parm = G_PART_PARM_INDEX;
1563                         break;
1564                 case 'l':
1565                         if (!strcmp(ap->name, "label"))
1566                                 parm = G_PART_PARM_LABEL;
1567                         break;
1568                 case 'o':
1569                         if (!strcmp(ap->name, "output"))
1570                                 parm = G_PART_PARM_OUTPUT;
1571                         break;
1572                 case 'p':
1573                         if (!strcmp(ap->name, "provider"))
1574                                 parm = G_PART_PARM_PROVIDER;
1575                         break;
1576                 case 's':
1577                         if (!strcmp(ap->name, "scheme"))
1578                                 parm = G_PART_PARM_SCHEME;
1579                         else if (!strcmp(ap->name, "size"))
1580                                 parm = G_PART_PARM_SIZE;
1581                         else if (!strcmp(ap->name, "start"))
1582                                 parm = G_PART_PARM_START;
1583                         break;
1584                 case 't':
1585                         if (!strcmp(ap->name, "type"))
1586                                 parm = G_PART_PARM_TYPE;
1587                         break;
1588                 case 'v':
1589                         if (!strcmp(ap->name, "verb"))
1590                                 continue;
1591                         else if (!strcmp(ap->name, "version"))
1592                                 parm = G_PART_PARM_VERSION;
1593                         break;
1594                 }
1595                 if ((parm & (mparms | oparms)) == 0) {
1596                         gctl_error(req, "%d param '%s'", EINVAL, ap->name);
1597                         return;
1598                 }
1599                 if (parm == G_PART_PARM_BOOTCODE)
1600                         p = gctl_get_param(req, ap->name, &len);
1601                 else
1602                         p = gctl_get_asciiparam(req, ap->name);
1603                 if (p == NULL) {
1604                         gctl_error(req, "%d param '%s'", ENOATTR, ap->name);
1605                         return;
1606                 }
1607                 switch (parm) {
1608                 case G_PART_PARM_ATTRIB:
1609                         error = g_part_parm_str(p, &gpp.gpp_attrib);
1610                         break;
1611                 case G_PART_PARM_BOOTCODE:
1612                         gpp.gpp_codeptr = p;
1613                         gpp.gpp_codesize = len;
1614                         error = 0;
1615                         break;
1616                 case G_PART_PARM_ENTRIES:
1617                         error = g_part_parm_uint(p, &gpp.gpp_entries);
1618                         break;
1619                 case G_PART_PARM_FLAGS:
1620                         if (p[0] == '\0')
1621                                 continue;
1622                         error = g_part_parm_str(p, &gpp.gpp_flags);
1623                         break;
1624                 case G_PART_PARM_FORCE:
1625                         error = g_part_parm_uint(p, &gpp.gpp_force);
1626                         break;
1627                 case G_PART_PARM_GEOM:
1628                         error = g_part_parm_geom(p, &gpp.gpp_geom);
1629                         break;
1630                 case G_PART_PARM_INDEX:
1631                         error = g_part_parm_uint(p, &gpp.gpp_index);
1632                         break;
1633                 case G_PART_PARM_LABEL:
1634                         /* An empty label is always valid. */
1635                         gpp.gpp_label = p;
1636                         error = 0;
1637                         break;
1638                 case G_PART_PARM_OUTPUT:
1639                         error = 0;      /* Write-only parameter */
1640                         break;
1641                 case G_PART_PARM_PROVIDER:
1642                         error = g_part_parm_provider(p, &gpp.gpp_provider);
1643                         break;
1644                 case G_PART_PARM_SCHEME:
1645                         error = g_part_parm_scheme(p, &gpp.gpp_scheme);
1646                         break;
1647                 case G_PART_PARM_SIZE:
1648                         error = g_part_parm_quad(p, &gpp.gpp_size);
1649                         break;
1650                 case G_PART_PARM_START:
1651                         error = g_part_parm_quad(p, &gpp.gpp_start);
1652                         break;
1653                 case G_PART_PARM_TYPE:
1654                         error = g_part_parm_str(p, &gpp.gpp_type);
1655                         break;
1656                 case G_PART_PARM_VERSION:
1657                         error = g_part_parm_uint(p, &gpp.gpp_version);
1658                         break;
1659                 default:
1660                         error = EDOOFUS;
1661                         break;
1662                 }
1663                 if (error) {
1664                         gctl_error(req, "%d %s '%s'", error, ap->name, p);
1665                         return;
1666                 }
1667                 gpp.gpp_parms |= parm;
1668         }
1669         if ((gpp.gpp_parms & mparms) != mparms) {
1670                 parm = mparms - (gpp.gpp_parms & mparms);
1671                 gctl_error(req, "%d param '%x'", ENOATTR, parm);
1672                 return;
1673         }
1674
1675         /* Obtain permissions if possible/necessary. */
1676         close_on_error = 0;
1677         table = NULL;
1678         if (modifies && (gpp.gpp_parms & G_PART_PARM_GEOM)) {
1679                 table = gpp.gpp_geom->softc;
1680                 if (table != NULL && table->gpt_corrupt &&
1681                     ctlreq != G_PART_CTL_DESTROY &&
1682                     ctlreq != G_PART_CTL_RECOVER) {
1683                         gctl_error(req, "%d table '%s' is corrupt",
1684                             EPERM, gpp.gpp_geom->name);
1685                         return;
1686                 }
1687                 if (table != NULL && !table->gpt_opened) {
1688                         error = g_access(LIST_FIRST(&gpp.gpp_geom->consumer),
1689                             1, 1, 1);
1690                         if (error) {
1691                                 gctl_error(req, "%d geom '%s'", error,
1692                                     gpp.gpp_geom->name);
1693                                 return;
1694                         }
1695                         table->gpt_opened = 1;
1696                         close_on_error = 1;
1697                 }
1698         }
1699
1700         /* Allow the scheme to check or modify the parameters. */
1701         if (table != NULL) {
1702                 error = G_PART_PRECHECK(table, ctlreq, &gpp);
1703                 if (error) {
1704                         gctl_error(req, "%d pre-check failed", error);
1705                         goto out;
1706                 }
1707         } else
1708                 error = EDOOFUS;        /* Prevent bogus uninit. warning. */
1709
1710         switch (ctlreq) {
1711         case G_PART_CTL_NONE:
1712                 panic("%s", __func__);
1713         case G_PART_CTL_ADD:
1714                 error = g_part_ctl_add(req, &gpp);
1715                 break;
1716         case G_PART_CTL_BOOTCODE:
1717                 error = g_part_ctl_bootcode(req, &gpp);
1718                 break;
1719         case G_PART_CTL_COMMIT:
1720                 error = g_part_ctl_commit(req, &gpp);
1721                 break;
1722         case G_PART_CTL_CREATE:
1723                 error = g_part_ctl_create(req, &gpp);
1724                 break;
1725         case G_PART_CTL_DELETE:
1726                 error = g_part_ctl_delete(req, &gpp);
1727                 break;
1728         case G_PART_CTL_DESTROY:
1729                 error = g_part_ctl_destroy(req, &gpp);
1730                 break;
1731         case G_PART_CTL_MODIFY:
1732                 error = g_part_ctl_modify(req, &gpp);
1733                 break;
1734         case G_PART_CTL_MOVE:
1735                 error = g_part_ctl_move(req, &gpp);
1736                 break;
1737         case G_PART_CTL_RECOVER:
1738                 error = g_part_ctl_recover(req, &gpp);
1739                 break;
1740         case G_PART_CTL_RESIZE:
1741                 error = g_part_ctl_resize(req, &gpp);
1742                 break;
1743         case G_PART_CTL_SET:
1744                 error = g_part_ctl_setunset(req, &gpp, 1);
1745                 break;
1746         case G_PART_CTL_UNDO:
1747                 error = g_part_ctl_undo(req, &gpp);
1748                 break;
1749         case G_PART_CTL_UNSET:
1750                 error = g_part_ctl_setunset(req, &gpp, 0);
1751                 break;
1752         }
1753
1754         /* Implement automatic commit. */
1755         if (!error) {
1756                 auto_commit = (modifies &&
1757                     (gpp.gpp_parms & G_PART_PARM_FLAGS) &&
1758                     strchr(gpp.gpp_flags, 'C') != NULL) ? 1 : 0;
1759                 if (auto_commit) {
1760                         KASSERT(gpp.gpp_parms & G_PART_PARM_GEOM, (__func__));
1761                         error = g_part_ctl_commit(req, &gpp);
1762                 }
1763         }
1764
1765  out:
1766         if (error && close_on_error) {
1767                 g_access(LIST_FIRST(&gpp.gpp_geom->consumer), -1, -1, -1);
1768                 table->gpt_opened = 0;
1769         }
1770 }
1771
1772 static int
1773 g_part_destroy_geom(struct gctl_req *req, struct g_class *mp,
1774     struct g_geom *gp)
1775 {
1776
1777         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, gp->name));
1778         g_topology_assert();
1779
1780         g_part_wither(gp, EINVAL);
1781         return (0);
1782 }
1783
1784 static struct g_geom *
1785 g_part_taste(struct g_class *mp, struct g_provider *pp, int flags __unused)
1786 {
1787         struct g_consumer *cp;
1788         struct g_geom *gp;
1789         struct g_part_entry *entry;
1790         struct g_part_table *table;
1791         struct root_hold_token *rht;
1792         int attr, depth;
1793         int error;
1794
1795         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s,%s)", __func__, mp->name, pp->name));
1796         g_topology_assert();
1797
1798         /* Skip providers that are already open for writing. */
1799         if (pp->acw > 0)
1800                 return (NULL);
1801
1802         /*
1803          * Create a GEOM with consumer and hook it up to the provider.
1804          * With that we become part of the topology. Optain read access
1805          * to the provider.
1806          */
1807         gp = g_new_geomf(mp, "%s", pp->name);
1808         cp = g_new_consumer(gp);
1809         error = g_attach(cp, pp);
1810         if (error == 0)
1811                 error = g_access(cp, 1, 0, 0);
1812         if (error != 0) {
1813                 if (cp->provider)
1814                         g_detach(cp);
1815                 g_destroy_consumer(cp);
1816                 g_destroy_geom(gp);
1817                 return (NULL);
1818         }
1819
1820         rht = root_mount_hold(mp->name);
1821         g_topology_unlock();
1822
1823         /*
1824          * Short-circuit the whole probing galore when there's no
1825          * media present.
1826          */
1827         if (pp->mediasize == 0 || pp->sectorsize == 0) {
1828                 error = ENODEV;
1829                 goto fail;
1830         }
1831
1832         /* Make sure we can nest and if so, determine our depth. */
1833         error = g_getattr("PART::isleaf", cp, &attr);
1834         if (!error && attr) {
1835                 error = ENODEV;
1836                 goto fail;
1837         }
1838         error = g_getattr("PART::depth", cp, &attr);
1839         depth = (!error) ? attr + 1 : 0;
1840
1841         error = g_part_probe(gp, cp, depth);
1842         if (error)
1843                 goto fail;
1844
1845         table = gp->softc;
1846
1847         /*
1848          * Synthesize a disk geometry. Some partitioning schemes
1849          * depend on it and since some file systems need it even
1850          * when the partitition scheme doesn't, we do it here in
1851          * scheme-independent code.
1852          */
1853         g_part_geometry(table, cp, pp->mediasize / pp->sectorsize);
1854
1855         error = G_PART_READ(table, cp);
1856         if (error)
1857                 goto fail;
1858         error = g_part_check_integrity(table, cp);
1859         if (error)
1860                 goto fail;
1861
1862         g_topology_lock();
1863         LIST_FOREACH(entry, &table->gpt_entry, gpe_entry) {
1864                 if (!entry->gpe_internal)
1865                         g_part_new_provider(gp, table, entry);
1866         }
1867
1868         root_mount_rel(rht);
1869         g_access(cp, -1, 0, 0);
1870         return (gp);
1871
1872  fail:
1873         g_topology_lock();
1874         root_mount_rel(rht);
1875         g_access(cp, -1, 0, 0);
1876         g_detach(cp);
1877         g_destroy_consumer(cp);
1878         g_destroy_geom(gp);
1879         return (NULL);
1880 }
1881
1882 /*
1883  * Geom methods.
1884  */
1885
1886 static int
1887 g_part_access(struct g_provider *pp, int dr, int dw, int de)
1888 {
1889         struct g_consumer *cp;
1890
1891         G_PART_TRACE((G_T_ACCESS, "%s(%s,%d,%d,%d)", __func__, pp->name, dr,
1892             dw, de));
1893
1894         cp = LIST_FIRST(&pp->geom->consumer);
1895
1896         /* We always gain write-exclusive access. */
1897         return (g_access(cp, dr, dw, dw + de));
1898 }
1899
1900 static void
1901 g_part_dumpconf(struct sbuf *sb, const char *indent, struct g_geom *gp,
1902     struct g_consumer *cp, struct g_provider *pp)
1903 {
1904         char buf[64];
1905         struct g_part_entry *entry;
1906         struct g_part_table *table;
1907
1908         KASSERT(sb != NULL && gp != NULL, (__func__));
1909         table = gp->softc;
1910
1911         if (indent == NULL) {
1912                 KASSERT(cp == NULL && pp != NULL, (__func__));
1913                 entry = pp->private;
1914                 if (entry == NULL)
1915                         return;
1916                 sbuf_printf(sb, " i %u o %ju ty %s", entry->gpe_index,
1917                     (uintmax_t)entry->gpe_offset,
1918                     G_PART_TYPE(table, entry, buf, sizeof(buf)));
1919                 /*
1920                  * libdisk compatibility quirk - the scheme dumps the
1921                  * slicer name and partition type in a way that is
1922                  * compatible with libdisk. When libdisk is not used
1923                  * anymore, this should go away.
1924                  */
1925                 G_PART_DUMPCONF(table, entry, sb, indent);
1926         } else if (cp != NULL) {        /* Consumer configuration. */
1927                 KASSERT(pp == NULL, (__func__));
1928                 /* none */
1929         } else if (pp != NULL) {        /* Provider configuration. */
1930                 entry = pp->private;
1931                 if (entry == NULL)
1932                         return;
1933                 sbuf_printf(sb, "%s<start>%ju</start>\n", indent,
1934                     (uintmax_t)entry->gpe_start);
1935                 sbuf_printf(sb, "%s<end>%ju</end>\n", indent,
1936                     (uintmax_t)entry->gpe_end);
1937                 sbuf_printf(sb, "%s<index>%u</index>\n", indent,
1938                     entry->gpe_index);
1939                 sbuf_printf(sb, "%s<type>%s</type>\n", indent,
1940                     G_PART_TYPE(table, entry, buf, sizeof(buf)));
1941                 sbuf_printf(sb, "%s<offset>%ju</offset>\n", indent,
1942                     (uintmax_t)entry->gpe_offset);
1943                 sbuf_printf(sb, "%s<length>%ju</length>\n", indent,
1944                     (uintmax_t)pp->mediasize);
1945                 G_PART_DUMPCONF(table, entry, sb, indent);
1946         } else {                        /* Geom configuration. */
1947                 sbuf_printf(sb, "%s<scheme>%s</scheme>\n", indent,
1948                     table->gpt_scheme->name);
1949                 sbuf_printf(sb, "%s<entries>%u</entries>\n", indent,
1950                     table->gpt_entries);
1951                 sbuf_printf(sb, "%s<first>%ju</first>\n", indent,
1952                     (uintmax_t)table->gpt_first);
1953                 sbuf_printf(sb, "%s<last>%ju</last>\n", indent,
1954                     (uintmax_t)table->gpt_last);
1955                 sbuf_printf(sb, "%s<fwsectors>%u</fwsectors>\n", indent,
1956                     table->gpt_sectors);
1957                 sbuf_printf(sb, "%s<fwheads>%u</fwheads>\n", indent,
1958                     table->gpt_heads);
1959                 sbuf_printf(sb, "%s<state>%s</state>\n", indent,
1960                     table->gpt_corrupt ? "CORRUPT": "OK");
1961                 sbuf_printf(sb, "%s<modified>%s</modified>\n", indent,
1962                     table->gpt_opened ? "true": "false");
1963                 G_PART_DUMPCONF(table, NULL, sb, indent);
1964         }
1965 }
1966
1967 static void
1968 g_part_orphan(struct g_consumer *cp)
1969 {
1970         struct g_provider *pp;
1971         struct g_part_table *table;
1972
1973         pp = cp->provider;
1974         KASSERT(pp != NULL, (__func__));
1975         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, pp->name));
1976         g_topology_assert();
1977
1978         KASSERT(pp->error != 0, (__func__));
1979         table = cp->geom->softc;
1980         if (table != NULL && table->gpt_opened)
1981                 g_access(cp, -1, -1, -1);
1982         g_part_wither(cp->geom, pp->error);
1983 }
1984
1985 static void
1986 g_part_spoiled(struct g_consumer *cp)
1987 {
1988
1989         G_PART_TRACE((G_T_TOPOLOGY, "%s(%s)", __func__, cp->provider->name));
1990         g_topology_assert();
1991
1992         g_part_wither(cp->geom, ENXIO);
1993 }
1994
1995 static void
1996 g_part_start(struct bio *bp)
1997 {
1998         struct bio *bp2;
1999         struct g_consumer *cp;
2000         struct g_geom *gp;
2001         struct g_part_entry *entry;
2002         struct g_part_table *table;
2003         struct g_kerneldump *gkd;
2004         struct g_provider *pp;
2005
2006         pp = bp->bio_to;
2007         gp = pp->geom;
2008         table = gp->softc;
2009         cp = LIST_FIRST(&gp->consumer);
2010
2011         G_PART_TRACE((G_T_BIO, "%s: cmd=%d, provider=%s", __func__, bp->bio_cmd,
2012             pp->name));
2013
2014         entry = pp->private;
2015         if (entry == NULL) {
2016                 g_io_deliver(bp, ENXIO);
2017                 return;
2018         }
2019
2020         switch(bp->bio_cmd) {
2021         case BIO_DELETE:
2022         case BIO_READ:
2023         case BIO_WRITE:
2024                 if (bp->bio_offset >= pp->mediasize) {
2025                         g_io_deliver(bp, EIO);
2026                         return;
2027                 }
2028                 bp2 = g_clone_bio(bp);
2029                 if (bp2 == NULL) {
2030                         g_io_deliver(bp, ENOMEM);
2031                         return;
2032                 }
2033                 if (bp2->bio_offset + bp2->bio_length > pp->mediasize)
2034                         bp2->bio_length = pp->mediasize - bp2->bio_offset;
2035                 bp2->bio_done = g_std_done;
2036                 bp2->bio_offset += entry->gpe_offset;
2037                 g_io_request(bp2, cp);
2038                 return;
2039         case BIO_FLUSH:
2040                 break;
2041         case BIO_GETATTR:
2042                 if (g_handleattr_int(bp, "GEOM::fwheads", table->gpt_heads))
2043                         return;
2044                 if (g_handleattr_int(bp, "GEOM::fwsectors", table->gpt_sectors))
2045                         return;
2046                 if (g_handleattr_int(bp, "PART::isleaf", table->gpt_isleaf))
2047                         return;
2048                 if (g_handleattr_int(bp, "PART::depth", table->gpt_depth))
2049                         return;
2050                 if (g_handleattr_str(bp, "PART::scheme",
2051                     table->gpt_scheme->name))
2052                         return;
2053                 if (!strcmp("GEOM::kerneldump", bp->bio_attribute)) {
2054                         /*
2055                          * Check that the partition is suitable for kernel
2056                          * dumps. Typically only swap partitions should be
2057                          * used. If the request comes from the nested scheme
2058                          * we allow dumping there as well.
2059                          */
2060                         if ((bp->bio_from == NULL ||
2061                             bp->bio_from->geom->class != &g_part_class) &&
2062                             G_PART_DUMPTO(table, entry) == 0) {
2063                                 g_io_deliver(bp, ENODEV);
2064                                 printf("GEOM_PART: Partition '%s' not suitable"
2065                                     " for kernel dumps (wrong type?)\n",
2066                                     pp->name);
2067                                 return;
2068                         }
2069                         gkd = (struct g_kerneldump *)bp->bio_data;
2070                         if (gkd->offset >= pp->mediasize) {
2071                                 g_io_deliver(bp, EIO);
2072                                 return;
2073                         }
2074                         if (gkd->offset + gkd->length > pp->mediasize)
2075                                 gkd->length = pp->mediasize - gkd->offset;
2076                         gkd->offset += entry->gpe_offset;
2077                 }
2078                 break;
2079         default:
2080                 g_io_deliver(bp, EOPNOTSUPP);
2081                 return;
2082         }
2083
2084         bp2 = g_clone_bio(bp);
2085         if (bp2 == NULL) {
2086                 g_io_deliver(bp, ENOMEM);
2087                 return;
2088         }
2089         bp2->bio_done = g_std_done;
2090         g_io_request(bp2, cp);
2091 }
2092
2093 static void
2094 g_part_init(struct g_class *mp)
2095 {
2096
2097         TAILQ_INSERT_HEAD(&g_part_schemes, &g_part_null_scheme, scheme_list);
2098 }
2099
2100 static void
2101 g_part_fini(struct g_class *mp)
2102 {
2103
2104         TAILQ_REMOVE(&g_part_schemes, &g_part_null_scheme, scheme_list);
2105 }
2106
2107 static void
2108 g_part_unload_event(void *arg, int flag)
2109 {
2110         struct g_consumer *cp;
2111         struct g_geom *gp;
2112         struct g_provider *pp;
2113         struct g_part_scheme *scheme;
2114         struct g_part_table *table;
2115         uintptr_t *xchg;
2116         int acc, error;
2117
2118         if (flag == EV_CANCEL)
2119                 return;
2120
2121         xchg = arg;
2122         error = 0;
2123         scheme = (void *)(*xchg);
2124
2125         g_topology_assert();
2126
2127         LIST_FOREACH(gp, &g_part_class.geom, geom) {
2128                 table = gp->softc;
2129                 if (table->gpt_scheme != scheme)
2130                         continue;
2131
2132                 acc = 0;
2133                 LIST_FOREACH(pp, &gp->provider, provider)
2134                         acc += pp->acr + pp->acw + pp->ace;
2135                 LIST_FOREACH(cp, &gp->consumer, consumer)
2136                         acc += cp->acr + cp->acw + cp->ace;
2137
2138                 if (!acc)
2139                         g_part_wither(gp, ENOSYS);
2140                 else
2141                         error = EBUSY;
2142         }
2143
2144         if (!error)
2145                 TAILQ_REMOVE(&g_part_schemes, scheme, scheme_list);
2146
2147         *xchg = error;
2148 }
2149
2150 int
2151 g_part_modevent(module_t mod, int type, struct g_part_scheme *scheme)
2152 {
2153         struct g_part_scheme *iter;
2154         uintptr_t arg;
2155         int error;
2156
2157         error = 0;
2158         switch (type) {
2159         case MOD_LOAD:
2160                 TAILQ_FOREACH(iter, &g_part_schemes, scheme_list) {
2161                         if (scheme == iter) {
2162                                 printf("GEOM_PART: scheme %s is already "
2163                                     "registered!\n", scheme->name);
2164                                 break;
2165                         }
2166                 }
2167                 if (iter == NULL) {
2168                         TAILQ_INSERT_TAIL(&g_part_schemes, scheme,
2169                             scheme_list);
2170                         g_retaste(&g_part_class);
2171                 }
2172                 break;
2173         case MOD_UNLOAD:
2174                 arg = (uintptr_t)scheme;
2175                 error = g_waitfor_event(g_part_unload_event, &arg, M_WAITOK,
2176                     NULL);
2177                 if (error == 0)
2178                         error = arg;
2179                 break;
2180         default:
2181                 error = EOPNOTSUPP;
2182                 break;
2183         }
2184
2185         return (error);
2186 }