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
check_open(mdsetname_t * sp,mdname_t * from_np,md_error_t * ep)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
meta_swap(mdsetname_t * sp,mdname_t * from_np,md_common_t * from_mdp,mdname_t * to_np,md_common_t * to_mdp,md_renop_t op,int flags,md_error_t * ep)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
meta_rename(mdsetname_t * sp,mdname_t * from_np,mdname_t * to_np,mdcmdopts_t options,md_error_t * ep)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
meta_exchange_need_to_flip(md_common_t * from_mdp,md_common_t * to_mdp)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
meta_exchange(mdsetname_t * sp,mdname_t * from_np,mdname_t * to_np,mdcmdopts_t options,md_error_t * ep)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