1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 /* 29 * Just in case we're not in a build environment, make sure that 30 * TEXT_DOMAIN gets set to something. 31 */ 32 #if !defined(TEXT_DOMAIN) 33 #define TEXT_DOMAIN "SYS_TEST" 34 #endif 35 36 /* 37 * change the identity of a metadevice 38 * These are the "do it" functions for the metarename command. 39 */ 40 41 #include <string.h> 42 #include <meta.h> 43 #include <sys/lvm/md_rename.h> 44 45 /* private */ 46 #define FORCE (0x00000001) 47 #define NOISY (0x00000010) 48 #define NOFLIP (0x00000020) 49 #define DRYRUN (0x00000040) 50 51 #define OP_STR(op) \ 52 ((op) == MDRNOP_EXCHANGE? "exchange": \ 53 (op) == MDRNOP_RENAME? "rename": \ 54 (op) == MDRNOP_UNK? "<unknown>": "garbage") 55 56 57 /* 58 * Check if from_np is open 59 * Return 0 if not open, -1 if open 60 */ 61 static int 62 check_open( 63 mdsetname_t *sp, 64 mdname_t *from_np, 65 md_error_t *ep) 66 { 67 int rc; 68 69 if ((rc = meta_isopen(sp, from_np, ep, (mdcmdopts_t)0)) < 0) { 70 assert(!mdisok(ep)); 71 return (-1); 72 73 } else if (rc > 0) { 74 if (mdisok(ep)) { 75 (void) mdmderror(ep, MDE_RENAME_BUSY, 76 meta_getminor(from_np->dev), 77 from_np->cname); 78 } 79 return (-1); 80 } 81 return (0); 82 } 83 84 /* 85 * meta_swap is the common code used by the 86 * meta_rename() and meta_exchange() entry points 87 */ 88 89 static int 90 meta_swap( 91 mdsetname_t *sp, 92 mdname_t *from_np, 93 md_common_t *from_mdp, 94 mdname_t *to_np, 95 md_common_t *to_mdp, 96 md_renop_t op, 97 int flags, 98 md_error_t *ep) 99 { 100 md_rename_t txn; 101 int from_add_flag = 0; 102 int to_add_flag = 0; 103 int from_is_fn, to_is_fn; 104 bool_t from_has_parent, to_has_parent; 105 106 /* 107 * What types of devices we have here? 108 * For MDRNOP_RENAME to_mdp is NULL 109 */ 110 from_is_fn = (from_mdp->revision & MD_FN_META_DEV); 111 from_has_parent = MD_HAS_PARENT(from_mdp->parent); 112 if (to_mdp) { 113 to_is_fn = (to_mdp->revision & MD_FN_META_DEV); 114 to_has_parent = MD_HAS_PARENT(to_mdp->parent); 115 } 116 117 /* 118 * If the device exists a key may already exist so need to find it 119 * otherwise we'll end up adding the key in again which will lead 120 * to an inconsistent n_count for the namespace record. 121 */ 122 if (from_np->dev != NODEV) { 123 (void) meta_getnmentbydev(sp->setno, MD_SIDEWILD, from_np->dev, 124 NULL, NULL, &from_np->key, ep); 125 } 126 127 if (to_np->dev != NODEV) { 128 (void) meta_getnmentbydev(sp->setno, MD_SIDEWILD, to_np->dev, 129 NULL, NULL, &to_np->key, ep); 130 } 131 132 if ((from_np->key == MD_KEYWILD) || (from_np->key == MD_KEYBAD)) { 133 /* 134 * If we are top and revision indicates that we 135 * should have key but we don't then something 136 * really goes wrong 137 */ 138 assert(!from_has_parent && !from_is_fn); 139 140 if (from_has_parent || from_is_fn) { 141 return (-1); 142 } 143 144 /* 145 * So only add the entry if necessary 146 */ 147 if (add_key_name(sp, from_np, NULL, ep) != 0) { 148 assert(!mdisok(ep)); 149 return (-1); 150 } else { 151 from_add_flag = 1; 152 } 153 } 154 155 (void) memset(&txn, 0, sizeof (txn)); 156 157 txn.op = op; 158 txn.revision = MD_RENAME_VERSION; 159 txn.flags = 0; 160 txn.from.mnum = meta_getminor(from_np->dev); 161 txn.from.key = from_np->key; 162 163 if ((to_np->key == MD_KEYWILD) || (to_np->key == MD_KEYBAD)) { 164 /* 165 * If we are top and revision indicates that we 166 * should have key but we don't then something 167 * really goes wrong 168 */ 169 assert(!to_has_parent && !to_is_fn); 170 171 if (to_has_parent || to_is_fn) { 172 return (-1); 173 } 174 175 /* 176 * So only add the entry if necessary 177 */ 178 if (add_key_name(sp, to_np, NULL, ep) != 0) { 179 assert(!mdisok(ep)); 180 if (from_add_flag) 181 (void) del_key_name(sp, from_np, ep); 182 return (-1); 183 } else { 184 to_add_flag = 1; 185 } 186 } 187 188 txn.to.mnum = meta_getminor(to_np->dev); 189 txn.to.key = to_np->key; 190 191 if (flags & NOISY) { 192 (void) fprintf(stderr, "\top: %s\n", OP_STR(txn.op)); 193 (void) fprintf(stderr, "\trevision: %d, flags: %d\n", 194 txn.revision, txn.flags); 195 (void) fprintf(stderr, 196 "\tfrom(mnum,key): %ld, %d\tto: %ld, %d\n", 197 txn.from.mnum, txn.from.key, 198 txn.to.mnum, txn.to.key); 199 } 200 201 mdclrerror(ep); 202 if (metaioctl(MD_IOCRENAME, &txn, &txn.mde, from_np->cname) != 0) { 203 if (from_add_flag) { 204 (void) del_key_name(sp, from_np, ep); 205 /* 206 * Attempt removal of device node 207 */ 208 (void) metaioctl(MD_IOCREM_DEV, &txn.from.mnum, 209 ep, NULL); 210 } 211 212 if (op == MDRNOP_RENAME || to_add_flag) { 213 (void) del_key_name(sp, to_np, ep); 214 /* 215 * Attempt removal of device node 216 */ 217 (void) metaioctl(MD_IOCREM_DEV, &txn.to.mnum, 218 ep, NULL); 219 } 220 221 return (mdstealerror(ep, &txn.mde)); 222 } 223 224 /* 225 * Since now the metadevice can be ref'd in the namespace 226 * by self and by the top device so upon the successful 227 * rename/xchange, we need to check the type and make 228 * necessary adjustment for the device's n_cnt in the namespace 229 * by calling add_key_name/del_key_name to do the tricks 230 */ 231 if (op == MDRNOP_RENAME && from_has_parent) { 232 (void) add_key_name(sp, to_np, NULL, ep); 233 if (from_is_fn) 234 (void) del_key_name(sp, from_np, ep); 235 (void) del_self_name(sp, from_np->key, ep); 236 } 237 238 if (op == MDRNOP_EXCHANGE && from_is_fn) { 239 (void) add_key_name(sp, from_np, NULL, ep); 240 } 241 242 /* force the name cache to re-read device state */ 243 meta_invalidate_name(from_np); 244 meta_invalidate_name(to_np); 245 246 return (0); 247 } 248 249 /* 250 * rename a metadevice 251 */ 252 int 253 meta_rename( 254 mdsetname_t *sp, 255 mdname_t *from_np, 256 mdname_t *to_np, 257 mdcmdopts_t options, 258 md_error_t *ep 259 ) 260 { 261 int flags = (options & MDCMD_FORCE)? FORCE: 0; 262 int rc = 0; 263 char *p; 264 md_common_t *from_mdp; 265 minor_t to_minor = meta_getminor(to_np->dev); 266 md_error_t status = mdnullerror; 267 md_error_t *t_ep = &status; 268 269 /* must have a set */ 270 assert(sp != NULL); 271 assert(sp->setno == MD_MIN2SET(meta_getminor(from_np->dev))); 272 273 mdclrerror(ep); 274 275 if (((p = getenv("MD_DEBUG")) != NULL) && 276 (strstr(p, "RENAME") != NULL)) { 277 flags |= NOISY; 278 } 279 /* if DOIT is not set, we are in dryrun mode */ 280 if ((options & MDCMD_DOIT) == 0) { 281 flags |= DRYRUN; 282 } 283 284 285 if (metachkmeta(from_np, ep) != 0) { 286 assert(!mdisok(ep)); 287 return (-1); 288 } 289 290 mdclrerror(ep); 291 292 if ((from_mdp = meta_get_unit(sp, from_np, ep)) == NULL) { 293 assert(!mdisok(ep)); 294 return (-1); 295 } 296 297 if (meta_get_unit(sp, to_np, ep) != NULL) { 298 if (mdisok(ep)) { 299 (void) mdmderror(ep, MDE_UNIT_ALREADY_SETUP, 300 meta_getminor(to_np->dev), 301 to_np->cname); 302 } 303 return (-1); 304 } 305 mdclrerror(ep); 306 307 /* 308 * The dest device name has been added early on 309 * by meta_init_make_device call so get the entry from 310 * the namespace 311 */ 312 if (meta_getnmentbydev(sp->setno, MD_SIDEWILD, to_np->dev, 313 NULL, NULL, &to_np->key, ep) == NULL) { 314 return (-1); 315 } 316 317 /* If FORCE is not set, check if metadevice is open */ 318 if (!(flags & FORCE)) { 319 if (check_open(sp, from_np, ep) != 0) { 320 (void) del_key_name(sp, to_np, t_ep); 321 (void) metaioctl(MD_IOCREM_DEV, &to_minor, t_ep, NULL); 322 return (-1); 323 } 324 } 325 326 /* 327 * All checks are done, now we do the real work. 328 * If we are in dryrun mode, clear the deivce node 329 * and we are done. 330 */ 331 if (flags & DRYRUN) { 332 (void) del_key_name(sp, to_np, t_ep); 333 (void) metaioctl(MD_IOCREM_DEV, &to_minor, t_ep, NULL); 334 return (0); /* success */ 335 } 336 337 if (to_np->key == MD_KEYBAD || to_np->key == MD_KEYWILD) { 338 assert(!mdisok(ep)); 339 return (-1); 340 } 341 342 rc = meta_swap(sp, from_np, from_mdp, to_np, NULL, MDRNOP_RENAME, 343 flags, ep); 344 345 if (rc == 0) { 346 if (options & MDCMD_PRINT) { 347 (void) fprintf(stdout, dgettext(TEXT_DOMAIN, 348 "%s: has been renamed to %s\n"), 349 from_np->cname, to_np->cname); 350 } 351 } 352 353 return (rc); 354 } 355 356 /* 357 * return TRUE if current <from>, <to> ordering would 358 * prevent <from> from being in the role of <self> 359 */ 360 static bool_t 361 meta_exchange_need_to_flip( 362 md_common_t *from_mdp, 363 md_common_t *to_mdp 364 ) 365 { 366 assert(from_mdp); 367 assert(to_mdp); 368 369 /* 370 * ? 371 * \ 372 * <to> 373 * \ 374 * <from> 375 */ 376 377 if (MD_HAS_PARENT(from_mdp->parent)) { 378 if (MD_HAS_PARENT(to_mdp->parent)) { 379 if (from_mdp->parent == 380 meta_getminor(to_mdp->namep->dev)) { 381 return (TRUE); 382 } 383 } 384 } 385 386 /* 387 * <from> 388 * \ 389 * <to> 390 * \ 391 * ? 392 */ 393 394 if (MD_HAS_PARENT(to_mdp->parent)) { 395 if (to_mdp->capabilities & MD_CAN_META_CHILD) { 396 return (TRUE); 397 } 398 } 399 400 /* 401 * <to> 402 * \ 403 * <from> 404 */ 405 406 if (MD_HAS_PARENT(from_mdp->parent)) { 407 if (from_mdp->parent == meta_getminor(to_mdp->namep->dev)) { 408 if (!(from_mdp->capabilities & MD_CAN_META_CHILD)) { 409 return (TRUE); 410 } 411 } 412 } 413 414 /* 415 * <from> or <to> 416 * \ \ 417 * <to> <from> 418 * \ 419 * ? 420 */ 421 422 return (FALSE); 423 } 424 425 /* 426 * exchange the names of two metadevices 427 */ 428 int 429 meta_exchange( 430 mdsetname_t *sp, 431 mdname_t *from_np, 432 mdname_t *to_np, 433 mdcmdopts_t options, 434 md_error_t *ep 435 ) 436 { 437 int flags = (options & MDCMD_FORCE)? FORCE: 0; 438 md_common_t *from_mdp, *to_mdp; 439 int rc; 440 char *p, *p2; 441 442 /* must have a set */ 443 assert(sp != NULL); 444 assert(sp->setno == MD_MIN2SET(meta_getminor(from_np->dev))); 445 assert(sp->setno == MD_MIN2SET(meta_getminor(to_np->dev))); 446 447 if (metachkmeta(from_np, ep) != 0) { 448 assert(!mdisok(ep)); 449 return (-1); 450 } 451 452 if (metachkmeta(to_np, ep) != 0) { 453 assert(!mdisok(ep)); 454 return (-1); 455 } 456 457 if ((options & MDCMD_DOIT) == 0) { 458 flags |= DRYRUN; 459 } 460 461 if ((p = getenv("MD_DEBUG")) != NULL) { 462 if ((p2 = strstr(p, "EXCHANGE=")) != NULL) { 463 flags |= NOISY; 464 if ((p2 = strchr(p2, '=')) != NULL) { 465 if (strcmp((p2+1), "NOFLIP") == 0) { 466 flags |= NOFLIP; 467 } 468 } 469 } else if (strstr(p, "EXCHANGE") != NULL) { 470 flags |= NOISY; 471 } 472 } 473 474 if ((from_mdp = meta_get_unit(sp, from_np, ep)) == NULL) { 475 assert(!mdisok(ep)); 476 return (-1); 477 } 478 479 if ((to_mdp = meta_get_unit(sp, to_np, ep)) == NULL) { 480 assert(!mdisok(ep)); 481 return (-1); 482 } 483 assert(mdisok(ep)); 484 485 486 /* If FORCE is not set, check if metadevice is open */ 487 if (!(flags & FORCE)) { 488 if (check_open(sp, from_np, ep) != 0) { 489 return (-1); 490 } 491 } 492 493 /* 494 * All checks are done, now we do the real work. 495 * If we are in dryrun mode, we're done. 496 */ 497 if (flags & DRYRUN) { 498 return (0); /* success */ 499 } 500 501 /* 502 * NOFLIP is used only for debugging; the driver 503 * will catch this and return MDE_RENAME_ORDER, if necessary 504 */ 505 if (((flags & NOFLIP) == 0) && 506 meta_exchange_need_to_flip(from_mdp, to_mdp)) { 507 rc = meta_swap(sp, to_np, to_mdp, from_np, from_mdp, 508 MDRNOP_EXCHANGE, flags, ep); 509 510 } else { 511 rc = meta_swap(sp, from_np, from_mdp, to_np, to_mdp, 512 MDRNOP_EXCHANGE, flags, ep); 513 } 514 515 if (rc == 0) { 516 if (options & MDCMD_PRINT) { 517 (void) fprintf(stdout, dgettext(TEXT_DOMAIN, 518 "%s and %s have exchanged identities\n"), 519 from_np->cname, to_np->cname); 520 } 521 } 522 523 return (rc); 524 } 525