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