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 "opt_geom.h" 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/kernel.h> 35 #include <sys/module.h> 36 #include <sys/lock.h> 37 #include <sys/mutex.h> 38 #include <sys/bio.h> 39 #include <sys/ctype.h> 40 #include <sys/malloc.h> 41 #include <sys/libkern.h> 42 #include <sys/sbuf.h> 43 #include <sys/sysctl.h> 44 #include <geom/geom.h> 45 #include <geom/geom_slice.h> 46 #include <geom/label/g_label.h> 47 48 FEATURE(geom_label, "GEOM labeling support"); 49 50 SYSCTL_DECL(_kern_geom); 51 SYSCTL_NODE(_kern_geom, OID_AUTO, label, CTLFLAG_RW, 0, "GEOM_LABEL stuff"); 52 u_int g_label_debug = 0; 53 SYSCTL_UINT(_kern_geom_label, OID_AUTO, debug, CTLFLAG_RWTUN, &g_label_debug, 0, 54 "Debug level"); 55 56 static int g_label_destroy_geom(struct gctl_req *req, struct g_class *mp, 57 struct g_geom *gp); 58 static int g_label_destroy(struct g_geom *gp, boolean_t force); 59 static struct g_geom *g_label_taste(struct g_class *mp, struct g_provider *pp, 60 int flags __unused); 61 static void g_label_config(struct gctl_req *req, struct g_class *mp, 62 const char *verb); 63 64 struct 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 */ 83 const struct g_label_desc *g_labels[] = { 84 &g_label_gpt, 85 &g_label_gpt_uuid, 86 #ifdef GEOM_LABEL 87 &g_label_ufs_id, 88 &g_label_ufs_volume, 89 &g_label_iso9660, 90 &g_label_msdosfs, 91 &g_label_ext2fs, 92 &g_label_reiserfs, 93 &g_label_ntfs, 94 &g_label_disk_ident, 95 #endif 96 NULL 97 }; 98 99 100 static int 101 g_label_destroy_geom(struct gctl_req *req __unused, struct g_class *mp, 102 struct g_geom *gp __unused) 103 { 104 105 /* 106 * XXX: Unloading a class which is using geom_slice:1.56 is currently 107 * XXX: broken, so we deny unloading when we have geoms. 108 */ 109 return (EOPNOTSUPP); 110 } 111 112 static void 113 g_label_orphan(struct g_consumer *cp) 114 { 115 116 G_LABEL_DEBUG(1, "Label %s removed.", 117 LIST_FIRST(&cp->geom->provider)->name); 118 g_slice_orphan(cp); 119 } 120 121 static void 122 g_label_spoiled(struct g_consumer *cp) 123 { 124 125 G_LABEL_DEBUG(1, "Label %s removed.", 126 LIST_FIRST(&cp->geom->provider)->name); 127 g_slice_spoiled(cp); 128 } 129 130 static void 131 g_label_resize(struct g_consumer *cp) 132 { 133 134 G_LABEL_DEBUG(1, "Label %s resized.", 135 LIST_FIRST(&cp->geom->provider)->name); 136 137 g_slice_config(cp->geom, 0, G_SLICE_CONFIG_FORCE, (off_t)0, 138 cp->provider->mediasize, cp->provider->sectorsize, "notused"); 139 } 140 141 static int 142 g_label_is_name_ok(const char *label) 143 { 144 const char *s; 145 146 /* Check if the label starts from ../ */ 147 if (strncmp(label, "../", 3) == 0) 148 return (0); 149 /* Check if the label contains /../ */ 150 if (strstr(label, "/../") != NULL) 151 return (0); 152 /* Check if the label ends at ../ */ 153 if ((s = strstr(label, "/..")) != NULL && s[3] == '\0') 154 return (0); 155 return (1); 156 } 157 158 static void 159 g_label_mangle_name(char *label, size_t size) 160 { 161 struct sbuf *sb; 162 const u_char *c; 163 164 sb = sbuf_new(NULL, NULL, size, SBUF_FIXEDLEN); 165 for (c = label; *c != '\0'; c++) { 166 if (!isprint(*c) || isspace(*c) || *c =='"' || *c == '%') 167 sbuf_printf(sb, "%%%02X", *c); 168 else 169 sbuf_putc(sb, *c); 170 } 171 if (sbuf_finish(sb) != 0) 172 label[0] = '\0'; 173 else 174 strlcpy(label, sbuf_data(sb), size); 175 sbuf_delete(sb); 176 } 177 178 static struct g_geom * 179 g_label_create(struct gctl_req *req, struct g_class *mp, struct g_provider *pp, 180 const char *label, const char *dir, off_t mediasize) 181 { 182 struct g_geom *gp; 183 struct g_provider *pp2; 184 struct g_consumer *cp; 185 char name[64]; 186 187 g_topology_assert(); 188 189 if (!g_label_is_name_ok(label)) { 190 G_LABEL_DEBUG(0, "%s contains suspicious label, skipping.", 191 pp->name); 192 G_LABEL_DEBUG(1, "%s suspicious label is: %s", pp->name, label); 193 if (req != NULL) 194 gctl_error(req, "Label name %s is invalid.", label); 195 return (NULL); 196 } 197 gp = NULL; 198 cp = NULL; 199 snprintf(name, sizeof(name), "%s/%s", dir, label); 200 LIST_FOREACH(gp, &mp->geom, geom) { 201 pp2 = LIST_FIRST(&gp->provider); 202 if (pp2 == NULL) 203 continue; 204 if ((pp2->flags & G_PF_ORPHAN) != 0) 205 continue; 206 if (strcmp(pp2->name, name) == 0) { 207 G_LABEL_DEBUG(1, "Label %s(%s) already exists (%s).", 208 label, name, pp->name); 209 if (req != NULL) { 210 gctl_error(req, "Provider %s already exists.", 211 name); 212 } 213 return (NULL); 214 } 215 } 216 gp = g_slice_new(mp, 1, pp, &cp, NULL, 0, NULL); 217 if (gp == NULL) { 218 G_LABEL_DEBUG(0, "Cannot create slice %s.", label); 219 if (req != NULL) 220 gctl_error(req, "Cannot create slice %s.", label); 221 return (NULL); 222 } 223 gp->orphan = g_label_orphan; 224 gp->spoiled = g_label_spoiled; 225 gp->resize = g_label_resize; 226 g_access(cp, -1, 0, 0); 227 g_slice_config(gp, 0, G_SLICE_CONFIG_SET, (off_t)0, mediasize, 228 pp->sectorsize, "%s", name); 229 G_LABEL_DEBUG(1, "Label for provider %s is %s.", pp->name, name); 230 return (gp); 231 } 232 233 static int 234 g_label_destroy(struct g_geom *gp, boolean_t force) 235 { 236 struct g_provider *pp; 237 238 g_topology_assert(); 239 pp = LIST_FIRST(&gp->provider); 240 if (pp != NULL && (pp->acr != 0 || pp->acw != 0 || pp->ace != 0)) { 241 if (force) { 242 G_LABEL_DEBUG(0, "Provider %s is still open, so it " 243 "can't be definitely removed.", pp->name); 244 } else { 245 G_LABEL_DEBUG(1, 246 "Provider %s is still open (r%dw%de%d).", pp->name, 247 pp->acr, pp->acw, pp->ace); 248 return (EBUSY); 249 } 250 } else if (pp != NULL) 251 G_LABEL_DEBUG(1, "Label %s removed.", pp->name); 252 g_slice_spoiled(LIST_FIRST(&gp->consumer)); 253 return (0); 254 } 255 256 static int 257 g_label_read_metadata(struct g_consumer *cp, struct g_label_metadata *md) 258 { 259 struct g_provider *pp; 260 u_char *buf; 261 int error; 262 263 g_topology_assert(); 264 265 pp = cp->provider; 266 g_topology_unlock(); 267 buf = g_read_data(cp, pp->mediasize - pp->sectorsize, pp->sectorsize, 268 &error); 269 g_topology_lock(); 270 if (buf == NULL) 271 return (error); 272 /* Decode metadata. */ 273 label_metadata_decode(buf, md); 274 g_free(buf); 275 276 return (0); 277 } 278 279 static void 280 g_label_orphan_taste(struct g_consumer *cp __unused) 281 { 282 283 KASSERT(1 == 0, ("%s called?", __func__)); 284 } 285 286 static void 287 g_label_start_taste(struct bio *bp __unused) 288 { 289 290 KASSERT(1 == 0, ("%s called?", __func__)); 291 } 292 293 static int 294 g_label_access_taste(struct g_provider *pp __unused, int dr __unused, 295 int dw __unused, int de __unused) 296 { 297 298 KASSERT(1 == 0, ("%s called", __func__)); 299 return (EOPNOTSUPP); 300 } 301 302 static struct g_geom * 303 g_label_taste(struct g_class *mp, struct g_provider *pp, int flags __unused) 304 { 305 struct g_label_metadata md; 306 struct g_consumer *cp; 307 struct g_geom *gp; 308 int i; 309 310 g_trace(G_T_TOPOLOGY, "%s(%s, %s)", __func__, mp->name, pp->name); 311 g_topology_assert(); 312 313 G_LABEL_DEBUG(2, "Tasting %s.", pp->name); 314 315 /* Skip providers that are already open for writing. */ 316 if (pp->acw > 0) 317 return (NULL); 318 319 if (strcmp(pp->geom->class->name, mp->name) == 0) 320 return (NULL); 321 322 gp = g_new_geomf(mp, "label:taste"); 323 gp->start = g_label_start_taste; 324 gp->access = g_label_access_taste; 325 gp->orphan = g_label_orphan_taste; 326 cp = g_new_consumer(gp); 327 g_attach(cp, pp); 328 if (g_access(cp, 1, 0, 0) != 0) 329 goto end; 330 do { 331 if (g_label_read_metadata(cp, &md) != 0) 332 break; 333 if (strcmp(md.md_magic, G_LABEL_MAGIC) != 0) 334 break; 335 if (md.md_version > G_LABEL_VERSION) { 336 printf("geom_label.ko module is too old to handle %s.\n", 337 pp->name); 338 break; 339 } 340 341 /* 342 * Backward compatibility: 343 */ 344 /* 345 * There was no md_provsize field in earlier versions of 346 * metadata. 347 */ 348 if (md.md_version < 2) 349 md.md_provsize = pp->mediasize; 350 351 if (md.md_provsize != pp->mediasize) 352 break; 353 354 g_label_create(NULL, mp, pp, md.md_label, G_LABEL_DIR, 355 pp->mediasize - pp->sectorsize); 356 } while (0); 357 for (i = 0; g_labels[i] != NULL; i++) { 358 char label[128]; 359 360 if (g_labels[i]->ld_enabled == 0) 361 continue; 362 g_topology_unlock(); 363 g_labels[i]->ld_taste(cp, label, sizeof(label)); 364 g_label_mangle_name(label, sizeof(label)); 365 g_topology_lock(); 366 if (label[0] == '\0') 367 continue; 368 g_label_create(NULL, mp, pp, label, g_labels[i]->ld_dir, 369 pp->mediasize); 370 } 371 g_access(cp, -1, 0, 0); 372 end: 373 g_detach(cp); 374 g_destroy_consumer(cp); 375 g_destroy_geom(gp); 376 return (NULL); 377 } 378 379 static void 380 g_label_ctl_create(struct gctl_req *req, struct g_class *mp) 381 { 382 struct g_provider *pp; 383 const char *name; 384 int *nargs; 385 386 g_topology_assert(); 387 388 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 389 if (nargs == NULL) { 390 gctl_error(req, "No '%s' argument", "nargs"); 391 return; 392 } 393 if (*nargs != 2) { 394 gctl_error(req, "Invalid number of arguments."); 395 return; 396 } 397 /* 398 * arg1 is the name of provider. 399 */ 400 name = gctl_get_asciiparam(req, "arg1"); 401 if (name == NULL) { 402 gctl_error(req, "No 'arg%d' argument", 1); 403 return; 404 } 405 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 406 name += strlen("/dev/"); 407 pp = g_provider_by_name(name); 408 if (pp == NULL) { 409 G_LABEL_DEBUG(1, "Provider %s is invalid.", name); 410 gctl_error(req, "Provider %s is invalid.", name); 411 return; 412 } 413 /* 414 * arg0 is the label. 415 */ 416 name = gctl_get_asciiparam(req, "arg0"); 417 if (name == NULL) { 418 gctl_error(req, "No 'arg%d' argument", 0); 419 return; 420 } 421 g_label_create(req, mp, pp, name, G_LABEL_DIR, pp->mediasize); 422 } 423 424 static const char * 425 g_label_skip_dir(const char *name) 426 { 427 char path[64]; 428 u_int i; 429 430 if (strncmp(name, "/dev/", strlen("/dev/")) == 0) 431 name += strlen("/dev/"); 432 if (strncmp(name, G_LABEL_DIR "/", strlen(G_LABEL_DIR "/")) == 0) 433 name += strlen(G_LABEL_DIR "/"); 434 for (i = 0; g_labels[i] != NULL; i++) { 435 snprintf(path, sizeof(path), "%s/", g_labels[i]->ld_dir); 436 if (strncmp(name, path, strlen(path)) == 0) { 437 name += strlen(path); 438 break; 439 } 440 } 441 return (name); 442 } 443 444 static struct g_geom * 445 g_label_find_geom(struct g_class *mp, const char *name) 446 { 447 struct g_geom *gp; 448 struct g_provider *pp; 449 const char *pname; 450 451 name = g_label_skip_dir(name); 452 LIST_FOREACH(gp, &mp->geom, geom) { 453 pp = LIST_FIRST(&gp->provider); 454 pname = g_label_skip_dir(pp->name); 455 if (strcmp(pname, name) == 0) 456 return (gp); 457 } 458 return (NULL); 459 } 460 461 static void 462 g_label_ctl_destroy(struct gctl_req *req, struct g_class *mp) 463 { 464 int *nargs, *force, error, i; 465 struct g_geom *gp; 466 const char *name; 467 char param[16]; 468 469 g_topology_assert(); 470 471 nargs = gctl_get_paraml(req, "nargs", sizeof(*nargs)); 472 if (nargs == NULL) { 473 gctl_error(req, "No '%s' argument", "nargs"); 474 return; 475 } 476 if (*nargs <= 0) { 477 gctl_error(req, "Missing device(s)."); 478 return; 479 } 480 force = gctl_get_paraml(req, "force", sizeof(*force)); 481 if (force == NULL) { 482 gctl_error(req, "No 'force' argument"); 483 return; 484 } 485 486 for (i = 0; i < *nargs; i++) { 487 snprintf(param, sizeof(param), "arg%d", i); 488 name = gctl_get_asciiparam(req, param); 489 if (name == NULL) { 490 gctl_error(req, "No 'arg%d' argument", i); 491 return; 492 } 493 gp = g_label_find_geom(mp, name); 494 if (gp == NULL) { 495 G_LABEL_DEBUG(1, "Label %s is invalid.", name); 496 gctl_error(req, "Label %s is invalid.", name); 497 return; 498 } 499 error = g_label_destroy(gp, *force); 500 if (error != 0) { 501 gctl_error(req, "Cannot destroy label %s (error=%d).", 502 LIST_FIRST(&gp->provider)->name, error); 503 return; 504 } 505 } 506 } 507 508 static void 509 g_label_config(struct gctl_req *req, struct g_class *mp, const char *verb) 510 { 511 uint32_t *version; 512 513 g_topology_assert(); 514 515 version = gctl_get_paraml(req, "version", sizeof(*version)); 516 if (version == NULL) { 517 gctl_error(req, "No '%s' argument.", "version"); 518 return; 519 } 520 if (*version != G_LABEL_VERSION) { 521 gctl_error(req, "Userland and kernel parts are out of sync."); 522 return; 523 } 524 525 if (strcmp(verb, "create") == 0) { 526 g_label_ctl_create(req, mp); 527 return; 528 } else if (strcmp(verb, "destroy") == 0 || 529 strcmp(verb, "stop") == 0) { 530 g_label_ctl_destroy(req, mp); 531 return; 532 } 533 534 gctl_error(req, "Unknown verb."); 535 } 536 537 DECLARE_GEOM_CLASS(g_label_class, g_label); 538