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