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