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