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