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