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