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