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_self_name(sp, from_np->key, ep); 235 } 236 237 if (op == MDRNOP_EXCHANGE && from_is_fn) { 238 (void) add_key_name(sp, from_np, NULL, ep); 239 } 240 241 /* force the name cache to re-read device state */ 242 meta_invalidate_name(from_np); 243 meta_invalidate_name(to_np); 244 245 return (0); 246 } 247 248 /* 249 * rename a metadevice 250 */ 251 int 252 meta_rename( 253 mdsetname_t *sp, 254 mdname_t *from_np, 255 mdname_t *to_np, 256 mdcmdopts_t options, 257 md_error_t *ep 258 ) 259 { 260 int flags = (options & MDCMD_FORCE)? FORCE: 0; 261 int rc = 0; 262 char *p; 263 md_common_t *from_mdp; 264 minor_t to_minor = meta_getminor(to_np->dev); 265 md_error_t status = mdnullerror; 266 md_error_t *t_ep = &status; 267 268 /* must have a set */ 269 assert(sp != NULL); 270 assert(sp->setno == MD_MIN2SET(meta_getminor(from_np->dev))); 271 272 mdclrerror(ep); 273 274 if (((p = getenv("MD_DEBUG")) != NULL) && 275 (strstr(p, "RENAME") != NULL)) { 276 flags |= NOISY; 277 } 278 /* if DOIT is not set, we are in dryrun mode */ 279 if ((options & MDCMD_DOIT) == 0) { 280 flags |= DRYRUN; 281 } 282 283 284 if (metachkmeta(from_np, ep) != 0) { 285 assert(!mdisok(ep)); 286 return (-1); 287 } 288 289 mdclrerror(ep); 290 291 if ((from_mdp = meta_get_unit(sp, from_np, ep)) == NULL) { 292 assert(!mdisok(ep)); 293 return (-1); 294 } 295 296 if (meta_get_unit(sp, to_np, ep) != NULL) { 297 if (mdisok(ep)) { 298 (void) mdmderror(ep, MDE_UNIT_ALREADY_SETUP, 299 meta_getminor(to_np->dev), 300 to_np->cname); 301 } 302 return (-1); 303 } 304 mdclrerror(ep); 305 306 /* 307 * The dest device name has been added early on 308 * by meta_init_make_device call so get the entry from 309 * the namespace 310 */ 311 if (meta_getnmentbydev(sp->setno, MD_SIDEWILD, to_np->dev, 312 NULL, NULL, &to_np->key, ep) == NULL) { 313 return (-1); 314 } 315 316 /* If FORCE is not set, check if metadevice is open */ 317 if (!(flags & FORCE)) { 318 if (check_open(sp, from_np, ep) != 0) { 319 (void) del_key_name(sp, to_np, t_ep); 320 (void) metaioctl(MD_IOCREM_DEV, &to_minor, t_ep, NULL); 321 return (-1); 322 } 323 } 324 325 /* 326 * All checks are done, now we do the real work. 327 * If we are in dryrun mode, clear the deivce node 328 * and we are done. 329 */ 330 if (flags & DRYRUN) { 331 (void) del_key_name(sp, to_np, t_ep); 332 (void) metaioctl(MD_IOCREM_DEV, &to_minor, t_ep, NULL); 333 return (0); /* success */ 334 } 335 336 if (to_np->key == MD_KEYBAD || to_np->key == MD_KEYWILD) { 337 assert(!mdisok(ep)); 338 return (-1); 339 } 340 341 rc = meta_swap(sp, from_np, from_mdp, to_np, NULL, MDRNOP_RENAME, 342 flags, ep); 343 344 if (rc == 0) { 345 if (options & MDCMD_PRINT) { 346 (void) fprintf(stdout, dgettext(TEXT_DOMAIN, 347 "%s: has been renamed to %s\n"), 348 from_np->cname, to_np->cname); 349 } 350 } 351 352 return (rc); 353 } 354 355 /* 356 * return TRUE if current <from>, <to> ordering would 357 * prevent <from> from being in the role of <self> 358 */ 359 static bool_t 360 meta_exchange_need_to_flip( 361 md_common_t *from_mdp, 362 md_common_t *to_mdp 363 ) 364 { 365 assert(from_mdp); 366 assert(to_mdp); 367 368 /* 369 * ? 370 * \ 371 * <to> 372 * \ 373 * <from> 374 */ 375 376 if (MD_HAS_PARENT(from_mdp->parent)) { 377 if (MD_HAS_PARENT(to_mdp->parent)) { 378 if (from_mdp->parent == 379 meta_getminor(to_mdp->namep->dev)) { 380 return (TRUE); 381 } 382 } 383 } 384 385 /* 386 * <from> 387 * \ 388 * <to> 389 * \ 390 * ? 391 */ 392 393 if (MD_HAS_PARENT(to_mdp->parent)) { 394 if (to_mdp->capabilities & MD_CAN_META_CHILD) { 395 return (TRUE); 396 } 397 } 398 399 /* 400 * <to> 401 * \ 402 * <from> 403 */ 404 405 if (MD_HAS_PARENT(from_mdp->parent)) { 406 if (from_mdp->parent == meta_getminor(to_mdp->namep->dev)) { 407 if (!(from_mdp->capabilities & MD_CAN_META_CHILD)) { 408 return (TRUE); 409 } 410 } 411 } 412 413 /* 414 * <from> or <to> 415 * \ \ 416 * <to> <from> 417 * \ 418 * ? 419 */ 420 421 return (FALSE); 422 } 423 424 /* 425 * exchange the names of two metadevices 426 */ 427 int 428 meta_exchange( 429 mdsetname_t *sp, 430 mdname_t *from_np, 431 mdname_t *to_np, 432 mdcmdopts_t options, 433 md_error_t *ep 434 ) 435 { 436 int flags = (options & MDCMD_FORCE)? FORCE: 0; 437 md_common_t *from_mdp, *to_mdp; 438 int rc; 439 char *p, *p2; 440 441 /* must have a set */ 442 assert(sp != NULL); 443 assert(sp->setno == MD_MIN2SET(meta_getminor(from_np->dev))); 444 assert(sp->setno == MD_MIN2SET(meta_getminor(to_np->dev))); 445 446 if (metachkmeta(from_np, ep) != 0) { 447 assert(!mdisok(ep)); 448 return (-1); 449 } 450 451 if (metachkmeta(to_np, ep) != 0) { 452 assert(!mdisok(ep)); 453 return (-1); 454 } 455 456 if ((options & MDCMD_DOIT) == 0) { 457 flags |= DRYRUN; 458 } 459 460 if ((p = getenv("MD_DEBUG")) != NULL) { 461 if ((p2 = strstr(p, "EXCHANGE=")) != NULL) { 462 flags |= NOISY; 463 if ((p2 = strchr(p2, '=')) != NULL) { 464 if (strcmp((p2+1), "NOFLIP") == 0) { 465 flags |= NOFLIP; 466 } 467 } 468 } else if (strstr(p, "EXCHANGE") != NULL) { 469 flags |= NOISY; 470 } 471 } 472 473 if ((from_mdp = meta_get_unit(sp, from_np, ep)) == NULL) { 474 assert(!mdisok(ep)); 475 return (-1); 476 } 477 478 if ((to_mdp = meta_get_unit(sp, to_np, ep)) == NULL) { 479 assert(!mdisok(ep)); 480 return (-1); 481 } 482 assert(mdisok(ep)); 483 484 485 /* If FORCE is not set, check if metadevice is open */ 486 if (!(flags & FORCE)) { 487 if (check_open(sp, from_np, ep) != 0) { 488 return (-1); 489 } 490 } 491 492 /* 493 * All checks are done, now we do the real work. 494 * If we are in dryrun mode, we're done. 495 */ 496 if (flags & DRYRUN) { 497 return (0); /* success */ 498 } 499 500 /* 501 * NOFLIP is used only for debugging; the driver 502 * will catch this and return MDE_RENAME_ORDER, if necessary 503 */ 504 if (((flags & NOFLIP) == 0) && 505 meta_exchange_need_to_flip(from_mdp, to_mdp)) { 506 rc = meta_swap(sp, to_np, to_mdp, from_np, from_mdp, 507 MDRNOP_EXCHANGE, flags, ep); 508 509 } else { 510 rc = meta_swap(sp, from_np, from_mdp, to_np, to_mdp, 511 MDRNOP_EXCHANGE, flags, ep); 512 } 513 514 if (rc == 0) { 515 if (options & MDCMD_PRINT) { 516 (void) fprintf(stdout, dgettext(TEXT_DOMAIN, 517 "%s and %s have exchanged identities\n"), 518 from_np->cname, to_np->cname); 519 } 520 } 521 522 return (rc); 523 } 524