xref: /illumos-gate/usr/src/cmd/format/menu_defect.c (revision 77c0a660417a046bfab6c8ef58d00c181c0264b3)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright (c) 2016 by Delphix. All rights reserved.
25  */
26 
27 /*
28  * This file contains functions to implement the defect menu commands.
29  */
30 #include "global.h"
31 #include <unistd.h>
32 #include <string.h>
33 #include "misc.h"
34 #include "menu_defect.h"
35 #include "param.h"
36 #include "ctlr_scsi.h"
37 
38 /*
39  * This is the working defect list.  All the commands here operate on
40  * the working list, except for 'commit'.  This way the user can
41  * change their mind at any time without having mangled the current defect
42  * list.
43  */
44 struct	defect_list work_list;
45 
46 static int	commit_list(void);
47 
48 /*
49  * This routine implements the 'restore' command.  It sets the working
50  * list equal to the current list.
51  */
52 int
53 d_restore(void)
54 {
55 	int	i;
56 
57 	assert(!EMBEDDED_SCSI);
58 
59 	/*
60 	 * If the working list has not been modified, there's nothing to do.
61 	 */
62 	if (!(work_list.flags & LIST_DIRTY)) {
63 		err_print("working list was not modified.\n");
64 		return (0);
65 	}
66 	/*
67 	 * Make sure the user is serious.
68 	 */
69 	if (check("Ready to update working list, continue"))
70 		return (-1);
71 	/*
72 	 * Lock out interrupts so the lists can't get mangled.
73 	 */
74 	enter_critical();
75 	/*
76 	 * Kill off the old working list.
77 	 */
78 	kill_deflist(&work_list);
79 	/*
80 	 * If the current isn't null, set the working list to be a
81 	 * copy of it.
82 	 */
83 	if (cur_list.list != NULL) {
84 		work_list.header = cur_list.header;
85 		work_list.list = (struct defect_entry *)zalloc(
86 		    deflist_size(cur_blksz, work_list.header.count) *
87 		    cur_blksz);
88 		for (i = 0; i < work_list.header.count; i++)
89 			*(work_list.list + i) = *(cur_list.list + i);
90 	}
91 	/*
92 	 * Initialize the flags since they are now in sync.
93 	 */
94 	work_list.flags = 0;
95 	if (work_list.list == NULL)
96 		fmt_print("working list set to null.\n\n");
97 	else
98 		fmt_print("working list updated, total of %d defects.\n\n",
99 		    work_list.header.count);
100 	exit_critical();
101 	return (0);
102 }
103 
104 /*
105  * This routine implements the 'original' command.  It extracts the
106  * manufacturer's defect list from the current disk.
107  */
108 int
109 d_original(void)
110 {
111 	int	status;
112 
113 
114 	/*
115 	 * If the controller does not support the extraction, we're out
116 	 * of luck.
117 	 */
118 	if (cur_ops->op_ex_man == NULL) {
119 		err_print("Controller does not support extracting ");
120 		err_print("manufacturer's defect list.\n");
121 		return (-1);
122 	}
123 	/*
124 	 * Make sure the user is serious.  Note, for SCSI disks
125 	 * since this is instantaneous, we will just do it and
126 	 * not ask for confirmation.
127 	 */
128 	if (!(cur_ctype->ctype_flags & CF_CONFIRM) &&
129 	    check(
130 "Ready to update working list. This cannot be interrupted\n\
131 and may take a long while. Continue"))
132 		return (-1);
133 	/*
134 	 * Lock out interrupts so we don't get half the list.
135 	 */
136 	enter_critical();
137 	/*
138 	 * Kill off the working list.
139 	 */
140 	kill_deflist(&work_list);
141 	fmt_print("Extracting manufacturer's defect list...");
142 	/*
143 	 * Do the extraction.
144 	 */
145 	status = (*cur_ops->op_ex_man)(&work_list);
146 	if (status)
147 		fmt_print("Extraction failed.\n\n");
148 	else {
149 		fmt_print("Extraction complete.\n");
150 		fmt_print("Working list updated, total of %d defects.\n\n",
151 		    work_list.header.count);
152 	}
153 	/*
154 	 * Mark the working list dirty since we modified it.
155 	 */
156 	work_list.flags |= LIST_DIRTY;
157 	exit_critical();
158 	/*
159 	 * Return status.
160 	 */
161 	return (status);
162 }
163 
164 /*
165  * This routine implements the 'extract' command.  It extracts the
166  * entire defect list from the current disk.
167  */
168 int
169 d_extract(void)
170 {
171 	int	status;
172 
173 	/*
174 	 * If the controller does not support the extraction, we are out
175 	 * of luck.
176 	 */
177 	if (cur_ops->op_ex_cur == NULL) {
178 		err_print("Controller does not support extracting ");
179 		err_print("current defect list.\n");
180 		return (-1);
181 	}
182 
183 	/*
184 	 * If disk is unformatted, you really shouldn't do this.
185 	 * However, ask user to be sure.
186 	 */
187 	if (! (cur_flags & DISK_FORMATTED) &&
188 	    (check(
189 "Cannot extract defect list from an unformatted disk. Continue")))
190 		return (-1);
191 
192 	/*
193 	 * If this takes a long time, let's ask the user if they
194 	 * doesn't mind waiting.  Note, for SCSI disks
195 	 * this operation is instantaneous so we won't ask for
196 	 * for confirmation.
197 	 */
198 	if (! (cur_ctype->ctype_flags & CF_CONFIRM) &&
199 	    check(
200 "Ready to extract working list. This cannot be interrupted\n\
201 and may take a long while. Continue"))
202 		return (-1);
203 	/*
204 	 * Lock out interrupts so we don't get half the list and
205 	 * Kill off the working list.
206 	 */
207 	enter_critical();
208 	kill_deflist(&work_list);
209 	fmt_print("Extracting defect list...");
210 
211 	/*
212 	 * Do the extraction.
213 	 */
214 	status = (*cur_ops->op_ex_cur)(&work_list);
215 	if (status) {
216 		if (!EMBEDDED_SCSI) {
217 			if (cur_flags & DISK_FORMATTED)
218 				read_list(&work_list);
219 
220 			if (work_list.list != NULL) {
221 				status = 0;
222 				fmt_print("Extraction complete.\n");
223 				fmt_print(
224 "Working list updated, total of %d defects.\n\n",
225 				    work_list.header.count);
226 			} else {
227 				fmt_print("Extraction failed.\n\n");
228 			}
229 		} else {
230 			fmt_print("Extraction failed.\n\n");
231 		}
232 	} else {
233 		fmt_print("Extraction complete.\n");
234 		fmt_print("Working list updated, total of %d defects.\n\n",
235 		    work_list.header.count);
236 	}
237 	/*
238 	 * Mark the working list dirty since we modified it.
239 	 */
240 	work_list.flags |= LIST_DIRTY;
241 	exit_critical();
242 	/*
243 	 * Return status.
244 	 */
245 	return (status);
246 }
247 
248 /*
249  * This routine implements the 'add' command.  It allows the user to
250  * enter the working defect list manually.  It loops infinitely until
251  * the user breaks out with a ctrl-C.
252  */
253 int
254 d_add(void)
255 {
256 	int			type, deflt, index;
257 	diskaddr_t		bn;
258 	u_ioparam_t		ioparam;
259 	struct defect_entry	def;
260 
261 	assert(!EMBEDDED_SCSI);
262 
263 	/*
264 	 * Ask the user which mode of input they'd like to use.
265 	 */
266 	fmt_print("        0. bytes-from-index\n");
267 	fmt_print("        1. logical block\n");
268 	deflt = 0;
269 	ioparam.io_bounds.lower = 0;
270 	ioparam.io_bounds.upper = 1;
271 	type = input(FIO_INT, "Select input format (enter its number)", ':',
272 	    &ioparam, &deflt, DATA_INPUT);
273 	fmt_print("\nEnter Control-C to terminate.\n");
274 loop:
275 	if (type) {
276 		/*
277 		 * Mode selected is logical block.  Input the defective block
278 		 * and fill in the defect entry with the info.
279 		 */
280 		def.bfi = def.nbits = UNKNOWN;
281 		ioparam.io_bounds.lower = 0;
282 		if (cur_disk->label_type == L_TYPE_SOLARIS) {
283 			ioparam.io_bounds.upper = physsects() - 1;
284 		} else {
285 			ioparam.io_bounds.upper = cur_parts->etoc->efi_last_lba;
286 		}
287 		bn = input(FIO_BN, "Enter defective block number", ':',
288 		    &ioparam, NULL, DATA_INPUT);
289 		def.cyl = bn2c(bn);
290 		def.head = bn2h(bn);
291 		def.sect = bn2s(bn);
292 	} else {
293 		/*
294 		 * Mode selected is bytes-from-index.  Input the information
295 		 * about the defect and fill in the defect entry.
296 		 */
297 		def.sect = UNKNOWN;
298 		ioparam.io_bounds.lower = 0;
299 		ioparam.io_bounds.upper = pcyl - 1;
300 		def.cyl = input(FIO_INT,
301 		    "Enter defect's cylinder number", ':',
302 		    &ioparam, NULL, DATA_INPUT);
303 		ioparam.io_bounds.upper = nhead - 1;
304 		def.head = input(FIO_INT, "Enter defect's head number",
305 		    ':', &ioparam, NULL, DATA_INPUT);
306 		ioparam.io_bounds.upper = cur_dtype->dtype_bpt - 1;
307 		def.bfi = input(FIO_INT, "Enter defect's bytes-from-index",
308 		    ':', &ioparam, NULL, DATA_INPUT);
309 		ioparam.io_bounds.lower = -1;
310 		ioparam.io_bounds.upper = (cur_dtype->dtype_bpt - def.bfi) * 8;
311 		if (ioparam.io_bounds.upper >= 32 * 1024)
312 			ioparam.io_bounds.upper = 32 * 1024 - 1;
313 		/*
314 		 * Note: a length of -1 means the length is not known.  We
315 		 * make this the default value.
316 		 */
317 		deflt = -1;
318 		def.nbits = input(FIO_INT, "Enter defect's length (in bits)",
319 		    ':', &ioparam, &deflt, DATA_INPUT);
320 	}
321 	/*
322 	 * Calculate where in the defect list this defect belongs
323 	 * and print it out.
324 	 */
325 	index = sort_defect(&def, &work_list);
326 	fmt_print(DEF_PRINTHEADER);
327 	pr_defect(&def, index);
328 
329 	/*
330 	 * Lock out interrupts so lists don't get mangled.
331 	 * Also, mark the working list dirty since we are modifying it.
332 	 */
333 	enter_critical();
334 	work_list.flags |= LIST_DIRTY;
335 	/*
336 	 * If the list is null, create it with zero length.  This is
337 	 * necessary because the routines to add a defect to the list
338 	 * assume the list is initialized.
339 	 */
340 	if (work_list.list == NULL) {
341 		work_list.header.magicno = (uint_t)DEFECT_MAGIC;
342 		work_list.header.count = 0;
343 		work_list.list = (struct defect_entry *)zalloc(
344 		    deflist_size(cur_blksz, 0) * cur_blksz);
345 	}
346 	/*
347 	 * Add the defect to the working list.
348 	 */
349 	add_def(&def, &work_list, index);
350 	fmt_print("defect number %d added.\n\n", index + 1);
351 	exit_critical();
352 	/*
353 	 * Loop back for the next defect.
354 	 */
355 	goto loop;
356 	/*NOTREACHED*/
357 #ifdef	lint
358 	return (0);
359 #endif
360 }
361 
362 /*
363  * This routine implements the 'delete' command.  It allows the user
364  * to manually delete a defect from the working list.
365  */
366 int
367 d_delete(void)
368 {
369 	int		i, count, num;
370 	u_ioparam_t	ioparam;
371 
372 	assert(!EMBEDDED_SCSI);
373 
374 	/*
375 	 * If the working list is null or zero length, there's nothing
376 	 * to delete.
377 	 */
378 	count = work_list.header.count;
379 	if (work_list.list == NULL || count == 0) {
380 		err_print("No defects to delete.\n");
381 		return (-1);
382 	}
383 	/*
384 	 * Ask the user which defect should be deleted. Bounds are off by
385 	 * one because user sees a one-relative list.
386 	 */
387 	ioparam.io_bounds.lower = 1;
388 	ioparam.io_bounds.upper = count;
389 	num = input(FIO_INT, "Specify defect to be deleted (enter its number)",
390 	    ':', &ioparam, NULL, DATA_INPUT);
391 	/*
392 	 *
393 	 * The user thinks it's one relative but it's not really.
394 	 */
395 	--num;
396 	/*
397 	 * Print the defect selected and ask the user for confirmation.
398 	 */
399 	fmt_print(DEF_PRINTHEADER);
400 	pr_defect(work_list.list + num, num);
401 	/*
402 	 * Lock out interrupts so the lists don't get mangled.
403 	 */
404 	enter_critical();
405 	/*
406 	 * Move down all the defects beyond the one deleted so the defect
407 	 * list is still fully populated.
408 	 */
409 	for (i = num; i < count - 1; i++)
410 		*(work_list.list + i) = *(work_list.list + i + 1);
411 	/*
412 	 * If the size of the list in sectors has changed, reallocate
413 	 * the list to shrink it appropriately.
414 	 */
415 	if (deflist_size(cur_blksz, count - 1) <
416 	    deflist_size(cur_blksz, count))
417 		work_list.list = (struct defect_entry *)rezalloc(
418 		    (void *)work_list.list,
419 		    deflist_size(cur_blksz, count - 1) * cur_blksz);
420 	/*
421 	 * Decrement the defect count.
422 	 */
423 	work_list.header.count--;
424 	/*
425 	 * Recalculate the list's checksum.
426 	 */
427 	(void) checkdefsum(&work_list, CK_MAKESUM);
428 	/*
429 	 * Mark the working list dirty since we modified it.
430 	 */
431 	work_list.flags |= LIST_DIRTY;
432 	fmt_print("defect number %d deleted.\n\n", ++num);
433 	exit_critical();
434 	return (0);
435 }
436 
437 /*
438  * This routine implements the 'print' command.  It prints the working
439  * defect list out in human-readable format.
440  */
441 int
442 d_print(void)
443 {
444 	int	i, nomore = 0;
445 	int	c, one_line = 0;
446 	int	tty_lines = get_tty_lines();
447 
448 	/*
449 	 * If the working list is null, there's nothing to print.
450 	 */
451 	if (work_list.list == NULL) {
452 		if (EMBEDDED_SCSI)
453 			err_print(
454 "No list defined,extract primary or grown or both defects list first.\n");
455 		else
456 			err_print("No working list defined.\n");
457 		return (-1);
458 	}
459 	/*
460 	 * If we're running from a file, don't use the paging scheme.
461 	 * If we are running interactive, turn off echoing.
462 	 */
463 	if (option_f || (!isatty(0)) || (!isatty(1)))
464 		nomore++;
465 	else {
466 		enter_critical();
467 		echo_off();
468 		charmode_on();
469 		exit_critical();
470 	}
471 	/* Print out the banner. */
472 	if (work_list.header.count != 0)
473 		fmt_print(DEF_PRINTHEADER);
474 
475 	/*
476 	 * Loop through the each defect in the working list.
477 	 */
478 	for (i = 0; i < work_list.header.count; i++) {
479 		/*
480 		 * If we are paging and hit the end of a page, wait for
481 		 * the user to hit either space-bar, "q", or return
482 		 * before going on.
483 		 */
484 		if (one_line ||
485 		    (!nomore && ((i + 1) % (tty_lines - 1) == 0))) {
486 			/*
487 			 * Get the next character.
488 			 */
489 			fmt_print("- hit space for more - ");
490 			c = getchar();
491 			fmt_print("\015");
492 			one_line = 0;
493 			/* Handle display one line command (return key) */
494 			if (c == '\012') {
495 				one_line++;
496 			}
497 			/* Handle Quit command */
498 			if (c == 'q') {
499 				fmt_print("                       \015");
500 				goto PRINT_EXIT;
501 			}
502 			/* Handle ^D */
503 			if (c == '\004')
504 				fullabort();
505 		}
506 		/*
507 		 * Print the defect.
508 		 */
509 		pr_defect(work_list.list + i, i);
510 	}
511 	fmt_print("total of %d defects.\n\n", i);
512 	/*
513 	 * If we were doing paging, turn echoing back on.
514 	 */
515 PRINT_EXIT:
516 	if (!nomore) {
517 		enter_critical();
518 		charmode_off();
519 		echo_on();
520 		exit_critical();
521 	}
522 	return (0);
523 }
524 
525 /*
526  * This routine implements the 'dump' command.  It writes the working
527  * defect list to a file.
528  */
529 int
530 d_dump(void)
531 {
532 	int	i, status = 0;
533 	char	*str;
534 	FILE	*fptr;
535 	struct	defect_entry *dptr;
536 
537 	/*
538 	 * If the working list is null, there's nothing to do.
539 	 */
540 	if (work_list.list == NULL) {
541 		if (EMBEDDED_SCSI)
542 			err_print(
543 "No list defined,extract primary or grown or both defects list first.\n");
544 		else
545 			err_print("No working list defined.\n");
546 		return (-1);
547 	}
548 	/*
549 	 * Ask the user for the name of the defect file.  Note that the
550 	 * input will be in malloc'd space since we are inputting
551 	 * type OSTR.
552 	 */
553 	str = (char *)(uintptr_t)input(FIO_OSTR, "Enter name of defect file",
554 	    ':', NULL, NULL, DATA_INPUT);
555 	/*
556 	 * Lock out interrupts so the file doesn't get half written.
557 	 */
558 	enter_critical();
559 	/*
560 	 * Open the file for writing.
561 	 */
562 	if ((fptr = fopen(str, "w+")) == NULL) {
563 		err_print("unable to open defect file.\n");
564 		status = -1;
565 		goto out;
566 	}
567 	/*
568 	 * Print a header containing the magic number, count, and checksum.
569 	 */
570 	(void) fprintf(fptr, "0x%08x%8d  0x%08x\n",
571 	    work_list.header.magicno,
572 	    work_list.header.count, work_list.header.cksum);
573 	/*
574 	 * Loop through each defect in the working list.  Write the
575 	 * defect info to the defect file.
576 	 */
577 	for (i = 0; i < work_list.header.count; i++) {
578 		dptr = work_list.list + i;
579 		(void) fprintf(fptr, "%4d%8d%7d%8d%8d%8d\n",
580 		    i+1, dptr->cyl, dptr->head,
581 		    dptr->bfi, dptr->nbits, dptr->sect);
582 	}
583 	fmt_print("defect file updated, total of %d defects.\n", i);
584 	/*
585 	 * Close the defect file.
586 	 */
587 	(void) fclose(fptr);
588 out:
589 	/*
590 	 * Destroy the string used for the file name.
591 	 */
592 	destroy_data(str);
593 	exit_critical();
594 	fmt_print("\n");
595 	return (status);
596 }
597 
598 /*
599  * This routine implements the 'load' command.  It reads the working
600  * list in from a file.
601  */
602 int
603 d_load(void)
604 {
605 	int	i, items, status = 0, count, cksum;
606 	uint_t	magicno;
607 	char	*str;
608 	TOKEN	filename;
609 	FILE	*fptr;
610 	struct	defect_entry *dptr;
611 
612 	assert(!EMBEDDED_SCSI);
613 
614 	/*
615 	 * Ask the user for the name of the defect file.  Note that the
616 	 * input will be malloc'd space since we inputted type OSTR.
617 	 */
618 	str = (char *)(uintptr_t)input(FIO_OSTR, "Enter name of defect file",
619 	    ':', NULL, NULL, DATA_INPUT);
620 	/*
621 	 * Copy the file name into local space then destroy the string
622 	 * it came in.  This is simply a precaution against later having
623 	 * to remember to destroy this space.
624 	 */
625 	enter_critical();
626 	(void) strcpy(filename, str);
627 	destroy_data(str);
628 	exit_critical();
629 	/*
630 	 * See if the defect file is accessable.  If not, we can't load
631 	 * from it.  We do this here just so we can get out before asking
632 	 * the user for confirmation.
633 	 */
634 	status = access(filename, 4);
635 	if (status) {
636 		err_print("defect file not accessable.\n");
637 		return (-1);
638 	}
639 	/*
640 	 * Make sure the user is serious.
641 	 */
642 	if (check("ready to update working list, continue"))
643 		return (-1);
644 	/*
645 	 * Lock out interrupts so the list doesn't get half loaded.
646 	 */
647 	enter_critical();
648 	/*
649 	 * Open the defect file.
650 	 */
651 	if ((fptr = fopen(filename, "r")) == NULL) {
652 		err_print("unable to open defect file.\n");
653 		exit_critical();
654 		return (-1);
655 	}
656 	/*
657 	 * Scan in the header.
658 	 */
659 	items = fscanf(fptr, "0x%x%d  0x%x\n", &magicno,
660 	    &count, (uint_t *)&cksum);
661 	/*
662 	 * If the header is wrong, this isn't a good defect file.
663 	 */
664 	if (items != 3 || count < 0 ||
665 	    (magicno != (uint_t)DEFECT_MAGIC &&
666 	    magicno != (uint_t)NO_CHECKSUM)) {
667 		err_print("Defect file is corrupted.\n");
668 		status = -1;
669 		goto out;
670 	}
671 	/*
672 	 * Kill off any old defects in the working list.
673 	 */
674 	kill_deflist(&work_list);
675 	/*
676 	 * Load the working list header with the header info.
677 	 */
678 	if (magicno == NO_CHECKSUM)
679 		work_list.header.magicno = (uint_t)DEFECT_MAGIC;
680 	else
681 		work_list.header.magicno = magicno;
682 	work_list.header.count = count;
683 	work_list.header.cksum = cksum;
684 	/*
685 	 * Allocate space for the new list.
686 	 */
687 	work_list.list = (struct defect_entry *)zalloc(
688 	    deflist_size(cur_blksz, count) * cur_blksz);
689 	/*
690 	 * Mark the working list dirty since we are modifying it.
691 	 */
692 	work_list.flags |= LIST_DIRTY;
693 	/*
694 	 * Loop through each defect in the defect file.
695 	 */
696 	for (i = 0; i < count; i++) {
697 		dptr = work_list.list + i;
698 		/*
699 		 * Scan the info into the defect entry.
700 		 */
701 		items = fscanf(fptr, "%*d%hd%hd%d%hd%hd\n", &dptr->cyl,
702 		    &dptr->head, &dptr->bfi, &dptr->nbits, &dptr->sect);
703 		/*
704 		 * If it didn't scan right, give up.
705 		 */
706 		if (items != 5)
707 			goto bad;
708 	}
709 	/*
710 	 * Check to be sure the checksum from the defect file was correct
711 	 * unless there wasn't supposed to be a checksum.
712 	 * If there was supposed to be a valid checksum and there isn't
713 	 * then give up.
714 	 */
715 	if (magicno != NO_CHECKSUM && checkdefsum(&work_list, CK_CHECKSUM))
716 		goto bad;
717 	fmt_print("working list updated, total of %d defects.\n", i);
718 	goto out;
719 
720 bad:
721 	/*
722 	 * Some kind of error occurred.  Kill off the working list and
723 	 * mark the status bad.
724 	 */
725 	err_print("Defect file is corrupted, working list set to NULL.\n");
726 	kill_deflist(&work_list);
727 	status = -1;
728 out:
729 	/*
730 	 * Close the defect file.
731 	 */
732 	(void) fclose(fptr);
733 	exit_critical();
734 	fmt_print("\n");
735 	return (status);
736 }
737 
738 /*
739  * This routine implements the 'commit' command.  It causes the current
740  * defect list to be set equal to the working defect list.  It is the only
741  * way that changes made to the working list can actually take effect in
742  * the next format.
743  */
744 int
745 d_commit(void)
746 {
747 	/*
748 	 * If the working list wasn't modified, no commit is necessary.
749 	 */
750 	if (work_list.list != NULL && !(work_list.flags & LIST_DIRTY)) {
751 		err_print("working list was not modified.\n");
752 		return (0);
753 	}
754 
755 	/*
756 	 * Make sure the user is serious.
757 	 */
758 	if (check("Ready to update Current Defect List, continue"))
759 		return (-1);
760 	return (do_commit());
761 }
762 
763 int
764 do_commit(void)
765 {
766 	int	status;
767 
768 	if ((status = commit_list()) == 0) {
769 		/*
770 		 * Remind the user to format the drive, since changing
771 		 * the list does nothing unless a format is performed.
772 		 */
773 		fmt_print(\
774 "Disk must be reformatted for changes to take effect.\n\n");
775 	}
776 	return (status);
777 }
778 
779 
780 static int
781 commit_list(void)
782 {
783 	int	i;
784 
785 	/*
786 	 * Lock out interrupts so the list doesn't get half copied.
787 	 */
788 	enter_critical();
789 	/*
790 	 * Kill off any current defect list.
791 	 */
792 	kill_deflist(&cur_list);
793 	/*
794 	 * If the working list is null, initialize it to zero length.
795 	 * This is so the user can do a commit on a null list and get
796 	 * a zero length list.  Otherwise there would be no way to get
797 	 * a zero length list conveniently.
798 	 */
799 	if (work_list.list == NULL) {
800 		work_list.header.magicno = (uint_t)DEFECT_MAGIC;
801 		work_list.header.count = 0;
802 		work_list.list = (struct defect_entry *)zalloc(
803 		    deflist_size(cur_blksz, 0) * cur_blksz);
804 	}
805 	/*
806 	 * Copy the working list into the current list.
807 	 */
808 	cur_list.header = work_list.header;
809 	cur_list.list = (struct defect_entry *)zalloc(
810 	    deflist_size(cur_blksz, cur_list.header.count) * cur_blksz);
811 	for (i = 0; i < cur_list.header.count; i++)
812 		*(cur_list.list + i) = *(work_list.list + i);
813 	/*
814 	 * Mark the working list clean, since it is now the same as the
815 	 * current list.  Note we do not mark the current list dirty,
816 	 * even though it has been changed.  This is because it does
817 	 * not reflect the state of disk, so we don't want it written
818 	 * out until a format has been done.  The format will mark it
819 	 * dirty and write it out.
820 	 */
821 	work_list.flags &= ~(LIST_DIRTY|LIST_RELOAD);
822 	cur_list.flags = work_list.flags;
823 	if (EMBEDDED_SCSI)
824 		fmt_print("Defect List has a total of %d defects.\n",
825 		    cur_list.header.count);
826 	else
827 		fmt_print("Current Defect List updated, total of %d defects.\n",
828 		    cur_list.header.count);
829 	exit_critical();
830 	return (0);
831 }
832 
833 
834 /*
835  * This routine implements the 'create' command.  It creates the
836  * manufacturer's defect on the current disk from the defect list
837  */
838 int
839 d_create(void)
840 {
841 	int	status;
842 
843 	assert(!EMBEDDED_SCSI);
844 
845 	/*
846 	 * If the controller does not support the extraction, we're out
847 	 * of luck.
848 	 */
849 	if (cur_ops->op_create == NULL) {
850 		err_print("Controller does not support creating ");
851 		err_print("manufacturer's defect list.\n");
852 		return (-1);
853 	}
854 	/*
855 	 * Make sure the user is serious.  Note, for SCSI disks
856 	 * since this is instantaneous, we will just do it and
857 	 * not ask for confirmation.
858 	 */
859 	if (! (cur_ctype->ctype_flags & CF_SCSI) &&
860 	    check(
861 "Ready to create the manufacturers defect information on the disk.\n\
862 This cannot be interrupted and may take a long while.\n\
863 IT WILL DESTROY ALL OF THE DATA ON THE DISK! Continue"))
864 		return (-1);
865 	/*
866 	 * Lock out interrupts so we don't get half the list.
867 	 */
868 	enter_critical();
869 	fmt_print("Creating manufacturer's defect list...");
870 	/*
871 	 * Do the Creation
872 	 */
873 	status = (*cur_ops->op_create)(&work_list);
874 	if (status) {
875 		fmt_print("Creation failed.\n\n");
876 	} else {
877 		fmt_print("Creation complete.\n");
878 	}
879 	exit_critical();
880 	/*
881 	 * Return status.
882 	 */
883 	return (status);
884 }
885 
886 
887 /*
888  * Extract primary defect list - SCSI only
889  */
890 int
891 d_primary(void)
892 {
893 	int	status;
894 
895 	assert(EMBEDDED_SCSI);
896 
897 	/*
898 	 * Lock out interrupts so we don't get half the list and
899 	 * Kill off the working list.
900 	 */
901 	enter_critical();
902 	kill_deflist(&work_list);
903 	fmt_print("Extracting primary defect list...");
904 
905 	/*
906 	 * Do the extraction.
907 	 */
908 	status = scsi_ex_man(&work_list);
909 	if (status) {
910 		fmt_print("Extraction failed.\n\n");
911 	} else {
912 		fmt_print("Extraction complete.\n");
913 		/*
914 		 * Mark the working list dirty since we modified it.
915 		 * Automatically commit it, for SCSI only.
916 		 */
917 		work_list.flags |= LIST_DIRTY;
918 		status = commit_list();
919 		fmt_print("\n");
920 	}
921 	exit_critical();
922 
923 	/*
924 	 * Return status.
925 	 */
926 	return (status);
927 }
928 
929 
930 /*
931  * Extract grown defects list - SCSI only
932  */
933 int
934 d_grown(void)
935 {
936 	int	status;
937 
938 	assert(EMBEDDED_SCSI);
939 
940 	/*
941 	 * Lock out interrupts so we don't get half the list and
942 	 * Kill off the working list.
943 	 */
944 	enter_critical();
945 	kill_deflist(&work_list);
946 	fmt_print("Extracting grown defects list...");
947 
948 	/*
949 	 * Do the extraction.
950 	 */
951 	status = scsi_ex_grown(&work_list);
952 	if (status) {
953 		fmt_print("Extraction failed.\n\n");
954 	} else {
955 		fmt_print("Extraction complete.\n");
956 		/*
957 		 * Mark the working list dirty since we modified it.
958 		 * Automatically commit it, for SCSI only.
959 		 */
960 		work_list.flags |= LIST_DIRTY;
961 		status = commit_list();
962 		fmt_print("\n");
963 	}
964 	exit_critical();
965 
966 	/*
967 	 * Return status.
968 	 */
969 	return (status);
970 }
971 
972 
973 /*
974  * Extract both primary and grown defects list - SCSI only
975  */
976 int
977 d_both(void)
978 {
979 	int	status;
980 
981 	assert(EMBEDDED_SCSI);
982 
983 	/*
984 	 * Lock out interrupts so we don't get half the list and
985 	 * Kill off the working list.
986 	 */
987 	enter_critical();
988 	kill_deflist(&work_list);
989 	fmt_print("Extracting both primary and grown defects lists...");
990 
991 	/*
992 	 * Do the extraction.
993 	 */
994 	status = scsi_ex_cur(&work_list);
995 	if (status) {
996 		fmt_print("Extraction failed.\n\n");
997 	} else {
998 		fmt_print("Extraction complete.\n");
999 		/*
1000 		 * Mark the working list dirty since we modified it.
1001 		 * Automatically commit it, for SCSI only.
1002 		 */
1003 		work_list.flags |= LIST_DIRTY;
1004 		status = commit_list();
1005 		fmt_print("\n");
1006 	}
1007 	exit_critical();
1008 
1009 	/*
1010 	 * Return status.
1011 	 */
1012 	return (status);
1013 }
1014