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