1/*- 2 * Copyright (c) 2004-2005 Pawel Jakub Dawidek <pjd@FreeBSD.org> 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 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include <sys/cdefs.h> 28__FBSDID("$FreeBSD$"); 29 30#include <sys/param.h> 31#include <sys/systm.h> 32#include <sys/kernel.h> 33#include <sys/module.h> 34#include <sys/lock.h> 35#include <sys/mutex.h> 36#include <sys/bio.h> 37#include <sys/ctype.h> 38#include <sys/malloc.h> 39#include <sys/libkern.h> 40#include <sys/sbuf.h> 41#include <sys/stddef.h> 42#include <sys/sysctl.h> 43#include <geom/geom.h> 44#include <geom/geom_slice.h> 45#include <geom/label/g_label.h> 46 47FEATURE(geom_label, "GEOM labeling support"); 48 49SYSCTL_DECL(_kern_geom); 50SYSCTL_NODE(_kern_geom, OID_AUTO, label, CTLFLAG_RW, 0, "GEOM_LABEL stuff"); 51u_int g_label_debug = 0; 52TUNABLE_INT("kern.geom.label.debug", &g_label_debug); 53SYSCTL_UINT(_kern_geom_label, OID_AUTO, debug, CTLFLAG_RW, &g_label_debug, 0, 54 "Debug level"); 55 56static int g_label_destroy_geom(struct gctl_req *req, struct g_class *mp, 57 struct g_geom *gp); 58static int g_label_destroy(struct g_geom *gp, boolean_t force); 59static struct g_geom *g_label_taste(struct g_class *mp, struct g_provider *pp, 60 int flags __unused); 61static void g_label_config(struct gctl_req *req, struct g_class *mp, 62 const char *verb); 63 64struct g_class g_label_class = { 65 .name = G_LABEL_CLASS_NAME, 66 .version = G_VERSION, 67 .ctlreq = g_label_config, 68 .taste = g_label_taste, 69 .destroy_geom = g_label_destroy_geom 70}; 71 72/* 73 * To add a new file system where you want to look for volume labels, 74 * you have to: 75 * 1. Add a file g_label_<file system>.c which implements labels recognition. 76 * 2. Add an 'extern const struct g_label_desc g_label_<file system>;' into 77 * g_label.h file. 78 * 3. Add an element to the table below '&g_label_<file system>,'. 79 * 4. Add your file to sys/conf/files. 80 * 5. Add your file to sys/modules/geom/geom_label/Makefile. 81 * 6. Add your file system to manual page sbin/geom/class/label/glabel.8. 82 */ 83const struct g_label_desc *g_labels[] = { 84 &g_label_ufs_id, 85 &g_label_ufs_volume, 86 &g_label_iso9660, 87 &g_label_msdosfs, 88 &g_label_ext2fs, 89 &g_label_reiserfs, 90 &g_label_ntfs, 91 &g_label_gpt, 92 &g_label_gpt_uuid, 93 &g_label_disk_ident, 94 NULL 95}; 96 97void 98g_label_rtrim(char *label, size_t size) 99{ 100 ptrdiff_t i; 101 102 for (i = size - 1; i >= 0; i--) { 103 if (label[i] == '\0') 104 continue; 105 else if (label[i] == ' ') 106 label[i] = '\0'; 107 else 108 break; 109 } 110} 111 112static int 113g_label_destroy_geom(struct gctl_req *req __unused, struct g_class *mp, 114 struct g_geom *gp __unused) 115{ 116 117 /* 118 * XXX: Unloading a class which is using geom_slice:1.56 is currently 119 * XXX: broken, so we deny unloading when we have geoms. 120 */ 121 return (EOPNOTSUPP); 122} 123 124static void 125g_label_orphan(struct g_consumer *cp) 126{ 127 128 G_LABEL_DEBUG(1, "Label %s removed.", 129 LIST_FIRST(&cp->geom->provider)->name); 130 g_slice_orphan(cp); 131} 132 133static void 134g_label_spoiled(struct g_consumer *cp) 135{ 136 137 G_LABEL_DEBUG(1, "Label %s removed.", 138 LIST_FIRST(&cp->geom->provider)->name); 139 g_slice_spoiled(cp); 140} 141 142static void 143g_label_resize(struct g_consumer *cp) 144{ 145 146 G_LABEL_DEBUG(1, "Label %s resized.", 147 LIST_FIRST(&cp->geom->provider)->name); 148 149 g_slice_config(cp->geom, 0, G_SLICE_CONFIG_FORCE, (off_t)0, 150 cp->provider->mediasize, cp->provider->sectorsize, "notused"); 151} 152 153static int 154g_label_is_name_ok(const char *label) 155{ 156 const char *s; 157 158 /* Check if the label starts from ../ */ 159 if (strncmp(label, "../", 3) == 0) 160 return (0); 161 /* Check if the label contains /../ */ 162 if (strstr(label, "/../") != NULL) 163 return (0); 164 /* Check if the label ends at ../ */ 165 if ((s = strstr(label, "/..")) != NULL && s[3] == '\0') 166 return (0); 167 return (1); 168} 169 170static void 171g_label_mangle_name(char *label, size_t size) 172{ 173 struct sbuf *sb; 174 const u_char *c; 175 176 sb = sbuf_new(NULL, NULL, size, SBUF_FIXEDLEN); 177 for (c = label; *c != '\0'; c++) { 178 if (!isprint(*c) || isspace(*c) || *c =='"' || *c == '%') 179 sbuf_printf(sb, "%%%02X", *c); 180 else 181 sbuf_putc(sb, *c); 182 } 183 if (sbuf_finish(sb) != 0) 184 label[0] = '\0'; 185 else 186 strlcpy(label, sbuf_data(sb), size); 187 sbuf_delete(sb); 188} 189 190static struct g_geom * 191g_label_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp, 192 const char *label, const char *dir, off_t mediasize) 193{ 194 struct g_geom *gp; 195 struct g_provider *pp2; 196 struct g_consumer *cp; 197 char name[64]; 198 199 g_topology_assert(); 200 201 if (!g_label_is_name_ok(label)) { 202 G_LABEL_DEBUG(0, "%s contains suspicious label, skipping.", 203 pp->name); 204 G_LABEL_DEBUG(1, "%s suspicious label is: %s", pp->name, label); 205 if (req != NULL) 206 gctl_error(req, "Label name %s is invalid.", label); 207 return (NULL); 208 } 209 gp = NULL; 210 cp = NULL; 211 snprintf(name, sizeof(name), "%s/%s", dir, label); 212 LIST_FOREACH(gp, &mp->geom, geom) { 213 pp2 = LIST_FIRST(&gp->provider); 214 if (pp2 == NULL) 215 continue; 216 if ((pp2->flags & G_PF_ORPHAN) != 0) 217 continue; 218 if (strcmp(pp2->name, name) == 0) { 219 G_LABEL_DEBUG(1, "Label %s(%s) already exists (%s).", 220 label, name, pp->name); 221 if (req != NULL) { 222 gctl_error(req, "Provider %s already exists.", 223 name); 224 } 225 return (NULL); 226 } 227 } 228 gp = g_slice_new(mp, 1, pp, &cp, NULL, 0, NULL); 229 if (gp == NULL) { 230 G_LABEL_DEBUG(0, "Cannot create slice %s.", label); 231 if (req != NULL) 232 gctl_error(req, "Cannot create slice %s.", label); 233 return (NULL); 234 } 235 gp->orphan = g_label_orphan; 236 gp->spoiled = g_label_spoiled; 237 gp->resize = g_label_resize; 238 g_access(cp, -1, 0, 0); 239 g_slice_config(gp, 0, G_SLICE_CONFIG_SET, (off_t)0, mediasize, 240 pp->sectorsize, "%s", name); 241 G_LABEL_DEBUG(1, "Label for provider %s is %s.", pp->name, name); 242 return (gp); 243} 244 245static int 246g_label_destroy(struct g_geom *gp, boolean_t force) 247{ 248 struct g_provider *pp; 249 250 g_topology_assert(); 251 pp = LIST_FIRST(&gp->provider); 252 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 253 if (force) { 254 G_LABEL_DEBUG(0, "Provider %s is still open, so it " 255 "can't be definitely removed.", pp->name); 256 } else { 257 G_LABEL_DEBUG(1, 258 "Provider %s is still open (r%dw%de%d).", pp->name, 259 pp->acr, pp->acw, pp->ace); 260 return (EBUSY); 261 } 262 } else if (pp != NULL) 263 G_LABEL_DEBUG(1, "Label %s removed.", pp->name); 264 g_slice_spoiled(LIST_FIRST(&gp->consumer)); 265 return (0); 266} 267 268static int 269g_label_read_metadata(struct g_consumer *cp, struct g_label_metadata *md) 270{ 271 struct g_provider *pp; 272 u_char *buf; 273 int error; 274 275 g_topology_assert(); 276 277 pp = cp->provider; 278 g_topology_unlock(); 279 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 280 &error); 281 g_topology_lock(); 282 if (buf == NULL) 283 return (error); 284 /* Decode metadata. */ 285 label_metadata_decode(buf, md); 286 g_free(buf); 287 288 return (0); 289} 290 291static void 292g_label_orphan_taste(struct g_consumer *cp __unused) 293{ 294 295 KASSERT(1 == 0, ("%s called?", __func__)); 296} 297 298static void 299g_label_start_taste(struct bio *bp __unused) 300{ 301 302 KASSERT(1 == 0, ("%s called?", __func__)); 303} 304 305static int 306g_label_access_taste(struct g_provider *pp __unused, int dr __unused, 307 int dw __unused, int de __unused) 308{ 309 310 KASSERT(1 == 0, ("%s called", __func__)); 311 return (EOPNOTSUPP); 312} 313 314static struct g_geom * 315g_label_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 316{ 317 struct g_label_metadata md; 318 struct g_consumer *cp; 319 struct g_geom *gp; 320 int i; 321 322 g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 323 g_topology_assert(); 324 325 G_LABEL_DEBUG(2, "Tasting %s.", pp->name); 326 327 /* Skip providers that are already open for writing. */ 328 if (pp->acw > 0) 329 return (NULL); 330 331 if (strcmp(pp->geom->class->name, mp->name) == 0) 332 return (NULL); 333 334 gp = g_new_geomf(mp, "label:taste"); 335 gp->start = g_label_start_taste; 336 gp->access = g_label_access_taste; 337 gp->orphan = g_label_orphan_taste; 338 cp = g_new_consumer(gp); 339 g_attach(cp, pp); 340 if (g_access(cp, 1, 0, 0) != 0) 341 goto end; 342 do { 343 if (g_label_read_metadata(cp, &md) != 0) 344 break; 345 if (strcmp(md.md_magic, G_LABEL_MAGIC) != 0) 346 break; 347 if (md.md_version > G_LABEL_VERSION) { 348 printf("geom_label.ko module is too old to handle %s.\n", 349 pp->name); 350 break; 351 } 352 353 /* 354 * Backward compatibility: 355 */ 356 /* 357 * There was no md_provsize field in earlier versions of 358 * metadata. 359 */ 360 if (md.md_version < 2) 361 md.md_provsize = pp->mediasize; 362 363 if (md.md_provsize != pp->mediasize) 364 break; 365 366 g_label_create(NULL, mp, pp, md.md_label, G_LABEL_DIR, 367 pp->mediasize - pp->sectorsize); 368 } while (0); 369 for (i = 0; g_labels[i] != NULL; i++) { 370 char label[128]; 371 372 if (g_labels[i]->ld_enabled == 0) 373 continue; 374 g_topology_unlock(); 375 g_labels[i]->ld_taste(cp, label, sizeof(label)); 376 g_label_mangle_name(label, sizeof(label)); 377 g_topology_lock(); 378 if (label[0] == '\0') 379 continue; 380 g_label_create(NULL, mp, pp, label, g_labels[i]->ld_dir, 381 pp->mediasize); 382 } 383 g_access(cp, -1, 0, 0); 384end: 385 g_detach(cp); 386 g_destroy_consumer(cp); 387 g_destroy_geom(gp); 388 return (NULL); 389} 390 391static void 392g_label_ctl_create(struct gctl_req *req, struct g_class *mp) 393{ 394 struct g_provider *pp; 395 const char *name; 396 int *nargs; 397 398 g_topology_assert(); 399 400 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 401 if (nargs == NULL) { 402 gctl_error(req, "No '%s' argument", "nargs"); 403 return; 404 } 405 if (*nargs != 2) { 406 gctl_error(req, "Invalid number of arguments."); 407 return; 408 } 409 /* 410 * arg1 is the name of provider. 411 */ 412 name = gctl_get_asciiparam(req, "arg1"); 413 if (name == NULL) { 414 gctl_error(req, "No 'arg%d' argument", 1); 415 return; 416 } 417 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 418 name += strlen("/dev/"); 419 pp = g_provider_by_name(name); 420 if (pp == NULL) { 421 G_LABEL_DEBUG(1, "Provider %s is invalid.", name); 422 gctl_error(req, "Provider %s is invalid.", name); 423 return; 424 } 425 /* 426 * arg0 is the label. 427 */ 428 name = gctl_get_asciiparam(req, "arg0"); 429 if (name == NULL) { 430 gctl_error(req, "No 'arg%d' argument", 0); 431 return; 432 } 433 g_label_create(req, mp, pp, name, G_LABEL_DIR, pp->mediasize); 434} 435 436static const char * 437g_label_skip_dir(const char *name) 438{ 439 char path[64]; 440 u_int i; 441 442 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 443 name += strlen("/dev/"); 444 if (strncmp(name, G_LABEL_DIR "/", strlen(G_LABEL_DIR "/")) == 0) 445 name += strlen(G_LABEL_DIR "/"); 446 for (i = 0; g_labels[i] != NULL; i++) { 447 snprintf(path, sizeof(path), "%s/", g_labels[i]->ld_dir); 448 if (strncmp(name, path, strlen(path)) == 0) { 449 name += strlen(path); 450 break; 451 } 452 } 453 return (name); 454} 455 456static struct g_geom * 457g_label_find_geom(struct g_class *mp, const char *name) 458{ 459 struct g_geom *gp; 460 struct g_provider *pp; 461 const char *pname; 462 463 name = g_label_skip_dir(name); 464 LIST_FOREACH(gp, &mp->geom, geom) { 465 pp = LIST_FIRST(&gp->provider); 466 pname = g_label_skip_dir(pp->name); 467 if (strcmp(pname, name) == 0) 468 return (gp); 469 } 470 return (NULL); 471} 472 473static void 474g_label_ctl_destroy(struct gctl_req *req, struct g_class *mp) 475{ 476 int *nargs, *force, error, i; 477 struct g_geom *gp; 478 const char *name; 479 char param[16]; 480 481 g_topology_assert(); 482 483 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 484 if (nargs == NULL) { 485 gctl_error(req, "No '%s' argument", "nargs"); 486 return; 487 } 488 if (*nargs <= 0) { 489 gctl_error(req, "Missing device(s)."); 490 return; 491 } 492 force = gctl_get_paraml(req, "force", sizeof(*force)); 493 if (force == NULL) { 494 gctl_error(req, "No 'force' argument"); 495 return; 496 } 497 498 for (i = 0; i < *nargs; i++) { 499 snprintf(param, sizeof(param), "arg%d", i); 500 name = gctl_get_asciiparam(req, param); 501 if (name == NULL) { 502 gctl_error(req, "No 'arg%d' argument", i); 503 return; 504 } 505 gp = g_label_find_geom(mp, name); 506 if (gp == NULL) { 507 G_LABEL_DEBUG(1, "Label %s is invalid.", name); 508 gctl_error(req, "Label %s is invalid.", name); 509 return; 510 } 511 error = g_label_destroy(gp, *force); 512 if (error != 0) { 513 gctl_error(req, "Cannot destroy label %s (error=%d).", 514 LIST_FIRST(&gp->provider)->name, error); 515 return; 516 } 517 } 518} 519 520static void 521g_label_config(struct gctl_req *req, struct g_class *mp, const char *verb) 522{ 523 uint32_t *version; 524 525 g_topology_assert(); 526 527 version = gctl_get_paraml(req, "version", sizeof(*version)); 528 if (version == NULL) { 529 gctl_error(req, "No '%s' argument.", "version"); 530 return; 531 } 532 if (*version != G_LABEL_VERSION) { 533 gctl_error(req, "Userland and kernel parts are out of sync."); 534 return; 535 } 536 537 if (strcmp(verb, "create") == 0) { 538 g_label_ctl_create(req, mp); 539 return; 540 } else if (strcmp(verb, "destroy") == 0 || 541 strcmp(verb, "stop") == 0) { 542 g_label_ctl_destroy(req, mp); 543 return; 544 } 545 546 gctl_error(req, "Unknown verb."); 547} 548 549DECLARE_GEOM_CLASS(g_label_class, g_label); 550